bugwarden
以下摘录自该仓库的 README(片段) · 在 GitHub 读完整 README →
**bugwarden** is a Model Context Protocol (MCP) server, written in Rust, with
operator-controlled security guards. It exposes a Bugzilla instance to LLM
clients — querying bugs,
searching, reading comments and history, and (where permitted) updating bugs —
while a policy file that the model can neither see nor change decides, per bug,
what the model is allowed to do.
The Bugzilla REST API already enforces *user* permissions via the API key. What
it cannot do is enforce a *narrower* set of permissions for an AI agent acting
on that user's behalf. bugwarden sits in between: the operator writes a small
TOML policy ("embargoed security bugs are invisible", "on the Security product
the agent may only read summaries and leave comments", "nothing younger than a
week exists"), and every tool call is checked against it before Bugzilla is
touched or data is returned.
Features
- **Complete Bugzilla tool surface**: bug details, history, comments,
attachment metadata and content, quicksearch, comment/status/field/
assignee/CC/dependency updates, duplicate marking, bug filing, attachment
upload, server info, quicksearch syntax docs, and a bug-summarization
prompt tool.
- **Guard policy engine**: per-bug allow / deny / restrict decisions
matched on product, component, group, keyword, status, severity, priority,
whiteboard, summary, group-restrictedness, bug age, and authorship (whether
the requesting account filed the bug) — with a fine-grained 13-capability
vocabulary for restrict.
- **No existence oracle**: a policy-denied bug is indistinguishable from a
nonexistent one.
- **Silent search filtering**: denied bugs simply never appear in search
results; summary-only bugs appear redacted.
- **Minimum-age quarantine**: min_bug_age_days makes recently filed bugs
(the ones most likely to contain not-yet-triaged sensitive data) invisible.
- **Read-only mode and tool disabling** remove write tools from the MCP tool
listing entirely — clients never see them, rather than seeing them error.
- **Audit stream**: an operator-only JSONL record of every tool call — the
guard's verdict, the deciding rule, the bug ids it suppressed — with
fail-closed modes that hold back further work while records cannot be
persisted. The stream is the operator's half of the bargain the guard
makes with the client, and no MCP surface can reach it.
- **Two transports**: streamable HTTP (per-request API key header, or a
server-held key via --api-key-file for fleet deployments) and stdio
(subprocess launch by a desktop MCP client).
- Single static binary, async throughout (tokio + rmcp),
shipping with a man page and bash/zsh/fish completions.
Security model
The guard concept
The guard policy is loaded **once, at startup**, from a TOML file passed via
--policy (or BUGWARDEN_POLICY). It lives on the operator's filesystem. The
MCP client — i.e. the model — has no tool to read it, list its rules, or
modify it. mcp_server_info intentionally exposes only coarse facts: the rule
count, the default action, min_bug_age_days, whether the server is
read-only, and which tool names are disabled. Rule names and match criteria
are never revealed.
Every tool that takes a bug id first fetches the bug's classification metadata
(product, groups, keywords, creation time, …) and evaluates the policy
**before** any side effect happens or any data is returned. The only exception
is bug_url, which computes a URL string locally and contacts nothing.
Invariants
- **Uniform denial.** A denied bug and a nonexistent bug produce the exact
same response: Bug {id} is not accessible through this server. No wording
or detail difference can be used as an existence oracle for embargoed bugs.
- **Silent search filtering.** Search results are post-filtered through the
policy; the client is never told how many results were dropped or that
filtering happened at all. The operator is: a server-side debug log
records it, and an audited deployment gets the scan's counts and the
withheld ids in the audit stream.
- **Fail closed.** If the classification fetch fails, if a bug is absent from
the response, or if a rule consulted for the operation being decided cannot
be decided because the bug object did not carry a field that rule asks
about — or, for the identity criterion created_by_me, because the
bug–caller relationship could not be established — the bug is treated as
denied — never as allowed. (A rule scoped away from the operation via
operations is not consulted at all — scoping changes which rules run,
never how a consulted rule resolves.)
- **Private-comment gate.** Private comments are returned only when the policy
sets allow_private_comments = true **and** the individual call opts in