There was no packaging target anywhere, no artifact, and no `key` in `manifest/chrome.json` — so an unpacked Chrome load derived its extension id, and therefore its `chrome.storage.local` partition, from the absolute checkout path. Moving or re-cloning the checkout presented an empty wallet, with no error and nothing in the UI to say so. `manifest/chrome.json` now carries a fixed `key`: the public half of an RSA keypair, which pins the extension id to `gipbhkogfopeahplcjhipkgpcimdpkip`. The private half is a credential and is not in this repo; no target generates one into the working tree, and `tests/extensionId.test.js` fails if a `.pem` is ever committed. Changing `key` changes the id and orphans every wallet stored under the old one. `make package` (script/package) runs `make build` — the only audited path to a release build — and writes one self-contained, versioned archive per browser into `release/`, plus `SHA256SUMS`. The archives are deterministic: entries sorted, timestamps fixed, compression level fixed, so two builds of one commit are byte-identical. Self-containment is checked rather than assumed: every path the manifests and the popup HTML reference is resolved and required to be inside the archive, a reference that climbs out of the extension root is a hard failure, and files left at the `dist/` root — `dist/styles.css`, which build.js copies into each browser directory — are reported as deliberately not shipped rather than dropped by a glob. The archive is then read back off disk and compared member by member against the directory it was built from. The zip writer and reader are stdlib zlib in `script/lib/zip.js`; no new dependency, and nothing unpinned. One version, enforced rather than generated. `script/lib/version.js` requires `package.json`, `manifest/chrome.json` and `manifest/firefox.json` to agree and fails the build naming each file and what it said, instead of reading from one of the three. `BUILD_COMMIT` now carries `-dirty` when the working tree does not match `HEAD`, and `-unknown` when git cannot say; the full hash behind the About screen's commit link stays clean so the link still resolves. Two real-browser observations, both run through the pinned harnesses: - `tests/e2e/storagePartition.js` loads the build from two different paths in one Chrome profile. With `key`: same id, and the second load reads the first load's storage. Without `key`: different ids, and the second load sees an empty partition. Loading both keyed copies at once yields one id, not two. - `tests/e2e/firefox/reinstall.js` installs the packaged XPI in a real Firefox, creates a wallet, quits the browser, restarts on the same profile, adds the add-on again, and decrypts the vault back to the original recovery phrase. It then observes that an explicit uninstall DESTROYS that storage — correct browser behaviour, but for a wallet it means Remove is irreversible except from the recovery phrase, so README.md says so. Firefox ships an UNSIGNED XPI. README.md states plainly that release Firefox and ESR will refuse it, that Developer Edition, Nightly or an Unbranded build is required, and that a temporary add-on does not survive a browser restart. AMO signing, CRX packing, tagging and any upload are deliberately out of scope.
113 lines
3.8 KiB
Makefile
113 lines
3.8 KiB
Makefile
.PHONY: bootstrap setup install test test-e2e test-e2e-firefox lint fmt fmt-check check check-censored docker hooks build build-debug package vendor-blocklist clean dev
|
|
|
|
# Standard targets are thin shims; the implementations live in script/
|
|
# per the scripts-to-rule-them-all pattern (see the Entrypoints section
|
|
# of README.md).
|
|
|
|
bootstrap:
|
|
@script/bootstrap
|
|
|
|
setup:
|
|
@script/setup
|
|
|
|
install:
|
|
@yarn install --frozen-lockfile
|
|
|
|
test:
|
|
@script/test
|
|
|
|
# Browser end-to-end suites. Both require docker; neither is part of check.
|
|
test-e2e:
|
|
@script/test-e2e
|
|
|
|
test-e2e-firefox:
|
|
@script/test-e2e-firefox
|
|
|
|
lint:
|
|
@script/lint
|
|
|
|
fmt:
|
|
@script/fmt
|
|
|
|
fmt-check:
|
|
@script/fmt-check
|
|
|
|
check:
|
|
@script/check
|
|
|
|
# Assert that the competitor name appears nowhere but its documented
|
|
# exceptions. Part of check, and re-run against dist/ at the end of a build;
|
|
# separate target for re-running it alone.
|
|
check-censored:
|
|
@script/check-censored
|
|
|
|
package:
|
|
@script/package
|
|
|
|
docker:
|
|
@script/docker
|
|
|
|
hooks:
|
|
@script/install-precommit
|
|
|
|
# build.js writes a receipt of everything it emitted — every path, its sha256,
|
|
# and whether it is a bundle containing constants.js — and script/verify-build
|
|
# checks dist/ against that. The receipt is made here, fresh per invocation,
|
|
# outside the repo, and deleted again: a standing file inside dist/ would be
|
|
# rewritten by whoever rewrote dist/, which is what made the old check
|
|
# satisfiable by a hand-written tree.
|
|
#
|
|
# The expected mode is an explicit argument and AUTISTMASK_DEBUG is scrubbed
|
|
# from the verifier's environment. The script no longer reads it at all; env -u
|
|
# is here so that stays true of anything it calls. It is deliberately NOT
|
|
# scrubbed from the build itself: with AUTISTMASK_DEBUG=1 exported, this target
|
|
# compiles a debug bundle and then fails on it, loudly, rather than quietly
|
|
# handing back something other than the release build that was asked for.
|
|
#
|
|
# Every step of this target is wrapped in script/discard-dist-on-failure, so a
|
|
# release build that fails removes dist/ instead of leaving a complete, loadable
|
|
# debug bundle there for whoever runs the build, sees it fail, and loads
|
|
# dist/chrome/ anyway. A step that succeeds removes nothing, and build-debug is
|
|
# deliberately not wrapped.
|
|
build:
|
|
@echo "Building extension..."
|
|
@set -eu; \
|
|
receipt="$$(mktemp "$${TMPDIR:-/tmp}/autistmask-build-receipt.XXXXXX")"; \
|
|
trap 'rm -f "$$receipt"' EXIT INT TERM; \
|
|
script/discard-dist-on-failure \
|
|
env AUTISTMASK_BUILD_RECEIPT="$$receipt" yarn run build 2>&1; \
|
|
script/discard-dist-on-failure \
|
|
env -u AUTISTMASK_DEBUG script/verify-build --expect release \
|
|
--receipt "$$receipt"
|
|
@script/discard-dist-on-failure script/check-censored --require-dist
|
|
|
|
# Development-only build: enables the red DEBUG / INSECURE banner and makes
|
|
# the hardcoded test recovery phrase the output of wallet creation. Never
|
|
# distribute the artifacts this produces.
|
|
#
|
|
# No discard-dist-on-failure here, on purpose: a debug build that fails is not
|
|
# producing an artifact anyone could mistake for a release one, and its dist/ is
|
|
# the evidence of what went wrong.
|
|
build-debug:
|
|
@echo "Building extension (DEBUG)..."
|
|
@set -eu; \
|
|
receipt="$$(mktemp "$${TMPDIR:-/tmp}/autistmask-build-receipt.XXXXXX")"; \
|
|
trap 'rm -f "$$receipt"' EXIT INT TERM; \
|
|
AUTISTMASK_DEBUG=1 AUTISTMASK_BUILD_RECEIPT="$$receipt" yarn run build 2>&1; \
|
|
env -u AUTISTMASK_DEBUG script/verify-build --expect debug \
|
|
--receipt "$$receipt"
|
|
@script/check-censored --require-dist
|
|
|
|
# Refresh src/shared/phishingBlocklist.json from its hash-pinned upstream.
|
|
# Run deliberately, land the diff: the extension does no runtime fetching, so
|
|
# the shipped list is as fresh as the last vendoring run that was released.
|
|
vendor-blocklist:
|
|
@script/vendor-blocklist
|
|
|
|
clean:
|
|
@rm -rf dist/ release/
|
|
|
|
dev:
|
|
@echo "Building in watch mode..."
|
|
@yarn run build --watch 2>&1
|