Source-code protection: gate raw source (thinking trace + citation fragments) behind source:view (#66) - #67
Merged
Merged
Conversation
Gate raw-source output to users while leaving read_source tool access and model reasoning unrestricted. One new default-deny capability (source:view) governs the thinking trace and citation-fragment bodies; the answer channel is protected by system-prompt guidance only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XPzYZXxyTdj5G25KoujpHb
Six tasks: source:view capability, /api/graph/source gate, thinking-event suppression, system-prompt rule, frontend gating, full-suite verification. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XPzYZXxyTdj5G25KoujpHb
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…66) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rs at the API (#66) A user without source:view still saw the reasoning trace and clickable citations: the client re-labels between-tool narration as 'thinking' steps, and the source/tool references were only hidden in the UI while still streaming over the SSE wire. Make the /chat stream answer-only for users without source:view: gate the tool, tool_result, and source events (and empty the done event's sources list) on can_view_source, alongside the existing thinking gate. Only token/done/error reach a restricted user. Audit accumulators are still built and logged server-side. Client renderTrace + Sources panel are gated on canViewSource as defense-in-depth. Spec amended to the answer-only decision. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XPzYZXxyTdj5G25KoujpHb
…, drop dead code (#66) Review of PR #67 surfaced four items: 1. (real) token narration leak: between-tool narration streams as `token` and, because tool events are suppressed for restricted users, the client never clears it — it accumulates and renders on the error path, and it crosses the wire regardless. Gate `token` too: for users without source:view the /chat stream is answer-only (done/error), and the answer is delivered via the done event (built from full/final_text). 2. (doc) /source requires only source:view: this is correct, not drift — /source backs the chat citation preview, so a source:view chat user without explore must reach it. Spec wording corrected to say so. 3. (cleanup) can_view_source reimplemented effective_capabilities by hand; now reuses auth.effective_capabilities(user, role) as the single source of truth (also drops the second capability-expansion copy). 4. (cleanup) SourceCard's canViewSource restricted path was unreachable (parent gates the panel); removed the dead prop/state/branch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XPzYZXxyTdj5G25KoujpHb
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.
Closes #66.
What & why
The agent ingests private source and can read it to answer questions — but a user could prompt it to dump whole files, turning the Q&A surface into a source-download channel. This gates the two channels that expose raw source to a web user, while leaving
read_sourcetool access and model reasoning unrestricted (reading code to answer is fine).Design decisions (from the user)
source:view.The three source channels, and how each is handled
token/done) → system-prompt rule (agent.py), applies to everyone including admin.thinkingSSE) → withheld unless the caller hassource:view(admin always sees it).GET /api/graph/source(the only endpoint returning rawlines) now requiressource:view. Reference rows (file:line) stay visible to all chat users; only the body is gated.Changes
auth.py— newCAP_SOURCE_VIEW = "source:view", default-deny (absent fromDEFAULT_CAPABILITIES, held by admin viaALL_CAPABILITIES).graph_api.py—/api/graph/sourcerequiressource:view; other graph routes unchanged.server.py—/chatcomputescan_view_sourcefrom the already-resolved role (fail-closed) and gates thethinkingemit.agent.py— system-prompt rule 7: never reproduce complete/near-complete files; decline dump requests.source:viewin the role editor (capabilities.ts),canViewSourcethreadedApp → Chat → SourceCard; citation preview shows a restricted note (and skips the fetch) without the cap.Migration / behavior change
Default-deny: existing non-admin roles lose the thinking panel and citation previews until an admin grants
source:view. Admin is unaffected.Testing
/api/graph/source→ 403 without the cap / 200 with it / 200 admin;thinkingevents suppressed without the cap and present with it (and for admin); system-prompt rule asserted.tsc -b && vite buildclean.Notes for reviewers
/sourceroute replacesCAP_EXPLOREwithCAP_SOURCE_VIEW(the spec said "in addition to");require_capchecks a single cap, and the net effect is stronger protection. Flagged in the final review as a non-blocking wording drift.mcp_server.py'sread_sourceremains unguarded by design — it's the stdio MCP server for local IDE use with no auth layer, outside this feature's boundary.🤖 Generated with Claude Code