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.
37 lines
1.1 KiB
JSON
37 lines
1.1 KiB
JSON
{
|
|
"manifest_version": 3,
|
|
"name": "AutistMask",
|
|
"version": "0.1.0",
|
|
"description": "Minimal Ethereum wallet for Chrome",
|
|
"permissions": ["storage", "activeTab", "alarms"],
|
|
"host_permissions": ["<all_urls>"],
|
|
"content_security_policy": {
|
|
"extension_pages": "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; object-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' https: http:; frame-src 'none'; form-action 'none'; base-uri 'none'"
|
|
},
|
|
"action": {
|
|
"default_popup": "src/popup/index.html"
|
|
},
|
|
"background": {
|
|
"service_worker": "src/background/index.js"
|
|
},
|
|
"content_scripts": [
|
|
{
|
|
"matches": ["<all_urls>"],
|
|
"js": ["src/content/inpage.js"],
|
|
"run_at": "document_start",
|
|
"world": "MAIN"
|
|
},
|
|
{
|
|
"matches": ["<all_urls>"],
|
|
"js": ["src/content/index.js"],
|
|
"run_at": "document_start"
|
|
}
|
|
],
|
|
"web_accessible_resources": [
|
|
{
|
|
"resources": ["src/content/inpage.js"],
|
|
"matches": ["<all_urls>"]
|
|
}
|
|
]
|
|
}
|