fix(config): redact credentials in config get and refuse them in .cu.yml - #50
Merged
Merged
Conversation
Completes the credential surface started in #46, which left two gaps. `config get <key>` still printed a token in full. The argument for leaving it alone was that naming the key is a deliberate act, but that does not match what redaction defends against: incidental disclosure — pasted terminal output, a screen-share, a script whose stdout lands in a CI log. `get` is the spelling most likely to be captured by one. It was never a way to reach a secret cu uses, since authentication reads the keyring, so nothing is lost by redacting it. The value goes to stdout redacted; the pointer to the real file goes to stderr, so a person is told where to look while piped output stays clean. `SaveProjectConfig` wrote its settings map verbatim. Init already refuses to read a credential back out of .cu.yml, so writing one there would strand a plaintext secret on disk that cu never uses — the exact state the refusal exists to prevent, reached from the other direction. Both directions now share one `stripCredentials` helper, which copies rather than deleting in place so a caller reusing its map does not silently lose keys. Tests cover both, and both were confirmed to fail without the change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ZEGsLHBQ2GzP6v48i4hXz
timimsms
added a commit
that referenced
this pull request
Aug 29, 2026
These four files were swept into the previous commit by a broad `git add` while another change was in progress in the same working tree. They are unrelated to the search/export work this branch is about, and that commit's message does not mention them — so a change to what `cu config get` prints, and a new write guard on .cu.yml, were riding along undisclosed on a PR titled as a performance fix. They now live on their own in #50, against a clean main, where the security surface gets reviewed on its own terms. Nothing here depended on them: no code on this branch references GlobalConfigPath, stripCredentials, RedactedValue or IsCredentialKey, and the suite passes without them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ZEGsLHBQ2GzP6v48i4hXz
4 tasks
timimsms
added a commit
that referenced
this pull request
Aug 30, 2026
Brings in #50 and #51. One conflict, in the export flag block, where this branch's --include-closed/--subtasks met #51 moving the destination file off -o/--output onto --file/-F. Both sides kept: the new filters stay, and -o is left to the global format flag as #51 intends. Resolved as a merge rather than a rebase because another session owns this branch; force-pushing rewritten history over it is the one operation that could destroy work in progress. Docs regenerated for the combined flag set — the auto-merged page still advertised -o as the output file, which is no longer true. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ZEGsLHBQ2GzP6v48i4hXz
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Completes the credential surface started in #46, which left two gaps I flagged in review there.
config get <key>still printed the token in full. The case for leaving it alone was that naming the key is a deliberate act — but that does not match what redaction actually defends against, which is incidental disclosure: pasted terminal output, a screen-share, a script whose stdout lands in a CI log.getis the spelling most likely to be captured by one. And it was never a route to a secret cu uses, since authentication reads the keyring, so redacting costs nothing real.Splitting the streams is the point: piped output stays clean, while an interactive user is told exactly where the value lives and how to remove it.
GlobalConfigPathis exported so that path is the oneSavereally writes rather than a guess.SaveProjectConfigwrote its settings map verbatim.Initalready refuses to read a credential back out of.cu.yml, so writing one there would strand a plaintext secret on disk that cu never uses — the exact state the refusal exists to prevent, reached from the other direction. The only caller passesdefault_list, so this was latent rather than live.Both directions now share one
stripCredentialshelper. It copies rather than deleting in place, so a caller that reuses its settings map does not silently lose keys — covered by its own test.Behavior deliberately unchanged
A pre-existing plaintext token is still preserved, not scrubbed, exactly as #46 decided. Redaction hides it from casual output; it does not delete the user's data, and
getnow names the file so removing it by hand is obvious.Tests
Five tests across both packages: the
getandlistredaction paths, the.cu.ymlwrite guard, the caller-map-not-mutated property, and thatGlobalConfigPathnames the fileSaveactually writes. I verified the two behavioral ones fail without the change rather than passing vacuously — reverting thegetredaction and theSaveProjectConfigguard each produces a failure naming the leaked value.Note on provenance
These four files were briefly swept into #48 by a concurrent commit in the same working tree; that PR's message describes only the search/export work, so the credential change was riding along undisclosed. This PR carries them on a clean
mainbase. #48 should have them removed so each gets reviewed on its own terms.Checklist
go build,go vet,gofmt, and the full test suite pass on a cleanorigin/mainbase