Skip to content

S39.01: close E39 with a security audit, a leakage sweep and a full read-through #784

Description

@DavidCozens

Parent epic: #782

E39 changes the TLS contract, both shipped adapters, a new Core module, two new
per-pack roles, the compliance matrix, both platform pages, the BDD targets and
both example repositories. It lands on main as a single merge, so there is one
moment at which all of it becomes public and one opportunity to be sure it is
right.

This story is that moment. Nothing merges to main until it passes.

It is not a substitute for the review each pull request gets on the way into
feature/tls-rework. Those catch defects in a change. This catches what only
becomes visible once the whole is assembled: a claim that was true when written
and is not true now, a platform detail that migrated into a general page across
three separate edits, an obligation that no longer matches the code that was
supposed to meet it.

1. Secure-coding audit

A repeat of the audit run against the contract on 2026-08-21, but against the
implemented code rather than the design. That audit found the credential
custody obligation overclaimed; the same class of finding is likelier once
there is an implementation to compare the words against.

Scope:

  • Every claim in docs/tls.md read against the code that implements it. The
    page is read by security officers and assessors, so an obligation the code
    does not meet is a liability rather than a documentation defect.
  • The credential lifetime as built: when material is obtained, when it is
    released, what each shipped backend actually holds and for how long, and
    whether Release fires exactly once on every path into Close.
  • The peer-authorisation decision table against fail-open. Specifically that a
    verification callback overriding the untrusted-chain result does not also
    override expiry, not-yet-valid or name mismatch, on either backend.
  • The fingerprint parser against malformed, oversized, truncated and
    wrong-algorithm input.
  • Whether any new error report leaks key material, a certificate or a path into
    a handler.

2. Leakage sweep across every touched document

Two boundaries, both of which the epic's own work puts under pressure:

  • No platform describes another. CI enforces the naming half of this; prose
    that describes a second platform's behaviour without naming it is not caught
    and has to be read for.
  • No platform detail in a general page. docs/tls.md,
    docs/rfc-compliance.md, docs/core/, docs/roles/ and the security and
    compliance guides state what the library does, not what an adapter does. A
    library type or an adapter behaviour appearing in any of them is a defect.

The sweep covers every document the epic touched, not only the ones changed
last. A page edited early and not revisited is where a claim goes stale.

2a. Decide whether the contract needs a status section at all

docs/tls.md carried a "Where this stands" section summarising which
obligations the shipped platforms fell short of. It was dropped during the
contract rewrite, because both platform pages already carry their divergences
entry by entry with the issue tracking each, and maintaining a summary beside
them meant writing every adapter change in two places - which is how one
revision came to state credential retention wrongly for one of the two
platforms.

The page now states obligations only and sends the reader to the platform page
for what a given adapter does. Confirm that still reads correctly once the work
lands, and decide deliberately rather than by omission whether anything needs
saying centrally.

The standing position, unless this check finds a reason against it: if the
platform pages carry the deviations, the contract page has no status section.

A reader who wants to know what a platform does reads that platform's page.

3. Full read-through

The maintainer reads the complete diff - code and documentation - end to end,
against main rather than commit by commit. This is a deliberate second pass
by a reader who has not been inside each change, and it is the last gate before
the branch merges.

4. Consider a Core header for the TLS error codes both packs share

A consideration, not a commitment. The point of raising it here is that the
end of the epic is the first moment there is enough evidence to answer it.

Each TLS pack owns its detail codes - SolidSyslogMbedTlsStreamErrors.h and its
OpenSSL counterpart - while the category axis is already shared from Core, in
SolidSyslogTlsStreamCategories.h. E39 pushes the two enums towards each other
deliberately: an incomplete client credential, a credential that would not
install, a bad configuration, and a name for each check that can refuse a
connection. By the time the epic lands the two lists will be close to mirror
images, and a third pack (#691) would copy the same list a third time.

So: does the shared subset belong in Core, beside the categories?

What it would buy. A handler written once against the fault rather than once
per pack. A new pack inheriting the vocabulary instead of re-inventing it, which
makes convergence a property of the type rather than of review. And a single
place to document what each fault means.

What makes it non-obvious. The error model matches on event->Source and
then reads event->Detail, so detail values are scoped per source today; sharing
them means committing to the same numeric values across packs, and the epic has
already had to be careful that a member inserted mid-list renumbers everything
after it. Some codes are genuinely pack-specific and would stay behind, so each
pack would carry two enums rather than one, and a handler author would need to
know which axis a code came from. It is also a public API change, and the epic's
one-break budget is spent.

How to decide it. Put the two finished enums side by side and count. If the
shared subset is most of both, the case is strong; if it is a handful among
pack-specific majorities, leave it alone. Either way this story records the
answer and raises an issue for a later release rather than widening this one.

Acceptance criteria

  • The security audit is run and every finding is closed, or recorded on this
    issue with the reason it was not.
  • Every document the epic touched is checked for both leakage boundaries, and
    the check is stated as done rather than assumed.
  • Every obligation on docs/tls.md is confirmed against the code that meets
    it, and every remaining shortfall is on its platform page with a live issue.
  • docs/rfc-compliance.md row statuses and summary counts are recomputed from
    the table rather than adjusted by hand.
  • The "Where this stands" section reflects what shipped.
  • The maintainer's read-through is complete.
  • The shared-error-code question is answered either way, with the count behind it.
  • CI is green on the branch, including the integration and BDD lanes.

Not in scope

Anything that turns into new functionality. A finding that is a genuine defect
in shipped behaviour gets fixed here; a finding that is an improvement gets an
issue and a milestone, so this gate cannot become the thing that holds the
release open indefinitely.

Metadata

Metadata

Assignees

No one assigned

    Labels

    storyStory issue

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions