diff --git a/.cursor/hooks/state/continual-learning.json b/.cursor/hooks/state/continual-learning.json index 86ab88d..bd92fec 100644 --- a/.cursor/hooks/state/continual-learning.json +++ b/.cursor/hooks/state/continual-learning.json @@ -1,8 +1,8 @@ { "version": 1, - "lastRunAtMs": 1779998560332, - "turnsSinceLastRun": 9, - "lastTranscriptMtimeMs": 1779998515529, - "lastProcessedGenerationId": "3fb0d1db-af2c-46f8-b80f-c068ff52d0b9", + "lastRunAtMs": 1781973755639, + "turnsSinceLastRun": 2, + "lastTranscriptMtimeMs": 1781973755517.7488, + "lastProcessedGenerationId": "b11602f7-77e2-496d-991f-fb97c31c6367", "trialStartedAtMs": null } diff --git a/_bmad-output/implementation-artifacts/spec-cross-project-audit.md b/_bmad-output/implementation-artifacts/spec-cross-project-audit.md new file mode 100644 index 0000000..1229109 --- /dev/null +++ b/_bmad-output/implementation-artifacts/spec-cross-project-audit.md @@ -0,0 +1,94 @@ +--- +title: 'Cross-Project Health Audit' +type: 'chore' +created: '2026-06-20' +status: 'in-progress' +baseline_commit: 'HEAD' +context: + - '{project-root}/AGENTS.md' + - '{project-root}/CLAUDE.md' + - '{project-root}/memento-note/docs/admin-billing-quotas-guide.md' +--- + + + +## Intent + +**Problem:** The Momento 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 check +- `memento-note/app/api/image-proxy/route.ts` -- open SSRF proxy +- `memento-note/lib/entitlements.ts` -- Redis fail-open on AI quotas +- `memento-note/auth.config.ts` -- `/insights`, `/graph`, `/revision`, `/support` not gated +- `memento-note/context/notebooks-context.tsx` -- broken AI toast note move (no home refresh) +- `mcp-server/index-sse.js` -- `x-user-id` header auth bypass +- `mcp-server/tools.js` -- missing userId filter + `ensureUserId()` first-user fallback +- `memento-note/extension/dist-chrome-store/` -- missing PNG icons, wide host_permissions +- `memento-mobile/app.json` -- references missing `assets/` +- `monitoring/metrics-token` -- placeholder secret committed +- `scripts/deploy-prod.sh` -- `pg_dump --clean`, full Docker build fallback +- `docker-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 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 + +**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-id` header 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 `/insights` redirects to login +- MCP tool call without API key returns 401 diff --git a/memento-note/locales/en.json b/memento-note/locales/en.json index 2fd68c1..c880083 100644 --- a/memento-note/locales/en.json +++ b/memento-note/locales/en.json @@ -2487,36 +2487,6 @@ "columnsAdd": "Add a column", "columnsDelete": "Delete columns", "columnsLabel": "columns", - "wizardTitle": "Create a notebook with AI", - "wizardChooseProfile": "What's your profile?", - "wizardProfileStudent": "Student", - "wizardProfileStudentDesc": "AI creates a structured course notebook with summaries, formulas and key takeaways", - "wizardProfileTeacher": "Teacher", - "wizardProfileTeacherDesc": "AI generates a course structure with chapters, exercises and learning objectives", - "wizardProfileEngineer": "Engineer / Professional", - "wizardProfileEngineerDesc": "AI creates organized technical documentation with specs and references", - "wizardTopicStudentPlaceholder": "e.g: Thermodynamics, Calculus, French Revolution...", - "wizardTopicTeacherPlaceholder": "e.g: Math 101, AP Physics, Algorithms...", - "wizardTopicEngineerPlaceholder": "e.g: Microservices architecture, Network security, ISO 27001...", - "wizardTopic": "Topic", - "wizardLevel": "Level", - "wizardLevelBeginner": "Beginner", - "wizardLevelIntermediate": "Intermediate", - "wizardLevelAdvanced": "Advanced", - "wizardLevelExpert": "Expert", - "wizardNoteCount": "Number of notes", - "wizardNotes": "notes", - "wizardConfirmHint": "AI will create a notebook with rich notes: callouts, collapsible sections, math formulas, comparison columns, outlines and links.", - "wizardGenerate": "Generate notebook", - "wizardLoading": "AI is creating your notebook with structured notes...", - "wizardProgressGenerating": "Generating content with AI...", - "wizardProgressCalling": "Calling AI...", - "wizardProgressParsing": "Parsing AI response...", - "wizardProgressCreating": "Creating notebook and notes...", - "wizardSuccess": "Notebook created successfully!", - "wizardCreated": "Notebook created:", - "wizardNotesCreated": "notes created", - "wizardOpenNotebook": "Open notebook", "calloutDelete": "Delete callout", "calloutUnwrap": "Disable callout", "calloutInfo": "Information", @@ -2634,14 +2604,6 @@ "aiGenerateExercisesDesc": "AI creates 5 exercises based on this note, with varied difficulty levels and detailed answers.", "aiGenerateExercisesCreated": "exercises created!", "aiGenerateGenerateExercises": "Generate", - "wizardStudyPlanner": "Study Plan", - "wizardStudyPlannerDesc": "AI creates a revision plan based on spaced repetition.", - "wizardExamDate": "Exam date", - "wizardGeneratePlan": "Generate plan", - "wizardStudyPlanLoading": "Creating plan...", - "wizardStudyPlanSuccess": "Plan created! Reminders have been added to your notes.", - "wizardDaysPlanned": "days planned", - "wizardStudyPlanReminders": "Reminders have been automatically added to your notes.", "structuredViewsImportCsv": "Import CSV file", "structuredViewsExportCsv": "Export as CSV", "structuredViewsOrganizer": "Organize", @@ -2697,16 +2659,6 @@ "tagApplied": "applied", "noSuggestions": "No suggestions — notebook looks well organized." }, - "wizardOrganizer": "Organize with AI", - "wizardOrganizerDesc": "AI analyzes your notes and suggests tags, groupings and duplicate detection.", - "wizardAnalyze": "Analyze notebook", - "wizardOrganizing": "Analyzing notes...", - "wizardSuggestedTags": "Suggested tags", - "wizardCategories": "Suggested groupings", - "wizardDuplicates": "Duplicates detected", - "wizardApply": "Apply", - "wizardTagApplied": "applied", - "wizardNoSuggestions": "No suggestions — notebook looks well organized.", "importMarkdown": "Import Markdown", "markdownExportSuccess": "Note exported as Markdown", "markdownExportError": "Failed to export note", @@ -3867,5 +3819,55 @@ "editor": { "voiceStart": "Dictate text (microphone)", "voiceStop": "Stop dictation" + }, + "wizard": { + "title": "Create a notebook with AI", + "chooseProfile": "What's your profile?", + "profileStudent": "Student", + "profileStudentDesc": "AI creates a structured course notebook with summaries, formulas and key takeaways", + "profileTeacher": "Teacher", + "profileTeacherDesc": "AI generates a course structure with chapters, exercises and learning objectives", + "profileEngineer": "Engineer / Professional", + "profileEngineerDesc": "AI creates organized technical documentation with specs and references", + "topicStudentPlaceholder": "e.g: Thermodynamics, Calculus, French Revolution...", + "topicTeacherPlaceholder": "e.g: Math 101, AP Physics, Algorithms...", + "topicEngineerPlaceholder": "e.g: Microservices architecture, Network security, ISO 27001...", + "topic": "Topic", + "level": "Level", + "levelBeginner": "Beginner", + "levelIntermediate": "Intermediate", + "levelAdvanced": "Advanced", + "levelExpert": "Expert", + "noteCount": "Number of notes", + "notes": "notes", + "confirmHint": "AI will create a notebook with rich notes: callouts, collapsible sections, math formulas, comparison columns, outlines and links.", + "generate": "Generate notebook", + "loading": "AI is creating your notebook with structured notes...", + "progressGenerating": "Generating content with AI...", + "progressCalling": "Calling AI...", + "progressParsing": "Parsing AI response...", + "progressCreating": "Creating notebook and notes...", + "success": "Notebook created successfully!", + "created": "Notebook created:", + "notesCreated": "notes created", + "openNotebook": "Open notebook", + "studyPlanner": "Study Plan", + "studyPlannerDesc": "AI creates a revision plan based on spaced repetition.", + "examDate": "Exam date", + "generatePlan": "Generate plan", + "studyPlanLoading": "Creating plan...", + "studyPlanSuccess": "Plan created! Reminders have been added to your notes.", + "daysPlanned": "days planned", + "studyPlanReminders": "Reminders have been automatically added to your notes.", + "organizer": "Organize with AI", + "organizerDesc": "AI analyzes your notes and suggests tags, groupings and duplicate detection.", + "analyze": "Analyze notebook", + "organizing": "Analyzing notes...", + "suggestedTags": "Suggested tags", + "categories": "Suggested groupings", + "duplicates": "Duplicates detected", + "apply": "Apply", + "tagApplied": "applied", + "noSuggestions": "No suggestions — notebook looks well organized." } -} \ No newline at end of file +} diff --git a/memento-note/locales/fr.json b/memento-note/locales/fr.json index 2c54cda..c867197 100644 --- a/memento-note/locales/fr.json +++ b/memento-note/locales/fr.json @@ -2491,36 +2491,6 @@ "columnsAdd": "Ajouter une colonne", "columnsDelete": "Supprimer les colonnes", "columnsLabel": "colonnes", - "wizardTitle": "Créer un carnet avec l'IA", - "wizardChooseProfile": "Quel est votre profil ?", - "wizardProfileStudent": "Étudiant", - "wizardProfileStudentDesc": "L'IA crée un carnet de cours structuré avec résumés, formules et encadrés à retenir", - "wizardProfileTeacher": "Professeur", - "wizardProfileTeacherDesc": "L'IA génère la structure d'un cours avec chapitres, exercices et objectifs pédagogiques", - "wizardProfileEngineer": "Ingénieur / Professionnel", - "wizardProfileEngineerDesc": "L'IA crée une documentation technique organisée avec spécifications et références", - "wizardTopicStudentPlaceholder": "Ex: Thermodynamique, Calcul différentiel, Histoire de la Révolution...", - "wizardTopicTeacherPlaceholder": "Ex: Mathématiques L1, Physique-Chimie Terminale, Algorithmique...", - "wizardTopicEngineerPlaceholder": "Ex: Architecture microservices, Sécurité réseau, Norme ISO 27001...", - "wizardTopic": "Sujet", - "wizardLevel": "Niveau", - "wizardLevelBeginner": "Débutant", - "wizardLevelIntermediate": "Intermédiaire", - "wizardLevelAdvanced": "Avancé", - "wizardLevelExpert": "Expert", - "wizardNoteCount": "Nombre de notes", - "wizardNotes": "notes", - "wizardConfirmHint": "L'IA va créer un carnet avec des notes riches : encadrés, sections repliables, formules mathématiques, colonnes de comparaison, sommaires et liens.", - "wizardGenerate": "Générer le carnet", - "wizardLoading": "L'IA crée votre carnet avec des notes structurées...", - "wizardProgressGenerating": "Génération du contenu par l'IA...", - "wizardProgressCalling": "Appel de l'IA...", - "wizardProgressParsing": "Analyse de la réponse de l'IA...", - "wizardProgressCreating": "Création du carnet et des notes...", - "wizardSuccess": "Carnet créé avec succès !", - "wizardCreated": "Carnet créé :", - "wizardNotesCreated": "notes créées", - "wizardOpenNotebook": "Ouvrir le carnet", "calloutDelete": "Supprimer l'encadré", "calloutUnwrap": "Désactiver l'encadré", "calloutInfo": "Information", @@ -2638,14 +2608,6 @@ "aiGenerateExercisesDesc": "L'IA crée 5 exercices basés sur cette note, avec des niveaux de difficulté variés et des corrigés détaillés.", "aiGenerateExercisesCreated": "exercices créés !", "aiGenerateGenerateExercises": "Générer", - "wizardStudyPlanner": "Planning de révision", - "wizardStudyPlannerDesc": "L'IA crée un planning de révision basé sur la répétition espacée.", - "wizardExamDate": "Date de l'examen", - "wizardGeneratePlan": "Générer le planning", - "wizardStudyPlanLoading": "Création du planning...", - "wizardStudyPlanSuccess": "Planning créé ! Des rappels ont été ajoutés à vos notes.", - "wizardDaysPlanned": "jours planifiés", - "wizardStudyPlanReminders": "Des rappels ont été ajoutés automatiquement à vos notes.", "structuredViewsImportCsv": "Importer un fichier CSV", "structuredViewsExportCsv": "Exporter en CSV", "structuredViewsOrganizer": "Organiser", @@ -2701,16 +2663,6 @@ "tagApplied": "appliqué", "noSuggestions": "Aucune suggestion — le carnet semble bien organisé." }, - "wizardOrganizer": "Organiser avec l'IA", - "wizardOrganizerDesc": "L'IA analyse vos notes et propose tags, regroupements et détection de doublons.", - "wizardAnalyze": "Analyser le carnet", - "wizardOrganizing": "Analyse des notes en cours...", - "wizardSuggestedTags": "Tags suggérés", - "wizardCategories": "Regroupements suggérés", - "wizardDuplicates": "Doublons détectés", - "wizardApply": "Appliquer", - "wizardTagApplied": "appliqué", - "wizardNoSuggestions": "Aucune suggestion — le carnet semble bien organisé.", "importMarkdown": "Importer un Markdown", "markdownExportSuccess": "Note exportée en Markdown", "markdownExportError": "Échec de l'export de la note", @@ -3871,5 +3823,55 @@ "editor": { "voiceStart": "Dicter du texte (microphone)", "voiceStop": "Arrêter la dictée" + }, + "wizard": { + "title": "Créer un carnet avec l'IA", + "chooseProfile": "Quel est votre profil ?", + "profileStudent": "Étudiant", + "profileStudentDesc": "L'IA crée un carnet de cours structuré avec résumés, formules et encadrés à retenir", + "profileTeacher": "Professeur", + "profileTeacherDesc": "L'IA génère la structure d'un cours avec chapitres, exercices et objectifs pédagogiques", + "profileEngineer": "Ingénieur / Professionnel", + "profileEngineerDesc": "L'IA crée une documentation technique organisée avec spécifications et références", + "topicStudentPlaceholder": "Ex: Thermodynamique, Calcul différentiel, Histoire de la Révolution...", + "topicTeacherPlaceholder": "Ex: Mathématiques L1, Physique-Chimie Terminale, Algorithmique...", + "topicEngineerPlaceholder": "Ex: Architecture microservices, Sécurité réseau, Norme ISO 27001...", + "topic": "Sujet", + "level": "Niveau", + "levelBeginner": "Débutant", + "levelIntermediate": "Intermédiaire", + "levelAdvanced": "Avancé", + "levelExpert": "Expert", + "noteCount": "Nombre de notes", + "notes": "notes", + "confirmHint": "L'IA va créer un carnet avec des notes riches : encadrés, sections repliables, formules mathématiques, colonnes de comparaison, sommaires et liens.", + "generate": "Générer le carnet", + "loading": "L'IA crée votre carnet avec des notes structurées...", + "progressGenerating": "Génération du contenu par l'IA...", + "progressCalling": "Appel de l'IA...", + "progressParsing": "Analyse de la réponse de l'IA...", + "progressCreating": "Création du carnet et des notes...", + "success": "Carnet créé avec succès !", + "created": "Carnet créé :", + "notesCreated": "notes créées", + "openNotebook": "Ouvrir le carnet", + "studyPlanner": "Planning de révision", + "studyPlannerDesc": "L'IA crée un planning de révision basé sur la répétition espacée.", + "examDate": "Date de l'examen", + "generatePlan": "Générer le planning", + "studyPlanLoading": "Création du planning...", + "studyPlanSuccess": "Planning créé ! Des rappels ont été ajoutés à vos notes.", + "daysPlanned": "jours planifiés", + "studyPlanReminders": "Des rappels ont été ajoutés automatiquement à vos notes.", + "organizer": "Organiser avec l'IA", + "organizerDesc": "L'IA analyse vos notes et propose tags, regroupements et détection de doublons.", + "analyze": "Analyser le carnet", + "organizing": "Analyse des notes en cours...", + "suggestedTags": "Tags suggérés", + "categories": "Regroupements suggérés", + "duplicates": "Doublons détectés", + "apply": "Appliquer", + "tagApplied": "appliqué", + "noSuggestions": "Aucune suggestion — le carnet semble bien organisé." } -} \ No newline at end of file +}