TODO.md names this as the repo's Next Step and no issue exists for it: "Pre-1.0 security review of the extension (key handling, DEBUG mode policy, RPC input validation) before any 1.0rc tag. Individual filed issues are parts of it, but the review is broader than any of them."
This is the one remaining piece of work between the current next and a version that can honestly be called 1.0.0. The milestone 1.0.0 PR (#190) has landed on main; that work was driven issue by issue and nobody has yet read the whole extension end to end with an attacker's eye.
Scope
An independent review session reads the whole tree on next and reports on, at minimum:
Key handling: src/shared/vault.js, src/shared/wallet.js, every path that decrypts, derives, displays or exports key material, what clears it and when, and where a secret could reach a log, a message, storage, or the DOM unexpectedly.
DEBUG mode: every DEBUG conditional in src/ against the policy in README.md and RULES.md (banner and test mnemonic only, no skipped functionality, no bypassed security), and that a release build cannot be made to behave as a debug one.
RPC and dApp input validation: everything src/background/index.js accepts from a page through src/content/inpage.js and src/content/index.js (method names, params, chain ids, addresses, typed data), everything it accepts from the configured RPC endpoint and the explorer, and everything the approval screens render from those inputs.
The permission model: site connection, origin checks, what a connected site can do without a prompt, and what a page can learn about the wallet without being connected.
Storage: what is persisted, what is encrypted, and whether a hostile stored record can still reach a signing path.
Definition of done
A review summary is posted on this issue: what was examined, what was found, and the boundary of what was not examined.
Every finding is filed as its own Gitea issue with a severity, a reproduction, and a definition of done; a finding that puts funds or key material at risk is added to milestone 1.0.0.
Findings are fixed and reviewed through the normal PR workflow; this issue closes when no finding rated funds-or-keys-at-risk remains open.
The result of the review is recorded in README.md or TODO.md at the width it actually holds, not wider.
model: claude-fable-5
`TODO.md` names this as the repo's Next Step and no issue exists for it: "Pre-1.0 security review of the extension (key handling, DEBUG mode policy, RPC input validation) before any 1.0rc tag. Individual filed issues are parts of it, but the review is broader than any of them."
This is the one remaining piece of work between the current `next` and a version that can honestly be called 1.0.0. The milestone 1.0.0 PR (https://git.eeqj.de/sneak/AutistMask/pulls/190) has landed on `main`; that work was driven issue by issue and nobody has yet read the whole extension end to end with an attacker's eye.
## Scope
An independent review session reads the whole tree on `next` and reports on, at minimum:
- Key handling: `src/shared/vault.js`, `src/shared/wallet.js`, every path that decrypts, derives, displays or exports key material, what clears it and when, and where a secret could reach a log, a message, storage, or the DOM unexpectedly.
- DEBUG mode: every `DEBUG` conditional in `src/` against the policy in `README.md` and `RULES.md` (banner and test mnemonic only, no skipped functionality, no bypassed security), and that a release build cannot be made to behave as a debug one.
- RPC and dApp input validation: everything `src/background/index.js` accepts from a page through `src/content/inpage.js` and `src/content/index.js` (method names, params, chain ids, addresses, typed data), everything it accepts from the configured RPC endpoint and the explorer, and everything the approval screens render from those inputs.
- The permission model: site connection, origin checks, what a connected site can do without a prompt, and what a page can learn about the wallet without being connected.
- Storage: what is persisted, what is encrypted, and whether a hostile stored record can still reach a signing path.
## Definition of done
- [ ] A review summary is posted on this issue: what was examined, what was found, and the boundary of what was not examined.
- [ ] Every finding is filed as its own Gitea issue with a severity, a reproduction, and a definition of done; a finding that puts funds or key material at risk is added to milestone 1.0.0.
- [ ] Findings are fixed and reviewed through the normal PR workflow; this issue closes when no finding rated funds-or-keys-at-risk remains open.
- [ ] The result of the review is recorded in `README.md` or `TODO.md` at the width it actually holds, not wider.
model: claude-fable-5
clawbot
added this to the 1.0.0 milestone 2026-09-21 09:19:11 +02:00
Reviewed the whole tree at 99292b9 with an attacker's eye, line by line:
key handling (vault.js, wallet.js, and every decrypt/derive/display/export
path), the DEBUG build boundary, everything the background accepts from a page
and from the configured RPC endpoint and explorer, the approval and verification
path, what the approval screens render, the permission model, and storage.
Found sound and not filed: the background populates every dApp transaction
itself, displays that object, and verifies the signed artifact against it field
by field, refusing transaction types other than 0/1/2 and any authorizationList/blob fields, and requiring the artifact to be the canonical
re-encoding of the approved transaction; secrets are decrypted only in the
popup, never cross the message boundary, and are wiped on view-leave under a
generation guard; the stored-record gate refuses an unusable profile before
normalization and uses own-property tests against prototype keys; extended keys
are round-trip checked on import; the CSP is pinned in both directions; there is
no externally_connectable; and isExtensionSender fails closed. Connecting an
opaque or non-http origin was considered and not filed: content scripts do not
run in sandboxed subframes (all_frames defaults to false) and file:// access
is off by default, so the path is not reachable in a default install.
Findings filed
Funds or keys at risk (milestone 1.0.0):
#399 — the fee backstop is checked
per field, so a page (or a hostile RPC) can produce a max fee of thousands of
ETH, and the wallet's own send path applies no fee ceiling at all.
Privacy:
#398 — the EIP-6963 provider UUID is
persisted and re-announced to every page, so any site can read a stable
per-install cross-site tracking identifier (milestone 1.0.0).
#410 — runtime debug mode logs full
request URLs and bodies, which can include an RPC credential.
Hardening:
#400 — typed-data signing shows no
warning for allowance shapes (Permit/Permit2) and displays a primaryType it
does not enforce (milestone 1.0.0).
#402 — remembered site permissions
are keyed by hostname only, dropping the scheme and port.
#403 — the personal_sign display
decodes only hex and shows control and bidirectional characters verbatim,
without the raw bytes.
#404 — a page-supplied transaction
nonce is honoured and shown only as a bare number.
#405 — a page can open unbounded
concurrent approval windows.
#406 — a session (non-remembered)
connection cannot be revoked from the UI, and the remove-site message does
nothing.
#407 — origin attribution falls back
to the top-level tab URL and then to the page-supplied origin.
Decisions (assigned to sneak):
#401 — Argon2id runs at INTERACTIVE
cost for the at-rest vault key (milestone 1.0.0).
#408 — a connected site switches the
wallet's network with no prompt.
#409 — eth_sign is signed as a
personal message while its warning and the README describe raw-hash signing.
Not examined
The two end-to-end suites were not run (per the review brief). The vendored
phishing blocklist and the bundled token-list data were not audited entry by
entry — only the code that consults them. ethers and libsodium-wrappers-sumo are trusted as audited third parties. No live-network
or real-funds pass was done; that is #385.
Disclosures
The planned independent finder-and-adversarial-verification pass did not
complete: 17 of 18 agents stopped at this account's model limit, so the merge,
verification and completeness-critic stages did not run. These findings rest
on the reviewer's direct line-by-line reading of the tree (and one completed
finder), each re-checked against the code before filing, not on the full
skeptic-panel verification the method intends.
Reviewed at 99292b9; origin/next has since advanced to 9ac7df0 (only #351), which touches none of these
findings.
Model: fable-5-1 (review); opus-4-8 (filing)
## Pre-1.0 security review — summary
Reviewed the whole tree at `99292b9` with an attacker's eye, line by line:
key handling (`vault.js`, `wallet.js`, and every decrypt/derive/display/export
path), the DEBUG build boundary, everything the background accepts from a page
and from the configured RPC endpoint and explorer, the approval and verification
path, what the approval screens render, the permission model, and storage.
Found sound and not filed: the background populates every dApp transaction
itself, displays that object, and verifies the signed artifact against it field
by field, refusing transaction types other than 0/1/2 and any
`authorizationList`/blob fields, and requiring the artifact to be the canonical
re-encoding of the approved transaction; secrets are decrypted only in the
popup, never cross the message boundary, and are wiped on view-leave under a
generation guard; the stored-record gate refuses an unusable profile before
normalization and uses own-property tests against prototype keys; extended keys
are round-trip checked on import; the CSP is pinned in both directions; there is
no `externally_connectable`; and `isExtensionSender` fails closed. Connecting an
opaque or non-http origin was considered and not filed: content scripts do not
run in sandboxed subframes (`all_frames` defaults to false) and `file://` access
is off by default, so the path is not reachable in a default install.
### Findings filed
Funds or keys at risk (milestone 1.0.0):
- https://git.eeqj.de/sneak/AutistMask/issues/399 — the fee backstop is checked
per field, so a page (or a hostile RPC) can produce a max fee of thousands of
ETH, and the wallet's own send path applies no fee ceiling at all.
Privacy:
- https://git.eeqj.de/sneak/AutistMask/issues/398 — the EIP-6963 provider UUID is
persisted and re-announced to every page, so any site can read a stable
per-install cross-site tracking identifier (milestone 1.0.0).
- https://git.eeqj.de/sneak/AutistMask/issues/410 — runtime debug mode logs full
request URLs and bodies, which can include an RPC credential.
Hardening:
- https://git.eeqj.de/sneak/AutistMask/issues/400 — typed-data signing shows no
warning for allowance shapes (Permit/Permit2) and displays a `primaryType` it
does not enforce (milestone 1.0.0).
- https://git.eeqj.de/sneak/AutistMask/issues/402 — remembered site permissions
are keyed by hostname only, dropping the scheme and port.
- https://git.eeqj.de/sneak/AutistMask/issues/403 — the personal_sign display
decodes only hex and shows control and bidirectional characters verbatim,
without the raw bytes.
- https://git.eeqj.de/sneak/AutistMask/issues/404 — a page-supplied transaction
nonce is honoured and shown only as a bare number.
- https://git.eeqj.de/sneak/AutistMask/issues/405 — a page can open unbounded
concurrent approval windows.
- https://git.eeqj.de/sneak/AutistMask/issues/406 — a session (non-remembered)
connection cannot be revoked from the UI, and the remove-site message does
nothing.
- https://git.eeqj.de/sneak/AutistMask/issues/407 — origin attribution falls back
to the top-level tab URL and then to the page-supplied origin.
Decisions (assigned to sneak):
- https://git.eeqj.de/sneak/AutistMask/issues/401 — Argon2id runs at INTERACTIVE
cost for the at-rest vault key (milestone 1.0.0).
- https://git.eeqj.de/sneak/AutistMask/issues/408 — a connected site switches the
wallet's network with no prompt.
- https://git.eeqj.de/sneak/AutistMask/issues/409 — eth_sign is signed as a
personal message while its warning and the README describe raw-hash signing.
### Not examined
The two end-to-end suites were not run (per the review brief). The vendored
phishing blocklist and the bundled token-list data were not audited entry by
entry — only the code that consults them. `ethers` and
`libsodium-wrappers-sumo` are trusted as audited third parties. No live-network
or real-funds pass was done; that is
https://git.eeqj.de/sneak/AutistMask/issues/385.
### Disclosures
- The planned independent finder-and-adversarial-verification pass did not
complete: 17 of 18 agents stopped at this account's model limit, so the merge,
verification and completeness-critic stages did not run. These findings rest
on the reviewer's direct line-by-line reading of the tree (and one completed
finder), each re-checked against the code before filing, not on the full
skeptic-panel verification the method intends.
- Reviewed at `99292b9`; `origin/next` has since advanced to `9ac7df0` (only
https://git.eeqj.de/sneak/AutistMask/issues/351), which touches none of these
findings.
Model: fable-5-1 (review); opus-4-8 (filing)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
TODO.mdnames this as the repo's Next Step and no issue exists for it: "Pre-1.0 security review of the extension (key handling, DEBUG mode policy, RPC input validation) before any 1.0rc tag. Individual filed issues are parts of it, but the review is broader than any of them."This is the one remaining piece of work between the current
nextand a version that can honestly be called 1.0.0. The milestone 1.0.0 PR (#190) has landed onmain; that work was driven issue by issue and nobody has yet read the whole extension end to end with an attacker's eye.Scope
An independent review session reads the whole tree on
nextand reports on, at minimum:src/shared/vault.js,src/shared/wallet.js, every path that decrypts, derives, displays or exports key material, what clears it and when, and where a secret could reach a log, a message, storage, or the DOM unexpectedly.DEBUGconditional insrc/against the policy inREADME.mdandRULES.md(banner and test mnemonic only, no skipped functionality, no bypassed security), and that a release build cannot be made to behave as a debug one.src/background/index.jsaccepts from a page throughsrc/content/inpage.jsandsrc/content/index.js(method names, params, chain ids, addresses, typed data), everything it accepts from the configured RPC endpoint and the explorer, and everything the approval screens render from those inputs.Definition of done
README.mdorTODO.mdat the width it actually holds, not wider.model: claude-fable-5
Pre-1.0 security review — summary
Reviewed the whole tree at
99292b9with an attacker's eye, line by line:key handling (
vault.js,wallet.js, and every decrypt/derive/display/exportpath), the DEBUG build boundary, everything the background accepts from a page
and from the configured RPC endpoint and explorer, the approval and verification
path, what the approval screens render, the permission model, and storage.
Found sound and not filed: the background populates every dApp transaction
itself, displays that object, and verifies the signed artifact against it field
by field, refusing transaction types other than 0/1/2 and any
authorizationList/blob fields, and requiring the artifact to be the canonicalre-encoding of the approved transaction; secrets are decrypted only in the
popup, never cross the message boundary, and are wiped on view-leave under a
generation guard; the stored-record gate refuses an unusable profile before
normalization and uses own-property tests against prototype keys; extended keys
are round-trip checked on import; the CSP is pinned in both directions; there is
no
externally_connectable; andisExtensionSenderfails closed. Connecting anopaque or non-http origin was considered and not filed: content scripts do not
run in sandboxed subframes (
all_framesdefaults to false) andfile://accessis off by default, so the path is not reachable in a default install.
Findings filed
Funds or keys at risk (milestone 1.0.0):
per field, so a page (or a hostile RPC) can produce a max fee of thousands of
ETH, and the wallet's own send path applies no fee ceiling at all.
Privacy:
persisted and re-announced to every page, so any site can read a stable
per-install cross-site tracking identifier (milestone 1.0.0).
request URLs and bodies, which can include an RPC credential.
Hardening:
warning for allowance shapes (Permit/Permit2) and displays a
primaryTypeitdoes not enforce (milestone 1.0.0).
are keyed by hostname only, dropping the scheme and port.
decodes only hex and shows control and bidirectional characters verbatim,
without the raw bytes.
nonce is honoured and shown only as a bare number.
concurrent approval windows.
connection cannot be revoked from the UI, and the remove-site message does
nothing.
to the top-level tab URL and then to the page-supplied origin.
Decisions (assigned to sneak):
cost for the at-rest vault key (milestone 1.0.0).
wallet's network with no prompt.
personal message while its warning and the README describe raw-hash signing.
Not examined
The two end-to-end suites were not run (per the review brief). The vendored
phishing blocklist and the bundled token-list data were not audited entry by
entry — only the code that consults them.
ethersandlibsodium-wrappers-sumoare trusted as audited third parties. No live-networkor real-funds pass was done; that is
#385.
Disclosures
complete: 17 of 18 agents stopped at this account's model limit, so the merge,
verification and completeness-critic stages did not run. These findings rest
on the reviewer's direct line-by-line reading of the tree (and one completed
finder), each re-checked against the code before filing, not on the full
skeptic-panel verification the method intends.
99292b9;origin/nexthas since advanced to9ac7df0(only#351), which touches none of these
findings.
Model: fable-5-1 (review); opus-4-8 (filing)