Compare commits
2 Commits
d749ed9212
...
c631cc7626
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c631cc7626 | ||
| 93e3f6e4e2 |
@@ -1,3 +1,6 @@
|
||||
# .git is deliberately NOT excluded: build.js shells out to `git rev-parse` for
|
||||
# build-info stamping and the Dockerfile runs `make build`, so excluding it
|
||||
# would make every built extension report commitHash "unknown".
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
|
||||
2
Makefile
2
Makefile
@@ -11,7 +11,7 @@ setup:
|
||||
@script/setup
|
||||
|
||||
install:
|
||||
@yarn install
|
||||
@yarn install --frozen-lockfile
|
||||
|
||||
test:
|
||||
@script/test
|
||||
|
||||
18
README.md
18
README.md
@@ -31,10 +31,13 @@ list exists to detect symbol spoofing attacks and improve UX.
|
||||
```bash
|
||||
git clone https://git.eeqj.de/sneak/autistmask.git
|
||||
cd autistmask
|
||||
make install
|
||||
make setup
|
||||
make build
|
||||
```
|
||||
|
||||
`make setup` is the entrypoint for a fresh clone: it installs dependencies from
|
||||
the lockfile and installs the git pre-commit hook.
|
||||
|
||||
Load the extension:
|
||||
|
||||
- **Chrome**: Navigate to `chrome://extensions/`, enable "Developer mode", click
|
||||
@@ -97,6 +100,19 @@ provide:
|
||||
- `script/precommit` — run by the git pre-commit hook; runs `script/check`
|
||||
- `script/install-precommit` — install the git pre-commit hook
|
||||
|
||||
The Makefile shims to those. It also carries a few targets that have no
|
||||
`script/` counterpart and are Makefile-only conveniences:
|
||||
|
||||
- `make install` — `yarn install --frozen-lockfile` on its own, without the rest
|
||||
of `script/bootstrap`. Frozen so a stale `yarn.lock` fails instead of being
|
||||
silently rewritten. Use `make setup` for a fresh clone.
|
||||
- `make hooks` — shims to `script/install-precommit`
|
||||
- `make build` — build the extension into `dist/chrome/` and `dist/firefox/`
|
||||
- `make build-debug` — the same build with `AUTISTMASK_DEBUG=1` (see
|
||||
[Debug Builds](#debug-builds))
|
||||
- `make clean` — remove `dist/`
|
||||
- `make dev` — build in watch mode
|
||||
|
||||
## End-to-End Tests
|
||||
|
||||
`make test-e2e` builds `dist/chrome/` and drives the **real popup in a real
|
||||
|
||||
10
TODO.md
10
TODO.md
@@ -44,6 +44,16 @@ undefined identifiers, which is how
|
||||
|
||||
# Completed Steps
|
||||
|
||||
- 2026-08-11: Policy compliance sweep — conditional verbose test rerun, local
|
||||
Tailwind binary instead of `npx`, `--frozen-lockfile` on `make install`, and
|
||||
the Makefile-only targets documented in the README
|
||||
([#166](https://git.eeqj.de/sneak/AutistMask/issues/166)).
|
||||
- 2026-08-11: `script/verify-build` diagnostics corrected: the both-markers
|
||||
message now states what is and is not proven, an unreadable bundle is
|
||||
diagnosed as an I/O fault rather than as changed output, the `*.js` assumption
|
||||
lives only in `build.js`, and the unlisted-bundle scan hard-fails when it
|
||||
cannot enumerate `dist/`
|
||||
([#180](https://git.eeqj.de/sneak/AutistMask/issues/180)).
|
||||
- 2026-08-11: Three `README.md` claims corrected against the code — blocklist
|
||||
attribution, token-display rule, navigation model
|
||||
([#213](https://git.eeqj.de/sneak/AutistMask/issues/213)).
|
||||
|
||||
17
build.js
17
build.js
@@ -29,6 +29,12 @@ function repoRelative(p) {
|
||||
// reports every input that contributed to an output in the metafile, which is
|
||||
// the authoritative answer to "is constants.js in this bundle" — unlike
|
||||
// searching the minified text, it does not depend on what survived minification.
|
||||
//
|
||||
// The ".js" filter below is the only place that assumption lives:
|
||||
// script/verify-build searches every file and symlink under dist/ for a
|
||||
// marker, without filtering by extension, and hard-fails if it cannot walk the
|
||||
// whole tree, so a bundle emitted under some other extension fails there as
|
||||
// unlisted rather than escaping both checks at once.
|
||||
function outputsContainingAuditedModule(metafile) {
|
||||
return Object.entries(metafile.outputs)
|
||||
.filter(([outFile, info]) => {
|
||||
@@ -115,8 +121,17 @@ async function build() {
|
||||
// build that never gets around to writing one cannot be verified against
|
||||
// a stale list.
|
||||
fs.rmSync(BUNDLE_MANIFEST, { force: true });
|
||||
// The locally installed binary, not `npx` — npx silently fetches from the
|
||||
// registry when the binary is absent, which is an unpinned network fetch
|
||||
// in the middle of a build.
|
||||
const tailwindBin = path.join(
|
||||
__dirname,
|
||||
"node_modules",
|
||||
".bin",
|
||||
"tailwindcss",
|
||||
);
|
||||
execSync(
|
||||
`npx @tailwindcss/cli -i ${tailwindInput} -o ${tailwindOutput} --minify`,
|
||||
`"${tailwindBin}" -i "${tailwindInput}" -o "${tailwindOutput}" --minify`,
|
||||
{ stdio: "inherit" },
|
||||
);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "jest --forceExit",
|
||||
"test:verbose": "jest --forceExit --verbose",
|
||||
"build": "node build.js",
|
||||
"lint": "prettier --check .",
|
||||
"fmt": "prettier --write .",
|
||||
|
||||
@@ -7,7 +7,13 @@ ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||
main() {
|
||||
cd "$ROOT"
|
||||
echo "Running tests..."
|
||||
timeout 30 yarn run test 2>&1
|
||||
timeout 30 yarn run test 2>&1 || {
|
||||
echo "--- Rerunning with --verbose for details ---"
|
||||
timeout 30 yarn run test:verbose 2>&1 || true
|
||||
# Always fail: the first run already proved the tests are broken, so a
|
||||
# flaky pass on the rerun must not turn the build green.
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
@@ -34,16 +34,51 @@ fail() {
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Is the literal $1 present in the file $2? Match (grep exit 0) and no-match
|
||||
# (exit 1) are answers about the emitted output. Anything else (exit 2: the
|
||||
# file could not be read) is not an answer at all, and must not be reported as
|
||||
# "no marker" — that would blame the bundle for a permissions or I/O fault.
|
||||
has_marker() {
|
||||
grep -q -F "$1" "$2" 2>/dev/null
|
||||
_hm_status=0
|
||||
grep -q -F -e "$1" -- "$2" || _hm_status=$?
|
||||
case "$_hm_status" in
|
||||
0) return 0 ;;
|
||||
1) return 1 ;;
|
||||
*)
|
||||
fail "grep exited $_hm_status reading $2, so the file could not be
|
||||
searched and its DEBUG state was not checked at all. That is a permissions
|
||||
or I/O fault on the artifact, not a change in the emitted output. Refusing
|
||||
to report success."
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Does the manifest list the path $1, as a whole line? Same discipline as
|
||||
# has_marker: exit 0 and 1 are answers about the manifest, exit 2 means the
|
||||
# manifest could not be read and is not an answer at all. Without this, an
|
||||
# unreadable manifest reads as "this file is not listed" and every emitted
|
||||
# bundle gets reported as an unlisted one.
|
||||
is_listed() {
|
||||
_il_status=0
|
||||
grep -q -x -F -e "$1" -- "$MANIFEST" || _il_status=$?
|
||||
case "$_il_status" in
|
||||
0) return 0 ;;
|
||||
1) return 1 ;;
|
||||
*)
|
||||
fail "grep exited $_il_status reading $MANIFEST, so it could not be
|
||||
searched and nothing was established about which bundles it lists. That is
|
||||
a permissions or I/O fault on the manifest, not a stale manifest. Refusing
|
||||
to report success."
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Read one bundle's DEBUG state into MARKER. Exactly one marker must be
|
||||
# present. Both means the ternary in constants.js was never folded, which is
|
||||
# what happens when the __BUILD_DEBUG__ define goes missing from build.js:
|
||||
# DEBUG stops being known at build time and the debug branch is live again.
|
||||
# Neither means we are reading output we do not understand. Both are hard
|
||||
# failures; neither is ever treated as absence of a problem.
|
||||
# DEBUG stops being known at build time. Neither means we are reading output
|
||||
# we do not understand. Both are hard failures; neither is ever treated as
|
||||
# absence of a problem.
|
||||
read_marker() {
|
||||
_file="$1"
|
||||
_on=no
|
||||
@@ -52,9 +87,14 @@ read_marker() {
|
||||
if has_marker "$MARKER_OFF" "$_file"; then _off=yes; fi
|
||||
|
||||
if [ "$_on" = yes ] && [ "$_off" = yes ]; then
|
||||
fail "$_file carries both debug markers, so the build-time DEBUG value
|
||||
was never resolved and the debug branch is still live. Check that build.js
|
||||
still defines __BUILD_DEBUG__."
|
||||
fail "$_file carries both debug markers, so DEBUG was not resolved at
|
||||
build time: the ternary in src/shared/constants.js survived into the
|
||||
emitted output. This does not mean the debug branch is live in this
|
||||
artifact: an unresolved __BUILD_DEBUG__ is undeclared in extension
|
||||
context, so DEBUG evaluates to false at runtime. It does mean the
|
||||
release/debug distinction is no longer enforced at build time, and which
|
||||
way that fallback happens to evaluate is then an accident a refactor can
|
||||
flip. Check that build.js still defines __BUILD_DEBUG__."
|
||||
fi
|
||||
if [ "$_on" = no ] && [ "$_off" = no ]; then
|
||||
fail "$_file carries no debug marker, so its DEBUG state cannot be
|
||||
@@ -70,13 +110,43 @@ read_marker() {
|
||||
}
|
||||
|
||||
# The manifest says which bundles must carry a marker. This says no other
|
||||
# emitted bundle may carry one, which catches a manifest that has gone stale
|
||||
# emitted file may carry one, which catches a manifest that has gone stale
|
||||
# or short rather than trusting whatever it happens to list.
|
||||
#
|
||||
# Deliberately unfiltered by extension. build.js selects manifest entries with
|
||||
# an endsWith(".js") test; repeating that literal here would mean a bundle
|
||||
# emitted under some other extension escaped the manifest AND this check at
|
||||
# once, which is the correlated blind spot the two-source design exists to
|
||||
# avoid. Every file under dist/ is searched, so build.js's filter is the only
|
||||
# place the assumption lives and this check is what catches it being wrong.
|
||||
#
|
||||
# That claim only holds if the walk is exhaustive, so two things are enforced
|
||||
# here rather than assumed:
|
||||
#
|
||||
# - find's exit status is checked. A subtree it cannot descend is reported on
|
||||
# stderr and then simply missing from the listing, so an unchecked status
|
||||
# turns "could not look" into "nothing was there" — the same conflation
|
||||
# has_marker exists to prevent. The status cannot be read off a pipeline
|
||||
# ending in sort, so the sort is a separate step.
|
||||
# - symlinks are walked too (-type l), not skipped. A marker-carrying bundle
|
||||
# reachable under an unlisted path in dist/ is a stale manifest whether the
|
||||
# path is a link or a file, and grep reads through the link. A link that
|
||||
# cannot be read through — dangling, or pointing at a directory — fails
|
||||
# hard via has_marker's exit-2 path, which is the fail-closed answer: the
|
||||
# build emits neither, so their DEBUG state is unproven, not fine.
|
||||
check_unlisted_bundles() {
|
||||
_listing="$(find dist -type f -name '*.js' | sort)"
|
||||
_find_status=0
|
||||
_listing="$(find dist \( -type f -o -type l \) -print)" || _find_status=$?
|
||||
[ "$_find_status" -eq 0 ] ||
|
||||
fail "find exited $_find_status enumerating dist/, so part of the tree
|
||||
was never walked and nothing was established about the files in it. Any
|
||||
unlisted bundle there went unchecked. That is a permissions or I/O fault on
|
||||
the artifact, not a stale manifest. Refusing to report success."
|
||||
_listing="$(printf '%s\n' "$_listing" | sort)"
|
||||
|
||||
while read -r _file; do
|
||||
[ -n "$_file" ] || continue
|
||||
if grep -q -x -F "$_file" "$MANIFEST"; then
|
||||
if is_listed "$_file"; then
|
||||
continue
|
||||
fi
|
||||
if has_marker "$MARKER_ON" "$_file" ||
|
||||
@@ -113,12 +183,18 @@ main() {
|
||||
fail "$MANIFEST is empty, so no emitted bundle was found to contain
|
||||
src/shared/constants.js. That is never correct, so it is a failure and not
|
||||
a pass."
|
||||
[ -r "$MANIFEST" ] ||
|
||||
fail "$MANIFEST is not readable, so nothing was inspected. That is a
|
||||
permissions or I/O fault, not a pass."
|
||||
|
||||
count=0
|
||||
while read -r file; do
|
||||
[ -n "$file" ] || continue
|
||||
[ -f "$file" ] ||
|
||||
fail "$MANIFEST lists $file, which does not exist."
|
||||
[ -s "$file" ] ||
|
||||
fail "$MANIFEST lists $file, which is empty. An empty bundle
|
||||
carries no marker and proves nothing, so this is a failure and not a pass."
|
||||
read_marker "$file"
|
||||
[ "$MARKER" = "$expected" ] ||
|
||||
fail "$file is $MARKER but this build expects $expected."
|
||||
|
||||
Reference in New Issue
Block a user