Skip to content

fix(server): serve bug_info bodies on their own verdict - #17

Merged
plusky merged 1 commit into
mainfrom
fix/bug-info-reclassify
Jul 27, 2026
Merged

fix(server): serve bug_info bodies on their own verdict#17
plusky merged 1 commit into
mainfrom
fix/bug-info-reclassify

Conversation

@plusky

@plusky plusky commented Jul 27, 2026

Copy link
Copy Markdown
Owner

The race

bug_info classified one fetch and returned another. The Read verdict came
from the CLASSIFY_FIELDS request; the body came from a second request issued
afterwards. A bug embargoed in between was still returned in full — the
server's key is privileged enough that Bugzilla keeps serving it, and the
guard had simply stopped looking.

The previous PR widened this: with per-id classification, a bug named early in
a call is classified several round trips before its body arrives.

The fix

Every fetched body goes back through the guard and is served on its own
verdict. It costs no upstream request — the body is a superset of the
classification fields, so the re-check reads what is already in hand. Outcomes:

re-check says served as
still readable the full body
only summary now the summary view a fresh call would return
nothing uniform restricted entry, and the flip is logged server-side (I3)

download_attachment already re-checked its blob response for this reason;
bug_info now matches it. The assembly moved into assemble_bug_info, a free
function, so the envelope is testable without an MCP request context.

Also: an I2 leak in the same block

The old code returned Failed to fetch bug info\nReason: {e} to the client,
and that {e} carries Bugzilla's own message — including Bug #N does not exist. That is exactly the distinction I2 exists to erase. It is logged
server-side now and the affected ids fall to the uniform denial, which also
means one unfetchable body no longer aborts the call.

This wasn't in the original finding list; it surfaced during review of the
previous PR. It lives in the lines this PR rewrites, so fixing it here avoids
touching them twice.

What this does NOT fix

The bodies are still fetched in one batch, so a Bugzilla that fails the
whole request over a single bad id still costs every Read-granted body in that
call — they all fall to the uniform denial together. That is availability, not
disclosure: only ids the caller already passed classification for can poison
it. Splitting it per id would multiply requests for bodies the caller is
entitled to anyway, so it is left alone deliberately.

Verification

140 tests (+7). The load-bearing one hands the assembler a Read verdict and a
body carrying an embargo group — it fails against the old code by
construction, which served full.get(id) unconditionally. Others pin the
summary downgrade, a body granting neither read nor summary, byte-identical
restricted entries across all three failure modes (including field count, so
no mode is distinguishable), and request-order output on a mixed call.

bug_info classified one fetch and returned another. The Read verdict came
from the CLASSIFY_FIELDS request; the body came from a second request
issued afterwards. A bug embargoed in between was still returned in full,
because the server's key is privileged enough that Bugzilla keeps serving
it — the guard had simply stopped looking. Per-id classification widened
that window, since a bug named early in a call is now classified several
round trips before its body arrives.

Put every fetched body back through the guard and serve it on its own
verdict. This costs no upstream request: the body is a superset of the
classification fields, so the re-check reads what is already in hand.
A body that now earns only summary is served as the summary view a fresh
call would return; one that earns nothing becomes the uniform restricted
entry, and the flip is logged server-side (I3) since it is the anomaly
worth knowing about. download_attachment already re-checked its blob for
the same reason.

The assembly moves into assemble_bug_info, a free function, so the
envelope can be tested without standing up an MCP request context.

Also stop forwarding the body fetch's error to the client. Bugzilla's
message names the bug and states whether it exists ("Bug #N does not
exist."), which is precisely the distinction I2 exists to erase; it is now
logged server-side and the affected ids fall to the uniform denial rather
than aborting the call.

The bodies are still fetched in ONE batch, so a Bugzilla that fails the
whole request over a single bad id still costs every Read-granted body in
that call — they all fall to the uniform denial together. That is
availability, not disclosure: only ids the caller already passed
classification for can poison it. Splitting that fetch per id would
multiply requests for bodies the caller is entitled to anyway, so it is
left alone deliberately.
@plusky
plusky merged commit c73ff0c into main Jul 27, 2026
10 checks passed
@plusky
plusky deleted the fix/bug-info-reclassify branch July 27, 2026 19:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant