Copies .dockerignore, .gitignore, .prettierignore, check.yml and
REPO_POLICIES.md from sneak/prompts at dd4027b. The repo's own entries
(dist/, release/, yarn files) are kept after the canonical content.
The Dockerfile gets separate lint and test phases. Its last stage
depends on both, checks the git describe version and runs make build.
script/lint, test, check, cibuild and docker are the canonical models.
check-censored moves into the lint phase and test-verify-build into the
test phase. fmt and fmt-check fall back to the nvm-installed node. The
e2e image builds are uncached. Comments that cited the old 20-second
test cap now say 60.
Model: opus-5-5
The token screen's decimals and holder count, the ETH price, every address
total and each balance row's USD value went into innerHTML unescaped, against
the rule at the top of src/popup/views/helpers.js. They are escaped now. None
could carry markup, but formatUsd() writes a value under a cent as "< $0.01".
displaySymbol() counts a symbol in code points, not UTF-16 units, so the cut
never leaves half of an emoji, which rendered as U+FFFD.
explorerLink() was already removed on next.
Model: opus-5-5
A hostile ERC-20's symbol() reached an innerHTML string unescaped, and neither
manifest declared default-src, so an attacker deploying a token with 1,000+
holders and airdropping one unit could render a full-viewport cross-origin
iframe over the wallet's own UI, on screens where the user types their
password.
escapeHtml is now a pure string replace over & < > " ' — the old version
round-tripped through textContent, which escapes neither quote, while already
being used inside data-copy="...". All 19 files in src/popup/views/ were
audited: beyond the reported symbol site, the explorer-supplied directionLabel
in all three transaction lists, wallet.name, addr.ensName, the blockie data:
URIs and two ad-hoc quote-only escapes were also unescaped. Explorer URLs now
go through one helper that percent-encodes the path segment.
Both manifests add default-src 'self', frame-src 'none', form-action 'none' and
base-uri 'none'. Three loosenings are pinned in tests/manifest.test.js and
justified in README.md: style-src 'unsafe-inline' (39 static style attributes;
Firefox implements neither style-src-attr nor 'unsafe-hashes'), img-src data:
(blockies), connect-src https: http: (user-configurable RPC).
Note frame-src 'none' blocks a frame loading, not the element existing, so the
zero-iframe assertion is a claim about the escaping alone; the test asserts the
element count and the literal rendered text separately, taking the count before
any click an overlay could intercept.
Verified: make check 39 suites / 811 tests, test-e2e 55/55 including the
WebAssembly-under-CSP assertion, test-e2e-firefox 8/8, zero CSP violations
asserted rather than merely unobserved. Reverting only balanceLine's
interpolation reproduces the attack as 2 iframes on the address screen.