Manual security review for the /release Security review lane. The diff classifier flagged this release as sensitive, so this review records the adversarial pass and the verdict. The supplement's Security hardening section links it.
- Base ref:
v0.18.2 - Head ref:
HEAD - Diff classifier: sensitive (
sensitive=true) — sole reason:src/actions.tsmatches theauth-middlewarepath heuristic. The store HTTP handler lives in this file; the diff adds no auth logic. - Provider: manual review.
- Protected routes manifest: 115 routes, in sync with
openapi.yaml. - Changed files: 7 (2 source, 1 schema, 2 inspector, 1 test, 1 generated catalog).
Treat the diff as if you were an attacker. For every concern below, propose at least one concrete request or code path that exercises the failure mode, then either confirm the gate would catch it or describe the missing test.
- Alternate-path auth. Can an unauthenticated external caller reach a privileged path through an alternate channel?
- Proxy trust. Does any new code trust
X-Forwarded-For,Forwarded,Host, orreq.socket.remoteAddressoutside the canonical helpers? - Local-dev widening. Does any new path reference
LOCAL_DEV_USER_ID,assertExplicitlyTrusted,NEOTOMA_TRUST_PROD_LOOPBACK, or adev/sandboxshortcut? - Unauth public route. For every new Express route, confirm it is in
protected_routes_manifest.jsonor the runtime allow-list. - Guest-access policy widening. Does the diff change
assertGuestWriteAllowed,routeAcceptsGuestPrincipal, or any guest-token issuer? - AAuth / agent identity downgrade. Does the diff make it easier to satisfy auth without a verified
aa-agent+jwt? - Cross-user read via content-addressed id. The new
createObservationexistence check looks up an observation by its content-addressed id. Can a caller use it to read or infer another user's observation?
- No new routes; manifest unchanged. The diff registers no Express routes.
security:manifest:checkconfirms the protected-routes manifest is in sync (115 routes). Concerns 1 and 4 do not apply. - No auth-logic change in
src/actions.ts. The change inhandleStorePostbranches on the existingsource_storagerequest field to choose reference vs. inline file handling. It is reached only aftergetAuthenticatedUserId(req, …), the same authentication boundary as before. No middleware, guest-policy, AAuth, or principal-resolution code is touched. Concerns 1, 5, and 6 do not apply. - No proxy-trust changes. No new use of
X-Forwarded-For,Forwarded,Host, orreq.socket.remoteAddress. Concern 2 does not apply. - No local-dev widening. No reference to
LOCAL_DEV_USER_ID,assertExplicitlyTrusted, orNEOTOMA_TRUST_PROD_LOOPBACKin the changed files. Concern 3 does not apply. - Cross-user read is not possible (concern 7). The new existence check in
createObservationis.eq("id", observationId).eq("user_id", params.user_id). It is scoped to the authenticated caller'suser_id, so a same-content observation owned by another user is never returned. This is the same scoping the MCP store path already uses (src/server.ts), and its comment documents the intent: prevent a different user's same-content observation from masking the current user's write. The change brings the REST path to parity; it does not widen read scope. An attacker who guesses another user's content-addressed observation id still gets no row back, because theuser_idfilter excludes it. - Reference-mode file handling. Reference mode resolves the supplied
file_pathto an absolute path and registers it viastoreRawReference. Path resolution and the host-local reference model are unchanged from the MCP path shipped in #1775/#1830; this release only makes the REST route reach the same code. No new filesystem-traversal surface beyond what MCP reference storage already exposes. - G1 classify-diff:
sensitive=true(path heuristic onsrc/actions.ts; no auth change in fact). - G2 security:lint: 0 errors, 125 warnings (all pre-existing; none in changed lines).
- G3 security:manifest:check + test:security:auth-matrix: both passed (115 routes in sync; 18 passed / 1 skipped).
tests/integration/http_store_reference_source.test.tscovers the REST reference path for both shapes. The existingtests/security/auth_topology_matrix.test.tscontinues to assert the store route's auth topology. No additional auth-adjacent negative test is required, because no auth path changed and the new query is user-scoped.
- None. The diff makes an existing, already-authenticated REST route honor an existing request field, and adds a user-scoped idempotency guard mirroring the MCP path. No auth surface is widened.
| Reviewer | Verdict | Date |
|---|---|---|
| ateles-agent (manual) | yes | 2026-06-30 |
Verdict yes — classifier-sensitive by file-path heuristic only; manual review confirms no authorization change and no cross-user read. No block.
src/actions.tssrc/services/observation_storage.tssrc/shared/action_schemas.tsinspector/src/hooks/use_graph.tsinspector/src/pages/embed_graph.tsxtests/integration/http_store_reference_source.test.tsdocs/testing/automated_test_catalog.md