open-standards/joule-code/joule-code-rs/crates/joule-moq-web/e2e/run.sh
Transaction Science cb766e7166 room page: take the endpoints a link names only on the page's own origin
The room client took its relay, room service, fixture, WebTransport URL,
certificate hashes and issuer directory from its query string as given, and
followed a relay's GOAWAY to any URI. A crafted link and a press of Join would
send a joiner's camera and microphone to another origin.

- web/room/endpoint.js: an endpoint is taken only on the page's own origin
  (`https://<host>`, or `wss://<host>` for a relay), never with user
  information; a page served from the loopback interface (a fixture page, the
  e2e harness) takes loopback addresses alone.
- app.js: `service`, `ws`, `wt`, `fixture` and `issuer_directory` are held to
  it; `hashes` only on a loopback page. A refused endpoint is recorded
  (`snapshot().refused`) and shown, Join stays off, and the page connects to
  nothing: `enter()` checks the relay before it asks for the camera, a grant or
  a session. A GOAWAY naming another origin is refused: the page closes its
  session and says so.
- web/room/endpoint.test.mjs (run by e2e/run.sh): own origin, another origin,
  scheme-relative, the page's host as user information, javascript:, a plain
  scheme on a secure page, an HTTP page, and loopback for the harness.
- e2e scenario `origin`: a foreign `?ws=` is refused at Join and nothing
  connects (no WebSocket, request, grant or camera); a foreign `?service=`,
  scheme-relative, behind user information or as javascript:, is refused with
  Join off; a GOAWAY to another origin is refused and the session ends. The
  `service` scenario checks that every request and WebSocket of every page
  stays on the page's origin, as the production `connect-src` requires.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-22 12:38:31 -04:00

86 lines
4.7 KiB
Shell
Executable file

