mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-22 05:46:42 +02:00
feat(crypto): Phase C — build-time registry ↔ Dexie audit
Before: adding a new Dexie table left the encryption decision implicit.
If you forgot to register it, the table silently shipped in plaintext
forever — no error, no warning, no footprint anywhere. The architecture
audit flagged this as the root of Concern 1.
- `scripts/audit-crypto-registry.mjs` parses database.ts's `.stores()`
blocks and registry.ts's entries, then enforces three invariants:
1. Every Dexie table is either in the encryption registry OR in the
new `plaintext-allowlist.ts` — one conscious classification per
table.
2. No dead registry entries (referring to tables that no longer
exist in Dexie).
3. No table appears in both — single authoritative source.
- `plaintext-allowlist.ts` auto-seeded from current state. 105 entries,
each tagged `// TODO: audit` as an invitation to review whether the
table truly holds nothing sensitive. The allowlist is intentionally
a separate file so additions are reviewable on their own (not buried
inside database.ts schema bumps).
- Wired into `pnpm run check:crypto` + CI validate job — a new table
now fails the PR check instead of slipping past review.
- `check:crypto:seed` regenerates the allowlist if ever needed.
Verified: drift simulation (removing aiMissions from the allowlist)
fails the audit with a clear message pointing at the missing
classification. Current state passes: 187 Dexie tables, 82 encrypted,
105 explicit plaintext.
Concern 1 is now fully closed (A: typed registry entries, B: dev-mode
runtime drift check, C: build-time audit enforcing coverage).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a2598b9c57
commit
c7af693c6d
5 changed files with 406 additions and 3 deletions
125
apps/mana/apps/web/src/lib/data/crypto/plaintext-allowlist.ts
Normal file
125
apps/mana/apps/web/src/lib/data/crypto/plaintext-allowlist.ts
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/**
|
||||
* Plaintext allowlist — Dexie tables that are intentionally NOT encrypted.
|
||||
*
|
||||
* Counterpart to ENCRYPTION_REGISTRY in crypto/registry.ts. The audit script
|
||||
* (`scripts/audit-crypto-registry.mjs`, wired as `pnpm run check:crypto`)
|
||||
* fails if a Dexie table is in neither list.
|
||||
*
|
||||
* Why a separate file: adding a table here is a conscious security decision
|
||||
* ("this genuinely holds no user-sensitive data") and should be reviewable
|
||||
* as its own diff, not buried inside database.ts.
|
||||
*
|
||||
* Auto-seeded from current state on 2026-04-20 — every entry below was
|
||||
* introduced before the audit script existed. The `// TODO: audit` markers
|
||||
* are an invitation to review each one: does this table really hold nothing
|
||||
* that would embarrass the user if it leaked? If not, move it to the
|
||||
* encryption registry.
|
||||
*/
|
||||
|
||||
export const PLAINTEXT_ALLOWLIST: readonly string[] = [
|
||||
'achievements', // TODO: audit
|
||||
'activities', // TODO: audit
|
||||
'aiMissions', // TODO: audit
|
||||
'albumItems', // TODO: audit
|
||||
'albums', // TODO: audit
|
||||
'automations', // TODO: audit
|
||||
'boardViews', // TODO: audit
|
||||
'budgets', // TODO: audit
|
||||
'calculations', // TODO: audit
|
||||
'calendars', // TODO: audit
|
||||
'ccFavorites', // TODO: audit
|
||||
'ccLocationTags', // TODO: audit
|
||||
'ccLocations', // TODO: audit
|
||||
'cities', // TODO: audit
|
||||
'companionConversations', // TODO: audit
|
||||
'companionGoals', // TODO: audit
|
||||
'companionMessages', // TODO: audit
|
||||
'contactTags', // TODO: audit
|
||||
'contextSpaces', // TODO: audit
|
||||
'conversationTags', // TODO: audit
|
||||
'customQuotes', // TODO: audit
|
||||
'dashboardConfigs', // TODO: audit
|
||||
'deckTags', // TODO: audit
|
||||
'documentTags', // TODO: audit
|
||||
'dreamTags', // TODO: audit
|
||||
'entryTags', // TODO: audit
|
||||
'eventInvitations', // TODO: audit
|
||||
'eventItems', // TODO: audit
|
||||
'eventTags', // TODO: audit
|
||||
'fileTags', // TODO: audit
|
||||
'financeCategories', // TODO: audit
|
||||
'foodFavorites', // TODO: audit
|
||||
'globalTags', // TODO: audit
|
||||
'goals', // TODO: audit
|
||||
'guideCollections', // TODO: audit
|
||||
'guideTags', // TODO: audit
|
||||
'habitLogs', // TODO: audit
|
||||
'habits', // TODO: audit
|
||||
'imageTags', // TODO: audit
|
||||
'invCategories', // TODO: audit
|
||||
'invCollections', // TODO: audit
|
||||
'invItemTags', // TODO: audit
|
||||
'invLocations', // TODO: audit
|
||||
'linkTags', // TODO: audit
|
||||
'manaLinks', // TODO: audit
|
||||
'markers', // TODO: audit
|
||||
'mealTags', // TODO: audit
|
||||
'memoSpaces', // TODO: audit
|
||||
'memoTags', // TODO: audit
|
||||
'memoroSpaces', // TODO: audit
|
||||
'moodTags', // TODO: audit
|
||||
'moods', // TODO: audit
|
||||
'mukkeProjects', // TODO: audit
|
||||
'newsCachedFeed', // TODO: audit
|
||||
'noteTags', // TODO: audit
|
||||
'pendingProposals', // TODO: audit
|
||||
'periodSymptoms', // TODO: audit
|
||||
'photoFavorites', // TODO: audit
|
||||
'photoMediaTags', // TODO: audit
|
||||
'placeTags', // TODO: audit
|
||||
'plantPhotos', // TODO: audit
|
||||
'plantTags', // TODO: audit
|
||||
'playlistSongs', // TODO: audit
|
||||
'presiDeckTags', // TODO: audit
|
||||
'qCollections', // TODO: audit
|
||||
'questionTags', // TODO: audit
|
||||
'quizAttempts', // TODO: audit
|
||||
'quotesFavorites', // TODO: audit
|
||||
'quotesListTags', // TODO: audit
|
||||
'quotesLists', // TODO: audit
|
||||
'reminders', // TODO: audit
|
||||
'ritualLogs', // TODO: audit
|
||||
'ritualSteps', // TODO: audit
|
||||
'rituals', // TODO: audit
|
||||
'runs', // TODO: audit
|
||||
'savedFormulas', // TODO: audit
|
||||
'sequences', // TODO: audit
|
||||
'skillTags', // TODO: audit
|
||||
'skills', // TODO: audit
|
||||
'songTags', // TODO: audit
|
||||
'spaceMembers', // TODO: audit
|
||||
'storageFolders', // TODO: audit
|
||||
'tagGroups', // TODO: audit
|
||||
'taskLabels', // TODO: audit
|
||||
'timeAlarms', // TODO: audit
|
||||
'timeBlockTags', // TODO: audit
|
||||
'timeClients', // TODO: audit
|
||||
'timeCountdownTimers', // TODO: audit
|
||||
'timeEntries', // TODO: audit
|
||||
'timeProjects', // TODO: audit
|
||||
'timeSettings', // TODO: audit
|
||||
'timeTemplates', // TODO: audit
|
||||
'timeWorldClocks', // TODO: audit
|
||||
'todoProjects', // TODO: audit
|
||||
'uloadFolders', // TODO: audit
|
||||
'uloadTags', // TODO: audit
|
||||
'userSettings', // TODO: audit
|
||||
'wateringLogs', // TODO: audit
|
||||
'wateringSchedules', // TODO: audit
|
||||
'wetterLocations', // TODO: audit
|
||||
'wetterSettings', // TODO: audit
|
||||
'wishesItems', // TODO: audit
|
||||
'wishesLists', // TODO: audit
|
||||
'wishesPriceChecks', // TODO: audit
|
||||
'workbenchScenes', // TODO: audit
|
||||
];
|
||||
|
|
@ -3,9 +3,11 @@
|
|||
* tables get encrypted.
|
||||
*
|
||||
* Strict allowlist semantics: anything not listed here stays plaintext.
|
||||
* Adding a new module = adding an entry here. Forgetting to add a field
|
||||
* means it ships in plaintext, which is the safer failure mode than the
|
||||
* inverse (a typo'd field name silently failing to decrypt).
|
||||
* Adding a new module = adding an entry here OR an entry in
|
||||
* `plaintext-allowlist.ts` (explicit "this table genuinely holds no
|
||||
* sensitive data"). The `pnpm run check:crypto` audit script enforces
|
||||
* that every Dexie table appears in exactly one of the two — forgetting
|
||||
* a new table now fails CI instead of silently shipping plaintext.
|
||||
*
|
||||
* Why a central registry instead of per-module config?
|
||||
* - One pull request to audit ahead of a release: "what is encrypted?"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue