Files
coder/coderd
Kyle Carberry 5576dc86b7 fix(agent/agentcontext): address Mike's review (symlink, secrets, watcher, caps)
Six concrete fixes from @ibetitsmike's review on PR #25983:

P1 symlink arbitrary read: the resolver previously called
os.ReadFile on any AGENTS.md/.mcp.json/SKILL.md it walked into,
including symlinks, so a workspace with
`nested/AGENTS.md -> ~/.ssh/id_rsa` would have shipped the
target. Symlinks are still followed (codex follows them too,
and they are the standard way to compose monorepo agent docs),
but the resolved target must now stay inside the contributing
scan root or the resource is emitted as StatusInvalid. Broken
symlinks and non-regular targets are emitted as StatusUnreadable.

P1 .mcp.json secret exfiltration: readMCPConfig used to ship
the raw file bytes, which embed Env tokens and Authorization
headers. The resolver now produces metadata only (path,
SizeBytes, ContentHash). Change detection still works; the
live tool list flows through MCPProvider as a KindMCPServer
resource, which is what consumers actually need.

P2 v2.10 contract ahead of behavior: revert
tailnet/proto.CurrentMinor to 9, switch the agent's connect
site back to ConnectRPC29WithRole, and disable the
PushContextState push goroutine. The DRPCAgentClient210
interface, the agentcontext.Manager, and the push code paths
remain compiled and tested so the v2.10 bump in the follow-up
that adds coderd persistence is a small, focused change. A
TestAgent_ContextStatePushed skip and a comment at the connect
site call out the re-enable steps.

P2 boot-time env sources lost: relative CODER_AGENT_EXP_*_DIRS
paths used to be dropped during agent.init when the manifest's
working directory was still unknown. Add Manager.SeedSources,
a trusted late-binding equivalent of InitialSources that
bypasses AllowedRoots, and call it from handleManifest with
the now-resolved working directory before Trigger.

P2 degraded watcher re-arm loop: when fsnotify init fails, Sync
used to schedule its OnChange callback, the Manager's signal
handler turned it into another Sync, and the cycle repeated
every 250ms forever. Degraded Sync is now a true no-op; manual
Resync, AddSource, and RemoveSource still drive re-resolves.

P2 stale-epoch Resync mutating the watcher: a Resync pass that
loses the epoch race already skipped publishing its stale
snapshot, but still called watcher.Sync with stale roots. That
removed watches on sources only the winning pass knew about.
The stale branch now returns the published snapshot without
touching the watcher.

P2 MCP byte cap not enforced: applyMCPCaps previously only
checked the resource-count cap, so a provider returning one
big KindMCPServer payload could exceed MaxSnapshotBytes with
StatusOK. The cap helper now picks up where applyCaps left off
and stamps StatusExcluded plus a snapshot error on MCP entries
that breach the count or byte budget.

Adds regression tests for symlink-inside, symlink-outside,
broken symlinks, MCP secret omission, MCP byte cap, and the
manifest-late SeedSources path.
2026-06-05 16:02:10 +00:00
..
2026-06-03 15:37:19 -05:00