Skip to content

Fix release-blocking defects found by the fresh-user release test - #3

Merged
thebenignhacker merged 2 commits into
mainfrom
fix/release-test-p1-findings
Jul 27, 2026
Merged

thebenignhacker merged 2 commits into
mainfrom
fix/release-test-p1-findings

Conversation

@thebenignhacker

Copy link
Copy Markdown
Member

The pre-release fresh-user pass on v0.3.0 found five P1 defects. Each one silently produced a confident wrong answer from a documented code path, so the release was blocked. All five verified independently before fixing.

P1 findings

--port never worked. Declared and documented as "overrides port in target", but never read. tlsanalyzer host -p 8443 scanned port 443 and graded a port the user never asked about. Only the host:port form worked.

Unreachable hosts produced fabricated reports. A name that did not resolve, or a refused port, yielded F (0/100), Risk: CRITICAL, HNDL: CRITICAL - no forward secrecy, and exit 0 with no error field anywhere. A host that was never contacted was indistinguishable from one measured to be insecure. In a --targets sweep, every typo or decommissioned host became a confident failing row. Now an error with a non-zero exit.

Only the negotiated cipher suite was reported, then presented as the whole configuration. Consequences: a grade rationale of "all ciphers have forward secrecy" after a single handshake, and a false CNSA 2.0 violation (key size below minimum, 128 bits) against servers that do offer AES-256. TLS 1.2 and earlier suites are now enumerated by probing each one.

TLS 1.3 suites cannot be enumerated this way, because Go ignores Config.CipherSuites for TLS 1.3 and always offers all three. Rather than leave that implicit, it is now stated in scanWarnings, so an absent suite is never read as an unsupported one.

--targets --format json emitted invalid JSON, as concatenated objects rather than an array. This is a headline example in --help; every strict parser rejected it.

--skip-quantum lowered the grade and printed false claims. The skipped assessment still counted zero points against a full 25-point maximum, so declining to run an analysis dropped the grade a whole band and reported a quantum score of 0 for servers that verifiably negotiate a hybrid ML-KEM group.

Found because of the fixes

Enumerating the full cipher list exposed two problems the single-suite view had been masking:

  • ChaCha20 and 3DES reported a key size of 0, making ChaCha20-Poly1305 read as weaker than AES-128 in reports and policy evaluation.
  • The cipher score saturated. It awarded bonuses per suite, so with a full list a server offering non-forward-secret RSA suites scored Cipher Strength 25/25 while the policy evaluator listed sixteen violations against that same configuration. Cipher strength is now graded by the weakest accepted suite, since negotiation can be steered toward it.

Verification

Each claim cross-checked against openssl rather than the tool itself:

  • github.com does accept AES128-SHA with no forward secrecy (New, SSLv3, Cipher is AES128-SHA), so its reduced cipher score is honest.
  • example.com does offer TLS_AES_256_GCM_SHA384, confirming the previous CNSA violation was false.

Regression tests added for all five P1s plus the two follow-on defects, including IPv6 handling in the port override. go test -race ./... clean. The README sample output was regenerated from the fixed build, since the previous one displayed the false CNSA violation.

The pre-release fresh-user pass found five defects that each silently produced a
confident wrong answer from a documented code path.

--port was declared and documented as overriding the port in the target, but was
never read, so 'tlsanalyzer host -p 8443' scanned port 443 and graded a port the
user never asked about. Only the host:port form worked.

An unreachable host produced a fabricated report: a name that did not resolve,
or a refused port, yielded F (0/100), risk CRITICAL, and exit 0 with no error
field. A host that was never contacted was indistinguishable from one measured
to be insecure, and in a --targets sweep every typo became a confident failing
row. Both are now errors with a non-zero exit.

Only the negotiated cipher suite was reported, then presented as the server's
whole configuration. That produced a grade rationale of 'all ciphers have
forward secrecy' after a single handshake, and a false CNSA 2.0 violation
against servers that do offer AES-256. TLS 1.2 and earlier suites are now
enumerated by probing each one. TLS 1.3 suites cannot be, because Go ignores
per-suite configuration for TLS 1.3, and that limit is now stated in
scanWarnings rather than left implicit.

--targets with --format json emitted concatenated objects rather than an array,
so the documented batch workflow produced JSON that strict parsers reject.

--skip-quantum still charged the full 25-point maximum while scoring zero, so
declining to run an analysis dropped the grade a whole band and reported a
quantum score of 0 for servers that verifiably negotiate a hybrid ML-KEM group.
A check that did not run now contributes neither points nor maximum.

Enumerating the full cipher list then exposed two further problems it had been
masking. ChaCha20 and 3DES suites reported a key size of 0, making
ChaCha20-Poly1305 read as weaker than AES-128. And the cipher score awarded
bonuses per suite and saturated at the maximum, so a server offering
non-forward-secret RSA suites scored 25/25 while the policy evaluator listed
sixteen violations against the same configuration. Cipher strength is now graded
by the weakest accepted suite, since negotiation can be steered toward it.

Verified against openssl: github.com does accept AES128-SHA with no forward
secrecy, and example.com does offer AES-256, which the previous single-suite
enumeration had reported as a violation.

README sample output regenerated from the fixed build.
The re-test confirmed three of five fixes but blocked on two.

--skip-quantum still fabricated a negative post-quantum finding. Excluding the
assessment from the grade was not enough: the report continued to print
Quantum Ready QV, a failed hybrid PQC checklist item, hybridPqcReady false, and
a priority-1 recommendation to enable hybrid key exchange, for servers that
demonstrably negotiate X25519MLKEM768 and whose CNSA section in the same report
listed that group as approved. A suppressed check now yields no quantum
findings, no quantum recommendations, and a quantum grade of 'not assessed'.

IPv6 targets could not be scanned at all. Connection addresses were built with
fmt.Sprintf("%s:%d", host, port), producing ::1:19502 rather than [::1]:19502,
so every IPv6 target failed as unreachable and looked like a real finding.
Verified against a local TLS server on [::1] that OpenSSL also reaches.

Also from the same pass: scan coverage warnings existed only in JSON and were
invisible in text, the default format; the cipher enumeration warning claimed
TLS 1.2 suites were 'enumerated in full' when Go does not implement the
CBC-SHA256 and CBC-SHA384 families, so a server can accept suites that do not
appear; and an unrecognized --format silently produced text with exit 0, so a
pipeline asking for --format JSON got text and a success code.
@thebenignhacker
thebenignhacker merged commit a869357 into main Jul 27, 2026
2 checks passed
@thebenignhacker
thebenignhacker deleted the fix/release-test-p1-findings branch July 27, 2026 21:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant