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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ACUcwo9mZsE5ocYVE9npih
El agujero que dejé señalado 3 veces esta sesión: nada podía saber el sellado VIGENTE de una receta
sin construirla, así que static-audit.sh auditaba el más reciente por mtime (ls -dt) y acusaba a
recetas ya sanas (dbus/libnl) por un sellado anterior a sus flags.
FIX = subcomando `hammer hash <receta> [--check]`. Calcula el ArtifactHash puro sobre las recetas
(source_id + compiler/target/link + patches + flags + fases + hashes de deps recursivos) SIN bajar
fuentes ni compilar. artifact_hash() ya era pub; el CLI sólo lo expone. Cero cambios en la lógica
de hashing ⇒ NINGÚN sellado se mueve.
Verificado: `hash` da EXACTAMENTE el mismo hash que `build` (samurai, byte a byte); `--check` sobre
receta editada → NO-SELLADO en 2ms, exit 1, sin construir nada.
static-audit.sh ahora selecciona el artefacto VIGENTE por hash, no el más reciente por mtime. Con
fallback a ls -dt si hammer no está compilado. Efecto en el store completo: las ~65 recetas cuyo
sellado no es el vigente pasan de 'auditadas' (falsa cobertura) a 'sin artefacto' (deuda de rebuild
REAL, ahora visible): estáticas de verdad 615 | MIENTEN 0 | sin artefacto 123. Corre en 15s, sin
build. El '58 sin medir' de antes estaba enmascarando ~65 recetas más.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
El audit tomaba 'ls -d store/*-<n> | head -1' = orden ALFABÉTICO POR HASH, no el
artefacto vigente. El store guarda TODOS los sellados de una receta (expat tenía
5, de junio a hoy), así que auditaba uno de hace tres semanas. Mismo bug de
clase que comparar libpng.a con libpng16.a por find|head -1.
Con 'ls -dt' (más reciente): 28 → 11. Tres de las supuestas mentirosas
(cargo-audit, git-absorb, yazi) nunca lo fueron: su artefacto reciente ya era
estático y el audit leía el viejo.
Lo que NO cambia: el hallazgo central es real y verificado a mano. libtool
ignora el -static del lab, el curl sellado NO arrancaba en el host, y de las 14
que arreglé cada una se verificó contra sus artefactos previos (expat: los 4
viejos con NEEDED=1, el nuevo con 0). Estaban rotas de verdad.
HONESTIDAD escrita en el script: 'más reciente' ≠ 'vigente'. Lo vigente sería el
artefacto cuyo hash corresponde a la receta de HOY, y hammer no lo expone sin
construir (no hay hammer hash / dry-run). Por eso el audit va DESPUÉS del
rebuild, nunca antes.
Quedan 11: file, helix, libcap, libgcrypt, pcre2, pkgconf, samurai, tuc,
util-linux, xz + naabu (nuevo: Go con libc.so.6 de GLIBC, otro caso).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Destapado migrando procps-ng. libtool lee el -static del lab como "preferí mis
.a", NO como flag al linker: el binario sale dinámico y la receta jura que es
estático. Con gcc pasaba igual — no es regresión de zig, es un agujero que
nadie había medido.
Tres consecuencias medidas, no teóricas:
1. NO CORREN. El curl sellado en el host: "Error relocating /lib/libz.so.1:
__snprintf_chk: symbol not found". Su NEEDED libc.so es el soname de la musl
de zig; en el host /lib/libc.so son 255B de linker script. El artefacto sólo
funciona dentro del sandbox.
2. ARRASTRAN GCC. helix, yazi, git-absorb, cargo-audit y tuc traen libgcc_s.so.1
de Alpine DENTRO del binario. Deuda de matar-gcc que ningún compiler="gcc"
declara: invisible para el frente entero hasta ahora.
3. Un NEEDED es una entrada no declarada — lo que harkaq mide en build, pero en
RUNTIME. Rompe el cono de affected.py (SDD 17 §4): un CVE en zlib no
alcanzaría a un curl que se declara estático.
Script, no gate duro: fallar hoy rompe 28 selladas de golpe, varias del sistema
base (curl, util-linux, sudo). Primero se arreglan con evidencia, después se
cierra la puerta. Exit 1 mientras haya mentirosos ⇒ sirve de gate en CI cuando
la lista llegue a cero.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>