Skip to content

feat: scope-compliance audit trail for every request#17

Merged
TrueNix merged 3 commits into
mainfrom
feature/scope-audit-log
Jul 12, 2026
Merged

feat: scope-compliance audit trail for every request#17
TrueNix merged 3 commits into
mainfrom
feature/scope-audit-log

Conversation

@TrueNix

@TrueNix TrueNix commented Jul 12, 2026

Copy link
Copy Markdown
Owner

What

Adds a scope-compliance audit trail: an append-only log recording the scope decision (pass / fail / skip) for every request the scanner makes. ScopeGuard already enforces scope, but keeps no record — this is the accountability layer that lets you prove, after an engagement, that every request stayed inside the authorized allowlist.

Pieces

  • audit_log.AuditLog — append-only JSONL (default ~/.hermes/audit.jsonl), one line per request: url, method, scope_check, ts, schema_version, plus optional session_id/response_status. Size-rotated with keep-backups, fcntl-locked appends, fail-closed validation, corruption-tolerant reads, and a summary() rollup that flags out-of-scope hosts. Dependency-free (stdlib only).
  • SecurityScanner gains an optional audit_log. _make_request routes both the initial and redirect scope checks through a new _scope_check, which records the outcome.

Safety of the wiring

Behaviour-preserving by construction: with no audit_log attached, _scope_check is identical to the previous inline self.scope.check(url) — same enforcement, same OutOfScopeError hard-stop. Audit I/O and validation errors are swallowed inside _record_scope, so a full disk or lock contention can never break a scan. It's an accountability log only — it carries the scope decision, never findings, payloads, or secrets.

Tests

tests/test_audit_log.py (module: counts, rotation, locking-safe append, fail-closed validation, corruption tolerance, session id) and tests/test_scope_audit.py (engine wiring: pass/fail/skip recorded, OutOfScopeError still propagates, inert without a log). Verified locally by executing the module directly and simulating the _scope_check logic against real ScopeGuard/AuditLog instances.

Attribution

Pattern adapted from shuvonsec/claude-bug-bounty (MIT) — its memory/audit_log.py logs scope_check per request. Reworked to this repo's conventions and consolidated (schema + rotation into one module).

⚠️ CI gates (pytest/ruff/mypy) weren't run in my environment (no pip). Syntax, line-length (≤100), and behavioural logic were verified directly; please rely on CI for the full gate.

🤖 Generated with Claude Code

TrueNix and others added 3 commits July 12, 2026 13:33
Adds an append-only audit log that records the scope decision (pass/fail/skip)
for every request the scanner makes. ScopeGuard already enforced scope but kept
no record; on a real engagement you often need to prove after the fact that
every request stayed inside the authorized allowlist. This is that record.

- New audit_log.AuditLog: append-only JSONL at ~/.hermes/audit.jsonl, one line
  per request (url, method, scope_check, ts, schema_version, optional
  session_id/status). Size-rotated, fcntl-locked, fail-closed validation,
  tolerant reads, and a summary() rollup that flags out-of-scope hosts.
- SecurityScanner gains an optional audit_log; _make_request routes both the
  initial and redirect scope checks through _scope_check, which records the
  outcome. Wiring is behaviour-preserving: with no audit log attached it is
  identical to the previous inline scope.check, and audit I/O never breaks a scan.
- Tests for the module and the engine wiring.

Pattern adapted from shuvonsec/claude-bug-bounty (MIT), whose memory/audit_log.py
logs scope_check per request. Reworked to this project's conventions and
consolidated (schema + rotation) into one dependency-free module.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@TrueNix
TrueNix merged commit bed3075 into main Jul 12, 2026
5 checks passed
@TrueNix
TrueNix deleted the feature/scope-audit-log branch July 12, 2026 12:11
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