#!/bin/bash
# Build what the browser MoQ client's tests need, and run them.
#
# e2e/run.sh [--only compose,webtransport,websocket,goaway,dvr,service,e2ee,age,co,ageco,binding,cowatch,origin] [--runs N]
#
# 1. joule-moq-relay, joule-moq-room (its own workspace) and wai_live_web (native,
# release: the test room and the host's composition);
# 2. joule-moq-web and wai-live-web for wasm32-unknown-unknown, through wasm-bindgen's web
# target into web/pkg/, each with its own --out-name; and joule-moq-web again with its
# feature e2ee (the member of an end-to-end encrypted room), under the workspace's `wasm`
# profile and with no name section, as joule_moq_web_e2ee, which a page loads for such a
# room alone; and wai-live-web again with its feature int_video, as wai_live_web_decode,
# the decoder on the browser sink's wasm route, which the binding page loads;
# 3. the browser-automation package from e2e/package-lock.json (`npm ci --ignore-scripts`:
# no install script runs), outside the tree unless PLAYWRIGHT_HOST names one installed;
# 4. the Node unit tests of the page's own modules;
# 5. e2e/room.e2e.mjs, in the installed browser whose executable BROWSER_BIN names (the
# CI job names the host's), otherwise the package's own, which PLAYWRIGHT_BROWSERS_PATH
# must then hold.
#
# Environment: CARGO_TARGET_DIR (one target directory for both workspaces), BROWSER_BIN,
# PLAYWRIGHT_HOST, MOQ_WEB_OUT (where artifacts go), CARGO_LOCKED (default --locked).
# Nothing listens beyond loopback: the relay and the page server bind 127.0.0.1.
set -euo pipefail
HERE=$(cd "$(dirname "$0")" && pwd)
CRATE=$(cd "$HERE/.." && pwd)
JC=$(cd "$CRATE/../.." && pwd)
ROOT=$(cd "$JC/../.." && pwd)
WLW="$ROOT/wai/wai-live-web"
LOCKED=${CARGO_LOCKED---locked}
TARGET=${CARGO_TARGET_DIR:-$JC/target}
export CARGO_TARGET_DIR="$TARGET"
OUT=${MOQ_WEB_OUT:-${TMPDIR:-/tmp}/moq-web-e2e}
mkdir -p "$OUT"
# Each run starts from no results: a scenario writes keys and logs there, and the room
# service's keygen refuses a key file a previous run left.
rm -rf "$OUT/results"
PKG="$CRATE/web/pkg"
mkdir -p "$PKG"
step() { echo "== $*"; }
step "native: joule-moq-relay, joule-moq-room, wai_live_web"
(cd "$JC" && cargo build $LOCKED --release -p joule-moq-relay --bin joule-moq-relay)
(cd "$CRATE/../joule-moq-room" && cargo build $LOCKED --release --bin joule-moq-room)
(cd "$WLW" && cargo build $LOCKED --release --bin wai_live_web)
step "wasm: joule-moq-web, wai-live-web"
command -v wasm-bindgen >/dev/null || { echo "wasm-bindgen CLI 0.2.127 is needed (cargo install --locked wasm-bindgen-cli --version 0.2.127)"; exit 2; }
wasm-bindgen --version | grep -q ' 0\.2\.127$' || { echo "the wasm-bindgen CLI must be 0.2.127, the version both crates pin"; exit 2; }
(cd "$JC" && cargo build $LOCKED --release -p joule-moq-web --lib --target wasm32-unknown-unknown)
wasm-bindgen --target web --out-dir "$PKG" --out-name joule_moq_web "$TARGET/wasm32-unknown-unknown/release/joule_moq_web.wasm"
(cd "$JC" && cargo build $LOCKED --profile wasm -p joule-moq-web --lib --target wasm32-unknown-unknown --features e2ee)
wasm-bindgen --target web --remove-name-section --remove-producers-section --out-dir "$PKG" --out-name joule_moq_web_e2ee "$TARGET/wasm32-unknown-unknown/wasm/joule_moq_web.wasm"
(cd "$WLW" && cargo build $LOCKED --release --lib --target wasm32-unknown-unknown)
wasm-bindgen --target web --out-dir "$PKG" --out-name wai_live_web "$TARGET/wasm32-unknown-unknown/release/wai_live_web.wasm"
(cd "$WLW" && cargo build $LOCKED --release --lib --target wasm32-unknown-unknown --features int_video)
wasm-bindgen --target web --out-dir "$PKG" --out-name wai_live_web_decode "$TARGET/wasm32-unknown-unknown/release/wai_live_web.wasm"
ls -l "$PKG"/*.wasm
step "browser automation"
if [ -z "${PLAYWRIGHT_HOST:-}" ]; then
PW="$OUT/pw"
mkdir -p "$PW"
cp "$HERE/package.json" "$HERE/package-lock.json" "$PW/"
(cd "$PW" && npm ci --ignore-scripts --no-audit --no-fund)
export PLAYWRIGHT_HOST="$PW/node_modules"
fi
BROWSER_BIN=${BROWSER_BIN:-}
step "unit tests of the page's modules"
node --test "$CRATE/web/moq/capsule.test.mjs" "$CRATE/web/moq/media.test.mjs" "$CRATE/web/ads/presentation.test.mjs" "$CRATE/web/room/privacy-pass.test.mjs" "$CRATE/web/room/binding.test.mjs" "$CRATE/web/room/cowatch.test.mjs" "$CRATE/web/room/markup.test.mjs" "$CRATE/web/room/endpoint.test.mjs"
step "end-to-end"
browser=()
[ -n "$BROWSER_BIN" ] && browser=(--browser "$BROWSER_BIN")
node "$HERE/room.e2e.mjs" \
--relay "$TARGET/release/joule-moq-relay" \
--room "$TARGET/release/joule-moq-room" \
--wai "$TARGET/release/wai_live_web" \
--web "$CRATE/web" \
--corpus "$ROOT/wai/live-conformance/compose/vectors.json" \
--wai-dir "$ROOT/wai" \
--out "$OUT/results" \
${browser[@]+"${browser[@]}"} \
"$@"