a2d17dc903ac
remove mobile webview build, drafts, deepsec, and agent scratch dirs from the repo
- time
- 2026-08-29 04:25 UTC · 1 month ago
- machine
- koh2
- agent
- claude-opus-4
- session
- repo cleanup: mobile removal
- parent
- 55554d95b4be
↓ peach-browser-a2d17dc9.tar.gzatlas at this save
files changed176 files
.amoignore+0 −2.deepsec/.gitignore+0 −9.deepsec/README.md+0 −74.deepsec/data/peach-browser/INFO.md+0 −88.deepsec/data/peach-browser/SETUP.md+0 −55.deepsec/data/peach-browser/config.json+0 −12.deepsec/data/peach-browser/files/scripts/ci.ts.json+0 −20.deepsec/data/peach-browser/files/scripts/generate-kat.mjs.json+0 −22.deepsec/data/peach-browser/files/scripts/release-firefox.ts.json+0 −78.deepsec/data/peach-browser/files/scripts/release.ts.json+0 −44.deepsec/data/peach-browser/files/src/background/aes.ts.json+0 −49.deepsec/data/peach-browser/files/src/background/chirp-identity.ts.json+0 −54.deepsec/data/peach-browser/files/src/background/chirp-sync-coordinator.ts.json+0 −54.deepsec/data/peach-browser/files/src/background/chirp-sync-vault-adapter.ts.json+0 −64.deepsec/data/peach-browser/files/src/background/clipboard.ts.json+0 −73.deepsec/data/peach-browser/files/src/background/codex-handlers.ts.json+0 −49.deepsec/data/peach-browser/files/src/background/desktop-pairing-bg.ts.json+0 −101.deepsec/data/peach-browser/files/src/background/desktop-pairing-mdns.ts.json+0 −49.deepsec/data/peach-browser/files/src/background/desktop-pairing-probe.ts.json+0 −46.deepsec/data/peach-browser/files/src/background/desktop-pairing.ts.json+0 −92.deepsec/data/peach-browser/files/src/background/health.ts.json+0 −46.deepsec/data/peach-browser/files/src/background/license.ts.json+0 −46.deepsec/data/peach-browser/files/src/background/messages.ts.json+0 −237.deepsec/data/peach-browser/files/src/background/offscreen.ts.json+0 −46.deepsec/data/peach-browser/files/src/background/passkey-crypto.ts.json+0 −70.deepsec/data/peach-browser/files/src/background/passkey.ts.json+0 −119.deepsec/data/peach-browser/files/src/background/recovery-pending.ts.json+0 −46.deepsec/data/peach-browser/files/src/background/self-test.ts.json+0 −49.deepsec/data/peach-browser/files/src/background/sync-alerts.ts.json+0 −66.deepsec/data/peach-browser/files/src/background/sync-trigger.ts.json+0 −46.deepsec/data/peach-browser/files/src/background/vault.ts.json+0 −144.deepsec/data/peach-browser/files/src/content/content-ml-detector.ts.json+0 −51.deepsec/data/peach-browser/files/src/content/detect.ts.json+0 −67.deepsec/data/peach-browser/files/src/content/fill.ts.json+0 −46.deepsec/data/peach-browser/files/src/content/identity-helper.ts.json+0 −47.deepsec/data/peach-browser/files/src/content/index.ts.json+0 −87.deepsec/data/peach-browser/files/src/content/inject.ts.json+0 −391.deepsec/data/peach-browser/files/src/content/passkeys-firefox-relay.ts.json+0 −77.deepsec/data/peach-browser/files/src/content/passkeys-firefox.ts.json+0 −70.deepsec/data/peach-browser/files/src/content/policy.ts.json+0 −62.deepsec/data/peach-browser/files/src/content/save.ts.json+0 −57.deepsec/data/peach-browser/files/src/crypto/peachPayload.ts.json+0 −47.deepsec/data/peach-browser/files/src/crypto/peachScript.ts.json+0 −20.deepsec/data/peach-browser/files/src/crypto/totp.ts.json+0 −20.deepsec/data/peach-browser/files/src/popup/App.tsx.json+0 −111.deepsec/data/peach-browser/files/src/popup/chirp-pair.ts.json+0 −29.deepsec/data/peach-browser/files/src/popup/codex/aztec.ts.json+0 −20.deepsec/data/peach-browser/files/src/popup/codex/erasure.ts.json+0 −32.deepsec/data/peach-browser/files/src/popup/codex/generate.ts.json+0 −23.deepsec/data/peach-browser/files/src/popup/codex/helpers.ts.json+0 −31.deepsec/data/peach-browser/files/src/popup/codex/node-wasm.ts.json+0 −20.deepsec/data/peach-browser/files/src/popup/codex/pdf-import.ts.json+0 −44.deepsec/data/peach-browser/files/src/popup/codex/pdf-layout.ts.json+0 −24.deepsec/data/peach-browser/files/src/popup/codex/pdf.ts.json+0 −32.deepsec/data/peach-browser/files/src/popup/codex/restore.ts.json+0 −21.deepsec/data/peach-browser/files/src/popup/codex/rs-loader.ts.json+0 −20.deepsec/data/peach-browser/files/src/popup/components/AdoptionConsentModal.tsx.json+0 −46.deepsec/data/peach-browser/files/src/popup/components/ChirpPairingFlow.tsx.json+0 −76.deepsec/data/peach-browser/files/src/popup/components/GeneratorDrawer.tsx.json+0 −101.deepsec/data/peach-browser/files/src/popup/components/NoteToolbar.tsx.json+0 −117.deepsec/data/peach-browser/files/src/popup/components/RuneCanvas.tsx.json+0 −46.deepsec/data/peach-browser/files/src/popup/firefox-passkey.ts.json+0 −28.deepsec/data/peach-browser/files/src/popup/views/Add.tsx.json+0 −74.deepsec/data/peach-browser/files/src/popup/views/AdoptVaultView.tsx.json+0 −46.deepsec/data/peach-browser/files/src/popup/views/ChirpSync.tsx.json+0 −176.deepsec/data/peach-browser/files/src/popup/views/CodexGenerateView.tsx.json+0 −26.deepsec/data/peach-browser/files/src/popup/views/CodexRestoreView.tsx.json+0 −31.deepsec/data/peach-browser/files/src/popup/views/Entry.tsx.json+0 −82.deepsec/data/peach-browser/files/src/popup/views/Export.tsx.json+0 −21.deepsec/data/peach-browser/files/src/popup/views/Health.tsx.json+0 −20.deepsec/data/peach-browser/files/src/popup/views/Import.tsx.json+0 −95.deepsec/data/peach-browser/files/src/popup/views/Lock.tsx.json+0 −122.deepsec/data/peach-browser/files/src/popup/views/NoteEditor.tsx.json+0 −91.deepsec/data/peach-browser/files/src/popup/views/NotesEditor.tsx.json+0 −90.deepsec/data/peach-browser/files/src/popup/views/NotesMode.tsx.json+0 −21.deepsec/data/peach-browser/files/src/popup/views/NotesTOC.tsx.json+0 −21.deepsec/data/peach-browser/files/src/popup/views/Onboarding.tsx.json+0 −20.deepsec/data/peach-browser/files/src/popup/views/Panic.tsx.json+0 −42.deepsec/data/peach-browser/files/src/popup/views/RecoveryWordDisplay.tsx.json+0 −21.deepsec/data/peach-browser/files/src/popup/views/Settings.tsx.json+0 −127.deepsec/data/peach-browser/files/src/popup/views/Vault.tsx.json+0 −72.deepsec/data/peach-browser/files/src/popup/views/vault-search.ts.json+0 −20.deepsec/data/peach-browser/files/src/shared/backup.ts.json+0 −29.deepsec/data/peach-browser/files/src/shared/chirp-adopt.ts.json+0 −20.deepsec/data/peach-browser/files/src/shared/chirp-handshake.ts.json+0 −26.deepsec/data/peach-browser/files/src/shared/chirp-pairing.ts.json+0 −22.deepsec/data/peach-browser/files/src/shared/chirp-payload.ts.json+0 −20.deepsec/data/peach-browser/files/src/shared/chirp-sync-transport.ts.json+0 −28.deepsec/data/peach-browser/files/src/shared/codex-types.ts.json+0 −34.deepsec/data/peach-browser/files/src/shared/config.ts.json+0 −20.deepsec/data/peach-browser/files/src/shared/device-manifest.ts.json+0 −28.deepsec/data/peach-browser/files/src/shared/domain.ts.json+0 −20.deepsec/data/peach-browser/files/src/shared/generate.ts.json+0 −24.deepsec/data/peach-browser/files/src/shared/import.ts.json+0 −23.deepsec/data/peach-browser/files/src/shared/local-network.ts.json+0 −20.deepsec/data/peach-browser/files/src/shared/native.ts.json+0 −20.deepsec/data/peach-browser/files/src/shared/noise-js.ts.json+0 −25.deepsec/data/peach-browser/files/src/shared/passkey-utils.ts.json+0 −20.deepsec/data/peach-browser/files/src/shared/recovery-word-list.ts.json+0 −22.deepsec/data/peach-browser/files/src/shared/recovery-words.ts.json+0 −33.deepsec/data/peach-browser/files/src/shared/shortcuts.ts.json+0 −25.deepsec/data/peach-browser/files/src/shared/sync-identity.ts.json+0 −32.deepsec/data/peach-browser/files/src/shared/sync-types.ts.json+0 −26.deepsec/data/peach-browser/files/src/shared/types.ts.json+0 −39.deepsec/data/peach-browser/files/vite.config.mobile.ts.json+0 −20.deepsec/data/peach-browser/files/vite.config.ts.json+0 −47.deepsec/data/peach-browser/project.json+0 −5.deepsec/data/peach-browser/runs/20260630213317-2fd9f00652156d33.json+0 −93.deepsec/data/peach-browser/runs/20260630213408-3897a42f3fa0e1ed.json+0 −27.deepsec/data/peach-browser/runs/20260630220437-80eef1872fbf5736.json+0 −93.deepsec/data/peach-browser/runs/20260630220521-d9fdba12b7a39539.json+0 −20.deepsec/data/peach-browser/runs/20260630220859-de7f3b7bf11d1260.json+0 −93.deepsec/data/peach-browser/runs/20260630220943-c7b8dba65193b129.json+0 −93.deepsec/data/peach-browser/runs/20260630221012-35253a56b27eeb43.json+0 −20.deepsec/data/peach-browser/runs/20260701075706-0feae092f70d6cf5.json+0 −27.deepsec/data/peach-browser/tech.json+0 −12.deepsec/deepsec.config.ts+0 −8.deepsec/package.json+0 −12.deepsec/pnpm-lock.yaml+0 −2215.deepsec/pnpm-workspace.yaml+0 −1README.md+1 −1biome.json+0 −3manifest.firefox.json+1 −1manifest.json+1 −1package.json+3 −4scripts/ci.ts+0 −4scripts/release-firefox.ts+0 −3scripts/release.ts+1 −4src/content/content.orchestration.test.ts+1 −1src/drafts/AUTOFILL-TESTS/INTEGRATION-CONTRACT.md+0 −136src/drafts/PCH-02-FIXES/INTEGRATION-CONTRACT.md+0 −100src/drafts/PCH-02-FIXES/PCH-02-001-vault-patch-sanitizers.patch.md+0 −124src/drafts/PCH-02-FIXES/PCH-02-003-sas-trust-model-comment.patch.md+0 −73src/drafts/PCH-02-FIXES/PCH-02-004-exact-pin-deps.patch.md+0 −84src/drafts/PCH-02-FIXES/PCH-02-005-legacy-pbkdf2-doc.patch.md+0 −118src/drafts/PCH-02-FIXES/PCH-02-007-hibp-add-padding.patch.md+0 −45src/drafts/PCH-02-FIXES/PCH-02-008-binaryreader-bounds.patch.md+0 −123src/drafts/PCH-02-FIXES/PCH-02-010-clipboard-hash-match.patch.md+0 −253src/drafts/PCH-02-FIXES/README.md+0 −27src/drafts/PCH-02-FIXES/WASM-PROVENANCE.md+0 −57src/drafts/PCH-02-FIXES/clipboard.hash-match.test.ts+0 −148src/drafts/PCH-02-FIXES/peachPayload.binaryreader-bounds.test.ts+0 −74src/drafts/PCH-02-FIXES/vault.hibp-padding.test.ts+0 −85src/drafts/PCH-02-FIXES/vault.update-entry-sanitize.test.ts+0 −213src/drafts/TEST-EXPANSION/INTEGRATION-CONTRACT.md+0 −174src/drafts/TEST-EXPANSION/argon2id.kat.test.ts+0 −140src/drafts/TEST-EXPANSION/backup.nfc-fallback.test.ts+0 −130src/drafts/TEST-EXPANSION/clipboard.double-failure.test.ts+0 −170src/drafts/TEST-EXPANSION/codex.multistripe.test.ts+0 −183src/drafts/TEST-EXPANSION/content.orchestration.test.ts+0 −175src/drafts/TEST-EXPANSION/generate.empty-pool.test.ts+0 −130src/drafts/TEST-EXPANSION/import.csv.formulainjection.test.ts+0 −138src/drafts/TEST-EXPANSION/license.verification.test.ts+0 −245src/drafts/TEST-EXPANSION/messages.batch-sanitize.test.ts+0 −198src/drafts/TEST-EXPANSION/messages.edit-origin.test.ts+0 −230src/drafts/TEST-EXPANSION/ml.predictbatch.test.ts+0 −185src/drafts/TEST-EXPANSION/passkey.ed25519.test.ts+0 −230src/drafts/TEST-EXPANSION/pdf-import.sandbox.test.ts+0 −111src/drafts/TEST-EXPANSION/peachPayload.bomb.test.ts+0 −257src/drafts/TEST-EXPANSION/recovery-words.checksum.test.ts+0 −105src/drafts/TEST-EXPANSION/recovery-words.integrity.test.ts+0 −57src/drafts/TEST-EXPANSION/recovery-words.kek.test.ts+0 −140src/drafts/TEST-EXPANSION/sync.disconnect.test.ts+0 −236src/drafts/TEST-EXPANSION/totp.rfc6238.test.ts+0 −137src/drafts/TEST-EXPANSION/vault.consolidate-passkeys.test.ts+0 −231src/drafts/TEST-EXPANSION/vault.corruption.test.ts+0 −209src/drafts/TEST-EXPANSION/vault.purge-trash.test.ts+0 −238src/drafts/TEST-EXPANSION/vault.rotate-sync-identity.test.ts+0 −223src/mobile/chrome-shim.test.ts+0 −137src/mobile/chrome-shim.ts+0 −463src/mobile/entry.ts+0 −64src/mobile/index.html+0 −51src/mobile/mobile.css+0 −24tsconfig.json+1 −2vite.config.mobile.ts+0 −41vitest.config.ts+0 −1.amoignore+0 −2
@@ -13,8 +13,6 @@1313 dist-chrome/1414 dist-firefox1515 dist-firefox/16-dist-mobile17-dist-mobile/1816 1917 # Koh internal version control state2018 .koh.deepsec/.gitignore+0 −9
@@ -1,9 +0,0 @@1-node_modules/2-.env*.local3-4-# Scan output — regenerated by `deepsec scan` / `process`. INFO.md5-# and SETUP.md (manually edited) stay tracked.6-data/*/files/7-data/*/runs/8-data/*/reports/9-data/*/project.json.deepsec/README.md+0 −74
@@ -1,74 +0,0 @@1-# deepsec2-3-This directory holds the [deepsec](https://www.npmjs.com/package/deepsec)4-config for the parent repo. Checked into git so teammates inherit5-project context (auth shape, threat model, custom matchers); generated6-scan output is gitignored.7-8-Currently configured project: `peach-browser` (target: `..`).9-10-## Setup11-12-1. `pnpm install` — installs deepsec.13-2. Add an AI Gateway / Anthropic / OpenAI token to `.env.local`. If14- you already have `claude` or `codex` CLI logged in on this15- machine, you can skip the token for non-sandbox runs (`process` /16- `revalidate` / `triage`); deepsec auto-detects and reuses the17- subscription. See18- `node_modules/deepsec/dist/docs/vercel-setup.md` after install.19-3. Open the parent repo in your coding agent (Claude Code, Cursor, …)20- and have it follow `data/peach-browser/SETUP.md` to fill in21- `data/peach-browser/INFO.md`.22-23-## Daily commands24-25-```bash26-pnpm deepsec scan27-pnpm deepsec process --concurrency 528-pnpm deepsec revalidate --concurrency 5 # cuts FP rate29-pnpm deepsec export --format md-dir --out ./findings30-```31-32-`--project-id` is auto-resolved while there's only one project in33-`deepsec.config.ts`. Once you've added a second project, pass34-`--project-id peach-browser` (or whichever id you want) explicitly.35-36-`scan` is free (regex only). `process` is the AI stage (≈$0.30/file37-on Opus by default). Run state goes to `data/peach-browser/`.38-39-## Adding another project40-41-To scan another codebase from this same `.deepsec/`:42-43-```bash44-pnpm deepsec init-project ../some-other-package # path relative to .deepsec/45-```46-47-Appends an entry to `deepsec.config.ts` and writes48-`data/<id>/{INFO.md,SETUP.md,project.json}`. Open the new SETUP.md49-in your agent to fill in INFO.md.50-51-## Layout52-53-```54-deepsec.config.ts Project list (one entry per scanned repo)55-data/peach-browser/56- INFO.md Repo context — checked into git, hand-curated57- SETUP.md Agent setup prompt — checked in, deletable58- project.json Generated (gitignored)59- files/ One JSON per scanned source file (gitignored)60- runs/ Run metadata (gitignored)61- reports/ Generated markdown reports (gitignored)62-AGENTS.md Pointer for coding agents63-.env.local Tokens (gitignored)64-```65-66-## Docs67-68-After `pnpm install`:69-70-- Skill: `node_modules/deepsec/SKILL.md`71-- Full docs: `node_modules/deepsec/dist/docs/{getting-started,configuration,models,writing-matchers,plugins,architecture,data-layout,vercel-setup,faq}.md`72-73-Or browse on74-[GitHub](https://github.com/vercel/deepsec/tree/main/docs)..deepsec/data/peach-browser/INFO.md+0 −88
@@ -1,88 +0,0 @@1-# peach-browser2-3-Repo context injected into every DeepSec batch. Keep findings focused on4-Peach-specific trust boundaries and invariants; built-in matchers cover5-generic web/app vulnerability classes.6-7-## What This Codebase Does8-9-Peach is a local-first, zero-knowledge password manager shipped as a browser10-extension and Capacitor web build. It has no server-side account model: the11-high-value assets are the local encrypted vault, recovered/adopted root key,12-paired-device identities, passkeys, TOTP secrets, and signed Pro license blob.13-14-Representative surfaces:15-- Background service worker: `entry.ts` starts synchronous MV3 listeners and16- `messages.ts` is the only background `runtime.onMessage` dispatcher.17-- Vault owner: `vault.ts` is the only module that should read/write the vault18- storage keys; popup/content reach it only through messages.19-- Page-facing content: `content/index.ts` and `inject.ts` run on `<all_urls>`20- and render autofill/passkey UI into arbitrary host pages.21-- Sync/pairing: chirp, desktop helper, and vault adoption exchange identities22- over Noise channels plus user-confirmed SAS.23-- Build targets differ: Chrome uses MV3 service worker/offscreen/mdns; Firefox24- has relay/proxy content scripts and no Chrome offscreen document.25-26-## Auth And Trust Shape27-28-- Unlock is the real vault authority. Most sensitive handlers first require29- `vault.isUnlocked()`; `restoreReady` gates cold-start reads so MV3 worker30- startup cannot race unlock state.31-- Pro is a signed local capability, not a server session. `validateLicense`,32- `activateLicense`, `isProUser`, and `requirePro(feature)` are the relevant33- primitives; sync pairing is deliberately ungated, while features such as34- Codex, canary creation, passkeys, duress, and advanced health still gate.35-- Page origin trust comes from `attestedSenderOrigin(sender)`, not payload data.36- Page-driven passkey/autofill messages arrive through content scripts; payload37- origins, rpIds, domains, and page strings are attacker-controlled until38- checked.39-- Destructive messages are a special boundary: `DESTRUCTIVE_MESSAGE_TYPES`40- must come from an extension page URL, not just an extension content script.41-- Pairing trust depends on SAS confirmation before persistence. Look for42- `derivePairingSAS`, `gateOnSAS`, `runPairingInitiatorOpenChannel`,43- `runPairingResponder`, and desktop `DESKTOP_PAIR_CONFIRM`.44-45-## Crypto And Storage Invariants46-47-- Vault slots are sealed with `encrypt`/`decrypt` in `aes.ts`; current callers48- should pass slot AAD such as `"slot_a_v1"`. The legacy untagged decrypt49- fallback is intentional compatibility only, not a pattern to copy.50-- `deriveKey` is the normal Argon2id path with password normalization.51- `deriveKeyRaw` is a legacy fallback path only.52-- Root-key adoption uses `CREATE_VAULT_FROM_ROOT_KEY` and ADOPT frames. Root53- key hex must be exact 32-byte material and vault-id mismatches must fail54- before local vault creation.55-- `computeVaultId` and sync identity helpers derive LAN-linkable vault/device56- identifiers from local key material. Treat them as pseudonymous identifiers,57- not public account IDs.58-- `zeroKey` and byte-array clearing matter for root/master key handling; avoid59- findings that suggest logging or retaining `root_key_hex`, rootKeyHex, or60- derived key bytes.61-62-## Project-Specific Patterns To Flag63-64-- New background handlers outside `messages.ts`, or direct popup/content65- imports of `vault.ts`, bypass the intended message boundary.66-- Any production callsite using test-only seams (`_testSeam`,67- `__PEACH_E2E_PASSKEYS__`, passkey bypass flags) is a real regression.68-- Raw external hex should not feed X25519, HKDF, vault adoption, or69- `computeVaultId` without strict 64-lowercase-hex / 32-byte validation.70-- Content-injected DOM should use `textContent`, controlled SVG strings, and71- closed shadow roots where intended. Page-controlled strings interpolated into72- templates are high signal.73-- The legacy bare `"popup"` lock port in `entry.ts` is compatibility-only; new74- lock-on-close logic should use nonce-like `popup-<uuid>` ports.75-76-## Expected False Positives77-78-- Tests contain KATs, malformed frames, fuzz payloads, and adversarial message79- calls by design. Treat `*.test.ts`, `*.spec.ts`, and adversarial suites as80- fixtures unless a test-only bypass leaks into production code.81-- `src/drafts/**` is integration draft material and is excluded from normal82- typecheck/test gates; it is not shipped source.83-- Generated/browser artifacts such as `dist-*`, `artifacts`, and `node_modules`84- are not source-of-truth inputs.85-- `offscreen.html` is the extension-internal clipboard-clear document, not a86- public web route.87-- HIBP uses SHA-1 prefixes because the public k-anonymity API requires it; the88- project-specific invariant is use of response padding and suffix-only match..deepsec/data/peach-browser/SETUP.md+0 −55
@@ -1,55 +0,0 @@1-# Agent setup for `peach-browser`2-3-This is a deepsec scanning workspace. Project `peach-browser` was just registered4-(target: `..`). Setup is incomplete — `data/peach-browser/INFO.md`5-still has placeholder sections.6-7-## What to do8-9-1. **Read the deepsec skill.** After `pnpm install`, the file is at10- `node_modules/deepsec/SKILL.md`. It maps every doc topic to a file11- under `node_modules/deepsec/dist/docs/`. Read `getting-started.md`,12- `configuration.md`, and `writing-matchers.md` (skim the rest).13-14-2. **Fill in `data/peach-browser/INFO.md`.** It's auto-injected into the AI15- prompt for every batch — keep it short and selective.16-17- **Length budget: 50–100 lines total.** Verbose context dilutes18- signal in the scanner's prompt window. The goal is "what would a19- reviewer miss if they didn't read this?", not exhaustive enumeration.20-21- **Per-section rubric**:22- - Pick 3–5 representative items per section. **Don't list every23- file, helper, or callsite** — pick the patterns.24- - Name primitives by their public name (e.g. `withAuthentication`,25- `auth.can()`, `isTeamAdmin`). **No line numbers.** Don't enumerate26- more than 5 paths in any list.27- - Skip generic CWE categories — built-in matchers already cover28- "SSRF", "SQL injection", "XSS". Cover what's *project-specific*:29- internal auth helpers, custom middleware names, fork-specific30- stubs, intended-public endpoints.31- - One short paragraph or 3–5 short bullets per section. Not both.32-33- Source material (read in this order, stop when you have enough):34- - `../README.md`35- - any `AGENTS.md` / `CLAUDE.md` in `..`36- - `../package.json` (or `go.mod`, `pyproject.toml`, etc.)37- - 5–10 representative code files (entry points, auth helpers) — not38- a full code tour.39-40-3. **(Optional) Add custom matchers** for repo-specific patterns the41- built-in matchers won't catch. Read42- `node_modules/deepsec/dist/docs/writing-matchers.md` first; the43- workflow there starts from a confirmed finding and grows the matcher44- from it. Don't add matchers speculatively — wait for a real TP.45-46-## When you're done47-48-The user will run:49-50-```bash51-pnpm deepsec scan --project-id peach-browser52-pnpm deepsec process --project-id peach-browser53-```54-55-You can delete this file once setup is complete..deepsec/data/peach-browser/config.json+0 −12
@@ -1,12 +0,0 @@1-{2- "ignorePaths": [3- "dist*/**",4- "public/ggwave/**",5- "artifacts/**",6- "node_modules/**",7- ".deepsec/node_modules/**",8- ".deepsec/data/**/files/dist*/**",9- ".deepsec/data/**/runs/**",10- ".koh/**"11- ]12-}.deepsec/data/peach-browser/files/scripts/ci.ts.json+0 −20
@@ -1,20 +0,0 @@1-{2- "filePath": "scripts/ci.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "rce",7- "lineNumbers": [8- 549- ],10- "snippet": "function run(step: Step): { ok: boolean; output: string; duration: number } {\n const start = Date.now();\n const result = spawnSync(step.cmd, {\n cwd: step.cwd ?? process.cwd(),\n env: process.env,",11- "matchedPattern": "spawnSync"12- }13- ],14- "lastScannedAt": "2026-06-30T22:09:43.939Z",15- "lastScannedRunId": "20260630220943-c7b8dba65193b129",16- "fileHash": "7fd2f531b7d3c6ab480a30a83fc286fe92e1920855b197d8f0ff18646c16cef3",17- "findings": [],18- "analysisHistory": [],19- "status": "error"20-}.deepsec/data/peach-browser/files/scripts/generate-kat.mjs.json+0 −22
@@ -1,22 +0,0 @@1-{2- "filePath": "scripts/generate-kat.mjs",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "crypto-usage",7- "lineNumbers": [8- 15,9- 22,10- 2911- ],12- "snippet": "const NONCE = Buffer.from(\"6b65792d6e6f6e63652d3031\", \"hex\");\n\nconst km = await crypto.subtle.importKey(\n \"raw\",\n new TextEncoder().encode(KAT_PASSWORD),",13- "matchedPattern": "Web Crypto API (crypto.subtle.*)"14- }15- ],16- "lastScannedAt": "2026-06-30T22:09:44.272Z",17- "lastScannedRunId": "20260630220943-c7b8dba65193b129",18- "fileHash": "fe6051968e82d6d89f32d953c70d9684fb3e5fdc624d738bf50d64e97fcaedc1",19- "findings": [],20- "analysisHistory": [],21- "status": "error"22-}.deepsec/data/peach-browser/files/scripts/release-firefox.ts.json+0 −78
@@ -1,78 +0,0 @@1-{2- "filePath": "scripts/release-firefox.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "rce",7- "lineNumbers": [8- 159- ],10- "snippet": " const display = label ?? cmd;\n console.log(`\\n▶ ${display}`);\n execSync(cmd, { stdio: \"inherit\", cwd: ROOT });\n}\n",11- "matchedPattern": "execSync"12- },13- {14- "vulnSlug": "rce",15- "lineNumbers": [16- 617- ],18- "snippet": "// Usage: bun run scripts/release-firefox.ts\n\nimport { execSync } from \"child_process\";\nimport { readFileSync, existsSync, mkdirSync } from \"fs\";\nimport { join } from \"path\";",19- "matchedPattern": "child_process import"20- },21- {22- "vulnSlug": "secret-env-var",23- "lineNumbers": [24- 28,25- 9326- ],27- "snippet": " fail(\"AMO_JWT_ISSUER not set. Add it to your shell profile and reload.\");\n}\nif (!process.env.AMO_JWT_SECRET) {\n fail(\"AMO_JWT_SECRET not set. Add it to your shell profile and reload.\");\n}",28- "matchedPattern": "Secret env var access"29- },30- {31- "vulnSlug": "env-var-as-bool",32- "lineNumbers": [33- 28,34- 9335- ],36- "snippet": " fail(\"AMO_JWT_ISSUER not set. Add it to your shell profile and reload.\");\n}\nif (!process.env.AMO_JWT_SECRET) {\n fail(\"AMO_JWT_SECRET not set. Add it to your shell profile and reload.\");\n}",37- "matchedPattern": "Secret env var used as boolean"38- },39- {40- "vulnSlug": "process-env-access",41- "lineNumbers": [42- 2543- ],44- "snippet": "\nif (!process.env.AMO_JWT_ISSUER) {\n fail(\"AMO_JWT_ISSUER not set. Add it to your shell profile and reload.\");",45- "matchedPattern": "process.env.AMO_JWT_ISSUER"46- },47- {48- "vulnSlug": "process-env-access",49- "lineNumbers": [50- 2851- ],52- "snippet": "}\nif (!process.env.AMO_JWT_SECRET) {\n fail(\"AMO_JWT_SECRET not set. Add it to your shell profile and reload.\");",53- "matchedPattern": "process.env.AMO_JWT_SECRET"54- },55- {56- "vulnSlug": "process-env-access",57- "lineNumbers": [58- 9259- ],60- "snippet": " \"--artifacts-dir artifacts\",\n `--api-key=${process.env.AMO_JWT_ISSUER}`,\n `--api-secret=${process.env.AMO_JWT_SECRET}`,",61- "matchedPattern": "process.env.AMO_JWT_ISSUER"62- },63- {64- "vulnSlug": "process-env-access",65- "lineNumbers": [66- 9367- ],68- "snippet": " `--api-key=${process.env.AMO_JWT_ISSUER}`,\n `--api-secret=${process.env.AMO_JWT_SECRET}`,\n \"--channel=listed\",",69- "matchedPattern": "process.env.AMO_JWT_SECRET"70- }71- ],72- "lastScannedAt": "2026-06-30T22:09:44.206Z",73- "lastScannedRunId": "20260630220943-c7b8dba65193b129",74- "fileHash": "dce382f86921d3b8b75cc10fbf1cd1ad834772a8cb340113d447c6247c8e3ebb",75- "findings": [],76- "analysisHistory": [],77- "status": "error"78-}.deepsec/data/peach-browser/files/scripts/release.ts.json+0 −44
@@ -1,44 +0,0 @@1-{2- "filePath": "scripts/release.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "rce",7- "lineNumbers": [8- 279- ],10- "snippet": "function run(cmd: string, label?: string) {\n console.log(`\\n${ACCENT}▶${RESET} ${label ?? cmd}`);\n execSync(cmd, { stdio: \"inherit\", cwd: ROOT });\n}\n",11- "matchedPattern": "execSync"12- },13- {14- "vulnSlug": "rce",15- "lineNumbers": [16- 1117- ],18- "snippet": "// AMO_JWT_ISSUER, AMO_JWT_SECRET (not needed for --chrome-only)\n\nimport { execSync } from \"child_process\";\nimport { readFileSync, existsSync, mkdirSync, rmSync } from \"fs\";\nimport { join } from \"path\";",19- "matchedPattern": "child_process import"20- },21- {22- "vulnSlug": "secrets-exposure",23- "lineNumbers": [24- 16525- ],26- "snippet": " \"--artifacts-dir artifacts\",\n `--api-key=\"${AMO_JWT_ISSUER}\"`,\n `--api-secret=\"${AMO_JWT_SECRET}\"`,\n \"--channel=listed\",\n ].join(\" \"),",27- "matchedPattern": "hardcoded credential"28- },29- {30- "vulnSlug": "insecure-crypto",31- "lineNumbers": [32- 1633- ],34- "snippet": "\nconst ROOT = process.cwd();\nconst CHROME_ONLY = process.argv.includes(\"--chrome-only\");\n\nconst BOLD = \"\\x1b[1m\";",35- "matchedPattern": "weak cipher algorithm"36- }37- ],38- "lastScannedAt": "2026-06-30T22:09:44.011Z",39- "lastScannedRunId": "20260630220943-c7b8dba65193b129",40- "fileHash": "274d70627ded7023959b4f5f2a447923dbb8db2ba332141a6186698fb5ab7403",41- "findings": [],42- "analysisHistory": [],43- "status": "error"44-}.deepsec/data/peach-browser/files/src/background/aes.ts.json+0 −49
@@ -1,49 +0,0 @@1-{2- "filePath": "src/background/aes.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "crypto-usage",7- "lineNumbers": [8- 43,9- 59,10- 90,11- 10512- ],13- "snippet": " aadTag?: string\n): Promise<{ nonce: string; ciphertext: string; tag: string }> {\n const key = await crypto.subtle.importKey(\n \"raw\",\n keyBytes.buffer as ArrayBuffer,",14- "matchedPattern": "Web Crypto API (crypto.subtle.*)"15- }16- ],17- "lastScannedAt": "2026-06-30T22:09:44.300Z",18- "lastScannedRunId": "20260630220943-c7b8dba65193b129",19- "fileHash": "309726b64bb0d51657e2a2bc24c10ebe373d375d896d2e0fbe7c943780708499",20- "findings": [],21- "analysisHistory": [22- {23- "runId": "20260701075706-0feae092f70d6cf5",24- "investigatedAt": "2026-07-01T08:03:56.992Z",25- "durationMs": 81450.2,26- "agentType": "codex",27- "model": "gpt-5.5",28- "modelConfig": {29- "model": "gpt-5.5"30- },31- "agentSessionId": "019f1cae-8382-71e3-8ad3-dbb6109ffe96",32- "findingCount": 0,33- "numTurns": 0.2,34- "phase": "process",35- "costUsd": 0.44014300000000006,36- "usage": {37- "inputTokens": 31713.8,38- "outputTokens": 3856.2,39- "cacheReadInputTokens": 331776,40- "cacheCreationInputTokens": 041- }42- }43- ],44- "status": "analyzed",45- "gitInfo": {46- "recentCommitters": [],47- "enrichedAt": "2026-07-01T08:03:57.001Z"48- }49-}.deepsec/data/peach-browser/files/src/background/chirp-identity.ts.json+0 −54
@@ -1,54 +0,0 @@1-{2- "filePath": "src/background/chirp-identity.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 99- ],10- "snippet": "// migration moves any legacy value out of `chrome.storage.sync`.\n//\n// `deviceName` defaults to a short, friendly browser+OS description and\n// can be overridden by the user from Settings → Sync.\n//",11- "matchedPattern": "weak cipher algorithm"12- },13- {14- "vulnSlug": "crypto-usage",15- "lineNumbers": [16- 13517- ],18- "snippet": " // a runtime is missing it (effectively unreachable for our targets).\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n const bytes = new Uint8Array(16);",19- "matchedPattern": "Node randomUUID"20- }21- ],22- "lastScannedAt": "2026-06-30T22:09:44.300Z",23- "lastScannedRunId": "20260630220943-c7b8dba65193b129",24- "fileHash": "c4ebd4894d47ac62db12e0dc34c991989ea7a414dbcfa61de843d976bd2d8e2c",25- "findings": [],26- "analysisHistory": [27- {28- "runId": "20260701075706-0feae092f70d6cf5",29- "investigatedAt": "2026-07-01T08:02:47.753Z",30- "durationMs": 67618.4,31- "agentType": "codex",32- "model": "gpt-5.5",33- "modelConfig": {34- "model": "gpt-5.5"35- },36- "agentSessionId": "019f1cae-8389-7013-b8fd-a575c0019e5c",37- "findingCount": 0,38- "numTurns": 0.2,39- "phase": "process",40- "costUsd": 0.4825014,41- "usage": {42- "inputTokens": 45683.4,43- "outputTokens": 3201,44- "cacheReadInputTokens": 316108.8,45- "cacheCreationInputTokens": 046- }47- }48- ],49- "status": "analyzed",50- "gitInfo": {51- "recentCommitters": [],52- "enrichedAt": "2026-07-01T08:02:47.760Z"53- }54-}.deepsec/data/peach-browser/files/src/background/chirp-sync-coordinator.ts.json+0 −54
@@ -1,54 +0,0 @@1-{2- "filePath": "src/background/chirp-sync-coordinator.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 549- ],10- "snippet": " /** Returns base64 ciphertext for each requested entry id. */\n getEncryptedEntries(entryIds: string[]): Promise<Record<string, string>>;\n /** Apply incoming encrypted entries; vault decides per-entry overwrite. */\n applyEntries(entries: Record<string, string>): Promise<void>;\n /** Apply incoming tombstones; vault retains the latest deletion ts. */",11- "matchedPattern": "weak cipher algorithm"12- },13- {14- "vulnSlug": "secrets-plaintext-exposure",15- "lineNumbers": [16- 27717- ],18- "snippet": " if (timer.signal.aborted) reject(new Error(\"chirp_sync_timeout\"));\n timer.signal.addEventListener(\"abort\", () => reject(new Error(\"chirp_sync_timeout\")));\n });\n const bytes = await Promise.race([pending, aborted]);\n // MV-3: decrypted plaintext can exceed the wire frame (and a non-WS",19- "matchedPattern": "plaintext reference inside logger/response/error/span call"20- }21- ],22- "lastScannedAt": "2026-06-30T22:09:44.319Z",23- "lastScannedRunId": "20260630220943-c7b8dba65193b129",24- "fileHash": "9f699247a730d180bb9d2b7f2d61c11959dad272ada2cf8c4bcca68a9ba560ba",25- "findings": [],26- "analysisHistory": [27- {28- "runId": "20260701075706-0feae092f70d6cf5",29- "investigatedAt": "2026-07-01T08:03:23.190Z",30- "durationMs": 73553.4,31- "agentType": "codex",32- "model": "gpt-5.5",33- "modelConfig": {34- "model": "gpt-5.5"35- },36- "agentSessionId": "019f1cae-8382-7992-945a-8c859f938715",37- "findingCount": 0,38- "numTurns": 0.2,39- "phase": "process",40- "costUsd": 0.5257544000000001,41- "usage": {42- "inputTokens": 36869.2,43- "outputTokens": 3475,44- "cacheReadInputTokens": 474316.8,45- "cacheCreationInputTokens": 046- }47- }48- ],49- "status": "analyzed",50- "gitInfo": {51- "recentCommitters": [],52- "enrichedAt": "2026-07-01T08:03:23.196Z"53- }54-}.deepsec/data/peach-browser/files/src/background/chirp-sync-vault-adapter.ts.json+0 −64
@@ -1,64 +0,0 @@1-{2- "filePath": "src/background/chirp-sync-vault-adapter.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 149- ],10- "snippet": "// entry id means a ciphertext minted for one entry slot cannot be replayed\n// into another: decryptEntryOrNull rejects any envelope whose decrypted\n// entry.id does not match the map key it arrived under. Both sides hold the\n// same syncMasterKey (same vault) so keys match without prior negotiation. The\n// Noise XX session encrypts the transport on top — the envelope is",11- "matchedPattern": "weak cipher algorithm"12- },13- {14- "vulnSlug": "unverified-lookup",15- "lineNumbers": [16- 12117- ],18- "snippet": " for (const id of entryIds) {\n const entry = getEntryById(id);\n if (!entry) continue;\n const key = await deriveEntryKey(masterKey, id);\n out[id] = await encryptEntry(key, entry);",19- "matchedPattern": "DB lookup by ID without ownership check in next 15 lines"20- },21- {22- "vulnSlug": "crypto-usage",23- "lineNumbers": [24- 362,25- 375,26- 39927- ],28- "snippet": " const info = new TextEncoder().encode(`${entryId}\\x00${ENTRY_KEY_INFO_SUFFIX}`);\n const raw = new HKDF(SHA256, masterKey, new Uint8Array(0), info).expand(32);\n return crypto.subtle.importKey(\n \"raw\",\n raw.buffer as ArrayBuffer,",29- "matchedPattern": "Web Crypto API (crypto.subtle.*)"30- }31- ],32- "lastScannedAt": "2026-06-30T22:09:44.299Z",33- "lastScannedRunId": "20260630220943-c7b8dba65193b129",34- "fileHash": "8c07699957e624057efc4399fd73d9ee44d0a6cdb5ad77de6d6cbd8fa851dc26",35- "findings": [],36- "analysisHistory": [37- {38- "runId": "20260701075706-0feae092f70d6cf5",39- "investigatedAt": "2026-07-01T08:03:23.183Z",40- "durationMs": 73553.4,41- "agentType": "codex",42- "model": "gpt-5.5",43- "modelConfig": {44- "model": "gpt-5.5"45- },46- "agentSessionId": "019f1cae-8382-7992-945a-8c859f938715",47- "findingCount": 0,48- "numTurns": 0.2,49- "phase": "process",50- "costUsd": 0.5257544000000001,51- "usage": {52- "inputTokens": 36869.2,53- "outputTokens": 3475,54- "cacheReadInputTokens": 474316.8,55- "cacheCreationInputTokens": 056- }57- }58- ],59- "status": "analyzed",60- "gitInfo": {61- "recentCommitters": [],62- "enrichedAt": "2026-07-01T08:03:23.190Z"63- }64-}.deepsec/data/peach-browser/files/src/background/clipboard.ts.json+0 −73
@@ -1,73 +0,0 @@1-{2- "filePath": "src/background/clipboard.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 6,9- 8710- ],11- "snippet": "// which does not survive MV3 service-worker eviction, and performed the\n// clear via `executeScript` into the *active tab*, which fails on\n// restricted pages (chrome://, web store) — both failure modes left the\n// secret on the OS clipboard indefinitely with the error swallowed.\n//",12- "matchedPattern": "weak cipher algorithm"13- },14- {15- "vulnSlug": "crypto-usage",16- "lineNumbers": [17- 6918- ],19- "snippet": "\nasync function hashForVerification(value: string): Promise<string> {\n const digest = await crypto.subtle.digest(\n \"SHA-256\",\n new TextEncoder().encode(value),",20- "matchedPattern": "Web Crypto API (crypto.subtle.*)"21- }22- ],23- "lastScannedAt": "2026-06-30T22:09:44.299Z",24- "lastScannedRunId": "20260630220943-c7b8dba65193b129",25- "fileHash": "72bb099a3bab17cf0a18d0884c8e3973b47cae4f3b678c490cbd2c712c019c8b",26- "findings": [27- {28- "severity": "MEDIUM",29- "vulnSlug": "other-info-disclosure",30- "title": "Clipboard clear alarm can skip clearing if the owed marker was not stored",31- "description": "copyToClipboard treats failure to store CLEAR_HASH_KEY as best-effort and continues scheduling the alarm. However, the alarm handler interprets a missing CLEAR_HASH_KEY as proof that no clear is owed, clears the alarm, and returns. If chrome.storage.session.set fails after a secret is copied and the MV3 worker is evicted before the setTimeout fast path runs, the alarm wakes a cold worker and skips clearing, leaving the copied secret on the OS clipboard past the advertised timeout.",32- "lineNumbers": [33- 105,34- 107,35- 109,36- 201,37- 202,38- 20439- ],40- "recommendation": "Track an explicit durable clear-owed flag and err toward clearing when marker state is unavailable or failed to write. Do not treat absence of the hash alone as proof that the clipboard has already been cleared.",41- "confidence": "medium",42- "producedByRunId": "20260701075706-0feae092f70d6cf5"43- }44- ],45- "analysisHistory": [46- {47- "runId": "20260701075706-0feae092f70d6cf5",48- "investigatedAt": "2026-07-01T08:02:47.760Z",49- "durationMs": 67618.4,50- "agentType": "codex",51- "model": "gpt-5.5",52- "modelConfig": {53- "model": "gpt-5.5"54- },55- "agentSessionId": "019f1cae-8389-7013-b8fd-a575c0019e5c",56- "findingCount": 1,57- "numTurns": 0.2,58- "phase": "process",59- "costUsd": 0.4825014,60- "usage": {61- "inputTokens": 45683.4,62- "outputTokens": 3201,63- "cacheReadInputTokens": 316108.8,64- "cacheCreationInputTokens": 065- }66- }67- ],68- "status": "analyzed",69- "gitInfo": {70- "recentCommitters": [],71- "enrichedAt": "2026-07-01T08:02:47.767Z"72- }73-}.deepsec/data/peach-browser/files/src/background/codex-handlers.ts.json+0 −49
@@ -1,49 +0,0 @@1-{2- "filePath": "src/background/codex-handlers.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 9,9- 25,10- 212,11- 22212- ],13- "snippet": " * here — that lives in messages.ts.\n *\n * Design references:\n * - /Users/june/Downloads/peach_codex_v2.md (\"Message handlers\", \"Pro gating\")\n * - src/shared/codex-types.ts for the wire contract",14- "matchedPattern": "weak cipher algorithm"15- }16- ],17- "lastScannedAt": "2026-06-30T22:09:44.025Z",18- "lastScannedRunId": "20260630220943-c7b8dba65193b129",19- "fileHash": "b4366478561ad42ad3a54c103f6ccd25d31f379e4d89f781afa725818a1466da",20- "findings": [],21- "analysisHistory": [22- {23- "runId": "20260701075706-0feae092f70d6cf5",24- "investigatedAt": "2026-07-01T08:05:03.759Z",25- "durationMs": 94792,26- "agentType": "codex",27- "model": "gpt-5.5",28- "modelConfig": {29- "model": "gpt-5.5"30- },31- "agentSessionId": "019f1cae-839f-7222-b3aa-685b0bafd4d0",32- "findingCount": 0,33- "numTurns": 0.2,34- "phase": "process",35- "costUsd": 0.6968018,36- "usage": {37- "inputTokens": 65268.6,38- "outputTokens": 4124.2,39- "cacheReadInputTokens": 493465.6,40- "cacheCreationInputTokens": 041- }42- }43- ],44- "status": "analyzed",45- "gitInfo": {46- "recentCommitters": [],47- "enrichedAt": "2026-07-01T08:05:03.765Z"48- }49-}.deepsec/data/peach-browser/files/src/background/desktop-pairing-bg.ts.json+0 −101
@@ -1,101 +0,0 @@1-{2- "filePath": "src/background/desktop-pairing-bg.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 1,9- 3,10- 5,11- 9,12- 10,13- 11,14- 12,15- 15,16- 16,17- 17,18- 18,19- 19,20- 24,21- 25,22- 47,23- 48,24- 89,25- 104,26- 142,27- 151,28- 156,29- 157,30- 160,31- 162,32- 165,33- 166,34- 168,35- 172,36- 176,37- 180,38- 18839- ],40- "snippet": "// Background wiring for desktop helper code pairing (Change 3 §Wiring).\n//\n// Keeps `desktop-pairing.ts` a PURE, dependency-injected client (so its unit",41- "matchedPattern": "weak cipher algorithm"42- },43- {44- "vulnSlug": "postmessage-origin",45- "lineNumbers": [46- 7147- ],48- "snippet": " };\n ws.onmessage = (ev: MessageEvent) => {\n const bytes = new Uint8Array(ev.data as ArrayBuffer);\n const r = readers.shift();\n if (r) { sawCloseBeforeFrame = false; r.resolve(bytes); }\n else inbox.push(bytes);",49- "matchedPattern": "postMessage handler WITHOUT origin validation"50- }51- ],52- "lastScannedAt": "2026-06-30T22:09:44.141Z",53- "lastScannedRunId": "20260630220943-c7b8dba65193b129",54- "fileHash": "2fff4b2ee27552146888813b15a1b33d604182a1a3e7dfdd20021a2153fd253c",55- "findings": [56- {57- "severity": "BUG",58- "vulnSlug": "other-state-leak",59- "title": "Desktop pairing can remain stuck waiting for confirmation",60- "description": "The background stores a single global pendingConfirm resolver and sets inFlight=true, but the confirm promise has no timeout or abort path. If the popup closes before the SAS screen is reached, if the advisory DESKTOP_PAIR_SAS push is missed, or if the confirm message is lost, pairWithDesktopHelper waits indefinitely and inFlight stays true until the service worker is restarted. That blocks future desktop pairing attempts with pairing_in_progress.",61- "lineNumbers": [62- 143,63- 144,64- 177,65- 178,66- 18067- ],68- "recommendation": "Add a bounded confirmation timeout and an abort/cancel path that clears pendingConfirm and inFlight. Consider associating each pairing attempt with an id so stale confirms cannot resolve a later attempt.",69- "confidence": "high",70- "producedByRunId": "20260701075706-0feae092f70d6cf5"71- }72- ],73- "analysisHistory": [74- {75- "runId": "20260701075706-0feae092f70d6cf5",76- "investigatedAt": "2026-07-01T08:02:47.731Z",77- "durationMs": 67618.4,78- "agentType": "codex",79- "model": "gpt-5.5",80- "modelConfig": {81- "model": "gpt-5.5"82- },83- "agentSessionId": "019f1cae-8389-7013-b8fd-a575c0019e5c",84- "findingCount": 1,85- "numTurns": 0.2,86- "phase": "process",87- "costUsd": 0.4825014,88- "usage": {89- "inputTokens": 45683.4,90- "outputTokens": 3201,91- "cacheReadInputTokens": 316108.8,92- "cacheCreationInputTokens": 093- }94- }95- ],96- "status": "analyzed",97- "gitInfo": {98- "recentCommitters": [],99- "enrichedAt": "2026-07-01T08:02:47.738Z"100- }101-}.deepsec/data/peach-browser/files/src/background/desktop-pairing-mdns.ts.json+0 −49
@@ -1,49 +0,0 @@1-{2- "filePath": "src/background/desktop-pairing-mdns.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 1,9- 11,10- 12,11- 9912- ],13- "snippet": "// mDNS discovery for the peach-sync desktop helper.\n//\n// peach-sync announces `_peach-sync._tcp.local` on the LAN (PTR + SRV + TXT +",14- "matchedPattern": "weak cipher algorithm"15- }16- ],17- "lastScannedAt": "2026-06-30T22:09:44.025Z",18- "lastScannedRunId": "20260630220943-c7b8dba65193b129",19- "fileHash": "99b744f3ad7115995fe707163072d67e45145ed42dc986a28f977d5f6eccb18e",20- "findings": [],21- "analysisHistory": [22- {23- "runId": "20260701075706-0feae092f70d6cf5",24- "investigatedAt": "2026-07-01T08:05:03.766Z",25- "durationMs": 94792,26- "agentType": "codex",27- "model": "gpt-5.5",28- "modelConfig": {29- "model": "gpt-5.5"30- },31- "agentSessionId": "019f1cae-839f-7222-b3aa-685b0bafd4d0",32- "findingCount": 0,33- "numTurns": 0.2,34- "phase": "process",35- "costUsd": 0.6968018,36- "usage": {37- "inputTokens": 65268.6,38- "outputTokens": 4124.2,39- "cacheReadInputTokens": 493465.6,40- "cacheCreationInputTokens": 041- }42- }43- ],44- "status": "analyzed",45- "gitInfo": {46- "recentCommitters": [],47- "enrichedAt": "2026-07-01T08:05:03.771Z"48- }49-}.deepsec/data/peach-browser/files/src/background/desktop-pairing-probe.ts.json+0 −46
@@ -1,46 +0,0 @@1-{2- "filePath": "src/background/desktop-pairing-probe.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 19- ],10- "snippet": "// Handshake-free /24 port probe for desktop pairing (Change 5).\n//\n// Unlike `probeSubnetForPeer` (chirp-sync-transport.ts), this does NOT run",11- "matchedPattern": "weak cipher algorithm"12- }13- ],14- "lastScannedAt": "2026-06-30T22:09:44.025Z",15- "lastScannedRunId": "20260630220943-c7b8dba65193b129",16- "fileHash": "7029d1d502f76b08d304d4c521d242bd2ac76823ad40657f36ad81b4b8c6b883",17- "findings": [],18- "analysisHistory": [19- {20- "runId": "20260701075706-0feae092f70d6cf5",21- "investigatedAt": "2026-07-01T08:05:03.772Z",22- "durationMs": 94792,23- "agentType": "codex",24- "model": "gpt-5.5",25- "modelConfig": {26- "model": "gpt-5.5"27- },28- "agentSessionId": "019f1cae-839f-7222-b3aa-685b0bafd4d0",29- "findingCount": 0,30- "numTurns": 0.2,31- "phase": "process",32- "costUsd": 0.6968018,33- "usage": {34- "inputTokens": 65268.6,35- "outputTokens": 4124.2,36- "cacheReadInputTokens": 493465.6,37- "cacheCreationInputTokens": 038- }39- }40- ],41- "status": "analyzed",42- "gitInfo": {43- "recentCommitters": [],44- "enrichedAt": "2026-07-01T08:05:03.778Z"45- }46-}.deepsec/data/peach-browser/files/src/background/desktop-pairing.ts.json+0 −92
@@ -1,92 +0,0 @@1-{2- "filePath": "src/background/desktop-pairing.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 1,9- 3,10- 13,11- 26,12- 30,13- 41,14- 50,15- 54,16- 63,17- 69,18- 112,19- 119,20- 121,21- 122,22- 138,23- 145,24- 147,25- 222,26- 227,27- 24228- ],29- "snippet": "// Desktop helper code-pairing client (Change 3).\n//\n// Pairs the extension with the `peach-sync` desktop helper:",30- "matchedPattern": "weak cipher algorithm"31- },32- {33- "vulnSlug": "cache-key-poisoning",34- "lineNumbers": [35- 23636- ],37- "snippet": " await deps.persistDevice(device);\n if (deps.cacheLastKnownIp && device.longTermPublicKeyHex) {\n await deps.cacheLastKnownIp(device.longTermPublicKeyHex, host).catch(() => undefined);\n }\n cb.onComplete(device);",38- "matchedPattern": "Cache key includes Host header"39- }40- ],41- "lastScannedAt": "2026-06-30T22:09:44.099Z",42- "lastScannedRunId": "20260630220943-c7b8dba65193b129",43- "fileHash": "5b6658fa6a76328b44478a5481779e651cc79bf8c2aadd14041569a303ce10b0",44- "findings": [45- {46- "severity": "BUG",47- "vulnSlug": "other-input-validation",48- "title": "Desktop pairing persists unvalidated peer metadata",49- "description": "decodePairIdentity validates the peer long_term_public_key_hex, but it does not validate device_type against the DeviceType enum, signing_public_key_hex as 32-byte lowercase hex, or ws_port as an integer in 1..65535. Those values are copied into the paired-device registry after SAS confirmation. A malformed or buggy helper can create a paired record that appears successful but later cannot sync, is omitted from manifests, or carries an unusable port.",50- "lineNumbers": [51- 98,52- 103,53- 108,54- 224,55- 227,56- 229,57- 23158- ],59- "recommendation": "Validate the entire PAIR_IDENTITY frame before persistence: restrict device_type to extension/android/ios/desktop, validate optional signing_public_key_hex with the same 64-lowercase-hex check, and accept ws_port only when it is a finite integer between 1 and 65535.",60- "confidence": "high",61- "producedByRunId": "20260701075706-0feae092f70d6cf5"62- }63- ],64- "analysisHistory": [65- {66- "runId": "20260701075706-0feae092f70d6cf5",67- "investigatedAt": "2026-07-01T08:02:47.739Z",68- "durationMs": 67618.4,69- "agentType": "codex",70- "model": "gpt-5.5",71- "modelConfig": {72- "model": "gpt-5.5"73- },74- "agentSessionId": "019f1cae-8389-7013-b8fd-a575c0019e5c",75- "findingCount": 1,76- "numTurns": 0.2,77- "phase": "process",78- "costUsd": 0.4825014,79- "usage": {80- "inputTokens": 45683.4,81- "outputTokens": 3201,82- "cacheReadInputTokens": 316108.8,83- "cacheCreationInputTokens": 084- }85- }86- ],87- "status": "analyzed",88- "gitInfo": {89- "recentCommitters": [],90- "enrichedAt": "2026-07-01T08:02:47.746Z"91- }92-}.deepsec/data/peach-browser/files/src/background/health.ts.json+0 −46
@@ -1,46 +0,0 @@1-{2- "filePath": "src/background/health.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "crypto-usage",7- "lineNumbers": [8- 369- ],10- "snippet": "\nasync function sha256Hex(plain: string): Promise<string> {\n const buf = await crypto.subtle.digest(\n \"SHA-256\",\n new TextEncoder().encode(plain)",11- "matchedPattern": "Web Crypto API (crypto.subtle.*)"12- }13- ],14- "lastScannedAt": "2026-06-30T22:09:44.298Z",15- "lastScannedRunId": "20260630220943-c7b8dba65193b129",16- "fileHash": "079c0a6e847c40afa2f4cd77903ca5b64b8dd865515686f26df2a20792124e42",17- "findings": [],18- "analysisHistory": [19- {20- "runId": "20260701075706-0feae092f70d6cf5",21- "investigatedAt": "2026-07-01T08:03:57.001Z",22- "durationMs": 81450.2,23- "agentType": "codex",24- "model": "gpt-5.5",25- "modelConfig": {26- "model": "gpt-5.5"27- },28- "agentSessionId": "019f1cae-8382-71e3-8ad3-dbb6109ffe96",29- "findingCount": 0,30- "numTurns": 0.2,31- "phase": "process",32- "costUsd": 0.44014300000000006,33- "usage": {34- "inputTokens": 31713.8,35- "outputTokens": 3856.2,36- "cacheReadInputTokens": 331776,37- "cacheCreationInputTokens": 038- }39- }40- ],41- "status": "analyzed",42- "gitInfo": {43- "recentCommitters": [],44- "enrichedAt": "2026-07-01T08:03:57.009Z"45- }46-}.deepsec/data/peach-browser/files/src/background/license.ts.json+0 −46
@@ -1,46 +0,0 @@1-{2- "filePath": "src/background/license.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "secrets-exposure",7- "lineNumbers": [8- 249- ],10- "snippet": "\nexport const PUBLIC_KEY_HEX =\n \"c7dc52d2c484696cf8566dbe6cfe69025d114cb47f6cf53c25088ecfd539bdfa\";\n\nexport interface LicenseInfo {",11- "matchedPattern": "potential 256-bit hex secret"12- }13- ],14- "lastScannedAt": "2026-06-30T22:09:44.003Z",15- "lastScannedRunId": "20260630220943-c7b8dba65193b129",16- "fileHash": "22537416b37c0027b95d898bc34208546d9b9485e1169db402cf40a04a8f7dbf",17- "findings": [],18- "analysisHistory": [19- {20- "runId": "20260701075706-0feae092f70d6cf5",21- "investigatedAt": "2026-07-01T08:02:47.746Z",22- "durationMs": 67618.4,23- "agentType": "codex",24- "model": "gpt-5.5",25- "modelConfig": {26- "model": "gpt-5.5"27- },28- "agentSessionId": "019f1cae-8389-7013-b8fd-a575c0019e5c",29- "findingCount": 0,30- "numTurns": 0.2,31- "phase": "process",32- "costUsd": 0.4825014,33- "usage": {34- "inputTokens": 45683.4,35- "outputTokens": 3201,36- "cacheReadInputTokens": 316108.8,37- "cacheCreationInputTokens": 038- }39- }40- ],41- "status": "analyzed",42- "gitInfo": {43- "recentCommitters": [],44- "enrichedAt": "2026-07-01T08:02:47.753Z"45- }46-}.deepsec/data/peach-browser/files/src/background/messages.ts.json+0 −237
@@ -1,237 +0,0 @@1-{2- "filePath": "src/background/messages.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "auth-bypass",7- "lineNumbers": [8- 3669- ],10- "snippet": " // window (peach_recovery_pending) shows the words immediately after\n // the user authenticated, and that confirmation UI has no password to\n // resend — skip the re-auth there only.\n if (!(await isRecoveryConfirmPending())) {\n const { password } = (msg.payload ?? {}) as { password?: string };",11- "matchedPattern": "auth skip/bypass"12- },13- {14- "vulnSlug": "insecure-crypto",15- "lineNumbers": [16- 138,17- 141,18- 142,19- 143,20- 272,21- 274,22- 601,23- 602,24- 603,25- 751,26- 1229,27- 1230,28- 1237,29- 1239,30- 1286,31- 1431,32- 1434,33- 1435,34- 1441,35- 1442,36- 1444,37- 1445,38- 1449,39- 1451,40- 145241- ],42- "snippet": "const FEEDBACK_ENDPOINT = \"\";\n\n// PCH-01-018: operations that irreversibly destroy or replace vault contents.\n// These are gated to trusted extension-page senders in handleMessage; the\n// confirmation UX otherwise lives only in the popup. Keep this in sync when",43- "matchedPattern": "weak cipher algorithm"44- },45- {46- "vulnSlug": "secret-in-log",47- "lineNumbers": [48- 28749- ],50- "snippet": " case \"CREATE_VAULT\": {\n const { password } = msg.payload as { password: string };\n // Refuse to create a vault under an empty master password — the derived",51- "matchedPattern": "Secret variable in log statement"52- },53- {54- "vulnSlug": "secret-in-log",55- "lineNumbers": [56- 30257- ],58- "snippet": " case \"CREATE_VAULT_FROM_ROOT_KEY\": {\n const { password, rootKeyHex } = (msg.payload ?? {}) as {\n password?: unknown;",59- "matchedPattern": "Secret variable in log statement"60- },61- {62- "vulnSlug": "secret-in-log",63- "lineNumbers": [64- 32165- ],66- "snippet": " case \"UNLOCK\": {\n const { password } = msg.payload as { password: string };\n const ok = await vault.unlock(password);",67- "matchedPattern": "Secret variable in log statement"68- },69- {70- "vulnSlug": "secret-in-log",71- "lineNumbers": [72- 36873- ],74- "snippet": " if (!(await isRecoveryConfirmPending())) {\n const { password } = (msg.payload ?? {}) as { password?: string };\n if (typeof password !== \"string\" || !password) {",75- "matchedPattern": "Secret variable in log statement"76- },77- {78- "vulnSlug": "secret-in-log",79- "lineNumbers": [80- 107881- ],82- "snippet": " case \"VERIFY_MAIN_PASSWORD\": {\n const { password } = msg.payload as { password: string };\n const ok = await vault.verifyMainPassword(password);",83- "matchedPattern": "Secret variable in log statement"84- },85- {86- "vulnSlug": "unverified-lookup",87- "lineNumbers": [88- 51389- ],90- "snippet": " if (incomingCanary && typeof incomingCanary === \"object\") {\n const existing = vault.getEntryById(id);\n if (!existing?.canary) {\n const gate = await requirePro(\"canary_entry\");\n if (!gate.allowed) return { ok: false, error: \"pro_required\" };",91- "matchedPattern": "DB lookup by ID without ownership check in next 15 lines"92- },93- {94- "vulnSlug": "unverified-lookup",95- "lineNumbers": [96- 52697- ],98- "snippet": " if (!trimmed) return { ok: false, error: \"url_required\" };\n const existing = vault.getEntryById(id);\n if (!existing) return { ok: false, error: \"entry_not_found\" };\n if (existing.type !== \"login\") return { ok: false, error: \"login_entries_only\" };\n const merged = [...(existing.additionalUrls ?? []), trimmed];",99- "matchedPattern": "DB lookup by ID without ownership check in next 15 lines"100- },101- {102- "vulnSlug": "unverified-lookup",103- "lineNumbers": [104- 560105- ],106- "snippet": " if (id) {\n const existing = vault.getEntryById(id);\n if (!existing) return { ok: false, error: \"not_found\" };\n await vault.updateEntry(id, { title: title || \"Untitled\", notes: body, modifiedAt: now });\n return { ok: true, data: vault.getEntryById(id) };",107- "matchedPattern": "DB lookup by ID without ownership check in next 15 lines"108- },109- {110- "vulnSlug": "unverified-lookup",111- "lineNumbers": [112- 563113- ],114- "snippet": " await vault.updateEntry(id, { title: title || \"Untitled\", notes: body, modifiedAt: now });\n return { ok: true, data: vault.getEntryById(id) };\n } else {\n // PCH-01-026 #10: addEntry stamps its own createdAt/modifiedAt\n // (EntryDraft omits both), so passing them was the only reason the",115- "matchedPattern": "DB lookup by ID without ownership check in next 15 lines"116- },117- {118- "vulnSlug": "unverified-lookup",119- "lineNumbers": [120- 711121- ],122- "snippet": " };\n const entry = vault.getEntryById(id);\n if (!entry) return { ok: false, error: \"entry_not_found\" };\n // Passkey-only entries (passkey credential, no password) cannot be\n // filled into a form — sign-in goes through the WebAuthn path. A",123- "matchedPattern": "DB lookup by ID without ownership check in next 15 lines"124- },125- {126- "vulnSlug": "unverified-lookup",127- "lineNumbers": [128- 794129- ],130- "snippet": " };\n const entry = vault.getEntryById(id);\n if (!entry) return { ok: false, error: \"entry_not_found\" };\n\n // PCH-01-006: re-validate the browser-attested sender origin, mirroring",131- "matchedPattern": "DB lookup by ID without ownership check in next 15 lines"132- },133- {134- "vulnSlug": "unverified-lookup",135- "lineNumbers": [136- 857137- ],138- "snippet": " for (const [id, count] of Object.entries(results)) {\n const entry = vault.getEntryById(id);\n if (entry && (entry.breach ?? 0) !== count) {\n await vault.updateEntry(id, { breach: count > 0 ? count : undefined });\n }",139- "matchedPattern": "DB lookup by ID without ownership check in next 15 lines"140- },141- {142- "vulnSlug": "untrusted-redirect-following",143- "lineNumbers": [144- 951145- ],146- "snippet": " try {\n const res = await fetch(webhookUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },",147- "matchedPattern": "fetch(webhookUrl) — default redirect: follow + caller-style URL"148- },149- {150- "vulnSlug": "untrusted-redirect-following",151- "lineNumbers": [152- 962153- ],154- "snippet": " // Non-test triggers are fire-and-forget\n fetch(webhookUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },",155- "matchedPattern": "fetch(webhookUrl) — default redirect: follow + caller-style URL"156- }157- ],158- "lastScannedAt": "2026-06-30T22:09:44.335Z",159- "lastScannedRunId": "20260630220943-c7b8dba65193b129",160- "fileHash": "ffce5ec93f9c193481c65976cc14a602d72457b00a2378239305cccbd9ec9c28",161- "findings": [162- {163- "severity": "HIGH",164- "vulnSlug": "acl-check",165- "title": "Root-key adoption material is exported without enforcing the pairing approval boundary",166- "description": "The CHIRP_GET_ADOPTION_MATERIAL handler returns rootKeyHex, vaultId, and possibly the Pro license whenever the vault is unlocked. The SAS confirmation and adoption consent happen in the popup orchestrator before it calls this handler, but the handler itself does not require an extension-page sender, a one-time pairing/adoption token, or any proof that SAS and consent occurred. Since content scripts are same-extension runtime senders, a page-adjacent content-script compromise can call this generic message and exfiltrate the vault root key, which is enough to derive sync identities and adopt/decrypt the vault on another device.",167- "lineNumbers": [168- 1361,169- 1367,170- 1368,171- 1371172- ],173- "recommendation": "Treat root-key export as a sensitive operation: require a trusted extension-page sender and bind the response to an in-memory, one-time adoption session created only after SAS confirmation and explicit consent. Prefer moving the root-key grant fully inside the pairing protocol rather than exposing it through a generic message.",174- "confidence": "medium",175- "producedByRunId": "20260701075706-0feae092f70d6cf5"176- },177- {178- "severity": "HIGH",179- "vulnSlug": "auth-bypass",180- "title": "Passkey approval responses are accepted from forgeable content messages",181- "description": "PASSKEY_AUTH_RESPONSE accepts any same-extension runtime sender and resolves the pending passkey approval solely by numeric requestId plus approved boolean. On Firefox, the page-world passkey relay allowlists PASSKEY_AUTH_RESPONSE, so page JavaScript can send approval messages through the relay. Combined with the Firefox proxy path's synthetic timestamp-based request ids, a page can race/guess the active request id and approve its own passkey ceremony without the user's popup/toast confirmation.",182- "lineNumbers": [183- 1192,184- 1200185- ],186- "recommendation": "Do not relay PASSKEY_AUTH_RESPONSE from page JS. Bind approvals to an unguessable per-request token delivered only to trusted extension UI, and verify the sender/context before resolving approvals.",187- "confidence": "high",188- "producedByRunId": "20260701075706-0feae092f70d6cf5"189- },190- {191- "severity": "HIGH_BUG",192- "vulnSlug": "other-data-loss",193- "title": "Vault creation handlers can overwrite an existing vault outside the destructive-message gate",194- "description": "DESTRUCTIVE_MESSAGE_TYPES is documented as the gate for operations that irreversibly destroy or replace vault contents, but it omits CREATE_VAULT and CREATE_VAULT_FROM_ROOT_KEY. Both handlers call vault creation paths that write fresh slot A/B contents and reset in-memory entries without first checking that no vault exists. A misrouted or content-script-originated message can replace the user's existing vault with an empty/new adopted vault without the extension-page destructive sender gate.",195- "lineNumbers": [196- 138,197- 142,198- 146,199- 286,200- 295,201- 301,202- 311203- ],204- "recommendation": "Reject CREATE_VAULT and CREATE_VAULT_FROM_ROOT_KEY when a vault already exists unless the request is part of an explicit, trusted extension-page replacement/adoption flow. Add replacement-capable creation messages to the destructive sender gate.",205- "confidence": "high",206- "producedByRunId": "20260701075706-0feae092f70d6cf5"207- }208- ],209- "analysisHistory": [210- {211- "runId": "20260701075706-0feae092f70d6cf5",212- "investigatedAt": "2026-07-01T08:03:23.160Z",213- "durationMs": 73553.4,214- "agentType": "codex",215- "model": "gpt-5.5",216- "modelConfig": {217- "model": "gpt-5.5"218- },219- "agentSessionId": "019f1cae-8382-7992-945a-8c859f938715",220- "findingCount": 3,221- "numTurns": 0.2,222- "phase": "process",223- "costUsd": 0.5257544000000001,224- "usage": {225- "inputTokens": 36869.2,226- "outputTokens": 3475,227- "cacheReadInputTokens": 474316.8,228- "cacheCreationInputTokens": 0229- }230- }231- ],232- "status": "analyzed",233- "gitInfo": {234- "recentCommitters": [],235- "enrichedAt": "2026-07-01T08:03:23.168Z"236- }237-}.deepsec/data/peach-browser/files/src/background/offscreen.ts.json+0 −46
@@ -1,46 +0,0 @@1-{2- "filePath": "src/background/offscreen.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "non-atomic-read-delete",7- "lineNumbers": [8- 309- ],10- "snippet": " document.body.appendChild(ta);\n ta.select();\n document.execCommand(\"copy\");\n ta.remove();\n sendResponse({ ok: true, via: \"execCommand\" });\n } catch {",11- "matchedPattern": "Read-then-modify without transaction — potential TOCTOU race"12- }13- ],14- "lastScannedAt": "2026-06-30T22:09:44.247Z",15- "lastScannedRunId": "20260630220943-c7b8dba65193b129",16- "fileHash": "76c8c58a347fb502198c7c0607f86c670bc361d8df18e25c49bc7c4886e0fa22",17- "findings": [],18- "analysisHistory": [19- {20- "runId": "20260701075706-0feae092f70d6cf5",21- "investigatedAt": "2026-07-01T08:03:57.009Z",22- "durationMs": 81450.2,23- "agentType": "codex",24- "model": "gpt-5.5",25- "modelConfig": {26- "model": "gpt-5.5"27- },28- "agentSessionId": "019f1cae-8382-71e3-8ad3-dbb6109ffe96",29- "findingCount": 0,30- "numTurns": 0.2,31- "phase": "process",32- "costUsd": 0.44014300000000006,33- "usage": {34- "inputTokens": 31713.8,35- "outputTokens": 3856.2,36- "cacheReadInputTokens": 331776,37- "cacheCreationInputTokens": 038- }39- }40- ],41- "status": "analyzed",42- "gitInfo": {43- "recentCommitters": [],44- "enrichedAt": "2026-07-01T08:03:57.018Z"45- }46-}.deepsec/data/peach-browser/files/src/background/passkey-crypto.ts.json+0 −70
@@ -1,70 +0,0 @@1-{2- "filePath": "src/background/passkey-crypto.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 1599- ],10- "snippet": "// an intentional architectural tradeoff — Peach stores the JWK inside the\n// encrypted vault blob to enable PeachScript backup, Codex restore, and cross-\n// device sync. A stronger design would use non-extractable platform keys, but\n// that would break portability. The private key never leaves the background\n// process: content scripts receive only signed assertions, never the key",11- "matchedPattern": "weak cipher algorithm"12- },13- {14- "vulnSlug": "crypto-usage",15- "lineNumbers": [16- 172,17- 177,18- 178,19- 179,20- 187,21- 192,22- 193,23- 194,24- 208,25- 215,26- 223,27- 281,28- 288,29- 296,30- 303,31- 315,32- 35633- ],34- "snippet": "}> {\n if (algorithm === -7) {\n const keyPair = await crypto.subtle.generateKey(\n { name: \"ECDSA\", namedCurve: \"P-256\" },\n true, // AUD-005: extractable — required for vault-stored portability",35- "matchedPattern": "Web Crypto API (crypto.subtle.*)"36- }37- ],38- "lastScannedAt": "2026-06-30T22:09:44.295Z",39- "lastScannedRunId": "20260630220943-c7b8dba65193b129",40- "fileHash": "4ea6992cc3f6da6efe34c97b3d6680aeb67bd6aa33dabe978abc814859c759ff",41- "findings": [],42- "analysisHistory": [43- {44- "runId": "20260701075706-0feae092f70d6cf5",45- "investigatedAt": "2026-07-01T08:03:56.983Z",46- "durationMs": 81450.2,47- "agentType": "codex",48- "model": "gpt-5.5",49- "modelConfig": {50- "model": "gpt-5.5"51- },52- "agentSessionId": "019f1cae-8382-71e3-8ad3-dbb6109ffe96",53- "findingCount": 0,54- "numTurns": 0.2,55- "phase": "process",56- "costUsd": 0.44014300000000006,57- "usage": {58- "inputTokens": 31713.8,59- "outputTokens": 3856.2,60- "cacheReadInputTokens": 331776,61- "cacheCreationInputTokens": 062- }63- }64- ],65- "status": "analyzed",66- "gitInfo": {67- "recentCommitters": [],68- "enrichedAt": "2026-07-01T08:03:56.992Z"69- }70-}.deepsec/data/peach-browser/files/src/background/passkey.ts.json+0 −119
@@ -1,119 +0,0 @@1-{2- "filePath": "src/background/passkey.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 692,9- 696,10- 83911- ],12- "snippet": " entry.passkey !== undefined && !entry.deletedAt\n );\n const allowedCredentialIds = (options.allowCredentials ?? []).map((descriptor) => descriptor.id);\n\n const match = passkeyBearing.find((entry) => {",13- "matchedPattern": "weak cipher algorithm"14- },15- {16- "vulnSlug": "insecure-crypto",17- "lineNumbers": [18- 842,19- 112320- ],21- "snippet": " // same millisecond. Mix in 20 random bits to match the synthetic-id pattern\n // at requestFirefoxApproval (M9/COR-010).\n const requestId = Date.now() + Math.floor(Math.random() * 1_000_000);\n const challenge = base64urlEncode(crypto.getRandomValues(new Uint8Array(32)));\n const userHandle = base64urlEncode(crypto.getRandomValues(new Uint8Array(32)));",22- "matchedPattern": "Math.random in security context"23- },24- {25- "vulnSlug": "secret-in-log",26- "lineNumbers": [27- 70928- ],29- "snippet": " // break RPs (e.g. Google) that issue a GET as part of their create-passkey flow.\n console.log(\"[Peach passkey] no matching credential — passing through to platform\", { requestId: request.requestId });\n try {",30- "matchedPattern": "Secret variable in log statement"31- },32- {33- "vulnSlug": "unsafe-deserialization",34- "lineNumbers": [35- 418,36- 64937- ],38- "snippet": " detailsJson: request.requestDetailsJson,\n });\n const options = JSON.parse(request.requestDetailsJson) as PublicKeyCredentialCreationOptionsJSON;\n const rpId = options.rp.id;\n const rpName = options.rp.name ?? options.rp.id;",39- "matchedPattern": "JSON.parse of request input (verify schema validation)"40- },41- {42- "vulnSlug": "crypto-usage",43- "lineNumbers": [44- 762,45- 130746- ],47- "snippet": " );\n const clientDataHash = new Uint8Array(\n await crypto.subtle.digest(\"SHA-256\", new Uint8Array(clientDataJSON))\n );\n const signature = await signPasskeyAssertion(",48- "matchedPattern": "Web Crypto API (crypto.subtle.*)"49- }50- ],51- "lastScannedAt": "2026-06-30T22:09:44.295Z",52- "lastScannedRunId": "20260630220943-c7b8dba65193b129",53- "fileHash": "889398c2884062092e4013af47fdc81d1fcd671fccf43095b1610c583036570d",54- "findings": [55- {56- "severity": "HIGH",57- "vulnSlug": "other-passkey-origin-confusion",58- "title": "Chrome passkey get matches allowCredentials without enforcing the requested RP ID",59- "description": "handleGetRequest chooses a stored passkey by credential id whenever allowCredentials is non-empty, without also requiring entry.passkey.rpId to equal options.rpId. It then builds authenticatorData and clientDataJSON from the stored credential's rpId, not the requested rpId. A malicious or phishing page that knows a victim credential id and has a target-site challenge can ask for its own rpId with the victim id in allowCredentials; Peach can select the victim credential and return an assertion scoped to the victim rpId/origin instead of passing through or rejecting.",60- "lineNumbers": [61- 692,62- 695,63- 696,64- 698,65- 699,66- 751,67- 75968- ],69- "recommendation": "Always require the stored credential rpId to match the requested options.rpId, even when allowCredentials is present. Build and sign only for the browser-requested RP after verifying the caller origin owns that RP; otherwise pass through or reject.",70- "confidence": "high",71- "producedByRunId": "20260701075706-0feae092f70d6cf5"72- },73- {74- "severity": "HIGH",75- "vulnSlug": "missing-auth",76- "title": "Firefox passkey completion stores page-supplied credentials without pending-request verification",77- "description": "completeFirefoxPasskeyCreate only checks that the vault is unlocked, then trusts the payload's rpId, credentialId, public key, username, and requestId. It does not read FIREFOX_PENDING_KEY, compare requestId/rpId/userHandle to the pending popup-created ceremony, verify a platform credential was actually created, or enforce the caller origin. Because the Firefox relay allowlists COMPLETE_FIREFOX_PASSKEY_CREATE from page JS, any page can inject or overwrite a Firefox passkey record in the unlocked vault, including for another rpId.",78- "lineNumbers": [79- 863,80- 878,81- 880,82- 901,83- 903,84- 90585- ],86- "recommendation": "Remove COMPLETE_FIREFOX_PASSKEY_CREATE from the page relay allowlist and accept it only from trusted extension UI. In completeFirefoxPasskeyCreate, load and validate the pending session entry, require exact requestId/rpId/userHandle matches, and clear it only after a verified completion.",87- "confidence": "high",88- "producedByRunId": "20260701075706-0feae092f70d6cf5"89- }90- ],91- "analysisHistory": [92- {93- "runId": "20260701075706-0feae092f70d6cf5",94- "investigatedAt": "2026-07-01T08:03:23.176Z",95- "durationMs": 73553.4,96- "agentType": "codex",97- "model": "gpt-5.5",98- "modelConfig": {99- "model": "gpt-5.5"100- },101- "agentSessionId": "019f1cae-8382-7992-945a-8c859f938715",102- "findingCount": 2,103- "numTurns": 0.2,104- "phase": "process",105- "costUsd": 0.5257544000000001,106- "usage": {107- "inputTokens": 36869.2,108- "outputTokens": 3475,109- "cacheReadInputTokens": 474316.8,110- "cacheCreationInputTokens": 0111- }112- }113- ],114- "status": "analyzed",115- "gitInfo": {116- "recentCommitters": [],117- "enrichedAt": "2026-07-01T08:03:23.183Z"118- }119-}.deepsec/data/peach-browser/files/src/background/recovery-pending.ts.json+0 −46
@@ -1,46 +0,0 @@1-{2- "filePath": "src/background/recovery-pending.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 99- ],10- "snippet": "//\n// Lives in chrome.storage.local so the flag survives browser restart. Cleared\n// on confirmation success and on DESTROY_VAULT (storage.local.clear()).\n\nconst KEY = \"peach_recovery_pending\";",11- "matchedPattern": "weak cipher algorithm"12- }13- ],14- "lastScannedAt": "2026-06-30T22:09:44.023Z",15- "lastScannedRunId": "20260630220943-c7b8dba65193b129",16- "fileHash": "21eb7e7f09428e568e1b96169b5b54d089577093753f1f842e687382fd50d727",17- "findings": [],18- "analysisHistory": [19- {20- "runId": "20260701075706-0feae092f70d6cf5",21- "investigatedAt": "2026-07-01T08:05:03.778Z",22- "durationMs": 94792,23- "agentType": "codex",24- "model": "gpt-5.5",25- "modelConfig": {26- "model": "gpt-5.5"27- },28- "agentSessionId": "019f1cae-839f-7222-b3aa-685b0bafd4d0",29- "findingCount": 0,30- "numTurns": 0.2,31- "phase": "process",32- "costUsd": 0.6968018,33- "usage": {34- "inputTokens": 65268.6,35- "outputTokens": 4124.2,36- "cacheReadInputTokens": 493465.6,37- "cacheCreationInputTokens": 038- }39- }40- ],41- "status": "analyzed",42- "gitInfo": {43- "recentCommitters": [],44- "enrichedAt": "2026-07-01T08:05:03.785Z"45- }46-}.deepsec/data/peach-browser/files/src/background/self-test.ts.json+0 −49
@@ -1,49 +0,0 @@1-{2- "filePath": "src/background/self-test.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "crypto-usage",7- "lineNumbers": [8- 49,9- 56,10- 74,11- 9212- ],13- "snippet": "async function deriveKatKey(password: string, salt: Uint8Array<ArrayBuffer>): Promise<CryptoKey> {\n const enc = new TextEncoder();\n const keyMaterial = await crypto.subtle.importKey(\n \"raw\",\n enc.encode(password),",14- "matchedPattern": "Web Crypto API (crypto.subtle.*)"15- }16- ],17- "lastScannedAt": "2026-06-30T22:09:44.294Z",18- "lastScannedRunId": "20260630220943-c7b8dba65193b129",19- "fileHash": "d57c5d31d980c9be0eb421ec2a45ae6994fe7ee1a095c862d718b4b8bc32d821",20- "findings": [],21- "analysisHistory": [22- {23- "runId": "20260701075706-0feae092f70d6cf5",24- "investigatedAt": "2026-07-01T08:03:57.018Z",25- "durationMs": 81450.2,26- "agentType": "codex",27- "model": "gpt-5.5",28- "modelConfig": {29- "model": "gpt-5.5"30- },31- "agentSessionId": "019f1cae-8382-71e3-8ad3-dbb6109ffe96",32- "findingCount": 0,33- "numTurns": 0.2,34- "phase": "process",35- "costUsd": 0.44014300000000006,36- "usage": {37- "inputTokens": 31713.8,38- "outputTokens": 3856.2,39- "cacheReadInputTokens": 331776,40- "cacheCreationInputTokens": 041- }42- }43- ],44- "status": "analyzed",45- "gitInfo": {46- "recentCommitters": [],47- "enrichedAt": "2026-07-01T08:03:57.026Z"48- }49-}.deepsec/data/peach-browser/files/src/background/sync-alerts.ts.json+0 −66
@@ -1,66 +0,0 @@1-{2- "filePath": "src/background/sync-alerts.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "crypto-usage",7- "lineNumbers": [8- 179- ],10- "snippet": " type: \"REMOTE_PASSWORD_CHANGE\" | \"REMOTE_DELETE\" | \"DEVICE_KEY_ROTATED\";\n /** VaultEntry.id — a UUID string in this codebase (spec deviation: the\n * fix spec typed this `number`, but entry ids are `crypto.randomUUID()`).\n * Empty string for DEVICE_KEY_ROTATED (not entry-scoped). */\n entryId: string;",11- "matchedPattern": "Node randomUUID"12- }13- ],14- "lastScannedAt": "2026-06-30T22:09:44.293Z",15- "lastScannedRunId": "20260630220943-c7b8dba65193b129",16- "fileHash": "684b6178f222bfae0566e95906bd20298c836f99650b07ad4d94f4687af3d904",17- "findings": [18- {19- "severity": "MEDIUM",20- "vulnSlug": "other-alert-suppression",21- "title": "Unbounded peer-controlled alert text can suppress durable sync warnings",22- "description": "recordSyncAlerts caps the queue at 200 entries but stores raw entryTitle and deviceName strings without length or total-byte limits. Those fields are populated from synced entry titles and signed device-manifest device names controlled by paired or vouched peers; upstream sync limits still allow very large per-entry payloads and manifest frames. A malicious peer can therefore make chrome.storage.local.set write a very large alert queue or throw, and the catch block silently drops the durable alert. This weakens the user-visible warning for remote password changes, deletes, or device key rotation.",23- "lineNumbers": [24- 31,25- 34,26- 36,27- 37,28- 72,29- 73,30- 74,31- 7532- ],33- "recommendation": "Normalize alerts before merging: cap entryTitle and deviceName to a small display length, require a finite timestamp, and enforce a maximum serialized queue size. If storage still fails, persist a small generic fallback alert instead of silently losing the warning.",34- "confidence": "medium",35- "producedByRunId": "20260701075706-0feae092f70d6cf5"36- }37- ],38- "analysisHistory": [39- {40- "runId": "20260701075706-0feae092f70d6cf5",41- "investigatedAt": "2026-07-01T08:05:03.752Z",42- "durationMs": 94792,43- "agentType": "codex",44- "model": "gpt-5.5",45- "modelConfig": {46- "model": "gpt-5.5"47- },48- "agentSessionId": "019f1cae-839f-7222-b3aa-685b0bafd4d0",49- "findingCount": 1,50- "numTurns": 0.2,51- "phase": "process",52- "costUsd": 0.6968018,53- "usage": {54- "inputTokens": 65268.6,55- "outputTokens": 4124.2,56- "cacheReadInputTokens": 493465.6,57- "cacheCreationInputTokens": 058- }59- }60- ],61- "status": "analyzed",62- "gitInfo": {63- "recentCommitters": [],64- "enrichedAt": "2026-07-01T08:05:03.759Z"65- }66-}.deepsec/data/peach-browser/files/src/background/sync-trigger.ts.json+0 −46
@@ -1,46 +0,0 @@1-{2- "filePath": "src/background/sync-trigger.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 1089- ],10- "snippet": " (d.deviceType === \"android\" ||\n d.deviceType === \"ios\" ||\n d.deviceType === \"desktop\") &&\n d.trustSource === \"direct\",\n );",11- "matchedPattern": "weak cipher algorithm"12- }13- ],14- "lastScannedAt": "2026-06-30T22:09:44.023Z",15- "lastScannedRunId": "20260630220943-c7b8dba65193b129",16- "fileHash": "4f9787de036d903561df7ea224fd1da82c4e79d1588423e844ef062653a49d01",17- "findings": [],18- "analysisHistory": [19- {20- "runId": "20260630221012-35253a56b27eeb43",21- "investigatedAt": "2026-06-30T22:13:02.922Z",22- "durationMs": 160629,23- "agentType": "codex",24- "model": "gpt-5.5",25- "modelConfig": {26- "model": "gpt-5.5"27- },28- "agentSessionId": "019f1a95-32b9-7811-8111-5ad97c53c2bc",29- "findingCount": 0,30- "numTurns": 1,31- "phase": "process",32- "costUsd": 1.033294,33- "usage": {34- "inputTokens": 100208,35- "outputTokens": 6273,36- "cacheReadInputTokens": 688128,37- "cacheCreationInputTokens": 038- }39- }40- ],41- "status": "analyzed",42- "gitInfo": {43- "recentCommitters": [],44- "enrichedAt": "2026-06-30T22:13:02.930Z"45- }46-}.deepsec/data/peach-browser/files/src/background/vault.ts.json+0 −144
@@ -1,144 +0,0 @@1-{2- "filePath": "src/background/vault.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "ssrf",7- "lineNumbers": [8- 2339- ],10- "snippet": " // PCH-02-007: request HIBP response padding so response length does not\n // reveal how many suffixes a queried prefix returned.\n const res = await fetch(`https://api.pwnedpasswords.com/range/${prefix}`, {\n headers: { \"Add-Padding\": \"true\" },\n });",11- "matchedPattern": "fetch with interpolated URL (non-constant base)"12- },13- {14- "vulnSlug": "insecure-crypto",15- "lineNumbers": [16- 127,17- 140,18- 144,19- 172,20- 258,21- 295,22- 382,23- 1213,24- 1559,25- 2080,26- 2131,27- 2145,28- 2239,29- 2796,30- 2836,31- 2846,32- 285133- ],34- "snippet": " /**\n * Text size scale for body copy and entry titles. Stops at \"large\" by\n * design — XL clips dense screens (Vault, Health) inside the fixed\n * 330×480 popup surface.\n */",35- "matchedPattern": "weak cipher algorithm"36- },37- {38- "vulnSlug": "open-redirect",39- "lineNumbers": [40- 2466,41- 248142- ],43- "snippet": "\n if (\"url\" in patch) {\n nextPatch.url = patch.url?.trim() ?? \"\";\n }\n",44- "matchedPattern": "redirect URL parameter"45- },46- {47- "vulnSlug": "secret-in-log",48- "lineNumbers": [49- 199550- ],51- "snippet": " ? [\n { password: priorPassword!, changedAt: current.passwordChangedAt ?? current.createdAt },\n ...(current.passwordHistory ?? []),",52- "matchedPattern": "Secret variable in log statement"53- },54- {55- "vulnSlug": "secret-in-log",56- "lineNumbers": [57- 241158- ],59- "snippet": " ) {\n throw new Error(\"invalid passkey credential\");\n }",60- "matchedPattern": "Secret variable in error response"61- },62- {63- "vulnSlug": "secret-in-log",64- "lineNumbers": [65- 252466- ],67- "snippet": " const sanitized = sanitizePasskey(patch.passkey);\n if (!sanitized) throw new Error(\"invalid passkey credential\");\n nextPatch.passkey = sanitized;",68- "matchedPattern": "Secret variable in error response"69- },70- {71- "vulnSlug": "unverified-lookup",72- "lineNumbers": [73- 227874- ],75- "snippet": "\nexport function getEntryById(id: string): VaultEntry | undefined {\n return entries.find((entry) => entry.id === id && !entry.deletedAt);\n}\n",76- "matchedPattern": "DB lookup by ID without ownership check in next 15 lines"77- },78- {79- "vulnSlug": "unsafe-deserialization",80- "lineNumbers": [81- 1505,82- 151183- ],84- "snippet": " let parsedSlotB: StoredVault | null = null;\n try {\n const rawA = JSON.parse(params.slotA) as unknown;\n if (!hasValidVaultSchema(rawA)) {\n return { ok: false, reason: \"invalid_slot_format\" };",85- "matchedPattern": "JSON.parse of request input (verify schema validation)"86- },87- {88- "vulnSlug": "crypto-usage",89- "lineNumbers": [90- 221,91- 263,92- 321,93- 361,94- 410,95- 1648,96- 1742,97- 1907,98- 282099- ],100- "snippet": " // the public API, not a security choice. Do not migrate to SHA-256 here.\n const encoder = new TextEncoder();\n const hashBuffer = await crypto.subtle.digest(\"SHA-1\", encoder.encode(password));\n const hashHex = Array.from(new Uint8Array(hashBuffer))\n .map((b) => b.toString(16).padStart(2, \"0\"))",101- "matchedPattern": "Web Crypto API (crypto.subtle.*), Node randomUUID"102- },103- {104- "vulnSlug": "secrets-plaintext-exposure",105- "lineNumbers": [106- 2342107- ],108- "snippet": " const slotB = await loadSlot(SLOT_B);\n if (!slotB) throw new Error(\"missing slot B\");\n\n // Decoy plaintext always reports duressConfigured=false — the decoy user\n // sees a clean slate. persistSlotB only runs in decoy mode, which only",109- "matchedPattern": "plaintext reference inside logger/response/error/span call"110- }111- ],112- "lastScannedAt": "2026-06-30T22:09:44.318Z",113- "lastScannedRunId": "20260630220943-c7b8dba65193b129",114- "fileHash": "fa76d6c230f3a86ab94e1198e2c43c982c60ebbb9f0a2d162b9eab0f4ae6ecc0",115- "findings": [],116- "analysisHistory": [117- {118- "runId": "20260701075706-0feae092f70d6cf5",119- "investigatedAt": "2026-07-01T08:03:23.168Z",120- "durationMs": 73553.4,121- "agentType": "codex",122- "model": "gpt-5.5",123- "modelConfig": {124- "model": "gpt-5.5"125- },126- "agentSessionId": "019f1cae-8382-7992-945a-8c859f938715",127- "findingCount": 0,128- "numTurns": 0.2,129- "phase": "process",130- "costUsd": 0.5257544000000001,131- "usage": {132- "inputTokens": 36869.2,133- "outputTokens": 3475,134- "cacheReadInputTokens": 474316.8,135- "cacheCreationInputTokens": 0136- }137- }138- ],139- "status": "analyzed",140- "gitInfo": {141- "recentCommitters": [],142- "enrichedAt": "2026-07-01T08:03:23.176Z"143- }144-}.deepsec/data/peach-browser/files/src/content/content-ml-detector.ts.json+0 −51
@@ -1,51 +0,0 @@1-{2- "filePath": "src/content/content-ml-detector.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 76,9- 128,10- 129,11- 130,12- 131,13- 13214- ],15- "snippet": " \"search\",\n \"url\",\n ].includes(inputType)\n ? 0\n : 1,",16- "matchedPattern": "weak cipher algorithm"17- }18- ],19- "lastScannedAt": "2026-06-30T22:09:44.021Z",20- "lastScannedRunId": "20260630220943-c7b8dba65193b129",21- "fileHash": "ce59f170cdcb3d4c3e66306a1eb69ffe8bbced67b42e4b231d698ea7051c2d97",22- "findings": [],23- "analysisHistory": [24- {25- "runId": "20260701075706-0feae092f70d6cf5",26- "investigatedAt": "2026-07-01T08:02:44.958Z",27- "durationMs": 65810.8,28- "agentType": "codex",29- "model": "gpt-5.5",30- "modelConfig": {31- "model": "gpt-5.5"32- },33- "agentSessionId": "019f1cae-8397-7eb2-959b-38434905ef6f",34- "findingCount": 0,35- "numTurns": 0.2,36- "phase": "process",37- "costUsd": 0.4504416,38- "usage": {39- "inputTokens": 53123.2,40- "outputTokens": 2947.2,41- "cacheReadInputTokens": 192819.2,42- "cacheCreationInputTokens": 043- }44- }45- ],46- "status": "analyzed",47- "gitInfo": {48- "recentCommitters": [],49- "enrichedAt": "2026-07-01T08:02:44.965Z"50- }51-}.deepsec/data/peach-browser/files/src/content/detect.ts.json+0 −67
@@ -1,67 +0,0 @@1-{2- "filePath": "src/content/detect.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 151,9- 282,10- 290,11- 294,12- 323,13- 38414- ],15- "snippet": " const form = identifierEl.closest(\"form\") ?? document.body;\n const hasRegistrationFields = Array.from(form.querySelectorAll<HTMLInputElement>(\"input\")).some(el => {\n if (![\"\", \"text\", \"tel\", \"number\"].includes(el.type.toLowerCase())) return false;\n if (!isVisible(el)) return false;\n return /\\b(first.?name|last.?name|fname|lname|given|family|phone|mobile|company|organization|address)\\b/i",16- "matchedPattern": "weak cipher algorithm"17- }18- ],19- "lastScannedAt": "2026-06-30T22:09:44.021Z",20- "lastScannedRunId": "20260630220943-c7b8dba65193b129",21- "fileHash": "2e3b1db37a320060b69b379aef59832b4643ebbbc33f7fcd50ff015165d228b9",22- "findings": [23- {24- "severity": "BUG",25- "vulnSlug": "other-logic-bug",26- "title": "Deep selector walk duplicates fields and can amplify page-controlled DOM cost",27- "description": "querySelectorAllDeep iterates every descendant of the root, then appends that descendant's matching descendants. For a normal document tree, the same input is returned once per ancestor, so detectLoginFields processes duplicated fields and password counts can be inflated. The repeated subtree queries also make detection much more expensive on large or deeply nested page-controlled DOMs, which can degrade autofill behavior on arbitrary pages.",28- "lineNumbers": [29- 68,30- 73,31- 75,32- 8233- ],34- "recommendation": "Replace the helper with a true depth-first walk that visits each element once, pushes the element itself when it matches, descends into children and open shadow roots, and/or deduplicates by element before returning.",35- "confidence": "high",36- "producedByRunId": "20260701075706-0feae092f70d6cf5"37- }38- ],39- "analysisHistory": [40- {41- "runId": "20260701075706-0feae092f70d6cf5",42- "investigatedAt": "2026-07-01T08:02:44.965Z",43- "durationMs": 65810.8,44- "agentType": "codex",45- "model": "gpt-5.5",46- "modelConfig": {47- "model": "gpt-5.5"48- },49- "agentSessionId": "019f1cae-8397-7eb2-959b-38434905ef6f",50- "findingCount": 1,51- "numTurns": 0.2,52- "phase": "process",53- "costUsd": 0.4504416,54- "usage": {55- "inputTokens": 53123.2,56- "outputTokens": 2947.2,57- "cacheReadInputTokens": 192819.2,58- "cacheCreationInputTokens": 059- }60- }61- ],62- "status": "analyzed",63- "gitInfo": {64- "recentCommitters": [],65- "enrichedAt": "2026-07-01T08:02:44.973Z"66- }67-}.deepsec/data/peach-browser/files/src/content/fill.ts.json+0 −46
@@ -1,46 +0,0 @@1-{2- "filePath": "src/content/fill.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 809- ],10- "snippet": " ? window.HTMLTextAreaElement.prototype\n : window.HTMLInputElement.prototype;\n const nativeValueSetter = Object.getOwnPropertyDescriptor(proto, \"value\")?.set;\n nativeValueSetter?.call(el, value);\n el.dispatchEvent(new Event(\"input\", { bubbles: true }));",11- "matchedPattern": "weak cipher algorithm"12- }13- ],14- "lastScannedAt": "2026-06-30T22:09:44.021Z",15- "lastScannedRunId": "20260630220943-c7b8dba65193b129",16- "fileHash": "4e7dfc6f637cb015a7cc74f74974dd97a1e73732574b99b070153668604ad447",17- "findings": [],18- "analysisHistory": [19- {20- "runId": "20260701075706-0feae092f70d6cf5",21- "investigatedAt": "2026-07-01T08:02:44.974Z",22- "durationMs": 65810.8,23- "agentType": "codex",24- "model": "gpt-5.5",25- "modelConfig": {26- "model": "gpt-5.5"27- },28- "agentSessionId": "019f1cae-8397-7eb2-959b-38434905ef6f",29- "findingCount": 0,30- "numTurns": 0.2,31- "phase": "process",32- "costUsd": 0.4504416,33- "usage": {34- "inputTokens": 53123.2,35- "outputTokens": 2947.2,36- "cacheReadInputTokens": 192819.2,37- "cacheCreationInputTokens": 038- }39- }40- ],41- "status": "analyzed",42- "gitInfo": {43- "recentCommitters": [],44- "enrichedAt": "2026-07-01T08:02:44.982Z"45- }46-}.deepsec/data/peach-browser/files/src/content/identity-helper.ts.json+0 −47
@@ -1,47 +0,0 @@1-{2- "filePath": "src/content/identity-helper.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 21,9- 5110- ],11- "snippet": " .join(\" \")\n .toLowerCase()\n .includes(q);\n}\n",12- "matchedPattern": "weak cipher algorithm"13- }14- ],15- "lastScannedAt": "2026-06-30T22:09:44.020Z",16- "lastScannedRunId": "20260630220943-c7b8dba65193b129",17- "fileHash": "b315338e1fd4ef5f562aa33557e5050fd8ca68110e911b0f4da4b680e58cdd92",18- "findings": [],19- "analysisHistory": [20- {21- "runId": "20260701075706-0feae092f70d6cf5",22- "investigatedAt": "2026-07-01T08:02:44.982Z",23- "durationMs": 65810.8,24- "agentType": "codex",25- "model": "gpt-5.5",26- "modelConfig": {27- "model": "gpt-5.5"28- },29- "agentSessionId": "019f1cae-8397-7eb2-959b-38434905ef6f",30- "findingCount": 0,31- "numTurns": 0.2,32- "phase": "process",33- "costUsd": 0.4504416,34- "usage": {35- "inputTokens": 53123.2,36- "outputTokens": 2947.2,37- "cacheReadInputTokens": 192819.2,38- "cacheCreationInputTokens": 039- }40- }41- ],42- "status": "analyzed",43- "gitInfo": {44- "recentCommitters": [],45- "enrichedAt": "2026-07-01T08:02:44.993Z"46- }47-}.deepsec/data/peach-browser/files/src/content/index.ts.json+0 −87
@@ -1,87 +0,0 @@1-{2- "filePath": "src/content/index.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "auth-bypass",7- "lineNumbers": [8- 12319- ],10- "snippet": "// webAuthenticationProxy, so without a banner the user has no signal that\n// Peach is the source.\n// On Chrome with a same-browser entry: skip — webAuthenticationProxy handles\n// the page's own credentials.get transparently and the popup approval view\n// already surfaces it. A banner here would just be noise.",11- "matchedPattern": "auth skip/bypass"12- },13- {14- "vulnSlug": "xss",15- "lineNumbers": [16- 23117- ],18- "snippet": " ? allEntries.filter(e => e.title.toLowerCase().includes(q) || (e.username ?? \"\").toLowerCase().includes(q))\n : allEntries;\n list.innerHTML = \"\";\n if (filtered.length === 0) {\n const em = document.createElement(\"div\");",19- "matchedPattern": "innerHTML assignment"20- },21- {22- "vulnSlug": "insecure-crypto",23- "lineNumbers": [24- 229,25- 32326- ],27- "snippet": " const q = query.toLowerCase();\n const filtered = q\n ? allEntries.filter(e => e.title.toLowerCase().includes(q) || (e.username ?? \"\").toLowerCase().includes(q))\n : allEntries;\n list.innerHTML = \"\";",28- "matchedPattern": "weak cipher algorithm"29- },30- {31- "vulnSlug": "dangerous-html",32- "lineNumbers": [33- 23134- ],35- "snippet": " const filtered = q\n ? allEntries.filter(e => e.title.toLowerCase().includes(q) || (e.username ?? \"\").toLowerCase().includes(q))\n : allEntries;\n list.innerHTML = \"\";\n if (filtered.length === 0) {\n const em = document.createElement(\"div\");\n em.className = \"em\";\n em.textContent = query ? \"no matches\" : \"vault is empty\";",36- "matchedPattern": "dangerouslySetInnerHTML from URL/request params (HIGH RISK)"37- },38- {39- "vulnSlug": "postmessage-origin",40- "lineNumbers": [41- 117042- ],43- "snippet": "// so we know it's safe to allow autofill again.\nwindow.addEventListener(\"message\", (e: MessageEvent) => {\n // Reject postMessages from cross-frame senders — only the same window's\n // injected toast may flip this flag.\n if (e.source !== window) return;\n const d = e.data as { source?: string; type?: string } | undefined;",44- "matchedPattern": "postMessage handler WITHOUT origin validation"45- },46- {47- "vulnSlug": "postmessage-origin",48- "lineNumbers": [49- 119550- ],51- "snippet": "\n window.addEventListener(\"message\", (event: MessageEvent) => {\n if (event.source !== window) return;\n const data = event.data as { source?: string; type?: string; rpId?: string; rpName?: string; userName?: string; userDisplayName?: string } | undefined;\n if (!data || data.source !== \"peach-passkey-firefox\") return;\n if (detectBrowser() !== \"firefox\") return;",52- "matchedPattern": "postMessage handler WITHOUT origin validation"53- }54- ],55- "lastScannedAt": "2026-06-30T22:09:44.141Z",56- "lastScannedRunId": "20260630220943-c7b8dba65193b129",57- "fileHash": "fcd52e25eb3287e5243364a8dd88313658cd55de9a224aa75013b96128881aa9",58- "findings": [],59- "analysisHistory": [60- {61- "runId": "20260701075706-0feae092f70d6cf5",62- "investigatedAt": "2026-07-01T08:02:57.081Z",63- "durationMs": 68381,64- "agentType": "codex",65- "model": "gpt-5.5",66- "modelConfig": {67- "model": "gpt-5.5"68- },69- "agentSessionId": "019f1cae-839f-7433-8a98-cb5a0fbcc6ea",70- "findingCount": 0,71- "numTurns": 0.2,72- "phase": "process",73- "costUsd": 0.5055938,74- "usage": {75- "inputTokens": 39749,76- "outputTokens": 3318,77- "cacheReadInputTokens": 414617.6,78- "cacheCreationInputTokens": 079- }80- }81- ],82- "status": "analyzed",83- "gitInfo": {84- "recentCommitters": [],85- "enrichedAt": "2026-07-01T08:02:57.088Z"86- }87-}.deepsec/data/peach-browser/files/src/content/inject.ts.json+0 −391
@@ -1,391 +0,0 @@1-{2- "filePath": "src/content/inject.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "xss",7- "lineNumbers": [8- 268,9- 350,10- 357,11- 548,12- 1610,13- 1616,14- 1623,15- 1841,16- 1945,17- 2017,18- 2084,19- 2122,20- 2277,21- 2334,22- 2411,23- 2442,24- 2559,25- 2562,26- 2588,27- 2618,28- 2681,29- 2920,30- 2930,31- 2966,32- 3213,33- 3233,34- 3513,35- 3569,36- 3785,37- 3966,38- 4172,39- 433140- ],41- "snippet": " // secondsUntilClear is a typed number — safe to interpolate; no page-derived\n // string ever reaches this markup.\n toast.innerHTML = `\n <div style=\"font-style: italic; font-family: 'Cormorant Garamond', serif; font-size: 13px; margin-bottom: 2px;\">\n 2FA code copied",42- "matchedPattern": "innerHTML assignment"43- },44- {45- "vulnSlug": "insecure-crypto",46- "lineNumbers": [47- 160,48- 424,49- 444,50- 1591,51- 2291,52- 2304,53- 2316,54- 2333,55- 2472,56- 2481,57- 2491,58- 2665,59- 2730,60- 2746,61- 2987,62- 3291,63- 3459,64- 3635,65- 446366- ],67- "snippet": " \"id\", \"identity\", \"secure\", \"portal\", \"my\", \"am\", \"idp\", \"connect\", \"oauth\",\n \"oidc\", \"federation\", \"saml\", \"cas\", \"ldap\"];\n const loginSubdomain = parts.length > 2 && AUTH_PREFIXES.includes(parts[0]!.toLowerCase());\n const rootName = parts.length >= 2 ? parts[parts.length - 2]! : parts[0]!;\n const title = rootName.charAt(0).toUpperCase() + rootName.slice(1);",68- "matchedPattern": "weak cipher algorithm"69- },70- {71- "vulnSlug": "dangerous-html",72- "lineNumbers": [73- 26874- ],75- "snippet": "\n // secondsUntilClear is a typed number — safe to interpolate; no page-derived\n // string ever reaches this markup.\n toast.innerHTML = `\n <div style=\"font-style: italic; font-family: 'Cormorant Garamond', serif; font-size: 13px; margin-bottom: 2px;\">\n 2FA code copied\n </div>\n <div style=\"opacity: 0.6; font-size: 10px;\">",76- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"77- },78- {79- "vulnSlug": "dangerous-html",80- "lineNumbers": [81- 35082- ],83- "snippet": " regenBtn.type = \"button\";\n regenBtn.setAttribute(\"aria-label\", \"Regenerate password\");\n regenBtn.title = \"Regenerate\";\n regenBtn.innerHTML = `<svg width=\"13\" height=\"13\" viewBox=\"0 0 13 13\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.1\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M11 2.5A5.5 5.5 0 1 0 12.5 7\"/><path d=\"M9.5 2.5H11V1\"/></svg>`;\n\n const copyBtn = displayRow.appendChild(document.createElement(\"button\"));\n copyBtn.className = \"pwgen-icon-btn\";\n copyBtn.type = \"button\";",84- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"85- },86- {87- "vulnSlug": "dangerous-html",88- "lineNumbers": [89- 35790- ],91- "snippet": " copyBtn.type = \"button\";\n copyBtn.setAttribute(\"aria-label\", \"Copy password\");\n copyBtn.title = \"Copy\";\n copyBtn.innerHTML = `<svg width=\"12\" height=\"12\" viewBox=\"0 0 14 14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"4\" y=\"4\" width=\"8\" height=\"9\" rx=\"1.5\"/><path d=\"M10 4V2.5A1.5 1.5 0 0 0 8.5 1h-6A1.5 1.5 0 0 0 1 2.5v7A1.5 1.5 0 0 0 2.5 11H4\"/></svg>`;\n\n const controls = root.appendChild(document.createElement(\"div\"));\n controls.className = \"pwgen-controls-row\";\n const slider = controls.appendChild(document.createElement(\"input\"));",92- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"93- },94- {95- "vulnSlug": "dangerous-html",96- "lineNumbers": [97- 54898- ],99- "snippet": " button.type = \"button\";\n button.setAttribute(\"aria-label\", \"Peach autofill\");\n button.title = \"Fill with Peach\";\n button.innerHTML = peachIconSvg();\n // position: fixed is load-bearing — not absolute. Absolute positioning\n // inherits the nearest positioned ancestor's box, which on sites with\n // overflow:hidden or sticky parents clips or offsets the icon. Fixed\n // anchors to the viewport and escapes all parent layout.",100- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"101- },102- {103- "vulnSlug": "dangerous-html",104- "lineNumbers": [105- 1610106- ],107- "snippet": " const icon = document.createElement(\"div\");\n icon.className = `entry-icon${isNew ? \" is-new\" : \"\"}`;\n if (isNew) {\n icon.innerHTML = plusSvgStr();\n } else {\n // Use the generic key glyph for all entries in the page-injected selector.\n // Avoids disclosing vault domains to any third party and avoids needing\n // to expose the browser favicon API to page context via web_accessible_resources.",108- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"109- },110- {111- "vulnSlug": "dangerous-html",112- "lineNumbers": [113- 1616114- ],115- "snippet": " // Avoids disclosing vault domains to any third party and avoids needing\n // to expose the browser favicon API to page context via web_accessible_resources.\n void url;\n icon.innerHTML = keySvgStr();\n }\n return icon;\n }\n",116- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"117- },118- {119- "vulnSlug": "dangerous-html",120- "lineNumbers": [121- 1623122- ],123- "snippet": "\n function renderIdentityHelper() {\n currentMode = \"identity-helper\";\n container.innerHTML = \"\";\n focusedIndex = -1;\n searchInput = null;\n\n const fallbackKind: IdentityKind = options.identityKind ?? \"username\";",124- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"125- },126- {127- "vulnSlug": "dangerous-html",128- "lineNumbers": [129- 1841130- ],131- "snippet": " */\n function renderSecondaryIdentityStep(stepKind: \"email\" | \"username\", firstValue: string) {\n currentMode = \"identity-helper\";\n container.innerHTML = \"\";\n\n const wrap = container.appendChild(document.createElement(\"div\"));\n wrap.className = \"gen-view\";\n",132- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"133- },134- {135- "vulnSlug": "dangerous-html",136- "lineNumbers": [137- 1945138- ],139- "snippet": " }\n\n function renderGenerator(filledEmail: string, filledUsername?: string) {\n container.innerHTML = \"\";\n const sitePolicy = detectPasswordPolicy();\n\n const wrap = container.appendChild(document.createElement(\"div\"));\n wrap.className = \"gen-view\";",140- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"141- },142- {143- "vulnSlug": "dangerous-html",144- "lineNumbers": [145- 2017146- ],147- "snippet": " }\n\n function renderSaveOffer(filledEmail: string, filledPassword: string, filledUsername?: string) {\n container.innerHTML = \"\";\n const domain = options.domain ?? location.hostname.replace(/^www\\./, \"\");\n\n const wrap = container.appendChild(document.createElement(\"div\"));\n wrap.className = \"save-offer\";",148- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"149- },150- {151- "vulnSlug": "dangerous-html",152- "lineNumbers": [153- 2084154- ],155- "snippet": " }\n\n function renderList() {\n container.innerHTML = \"\";\n focusedIndex = -1;\n searchInput = null;\n currentMode = \"list\";\n",156- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"157- },158- {159- "vulnSlug": "dangerous-html",160- "lineNumbers": [161- 2122162- ],163- "snippet": " backBtn.type = \"button\";\n backBtn.setAttribute(\"aria-label\", \"Back to suggested entry\");\n backBtn.title = \"Back to suggested entry\";\n backBtn.innerHTML = backSvgStr();\n backBtn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n if (silverPlatterOrigin) renderSilverPlatter(silverPlatterOrigin);\n });",164- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"165- },166- {167- "vulnSlug": "dangerous-html",168- "lineNumbers": [169- 2277170- ],171- "snippet": " const collapseBtn = collapseRow.appendChild(document.createElement(\"button\"));\n collapseBtn.type = \"button\";\n collapseBtn.className = \"identity-collapse-btn\";\n collapseBtn.innerHTML = `<svg width=\"10\" height=\"6\" viewBox=\"0 0 10 6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M1 1L5 5L9 1\"/></svg>`;\n collapseBtn.addEventListener(\"mousedown\", (e) => {\n e.preventDefault();\n collapsed = !collapsed;\n collapseBtn.classList.toggle(\"is-collapsed\", collapsed);",172- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"173- },174- {175- "vulnSlug": "dangerous-html",176- "lineNumbers": [177- 2334178- ],179- "snippet": " function render() {\n const q = inp.value.toLowerCase();\n const filtered = typed.filter(s => !q || s.value.toLowerCase().includes(q)).slice(0, 8);\n rows.innerHTML = \"\";\n if (!filtered.length) { hideList(); return; }\n for (const s of filtered) {\n const row = rows.appendChild(document.createElement(\"button\"));\n row.type = \"button\"; row.className = \"identity-suggest-row\";",180- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"181- },182- {183- "vulnSlug": "dangerous-html",184- "lineNumbers": [185- 2411186- ],187- "snippet": "\n async function showCreateForm() {\n currentMode = \"create\";\n container.innerHTML = \"\";\n\n // Guard: the service worker may lock the vault between when the selector\n // opens and when the user clicks \"New entry\". Show the inline unlock form\n // instead of silently failing on save.",188- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"189- },190- {191- "vulnSlug": "dangerous-html",192- "lineNumbers": [193- 2442194- ],195- "snippet": " // Header\n const head = document.createElement(\"div\"); head.className = \"create-head\";\n const backBtn = document.createElement(\"button\"); backBtn.className = \"back-btn\";\n backBtn.innerHTML = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"7\" height=\"12\" viewBox=\"0 0 8 14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" stroke-linecap=\"round\"><path d=\"M6 1L1.5 7L6 13\"/></svg>`;\n backBtn.addEventListener(\"click\", () => { currentMode = \"list\"; renderList(); });\n const lbl = document.createElement(\"div\"); lbl.className = \"create-label\"; lbl.textContent = \"New entry\";\n head.append(backBtn, lbl);\n",196- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"197- },198- {199- "vulnSlug": "dangerous-html",200- "lineNumbers": [201- 2559202- ],203- "snippet": "\n function showSuccess(title: string) {\n currentMode = \"success\";\n container.innerHTML = \"\";\n const wrap = document.createElement(\"div\"); wrap.className = \"success-wrap\";\n const ring = document.createElement(\"div\"); ring.className = \"success-ring\";\n ring.innerHTML = checkSvgStr();\n const t = document.createElement(\"div\"); t.className = \"success-text\"; t.textContent = `${title} saved`;",204- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"205- },206- {207- "vulnSlug": "dangerous-html",208- "lineNumbers": [209- 2562210- ],211- "snippet": " container.innerHTML = \"\";\n const wrap = document.createElement(\"div\"); wrap.className = \"success-wrap\";\n const ring = document.createElement(\"div\"); ring.className = \"success-ring\";\n ring.innerHTML = checkSvgStr();\n const t = document.createElement(\"div\"); t.className = \"success-text\"; t.textContent = `${title} saved`;\n const s = document.createElement(\"div\"); s.className = \"success-sub\"; s.textContent = sOrigin;\n wrap.append(ring, t, s);\n container.appendChild(wrap);",212- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"213- },214- {215- "vulnSlug": "dangerous-html",216- "lineNumbers": [217- 2588218- ],219- "snippet": " }\n\n function renderSilverPlatter(entry: SelectorEntry) {\n container.innerHTML = \"\";\n searchInput = null;\n focusedIndex = -1;\n currentMode = \"silver-platter\";\n silverPlatterOrigin = entry;",220- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"221- },222- {223- "vulnSlug": "dangerous-html",224- "lineNumbers": [225- 2618226- ],227- "snippet": " searchBtn.type = \"button\";\n searchBtn.setAttribute(\"aria-label\", \"Search for a different entry\");\n searchBtn.title = \"Search for a different entry\";\n searchBtn.innerHTML = searchSvgStr();\n searchBtn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n query = \"\";\n renderList();",228- "matchedPattern": "dangerouslySetInnerHTML from URL/request params (HIGH RISK)"229- },230- {231- "vulnSlug": "dangerous-html",232- "lineNumbers": [233- 2681234- ],235- "snippet": " if (fillBtn.disabled) return;\n fillBtn.disabled = true;\n fillBtn.classList.add(\"filled\");\n fillBtn.innerHTML = checkSvgStr();\n // Fire the fill, then exit via the standard exit animation. The fill\n // itself is async (handled in index.ts) — by 400ms the form is filled\n // and the popup begins its exit. Total dwell from click to host\n // removal: 400ms + 130ms (exit animation) = 530ms.",236- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"237- },238- {239- "vulnSlug": "dangerous-html",240- "lineNumbers": [241- 2920242- ],243- "snippet": " */\n async function renderInlineEdit(summary: SelectorEntry) {\n currentMode = \"edit\";\n container.innerHTML = \"\";\n const wrap = document.createElement(\"div\");\n wrap.className = \"create-form-wrap\";\n\n const head = document.createElement(\"div\");",244- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"245- },246- {247- "vulnSlug": "dangerous-html",248- "lineNumbers": [249- 2930250- ],251- "snippet": " back.className = \"back-btn\";\n back.type = \"button\";\n back.title = \"Cancel\";\n back.innerHTML = `<svg width=\"11\" height=\"11\" viewBox=\"0 0 11 11\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" stroke-linecap=\"round\"><path d=\"M7 2L3 5.5L7 9\"/></svg>`;\n back.addEventListener(\"click\", () => renderSilverPlatter(summary));\n head.appendChild(back);\n const headLbl = document.createElement(\"div\");\n headLbl.className = \"create-label\";",252- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"253- },254- {255- "vulnSlug": "dangerous-html",256- "lineNumbers": [257- 2966258- ],259- "snippet": " loading.textContent = \"couldn't load this entry. try again from the popup.\";\n return;\n }\n body.innerHTML = \"\";\n\n function field(label: string, value: string, type: \"text\" | \"password\" = \"text\"): { g: HTMLDivElement; inp: HTMLInputElement } {\n const group = document.createElement(\"div\");\n group.className = \"f-group\";",260- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"261- },262- {263- "vulnSlug": "dangerous-html",264- "lineNumbers": [265- 3213266- ],267- "snippet": " }\n\n function renderLocked() {\n container.innerHTML = \"\";\n\n // Domain header (same as list view)\n const domHdr = document.createElement(\"div\");\n domHdr.className = \"domain-header\";",268- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"269- },270- {271- "vulnSlug": "dangerous-html",272- "lineNumbers": [273- 3233274- ],275- "snippet": " // Icon + label row\n const head = document.createElement(\"div\"); head.className = \"locked-head\";\n const icon = document.createElement(\"div\"); icon.className = \"locked-icon\";\n icon.innerHTML = keySvgStr();\n const title = document.createElement(\"div\"); title.className = \"locked-title\";\n title.textContent = \"unlock to autofill\";\n head.append(icon, title);\n",276- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"277- },278- {279- "vulnSlug": "dangerous-html",280- "lineNumbers": [281- 3513282- ],283- "snippet": " }\n\n function render() {\n container.innerHTML = \"\";\n\n const domHdr = document.createElement(\"div\");\n domHdr.className = \"domain-header\";\n const headText = document.createElement(\"div\");",284- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"285- },286- {287- "vulnSlug": "dangerous-html",288- "lineNumbers": [289- 3569290- ],291- "snippet": "\n const icon = document.createElement(\"div\");\n icon.className = \"entry-icon\";\n icon.innerHTML = keySvgStr();\n row.appendChild(icon);\n const info = document.createElement(\"div\");\n info.className = \"entry-info\";\n const title = document.createElement(\"div\");",292- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"293- },294- {295- "vulnSlug": "dangerous-html",296- "lineNumbers": [297- 3785298- ],299- "snippet": " const header = document.createElement(\"div\");\n header.className = \"gate-header\";\n const icon = document.createElement(\"span\");\n icon.innerHTML = peachIconSvg();\n const domainEl = document.createElement(\"span\");\n domainEl.className = \"gate-domain\";\n domainEl.textContent = domain;\n header.append(icon, domainEl);",300- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"301- },302- {303- "vulnSlug": "dangerous-html",304- "lineNumbers": [305- 3966306- ],307- "snippet": " const header = document.createElement(\"div\");\n header.className = \"offer-header\";\n const icon = document.createElement(\"span\");\n icon.innerHTML = peachIconSvg();\n const domainEl = document.createElement(\"span\");\n domainEl.className = \"offer-domain\";\n domainEl.textContent = domain;\n header.append(icon, domainEl);",308- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"309- },310- {311- "vulnSlug": "dangerous-html",312- "lineNumbers": [313- 4172314- ],315- "snippet": " const header = document.createElement(\"div\");\n header.className = \"avail-header\";\n const icon = document.createElement(\"span\");\n icon.innerHTML = peachIconSvg();\n const domainEl = document.createElement(\"span\");\n domainEl.className = \"avail-domain\";\n domainEl.textContent = domain;\n header.append(icon, domainEl);",316- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"317- },318- {319- "vulnSlug": "dangerous-html",320- "lineNumbers": [321- 4331322- ],323- "snippet": " if (fillBtn.disabled) return;\n fillBtn.disabled = true;\n fillBtn.classList.add(\"filled\");\n fillBtn.innerHTML = checkSvgStr();\n const resp = await chrome.runtime.sendMessage({\n type: \"GET_ENTRY_FOR_FILL\",\n // MV-1: explicit user click to fill this card's details.\n payload: { id: card.id, explicitSummon: true },",324- "matchedPattern": "dangerouslySetInnerHTML from fetched data (MEDIUM RISK)"325- },326- {327- "vulnSlug": "unverified-lookup",328- "lineNumbers": [329- 236330- ],331- "snippet": "export function showTotpCopyToast(secondsUntilClear: number): void {\n document.getElementById(\"peach-totp-toast\")?.remove();\n if (totpToastDismissTimer) {\n clearTimeout(totpToastDismissTimer);\n totpToastDismissTimer = null;",332- "matchedPattern": "DB lookup by ID without ownership check in next 15 lines"333- },334- {335- "vulnSlug": "non-atomic-read-delete",336- "lineNumbers": [337- 3436338- ],339- "snippet": " // Idempotent: dismiss() can be called from multiple paths (Escape,\n // outside click, entry select, fill success, host page nav). The exit\n // animation must only play once and host.remove() must run exactly once.\n if (exitScheduled) return;\n exitScheduled = true;\n // Detach listeners synchronously so nothing can re-trigger during the",340- "matchedPattern": "Read-then-modify without transaction — potential TOCTOU race"341- }342- ],343- "lastScannedAt": "2026-06-30T22:09:44.246Z",344- "lastScannedRunId": "20260630220943-c7b8dba65193b129",345- "fileHash": "14e6421d2b636aab5dd74fa6fd11b447cd6b326692efe44a48626caa2f229c93",346- "findings": [347- {348- "severity": "HIGH",349- "vulnSlug": "other-user-gesture-bypass",350- "title": "Synthetic keydown can fill TOTP codes without user action",351- "description": "showTOTPSelector registers a document-level keydown handler but does not reject untrusted KeyboardEvents. When the selector is open, a page script can dispatch an untrusted Enter key event; handleKeyDown then calls choose() and the caller in src/content/index.ts fetches GET_ENTRY_FOR_FILL and writes the current TOTP code into the page input. Because the TOTP selector can be opened from focus on a detected OTP field, malicious script running on a matching origin, such as an XSS, can extract the current 2FA code from an unlocked vault without a real user gesture.",352- "lineNumbers": [353- 3599,354- 3606,355- 3608,356- 3638357- ],358- "recommendation": "Mirror the main autofill selector guard by ignoring !e.isTrusted before handling Enter, Arrow, Escape, or Tab in the TOTP selector. Consider requiring a recent trusted activation before any TOTP fill path fetches or writes the code.",359- "confidence": "high",360- "producedByRunId": "20260701075706-0feae092f70d6cf5"361- }362- ],363- "analysisHistory": [364- {365- "runId": "20260701075706-0feae092f70d6cf5",366- "investigatedAt": "2026-07-01T08:02:57.073Z",367- "durationMs": 68381,368- "agentType": "codex",369- "model": "gpt-5.5",370- "modelConfig": {371- "model": "gpt-5.5"372- },373- "agentSessionId": "019f1cae-839f-7433-8a98-cb5a0fbcc6ea",374- "findingCount": 1,375- "numTurns": 0.2,376- "phase": "process",377- "costUsd": 0.5055938,378- "usage": {379- "inputTokens": 39749,380- "outputTokens": 3318,381- "cacheReadInputTokens": 414617.6,382- "cacheCreationInputTokens": 0383- }384- }385- ],386- "status": "analyzed",387- "gitInfo": {388- "recentCommitters": [],389- "enrichedAt": "2026-07-01T08:02:57.081Z"390- }391-}.deepsec/data/peach-browser/files/src/content/passkeys-firefox-relay.ts.json+0 −77
@@ -1,77 +0,0 @@1-{2- "filePath": "src/content/passkeys-firefox-relay.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "postmessage-origin",7- "lineNumbers": [8- 319- ],10- "snippet": "\nwindow.addEventListener(\"message\", async (event: MessageEvent) => {\n if (event.source !== window) return;\n const data = event.data as\n | { type?: string; id?: string; payload?: unknown }\n | undefined;",11- "matchedPattern": "postMessage handler WITHOUT origin validation"12- }13- ],14- "lastScannedAt": "2026-06-30T22:09:44.141Z",15- "lastScannedRunId": "20260630220943-c7b8dba65193b129",16- "fileHash": "11a9db7ef6328cf42631de3be79dcc22d853b2babdc7d5ca6864a10a70c95283",17- "findings": [18- {19- "severity": "HIGH",20- "vulnSlug": "acl-check",21- "title": "Page scripts can forge Firefox passkey approval responses",22- "description": "The relay accepts any same-window PEACH_PASSKEY_REQUEST and forwards allowlisted payloads to the background. Because PASSKEY_AUTH_RESPONSE is in that allowlist, arbitrary page JavaScript can send approval messages through the extension-world relay. The background treats this message as the user's passkey approval, and the Firefox proxy approval request IDs are low-entropy timestamp-derived numbers in background/passkey.ts, making brute-force approval during the 60 second window practical. This bypasses Peach's user approval step for a stored passkey when the vault is already unlocked.",23- "lineNumbers": [24- 19,25- 28,26- 31,27- 5828- ],29- "recommendation": "Remove PASSKEY_AUTH_RESPONSE from the page-facing relay. Only extension UI/content code should send approval responses directly, and the background should require an extension-page sender or an unguessable per-request approval token. Use cryptographically random approval IDs rather than timestamp-derived numeric IDs.",30- "confidence": "high",31- "producedByRunId": "20260701075706-0feae092f70d6cf5"32- },33- {34- "severity": "HIGH",35- "vulnSlug": "acl-check",36- "title": "Page scripts can write passkey metadata into the vault",37- "description": "The relay also exposes INITIATE_FIREFOX_PASSKEY_CREATE and COMPLETE_FIREFOX_PASSKEY_CREATE to arbitrary page JavaScript. The background completion path stores the supplied passkey metadata when the vault is unlocked and does not verify that the sender is the popup, that the requestId matches a pending Firefox request, or that the rpId belongs to the page origin. A malicious page can therefore create fake passkey entries or overwrite an existing entry's passkey metadata for another rpId, corrupting vault state and potentially destroying a stored Peach-held passkey private key.",38- "lineNumbers": [39- 23,40- 24,41- 31,42- 5843- ],44- "recommendation": "Do not relay popup/internal passkey creation messages from MAIN-world page messages. Restrict COMPLETE_FIREFOX_PASSKEY_CREATE to trusted extension-page senders and verify it against a pending session request with a cryptographically random nonce and rpId match.",45- "confidence": "high",46- "producedByRunId": "20260701075706-0feae092f70d6cf5"47- }48- ],49- "analysisHistory": [50- {51- "runId": "20260701075706-0feae092f70d6cf5",52- "investigatedAt": "2026-07-01T08:02:44.949Z",53- "durationMs": 65810.8,54- "agentType": "codex",55- "model": "gpt-5.5",56- "modelConfig": {57- "model": "gpt-5.5"58- },59- "agentSessionId": "019f1cae-8397-7eb2-959b-38434905ef6f",60- "findingCount": 2,61- "numTurns": 0.2,62- "phase": "process",63- "costUsd": 0.4504416,64- "usage": {65- "inputTokens": 53123.2,66- "outputTokens": 2947.2,67- "cacheReadInputTokens": 192819.2,68- "cacheCreationInputTokens": 069- }70- }71- ],72- "status": "analyzed",73- "gitInfo": {74- "recentCommitters": [],75- "enrichedAt": "2026-07-01T08:02:44.957Z"76- }77-}.deepsec/data/peach-browser/files/src/content/passkeys-firefox.ts.json+0 −70
@@ -1,70 +0,0 @@1-{2- "filePath": "src/content/passkeys-firefox.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "postmessage-origin",7- "lineNumbers": [8- 2299- ],10- "snippet": " }\n window.addEventListener(\"message\", handler);\n window.postMessage({ type: \"PEACH_PASSKEY_REQUEST\", id, payload: message }, \"*\");\n });\n }\n",11- "matchedPattern": "postMessage handler WITHOUT origin validation"12- },13- {14- "vulnSlug": "crypto-usage",15- "lineNumbers": [16- 23517- ],18- "snippet": "\n function generateRequestId(): string {\n if (typeof crypto.randomUUID === \"function\") return crypto.randomUUID();\n const bytes = crypto.getRandomValues(new Uint8Array(16));\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");",19- "matchedPattern": "Node randomUUID"20- }21- ],22- "lastScannedAt": "2026-06-30T22:09:44.283Z",23- "lastScannedRunId": "20260630220943-c7b8dba65193b129",24- "fileHash": "f2821816156b02cbb3cadc8a038474f8df677e5b85d3e772932ed6980d531eb0",25- "findings": [26- {27- "severity": "HIGH",28- "vulnSlug": "auth-bypass",29- "title": "Page-reachable Firefox passkey relay can approve ceremonies without the user",30- "description": "passkeys-firefox.ts sends privileged passkey operations over a window.postMessage relay. The paired relay allows page-synthesized PEACH_PASSKEY_REQUEST messages for PASSKEY_AUTH_RESPONSE and COMPLETE_FIREFOX_PASSKEY_CREATE, while the background accepts PASSKEY_AUTH_RESPONSE without requiring an extension-page sender. The Firefox proxy approval ID is generated as Date.now() plus a small Math.random() range in background/passkey.ts, so a page that starts navigator.credentials.get/create can flood guessed request IDs through the relay and approve its own pending ceremony. After that, the background signs and returns a valid assertion for the page's rpId or creates a passkey, bypassing Peach's intended approval UI.",31- "lineNumbers": [32- 203,33- 209,34- 229,35- 23036- ],37- "recommendation": "Restrict the page relay to ceremony request types only, and remove approval/completion/storage messages from the relay allowlist. Require extension-page sender URLs for PASSKEY_AUTH_RESPONSE and COMPLETE_FIREFOX_PASSKEY_CREATE, bind completion to a stored pending request, and use unguessable random request IDs or nonces.",38- "confidence": "high",39- "producedByRunId": "20260701075706-0feae092f70d6cf5"40- }41- ],42- "analysisHistory": [43- {44- "runId": "20260701075706-0feae092f70d6cf5",45- "investigatedAt": "2026-07-01T08:02:57.088Z",46- "durationMs": 68381,47- "agentType": "codex",48- "model": "gpt-5.5",49- "modelConfig": {50- "model": "gpt-5.5"51- },52- "agentSessionId": "019f1cae-839f-7433-8a98-cb5a0fbcc6ea",53- "findingCount": 1,54- "numTurns": 0.2,55- "phase": "process",56- "costUsd": 0.5055938,57- "usage": {58- "inputTokens": 39749,59- "outputTokens": 3318,60- "cacheReadInputTokens": 414617.6,61- "cacheCreationInputTokens": 062- }63- }64- ],65- "status": "analyzed",66- "gitInfo": {67- "recentCommitters": [],68- "enrichedAt": "2026-07-01T08:02:57.095Z"69- }70-}.deepsec/data/peach-browser/files/src/content/policy.ts.json+0 −62
@@ -1,62 +0,0 @@1-{2- "filePath": "src/content/policy.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 15,9- 26,10- 27,11- 28,12- 29,13- 30,14- 31,15- 111,16- 11217- ],18- "snippet": "// Characters that plausibly appear in a site's forbidden-character list.\n// Restricted to ASCII punctuation a user could actually type. Anything else\n// (letters, digits, whitespace) is ignored so descriptive prose like\n// \"cannot contain the users first name, last name\" does not leak the comma.\nconst FORBIDDEN_CHAR_SET = new Set([",19- "matchedPattern": "weak cipher algorithm"20- },21- {22- "vulnSlug": "unverified-lookup",23- "lineNumbers": [24- 3025- ],26- "snippet": " for (const id of describedBy.trim().split(/\\s+/)) {\n const desc = el.ownerDocument.getElementById(id);\n if (desc?.textContent) parts.push(desc.textContent);\n }\n }",27- "matchedPattern": "DB lookup by ID without ownership check in next 15 lines"28- }29- ],30- "lastScannedAt": "2026-06-30T22:09:44.112Z",31- "lastScannedRunId": "20260630220943-c7b8dba65193b129",32- "fileHash": "d1ef1a0d6dfa3957b22abf525145ab37c31bfb0b566b77b862b7c95211c8a00b",33- "findings": [],34- "analysisHistory": [35- {36- "runId": "20260701075706-0feae092f70d6cf5",37- "investigatedAt": "2026-07-01T08:02:57.095Z",38- "durationMs": 68381,39- "agentType": "codex",40- "model": "gpt-5.5",41- "modelConfig": {42- "model": "gpt-5.5"43- },44- "agentSessionId": "019f1cae-839f-7433-8a98-cb5a0fbcc6ea",45- "findingCount": 0,46- "numTurns": 0.2,47- "phase": "process",48- "costUsd": 0.5055938,49- "usage": {50- "inputTokens": 39749,51- "outputTokens": 3318,52- "cacheReadInputTokens": 414617.6,53- "cacheCreationInputTokens": 054- }55- }56- ],57- "status": "analyzed",58- "gitInfo": {59- "recentCommitters": [],60- "enrichedAt": "2026-07-01T08:02:57.101Z"61- }62-}.deepsec/data/peach-browser/files/src/content/save.ts.json+0 −57
@@ -1,57 +0,0 @@1-{2- "filePath": "src/content/save.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 286,9- 287,10- 288,11- 28912- ],13- "snippet": " !username &&\n (type === \"text\" || type === \"email\" || type === \"tel\") &&\n (name.includes(\"user\") ||\n name.includes(\"email\") ||\n name.includes(\"login\") ||",14- "matchedPattern": "weak cipher algorithm"15- },16- {17- "vulnSlug": "secret-in-log",18- "lineNumbers": [19- 5920- ],21- "snippet": " type: \"UPDATE_ENTRY\",\n payload: { id, patch: { password } },\n });",22- "matchedPattern": "Secret variable in log statement"23- }24- ],25- "lastScannedAt": "2026-06-30T22:09:44.072Z",26- "lastScannedRunId": "20260630220943-c7b8dba65193b129",27- "fileHash": "c08cc7f40d099f8a147d2d5db98b51ad096fd0b506c501dabd9e9c703ba8c6e6",28- "findings": [],29- "analysisHistory": [30- {31- "runId": "20260701075706-0feae092f70d6cf5",32- "investigatedAt": "2026-07-01T08:02:57.101Z",33- "durationMs": 68381,34- "agentType": "codex",35- "model": "gpt-5.5",36- "modelConfig": {37- "model": "gpt-5.5"38- },39- "agentSessionId": "019f1cae-839f-7433-8a98-cb5a0fbcc6ea",40- "findingCount": 0,41- "numTurns": 0.2,42- "phase": "process",43- "costUsd": 0.5055938,44- "usage": {45- "inputTokens": 39749,46- "outputTokens": 3318,47- "cacheReadInputTokens": 414617.6,48- "cacheCreationInputTokens": 049- }50- }51- ],52- "status": "analyzed",53- "gitInfo": {54- "recentCommitters": [],55- "enrichedAt": "2026-07-01T08:02:57.108Z"56- }57-}.deepsec/data/peach-browser/files/src/crypto/peachPayload.ts.json+0 −47
@@ -1,47 +0,0 @@1-{2- "filePath": "src/crypto/peachPayload.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 1209- ],10- "snippet": "// boolean flag bit here does NOT require a format bump. But adding a new\n// VARIABLE-LENGTH field gated by a new flag bit DOES — older v3 decoders don't\n// know to skip the new field and the read cursor desyncs for everything after\n// it. If you need a new variable-length field, bump FORMAT_V3 → FORMAT_V4 and\n// gate the decoder on the format byte. Only add single-bit booleans to flags2.",11- "matchedPattern": "weak cipher algorithm"12- },13- {14- "vulnSlug": "secret-in-log",15- "lineNumbers": [16- 61617- ],18- "snippet": " ...(pk ? {\n ...(entry.password !== undefined ? { password: entry.password } : {}),\n ...(entry.url !== undefined ? { url: entry.url } : {}),",19- "matchedPattern": "Secret variable in log statement"20- },21- {22- "vulnSlug": "crypto-usage",23- "lineNumbers": [24- 1013,25- 1020,26- 1038,27- 104628- ],29- "snippet": " plaintext: Uint8Array\n): Promise<{ ciphertext: Uint8Array; tag: Uint8Array }> {\n const cryptoKey = await crypto.subtle.importKey(\n \"raw\",\n key.buffer.slice(key.byteOffset, key.byteOffset + key.byteLength) as ArrayBuffer,",30- "matchedPattern": "Web Crypto API (crypto.subtle.*)"31- },32- {33- "vulnSlug": "secrets-plaintext-exposure",34- "lineNumbers": [35- 26936- ],37- "snippet": " const innerLen = new DataView(decrypted.buffer, decrypted.byteOffset, decrypted.byteLength).getUint32(1, true);\n if (5 + innerLen > decrypted.length) throw new Error(\"PAYLOAD_TOO_SHORT\");\n inner = decrypted.subarray(5, 5 + innerLen);\n } else {\n inner = decrypted; // v1 format — no length prefix, no padding",38- "matchedPattern": "plaintext reference inside logger/response/error/span call"39- }40- ],41- "lastScannedAt": "2026-06-30T22:09:44.318Z",42- "lastScannedRunId": "20260630220943-c7b8dba65193b129",43- "fileHash": "ee4f00435526212dd91f8856f8634b60b1d98cbf714e3e06fd4d1505c9ddf5c9",44- "findings": [],45- "analysisHistory": [],46- "status": "error"47-}.deepsec/data/peach-browser/files/src/crypto/peachScript.ts.json+0 −20
@@ -1,20 +0,0 @@1-{2- "filePath": "src/crypto/peachScript.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 89- ],10- "snippet": " * in U+E000..U+E0FF. The bundled `PeachScript-Regular.otf` renders those\n * 256 code points as the visual script. Outside Peach the string renders\n * as boxes — that is the desired property: PeachScript is meant to be\n * read by Peach, not by humans.\n *",11- "matchedPattern": "weak cipher algorithm"12- }13- ],14- "lastScannedAt": "2026-06-30T22:09:44.017Z",15- "lastScannedRunId": "20260630220943-c7b8dba65193b129",16- "fileHash": "2a4fe0bdba7c6e14ce5b5d64665d64f4f0a622c37824e44789fae35a976e651e",17- "findings": [],18- "analysisHistory": [],19- "status": "error"20-}.deepsec/data/peach-browser/files/src/crypto/totp.ts.json+0 −20
@@ -1,20 +0,0 @@1-{2- "filePath": "src/crypto/totp.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "secret-in-log",7- "lineNumbers": [8- 209- ],10- "snippet": " // params (undefined) fall through to the RFC defaults (SHA-1 / 6 / 30 s).\n const totp = new TOTP({ secret: base32Secret, digits, period, algorithm });\n const code = totp.generate();",11- "matchedPattern": "Secret variable in log statement"12- }13- ],14- "lastScannedAt": "2026-06-30T22:09:44.072Z",15- "lastScannedRunId": "20260630220943-c7b8dba65193b129",16- "fileHash": "3253836953a15b2b15531d7e554177de73766c79d8180e84d08808c9c08f7a20",17- "findings": [],18- "analysisHistory": [],19- "status": "error"20-}.deepsec/data/peach-browser/files/src/popup/App.tsx.json+0 −111
@@ -1,111 +0,0 @@1-{2- "filePath": "src/popup/App.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "missing-auth",7- "lineNumbers": [8- 1389- ],10- "snippet": "const ENTRY_TRANSITION_SETTLE_MS = 135;\n\nexport default function App() {\n const [bootState, setBootState] = createSignal<BootState>(\"loading\");\n const [view, setView] = createSignal<View>(\"lock\");",11- "matchedPattern": "HTTP entry point: default export handler (weak candidate)"12- },13- {14- "vulnSlug": "xss",15- "lineNumbers": [16- 173417- ],18- "snippet": " </Show>\n <Show when={showNotes()}>\n <div class={`peach-notes-overlay${showNotes() ? \" is-visible\" : \"\"}`}>\n <NotesMode\n notes={entries().filter((e) => e.type === \"note\")}",19- "matchedPattern": "template literal in HTML"20- },21- {22- "vulnSlug": "insecure-crypto",23- "lineNumbers": [24- 24,25- 210,26- 220,27- 303,28- 312,29- 314,30- 426,31- 446,32- 627,33- 958,34- 1108,35- 1111,36- 1496,37- 1691,38- 1785,39- 179440- ],41- "snippet": " getActiveBindings,\n matchKeybinding,\n SHORTCUT_DESCRIPTORS,\n type Keybinding,\n type ShortcutAction,",42- "matchedPattern": "weak cipher algorithm"43- },44- {45- "vulnSlug": "open-redirect",46- "lineNumbers": [47- 221,48- 1115,49- 1116,50- 1688,51- 170452- ],53- "snippet": " // onBack fires we return to Panic instead of Settings so the user keeps the\n // destruction context they came from.\n const [returnToPanic, setReturnToPanic] = createSignal(false);\n // The view to return to when the add/edit view is dismissed. Normally \"entry\"\n // but \"health\" when the user arrives via the fix-entry path from Vault Health.",54- "matchedPattern": "redirect URL parameter"55- },56- {57- "vulnSlug": "unsafe-redirect",58- "lineNumbers": [59- 22160- ],61- "snippet": " // onBack fires we return to Panic instead of Settings so the user keeps the\n // destruction context they came from.\n const [returnToPanic, setReturnToPanic] = createSignal(false);\n // The view to return to when the add/edit view is dismissed. Normally \"entry\"\n // but \"health\" when the user arrives via the fix-entry path from Vault Health.",62- "matchedPattern": "Redirect URL from header/param (NO validNextRedirect — investigate)"63- },64- {65- "vulnSlug": "unsafe-redirect",66- "lineNumbers": [67- 111568- ],69- "snippet": "\n function handleExportBack() {\n if (returnToPanic()) {\n setReturnToPanic(false);\n setView(\"panic\");",70- "matchedPattern": "Redirect URL from header/param (NO validNextRedirect — investigate)"71- },72- {73- "vulnSlug": "unsafe-redirect",74- "lineNumbers": [75- 111676- ],77- "snippet": " function handleExportBack() {\n if (returnToPanic()) {\n setReturnToPanic(false);\n setView(\"panic\");\n return;",78- "matchedPattern": "Redirect URL from header/param (NO validNextRedirect — investigate)"79- },80- {81- "vulnSlug": "unsafe-redirect",82- "lineNumbers": [83- 168884- ],85- "snippet": " onCancel={() => setView(\"settings\")}\n onOpenExport={() => {\n setReturnToPanic(true);\n setView(\"export\");\n }}",86- "matchedPattern": "Redirect URL from header/param (NO validNextRedirect — investigate)"87- },88- {89- "vulnSlug": "unsafe-redirect",90- "lineNumbers": [91- 170492- ],93- "snippet": " setPendingProNotice(null);\n setPendingCelebration(false);\n setReturnToPanic(false);\n setContentPhase(\"idle\");\n setView(\"lock\");",94- "matchedPattern": "Redirect URL from header/param (NO validNextRedirect — investigate)"95- },96- {97- "vulnSlug": "crypto-usage",98- "lineNumbers": [99- 528100- ],101- "snippet": " // listener accepts the \"popup-\" prefix; the random suffix is defence in\n // depth against a port named exactly \"popup\" from anything but our popup.\n popupPort = chrome.runtime.connect({ name: `popup-${crypto.randomUUID()}` });\n } catch { /* extension context invalidated — ignore */ }\n onCleanup(() => { try { popupPort?.disconnect(); } catch {} });",102- "matchedPattern": "Node randomUUID"103- }104- ],105- "lastScannedAt": "2026-06-30T22:09:44.277Z",106- "lastScannedRunId": "20260630220943-c7b8dba65193b129",107- "fileHash": "1fda170243c6a7441693f64a1a4e15c9839f38845a918e962f8e1683f1eb8df4",108- "findings": [],109- "analysisHistory": [],110- "status": "error"111-}.deepsec/data/peach-browser/files/src/popup/chirp-pair.ts.json+0 −29
@@ -1,29 +0,0 @@1-{2- "filePath": "src/popup/chirp-pair.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 140,9- 39810- ],11- "snippet": "\n // Each role only runs on the matching platform — the extension is always\n // responder (it scans the LAN), Android/iOS/desktop are always initiator\n // (they open the WS server). Mismatches indicate a wiring bug upstream.\n if (role === \"responder\" && localIdentity.deviceType !== \"extension\") {",12- "matchedPattern": "weak cipher algorithm"13- },14- {15- "vulnSlug": "crypto-usage",16- "lineNumbers": [17- 43918- ],19- "snippet": "function generateUuid(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n const bytes = new Uint8Array(16);",20- "matchedPattern": "Node randomUUID"21- }22- ],23- "lastScannedAt": "2026-06-30T22:09:44.274Z",24- "lastScannedRunId": "20260630220943-c7b8dba65193b129",25- "fileHash": "71164ce9c3c1a4c61f3d105f81e71ee431b36b0dda318e3e9f541e8ba00c9cb8",26- "findings": [],27- "analysisHistory": [],28- "status": "error"29-}.deepsec/data/peach-browser/files/src/popup/codex/aztec.ts.json+0 −20
@@ -1,20 +0,0 @@1-{2- "filePath": "src/popup/codex/aztec.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 749- ],10- "snippet": " */\nexport function verifyMatrixRoundTrip(matrix: BitMatrix, payload: string): boolean {\n const modules = matrix.getWidth(); // Aztec codes are always square\n const scale = 4;\n const pad = 2 * scale; // 2-module quiet zone = 8px at scale 4",11- "matchedPattern": "weak cipher algorithm"12- }13- ],14- "lastScannedAt": "2026-06-30T22:09:44.034Z",15- "lastScannedRunId": "20260630220943-c7b8dba65193b129",16- "fileHash": "82d874ced509b2b1dee52e3bdd25144c582c9a7e84a4484b8992b92ad0cf2c20",17- "findings": [],18- "analysisHistory": [],19- "status": "error"20-}.deepsec/data/peach-browser/files/src/popup/codex/erasure.ts.json+0 −32
@@ -1,32 +0,0 @@1-{2- "filePath": "src/popup/codex/erasure.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 32,9- 90,10- 128,11- 297,12- 31513- ],14- "snippet": " * instance per call avoids the failure mode entirely; the cost is a few\n * milliseconds per call — negligible compared to compressing the vault and\n * rendering Aztec codes. We keep the loader's cached instance only as a\n * liveness probe (it confirms the WASM is instantiable on this platform).\n */",15- "matchedPattern": "weak cipher algorithm"16- },17- {18- "vulnSlug": "untrusted-redirect-following",19- "lineNumbers": [20- 6621- ],22- "snippet": " const url = chrome.runtime.getURL(\"codex/reed_solomon_erasure_bg.wasm\");\n const response = await fetch(url);\n if (!response.ok) {\n throw new CodexError(",23- "matchedPattern": "fetch(url) — default redirect: follow + caller-style URL"24- }25- ],26- "lastScannedAt": "2026-06-30T22:09:44.336Z",27- "lastScannedRunId": "20260630220943-c7b8dba65193b129",28- "fileHash": "1b22a916a75197d23ba3627a268f05c43672f854064eedede1f35b18a62e658b",29- "findings": [],30- "analysisHistory": [],31- "status": "error"32-}.deepsec/data/peach-browser/files/src/popup/codex/generate.ts.json+0 −23
@@ -1,23 +0,0 @@1-{2- "filePath": "src/popup/codex/generate.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 20,9- 138,10- 165,11- 28312- ],13- "snippet": "import {\n CHUNK_SIZE,\n CODES_PER_PAGE,\n CodexError,\n computeParityShards,",14- "matchedPattern": "weak cipher algorithm"15- }16- ],17- "lastScannedAt": "2026-06-30T22:09:44.034Z",18- "lastScannedRunId": "20260630220943-c7b8dba65193b129",19- "fileHash": "6f1af5930065b37af48863b05a04e489872033a5b8865be4fee19162e824e031",20- "findings": [],21- "analysisHistory": [],22- "status": "error"23-}.deepsec/data/peach-browser/files/src/popup/codex/helpers.ts.json+0 −31
@@ -1,31 +0,0 @@1-{2- "filePath": "src/popup/codex/helpers.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 8,9- 95,10- 124,11- 14412- ],13- "snippet": " * without any cross-import surface.\n *\n * SHA-256 stands in for BLAKE3 from the design doc. SHA-256 is available\n * everywhere via WebCrypto (no WASM, no polyfill), preimage-resistant, and\n * gives a stable 8-char-prefix fingerprint that is more than sufficient for",14- "matchedPattern": "weak cipher algorithm"15- },16- {17- "vulnSlug": "crypto-usage",18- "lineNumbers": [19- 7820- ],21- "snippet": " const owned = new Uint8Array(bytes.byteLength);\n owned.set(bytes);\n const hash = await crypto.subtle.digest(\"SHA-256\", owned);\n const view = new Uint8Array(hash);\n let hex = \"\";",22- "matchedPattern": "Web Crypto API (crypto.subtle.*)"23- }24- ],25- "lastScannedAt": "2026-06-30T22:09:44.313Z",26- "lastScannedRunId": "20260630220943-c7b8dba65193b129",27- "fileHash": "559be59a2597555224ae1c8d8c3b4c4be7dea7985fa5588760efe836281f4ff0",28- "findings": [],29- "analysisHistory": [],30- "status": "error"31-}.deepsec/data/peach-browser/files/src/popup/codex/node-wasm.ts.json+0 −20
@@ -1,20 +0,0 @@1-{2- "filePath": "src/popup/codex/node-wasm.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "xss",7- "lineNumbers": [8- 239- ],10- "snippet": "\nasync function importNodeModule<T>(name: string): Promise<T> {\n return import(/* @vite-ignore */ (`node:${name}`)) as Promise<T>;\n}\n",11- "matchedPattern": "template literal in HTML"12- }13- ],14- "lastScannedAt": "2026-06-30T22:09:43.937Z",15- "lastScannedRunId": "20260630220943-c7b8dba65193b129",16- "fileHash": "53672719c14144cf26e9d6107a4192b0f09a074b6bfe733600c2f5e88f4f3a59",17- "findings": [],18- "analysisHistory": [],19- "status": "error"20-}.deepsec/data/peach-browser/files/src/popup/codex/pdf-import.ts.json+0 −44
@@ -1,44 +0,0 @@1-{2- "filePath": "src/popup/codex/pdf-import.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 8,9- 21,10- 36,11- 37,12- 38,13- 102,14- 108,15- 110,16- 112,17- 128,18- 134,19- 144,20- 154,21- 160,22- 161,23- 208,24- 211,25- 212,26- 224,27- 408,28- 432,29- 448,30- 449,31- 490,32- 50433- ],34- "snippet": " * cell is decoded individually via `RGBLuminanceSource` + `AztecCodeReader`,\n * which is far more reliable than a full-page scan because adjacent Aztec\n * codes don't compete for the binarizer's attention.\n *\n * The top-level `importCodexPDF` entry point needs a DOM (canvas + pdf.js).",35- "matchedPattern": "weak cipher algorithm"36- }37- ],38- "lastScannedAt": "2026-06-30T22:09:44.033Z",39- "lastScannedRunId": "20260630220943-c7b8dba65193b129",40- "fileHash": "80724bd0efaf03263a089a13657a687ed40860525aa175e92de6f17387d039e8",41- "findings": [],42- "analysisHistory": [],43- "status": "error"44-}.deepsec/data/peach-browser/files/src/popup/codex/pdf-layout.ts.json+0 −24
@@ -1,24 +0,0 @@1-{2- "filePath": "src/popup/codex/pdf-layout.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 5,9- 10,10- 15,11- 28,12- 3013- ],14- "snippet": " * the PDF importer.\n *\n * The generator (`pdf.ts`) draws Aztec codes at exact pt positions on the\n * page. The importer (`pdf-import.ts`) must crop the matching regions out\n * of the rendered canvas before handing them to ZXing — Aztec decoding",15- "matchedPattern": "weak cipher algorithm"16- }17- ],18- "lastScannedAt": "2026-06-30T22:09:44.033Z",19- "lastScannedRunId": "20260630220943-c7b8dba65193b129",20- "fileHash": "5906cd43095d6da36553eb44be3e95c9d29cd0afabc5e1c513fa4ba21f57f869",21- "findings": [],22- "analysisHistory": [],23- "status": "error"24-}.deepsec/data/peach-browser/files/src/popup/codex/pdf.ts.json+0 −32
@@ -1,32 +0,0 @@1-{2- "filePath": "src/popup/codex/pdf.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 4,9- 6,10- 16,11- 21,12- 22,13- 23,14- 66,15- 70,16- 78,17- 82,18- 83,19- 184,20- 18521- ],22- "snippet": " * pdf.ts — render the printable Codex PDF.\n *\n * Layout follows section \"PDF rendering\" of the design doc. The input is a\n * flat list of per-Aztec PNG byte arrays (one PNG per code, not one per page).\n * We compute pagination from `CODES_PER_PAGE` and embed each PNG into the",23- "matchedPattern": "weak cipher algorithm"24- }25- ],26- "lastScannedAt": "2026-06-30T22:09:44.033Z",27- "lastScannedRunId": "20260630220943-c7b8dba65193b129",28- "fileHash": "2fbd436a6dd92d2972cbec33648236f9508cae2292b14da21890709e4525d509",29- "findings": [],30- "analysisHistory": [],31- "status": "error"32-}.deepsec/data/peach-browser/files/src/popup/codex/restore.ts.json+0 −21
@@ -1,21 +0,0 @@1-{2- "filePath": "src/popup/codex/restore.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 111,9- 27210- ],11- "snippet": " // payload becomes a parse_error rejection instead of a thrown exception out\n // of add() (honouring the documented \"never throws\" contract).\n // verifyChunkIntegrity also decodes `d`, so guarding here covers both that\n // decode and the accept-path decode below, which reuses `decoded`.\n const decoded = tryBase64UrlToBytes(envelope.d);",12- "matchedPattern": "weak cipher algorithm"13- }14- ],15- "lastScannedAt": "2026-06-30T22:09:44.033Z",16- "lastScannedRunId": "20260630220943-c7b8dba65193b129",17- "fileHash": "23d0e0cb9b8a775c8df3d2a41d26e246b970ba6dbde19d7ac7937785288ae42f",18- "findings": [],19- "analysisHistory": [],20- "status": "error"21-}.deepsec/data/peach-browser/files/src/popup/codex/rs-loader.ts.json+0 −20
@@ -1,20 +0,0 @@1-{2- "filePath": "src/popup/codex/rs-loader.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "untrusted-redirect-following",7- "lineNumbers": [8- 339- ],10- "snippet": " const url = chrome.runtime.getURL(WASM_RELATIVE_PATH);\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`fetch ${url} → ${response.status}`);",11- "matchedPattern": "fetch(url) — default redirect: follow + caller-style URL"12- }13- ],14- "lastScannedAt": "2026-06-30T22:09:44.336Z",15- "lastScannedRunId": "20260630220943-c7b8dba65193b129",16- "fileHash": "b97e3fb2677b7e69c5f070292ef7117f75e48be682cb8f3b9957726ac68ded56",17- "findings": [],18- "analysisHistory": [],19- "status": "error"20-}.deepsec/data/peach-browser/files/src/popup/components/AdoptionConsentModal.tsx.json+0 −46
@@ -1,46 +0,0 @@1-{2- "filePath": "src/popup/components/AdoptionConsentModal.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 659- ],10- "snippet": " disabled={busy()}\n aria-invalid={error() ? \"true\" : \"false\"}\n aria-describedby={error() ? \"adoption-consent-error\" : undefined}\n onInput={(event) => {\n setPassword((event.target as HTMLInputElement).value);",11- "matchedPattern": "weak cipher algorithm"12- }13- ],14- "lastScannedAt": "2026-06-30T22:09:44.032Z",15- "lastScannedRunId": "20260630220943-c7b8dba65193b129",16- "fileHash": "72471661cd77d9d8e594c48135cdc596c4a3f2293707ada606863c75f5817019",17- "findings": [],18- "analysisHistory": [19- {20- "runId": "20260701075706-0feae092f70d6cf5",21- "investigatedAt": "2026-07-01T08:01:38.126Z",22- "durationMs": 52246.4,23- "agentType": "codex",24- "model": "gpt-5.5",25- "modelConfig": {26- "model": "gpt-5.5"27- },28- "agentSessionId": "019f1cae-8382-7340-9f87-6a360cc11a90",29- "findingCount": 0,30- "numTurns": 0.2,31- "phase": "process",32- "costUsd": 0.28751920000000003,33- "usage": {34- "inputTokens": 24780.4,35- "outputTokens": 2380.2,36- "cacheReadInputTokens": 184422.4,37- "cacheCreationInputTokens": 038- }39- }40- ],41- "status": "analyzed",42- "gitInfo": {43- "recentCommitters": [],44- "enrichedAt": "2026-07-01T08:01:38.134Z"45- }46-}.deepsec/data/peach-browser/files/src/popup/components/ChirpPairingFlow.tsx.json+0 −76
@@ -1,76 +0,0 @@1-{2- "filePath": "src/popup/components/ChirpPairingFlow.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "xss",7- "lineNumbers": [8- 90,9- 233,10- 341,11- 404,12- 47713- ],14- "snippet": " </div>\n\n <div class={`peach-chirp-stage act-${phase()}`} aria-hidden=\"true\">\n <div class=\"peach-chirp-bird-slot peach-chirp-bird-slot-desktop\">\n <DesktopBird pose={desktopPose()} />",15- "matchedPattern": "template literal in HTML"16- },17- {18- "vulnSlug": "insecure-crypto",19- "lineNumbers": [20- 4,21- 14,22- 38,23- 39,24- 44,25- 60,26- 61,27- 67,28- 70,29- 91,30- 92,31- 93,32- 104,33- 139,34- 140,35- 348,36- 386,37- 401,38- 40439- ],40- "snippet": "//\n// Renders the three-act bird animation specified in the brief §6.2:\n// Act 1 (waiting) — desktop finch chirps, soft rings pulse from its beak\n// Act 2 (handshake) — mobile swallow hops in, sound-wave arcs fly between\n// the two birds, both heads tilt toward each other",41- "matchedPattern": "weak cipher algorithm"42- }43- ],44- "lastScannedAt": "2026-06-30T22:09:44.032Z",45- "lastScannedRunId": "20260630220943-c7b8dba65193b129",46- "fileHash": "0048370fec4b890d06c96aa4b6843225ed129408a8c9b3a0d93528071facebe7",47- "findings": [],48- "analysisHistory": [49- {50- "runId": "20260701075706-0feae092f70d6cf5",51- "investigatedAt": "2026-07-01T08:01:38.107Z",52- "durationMs": 52246.4,53- "agentType": "codex",54- "model": "gpt-5.5",55- "modelConfig": {56- "model": "gpt-5.5"57- },58- "agentSessionId": "019f1cae-8382-7340-9f87-6a360cc11a90",59- "findingCount": 0,60- "numTurns": 0.2,61- "phase": "process",62- "costUsd": 0.28751920000000003,63- "usage": {64- "inputTokens": 24780.4,65- "outputTokens": 2380.2,66- "cacheReadInputTokens": 184422.4,67- "cacheCreationInputTokens": 068- }69- }70- ],71- "status": "analyzed",72- "gitInfo": {73- "recentCommitters": [],74- "enrichedAt": "2026-07-01T08:01:38.116Z"75- }76-}.deepsec/data/peach-browser/files/src/popup/components/GeneratorDrawer.tsx.json+0 −101
@@ -1,101 +0,0 @@1-{2- "filePath": "src/popup/components/GeneratorDrawer.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "xss",7- "lineNumbers": [8- 221,9- 223,10- 287,11- 288,12- 289,13- 290,14- 372,15- 373,16- 374,17- 37518- ],19- "snippet": "\n return (\n <div class={`peach-generator-drawer ${props.open ? \"is-open\" : \"\"}`}>\n <div class=\"peach-generator-drawer-inner\">\n <div class={`peach-generator-drawer-content${props.compact ? \" peach-generator-drawer-content--compact\" : \"\"}`}>",20- "matchedPattern": "template literal in HTML"21- },22- {23- "vulnSlug": "insecure-crypto",24- "lineNumbers": [25- 12526- ],27- "snippet": " includeLowercase: options?.lower ?? lower(),\n includeNumbers: options?.numbers ?? numbers(),\n includeSymbols: options?.symbols ?? symbols(),\n forbiddenChars: forbiddenChars(),\n });",28- "matchedPattern": "weak cipher algorithm"29- }30- ],31- "lastScannedAt": "2026-06-30T22:09:44.032Z",32- "lastScannedRunId": "20260630220943-c7b8dba65193b129",33- "fileHash": "32c7af8e539b6137d3194d41cfc37dff4419cdd00bc1c4a2191f1959953304a0",34- "findings": [35- {36- "severity": "HIGH_BUG",37- "vulnSlug": "other-data-loss",38- "title": "Hidden generator can overwrite the form password on mount",39- "description": "GeneratorDrawer calls regenerate() during onMount, and regenerate() always calls props.onChange with the newly generated password. In AddView the drawer is mounted even when open is false, and onChange writes directly to the password form signal. Because AddView is also used with editEntry(), simply opening an existing login for edit can replace the saved password in form state before the user opens the generator; saving any unrelated change would persist that random replacement.",40- "lineNumbers": [41- 73,42- 83,43- 120,44- 128,45- 12946- ],47- "recommendation": "Do not generate or emit onChange while the drawer is closed. Gate the initial regeneration on props.open, mount the drawer only when visible, or separate preview updates from committed form changes so hidden initialization cannot mutate the parent password field.",48- "confidence": "high",49- "producedByRunId": "20260701075706-0feae092f70d6cf5"50- },51- {52- "severity": "MEDIUM",53- "vulnSlug": "other-resource-exhaustion",54- "title": "Page-controlled password policy can force unbounded generation work",55- "description": "When no policy prop is supplied, the drawer asks the active tab for GET_SITE_PASSWORD_POLICY. That policy is scraped from arbitrary page markup, but initFromPolicy copies policy.maxLength and policy.minLength into the generator length without a hard cap. The slider max is capped, but the actual length is not. A hostile page can advertise a huge maxlength, pattern count, or textual minimum, causing onMount to call regenerate() with that value. generatePassword then allocates and loops for the requested length, which can freeze or crash the popup.",56- "lineNumbers": [57- 37,58- 38,59- 44,60- 46,61- 53,62- 54,63- 77,64- 80,65- 83,66- 12067- ],68- "recommendation": "Treat scraped policy values as untrusted. Validate that minLength and maxLength are finite positive integers, clamp both the UI bounds and actual generation length to the same small maximum such as 128, and ignore or normalize contradictory ranges before calling generatePassword.",69- "confidence": "high",70- "producedByRunId": "20260701075706-0feae092f70d6cf5"71- }72- ],73- "analysisHistory": [74- {75- "runId": "20260701075706-0feae092f70d6cf5",76- "investigatedAt": "2026-07-01T08:01:38.116Z",77- "durationMs": 52246.4,78- "agentType": "codex",79- "model": "gpt-5.5",80- "modelConfig": {81- "model": "gpt-5.5"82- },83- "agentSessionId": "019f1cae-8382-7340-9f87-6a360cc11a90",84- "findingCount": 2,85- "numTurns": 0.2,86- "phase": "process",87- "costUsd": 0.28751920000000003,88- "usage": {89- "inputTokens": 24780.4,90- "outputTokens": 2380.2,91- "cacheReadInputTokens": 184422.4,92- "cacheCreationInputTokens": 093- }94- }95- ],96- "status": "analyzed",97- "gitInfo": {98- "recentCommitters": [],99- "enrichedAt": "2026-07-01T08:01:38.125Z"100- }101-}.deepsec/data/peach-browser/files/src/popup/components/NoteToolbar.tsx.json+0 −117
@@ -1,117 +0,0 @@1-{2- "filePath": "src/popup/components/NoteToolbar.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "rce",7- "lineNumbers": [8- 6,9- 7,10- 8,11- 11,12- 15,13- 17,14- 1815- ],16- "snippet": "export type ToolbarExec = (cmd: string, val?: string) => void;\n\nfunction applyBold(exec: ToolbarExec) { exec(\"bold\"); }\nfunction applyItalic(exec: ToolbarExec) { exec(\"italic\"); }\nfunction applyUnderline(exec: ToolbarExec) { exec(\"underline\"); }",17- "matchedPattern": "exec with string"18- },19- {20- "vulnSlug": "insecure-crypto",21- "lineNumbers": [22- 32,23- 3324- ],25- "snippet": " when={isNativePlatform()}\n fallback={\n // Desktop: toolbar rendered inline above the body, shown always\n <div class={props.class ?? \"peach-note-toolbar-desktop-inline\"}>\n <button class=\"peach-note-toolbar-btn\" onMouseDown={(e) => { e.preventDefault(); applyBold(props.exec); }} title=\"Bold\">B</button>",26- "matchedPattern": "weak cipher algorithm"27- },28- {29- "vulnSlug": "agent-tool-definition",30- "lineNumbers": [31- 632- ],33- "snippet": "\nfunction applyBold(exec: ToolbarExec) { exec(\"bold\"); }\nfunction applyItalic(exec: ToolbarExec) { exec(\"italic\"); }\nfunction applyUnderline(exec: ToolbarExec) { exec(\"underline\"); }\nfunction applyHeading(exec: ToolbarExec) {",34- "matchedPattern": "Tool executes shell commands — RCE via prompt injection"35- },36- {37- "vulnSlug": "agent-tool-definition",38- "lineNumbers": [39- 740- ],41- "snippet": "function applyBold(exec: ToolbarExec) { exec(\"bold\"); }\nfunction applyItalic(exec: ToolbarExec) { exec(\"italic\"); }\nfunction applyUnderline(exec: ToolbarExec) { exec(\"underline\"); }\nfunction applyHeading(exec: ToolbarExec) {\n const val = document.queryCommandValue(\"formatBlock\").toLowerCase();",42- "matchedPattern": "Tool executes shell commands — RCE via prompt injection"43- },44- {45- "vulnSlug": "agent-tool-definition",46- "lineNumbers": [47- 848- ],49- "snippet": "function applyItalic(exec: ToolbarExec) { exec(\"italic\"); }\nfunction applyUnderline(exec: ToolbarExec) { exec(\"underline\"); }\nfunction applyHeading(exec: ToolbarExec) {\n const val = document.queryCommandValue(\"formatBlock\").toLowerCase();\n exec(\"formatBlock\", val === \"h2\" ? \"p\" : \"h2\");",50- "matchedPattern": "Tool executes shell commands — RCE via prompt injection"51- },52- {53- "vulnSlug": "agent-tool-definition",54- "lineNumbers": [55- 1156- ],57- "snippet": " const val = document.queryCommandValue(\"formatBlock\").toLowerCase();\n exec(\"formatBlock\", val === \"h2\" ? \"p\" : \"h2\");\n}\nfunction applyQuote(exec: ToolbarExec) {\n const val = document.queryCommandValue(\"formatBlock\").toLowerCase();",58- "matchedPattern": "Tool executes shell commands — RCE via prompt injection"59- },60- {61- "vulnSlug": "agent-tool-definition",62- "lineNumbers": [63- 1564- ],65- "snippet": " const val = document.queryCommandValue(\"formatBlock\").toLowerCase();\n exec(\"formatBlock\", val === \"blockquote\" ? \"p\" : \"blockquote\");\n}\nfunction applyBullet(exec: ToolbarExec) { exec(\"insertUnorderedList\"); }\nfunction applyNumbered(exec: ToolbarExec) { exec(\"insertOrderedList\"); }",66- "matchedPattern": "Tool executes shell commands — RCE via prompt injection"67- },68- {69- "vulnSlug": "agent-tool-definition",70- "lineNumbers": [71- 1772- ],73- "snippet": "}\nfunction applyBullet(exec: ToolbarExec) { exec(\"insertUnorderedList\"); }\nfunction applyNumbered(exec: ToolbarExec) { exec(\"insertOrderedList\"); }\n\ninterface ToolbarProps {",74- "matchedPattern": "Tool executes shell commands — RCE via prompt injection"75- },76- {77- "vulnSlug": "agent-tool-definition",78- "lineNumbers": [79- 1880- ],81- "snippet": "function applyBullet(exec: ToolbarExec) { exec(\"insertUnorderedList\"); }\nfunction applyNumbered(exec: ToolbarExec) { exec(\"insertOrderedList\"); }\n\ninterface ToolbarProps {\n exec: ToolbarExec;",82- "matchedPattern": "Tool executes shell commands — RCE via prompt injection"83- }84- ],85- "lastScannedAt": "2026-06-30T22:09:44.186Z",86- "lastScannedRunId": "20260630220943-c7b8dba65193b129",87- "fileHash": "f5c9c2afdf4b8ecf795a532218c0d0412dc9a75fad6cbb0ef6d74ca5ed9d3726",88- "findings": [],89- "analysisHistory": [90- {91- "runId": "20260701075706-0feae092f70d6cf5",92- "investigatedAt": "2026-07-01T08:01:38.090Z",93- "durationMs": 52246.4,94- "agentType": "codex",95- "model": "gpt-5.5",96- "modelConfig": {97- "model": "gpt-5.5"98- },99- "agentSessionId": "019f1cae-8382-7340-9f87-6a360cc11a90",100- "findingCount": 0,101- "numTurns": 0.2,102- "phase": "process",103- "costUsd": 0.28751920000000003,104- "usage": {105- "inputTokens": 24780.4,106- "outputTokens": 2380.2,107- "cacheReadInputTokens": 184422.4,108- "cacheCreationInputTokens": 0109- }110- }111- ],112- "status": "analyzed",113- "gitInfo": {114- "recentCommitters": [],115- "enrichedAt": "2026-07-01T08:01:38.099Z"116- }117-}.deepsec/data/peach-browser/files/src/popup/components/RuneCanvas.tsx.json+0 −46
@@ -1,46 +0,0 @@1-{2- "filePath": "src/popup/components/RuneCanvas.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "rce",7- "lineNumbers": [8- 1129- ],10- "snippet": " const alpha = Math.max(0, lifetimeEnvelope) * pulseAlpha * 0.85;\n glowMap.set(`${g.row},${g.col}`, alpha);\n if (g.age >= g.lifetime) Object.assign(g, spawn());\n }\n",11- "matchedPattern": "spawn"12- }13- ],14- "lastScannedAt": "2026-06-30T22:09:43.950Z",15- "lastScannedRunId": "20260630220943-c7b8dba65193b129",16- "fileHash": "c2670e744a9f61ec251b1426904e1a3d030d86209f9fc67efb186c165320339f",17- "findings": [],18- "analysisHistory": [19- {20- "runId": "20260701075706-0feae092f70d6cf5",21- "investigatedAt": "2026-07-01T08:01:38.100Z",22- "durationMs": 52246.4,23- "agentType": "codex",24- "model": "gpt-5.5",25- "modelConfig": {26- "model": "gpt-5.5"27- },28- "agentSessionId": "019f1cae-8382-7340-9f87-6a360cc11a90",29- "findingCount": 0,30- "numTurns": 0.2,31- "phase": "process",32- "costUsd": 0.28751920000000003,33- "usage": {34- "inputTokens": 24780.4,35- "outputTokens": 2380.2,36- "cacheReadInputTokens": 184422.4,37- "cacheCreationInputTokens": 038- }39- }40- ],41- "status": "analyzed",42- "gitInfo": {43- "recentCommitters": [],44- "enrichedAt": "2026-07-01T08:01:38.107Z"45- }46-}.deepsec/data/peach-browser/files/src/popup/firefox-passkey.ts.json+0 −28
@@ -1,28 +0,0 @@1-{2- "filePath": "src/popup/firefox-passkey.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "secret-in-log",7- "lineNumbers": [8- 679- ],10- "snippet": " if (!result || !(result instanceof PublicKeyCredential)) {\n return { ok: false, error: \"no credential returned\" };\n }",11- "matchedPattern": "Secret variable in error response"12- },13- {14- "vulnSlug": "secret-in-log",15- "lineNumbers": [16- 7417- ],18- "snippet": " }\n return { ok: false, error: err instanceof Error ? err.message : \"credential creation failed\" };\n }",19- "matchedPattern": "Secret variable in error response"20- }21- ],22- "lastScannedAt": "2026-06-30T22:09:44.071Z",23- "lastScannedRunId": "20260630220943-c7b8dba65193b129",24- "fileHash": "24b3ca73d8f62e02d8047350b57759a13b02e7d3f35e0f70b528e93e4fcaca1e",25- "findings": [],26- "analysisHistory": [],27- "status": "error"28-}.deepsec/data/peach-browser/files/src/popup/views/Add.tsx.json+0 −74
@@ -1,74 +0,0 @@1-{2- "filePath": "src/popup/views/Add.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "xss",7- "lineNumbers": [8- 660,9- 74510- ],11- "snippet": " <span class=\"peach-tag-chip\">\n {tag}\n <button class=\"peach-tag-chip-remove\" onClick={() => removeTag(tag)} aria-label={`Remove ${tag}`}>×</button>\n </span>\n )}",12- "matchedPattern": "template literal in HTML"13- },14- {15- "vulnSlug": "insecure-crypto",16- "lineNumbers": [17- 58,18- 80,19- 140,20- 14621- ],22- "snippet": " const em = entry.email?.trim() ?? \"\";\n if (uname) {\n if (uname.includes(\"@\")) emailMap.set(uname, (emailMap.get(uname) ?? 0) + 1);\n else usernameMap.set(uname, (usernameMap.get(uname) ?? 0) + 1);\n }",23- "matchedPattern": "weak cipher algorithm"24- }25- ],26- "lastScannedAt": "2026-06-30T22:09:44.032Z",27- "lastScannedRunId": "20260630220943-c7b8dba65193b129",28- "fileHash": "498fcc45f43eafefe1d88b7fd061e28d0a991f874a13d77ac199ed9f73214995",29- "findings": [30- {31- "severity": "HIGH_BUG",32- "vulnSlug": "other-totp-parameter-loss",33- "title": "Saving otpauth TOTP secrets discards non-default parameters",34- "description": "getNormalizedTotp parses otpauth:// URIs but returns only parsed.secret, and handleSave sends only payload.totp. The shared TOTP model supports algorithm, digit count, and period, so entries using SHA256/SHA512, 8 digits, or non-30-second periods are saved with incomplete data and will generate invalid codes.",35- "lineNumbers": [36- 194,37- 198,38- 199,39- 23140- ],41- "recommendation": "Return the full parsed TOTP parameters and include totpAlgorithm, totpDigits, and totpPeriod in ADD_ENTRY/UPDATE_ENTRY payloads. Clear stale parameter fields when the TOTP secret is cleared or replaced with a bare seed.",42- "confidence": "high",43- "producedByRunId": "20260701075706-0feae092f70d6cf5"44- }45- ],46- "analysisHistory": [47- {48- "runId": "20260701075706-0feae092f70d6cf5",49- "investigatedAt": "2026-07-01T08:03:29.356Z",50- "durationMs": 74088.8,51- "agentType": "codex",52- "model": "gpt-5.5",53- "modelConfig": {54- "model": "gpt-5.5"55- },56- "agentSessionId": "019f1cae-8382-76c3-90c5-149e9e1f7918",57- "findingCount": 1,58- "numTurns": 0.2,59- "phase": "process",60- "costUsd": 0.49242100000000005,61- "usage": {62- "inputTokens": 38977,63- "outputTokens": 3398.4,64- "cacheReadInputTokens": 391168,65- "cacheCreationInputTokens": 066- }67- }68- ],69- "status": "analyzed",70- "gitInfo": {71- "recentCommitters": [],72- "enrichedAt": "2026-07-01T08:03:29.363Z"73- }74-}.deepsec/data/peach-browser/files/src/popup/views/AdoptVaultView.tsx.json+0 −46
@@ -1,46 +0,0 @@1-{2- "filePath": "src/popup/views/AdoptVaultView.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "secret-in-log",7- "lineNumbers": [8- 919- ],10- "snippet": " type: \"CREATE_VAULT_FROM_ROOT_KEY\",\n payload: { password: password(), rootKeyHex },\n }) as { ok: boolean; vaultId?: string; error?: string };",11- "matchedPattern": "Secret variable in log statement"12- }13- ],14- "lastScannedAt": "2026-06-30T22:09:44.077Z",15- "lastScannedRunId": "20260630220943-c7b8dba65193b129",16- "fileHash": "7f890fd3ba48152f6a22254ad85804c912cc6188a2f57fe996971afb31d4568d",17- "findings": [],18- "analysisHistory": [19- {20- "runId": "20260701075706-0feae092f70d6cf5",21- "investigatedAt": "2026-07-01T08:03:29.342Z",22- "durationMs": 74088.8,23- "agentType": "codex",24- "model": "gpt-5.5",25- "modelConfig": {26- "model": "gpt-5.5"27- },28- "agentSessionId": "019f1cae-8382-76c3-90c5-149e9e1f7918",29- "findingCount": 0,30- "numTurns": 0.2,31- "phase": "process",32- "costUsd": 0.49242100000000005,33- "usage": {34- "inputTokens": 38977,35- "outputTokens": 3398.4,36- "cacheReadInputTokens": 391168,37- "cacheCreationInputTokens": 038- }39- }40- ],41- "status": "analyzed",42- "gitInfo": {43- "recentCommitters": [],44- "enrichedAt": "2026-07-01T08:03:29.348Z"45- }46-}.deepsec/data/peach-browser/files/src/popup/views/ChirpSync.tsx.json+0 −176
@@ -1,176 +0,0 @@1-{2- "filePath": "src/popup/views/ChirpSync.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "xss",7- "lineNumbers": [8- 3939- ],10- "snippet": " // so the bird animation is present regardless of sync method.\n const desktopBirdStage = (active: boolean) => (\n <div class={`peach-chirp-stage ${active ? \"act-handshake\" : \"act-listening\"}`} aria-hidden=\"true\">\n <div class=\"peach-chirp-bird-slot peach-chirp-bird-slot-desktop\">\n <DesktopBird pose=\"chirping\" />",11- "matchedPattern": "template literal in HTML"12- },13- {14- "vulnSlug": "insecure-crypto",15- "lineNumbers": [16- 11,17- 13,18- 14,19- 31,20- 51,21- 58,22- 60,23- 62,24- 63,25- 64,26- 71,27- 73,28- 79,29- 88,30- 89,31- 125,32- 127,33- 129,34- 130,35- 131,36- 132,37- 133,38- 134,39- 136,40- 138,41- 139,42- 140,43- 141,44- 142,45- 143,46- 144,47- 145,48- 153,49- 154,50- 157,51- 158,52- 159,53- 160,54- 163,55- 164,56- 169,57- 170,58- 173,59- 174,60- 176,61- 180,62- 181,63- 182,64- 183,65- 185,66- 186,67- 188,68- 190,69- 197,70- 198,71- 206,72- 209,73- 214,74- 215,75- 217,76- 218,77- 221,78- 223,79- 224,80- 234,81- 235,82- 390,83- 392,84- 394,85- 395,86- 403,87- 404,88- 409,89- 413,90- 414,91- 415,92- 422,93- 424,94- 425,95- 430,96- 432,97- 440,98- 442,99- 443,100- 448,101- 449,102- 450,103- 458,104- 459,105- 465,106- 466,107- 469,108- 472,109- 478,110- 481,111- 488,112- 489,113- 492,114- 500,115- 501,116- 502,117- 503,118- 506,119- 511,120- 546,121- 563,122- 564123- ],124- "snippet": "// - Chirp — ChirpPairingFlow takes over the surface and drives the three-act\n// bird animation in lockstep with the audio protocol.\n// - Code — the peach-sync desktop-helper NK flow (enter code → SAS → paired).\n//\n// The chirp orchestrator lives in `popup/chirp-pair.ts`; the desktop NK pairing",125- "matchedPattern": "weak cipher algorithm"126- },127- {128- "vulnSlug": "secret-in-log",129- "lineNumbers": [130- 579131- ],132- "snippet": " verifyMasterPassword={async (password) => {\n const res = await sendMessage({ type: \"VERIFY_MAIN_PASSWORD\", payload: { password } });\n return Boolean((res as { ok: boolean; data?: boolean }).ok && (res as { data?: boolean }).data);",133- "matchedPattern": "Secret variable in log statement"134- },135- {136- "vulnSlug": "postmessage-origin",137- "lineNumbers": [138- 347139- ],140- "snippet": " };\n ws.onmessage = (ev) => {\n if (!(ev.data instanceof ArrayBuffer)) return;\n const bytes = new Uint8Array(ev.data);\n const w = waiters.shift();\n if (w) w(bytes); else inbox.push(bytes);",141- "matchedPattern": "postMessage handler WITHOUT origin validation"142- }143- ],144- "lastScannedAt": "2026-06-30T22:09:44.142Z",145- "lastScannedRunId": "20260630220943-c7b8dba65193b129",146- "fileHash": "9379774b71a7eea21b9fe4e319f70e12cf58c1c4ead27d4cc75e729f39780f06",147- "findings": [],148- "analysisHistory": [149- {150- "runId": "20260701075706-0feae092f70d6cf5",151- "investigatedAt": "2026-07-01T08:03:12.989Z",152- "durationMs": 71720.2,153- "agentType": "codex",154- "model": "gpt-5.5",155- "modelConfig": {156- "model": "gpt-5.5"157- },158- "agentSessionId": "019f1cae-8389-7bf1-af72-8787e1f8e8ba",159- "findingCount": 0,160- "numTurns": 0.2,161- "phase": "process",162- "costUsd": 0.4486958,163- "usage": {164- "inputTokens": 37432.6,165- "outputTokens": 3352,166- "cacheReadInputTokens": 321945.6,167- "cacheCreationInputTokens": 0168- }169- }170- ],171- "status": "analyzed",172- "gitInfo": {173- "recentCommitters": [],174- "enrichedAt": "2026-07-01T08:03:12.998Z"175- }176-}.deepsec/data/peach-browser/files/src/popup/views/CodexGenerateView.tsx.json+0 −26
@@ -1,26 +0,0 @@1-{2- "filePath": "src/popup/views/CodexGenerateView.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 317,9- 318,10- 391,11- 439,12- 553,13- 579,14- 60215- ],16- "snippet": " const total = progressTotal();\n const current = progressCurrent();\n if (total > 0) return `generating codes (${current} of ${total})`;\n return \"generating codes\";\n }",17- "matchedPattern": "weak cipher algorithm"18- }19- ],20- "lastScannedAt": "2026-06-30T22:09:44.031Z",21- "lastScannedRunId": "20260630220943-c7b8dba65193b129",22- "fileHash": "c4b02c1ccf6c7313b3ac95d3531fa925eb5fda423a1c569ac70f4351074ebcfa",23- "findings": [],24- "analysisHistory": [],25- "status": "error"26-}.deepsec/data/peach-browser/files/src/popup/views/CodexRestoreView.tsx.json+0 −31
@@ -1,31 +0,0 @@1-{2- "filePath": "src/popup/views/CodexRestoreView.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 4,9- 96,10- 108,11- 226,12- 246,13- 325,14- 327,15- 329,16- 357,17- 519,18- 605,19- 61920- ],21- "snippet": "//\n// Two entry paths, both feeding the same ChunkBuffer:\n// • Camera scan — Aztec codes via @zxing/library\n// • PDF import — pdf.js + grid scan of each rendered page\n//",22- "matchedPattern": "weak cipher algorithm"23- }24- ],25- "lastScannedAt": "2026-06-30T22:09:44.031Z",26- "lastScannedRunId": "20260630220943-c7b8dba65193b129",27- "fileHash": "94e5e18510e4438e215c315b033798b6cb5c7eebd213cd09122b6dddb1a8044d",28- "findings": [],29- "analysisHistory": [],30- "status": "error"31-}.deepsec/data/peach-browser/files/src/popup/views/Entry.tsx.json+0 −82
@@ -1,82 +0,0 @@1-{2- "filePath": "src/popup/views/Entry.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "xss",7- "lineNumbers": [8- 615,9- 623,10- 862,11- 142412- ],13- "snippet": " onClick={() => startEdit(\"title\", entry().title)}\n >\n <h1 class={`peach-entry-name ${titleSizeClass()}`} aria-label={entry().title}>\n <span class=\"peach-entry-name-text\">\n <Show",14- "matchedPattern": "template literal in HTML"15- },16- {17- "vulnSlug": "insecure-crypto",18- "lineNumbers": [19- 162,20- 184,21- 346,22- 400,23- 959,24- 1010,25- 1016,26- 1026,27- 108928- ],29- "snippet": " const em = e.email?.trim() ?? \"\";\n if (uname) {\n if (uname.includes(\"@\")) emailMap.set(uname, (emailMap.get(uname) ?? 0) + 1);\n else usernameMap.set(uname, (usernameMap.get(uname) ?? 0) + 1);\n }",30- "matchedPattern": "weak cipher algorithm"31- }32- ],33- "lastScannedAt": "2026-06-30T22:09:44.030Z",34- "lastScannedRunId": "20260630220943-c7b8dba65193b129",35- "fileHash": "e70ff94eb8cacd7cb9e5dd78cdd89b250ea0200bd66672e98bb9d5f379032fbe",36- "findings": [37- {38- "severity": "HIGH_BUG",39- "vulnSlug": "other-totp-parameter-loss",40- "title": "Entry view ignores and erases non-default TOTP parameters",41- "description": "The displayed TOTP code calls generateTOTP with only the stored secret, ignoring entry.totpAlgorithm, entry.totpDigits, and entry.totpPeriod. Editing an otpauth:// value also patches only totp, so new parameters are not saved and stale parameters can remain after replacement.",42- "lineNumbers": [43- 211,44- 214,45- 307,46- 309,47- 31648- ],49- "recommendation": "Pass stored TOTP parameters into generateTOTP, and when saving the TOTP field patch the parsed algorithm/digits/period alongside the secret. Clear parameter fields when the secret is removed or replaced by a bare seed.",50- "confidence": "high",51- "producedByRunId": "20260701075706-0feae092f70d6cf5"52- }53- ],54- "analysisHistory": [55- {56- "runId": "20260701075706-0feae092f70d6cf5",57- "investigatedAt": "2026-07-01T08:03:29.363Z",58- "durationMs": 74088.8,59- "agentType": "codex",60- "model": "gpt-5.5",61- "modelConfig": {62- "model": "gpt-5.5"63- },64- "agentSessionId": "019f1cae-8382-76c3-90c5-149e9e1f7918",65- "findingCount": 1,66- "numTurns": 0.2,67- "phase": "process",68- "costUsd": 0.49242100000000005,69- "usage": {70- "inputTokens": 38977,71- "outputTokens": 3398.4,72- "cacheReadInputTokens": 391168,73- "cacheCreationInputTokens": 074- }75- }76- ],77- "status": "analyzed",78- "gitInfo": {79- "recentCommitters": [],80- "enrichedAt": "2026-07-01T08:03:29.369Z"81- }82-}.deepsec/data/peach-browser/files/src/popup/views/Export.tsx.json+0 −21
@@ -1,21 +0,0 @@1-{2- "filePath": "src/popup/views/Export.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 66,9- 30710- ],11- "snippet": " <p class=\"peach-export-card-copy\">\n Your vault as a string of Peach symbols you can paste into Peach\n on another device. Designed for Peach-to-Peach transfer.\n </p>\n <button",12- "matchedPattern": "weak cipher algorithm"13- }14- ],15- "lastScannedAt": "2026-06-30T22:09:44.030Z",16- "lastScannedRunId": "20260630220943-c7b8dba65193b129",17- "fileHash": "4f4bc6b398c0d8098b90d7f532749816cead6b19dd1876c0ce8882d80c705900",18- "findings": [],19- "analysisHistory": [],20- "status": "error"21-}.deepsec/data/peach-browser/files/src/popup/views/Health.tsx.json+0 −20
@@ -1,20 +0,0 @@1-{2- "filePath": "src/popup/views/Health.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "crypto-usage",7- "lineNumbers": [8- 119- ],10- "snippet": " const encoder = new TextEncoder();\n const data = encoder.encode(plain);\n return crypto.subtle.digest(\"SHA-256\", data).then((buf) => {\n return Array.from(new Uint8Array(buf))\n .map((b) => b.toString(16).padStart(2, \"0\"))",11- "matchedPattern": "Web Crypto API (crypto.subtle.*)"12- }13- ],14- "lastScannedAt": "2026-06-30T22:09:44.306Z",15- "lastScannedRunId": "20260630220943-c7b8dba65193b129",16- "fileHash": "b84b9eb1ea452ea96b31754bf4c6a517cbf8236633d39680d2c86b94e9700852",17- "findings": [],18- "analysisHistory": [],19- "status": "error"20-}.deepsec/data/peach-browser/files/src/popup/views/Import.tsx.json+0 −95
@@ -1,95 +0,0 @@1-{2- "filePath": "src/popup/views/Import.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "xss",7- "lineNumbers": [8- 459- ],10- "snippet": " <>\n <div class=\"peach-newvault-label-row\">\n <label class=\"peach-label\" for={`${props.idPrefix}-master-password`}>\n new master password\n </label>",11- "matchedPattern": "template literal in HTML"12- },13- {14- "vulnSlug": "secret-in-log",15- "lineNumbers": [16- 26517- ],18- "snippet": " type: \"CREATE_VAULT\",\n payload: { password: masterPassword() },\n });",19- "matchedPattern": "Secret variable in log statement"20- },21- {22- "vulnSlug": "secret-in-log",23- "lineNumbers": [24- 55725- ],26- "snippet": " type: \"CREATE_VAULT\",\n payload: { password: masterPassword() },\n });",27- "matchedPattern": "Secret variable in log statement"28- }29- ],30- "lastScannedAt": "2026-06-30T22:09:44.076Z",31- "lastScannedRunId": "20260630220943-c7b8dba65193b129",32- "fileHash": "ef2800a747b5025084e3ef3402b6082e1b6d2f692172aa6e7f1d66f1a83bdd6d",33- "findings": [34- {35- "severity": "HIGH_BUG",36- "vulnSlug": "other-passkey-import-corruption",37- "title": "Credential Exchange passkey imports drop credential material",38- "description": "The import UI advertises FIDO Credential Exchange passkeys and sends parsed entries directly to BATCH_ADD_ENTRIES. The credential-exchange parser emits legacy flat type:\"passkey\" entries, but the batch-add sanitizer only persists the current nested entry.passkey shape. The import can therefore report success while creating login entries with no passkey/private key, leaving users with unusable restored passkeys.",39- "lineNumbers": [40- 579,41- 580,42- 581,43- 636,44- 63745- ],46- "recommendation": "Emit the current nested passkey shape from the credential-exchange parser, or run the same legacy passkey migration/validation used by restoreEntries before BATCH_ADD_ENTRIES. Treat rejected passkeys as explicit import failures.",47- "confidence": "high",48- "producedByRunId": "20260701075706-0feae092f70d6cf5"49- },50- {51- "severity": "HIGH_BUG",52- "vulnSlug": "other-non-atomic-replace",53- "title": "Replace import clears the active vault before replacement commit",54- "description": "In replace mode, the UI calls CLEAR_VAULT_ENTRIES before BATCH_ADD_ENTRIES or the per-entry fallback succeeds. If the add step fails, partially succeeds, or imports corrupted entries, the existing active vault has already been moved to trash and the replacement is not atomic.",55- "lineNumbers": [56- 564,57- 565,58- 579,59- 588,60- 59861- ],62- "recommendation": "Move replace into one background operation that validates/sanitizes all incoming entries, persists the replacement atomically, and leaves the existing active vault untouched on failure.",63- "confidence": "medium",64- "producedByRunId": "20260701075706-0feae092f70d6cf5"65- }66- ],67- "analysisHistory": [68- {69- "runId": "20260701075706-0feae092f70d6cf5",70- "investigatedAt": "2026-07-01T08:03:29.334Z",71- "durationMs": 74088.8,72- "agentType": "codex",73- "model": "gpt-5.5",74- "modelConfig": {75- "model": "gpt-5.5"76- },77- "agentSessionId": "019f1cae-8382-76c3-90c5-149e9e1f7918",78- "findingCount": 2,79- "numTurns": 0.2,80- "phase": "process",81- "costUsd": 0.49242100000000005,82- "usage": {83- "inputTokens": 38977,84- "outputTokens": 3398.4,85- "cacheReadInputTokens": 391168,86- "cacheCreationInputTokens": 087- }88- }89- ],90- "status": "analyzed",91- "gitInfo": {92- "recentCommitters": [],93- "enrichedAt": "2026-07-01T08:03:29.341Z"94- }95-}.deepsec/data/peach-browser/files/src/popup/views/Lock.tsx.json+0 −122
@@ -1,122 +0,0 @@1-{2- "filePath": "src/popup/views/Lock.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "xss",7- "lineNumbers": [8- 4739- ],10- "snippet": " >\n <div class=\"peach-password-strength\">\n <div class={`peach-strength-bar peach-strength-bar--${passwordStrength()}`}>\n <div class=\"peach-strength-fill\"></div>\n </div>",11- "matchedPattern": "template literal in HTML"12- },13- {14- "vulnSlug": "insecure-crypto",15- "lineNumbers": [16- 196,17- 197,18- 206,19- 1056,20- 105721- ],22- "snippet": " } catch (e) {\n const reason = e instanceof Error ? e.message : String(e);\n if (reason.includes(\"user_cancelled\")) return;\n if (reason.includes(\"biometric_key_invalidated\")) {\n await sendMessage({",23- "matchedPattern": "weak cipher algorithm"24- },25- {26- "vulnSlug": "insecure-crypto",27- "lineNumbers": [28- 704,29- 705,30- 706,31- 707,32- 708,33- 709,34- 710,35- 71136- ],37- "snippet": " if (runes.length === 0) {\n runes = Array.from({ length: 60 }, () => ({\n x: Math.random() * W,\n y: Math.random() * H,\n vx: (Math.random() - 0.5) * 3,",38- "matchedPattern": "Math.random in security context"39- },40- {41- "vulnSlug": "secret-in-log",42- "lineNumbers": [43- 13144- ],45- "snippet": " type: \"UNLOCK\",\n payload: { password: password() },\n });",46- "matchedPattern": "Secret variable in log statement"47- },48- {49- "vulnSlug": "secret-in-log",50- "lineNumbers": [51- 23552- ],53- "snippet": " type: \"CREATE_VAULT\",\n payload: { password: password() },\n });",54- "matchedPattern": "Secret variable in log statement"55- },56- {57- "vulnSlug": "secret-in-log",58- "lineNumbers": [59- 83660- ],61- "snippet": " if (!props.hasVault) {\n const cr = await sendMessage({ type: \"CREATE_VAULT\", payload: { password: backupPass() } });\n if (!(cr as { ok: boolean }).ok) throw new Error(\"CREATE_VAULT_FAILED\");",62- "matchedPattern": "Secret variable in log statement"63- },64- {65- "vulnSlug": "secret-in-log",66- "lineNumbers": [67- 98568- ],69- "snippet": " // empty one (an empty key derives a publicly-known vault key).\n const cr = await sendMessage({ type: \"CREATE_VAULT\", payload: { password: newPass() } });\n if (!(cr as { ok: boolean }).ok) throw new Error(\"CREATE_VAULT_FAILED\");",70- "matchedPattern": "Secret variable in log statement"71- }72- ],73- "lastScannedAt": "2026-06-30T22:09:44.076Z",74- "lastScannedRunId": "20260630220943-c7b8dba65193b129",75- "fileHash": "957fe37ea40631a5f7e98aebc380affeff5fa51d2381f021a6562ac8b5629a66",76- "findings": [77- {78- "severity": "MEDIUM",79- "vulnSlug": "other-weak-master-password-policy",80- "title": "Restore-created vaults bypass the normal password strength gate",81- "description": "The normal new-vault path requires at least 12 characters and a zxcvbn score of 2 or better before CREATE_VAULT is sent, but the restore paths do not enforce the same policy. PeachScript restore only requires a non-empty backup password before creating a new vault, and CSV restore only checks length and equality. The traced background CREATE_VAULT handler only rejects an empty password, so these UI restore paths can create an encrypted vault protected by a very weak master password. If the local vault storage is later stolen, this materially weakens the offline attack resistance that the main onboarding flow tries to enforce.",82- "lineNumbers": [83- 823,84- 836,85- 954,86- 958,87- 98588- ],89- "recommendation": "Use one shared master-password policy for onboarding, restore, adoption, and password changes. Enforce the non-negotiable minimum in the background CREATE_VAULT path as well as in the UI.",90- "confidence": "high",91- "producedByRunId": "20260701075706-0feae092f70d6cf5"92- }93- ],94- "analysisHistory": [95- {96- "runId": "20260701075706-0feae092f70d6cf5",97- "investigatedAt": "2026-07-01T08:03:12.971Z",98- "durationMs": 71720.2,99- "agentType": "codex",100- "model": "gpt-5.5",101- "modelConfig": {102- "model": "gpt-5.5"103- },104- "agentSessionId": "019f1cae-8389-7bf1-af72-8787e1f8e8ba",105- "findingCount": 1,106- "numTurns": 0.2,107- "phase": "process",108- "costUsd": 0.4486958,109- "usage": {110- "inputTokens": 37432.6,111- "outputTokens": 3352,112- "cacheReadInputTokens": 321945.6,113- "cacheCreationInputTokens": 0114- }115- }116- ],117- "status": "analyzed",118- "gitInfo": {119- "recentCommitters": [],120- "enrichedAt": "2026-07-01T08:03:12.979Z"121- }122-}.deepsec/data/peach-browser/files/src/popup/views/NoteEditor.tsx.json+0 −91
@@ -1,91 +0,0 @@1-{2- "filePath": "src/popup/views/NoteEditor.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "xss",7- "lineNumbers": [8- 115,9- 12210- ],11- "snippet": " onMount(() => {\n if (!editorRef) return;\n editorRef.innerHTML = toHtml(props.initialContent);\n if (props.autoFocus !== false) editorRef.focus();\n props.onReady?.(editorRef);",12- "matchedPattern": "innerHTML assignment"13- },14- {15- "vulnSlug": "rce",16- "lineNumbers": [17- 148,18- 149,19- 15020- ],21- "snippet": " const isLetterKey = e.key.length === 1;\n if (isLetterKey) return;\n if (mod && e.key === \"b\") { e.preventDefault(); exec(\"bold\"); }\n if (mod && e.key === \"i\") { e.preventDefault(); exec(\"italic\"); }\n if (mod && e.key === \"u\") { e.preventDefault(); exec(\"underline\"); }",22- "matchedPattern": "exec with string"23- },24- {25- "vulnSlug": "dangerous-html",26- "lineNumbers": [27- 11528- ],29- "snippet": "\n onMount(() => {\n if (!editorRef) return;\n editorRef.innerHTML = toHtml(props.initialContent);\n if (props.autoFocus !== false) editorRef.focus();\n props.onReady?.(editorRef);\n });\n",30- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"31- },32- {33- "vulnSlug": "dangerous-html",34- "lineNumbers": [35- 12236- ],37- "snippet": "\n createEffect(on(() => props.entryKey, (key, prevKey) => {\n if (prevKey === undefined || key === prevKey || !editorRef) return;\n editorRef.innerHTML = toHtml(props.initialContent);\n props.onReady?.(editorRef);\n }));\n\n function exec(cmd: string, value?: string) {",38- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"39- }40- ],41- "lastScannedAt": "2026-06-30T22:09:44.054Z",42- "lastScannedRunId": "20260630220943-c7b8dba65193b129",43- "fileHash": "baf1ca85f481256fba8e71cbd850cfe57dbb451bbbbfd47c8f81169bf8a78b99",44- "findings": [45- {46- "severity": "BUG",47- "vulnSlug": "other-logic-bug",48- "title": "Inline note keyboard shortcuts are unreachable",49- "description": "handleKeyDown returns early for every single-character key before checking the Ctrl/Cmd+B, Ctrl/Cmd+I, and Ctrl/Cmd+U shortcuts. Since those shortcuts use single-character keys, the exec calls are never reached and the advertised formatting shortcuts do not work.",50- "lineNumbers": [51- 142,52- 146,53- 147,54- 148,55- 149,56- 15057- ],58- "recommendation": "Check the modifier shortcuts before returning for ordinary text input, or make the early return conditional on no Ctrl/Cmd modifier being pressed.",59- "confidence": "high",60- "producedByRunId": "20260701075706-0feae092f70d6cf5"61- }62- ],63- "analysisHistory": [64- {65- "runId": "20260701075706-0feae092f70d6cf5",66- "investigatedAt": "2026-07-01T08:03:12.998Z",67- "durationMs": 71720.2,68- "agentType": "codex",69- "model": "gpt-5.5",70- "modelConfig": {71- "model": "gpt-5.5"72- },73- "agentSessionId": "019f1cae-8389-7bf1-af72-8787e1f8e8ba",74- "findingCount": 1,75- "numTurns": 0.2,76- "phase": "process",77- "costUsd": 0.4486958,78- "usage": {79- "inputTokens": 37432.6,80- "outputTokens": 3352,81- "cacheReadInputTokens": 321945.6,82- "cacheCreationInputTokens": 083- }84- }85- ],86- "status": "analyzed",87- "gitInfo": {88- "recentCommitters": [],89- "enrichedAt": "2026-07-01T08:03:13.006Z"90- }91-}.deepsec/data/peach-browser/files/src/popup/views/NotesEditor.tsx.json+0 −90
@@ -1,90 +0,0 @@1-{2- "filePath": "src/popup/views/NotesEditor.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "xss",7- "lineNumbers": [8- 1029- ],10- "snippet": " if (titleRef) titleRef.textContent = props.note?.title ?? \"\";\n // M15/THREAT-006: sanitize stored note body before assigning to innerHTML.\n if (bodyRef) bodyRef.innerHTML = toHtml(props.note?.notes ?? \"\");\n\n if (props.autoFocusTitle && titleRef) {",11- "matchedPattern": "innerHTML assignment"12- },13- {14- "vulnSlug": "rce",15- "lineNumbers": [16- 238,17- 239,18- 24019- ],20- "snippet": " onKeyDown={(e) => {\n const mod = e.metaKey || e.ctrlKey;\n if (mod && e.key === \"b\") { e.preventDefault(); exec(\"bold\"); }\n if (mod && e.key === \"i\") { e.preventDefault(); exec(\"italic\"); }\n if (mod && e.key === \"u\") { e.preventDefault(); exec(\"underline\"); }",21- "matchedPattern": "exec with string"22- },23- {24- "vulnSlug": "insecure-crypto",25- "lineNumbers": [26- 20227- ],28- "snippet": " style={contentHeight() !== null ? { height: `${contentHeight()}px` } : undefined}\n >\n {/* Desktop toolbar — above content */}\n <Show when={!isNativePlatform()}>\n <NoteToolbar exec={execFn} />",29- "matchedPattern": "weak cipher algorithm"30- },31- {32- "vulnSlug": "dangerous-html",33- "lineNumbers": [34- 10235- ],36- "snippet": " // Populate editor content\n if (titleRef) titleRef.textContent = props.note?.title ?? \"\";\n // M15/THREAT-006: sanitize stored note body before assigning to innerHTML.\n if (bodyRef) bodyRef.innerHTML = toHtml(props.note?.notes ?? \"\");\n\n if (props.autoFocusTitle && titleRef) {\n setTimeout(() => {\n titleRef.focus();",37- "matchedPattern": "dangerouslySetInnerHTML from props (trace source)"38- }39- ],40- "lastScannedAt": "2026-06-30T22:09:44.054Z",41- "lastScannedRunId": "20260630220943-c7b8dba65193b129",42- "fileHash": "358a9ef6c2a9f102399b449c0e73144e810db52e1afbc8331b86301f5b6f9249",43- "findings": [44- {45- "severity": "HIGH",46- "vulnSlug": "dangerous-html",47- "title": "Standalone notes editor accepts and autosaves unsanitized pasted HTML",48- "description": "Stored note bodies are sanitized with toHtml before being assigned to innerHTML on mount, but the editable body has no paste handler and onInput saves bodyRef.innerHTML directly. A hostile page can place rich HTML on the clipboard and, if the user pastes it into a secure note, the browser inserts attacker-controlled tags and attributes into the extension popup DOM before any sanitizer runs; the autosave path then persists that raw HTML. NoteEditorInline already treats this as a threat and forces paste to plain text, but NotesEditor does not. Extension CSP may limit inline script execution, but dangerous HTML such as styles, iframes, forms, and future unsanitized render paths remain a privileged-extension HTML injection surface.",49- "lineNumbers": [50- 47,51- 49,52- 98,53- 102,54- 227,55- 23356- ],57- "recommendation": "Add the same paste-as-plain-text handler used by NoteEditorInline to NotesEditor, and sanitize or normalize the body before every save rather than only when loading stored content.",58- "confidence": "medium",59- "producedByRunId": "20260701075706-0feae092f70d6cf5"60- }61- ],62- "analysisHistory": [63- {64- "runId": "20260701075706-0feae092f70d6cf5",65- "investigatedAt": "2026-07-01T08:03:13.006Z",66- "durationMs": 71720.2,67- "agentType": "codex",68- "model": "gpt-5.5",69- "modelConfig": {70- "model": "gpt-5.5"71- },72- "agentSessionId": "019f1cae-8389-7bf1-af72-8787e1f8e8ba",73- "findingCount": 1,74- "numTurns": 0.2,75- "phase": "process",76- "costUsd": 0.4486958,77- "usage": {78- "inputTokens": 37432.6,79- "outputTokens": 3352,80- "cacheReadInputTokens": 321945.6,81- "cacheCreationInputTokens": 082- }83- }84- ],85- "status": "analyzed",86- "gitInfo": {87- "recentCommitters": [],88- "enrichedAt": "2026-07-01T08:03:13.015Z"89- }90-}.deepsec/data/peach-browser/files/src/popup/views/NotesMode.tsx.json+0 −21
@@ -1,21 +0,0 @@1-{2- "filePath": "src/popup/views/NotesMode.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 79,9- 9410- ],11- "snippet": " return (\n <div class=\"peach-notes-mode\" data-phase={phase()}>\n {/* TOC — fades out when editor opens */}\n <div\n class=\"peach-notes-toc-wrapper\"",12- "matchedPattern": "weak cipher algorithm"13- }14- ],15- "lastScannedAt": "2026-06-30T22:09:44.028Z",16- "lastScannedRunId": "20260630220943-c7b8dba65193b129",17- "fileHash": "43363c3d45e1857f9a96feebff4683be43c3534205351efb1c234a4a35656584",18- "findings": [],19- "analysisHistory": [],20- "status": "error"21-}.deepsec/data/peach-browser/files/src/popup/views/NotesTOC.tsx.json+0 −21
@@ -1,21 +0,0 @@1-{2- "filePath": "src/popup/views/NotesTOC.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 26,9- 2710- ],11- "snippet": " if (!q) return props.notes;\n return props.notes.filter((n) =>\n (n.title ?? \"\").toLowerCase().includes(q) ||\n (n.notes ?? \"\").toLowerCase().includes(q)\n );",12- "matchedPattern": "weak cipher algorithm"13- }14- ],15- "lastScannedAt": "2026-06-30T22:09:44.028Z",16- "lastScannedRunId": "20260630220943-c7b8dba65193b129",17- "fileHash": "5cbb178e059c4a8d09b460163b24f2eeb1251ad20641bc61a0293f30f75cd31b",18- "findings": [],19- "analysisHistory": [],20- "status": "error"21-}.deepsec/data/peach-browser/files/src/popup/views/Onboarding.tsx.json+0 −20
@@ -1,20 +0,0 @@1-{2- "filePath": "src/popup/views/Onboarding.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 319- ],10- "snippet": " headline: \"Everything\\nyou need.\",\n body: [\n \"Free includes passwords, cards, secure notes, two-factor codes, autofill, strong passwords, and breach checks.\",\n \"Pro adds encrypted sync, Peach Codex paper restore, passkeys, advanced vault health, canary entries, and duress tools.\",\n ],",11- "matchedPattern": "weak cipher algorithm"12- }13- ],14- "lastScannedAt": "2026-06-30T22:09:44.028Z",15- "lastScannedRunId": "20260630220943-c7b8dba65193b129",16- "fileHash": "f95796d8cf683bd01f039c234ebf68d4382bd776b690b3b08987747ef2064343",17- "findings": [],18- "analysisHistory": [],19- "status": "error"20-}.deepsec/data/peach-browser/files/src/popup/views/Panic.tsx.json+0 −42
@@ -1,42 +0,0 @@1-{2- "filePath": "src/popup/views/Panic.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 8,9- 37,10- 39,11- 41,12- 42,13- 43,14- 44,15- 46,16- 49,17- 58,18- 60,19- 78,20- 87,21- 93,22- 96,23- 103,24- 108,25- 111,26- 133,27- 146,28- 147,29- 149,30- 15131- ],32- "snippet": " onCancel: () => void;\n onOpenExport: () => void;\n onDestroyed: () => void | Promise<void>;\n}\n",33- "matchedPattern": "weak cipher algorithm"34- }35- ],36- "lastScannedAt": "2026-06-30T22:09:44.028Z",37- "lastScannedRunId": "20260630220943-c7b8dba65193b129",38- "fileHash": "f1d00ffeebfc8ae8d4ce7062f6070c1fdfa8686896c3a99882e6ae368a5acf1b",39- "findings": [],40- "analysisHistory": [],41- "status": "error"42-}.deepsec/data/peach-browser/files/src/popup/views/RecoveryWordDisplay.tsx.json+0 −21
@@ -1,21 +0,0 @@1-{2- "filePath": "src/popup/views/RecoveryWordDisplay.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 139,9- 23510- ],11- "snippet": " the Android download sink lands in world-readable public Downloads\n (audit MEDIUM). Offer a transient clipboard copy the user routes\n into their own secure store instead. Desktop keeps the\n user-chosen-location .txt download. */}\n <Show",12- "matchedPattern": "weak cipher algorithm"13- }14- ],15- "lastScannedAt": "2026-06-30T22:09:44.028Z",16- "lastScannedRunId": "20260630220943-c7b8dba65193b129",17- "fileHash": "9ae920fbd6e111fa0a726498762087c5db62ef08193e7a9e75557a7f9be272a9",18- "findings": [],19- "analysisHistory": [],20- "status": "error"21-}.deepsec/data/peach-browser/files/src/popup/views/Settings.tsx.json+0 −127
not shown here: too long for one page
.deepsec/data/peach-browser/files/src/popup/views/Vault.tsx.json+0 −72
@@ -1,72 +0,0 @@1-{2- "filePath": "src/popup/views/Vault.tsx",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "xss",7- "lineNumbers": [8- 495,9- 545,10- 59411- ],12- "snippet": " >\n <Show when={isVirtualized()}>\n <div style={{ height: `${visibleRange().start * ROW_HEIGHT}px` }} aria-hidden=\"true\" />\n </Show>\n <For each={windowedEntries()}>",13- "matchedPattern": "template literal in HTML"14- },15- {16- "vulnSlug": "non-atomic-read-delete",17- "lineNumbers": [18- 60919- ],20- "snippet": "\n {/* Bottom bar — new entry normally, delete action in select mode */}\n <Show\n when={selecting()}\n fallback={\n <button class=\"peach-new-entry-row\" onClick={props.onAdd}>",21- "matchedPattern": "Read-then-modify without transaction — potential TOCTOU race"22- },23- {24- "vulnSlug": "non-atomic-read-delete",25- "lineNumbers": [26- 62227- ],28- "snippet": " <button\n class={`peach-select-action-bar${selectedIds().size === 0 ? \" is-empty\" : \"\"}`}\n disabled={selectedIds().size === 0 || deleting()}\n onClick={() => void handleDeleteSelected()}\n >\n <svg width=\"12\" height=\"13\" viewBox=\"0 0 12 13\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.1\" stroke-linecap=\"round\" stroke-linejoin=\"round\">",29- "matchedPattern": "Read-then-modify without transaction — potential TOCTOU race"30- },31- {32- "vulnSlug": "non-atomic-read-delete",33- "lineNumbers": [34- 63135- ],36- "snippet": " when={selectedIds().size > 0}\n fallback={<span>select entries to delete</span>}\n >\n <span>\n {deleting()\n ? \"moving to trash…\"",37- "matchedPattern": "Read-then-modify without transaction — potential TOCTOU race"38- }39- ],40- "lastScannedAt": "2026-06-30T22:09:44.248Z",41- "lastScannedRunId": "20260630220943-c7b8dba65193b129",42- "fileHash": "b0d9682f3aac172e22df074aef0b3569d87157dc14d3dc72370f41f62654c0a7",43- "findings": [],44- "analysisHistory": [45- {46- "runId": "20260701075706-0feae092f70d6cf5",47- "investigatedAt": "2026-07-01T08:03:29.349Z",48- "durationMs": 74088.8,49- "agentType": "codex",50- "model": "gpt-5.5",51- "modelConfig": {52- "model": "gpt-5.5"53- },54- "agentSessionId": "019f1cae-8382-76c3-90c5-149e9e1f7918",55- "findingCount": 0,56- "numTurns": 0.2,57- "phase": "process",58- "costUsd": 0.49242100000000005,59- "usage": {60- "inputTokens": 38977,61- "outputTokens": 3398.4,62- "cacheReadInputTokens": 391168,63- "cacheCreationInputTokens": 064- }65- }66- ],67- "status": "analyzed",68- "gitInfo": {69- "recentCommitters": [],70- "enrichedAt": "2026-07-01T08:03:29.356Z"71- }72-}.deepsec/data/peach-browser/files/src/popup/views/vault-search.ts.json+0 −20
@@ -1,20 +0,0 @@1-{2- "filePath": "src/popup/views/vault-search.ts",3- "projectId": "peach-browser",4- "candidates": [5- {6- "vulnSlug": "insecure-crypto",7- "lineNumbers": [8- 1239- ],10- "snippet": " if (filters.selectedTags.size > 0) {\n result = result.filter((entry) =>\n Array.from(filters.selectedTags).every((tag) => entry.tags?.includes(tag))\n );\n }",11- "matchedPattern": "weak cipher algorithm"12- }13- ],14- "lastScannedAt": "2026-06-30T22:09:44.026Z",15- "lastScannedRunId": "20260630220943-c7b8dba65193b129",16- "fileHash": "a698b1416047282c99b0fa2b4976c08b960e091832db66c3de09d537471b34d3",17- "findings": [],18- "analysisHistory": [],19- "status": "error"20-}.deepsec/data/peach-browser/files/src/shared/backup.ts.json+0 −29
not shown here: too long for one page
.deepsec/data/peach-browser/files/src/shared/chirp-adopt.ts.json+0 −20
not shown here: too long for one page
.deepsec/data/peach-browser/files/src/shared/chirp-handshake.ts.json+0 −26
not shown here: too long for one page
.deepsec/data/peach-browser/files/src/shared/chirp-pairing.ts.json+0 −22
not shown here: too long for one page
.deepsec/data/peach-browser/files/src/shared/chirp-payload.ts.json+0 −20
not shown here: too long for one page
.deepsec/data/peach-browser/files/src/shared/chirp-sync-transport.ts.json+0 −28
not shown here: too long for one page
.deepsec/data/peach-browser/files/src/shared/codex-types.ts.json+0 −34
not shown here: too long for one page
.deepsec/data/peach-browser/files/src/shared/config.ts.json+0 −20
not shown here: too long for one page
.deepsec/data/peach-browser/files/src/shared/device-manifest.ts.json+0 −28
not shown here: too long for one page
.deepsec/data/peach-browser/files/src/shared/domain.ts.json+0 −20
not shown here: too long for one page
.deepsec/data/peach-browser/files/src/shared/generate.ts.json+0 −24
not shown here: too long for one page
.deepsec/data/peach-browser/files/src/shared/import.ts.json+0 −23
not shown here: too long for one page
.deepsec/data/peach-browser/files/src/shared/local-network.ts.json+0 −20
not shown here: too long for one page
.deepsec/data/peach-browser/files/src/shared/native.ts.json+0 −20
not shown here: too long for one page
.deepsec/data/peach-browser/files/src/shared/noise-js.ts.json+0 −25
not shown here: too long for one page
.deepsec/data/peach-browser/files/src/shared/passkey-utils.ts.json+0 −20
not shown here: too long for one page
.deepsec/data/peach-browser/files/src/shared/recovery-word-list.ts.json+0 −22
not shown here: too long for one page
.deepsec/data/peach-browser/files/src/shared/recovery-words.ts.json+0 −33
not shown here: too long for one page
.deepsec/data/peach-browser/files/src/shared/shortcuts.ts.json+0 −25
not shown here: too long for one page
.deepsec/data/peach-browser/files/src/shared/sync-identity.ts.json+0 −32
not shown here: too long for one page
.deepsec/data/peach-browser/files/src/shared/sync-types.ts.json+0 −26
not shown here: too long for one page
.deepsec/data/peach-browser/files/src/shared/types.ts.json+0 −39
not shown here: too long for one page
.deepsec/data/peach-browser/files/vite.config.mobile.ts.json+0 −20
not shown here: too long for one page
.deepsec/data/peach-browser/files/vite.config.ts.json+0 −47
not shown here: too long for one page
.deepsec/data/peach-browser/project.json+0 −5
@@ -1,5 +0,0 @@1-{2- "projectId": "peach-browser",3- "rootPath": "/Users/june/projects/peach-browser",4- "createdAt": "2026-06-30T21:31:37.103Z"5-}.deepsec/data/peach-browser/runs/20260630213317-2fd9f00652156d33.json+0 −93
not shown here: too long for one page
.deepsec/data/peach-browser/runs/20260630213408-3897a42f3fa0e1ed.json+0 −27
not shown here: too long for one page
.deepsec/data/peach-browser/runs/20260630220437-80eef1872fbf5736.json+0 −93
not shown here: too long for one page
.deepsec/data/peach-browser/runs/20260630220521-d9fdba12b7a39539.json+0 −20
not shown here: too long for one page
.deepsec/data/peach-browser/runs/20260630220859-de7f3b7bf11d1260.json+0 −93
not shown here: too long for one page
.deepsec/data/peach-browser/runs/20260630220943-c7b8dba65193b129.json+0 −93
not shown here: too long for one page
.deepsec/data/peach-browser/runs/20260630221012-35253a56b27eeb43.json+0 −20
not shown here: too long for one page
.deepsec/data/peach-browser/runs/20260701075706-0feae092f70d6cf5.json+0 −27
not shown here: too long for one page
.deepsec/data/peach-browser/tech.json+0 −12
not shown here: too long for one page
src/drafts/AUTOFILL-TESTS/INTEGRATION-CONTRACT.md+0 −136
not shown here: too long for one page
src/drafts/PCH-02-FIXES/INTEGRATION-CONTRACT.md+0 −100
not shown here: too long for one page
src/drafts/PCH-02-FIXES/PCH-02-001-vault-patch-sanitizers.patch.md+0 −124
not shown here: too long for one page
src/drafts/PCH-02-FIXES/PCH-02-003-sas-trust-model-comment.patch.md+0 −73
not shown here: too long for one page
src/drafts/PCH-02-FIXES/PCH-02-004-exact-pin-deps.patch.md+0 −84
not shown here: too long for one page
src/drafts/PCH-02-FIXES/PCH-02-005-legacy-pbkdf2-doc.patch.md+0 −118
not shown here: too long for one page
src/drafts/PCH-02-FIXES/PCH-02-007-hibp-add-padding.patch.md+0 −45
not shown here: too long for one page
src/drafts/PCH-02-FIXES/PCH-02-008-binaryreader-bounds.patch.md+0 −123
not shown here: too long for one page
src/drafts/PCH-02-FIXES/PCH-02-010-clipboard-hash-match.patch.md+0 −253
not shown here: too long for one page
src/drafts/PCH-02-FIXES/README.md+0 −27
not shown here: too long for one page
src/drafts/PCH-02-FIXES/WASM-PROVENANCE.md+0 −57
not shown here: too long for one page
src/drafts/PCH-02-FIXES/clipboard.hash-match.test.ts+0 −148
not shown here: too long for one page
src/drafts/PCH-02-FIXES/peachPayload.binaryreader-bounds.test.ts+0 −74
not shown here: too long for one page
src/drafts/PCH-02-FIXES/vault.hibp-padding.test.ts+0 −85
not shown here: too long for one page
src/drafts/PCH-02-FIXES/vault.update-entry-sanitize.test.ts+0 −213
not shown here: too long for one page
src/drafts/TEST-EXPANSION/INTEGRATION-CONTRACT.md+0 −174
not shown here: too long for one page
src/drafts/TEST-EXPANSION/argon2id.kat.test.ts+0 −140
not shown here: too long for one page
src/drafts/TEST-EXPANSION/backup.nfc-fallback.test.ts+0 −130
not shown here: too long for one page
src/drafts/TEST-EXPANSION/clipboard.double-failure.test.ts+0 −170
not shown here: too long for one page
src/drafts/TEST-EXPANSION/codex.multistripe.test.ts+0 −183
not shown here: too long for one page
src/drafts/TEST-EXPANSION/content.orchestration.test.ts+0 −175
not shown here: too long for one page
src/drafts/TEST-EXPANSION/generate.empty-pool.test.ts+0 −130
not shown here: too long for one page
src/drafts/TEST-EXPANSION/import.csv.formulainjection.test.ts+0 −138
not shown here: too long for one page
src/drafts/TEST-EXPANSION/license.verification.test.ts+0 −245
not shown here: too long for one page
src/drafts/TEST-EXPANSION/messages.batch-sanitize.test.ts+0 −198
not shown here: too long for one page
src/drafts/TEST-EXPANSION/messages.edit-origin.test.ts+0 −230
not shown here: too long for one page
src/drafts/TEST-EXPANSION/ml.predictbatch.test.ts+0 −185
not shown here: too long for one page
src/drafts/TEST-EXPANSION/passkey.ed25519.test.ts+0 −230
not shown here: too long for one page
src/drafts/TEST-EXPANSION/pdf-import.sandbox.test.ts+0 −111
not shown here: too long for one page
src/drafts/TEST-EXPANSION/peachPayload.bomb.test.ts+0 −257
not shown here: too long for one page
src/drafts/TEST-EXPANSION/recovery-words.checksum.test.ts+0 −105
not shown here: too long for one page
src/drafts/TEST-EXPANSION/recovery-words.integrity.test.ts+0 −57
not shown here: too long for one page
src/drafts/TEST-EXPANSION/recovery-words.kek.test.ts+0 −140
not shown here: too long for one page
src/drafts/TEST-EXPANSION/sync.disconnect.test.ts+0 −236
not shown here: too long for one page
src/drafts/TEST-EXPANSION/totp.rfc6238.test.ts+0 −137
not shown here: too long for one page
src/drafts/TEST-EXPANSION/vault.consolidate-passkeys.test.ts+0 −231
not shown here: too long for one page
src/drafts/TEST-EXPANSION/vault.corruption.test.ts+0 −209
not shown here: too long for one page
src/drafts/TEST-EXPANSION/vault.purge-trash.test.ts+0 −238
not shown here: too long for one page
src/drafts/TEST-EXPANSION/vault.rotate-sync-identity.test.ts+0 −223
not shown here: too long for one page