fix(security): Phase 1 P0 hardening from cross-project audit
All checks were successful
CI / Lint, Unit Tests & Build (push) Successful in 5m42s
CI / Deploy production (on server) (push) Successful in 33s

Close open uploads, image-proxy SSRF, fail-open AI quotas in production,
auth gaps on app routes, and MCP tenant isolation issues.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Antigravity
2026-06-20 16:53:19 +00:00
parent 40f30155c2
commit 4d96605144
16 changed files with 264 additions and 95 deletions

View File

@@ -2,8 +2,8 @@
title: 'Cross-Project Health Audit'
type: 'chore'
created: '2026-06-20'
status: 'in-progress'
baseline_commit: 'HEAD'
status: 'done'
baseline_commit: '40f30155c2a0f118ca41c8226992035bcab55a46'
context:
- '{project-root}/AGENTS.md'
- '{project-root}/CLAUDE.md'
@@ -46,13 +46,13 @@ context:
## Tasks & Acceptance
**Execution (Phase 1 — P0 security only, if approved):**
- [ ] `memento-note/app/api/uploads/[...path]/route.ts` -- require auth + verify note ownership for file path -- prevent private attachment leak
- [ ] `memento-note/app/api/image-proxy/route.ts` -- require auth + domain allowlist -- close SSRF/abuse
- [ ] `memento-note/lib/entitlements.ts` -- fail-closed when Redis unavailable in production -- align with billing policy
- [ ] `memento-note/auth.config.ts` -- add `/insights`, `/graph`, `/revision`, `/support` to protected routes -- match API 401 behavior
- [ ] `mcp-server/index-sse.js` -- remove or restrict `x-user-id`; require API key on all tool paths -- close identity spoofing
- [ ] `mcp-server/tools.js` -- always scope queries to authenticated userId; remove `ensureUserId()` fallback -- multi-tenant isolation
- [ ] `docker-compose.yml` -- bind MCP `127.0.0.1:3001:3001` -- reduce network exposure
- [x] `memento-note/app/api/uploads/[...path]/route.ts` -- require auth + verify note ownership for file path -- prevent private attachment leak
- [x] `memento-note/app/api/image-proxy/route.ts` -- require auth + domain allowlist -- close SSRF/abuse
- [x] `memento-note/lib/entitlements.ts` -- fail-closed when Redis unavailable in production -- align with billing policy
- [x] `memento-note/auth.config.ts` -- add `/insights`, `/graph`, `/revision`, `/support` to protected routes -- match API 401 behavior
- [x] `mcp-server/index-sse.js` -- remove or restrict `x-user-id`; require API key on all tool paths -- close identity spoofing
- [x] `mcp-server/tools.js` -- always scope queries to authenticated userId; remove `ensureUserId()` fallback -- multi-tenant isolation
- [x] `docker-compose.yml` -- bind MCP `127.0.0.1:3001:3001` -- reduce network exposure
**Acceptance Criteria:**
- Given an unauthenticated request, when GET `/api/uploads/notes/{uuid}.png`, then response is 401/403
@@ -92,3 +92,53 @@ Full audit by project (2026-06-20):
**Manual checks:**
- Logged-out visit to `/insights` redirects to login
- MCP tool call without API key returns 401
## Suggested Review Order
**Upload access control**
- Ownership check before serving note attachments; published notes stay public
[`route.ts:30`](../../memento-note/app/api/uploads/[...path]/route.ts#L30)
- Prisma lookup tying filename to note content/images
[`upload-access.ts:4`](../../memento-note/lib/upload-access.ts#L4)
**SSRF / image proxy**
- Session gate + blocked private hosts on server-side fetch
[`route.ts:6`](../../memento-note/app/api/image-proxy/route.ts#L6)
- Shared SSRF hostname denylist
[`ssrf-guard.ts:2`](../../memento-note/lib/ssrf-guard.ts#L2)
**Billing quotas fail-closed**
- Production denies when Redis is unavailable
[`entitlements.ts:88`](../../memento-note/lib/entitlements.ts#L88)
- Routes return 503 instead of silent fail-open
[`route.ts:81`](../../memento-note/app/api/chat/route.ts#L81)
**Auth middleware**
- Protect insights, graph, revision, support routes
[`auth.config.ts:30`](../../memento-note/auth.config.ts#L30)
**MCP multi-tenant isolation**
- API key only; x-user-id removed
[`index-sse.js:291`](../../mcp-server/index-sse.js#L291)
- Mandatory userId on all tool handlers
[`tools.js:490`](../../mcp-server/tools.js#L490)
**Infrastructure**
- MCP port bound to localhost only
[`docker-compose.yml:130`](../../docker-compose.yml#L130)
**Tests**
- Fail-open dev vs fail-closed prod entitlements
[`entitlements.test.ts:145`](../../memento-note/tests/unit/entitlements.test.ts#L145)