Skip to content

Relay fixes round 2, batch 2: dashboard path traversal, MCP errors, query bound - #45

Merged
alexeybe1kin merged 7 commits into
mainfrom
fix/relay-memorygate-r2-b2
Nov 3, 2026
Merged

alexeybe1kin merged 7 commits into
mainfrom
fix/relay-memorygate-r2-b2

Conversation

@alexeybe1kin

Copy link
Copy Markdown
Collaborator

GLM relay round 2 (hunt, vote, fix), reviewed by Claude.

  • Dashboard server: a path with encoded ../ could serve files outside dist/; now resolved and checked with is_relative_to.
  • Screenshot interact step: missing search input no longer crashes.
  • MCP server: unknown methods return JSON-RPC "Method not found" instead of a success; batch (array) requests get a clear error; the context tool's query is bounded (2000 chars, no extra properties).
  • Package boundary check: app/__init__.py is no longer flagged as a loose module.

Review corrections (last commit): the boundary check still reads imports in __init__.py files (GLM had skipped them, which would hide real edges); ping is answered with {} so MCP clients keep working.

Checks: 406 passed locally (services/api, contract test excluded as in CI); ruff clean; package boundaries ok (12 edges); MCP stdin probe: ping → {}, unknown → error, [1] → batch error, empty query → error.

🤖 Generated with Claude Code

alexeybe1kin and others added 7 commits November 3, 2026 13:45
Calling with actions like 'search:foo' on a page without input[placeholder="Search memories..."] (e.g. wrong route or unauthenticated state) makes `input` null; `input.type` throws TypeError and no screenshot is saved.

Found by the GLM relay (memorygate__dashboard__screenshot-interact.mjs_p0__bugs__0), confirmed by GLM votes (round 2).
Regression test to add: Run the script with actions 'search:foo' against a route lacking the search input and verify it logs an error, skips the action, and still saves the screenshot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… dist/

GET /..%2F..%2F..%2Fetc%2Fpasswd -> candidate = DIST_DIR/../../../etc/passwd resolves outside dist; candidate.is_file() is True and FileResponse returns the arbitrary system file (the {full_path:path} converter does not reject dot-segments after URL decoding).

Found by the GLM relay (memorygate__dashboard__server.py_p0__bugs__0), confirmed by GLM votes (round 2).
Regression test to add: Test that a GET to /..%2F..%2F..%2Fetc%2Fpasswd returns the SPA index.html (or a non-passwd response) rather than /etc/passwd contents, and that normal files under dist/ still serve.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
If services/api/app/ contains a normal __init__.py (required for `from app import ...` to resolve in most setups), APP.glob("*.py") matches it, and since its name is not main.py the check reports '__init__.py: modules live in a package named for their type' and exits 1 — the invariant check fails on a perfectly legal layout.

Found by the GLM relay (memorygate__scripts__check_package_boundaries.py_p0__bugs__0), confirmed by GLM votes (round 2).
Regression test to add: Add an empty services/api/app/__init__.py and verify the script exits 0 and reports no loose-module or cross-package problems.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A request with an id and an unrecognized method (e.g. "resources/list") falls into the elif "id" branch and gets an empty success result instead of a JSON-RPC Method not found error, so clients believe the capability exists.

Found by the GLM relay (memorygate__services__mcp_p0__bugs__0), confirmed by GLM votes (round 2).
Regression test to add: Send a JSON-RPC request with method "resources/list" and an id, and assert the response contains error code -32601 ("Method not found") rather than an empty result.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A batch payload like "[{\"id\":1,\"method\":\"tools/list\"}]" makes request.get raise AttributeError; the except branch then emits a single response with null id, so the batch's per-item responses are never delivered.

Found by the GLM relay (memorygate__services__mcp_p0__bugs__1), confirmed by GLM votes (round 2).
Regression test to add: A test should feed the line '[{"id":1,"method":"tools/list"}]' to the server and assert it emits an Invalid request error response with null id without crashing or attempting request.get on a list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
memorygate_context's inputSchema imposes no maxLength on 'query' (unlike EXPLORE's strict schema), so a model/client can send an arbitrarily large query string that is forwarded verbatim to the backend, enabling unbounded input / DoS or oversized storage of query text.

Found by the GLM relay (memorygate__services__mcp_p0__sec__0), confirmed by GLM votes (round 2).
Regression test to add: A regression test should send a memorygate_context tools/call with a query longer than 2000 characters (or missing/non-string) and assert the server returns an error without issuing a request to the backend, while a query at or under the limit still succeeds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The boundary check must still read imports in package __init__.py files (only the
loose-module rule allows them). MCP clients send ping; the new Method-not-found branch
would have refused it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alexeybe1kin
alexeybe1kin merged commit c796d3b into main Nov 3, 2026
3 checks passed
@alexeybe1kin
alexeybe1kin deleted the fix/relay-memorygate-r2-b2 branch November 3, 2026 12:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant