Repository navigation
feat(insurance)!: make Config.APIKey an opaque Secret - #338
Conversation
Closes #330. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Gate finding 1 (blocker) at e88c535:
errcheck (check-blank) flags |
|
Gate review (Opus 5.5, credential path) at e88c535: CHANGES REQUESTED, not merged. Blockers:
Non-blocking (please fold into the same push): CHANGELOG omits two further source/behaviour breaks caused by the func field: Verified clean (local, GOTOOLCHAIN=go1.26.9, from git archive of the head):
Re-gate needed at the new head after the lint fix and body update. |
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Merge-order note from the #339 gate: #339 merges textually clean with this PR, but the combined tree does not compile. #339 adds |
|
Gate re-review (Opus 5.5): APPROVED at 11f0e03, merging.
|
Closes #330.
What and why
insurance.Config.APIKeywas an exportedstring, so a caller that holds aConfigin an unexported struct field printed the key raw: fmt does not callFormaton unexported fields.APIKeyis nowinsurance.Secret, which keeps the key in a closure so no reflection-based printer can reach it. Methods:NewSecret,Reveal(nil-guarded; used only at client.go header-set and the blank-key check),Format(all fmt verbs and slog text) andMarshalText(json, xml, yaml.v3, slog JSON).Config.Formatand its %p/%T caveat are removed.Fail-before control (scratch test on 16b5795, not shipped; one file cannot compile with both types)
Struct with an unexported
cfg Configfield:The gate reproduced the same with key
fb-SECRET(APIKey:fb-SECRET). After this change no verb, json, xml, yaml, gob, slog or text/template output contains the key.Tests
Extends
TestConfigAndClient_NeverFormatTheKeyand addsTestSecret_NeverRevealedByAnyPrinter(unexported Config/*Config/Secret holders, exact[redacted]on exported paths). Storing the key as a string field in Secret fails 44 assertions.go test ./insuranceand golangci-lint on pkg pass.Breaking changes (CHANGELOG)
Source break
Config{APIKey: k}->Config{APIKey: insurance.NewSecret(k)};%vof a Config is{[redacted] <org>};reflect.DeepEqualof two copies of one Config is false; Config is no longer comparable (==fails to compile, map key panics unhashable);json.Unmarshalinto a Config withAPIKeynow errors (was ignored byjson:"-").Consumer migration (separate PRs after merge)
internal/archera/provider.go:138:APIKey: insurance.NewSecret(key).keyas theget()string forarcheraError, wrap withNewSecretwhen building the Config, and editarchera_comparison_test.gocall sites (:121, :137, :364, :366).Labels mirrored from #330.
🤖 Generated with Claude Code