38596b4c79ad8903339a10d04a1c14ae53b0ac75
7 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 38596b4c79 |
build: assert DEBUG is off in every emitted bundle (closes #170)
All checks were successful
check / check (push) Successful in 47s
PR #169 made DEBUG a build-time flag defaulting off, but nothing guarded the wiring. The tests load src/shared/constants.js outside a bundle and take the jest fallback branch, so deleting the __BUILD_DEBUG__ define from build.js left all tests passing and make check green while silently restoring the drainable-wallet vulnerability in every shipped artifact. The property only exists in the emitted output, so it is now asserted against the emitted output. script/verify-build reads two independent facts per bundle. Which bundles must be inspected comes from esbuild's metafile: build.js writes dist/constants-bundles.txt naming every emitted JS output whose input set includes constants.js, so the set is derived from the real dependency graph rather than a hardcoded count or filenames. What each bundle's DEBUG state is comes from BUILD_DEBUG_MARKER, a new constant derived from DEBUG itself that the bundler folds to exactly one of two string literals. Deriving the bundle set from the marker would be the silent-pass hole: a bundle with no marker would be indistinguishable from content/index.js, which legitimately contains none. The marker is a plain string rather than a match on minified `DEBUG:!1`, because minifier output is not a contract across esbuild versions. When DEBUG is not known at build time the fold cannot happen and both literals survive, which is exactly the shape of the regression this guards against. Every way of failing to determine a bundle's state is a hard failure: missing manifest, empty manifest, a listed file that does not exist, both markers, neither marker, the wrong marker, or a bundle carrying a marker while absent from the manifest. There is no path on which the script exits 0 without positively identifying the expected marker in at least one bundle. It runs on the build path only. make build and make build-debug both invoke it, the latter asserting the inverse, and Dockerfile:17 runs a bare make build, so CI fails on a release build with a live debug branch. It is deliberately not in script/check: that would make check depend on dist/ existing and pull a full build into its time budget, and the obvious workaround -- skip when dist/ is absent -- is precisely the silently-green behaviour this exists to prevent. |
|||
| f7f141a757 |
security: make DEBUG a build-time flag defaulting to off (closes #149) (#169)
Some checks failed
check / check (push) Has been cancelled
Fixes the highest-severity item in the repo: `src/shared/constants.js` had `const DEBUG = true;`, so `generateMnemonic()` returned the publicly committed `DEBUG_MNEMONIC` for every wallet created from a build of `main`, and the real entropy path was dead code in every artifact we could produce. ## What changed **`build.js`** — `AUTISTMASK_DEBUG` is read from the environment and injected as a `__BUILD_DEBUG__` entry in the existing esbuild `define` map, next to the other `__BUILD_*__` defines. Only the exact value `1` enables it; unset, empty, `true`, or a typo all yield a release build, so the insecure direction requires a deliberate opt-in and any mistake fails safe. The build prints `Build mode: release (DEBUG off)` or `Build mode: DEBUG (INSECURE - hardcoded test mnemonic, do not ship)`. **`src/shared/constants.js`** — `DEBUG` now uses the same `typeof` guard that `src/shared/buildInfo.js` already uses for the other build-time defines, and defaults to `false` when the define is absent (jest, plain `require`). `DEBUG_MNEMONIC` stays in the tree and stays exported. **`Makefile`** — new `build-debug` target (`AUTISTMASK_DEBUG=1` + the same build) so a debug build stays a one-liner for development. **`README.md`** — new "Debug Builds" subsection under Getting Started, and the DEBUG Mode Policy section now states that `DEBUG` is build-time-only and spells out the boundary against the runtime toggle. **`tests/wallet.test.js`** — new, covering both build modes. **`TODO.md`** — refreshed in the same commit (details at the bottom). No new `if (DEBUG)` branch was added and nothing about what DEBUG *does* changed: still exactly the red banner plus the hardcoded test phrase, per the README DEBUG Mode Policy and `RULES.md:76-80`. ## The interaction with the #145 settings toggle This is the subtle part, so spelling out the reasoning. There are two distinct debug flags in the tree after #145: 1. the compile-time `DEBUG` constant from `constants.js`, and 2. the runtime `debugMode` state flag, which the settings easter egg toggles and which `settings.js:379` pushes into `log.js` via `setRuntimeDebug()`. `log.js` merges them: `isDebug()` is `DEBUG || _runtimeDebug`. That merged value feeds exactly two things — the log level threshold (`log.js:24`) and the red banner (`views/helpers.js:71`). Making the banner user-toggleable is the intended behavior of #145, and this PR leaves it alone. `generateMnemonic()` does **not** consult `isDebug()`. It reads the compile-time `DEBUG` binding directly. That distinction is what makes a release build coherent: with `__BUILD_DEBUG__` false, `DEBUG` is false in the bundle, so no amount of clicking the version ten times and flipping the toggle can reach `return DEBUG_MNEMONIC`. The user can turn the banner and verbose logging on in a release build; they cannot turn the hardcoded phrase on. The failure mode to guard against is someone later "tidying up" the two flags by routing `wallet.js` through `isDebug()`, which would silently reintroduce this exact vulnerability with the runtime toggle as the trigger. Three things now guard that: a comment at the `wallet.js` call site saying it must stay the compile-time constant and why, the same statement in the README DEBUG Mode Policy, and a regression test that calls `setRuntimeDebug(true)`, asserts `isDebug()` is genuinely true, and then asserts `generateMnemonic()` still returns fresh entropy. I considered instead making the runtime toggle unavailable in release builds, but rejected it: that removes a feature #145 deliberately added, and it defends the wrong boundary. The banner is not the dangerous part; the mnemonic path is, and that one is already unreachable. ## Verification `make check` — green, 5 suites, 55 tests, plus lint and fmt-check. It also ran via the pre-commit hook on the commit itself. The new tests, per the verification standard in the manager comment on the issue (not just `a !== b`) — with the flag off: two successive `generateMnemonic()` calls differ, both pass `isValidMnemonic`, both are 12 words, neither equals `DEBUG_MNEMONIC`, and the result derives a usable HD wallet (`xpub` + a well-formed first address), so a broken implementation returning a counter or a truncated phrase would fail. Same assertions again with the runtime toggle forced on. With the flag on (`__BUILD_DEBUG__` defined before a `jest.resetModules()` re-require): `DEBUG` is `true` and `generateMnemonic()` returns `DEBUG_MNEMONIC`, so the debug path is proven working rather than silently deleted. Build artifacts — `make build` and `make build-debug` both produce `dist/chrome` and `dist/firefox` successfully. Grepping the minified bundles for the emitted `DEBUG` export value across all four bundles (chrome popup, chrome background, firefox popup, firefox background): # after make build $ grep -roh 'DEBUG:![01]' dist/chrome dist/firefox | sort | uniq -c 4 DEBUG:!1 # after make build-debug $ grep -roh 'DEBUG:![01]' dist/chrome dist/firefox | sort | uniq -c 4 DEBUG:!0 `!1` is minified `false`, `!0` is `true`. Also checked the fail-safe path: `AUTISTMASK_DEBUG=true make build` prints `Build mode: release (DEBUG off)` and likewise yields `4 DEBUG:!1`. One thing a reviewer should know about the grep: the `DEBUG_MNEMONIC` string literal is still present in the release bundle. That is not a leak of anything (the phrase is in this public repo already) and it does not mean the branch is live — esbuild cannot tree-shake a CommonJS `module.exports` object, so the constant survives while `DEBUG` folds to `false`. The compiled function is `function PL(){return ML?UL:f_.fromEntropy(globalThis.crypto.getRandomValues(new Uint8Array(16))).phrase}` where `ML` is the `DEBUG:!1` export. So "the phrase string is absent" is *not* the right test for a release build; "the exported `DEBUG` is `!1`" is, which is what I checked. ## `TODO.md` refresh Per the manager comment: Status rewritten (no branch in flight — `feat/issue-144-settings-about` landed as #145, scripts-to-rule-them-all landed as #148, so the `scripts/` question is resolved; `make check` recorded as verified green on `main` at `23aeae4`); the completed "Verify main passes make check" Future Step removed; Future Steps rewritten against the #149-#168 backlog in rough priority order, keeping branch pruning (now #167) and the pre-1.0 security review (noting #149 and #157 are parts of it but it is broader). One deliberate deviation to flag rather than bury: the manager asked that Next Step become this issue. Taken literally against the Workflow section, this commit *completes* #149, which would normally move it into Completed Steps. I followed the repo's existing convention for in-flight work instead — the previous Next Step was phrased as "Land feat/issue-144-settings-about", so Next Step is now "Land #149 ... PR open, awaiting review", which is accurate until this merges. Whoever merges should move it to Completed Steps and promote the first Future Step. Happy to change it if the reviewer prefers the strict reading. ## Out of scope `script/lint` being `prettier --check` only and unable to catch undefined identifiers (#152) — noted in the TODO but not fixed here; I greped for `DEBUG` consumers by hand rather than relying on lint, as advised. Nothing else in the DEBUG consumer set (`log.js`, `helpers.js`, `state.js`, `settings.js`) changed behavior. Co-authored-by: sneak <sneak@sneak.berlin> Reviewed-on: #169 Co-authored-by: clawbot <clawbot@noreply.example.org> Co-committed-by: clawbot <clawbot@noreply.example.org> |
|||
| 23aeae4841 |
feat: add About well to settings with build info and debug easter egg (#145)
All checks were successful
check / check (push) Successful in 22s
## Summary Add a new well at the bottom of the settings view displaying application info (license, author, version, build date, git commit hash linked to Gitea), and an easter egg that reveals a debug mode toggle after clicking the version number 10 times. ## Changes - **`src/popup/index.html`**: Added About well with license, author, version, build date, and linked commit hash. Added hidden debug well with debug mode toggle. - **`src/popup/views/settings.js`**: Populate About well from build-time constants. Implement version click counter (10 clicks reveals debug well). Wire up debug mode toggle to state and runtime logger. - **`src/shared/buildInfo.js`** (new): Module exporting build-time constants (`BUILD_VERSION`, `BUILD_LICENSE`, `BUILD_AUTHOR`, `BUILD_COMMIT`, `BUILD_DATE`, `GITEA_COMMIT_URL`) injected by esbuild define. - **`build.js`**: Read git commit hash (short + full) and version from package.json, pass as esbuild `define` constants. Also reads `GIT_COMMIT_SHORT`/`GIT_COMMIT_FULL` env vars for Docker builds. - **`Dockerfile`**: Accept `GIT_COMMIT_SHORT` and `GIT_COMMIT_FULL` build args, set as env vars for the build step. - **`Makefile`**: Pass git commit hashes as Docker build args. - **`src/shared/state.js`**: Add `debugMode` boolean to state (persisted). - **`src/shared/log.js`**: Add runtime debug flag that supplements the compile-time `DEBUG` constant. Debug mode toggle updates this flag immediately. closes #144 Co-authored-by: clawbot <clawbot@noreply.git.eeqj.de> Co-authored-by: clawbot <clawbot@sneak.cloud> Co-authored-by: user <user@Mac.lan guest wan> Co-authored-by: clawbot <clawbot@eeqj.de> Co-authored-by: sneak <sneak@sneak.berlin> Reviewed-on: #145 Co-authored-by: clawbot <clawbot@noreply.example.org> Co-committed-by: clawbot <clawbot@noreply.example.org> |
|||
| 1c9d5a9f2d |
Implement EIP-1193 provider for dApp connectivity
All checks were successful
check / check (push) Successful in 13s
Three-part architecture: - inpage.js: creates window.ethereum in page context with request(), on(), send(), sendAsync(), enable() methods. Sets isMetaMask=true for compatibility. - content/index.js: bridge between page and extension via postMessage (page<->content) and runtime.sendMessage (content<->background). - background/index.js: handles RPC routing. Proxies read-only methods (eth_call, eth_getBalance, etc.) to configured RPC. Handles eth_requestAccounts (auto-connect for now), wallet_switchEthereumChain (mainnet only), and returns informative errors for unimplemented signing methods. Manifests updated with web_accessible_resources for inpage.js. Build updated to bundle inpage.js as a separate output file. |
|||
| da30c0667f |
Use ethers.js Mnemonic for real BIP-39 phrase generation
All checks were successful
check / check (push) Successful in 22s
Replace stub wordlist with ethers.Mnemonic.fromEntropy() using crypto.getRandomValues(). Add esbuild to bundle popup JS so it can import ethers directly — no background messaging needed. Each die click now generates a valid, random BIP-39 mnemonic. |
|||
| d9eda1d503 |
Add basic monochrome popup UI with Tailwind CSS
All checks were successful
check / check (push) Successful in 11s
Black-on-white, monospace, Universal Paperclips aesthetic. All views: lock, setup/create/import, main account, send, receive, add token, settings, and approval. Vanilla JS view switching with stub state. README updated with full UI design philosophy, external services documentation, and view descriptions. |
|||
| 065f0eaa81 |
Add project scaffolding
All checks were successful
check / check (push) Successful in 10s
Makefile, Dockerfile, CI workflow, prettier config, manifests for Chrome (MV3) and Firefox (MV2), source directory structure, and minimal test suite. All checks pass. |