Commit 1480ab4
fix: three critical issues blocking v1.0 (C1 telemetry persistence + C2 credential leak + C3 context race) (#95)
* fix(telemetry): split skip tiers so --json doesn't kill consent state (C1)
The first-run consent prompt previously persisted `decided=off` on any
skip signal — including transient ones like `--json`. A user running
`raid context --json | jq` once was silently opted out of telemetry for
life, never seeing the prompt in any future interactive run.
Split MaybePromptForConsent into two skip tiers:
- Persistent (--yes / --headless / RAID_HEADLESS / non-TTY /
DO_NOT_TRACK): long-term "this host is non-interactive" signals
that legitimately should persist decided=off so we don't keep
re-prompting.
- Transient (--json): per-invocation "this one command wants
machine-readable output" that must NOT poison the consent state.
Precedence: persistent wins when both signals are set (`--yes --json`
together → persist). Reordered the body to check persistent first; a
regression test pins this contract.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(security): scrub repo URL credentials + 0600 vars file (C2)
Two related changes that close a credential-leak path:
1. setRepoVars now scrubs userinfo from each repo's URL before
writing it to raidVars. A profile YAML using the common
`https://user:token@github.com/...` clone-URL pattern no longer
persists the credential to ~/.raid/vars, no longer surfaces it
through the MCP `raid://workspace/vars` resource, and no longer
lands verbatim in $RAID_REPO_<NAME>_URL for subprocesses to see.
SSH-style `git@host:repo.git` URLs and unparseable inputs pass
through unchanged. Re-exported as `raid.ScrubURL` and applied in
the MCP `raid_list_repos` handler too.
2. execSetVar's atomic write now chmods the tempfile to 0600
before the rename so the vars file lands at the tight mode
regardless of godotenv's 0644 default. loadRaidVars best-effort
tightens existing 0644 files on the next load for migration.
The vars file holds scrubbed-but-still-private RAID_REPO_*_URL
entries and arbitrary user-defined Set values (which project
authors may treat as secret-ish). 0644 + a shared dev machine is
a real leak path; 0600 is the right baseline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(concurrency): atomic.Pointer for global workspace context (C3)
The package-global `context *Context` in src/internal/lib was read
concurrently by MCP read handlers (raid_list_repos, raid_describe_repo,
raid_list_profiles, ...) while mutating handlers (raid_install,
raid_env_switch) called ForceLoad which rewrote the pointer. mcp-go
dispatches tool calls from a 5-worker pool, so the reader/writer
interleave was a real undefined-behavior race — `go test -race` would
have flagged it against any representative MCP scenario.
Replace with atomic.Pointer[Context]. Reads go through loadContext();
writes through storeContext(). The value is always wholly replaced
(never partially mutated) so the atomic pointer is the right primitive:
zero contention on the read hot path, well-defined swap semantics for
the rare write path.
Mechanical sweep:
- 15 production read sites: bind the result of loadContext() to a
local before nil-checking + field access, so a swap landing
between the nil-check and the read can't confuse subsequent
reads from the same logical operation.
- 89 test sites: same treatment, with the bare `context = &Context{}`
test pattern replaced by storeContext(&Context{}).
New TestContextRaceForceLoadVsReads stresses the interleave with 8
concurrent readers + 1 writer; the race detector enforces the
contract. Any future contributor reintroducing a non-atomic write to
the context pointer trips this test.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: address Copilot review — scrub semantics, vars chmod guard, test portability
- ScrubURL: only strip userinfo from http/https schemes; preserve SSH usernames
so `ssh://git@host/...` stays a valid clone URL after scrubbing.
- loadRaidVars: Lstat + IsRegular guard before chmod so a misconfigured path
pointing at a directory or symlink isn't tightened/followed.
- handleListRepos: add credential-bearing-URL regression test at the MCP
handler boundary so a future refactor can't bypass raid.ScrubURL.
- prompt.go: realign the comment with the no-API-key short-circuit — dev
builds don't persist decided=off, they just skip without recording state.
- task_runner_extra_test: skip 0600 perm-bit assertions on Windows where
os.Chmod only toggles the read-only attribute.
- lib_test: add a start barrier to the C3 race regression so reads and
writes are guaranteed to overlap; document the CI gap that the race
detector isn't on by default in the project workflow.
Co-Authored-By: Copilot <copilot@github.com>
* chore: bump version 0.17.0 → 0.17.1 + what's-new for hotfix
Three bug fixes per the versioning rule warrant a patch bump. Adds a
0.17.1 what's-new section covering all three: the --json consent
bug, the credential-leak via vars file (with the explicit "rotate
your token" warning for users on prior versions), the 0600 file
permission tighten, and the MCP context race.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Mr. Meeseeks <meeseeks@alexsalerno.dev>
Co-authored-by: Copilot <copilot@github.com>1 parent b70e93c commit 1480ab4
23 files changed
Lines changed: 705 additions & 262 deletions
File tree
- site/docs
- src
- cmd
- context
- internal
- lib
- telemetry
- raid
- resources
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
12 | 24 | | |
13 | 25 | | |
14 | 26 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
520 | 520 | | |
521 | 521 | | |
522 | 522 | | |
523 | | - | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
524 | 527 | | |
525 | 528 | | |
526 | 529 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
563 | 563 | | |
564 | 564 | | |
565 | 565 | | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
566 | 604 | | |
567 | 605 | | |
568 | 606 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
169 | 169 | | |
170 | 170 | | |
171 | 171 | | |
172 | | - | |
173 | | - | |
174 | | - | |
175 | | - | |
176 | | - | |
177 | | - | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
178 | 182 | | |
179 | | - | |
180 | | - | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
181 | 186 | | |
182 | 187 | | |
183 | 188 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
67 | 67 | | |
68 | 68 | | |
69 | 69 | | |
70 | | - | |
| 70 | + | |
| 71 | + | |
71 | 72 | | |
72 | 73 | | |
73 | | - | |
| 74 | + | |
74 | 75 | | |
75 | 76 | | |
76 | 77 | | |
77 | 78 | | |
78 | | - | |
| 79 | + | |
| 80 | + | |
79 | 81 | | |
80 | 82 | | |
81 | | - | |
| 83 | + | |
82 | 84 | | |
83 | 85 | | |
84 | 86 | | |
| |||
0 commit comments