fix(security): Phase 1 P0 hardening from cross-project audit
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:
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user