feat: audit-log Gitea MCP mutating actions with profile metadata (#18)

Add durable, opt-in audit logging for every mutating Gitea MCP action so an
operator can see which execution profile and authenticated Gitea user
performed (or was blocked from / failed) each mutation.

- New gitea_audit.py: pure, no-network module — recursive secret redaction
  (token/password/authorization keys; token/Basic/Bearer value runs),
  build_event (timestamp, action, result, profile, audit label, authenticated
  username, repo, issue/PR, target branch, head SHA, redacted request
  metadata), and an append-only JSON Lines sink.
- mcp_server.py: _audit helper + _audited context manager (simple mutations)
  and an _audit_pr_result decorator (gated review/merge tools, reading their
  own result dict) wired into create_issue, create_pr, edit_pr, close_issue,
  commit_files, delete_branch, create_label, set_issue_labels, mark_issue
  (label/unlabel), gitea_submit_pr_review, and gitea_merge_pr.
- Outcomes recorded as allowed/blocked/failed/succeeded; blocked and failed
  eligibility checks are logged, not just successes.

Off by default: records are written only when GITEA_AUDIT_LOG is set. When it
is unset every audit path short-circuits — no records, no extra API calls — so
existing tool behaviour and API call sequences are unchanged. Auditing never
raises; sink writes are best-effort. Tokens are never written.

Docs: README env table + audit note, .env.example placeholder.
Tests: tests/test_audit.py (19 cases) — redaction, event build, sink writes,
per-tool success/failure/blocked records, secret-free output, off-by-default
no-op, and audit-failure-never-breaks-action.

Closes #18

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-01 22:20:51 -04:00
parent 20dd717b9c
commit c3c48fb7c2
5 changed files with 635 additions and 15 deletions
+7 -3
View File
@@ -168,6 +168,7 @@ Recognized environment fields (see [`.env.example`](.env.example) for placeholde
| `GITEA_AUDIT_LABEL` | Optional short label for this runtime, for audit purposes. |
| `GITEA_TOKEN_SOURCE` | Optional *name* of the token source (e.g. an env var name). A name only — never the token value. |
| `GITEA_BASE_URL` | Optional informational base URL. |
| `GITEA_AUDIT_LOG` | Optional path to an audit log file. When set, mutating actions append one redacted JSON record each (profile + authenticated user + outcome). Unset ⇒ auditing off (no records, no extra API calls). |
Notes:
@@ -180,9 +181,12 @@ Notes:
can inspect which runtime it is talking to before deciding to act.
- See [`docs/gitea-execution-profiles.md`](docs/gitea-execution-profiles.md) for
the full profile model.
- **Audit logging:** #16 returns structured gate/merge results but does not add
durable audit logging. Durable audit logging for Gitea MCP mutating actions is
tracked by #18.
- **Audit logging (#18):** mutating actions emit a durable, redacted JSON audit
record — timestamp, action, result (`allowed`/`blocked`/`failed`/`succeeded`),
profile name + audit label, authenticated username, target repo/issue/PR,
branch and head SHA where applicable — when `GITEA_AUDIT_LOG` is set. Auditing
is off by default and never adds API calls or breaks the action when off.
See [`gitea_audit.py`](gitea_audit.py).
</details>
<details>