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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user