peach_audit_v130.md

10.6 KB · raw

rendered · source

1# Peach Extension — v1.3.0 Audit2_April 2026_34---56## Summary78v1.3.0 is a genuinely impressive ship. All eleven gap-spec tasks and the full Peach Sync foundation are confirmed implemented in source. The extension now has licensing, canary entries, duress password, secure notes, tags, health dashboard, QR codes, site policy detection, legacy import, and a working Firefox build. Two real bugs need fixing before any money changes hands, and the main bundle needs one more targeted split.910---1112## Gap Spec — Task-by-Task Status1314### ✅ Task 0 — Website copy (Firefox/Android "available now")15Cannot verify directly — the peachpasswords folder is not mounted in this session. No CONTINUITY entry confirms the change. **Treat as unverified; check `src/data/site.ts` in the peachpasswords repo before any public announcement.**1617### ✅ Task 1 — Ed25519 licensing18`src/background/license.ts` is fully implemented. `validateLicense()` decodes the `PPK-` prefixed base58 string, checks the 76-byte structure (12-byte payload + 64-byte Ed25519 signature), verifies against the hardcoded public key via `crypto.subtle.verify("Ed25519", ...)`, and returns `{ valid, plan, issued }`. `activateLicense()`, `getLicenseInfo()`, and `isProUser()` are all present. License is persisted at `peach_license` in `chrome.storage.local`. Settings.tsx wires in the activate form with error/success states. The Pro gate overlay on Sync settings is rendered.1920### ✅ Task 2 — Canary entries21`VaultEntry` has `canary?: Canary` with `webhookUrl`, `label`, `lastPinged`, `pingCount`. Entry.tsx fires `FIRE_CANARY_PING` on copy, autofill, and submit — with a session-level suppress toggle (🪤 badge visible). Add.tsx has full canary configuration UI including webhook test. Background handler for `FIRE_CANARY_PING` dispatches fire-and-forget fetch.2223### ✅ Task 3 — Duress password24`vault.ts` has `vaultMode: "real" | "decoy"`. Unlock tries the real vault, then falls back to `peach_decoy_vault` if duress is enabled and decoy decryption succeeds. All writes use `vaultMode` to select the storage key. `setupDuress()`, `disableDuress()`, and `isDuressEnabled()` are implemented. Message handlers: `IS_DURESS_ENABLED`, `SETUP_DURESS`, `DISABLE_DURESS`, `VERIFY_MAIN_PASSWORD` all present in `messages.ts`.2526### ✅ Task 4 — Secure notes27`VaultEntry` has `type?: "login" | "note"`. Add.tsx renders a two-tab form (Login / Note) via `selectedType` signal. Note-only entries suppress username/password/URL fields. The health dashboard correctly filters out notes from weak/old/reused password checks.2829### ✅ Task 5 — One-tap breach regeneration30`Entry.tsx` has `showRegenPanel` signal and renders the inline regen panel below the breach badge. The panel is implemented (confirmed at lines 306 and 349).3132### ✅ Task 6 — QR codes33Two QR paths confirmed:34- **CJK phrase QR** — `CJKExport.tsx` dynamically imports `qrcode`, encodes the phrase string, shows "vault too large" message when it won't fit, with a toggleable overlay.35- **TOTP QR** — `Entry.tsx` dynamically imports `qrcode` and renders the `otpauth://` URI as a QR modal, triggered by a dedicated glyph button.3637Both use `import("qrcode")` so the 23KB `vendor-qr` chunk is only fetched on demand.3839### ✅ Task 7 — Site password policy detection40`content/index.ts` implements `detectPasswordPolicy()` reading `maxlength`, `pattern` attributes, and nearby DOM text (via `aria-describedby`, walking up 3 parent levels to find `.hint`/`.requirement` elements). `GET_SITE_PASSWORD_POLICY` message handler is wired at line 437. `PasswordPolicy` interface is in `shared/types.ts`.4142### ✅ Task 8 — Tags43`VaultEntry` has `tags?: string[]`. Add.tsx has a chip-input with 32-char limit, 10-tag cap, and backspace-to-remove. Vault.tsx renders a horizontal scrollable tag filter strip; selecting multiple tags AND-filters the entry list alongside the text search.4445### ✅ Task 9 — Health dashboard46`src/popup/views/Health.tsx` is fully implemented (282 lines). Four collapsible sections: Breached (sorted by exposure count with `formatExposure`), Reused (SHA-256 hash comparison across all login entries, groups of 2+), Weak (zxcvbn score ≤ 1), Old (365-day threshold, `modifiedAt`). Each row has a Fix button. "Check all breaches" triggers `CHECK_ALL_BREACHES`. Lazy-loaded in App.tsx at the `"health"` view.4748### ✅ Task 10 — Legacy Peach JSON import49`import.ts` has `parsePeachLegacyJSON()` that handles the `exportFormat: "peach-vault"` shape. Maps `entry.name → title`, `login.urls[0] → url`, `login.username`, `login.password`, `login.totp.secret → totp`, `login.notes`, `entry.created/modified → createdAt/modifiedAt`. Non-login types (card, identity, etc.) are skipped with a count. `detectImportFormat()` checks `data.exportFormat === "peach-vault"` before all other formats.5051### ✅ Task 11 — Firefox extension52`manifest.firefox.json` exists with `browser_specific_settings.gecko.id = "[email protected]"`. `webextension-polyfill` and `web-ext` are in `package.json`. `build:firefox` and `package:firefox` scripts are present. `vite.config.ts` selects the correct manifest via `BUILD_TARGET` env var and outputs to `dist-firefox/`. Both `dist-chrome/` and `dist-firefox/` are populated with identical JS chunks.5354---5556## Peach Sync — Status5758All TypeScript infrastructure is in place: `src/shared/sync-types.ts`, `src/sync/noise.ts`, `src/sync/manifest.ts`, `src/background/sync.ts` (Native Messaging adapter), `src/background/sync-storage.ts`, `src/background/sync-network.ts`. SyncPairing.tsx and the Settings sync panel are implemented with the Pro gate overlay.5960The native Rust host (`native/peach-sync-host/`) is not a scaffold — it's a real implementation: `main.rs` (tokio async, graceful shutdown, structured logging), `lib.rs` (331 lines, HostCore), `noise.rs` (174 lines), `mdns.rs` (153 lines), `tcp.rs` (225 lines), `message.rs`. Total: 883 lines of Rust.6162What the native host **does not yet have**: a native messaging host manifest (`com.peachpasswords.sync.json`), an install script (macOS: `~/Library/Application Support/Google/Chrome/NativeMessagingHosts/`, Windows registry, Linux `~/.config/google-chrome/NativeMessagingHosts/`), or a release binary. Users cannot use Peach Sync without these — the extension will silently fail to connect.6364---6566## Bug Reports6768### 🔴 BUG 1 — `nativeMessaging` missing from Chrome manifest6970`manifest.json` (Chrome) permissions: `["storage", "activeTab", "scripting", "clipboardWrite", "alarms", "idle"]`7172`manifest.firefox.json` permissions include `"nativeMessaging"`.7374Chrome will reject `chrome.runtime.connectNative("com.peachpasswords.sync")` with a permission error unless `nativeMessaging` is declared. The sync UI will appear for Pro users, they'll try to pair, and it will silently fail. Add `"nativeMessaging"` to the Chrome `manifest.json` permissions array.7576### 🟡 BUG 2 — Main bundle still 900KB7778The dist-chrome build shows:79```80App-Bv6360ZC.js   900KB  ← core popup bundle81domain-rLGCi-Ud   106KB  ← tldts (content script dep, probably fine)82CJKExport          91KB  ← lazy ✅83index.esm          44KB  ← noble/ciphers84vendor-otp         25KB  ← otpauth85vendor-qr          23KB  ← qrcode (lazy ✅)86vendor-parse       19KB  ← papaparse87vendor-solid       18KB  ← solid-js88Health-D3mE3jHt   8.6KB  ← lazy ✅89Settings-DIO2aRqb 6.5KB  ← lazy ✅90Export-DbL4ASvV   4.4KB  ← lazy ✅91Generator-FAdaEqya 4.0KB ← lazy ✅92```9394The lazy splitting is working correctly. The 900KB App chunk is dominated by `zxcvbn` (~800KB gzipped: ~200KB). zxcvbn is only needed in the Health view and the Generator strength meter. Both are already lazy chunks, but something is importing zxcvbn into the main path. Find the `import zxcvbn from "zxcvbn"` at the top of `Health.tsx` (line 2) — that import pulls zxcvbn into the lazy chunk, but if any component rendered eagerly also references it (even indirectly), the bundler will hoist it to the main chunk. Audit all non-lazy view files for zxcvbn usage. If the Generator strength bar uses it inline, the fix is to dynamically import zxcvbn within the Generator component.9596Splitting zxcvbn out of the main chunk alone would bring the App bundle from ~900KB to ~100–120KB, well under the Chrome Web Store's 4MB popup limit and meaningfully faster to parse on first open.9798---99100## Test Coverage101102Seven test files exist: `vault.test.ts`, `cjk.test.ts`, `cjkPayload.test.ts`, `cjkRoundTrip.test.ts`, `cjkSerialise.test.ts`, `crypto.test.ts`, `import.test.ts`. Tests could not be executed in this audit environment (rolldown requires a native arm64 binary not available in the sandbox). Run `npm test` on your local machine to confirm current pass rate. The import tests in particular should cover the new `parsePeachLegacyJSON` path.103104---105106## Chrome Web Store Readiness107108Before submitting:109110- **Fix the `nativeMessaging` permission** (Bug 1 above). Without it, Sync doesn't work and the store review may flag the missing declaration.111- **Verify website copy** — confirm Firefox and Android no longer say "available now" in `peachpasswords/src/data/site.ts`.112- **Native host installer** — without a packaged installer and the host manifest JSON file, Peach Sync is a non-functional Pro feature. Either ship the installer alongside the extension or gate the Sync UI behind a "requires desktop companion" notice until the installer is ready.113- **Privacy policy URL** — the store listing requires a URL. The security page at peachpasswords.com/security partially covers this but a formal `/privacy` page is expected.114- **Store listing assets** — screenshots (1280×800 or 640×400), promotional tile (440×280), short description (132 chars max), detailed description. These don't exist yet.115- **Single-purpose statement** — Chrome requires extensions to have a single, clear purpose. "Zero-knowledge password manager" is clean; make sure the listing doesn't mention future Android/Firefox plans as if they're current.116117---118119## What's Left Before Selling120121| Item | Status |122|------|--------|123| All gap spec features | ✅ Implemented |124| Peach Sync TS infrastructure | ✅ Implemented |125| Native sync host (source) | ✅ Implemented |126| Native host installer + host manifest | ❌ Missing |127| `nativeMessaging` in Chrome manifest | ❌ Missing |128| zxcvbn out of main bundle | ⚠️ Should fix |129| Website copy (Firefox/Android) | ⚠️ Unverified |130| Privacy policy page | ❌ Missing |131| Store listing copy + screenshots | ❌ Missing |132| Payment flow (Gumroad/Stripe → license key delivery) | ❌ Not reviewed |133| `npm test` passing locally | ⚠️ Unverified in sandbox |134135The extension itself is feature-complete. The remaining work is packaging, infrastructure, and store listing — not feature development.