Skip to content

fix: an unreadable public value is refused, never answered from the secret namespace (r139) - #88

Merged
antoinetoussaint-byte merged 1 commit into
mainfrom
fix/workspace-value-fails-closed
Oct 26, 2026
Merged

antoinetoussaint-byte merged 1 commit into
mainfrom
fix/workspace-value-fails-closed

Conversation

@antoinetoussaint-byte

@antoinetoussaint-byte antoinetoussaint-byte commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Follow-up to #87, from review r139. m3 is a security fix; m1 brings doc comments and tests in line with the code.

m3: an unreadable public value is refused, never answered from the secret namespace. WorkspaceValue, ReadAuthority and Recheck all read through workspaceValueLive. When the public read failed, for example because a file carrier names a missing file or a value is delivered both inline and by file, workspaceValueLive threw the error away and returned whatever the secret namespace held under the same name. r139 probed this: a missing public file carrier plus CODEFLY__WORKSPACE_SECRET_CONFIGURATION__PROBE_FB__BINDING=secretvalue made WorkspaceValue("probe-fb","BINDING") return "secretvalue" with a nil error. Through the same path, ReadAuthority could pin an authority value from a delivery the host never made for that name. Recheck could also pass after the public delivery broke, as long as the secret namespace held the pinned value.

  • workspaceValueLive now goes through workspaceValueIfSetLive. That function moves on to the secret namespace only when the public value is absent, and returns every other failure. This is one lookup instead of two that disagreed.
  • WorkspaceValue returns the read failure as it is, pinned or not. ReadAuthority wraps it as ErrAuthorityValueMissing: <name> cannot be read: <cause>, and Recheck as ErrAuthorityValueChanged: <name> cannot be read: <cause>. Both sentinel and cause stay reachable through errors.Is, so every refusal names its reason. Absence keeps its existing messages.
  • The WorkspaceValueIfSet doc no longer says WorkspaceValue folds an unreadable delivery into "not configured".

m1: listing docs and tests. The WorkspaceConfigurationKeys and deliveredNames comments now say that a live file carrier is listed by its path. A carrier naming an empty file is therefore listed, and then reads as absent. A missing file, or a value delivered both inline and by file, is listed and fails by name when read. TestWorkspaceConfigurationKeysSeparatesAbsenceFromFailure gains both cases.

Fix or hack

Fix, at the owner. The bug was a second absent-or-failed classification in workspaceValueLive that disagreed with the one workspaceValueIfSetLive already had. This PR deletes the second and reuses the first. It adds no new mechanism.

Test plan

  • New TestAnUnreadablePublicValueIsNeverAnsweredFromTheSecretNamespace reproduces r139's probe. It establishes the premise (the public read fails with ErrFileCarrier, and the secret read returns secretvalue). It then asserts that WorkspaceValue fails with ErrFileCarrier, that the result is not absence and does not contain the secret, that ReadAuthority refuses with ErrAuthorityValueMissing plus ErrFileCarrier, and that Recheck on a pinned value whose public delivery broke refuses with ErrAuthorityValueChanged plus ErrFileCarrier even though the secret namespace holds the pinned value.
  • Reverting for.go and authority.go to main makes that test fail at the WorkspaceValue assertion, which gets a nil error.
  • ./scripts/check-one-implementation.sh: ok (86 files). ./scripts/security-posture.sh: SP-WC-01, SP-WC-05 ok.
  • go test -race -count=1 ./... passes in the root and in workcontext.
  • Root coverage is 80.1% (gate 75). workspaceValueLive and Recheck are at 100%, ReadAuthority 94.7%, WorkspaceValue 91.7%.
  • golangci-lint run ./...: 0 issues in both modules. go vet is clean.

Behaviour change for callers

A caller of WorkspaceValue whose public delivery is broken now gets the read error instead of the secret namespace's value. That is the fix. In the repository, only the per-key accessors' own tests rely on WorkspaceValue reaching the secret namespace, and only when the public value is absent, which still works.

release/0.4

#89 carries #87's two commits and this one onto release/0.4 (df5bd6a) with cherry-pick -x. All three apply with no conflicts.

Not verified

  • No service booted against a real runtime.

🤖 Generated with Claude Code

…ecret namespace (r139)

WorkspaceValue, ReadAuthority and Recheck read through workspaceValueLive. When
the public read failed (a missing file carrier, a value delivered both inline
and by file), workspaceValueLive discarded the error and returned whatever the
secret namespace held under the same name. Review r139 probed this and got
"secretvalue" back with a nil error. An authority read could pin, or a Recheck
could pass, on a value from a delivery the host never made for that name.

workspaceValueLive now goes through workspaceValueIfSetLive, which moves on to
the secret namespace only when the public value is absent and returns any other
failure. WorkspaceValue returns that failure as it is; ReadAuthority and
Recheck wrap it under ErrAuthorityValueMissing / ErrAuthorityValueChanged with
"cannot be read", so the refusal names the delivery's reason. The new test
fails against the old code at the WorkspaceValue assertion.

The listing doc comments now say what the code does: a live file carrier is
listed by its path, so one naming an empty file is listed and then reads as
absent. TestWorkspaceConfigurationKeysSeparatesAbsenceFromFailure covers that
case and a value delivered both inline and by file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@antoinetoussaint-byte
antoinetoussaint-byte merged commit c81481d into main Oct 26, 2026
16 checks passed
antoinetoussaint-byte added a commit that referenced this pull request Oct 26, 2026
…sed-0.4

release/0.4: workspace key listing (#87) and unreadable public value fails closed (#88)
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