Publication IA: - 4 templates (magazine, brief, essay, simple) avec CSS riche - Rewrite IA (article/exercises/tutorial/reference/mixed) - Modération avec timeout 12s + fallback safe - Quotas publish_enhance par tier (basic=2, pro=15, business=100) - Détection contenu stale (hash) - Migration DB publishedContent/publishedTemplate/publishedSourceHash Fixes: - cheerio v1.2: Element -> AnyNode (domhandler), decodeEntities cast - _isShared ajouté au type Note (champ virtuel serveur) - callout colors PDF export: extraction fonction pure testable - admin/published: guard note.userId null - Cmd+S fonctionne en mode dialog (pas seulement fullPage) i18n: - 23 clés publish* traduites dans les 15 locales - Extension Web Clipper: 13 locales mise à jour Tests: - callout-colors.test.ts (6 tests) - note-visible-in-view.test.ts (5 tests) - entitlements.test.ts + byok-entitlements.test.ts: mock usageLog + unstubAllEnvs - 199/199 tests passent Tracker: user-stories.md sync avec sprint-status.yaml
6.8 KiB
title, type, created, status, baseline_commit, context
| title | type | created | status | baseline_commit | context | |||
|---|---|---|---|---|---|---|---|---|
| Cross-Project Health Audit | chore | 2026-06-20 | done | 40f30155c2 |
|
Intent
Problem: The Memento workspace spans multiple deployable and reference projects (memento-note, mcp-server, extension, mobile, monitoring, CI/CD, prototypes) without a consolidated security and quality audit; several P0 issues were found (open uploads, MCP auth bypass, fail-open quotas).
Approach: Deliver a prioritized cross-project audit report (bugs + improvements) and, upon approval, remediate Phase 1 P0 security items first — app uploads/auth, MCP tenant isolation, extension Store readiness — before UX/i18n/prototype parity work.
Boundaries & Constraints
Always: No destructive DB commands; backup via dump-db.sh before any schema change; i18n via 15 locales when touching UI; follow prototype architectural-grid for UX gaps; fail-closed for billing quotas in production.
Ask First: Whether to fail-open vs fail-closed quotas during Redis outage; whether MCP x-user-id should be removed entirely or restricted to internal network; Chrome Web Store host_permissions scope; archiving architectural-grid1 vs keeping both prototypes.
Never: prisma migrate reset, db push --force-reset, pg_dump --clean in deploy scripts; exposing API keys in client bundles; shipping extension with dev host_permissions; implementing all audit items in one PR.
Code Map
memento-note/app/api/uploads/[...path]/route.ts-- uploads served without session checkmemento-note/app/api/image-proxy/route.ts-- open SSRF proxymemento-note/lib/entitlements.ts-- Redis fail-open on AI quotasmemento-note/auth.config.ts--/insights,/graph,/revision,/supportnot gatedmemento-note/context/notebooks-context.tsx-- broken AI toast note move (no home refresh)mcp-server/index-sse.js--x-user-idheader auth bypassmcp-server/tools.js-- missing userId filter +ensureUserId()first-user fallbackmemento-note/extension/dist-chrome-store/-- missing PNG icons, wide host_permissionsmemento-mobile/app.json-- references missingassets/monitoring/metrics-token-- placeholder secret committedscripts/deploy-prod.sh--pg_dump --clean, full Docker build fallbackdocker-compose.yml-- MCP/Ollama ports bound 0.0.0.0
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 leakmemento-note/app/api/image-proxy/route.ts-- require auth + domain allowlist -- close SSRF/abusememento-note/lib/entitlements.ts-- fail-closed when Redis unavailable in production -- align with billing policymemento-note/auth.config.ts-- add/insights,/graph,/revision,/supportto protected routes -- match API 401 behaviormcp-server/index-sse.js-- remove or restrictx-user-id; require API key on all tool paths -- close identity spoofingmcp-server/tools.js-- always scope queries to authenticated userId; removeensureUserId()fallback -- multi-tenant isolationdocker-compose.yml-- bind MCP127.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 - Given Redis down in production, when AI quota check runs, then request is denied (not unlimited)
- Given unauthenticated browser, when navigating to
/insights, then redirect to login - Given MCP request with only
x-user-idheader and no API key, when tool invoked, then 401 - Given authenticated user A, when MCP lists notes, then only user A notes returned
Spec Change Log
Design Notes
Full audit by project (2026-06-20):
memento-note CRITICAL: open uploads; image-proxy SSRF; fail-open quotas; middleware gaps; XSS via dangerouslySetInnerHTML (published pages, peek, SVG gallery); weak mobile HMAC; broken AI toast note move.
mcp-server CRITICAL: x-user-id spoofing; null userId returns all users' data; port 3001 public; Prisma schema drift vs main app.
extension CRITICAL: Store build missing PNG icons; host_permissions too broad for CWS.
mobile CRITICAL: missing assets folder; NativeWind configured but not installed.
monitoring CRITICAL: placeholder metrics token in git; Grafana on 0.0.0.0:3002.
CI/CD CRITICAL: manual deploy triggers full Docker build (fails on prod); pg_dump --clean in deploy script.
prototypes MEDIUM: Gemini API key in Vite client bundle; port 3000 conflict; grid vs grid1 divergence.
Deferred phases: P1 insights i18n + GraphKnowledgeMap; P2 editor drag-handle migration; P3 mobile EAS/CI; P4 monitoring alert cleanup.
Verification
Commands:
cd memento-note && npm run test:unit-- expected: pass (no regressions on touched modules)- Manual curl unauthenticated upload URL -- expected: 401/403 after Phase 1
Manual checks:
- Logged-out visit to
/insightsredirects 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 -
Prisma lookup tying filename to note content/images
upload-access.ts:4
SSRF / image proxy
-
Session gate + blocked private hosts on server-side fetch
route.ts:6 -
Shared SSRF hostname denylist
ssrf-guard.ts:2
Billing quotas fail-closed
-
Production denies when Redis is unavailable
entitlements.ts:88 -
Routes return 503 instead of silent fail-open
route.ts:81
Auth middleware
- Protect insights, graph, revision, support routes
auth.config.ts:30
MCP multi-tenant isolation
-
API key only; x-user-id removed
index-sse.js:291 -
Mandatory userId on all tool handlers
tools.js:490
Infrastructure
- MCP port bound to localhost only
docker-compose.yml:130
Tests
- Fail-open dev vs fail-closed prod entitlements
entitlements.test.ts:145