fix: verify the build against its own receipt, with the expected mode as an argument (closes #309)
script/verify-build computed its expectation from AUTISTMASK_DEBUG in its own environment, and the Makefile invoked it bare, so an operator with that flag exported who ran the release target got a debug bundle -- every wallet it creates carrying the publicly committed test recovery phrase -- verified green at exit 0. The mode is now the required argument --expect release|debug, with no default and nothing read from the environment; make build passes --expect release on an env -u AUTISTMASK_DEBUG environment and make build-debug passes --expect debug. The flag is deliberately still allowed to reach the compiler, so a shell that has it exported fails make build loudly rather than quietly receiving something other than the release build it asked for. The other half was provenance. The check was a marker grep over a file list read back out of dist/, so a 26-byte file containing only autistmask-build-debug=off verified ok, manifest.json and the content script that runs on every page were never read at all, and an entire hand-written dist/ passed as "1 bundle(s) verified". build.js now records every file it emits and writes a receipt of them -- path, sha256, and whether the file is one of the bundles containing constants.js -- to a path the Makefile creates with mktemp per invocation, outside the repo, and deletes afterwards; a receipt path inside dist/ is refused. dist/ is cleared before a build, so it holds only what that build wrote. dist/constants-bundles.txt is gone, and with it the standalone make verify-build target: re-verifying a dist/ out of the dist/ itself is the thing that was broken. verify-build now checks the receipt's shape, then that dist/ contains nothing the build did not emit and no symlinks, then each recorded file's bytes against its digest and each audited bundle's marker against --expect. The guarantee is narrow and README.md states it as such: dist/ is byte for byte the output of the build.js run that just finished. It proves nothing about the honesty of the source tree or of build.js, and offers nothing to a third party holding a dist/. That is signing: #310 script/test-verify-build goes from 18 cases to 39, extended in place: one per demonstrated bypass, the missing/invalid argument cases, an AUTISTMASK_DEBUG=1 environment that the verifier must ignore, debug bundles that must fail --expect release, and four checks that read the make build and make build-debug recipes back out of make -n. The existing failure modes (grep exit-2, find's status, newline and trailing-space paths, symlinked dist/, and the root probe that refuses to count permission cases vacuously) are kept. Verified: make check green (39 suites / 811 tests, 39 verify-build cases, permission cases enabled), and green again inside the pinned image via script/cibuild with --no-cache-filter=check, where the harness runs as root and reports the setpriv runner rather than skipping. Non-vacuity proved by mutation: disabling the digest comparison fails exactly the four bypass cases, removing the dist/ walk fails the eight extra-file and symlink cases, restoring the ambient AUTISTMASK_DEBUG fallback fails the no---expect case, breaking the Makefile recipe fails the wiring cases, and dropping manifest.json from the recorded emissions fails a real make build.
This commit is contained in:
36
Makefile
36
Makefile
@@ -1,4 +1,4 @@
|
||||
.PHONY: bootstrap setup install test test-e2e test-e2e-firefox lint fmt fmt-check check check-censored docker hooks build build-debug verify-build vendor-blocklist clean dev
|
||||
.PHONY: bootstrap setup install test test-e2e test-e2e-firefox lint fmt fmt-check check check-censored docker hooks build build-debug 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
|
||||
@@ -47,10 +47,27 @@ 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.
|
||||
build:
|
||||
@echo "Building extension..."
|
||||
@yarn run build 2>&1
|
||||
@script/verify-build
|
||||
@set -eu; \
|
||||
receipt="$$(mktemp "$${TMPDIR:-/tmp}/autistmask-build-receipt.XXXXXX")"; \
|
||||
trap 'rm -f "$$receipt"' EXIT INT TERM; \
|
||||
AUTISTMASK_BUILD_RECEIPT="$$receipt" yarn run build 2>&1; \
|
||||
env -u AUTISTMASK_DEBUG script/verify-build --expect release \
|
||||
--receipt "$$receipt"
|
||||
@script/check-censored --require-dist
|
||||
|
||||
# Development-only build: enables the red DEBUG / INSECURE banner and makes
|
||||
@@ -58,15 +75,14 @@ build:
|
||||
# distribute the artifacts this produces.
|
||||
build-debug:
|
||||
@echo "Building extension (DEBUG)..."
|
||||
@AUTISTMASK_DEBUG=1 yarn run build 2>&1
|
||||
@AUTISTMASK_DEBUG=1 script/verify-build
|
||||
@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
|
||||
|
||||
# Assert the compiled DEBUG state of the bundles already in dist/. Runs at
|
||||
# the end of build and build-debug; separate target for re-running it alone.
|
||||
verify-build:
|
||||
@script/verify-build
|
||||
|
||||
# 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.
|
||||
|
||||
Reference in New Issue
Block a user