Skip to content

feat: a weakened security default is a build fact like any other (ADR 0081) - #120

Merged
kfeofantov merged 1 commit into
mainfrom
feat/godebug-security-names
Sep 16, 2026
Merged

kfeofantov merged 1 commit into
mainfrom
feat/godebug-security-names

Conversation

@kfeofantov

Copy link
Copy Markdown
Contributor

Twelve GODEBUG names join the allow-list ADR 0050 opened for two, on a second axis. Each gates a security property the toolchain tightened, and each reaches the binary as a build setting no image, manifest or container environment states — so it is legible only from the binary, which is where the node scanner already looks.

What is kept, and on what test

A name is kept when it is not Opaque in the toolchain's internal/godebugs table, holds 0 or 1, and one of its two values is a documented weakening. That is ADR 0019's test for the list unchanged, applied to a second kind of question.

Group Names
transport and certificates tls10server, tls3des, tlsrsakex, tlssha1, tlsunsafeekm, x509negativeserial
key strength, randomness rsa1024min, cryptocustomrand
parsers that accept what a stricter one rejects httplaxcontentlength, tarinsecurepath, zipinsecurepath, execerrdot

The parse ADR 0050 built is untouched; only the set of names it keeps.

Measurements

Both on go1.26.7, and both fixed in tests as constants rather than paraphrased.

  • A main module declaring go 1.21 gets a DefaultGODEBUG of 30 names in 502 characters, nine of them holding a security default at its pre-tightening value. Nobody chose those nine — they follow from the go directive, which is the fact ADR 0050 §2 already identified as deciding GODEBUG defaults.
  • A go 1.26 module carrying three //go:debug lines emits exactly those three names. execerrdot, tarinsecurepath and zipinsecurepath have no version-dependent default, so carrying them means the weakening was chosen rather than inherited.

Two things the agent deliberately does not do

It does not encode which value is the weak one. tls10server=1 and rsa1024min=0 both weaken. A mapping compiled into the collector would be a second place for the toolchain's table to be wrong, and judgement belongs outside the cluster.

It does not guess at a name it cannot justify. ADR 0081 §4 states the rule for the first time: a name whose one-line justification cannot be written from the standard library's own documentation does not go on a list whose entries are justified one line each. urlstrictcolons is refused on it, alongside the Opaque names and the ones whose value is a number rather than 0/1.

Docs

  • ADR 0081 — new, Amends: 0050 §1, with the mirror line on ADR 0050 and the index entry.
  • docs/security.md — fourteen names promised where two were.
  • docs/backend-requirements.md — normative: an absent name is not 0; for the three directive-only names go_version says nothing at all; 1 may not be read as the weak value.

Prose ceilings: the addition was compressed first (the 30-name measurement moved to the ADR, where a measured fact belongs), then security.md 1111 → 1116 and backend-requirements.md 794 → 799, each raise naming its obligation in the table comment.

Not changed

No new RBAC, no new read, no new privilege. DefaultGODEBUG is in build info the node scanner already opens, and the parse runs on the node before the node→controller channel, so nothing outside the list crosses the wire (CLAUDE.md invariant 4). go_build stays write-once per image digest.

Verification

go test ./...                       pass
golangci-lint run ./...             3 gosec findings, unchanged from main
git archive HEAD -> clean tree      builds and tests green
golden go_build                     836 -> 959 bytes

🤖 Generated with Claude Code

https://claude.ai/code/session_01KeZECJXTDHL6Nts95eKMJA

… 0081)

Twelve GODEBUG names join the allow-list ADR 0050 opened for two, on a
second axis: each gates a security property the toolchain tightened, and
each reaches the binary as a build setting no image, manifest or container
environment states.

Measured on go1.26.7: a main module declaring `go 1.21` gets a
DefaultGODEBUG of 30 names in 502 characters, nine of them holding a
security default at its pre-tightening value. Three further names —
execerrdot, tarinsecurepath, zipinsecurepath — have no version-dependent
default and reach a build only through an explicit //go:debug line, so
carrying them says someone chose the weakening rather than inherited it.

The parse ADR 0050 built is untouched; only the set of names it keeps.
No new read, no new RBAC: the setting is in build info the node scanner
already opens, and the filtering stays on the node.

    go test ./... — pass
    golden go_build — 836 -> 959 bytes

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeZECJXTDHL6Nts95eKMJA
@kfeofantov
kfeofantov merged commit 6e56f10 into main Sep 16, 2026
4 checks passed
@kfeofantov
kfeofantov deleted the feat/godebug-security-names branch September 16, 2026 08:41
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