Three build-breaking check scripts + two smoke test harnesses + two fixture dirs: - scripts/check-bus-protocol-version.sh: greps Swift vs TS BUS_PROTOCOL_VERSION constants, diffs the two fixture directories (packages/Bus/Tests/BusTests/ Fixtures/ vs webview/packages/bus/fixtures/). Fails build on any drift. - scripts/check-no-evaluate-javascript.sh: scans App/ + packages/ for `.evaluateJavaScript(` outside test dirs; HUD-04 architectural lint. - scripts/check-bus-harness-parity.sh: byte-identical check between canonical webview/bus-harness.html and bundle copy (Plan 02-03 provides the bundle copy; this script enforces no-drift going forward). - scripts/test-check-bus-protocol-version.sh + bus-mismatch/ fixture: drives the protocol-version checker against a v2.0.0/v1.9.0 mismatch, asserts exit 1 + both versions in error text. - scripts/test-check-no-evaluate-javascript.sh + bus-evaluate-js/ fixture: drives HUD-04 lint against bad.swift (evaluateJavaScript) + good.swift (callAsyncJavaScript). Asserts bad.swift flagged, good.swift not flagged. Fixture is copied to a temp dir to bypass the production */test-fixtures/* path-skip rule. [Rule 3 - Blocking] Normalized Swift-side fixtures to drop trailing newlines. The 14 JSON files under packages/Bus/Tests/BusTests/Fixtures/ had stray trailing \n while the TS mirror at webview/packages/bus/fixtures/ did not. Plan 02-02's SUMMARY documented the no-trailing-newline convention (matches Swift JSONEncoder default) but Plan 02-01's fixtures were authored via a text editor that added them. Stripping \n lets check-bus-protocol-version.sh pass without relaxing the byte-equality invariant. Swift CodableRoundTripTests remain green (24/24) because JSONSerialization ignores trailing whitespace.
59 lines
2.1 KiB
Bash
Executable File
59 lines
2.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# check-no-evaluate-javascript.sh — Xcode pre-build phase.
|
|
#
|
|
# Forbids `.evaluateJavaScript(` in Swift sources outside test directories.
|
|
# HUD-04 architecturally forbids the API — interpolating JSON into an
|
|
# evaluateJavaScript body is an XSS foothold (`</script>`, U+2028 line
|
|
# terminator, etc.). The one allowed JS entry is
|
|
# `callAsyncJavaScript(_:arguments:in:contentWorld:)`, which takes arguments
|
|
# as typed values and never string-interpolates the payload.
|
|
#
|
|
# Usage:
|
|
# scripts/check-no-evaluate-javascript.sh # production
|
|
# REPO=/tmp/fix SEARCH_ROOTS=my-fixture-dir \
|
|
# scripts/check-no-evaluate-javascript.sh # test-harness mode
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
: "${REPO:=$(cd "$SCRIPT_DIR/.." && pwd)}"
|
|
: "${SEARCH_ROOTS:=App packages}"
|
|
|
|
HITS=()
|
|
|
|
for root in $SEARCH_ROOTS; do
|
|
full="$REPO/$root"
|
|
[[ -d "$full" ]] || continue
|
|
|
|
while IFS= read -r -d '' file; do
|
|
# Skip test directories — tests may reference the forbidden API
|
|
# in comments, string literals, or control fixtures.
|
|
case "$file" in
|
|
*/Tests/*) continue ;;
|
|
*/tests/*) continue ;;
|
|
*/test-fixtures/*) continue ;;
|
|
esac
|
|
if matches=$(grep -nE '\.evaluateJavaScript\(' "$file" 2>/dev/null); then
|
|
while IFS= read -r m; do
|
|
HITS+=("$file:$m")
|
|
done <<<"$matches"
|
|
fi
|
|
done < <(find "$full" -name '*.swift' -type f -print0)
|
|
done
|
|
|
|
if [[ ${#HITS[@]} -gt 0 ]]; then
|
|
{
|
|
echo "FAIL: forbidden .evaluateJavaScript( call in Swift sources — HUD-04 violation"
|
|
for hit in "${HITS[@]}"; do echo " $hit"; done
|
|
echo ""
|
|
echo "Use webView.callAsyncJavaScript(_:arguments:in:contentWorld:) with a"
|
|
echo "primitive-string payload argument instead. Interpolating JSON into an"
|
|
echo "evaluateJavaScript body is an XSS foothold (</script>, U+2028)."
|
|
echo ""
|
|
echo "See packages/Bus/Sources/Bus/WebviewBridge.swift for the canonical pattern."
|
|
} >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "no evaluateJavaScript calls — HUD-04 OK"
|