From 5155f90ab931d3af9b1b5dc3dc7528fce8a33a3b Mon Sep 17 00:00:00 2001 From: Sergio Date: Mon, 31 Aug 2026 15:04:26 +0000 Subject: [PATCH] =?UTF-8?q?static-audit:=20miraba=20UN=20binario=20y=20cap?= =?UTF-8?q?aba=20a=2040=20=E2=80=94=20dos=20pases=20en=20falso=20probados?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Arreglando las tres hojas salio que el guardian tenia dos agujeros, y los dos devuelven un pase en falso, que es la peor direccion posible para un gate. 1. `break` en el PRIMER ELF ejecutable. e2fsprogs: el primero que encontraba era `bin/lsattr` (estatico) ⇒ receta HONESTA, mientras `e2fsck` y los tres `fsck.ext*` eran dinamicos. 4 de 31, invisibles. 2. `find | head -40`. bash trae 42 ejecutables y CUARENTA son los modulos cargables `usr/lib/bash/*`, que son ELF *shared object* y no matchean `ELF.*executable`. El cap cortaba la lista ANTES de llegar a `bin/bash` ⇒ el audit concluia «sin ELF» y eso en el resumen se lee como que no hay nada que objetar. El shell del perfil base, dinamico, sin que nadie lo viera. Con dwarves el `break` habia acertado de casualidad —el primero tambien era dinamico— y por eso reportaba 1 donde habia 10. Ahora recorre TODOS los ejecutables, sin cap, cuenta cuantos mienten sobre cuantos hay, y nombra un ejemplo: «gtk4 dice static, 8 de 8 ejecutables DINAMICOS (p.ej. usr/bin/gtk4-rendernode-tool)». Barrido completo con el metodo estricto: 684 estaticos de verdad, MIENTEN 3 (gtk4, bash, e2fsprogs), 59 sin artefacto. Con el metodo viejo, sobre el mismo store, salia 685/1/60. La diferencia son los dos que se escapaban. Es «no comprobar no es aprobar» aplicado al propio guardian — el mismo fallo que `hammer kernel contract` ya habia tenido que corregir en SDD 25 §4.ter. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01ACUcwo9mZsE5ocYVE9npih --- scripts/static-audit.sh | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/scripts/static-audit.sh b/scripts/static-audit.sh index b6d9db02..ebea5b63 100755 --- a/scripts/static-audit.sh +++ b/scripts/static-audit.sh @@ -72,18 +72,34 @@ for f in $recetas; do [ -z "${art:-}" ] && { sin=$((sin + 1)); continue; } fi - # El primer ELF *ejecutable* del artefacto. Una `.so` tiene NEEDED por definición y no prueba - # nada; sólo el ejecutable dice si el enlace final fue estático. - bin="" - for b in $(find "$art" -type f -perm -u+x 2>/dev/null | head -40); do - if file -b "$b" 2>/dev/null | grep -q 'ELF.*executable'; then bin="$b"; break; fi - done - [ -z "$bin" ] && { sin=$((sin + 1)); continue; } - - needed=$(readelf -d "$bin" 2>/dev/null | grep -oE 'Shared library: \[[^]]+\]' \ + # TODOS los ELF *ejecutables* del artefacto, no el primero. Una `.so` tiene NEEDED por + # definición y no prueba nada; sólo los ejecutables dicen si el enlace final fue estático. + # + # ⚠ Antes esto miraba UNO y cortaba (`break`). Con dwarves se vio lo que eso deja pasar: sus + # DIEZ ejecutables eran dinámicos y el audit reportaba uno — acertó de casualidad, porque el + # primero también lo era. El caso que se escapaba en silencio es el inverso: primer binario + # estático y el resto no, que se leía como receta honesta. Es «no comprobar no es aprobar» + # aplicado al propio guardián (2026-08-31). + vistos=0; needed=""; primer_malo=""; malos=0 + for b in $(find "$art" -type f -perm -u+x 2>/dev/null); do + file -b "$b" 2>/dev/null | grep -q 'ELF.*executable' || continue + vistos=$((vistos + 1)) + nb=$(readelf -d "$b" 2>/dev/null | grep -oE 'Shared library: \[[^]]+\]' \ | sed 's/.*\[//;s/\]//' | tr '\n' ' ') || true + if [ -n "$nb" ]; then + malos=$((malos + 1)) + [ -z "$primer_malo" ] && { primer_malo="${b#$art/}"; needed="$nb"; } + fi + done + [ "$vistos" -eq 0 ] && { sin=$((sin + 1)); continue; } + if [ -n "$needed" ]; then - printf ' ✗ %-18s dice static, es DINÁMICO → %s\n' "$n" "$needed" + if [ "$malos" -gt 1 ]; then + printf ' ✗ %-18s dice static, %d de %d ejecutables DINÁMICOS (p.ej. %s) → %s\n' \ + "$n" "$malos" "$vistos" "$primer_malo" "$needed" + else + printf ' ✗ %-18s dice static, es DINÁMICO (%s) → %s\n' "$n" "$primer_malo" "$needed" + fi case "$needed" in *libgcc_s*) printf ' %-18s ↑ arrastra libgcc_s de Alpine: deuda de matar-gcc no declarada\n' "" ;; esac case "$needed" in *libc.so*) printf ' %-18s ↑ `libc.so` es la musl de zig: en el host son 255B de linker script ⇒ NO CORRE fuera del sandbox\n' "" ;; esac mienten=$((mienten + 1))