ci: ratchet the bundle budget and scan builds for credentials (FH-3.3, FH-3.5)
Bundle budget was warning-only: initial warning 700 kB, error 1.8 MB. Measured today the initial bundle is 1.55 MB raw / 324.58 kB transfer - up from the 1.15 MB measured on 11 August, so it had been growing with nothing to stop it. Lowers maximumError to 1.6 MB. That is a ratchet, not a target: just above today's size so the bundle cannot grow, with the 700 kB warning left in place as the goal. Lower it each time the number comes down. Adds scripts/ci/scan-bundle.sh (npm run scan:bundle), run in CI after the build. Seven patterns: both provider auth headers, the partner ID shape, client_secret, private key blocks, AWS keys, Telegram bot tokens. The legacy payment code that put credentials in the browser is already deleted; this is what stops it coming back. Verified in both directions - clean against the real dist, exit 1 against a planted credential. Measurement also corrected two assumptions recorded in the harvest TODO: admin and editor code is already lazy-loaded, so the initial bundle is main alone rather than a deployable-split problem; and mock gateway fixtures do reach production chunks, which is now filed as FH-E.6 with the cause identified. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
56
scripts/ci/scan-bundle.sh
Normal file
56
scripts/ci/scan-bundle.sh
Normal file
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
# Fails the build if a production bundle contains anything that should only
|
||||
# ever exist server-side.
|
||||
#
|
||||
# Why this exists: the storefront used to send provider payment credentials
|
||||
# from the browser - an `authorization-key` header, a `userid-value` header,
|
||||
# and a hardcoded partner ID literal compiled into the bundle. That code is
|
||||
# gone (FH-1.3), and this check is what stops it coming back. A credential in
|
||||
# a JS bundle is not a leak you can revoke quietly; it is published.
|
||||
#
|
||||
# Usage:
|
||||
# npm run build && scripts/ci/scan-bundle.sh [dist-dir]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
DIST="${1:-dist}"
|
||||
|
||||
if [[ ! -d "$DIST" ]]; then
|
||||
echo "scan-bundle: '$DIST' does not exist - build first" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Each entry is "label|extended-regex". Keep patterns specific: a pattern that
|
||||
# fires on ordinary code trains people to ignore this check.
|
||||
PATTERNS=(
|
||||
"provider auth header|authorization-key"
|
||||
"provider user header|userid-value"
|
||||
"hardcoded partner id|web-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
|
||||
"oauth client secret|client_secret[\"']?[[:space:]]*[:=]"
|
||||
"private key block|BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY"
|
||||
"aws access key|AKIA[0-9A-Z]{16}"
|
||||
"telegram bot token|[0-9]{8,10}:AA[0-9A-Za-z_-]{33}"
|
||||
)
|
||||
|
||||
failed=0
|
||||
|
||||
for entry in "${PATTERNS[@]}"; do
|
||||
label="${entry%%|*}"
|
||||
pattern="${entry#*|}"
|
||||
|
||||
if matches="$(grep -rIlE "$pattern" "$DIST" 2>/dev/null)"; then
|
||||
if [[ -n "$matches" ]]; then
|
||||
echo "FAIL: $label found in the built bundle" >&2
|
||||
echo "$matches" | sed 's/^/ /' >&2
|
||||
failed=1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $failed -ne 0 ]]; then
|
||||
echo >&2
|
||||
echo "A credential reached the browser bundle. Move it behind the API." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "scan-bundle: clean ($DIST)"
|
||||
Reference in New Issue
Block a user