fix(server): gate issue_info attachment metadata on capability - #9
Merged
Conversation
issue_info's Read path fetches the issue with no field projection, so Jira's default navigable field set — attachment, comment, worklog — reached the client on a bare read grant, bypassing the capabilities that gate those families on every other route. Guard::scrub_gated_fields now runs on both the full-body Read path and the summary/projection fallback before a body is served (I6): - fields.attachment requires Capability::Attachments — filenames, sizes, and content URLs are reachable only through the attachments capability, matching list_attachments/download_attachment. - fields.comment requires Capability::Comments, matching issue_comments; when granted, restricted-visibility comments are still removed because issue_info has no per-call restricted opt-in, so I5's dual opt-in cannot be satisfied on this route. - fields.worklog is always stripped: no v1 capability covers worklog content, and ungoverned content is stripped, not served (I4). - count-only fields.watches / fields.votes stay in the read body (deliberate: counts, no identities, no restricted content). Removals are silent to the client (I3 spirit): no markers, and the embedded comment total tracks the served list. The scrub composes with the existing I14 link scrub on the same path. DESIGN.md I6 records the resulting Read field contract. Covered by guard unit tests and MCP-level wiremock tests: read without attachments/comments serves the body minus those families while list_attachments and issue_comments stay uniformly denied (I2), a granted comments capability still filters the restricted comment, and a granted attachments capability serves the metadata. Invariants touched: I2, I3 (spirit), I4, I5, I6, I14.
plusky
enabled auto-merge (rebase)
August 8, 2026 19:53
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.
issue_infore-fetched Read-granted issues with no field selection and served the body as-is, sofields.attachment— filenames, sizes, authors, content URLs — reached the client even when the policy withheldCapability::Attachments.list_attachmentsgates exactly that metadata on the capability, so the capability was enforced on two of the three routes to the same data. I6 saysreadimpliessummaryand nothing else.issue_infonow stripsfields.attachmentunless the grant includesattachments, on both the full-body path and the projection fallback path; the removal is not reported to the client. DESIGN.md records the Read-vs-Attachments field contract.Scope note: the finding's broader "review other field families on the unfiltered fetch" item is deliberately not part of this PR.
Invariants: I6; removal invisible to the client (I3 in spirit); composes with the existing I14 link scrub on the same path.
Tests: pins that
read-without-attachmentsserves the issue withoutfields.attachmentwhile the rest of the body is intact; confirmed to fail against the unfixed code. (This PR passed adversarial review on its second round — the first round was refuted and revised.)Process: AI-assisted — implemented and independently adversarially reviewed against
docs/DESIGN.md(final verdict: approve, with an empirical pre-fix-failure check). Full verification suite green at the commit.