BentoPDF v2.8.8 built from source at its pinned commit with BentoPDF's own air-gapped build options, every module it would load from a CDN vendored and checked against vendor.lock, a Content-Security-Policy without a single host, digital-sign-pdf and timestamp-pdf disabled because both need a third party, and the hand-off glue on every page: the AGPL source link, the hand-in of a file from bau-pm through a one-minute cookie into sessionStorage, confirmed by the tool's reaction, the hand-back of a result from every window, and the signing tool's flatten switch locked on. The zero-host gate drives all 117 enabled tool pages in Chromium and fails on any request to another origin, any CSP violation, a CSP that names a host, and a disabled id that names no page. Green on this image (3 min 52 s on a private network), red on the stock upstream image (115 findings) and on a misspelled id. CI runs it on every push, the release workflow before it pushes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
46 lines
1.8 KiB
Bash
Executable File
46 lines
1.8 KiB
Bash
Executable File
#!/bin/sh
|
|
# What this repository adds to BentoPDF's finished build, run in the
|
|
# Dockerfile's builder stage after `npm run build`:
|
|
#
|
|
# finish.sh <app-dir>
|
|
#
|
|
# 1. a <script src="/bau-pm/handoff.js"> in every HTML page, the PDF.js
|
|
# viewers' pages included (the sign tool saves from inside one of them);
|
|
# 2. the precompressed copies of those pages regenerated, because nginx's
|
|
# gzip_static would otherwise serve the old page without the tag;
|
|
# 3. every host taken out of the generated Content-Security-Policy, so the
|
|
# pages may load from their own origin and from nowhere else.
|
|
#
|
|
# Each step checks its own result and fails the build when it did not happen.
|
|
set -eu
|
|
|
|
app=$1
|
|
dist=$app/dist
|
|
tag='<script src="/bau-pm/handoff.js"></script>'
|
|
|
|
pages=$(find "$dist" -name '*.html' | wc -l)
|
|
[ "$pages" -gt 100 ] || { echo "finish: only $pages pages in $dist" >&2; exit 1; }
|
|
|
|
find "$dist" -name '*.html' | while read -r page; do
|
|
grep -q '</head>' "$page" || { echo "finish: $page has no </head>" >&2; exit 1; }
|
|
sed -i "s#</head>#$tag</head>#" "$page"
|
|
rm -f "$page.gz" "$page.br"
|
|
gzip -9 -k -n "$page"
|
|
done
|
|
# find -exec rather than grep --include, which busybox's grep does not know.
|
|
tagged=$(find "$dist" -name '*.html' -exec grep -lF "$tag" {} + | wc -l)
|
|
[ "$tagged" -eq "$pages" ] || { echo "finish: $tagged of $pages pages carry the tag" >&2; exit 1; }
|
|
stale=$(find "$dist" -name '*.html.br' | wc -l)
|
|
[ "$stale" -eq 0 ] || { echo "finish: $stale brotli copies left" >&2; exit 1; }
|
|
|
|
for conf in security-headers.conf security-headers-docs.conf; do
|
|
sed -E -i 's# https?:(//[^ ;"]*)?##g' "$app/$conf"
|
|
if grep -Eq 'https?:' "$app/$conf"; then
|
|
echo "finish: $conf still names a host:" >&2
|
|
grep -Eo 'https?:[^ ;"]*' "$app/$conf" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
echo "finish: $tagged pages tagged, CSP without hosts"
|