diff --git a/scripts/test-guardianes-wasm.sh b/scripts/test-guardianes-wasm.sh new file mode 100755 index 00000000..565ca2d4 --- /dev/null +++ b/scripts/test-guardianes-wasm.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env sh +# test-guardianes-wasm.sh — le pone delante a los guardianes de la cadena wasm las formas conocidas +# de romperla y exige que las maten. Y corre un CONTROL con el sysroot intacto que tiene que PASAR. +# +# POR QUÉ EXISTE. Un guardián que nunca falló no se sabe si sirve, y uno que falla SIEMPRE se ve +# idéntico a uno que funciona: por eso el control no es un extra, es la mitad de la prueba. El +# método lo aportó la sesión hammer-f8 (su scripts/test-atuq-politica.py hace lo mismo con el cruce +# política↔XPI). +# +# NO CONSTRUYE NADA, NO TOMA EL LOCK Y NO ESCRIBE EN EL STORE: trabaja sobre copias temporales de +# artefactos ya sellados, así que se puede correr con la granja a pleno. +# +# ./scripts/test-guardianes-wasm.sh [--store DIR] +set -u +STORE="./store" +[ "${1:-}" = "--store" ] && STORE="$2" +LAB="./.dev-fs/alpine" +TMP="${TMPDIR:-/tmp}/guardianes-wasm.$$" +ok=0; mal=0 + +di() { printf '%s\n' "$*"; } +fin() { rm -rf "$TMP"; } +trap fin EXIT + +# ⚠ EL ARTEFACTO SE RESUELVE POR EL HASH DE LA RECETA, NUNCA POR GLOB. La primera versión de este +# script hacía `ls -d "$STORE"/*-wasi-libcxx | head -1` y agarró el artefacto VIEJO —el de antes de +# que la receta creara el directorio-sonda— así que el CONTROL falló y los cuatro sabotajes +# "pasaron". Es la misma trampa que el cache-hit que congela regresiones: un artefacto anterior se +# hace pasar por el actual y la prueba mide otra cosa. Y es, además, la demostración de para qué +# sirve el control: sin él eso se habría leído como "4 en verde, guardianes ok". +art() { + h=$(./target/release/hammer --store "$STORE" hash "recipes/$1.toml" 2>/dev/null | tail -1) + h=${h#b3:} + [ -n "$h" ] && [ -d "$STORE/$h-$1" ] && printf '%s' "$STORE/$h-$1" +} +libc=$(art wasi-libc) +cxx=$(art wasi-libcxx) +[ -n "$libc" ] && [ -n "$cxx" ] || { di "el store no tiene el artefacto VIGENTE de wasi-libc/wasi-libcxx (¿sin construir?) — nada que probar"; exit 77; } +di "artefactos vigentes: $(basename "$libc" | cut -c1-12)… / $(basename "$cxx" | cut -c1-12)…" +[ -d "$LAB" ] || { di "sin lab en $LAB"; exit 77; } + +# Arma un sysroot fusionado como el que ve firefox, y le aplica el sabotaje que se le pida. +armar() { + rm -rf "$TMP/s"; mkdir -p "$TMP/s" + cp -a "$libc"/usr/share/wasi-sysroot/. "$TMP/s"/ + cp -a "$cxx"/usr/share/wasi-sysroot/. "$TMP/s"/ + case "${1:-}" in + sin-sonda) rm -rf "$TMP/s/include/c++/v1" ;; + sin-libcxx) rm -f "$TMP/s"/lib/*/libc++.a ;; + sin-libc) rm -f "$TMP/s"/lib/*/libc.a ;; + sin-cstring) find "$TMP/s/include" -name cstring -delete ;; + intacto) : ;; + esac +} + +# La prueba EXACTA que hace el configure de firefox, dentro del lab. +probar_cxx() { + bwrap --overlay-src "$LAB" --tmp-overlay / --proc /proc --dev /dev --tmpfs /tmp \ + --ro-bind "$TMP/s" /sysroot --unshare-all --die-with-parent \ + /bin/sh -c 'echo "#include " > /tmp/p.cpp + clang++ -std=gnu++20 --target=wasm32-wasip1 /tmp/p.cpp --sysroot=/sysroot -c -o /tmp/p.o' \ + >/dev/null 2>&1 +} + +# --- EL CONTROL. Va PRIMERO a propósito: si esto no pasa, los demás resultados no significan nada, +# porque un guardián que mata todo se ve igual que uno que discrimina. +armar intacto +if probar_cxx; then + di "✓ CONTROL sysroot intacto -> compila (el guardián NO es un «mata siempre»)"; ok=$((ok+1)) +else + di "✗ CONTROL sysroot intacto -> NO compila. El resto de esta prueba no vale nada."; mal=$((mal+1)) +fi + +# --- LOS SABOTAJES. Cada uno es una forma conocida de romper la cadena; todos tienen que matar. +for caso in sin-sonda sin-cstring; do + armar "$caso" + if probar_cxx; then + di "✗ SABOTAJE $caso -> compiló igual. El guardián NO cubre este caso."; mal=$((mal+1)) + else + di "✓ SABOTAJE $caso -> muere, como debe"; ok=$((ok+1)) + fi +done + +# --- Los de presencia pura, que son los que el caso `sin-sonda` demostró insuficientes por sí solos. +for caso in sin-libcxx sin-libc; do + armar "$caso" + falta=0 + for t in wasm32-wasip1 wasm32-wasip1-threads; do + [ -s "$TMP/s/lib/$t/libc++.a" ] && [ -s "$TMP/s/lib/$t/libc.a" ] || falta=1 + done + if [ "$falta" = 1 ]; then + di "✓ SABOTAJE $caso -> el test de presencia lo caza"; ok=$((ok+1)) + else + di "✗ SABOTAJE $caso -> el test de presencia NO lo caza"; mal=$((mal+1)) + fi +done + +di "" +di "resumen: $ok en verde, $mal en rojo" +[ "$mal" = 0 ] || exit 1