# The docs are a contract, so they get audited like code, and one audit found an auth bypass

> On main since rc2: The docs are a contract, so they get audited like code, and one audit found an auth bypass.

Source: https://kern-lang.eu/releases/docs-audited-like-code

On main since rc2

## Release notes

An adopter put it plainly: the documentation is what actually gets read, so a document promising a capability the code does not deliver is a false contract. A doc-versus-code audit was run across the README, the agent-facing `llms.txt`, the internal guide and every stdlib docstring, and it surfaced one **critical security bug** alongside a batch of guarantees that were narrower in the code than on the page. The critical one is worth stating without softening, because it lands on a module this site listed as shipped: `webauthn_verify_assertion` returned **valid** after checking the challenge, the relying-party hash, the user-presence flag and the signature counter, and **never verified the signature at all**; the imported verifier was dead code.

Any request carrying the right relying-party hash and challenge would authenticate as any user, with an arbitrary signature and an unrelated public key. That is a total authentication bypass in a module advertised as phishing-resistant FIDO2\. The root cause is structural rather than careless: a WebAuthn signature covers `authenticatorData` concatenated with the SHA-256 of the client data, a binary message a NUL-terminated Kern string cannot carry, and the existing verify primitive measures its message with `strlen`, so _no_ primitive in the language could have verified it correctly.

The fix was to make the function **fail closed**, returning invalid with an explicit reason so it can never authenticate without a real signature check, and to flip the conformance test that had been asserting an all-zero signature was valid so it now pins the bypass shut. A binary-safe verifier leaf (ES256 through mbedTLS P-256, EdDSA through libsodium) is scoped as the follow-up; until it lands, passkey verification is stated as unavailable rather than quietly broken.

The rest of the sweep narrowed claims to what the code does: `cert_is_self_signed` was documented as checking for a known CA while returning subject-equals-issuer, an _inverted_ trust decision; the mTLS module's “peer certificate verification” and “enforced minimum TLS version” are policy checks over fields TLS has already verified, with the minimum version recorded rather than enforced; the SIEM module's plain SHA-256 chain was described as tamper-evident when an unkeyed chain is re-forgeable by whoever writes the log, so its header now points at the keyed chain; hostname validation was documented as an SSRF control when the real control is the resolver guard; and the tracked-nonce helper is bookkeeping, not the cipher's own nonce, so it does not prevent nonce reuse.

In `llms.txt` the flagship SQL-literal rule had been documented under the wrong error code entirely, which means code-generating models were being taught the wrong code for the headline feature; the generator is corrected. A parallel crypto audit found the matching pair: `pseudonymise` was an **unkeyed** hash of personal data, and an unkeyed hash of a name, an e-mail or a national ID is dictionary-reversible, so it did not pseudonymise in the GDPR Art.

4(5) sense at all. It is now keyed HMAC-SHA256 with a domain-separated subkey and a version tag, and an empty key is **refused** rather than silently producing a reversible value. The same pass corrected four places, including this site, that described the event-log chain as BLAKE2b when the code has used keyed HMAC-SHA256 for some time. None of this is comfortable to publish. It is also the only version of a trust claim worth anything: the register names what was wrong, including in the documentation, and the corrections ship with the tests that pin them.
