Commit Graph

3 Commits

Author SHA1 Message Date
d0e6758bd8 fix: declare toolbar icons and ship real PNGs in both archives (closes #371)
All checks were successful
check / check (push) Successful in 32s
e2e / e2e-chrome (push) Successful in 1m44s
e2e / e2e-firefox (push) Successful in 31s
Neither manifest declared an `icons` block, so Chrome and Firefox both drew the
generic puzzle piece for this extension. That is the first thing the owner sees
on every launch, and an unbranded placeholder is also how a user fails to tell a
real extension from a look-alike.

Both manifests now declare 16/32/48/128 as `icons/icon<size>.png`, and the four
PNGs live at `icons/` in the tree. build.js copies them into each browser
directory relative to it, so nothing points up and out the way `dist/styles.css`
does, and the receipt records them like every other emitted file.

The sizes copied come from the manifest that will ship next to them, not from a
second list in build.js: a size a manifest declares and `icons/` does not hold
fails the build with the path that is missing, rather than emitting a directory
whose manifest references nothing. script/lib/package.js already resolved `.png`
strings, so an icon that reached a manifest but not the archive fails
self-containment; tests/packaging.test.js now pins that case, since it was
covered only incidentally before.

tests/manifest.test.js asserts the declaration in both manifests, that both
declare the same set, and that each referenced file is a PNG whose IHDR states
the size the entry claims — a declaration alone would still permit a reference
to a file that is not there or is not an image.

The artwork is original, drawn from geometry rather than traced or downloaded: a
flat dark-navy rounded square (#101A2E) with a teal (#35E0C2) triangular "A" —
one outer triangle minus a triangular counter — rasterised with 8x8 supersampling
and encoded as RGBA PNG. One shape, two flat colours, which is what a 16px
toolbar slot can carry.

Verified: make check 56 suites / 1023 tests, make build and make package green,
both archives unpacked and the four icons confirmed inside each with bytes
identical to the tree, make test-e2e 55/55 and 5/5, make test-e2e-firefox 8/8 and
7/7 (the latter installs the packaged XPI). Removing icons/icon48.png fails
make build; making build.js skip one copy fails make package with "the chrome
archive would not be self-contained: it is told to load icons/icon48.png, which
is not in it".
2026-08-23 19:15:42 +00:00
c8c2af0c6b harden: escape every interpolation into popup innerHTML, and add default-src to both manifests (closes #307)
All checks were successful
check / check (push) Successful in 28s
e2e / e2e-chrome (push) Successful in 1m10s
e2e / e2e-firefox (push) Successful in 22s
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.
2026-08-20 13:47:28 +02:00
bf1dbec87c fix: run libsodium on WebAssembly under the extension CSP (closes #182)
All checks were successful
check / check (push) Successful in 26s
2026-08-12 10:30:15 +02:00