#!/bin/sh # script/check-censored: assert that the competitor name RULES.md bars appears # nowhere in this repo, and nowhere in the built extension, except where it is # deliberate. Our own extension to scripts-to-rule-them-all, run from # script/check and from make build. # # Where the name is allowed, and why each one is not negotiable away: # # - script/vendor-blocklist. Build-time tooling, never shipped. A pinned # source reference that does not say what the source is cannot be verified # by anyone, so it names it. Whole-file exemption. # - the literal `isMetaMask` and the `_metamask` object in # src/content/inpage.js. A protocol identifier dApps feature-detect on; # renaming it does not rename it in their code, it only stops this wallet # working on their sites. # - the literal `MetaMask USD` in src/shared/tokenList.js. The on-chain name # of an ERC-20 the user may hold, recorded next to its address and symbol, # which is what lets symbol-spoofing detection tell the real one from a # forgery. Altering it would misidentify an asset to its owner. # # Everything else fails, in the working tree and under dist/. The last two are # literals rather than files, so they are enforced by counting: a file may # contain the name only as many times as it contains those exact strings. The # emitted bundles carry both, so a plain "the name must not appear in dist/" # could never have passed. # # The name itself is not written here. script/vendor-blocklist is the one place # in this repo that defines it, and this reads it back out of there — so the # repo-wide grep this check exists to enforce keeps returning exactly the files # named above, and this file is not one of them. set -eu ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" # Absolute path to this script, resolved before anything cd's anywhere: the # scan half runs in a re-invocation through xargs, so that the paths it works on # arrive as arguments and cannot be reshaped by field splitting on the way in. SELF="$(cd "$(dirname "$0")" && pwd -P)/$(basename "$0")" # Internal re-entry flag. Not part of the command-line interface. SCAN_FLAG="--scan-paths" VENDOR_SCRIPT="$ROOT/script/vendor-blocklist" # Set by extract_name / write_allowed_literals. NAME="" ALLOWED_LITERALS_FILE="" FAILED=0 cleanup() { [ -z "$ALLOWED_LITERALS_FILE" ] || rm -f "$ALLOWED_LITERALS_FILE" } trap cleanup EXIT INT TERM fail() { echo "check-censored: FAIL: $*" >&2 exit 1 } # The name, taken from the single place that defines it. A check scanning for a # pattern it failed to read would pass against anything, so this refuses to # continue unless it got something that looks like the definition. extract_name() { [ -f "$VENDOR_SCRIPT" ] || fail "$VENDOR_SCRIPT is missing, and it is where the name being checked for is defined. Nothing was scanned." NAME="$(grep -m1 '^UPSTREAM_ORG=' "$VENDOR_SCRIPT" | cut -d'"' -f2)" || fail "could not read UPSTREAM_ORG from $VENDOR_SCRIPT. Nothing was scanned." case "$NAME" in "" | *[!A-Za-z0-9]*) fail "UPSTREAM_ORG in $VENDOR_SCRIPT did not yield a plain name (got: '$NAME'). Scanning for that would prove nothing. Nothing was scanned." ;; esac } # Occurrences allowed anywhere, including in the emitted bundles. Each contains # the name exactly once, which is what makes counting them sound. write_allowed_literals() { ALLOWED_LITERALS_FILE="$(mktemp \ "${TMPDIR:-/tmp}/autistmask-censored.XXXXXX")" || fail "could not create a temporary file, so nothing was scanned." { echo "is$NAME" echo "_$NAME" echo "$NAME USD" } >"$ALLOWED_LITERALS_FILE" } # How many times does $1 contain the name (TOTAL), and how many of those are one # of the allowed literals (ALLOWED)? Same discipline the rest of this repo's # shell checks apply to grep: exit 0 and 1 are answers about the file, anything # else means the file was not searched and is not an answer at all. count_matches() { _cm_status=0 _cm_out="$(grep -a -o -i -F -e "$NAME" -- "$1")" || _cm_status=$? case "$_cm_status" in 0) TOTAL="$(printf '%s\n' "$_cm_out" | grep -c .)" ;; 1) TOTAL=0 ;; *) fail "grep exited $_cm_status reading $1, so the file was never searched and nothing was established about it. That is a permissions or I/O fault, not a clean file. Refusing to report success." ;; esac if [ "$TOTAL" -eq 0 ]; then ALLOWED=0 return 0 fi _cm_status=0 _cm_out="$(grep -a -o -i -F -f "$ALLOWED_LITERALS_FILE" -- "$1")" || _cm_status=$? case "$_cm_status" in 0) ALLOWED="$(printf '%s\n' "$_cm_out" | grep -c .)" ;; 1) ALLOWED=0 ;; *) fail "grep exited $_cm_status matching the allowed literals in $1. Refusing to report success." ;; esac } # The per-path half, run in a re-invocation of this script so it uses the same # counting as everything else rather than a second copy of it. scan_paths() { for _file in "$@"; do case "$_file" in "$VENDOR_SCRIPT" | "script/vendor-blocklist") continue ;; esac [ -f "$_file" ] || continue count_matches "$_file" [ "$TOTAL" -gt "$ALLOWED" ] || continue FAILED=$((FAILED + 1)) echo "check-censored: $_file: $TOTAL occurrence(s) of the name," \ "$ALLOWED of them allowed" >&2 grep -a -n -i -F -e "$NAME" -- "$_file" | cut -c1-140 | head -5 >&2 done [ "$FAILED" -eq 0 ] } # Hand a NUL-delimited listing to the scan half. Returns non-zero if any path # failed, or if the scan could not be run at all. scan_listing() { xargs -0 "$SELF" "$SCAN_FLAG" <"$1" } # Every file git tracks, plus everything untracked and not ignored: the working # tree as a reviewer would see it, and never node_modules or dist/ (both are # ignored; dist/ is walked separately below). check_worktree() { _list="$(mktemp "${TMPDIR:-/tmp}/autistmask-censored-tree.XXXXXX")" || fail "could not create a temporary file, so nothing was scanned." _status=0 git ls-files -z --cached --others --exclude-standard >"$_list" || _status=$? [ "$_status" -eq 0 ] || { rm -f "$_list" fail "git ls-files exited $_status, so the working tree was never enumerated and nothing was established about it." } # Repo-relative paths. The scan half cd's to the repo root before it opens # anything, so they reach it intact and unjoined. WORKTREE_COUNT="$(tr -dc '\0' <"$_list" | wc -c | tr -d ' ')" _status=0 scan_listing "$_list" || _status=$? rm -f "$_list" return "$_status" } check_dist() { _list="$(mktemp "${TMPDIR:-/tmp}/autistmask-censored-dist.XXXXXX")" || fail "could not create a temporary file, so dist/ was not scanned." _status=0 find "$ROOT/dist" -type f -print0 >"$_list" || _status=$? [ "$_status" -eq 0 ] || { rm -f "$_list" fail "find exited $_status enumerating dist/, so part of the emitted tree was never walked and an unchecked file there went unchecked. Refusing to report success." } DIST_COUNT="$(tr -dc '\0' <"$_list" | wc -c | tr -d ' ')" _status=0 scan_listing "$_list" || _status=$? rm -f "$_list" return "$_status" } usage() { echo "usage: script/check-censored [--require-dist]" >&2 exit 2 } main() { cd "$ROOT" # Internal re-entry from scan_listing's xargs. if [ "${1-}" = "$SCAN_FLAG" ]; then shift extract_name write_allowed_literals scan_paths "$@" return $? fi require_dist=no case "${1-}" in "") ;; --require-dist) require_dist=yes ;; *) usage ;; esac extract_name write_allowed_literals echo "Checking for censored names..." tree_status=0 check_worktree || tree_status=$? dist_status=0 dist_inspected=no DIST_COUNT=0 if [ -d "$ROOT/dist" ]; then dist_inspected=yes check_dist || dist_status=$? fi if [ "$tree_status" -ne 0 ] || [ "$dist_status" -ne 0 ]; then fail "the name appears outside the deliberate exceptions (reported above). See the header of script/check-censored for what is allowed and why." fi if [ "$dist_inspected" = no ]; then if [ "$require_dist" = yes ]; then fail "there is no dist/ to inspect and this run was asked to require one. Run make build." fi cat <