Repository navigation
v0.22.0 — absences that were guaranteed before we looked
One correctness fix to the mined-symbol layer, plus the documentation split. The fix removes wrong answers; it adds no new rule-outs.
Refuse a mined name whose absence was guaranteed (#33)
The mined-symbol layer had two gates: the name must appear literally in the advisory text, and it must share a namespace with something the package exports. Both say where the name came from. Neither says the name is a function this build would have exported, and that is the premise the whole absence test rests on.
A namespace is shared by far more than its functions. OSSL_CMP_CTX is a struct tag, OPENSSL_NO_COMP_ALG is a build macro, ASN1_TYPE is a type, SSL_OP_NO_RX_CERTIFICATE_COMPRESSION is an option constant. None of them is in any build's symbol table, of any version, ever — so their absence was guaranteed before the image was opened, and the layer was reading it as a fact about which version was compiled.
The same hole swallows a name exported under a different decoration. PCRE2 ships one library per code-unit width and suffixes every export, so an advisory written about pcre2_compile is absent from libpcre2-8-0 — which exports pcre2_compile_8 — no matter what is installed.
A third gate refuses both. A name with no lower-case letter is refused, because C spells macros, constants and type names in upper case; and an ABI-width suffix is normalised off both sides before comparing. A refused hint is inert — recorded as an observation naming the symbol and the reason, like every other validation failure. The static cgo discharge inherits the fix, since it consumes the same validated set.
How it was found
By running a real miner — claude -p, via --llm-command — against Rancher's RKE2 v1.36.0 images and auditing every rule-out it produced. Eleven package rows across four images were cleared on the strength of a struct tag, a build macro, an option constant, a type name or a code-unit suffix:
| image | advisory | rows cleared | mined name |
|---|---|---|---|
| klipper-lb:v0.4.16 | CVE-2026-63074 | libcrypto3, libssl3 | OSSL_CMP_CTX |
| klipper-helm:v0.9.17 | CVE-2026-63074 | libcrypto3, libssl3 | OSSL_CMP_CTX |
| hubble-ui:v0.13.3 | CVE-2025-66199 | libcrypto3, libssl3 | OPENSSL_NO_COMP_ALG, SSL_OP_NO_RX_CERTIFICATE_COMPRESSION |
| hubble-ui:v0.13.3 | CVE-2026-63074 | libcrypto3, libssl3 | OSSL_CMP_CTX |
| hubble-ui:v0.13.3 | CVE-2026-22795 | libcrypto3 | ASN1_TYPE |
| hubble-ui:v0.13.3 | CVE-2026-28388 | libcrypto3 | X509_V_FLAG_USE_DELTAS |
| hardened-calico:v3.32.0 | SUSE-SU-2026:4241-1 | libpcre2-8-0 | pcre2_compile_32, pcre2_dfa_match, pcre2_jit_match |
Three of those advisories were cleared on every row they appeared on and left their reports entirely. Two more survived only because a sibling package row happened to stay open — luck, not a guard.
Re-measured with the gate in place
Same four images, same miner. All eleven rows come back and nothing else moves:
| image | open OS findings | dynsym rule-outs |
|---|---|---|
| klipper-lb | 42 → 44 | 8 → 6 |
| klipper-helm | 57 → 59 | 8 → 6 |
| hubble-ui | 93 → 99 | 14 → 8 |
| hardened-calico | 44 → 45 | 1 → 0 |
Every rule-out resting on a lower-case-bearing name with no decorated twin survives untouched: BIO_free, OPENSSL_malloc, d2i_PKCS7/d2i_X509, BIO_vsnprintf, ERR_raise_data, BIO_f_linebuffer, OPENSSL_uni2utf8, OSSL_CMP_get1_rootCaKeyUpdate, OSSL_CMP_SRV_process_request.
The cost is that a genuinely all-caps export — MD5, SHA1 — can no longer be ruled out. A lost conclusion rather than a wrong one, which is the trade this layer is supposed to make.
This is the gate Java already had: a mined class name must be a dotted name whose last segment is capitalised, "because there is no doLookup.class and concluding absence from a method name's absence would be a plain lie." The OS path was missing the equivalent.
Not included
26 of the 31 original rule-outs tested a libcrypto symbol against the libssl3 row. Those rule-outs are true and the CVE survives on the sibling row, so nothing is hidden — but attributing an advisory's symbols to the right package row is a separate question and a separate change.
Split the README into a Docusaurus site (#31)
The README had grown to roughly 3,300 lines and served as a full reference manual. It is now a concise overview, with the deep material under docs/ and published to GitHub Pages.
Add a remediation view to the HTML dashboard (#32)
This release rules out no additional CVEs. Every change on the mined-symbol path removes an answer that should never have been given.