From 9d769efbb932d107e1b9ddf508aa124d0bf5858e Mon Sep 17 00:00:00 2001 From: shankar0123 Date: Sat, 25 Apr 2026 21:18:27 +0000 Subject: [PATCH] =?UTF-8?q?docs(CHANGELOG):=20Bundle=204=20EST/SCEP=20Hard?= =?UTF-8?q?ening=20=E2=80=94=203=20audit=20findings=20closed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit H-004 (PKCS#7 fuzz target gap), M-021 (EST TLS channel binding), L-005 (EST/SCEP issuer-binding fail-loud at startup). Bundle 4 of the 2026-04-25 comprehensive audit (cowork/comprehensive-audit-2026-04-25/). Tracker movement: 0/55 → 3/55 closed. --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0ea624..566cbed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,32 @@ All notable changes to certctl are documented in this file. Dates use ISO 8601. ## [unreleased] — 2026-04-25 +### Bundle 4 (EST/SCEP Hardening): 3 audit findings closed + +> First closure bundle from the 2026-04-25 comprehensive audit +> (`cowork/comprehensive-audit-2026-04-25/`). Hardens the only attack surface +> reachable by an anonymous network attacker in certctl: the unauthenticated +> EST + SCEP enrollment endpoints. + +#### Added + +- **PKCS#7 fuzz targets (Audit H-004)** — 4 new `Fuzz*` test targets covering both the network-reachable hand-rolled ASN.1 parser (`internal/api/handler/scep.go::extractCSRFromPKCS7` + `parseSignedDataForCSR`) and defense-in-depth on the PKCS#7 encoder helpers (`internal/pkcs7/PEMToDERChain`, `ASN1EncodeLength`). Local smoke runs (~2M execs across all 4) found zero panics. Run via `go test -run='^$' -fuzz=Fuzz -fuzztime=10m`. CWE-1287 + CWE-674 + CWE-770. +- **EST TLS transport pre-conditions (Audit M-021)** — `internal/api/handler/est.go::verifyESTTransport` enforces `r.TLS != nil`, `HandshakeComplete`, and TLS version ≥ 1.2 before any state mutation in `SimpleEnroll` and `SimpleReEnroll`. Defense-in-depth at the EST trust boundary; the full RFC 7030 §3.2.3 channel binding only applies when EST mTLS is in use, which certctl does not currently support. RFC 9266 (TLS 1.3 `tls-exporter`) and EST mTLS support documented as deferred follow-ups. +- **EST/SCEP issuer-binding startup validation (Audit L-005)** — `cmd/server/main.go::preflightEnrollmentIssuer` calls `GetCACertPEM(ctx)` at startup with a 10-second timeout. Pre-Bundle-4, an operator binding `CERTCTL_EST_ISSUER_ID` to an ACME / DigiCert / Sectigo / etc. issuer would boot successfully and only fail at first `/est/cacerts` request (those issuer types return explicit error from `GetCACertPEM`). Post-Bundle-4: the server fails-loud at startup with the connector's own error message + `os.Exit(1)`. + +#### Tests + +- `internal/api/handler/est_transport_test.go` — 5 table cases for `verifyESTTransport` +- `cmd/server/preflight_test.go` — `TestPreflightEnrollmentIssuer` covering nil-connector / error-from-issuer / empty-PEM / valid cases +- `internal/api/handler/scep_fuzz_test.go` — `FuzzExtractCSRFromPKCS7`, `FuzzParseSignedDataForCSR` +- `internal/pkcs7/pkcs7_fuzz_test.go` — `FuzzPEMToDERChain`, `FuzzASN1EncodeLength` +- `internal/api/handler/est_handler_test.go` (modified) — 7 POST sites stamp `r.TLS` to satisfy the new transport pre-condition +- `internal/integration/negative_test.go` (modified) — `setupTestServer` wraps the test handler with a fake-TLS-state injector + +#### Why this matters + +Pre-Bundle-4, certctl exposed an unauthenticated network attack surface (EST simpleenroll / SCEP PKCSReq) that called into a hand-rolled ASN.1 parser with no fuzz coverage and no TLS pre-conditions. An attacker could submit crafted PKCS#7 envelopes targeting parser bugs; replay CSRs across TLS sessions without channel-binding catching it; or cause silent runtime failure if operator misconfigured EST/SCEP issuer wiring (no startup validation). Bundle 4 closes all three. + ### T-1 + Q-1: Final-tail closure of the 2026-04-24 audit — 47/47 (100%) > The last two findings from the v5 unified audit closed in two independent