Skip to content

fix: add SHA-256 hash chain to audit log ring buffer for tamper-evidence (PILOT-303)#26

Open
matthew-pilot wants to merge 1 commit into
mainfrom
openclaw/pilot-303-20260530-184500
Open

fix: add SHA-256 hash chain to audit log ring buffer for tamper-evidence (PILOT-303)#26
matthew-pilot wants to merge 1 commit into
mainfrom
openclaw/pilot-303-20260530-184500

Conversation

@matthew-pilot
Copy link
Copy Markdown
Collaborator

What

Adds a cryptographic hash chain to the audit log ring buffer in audit/audit.go.

Each Entry now carries two new fields:

  • PrevHash — SHA-256 hex digest of the preceding entry (empty for genesis)
  • Hash — SHA-256 hex digest of this entry, encompassing PrevHash + all payload fields

The chain is maintained by Append() on every write and rebuilt by RestoreLog() when restoring from a pre-chain snapshot. VerifyIntegrity() walks the chain and returns the index of the first tampered entry (or -1 when intact).

Why

The ring buffer was plain structs with no cryptographic integrity. An attacker with memory access (debugger, ptrace, JTAG) could drop or rewrite entries undetectably. The hash chain makes tampering computationally evident — any single-entry modification breaks the chain and is caught by VerifyIntegrity().

Scope

  • 1 file, +98/−1 lines
  • Deterministic binary serialisation (no JSON field-ordering ambiguity)
  • Backward-compatible: old snapshots without hash fields are chain-rebuilt on restore
  • All existing tests pass

Ticket

PILOT-303

…nce (PILOT-303)

Each audit Entry now carries PrevHash (digest of the preceding
entry) and Hash (digest of this entry, encompassing PrevHash
and all payload fields). The hash chain is maintained by Append()
and rebuilt on RestoreLog() when restoring from a snapshot that
predates the chain. VerifyIntegrity() walks the chain to detect
tampering.

Deterministic binary serialisation avoids JSON field-ordering
ambiguities across versions.
@matthew-pilot matthew-pilot added the matthew-fix-larger Medium-scope autonomous fix (≤10 files, ≤200 LoC) label May 30, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented May 30, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@matthew-pilot
Copy link
Copy Markdown
Collaborator Author

🦫 Matthew PR Check — #26 PILOT-303

Status

  • State: OPEN · MERGEABLE ✅
  • CI: 2/2 passing (test ✅, codecov/patch ✅)
  • Files: 1 (+98/−1 audit/audit.go)
  • Labels: matthew-fix-larger
  • Created: 2026-05-30 18:51 UTC
  • Branch: openclaw/pilot-303-20260530-184500main

Verdict

CLEAN — all CI green, MERGEABLE. Single-file change to audit/audit.go adding SHA-256 hash chain integrity to the ring buffer.

⚡ Autonomous PR by matthew-pilot · PILOT-303

@matthew-pilot
Copy link
Copy Markdown
Collaborator Author

🦫 Matthew Explains — #26 PILOT-303

What this does

Adds a cryptographic SHA-256 hash chain to the audit log ring buffer. Each Entry now carries:

  • PrevHash — digest of the preceding entry (empty for genesis)
  • Hash — digest of this entry (PrevHash + all payload fields, deterministically serialized)

The chain is maintained by Append() on every write and rebuilt by RestoreLog() when restoring from a pre-chain snapshot. A new VerifyIntegrity() method walks the chain and returns the index of the first tampered entry (or -1 when intact).

Why

The ring buffer was plain structs — no cryptographic integrity. An attacker with memory access (debugger, ptrace, JTAG) could drop or rewrite entries without detection. The hash chain makes any single-entry modification computationally evident.

Scope

  • 1 file, +98/−1 lines (audit/audit.go)
  • Deterministic binary serialization (no JSON field-ordering ambiguity)
  • Backward-compatible: old snapshots without hash fields are chain-rebuilt on restore
  • All existing tests pass

⚡ Autonomous PR by matthew-pilot · PILOT-303

@matthew-pilot
Copy link
Copy Markdown
Collaborator Author

📊 PR Status — #26 PILOT-303

Field Value
State OPEN
Mergeable ✅ MERGEABLE (CLEAN)
Draft No
Branch openclaw/pilot-303-20260530-184500main
Files 1 file, +98/−1 (audit/audit.go)
Labels matthew-fix-larger

CI Checks (2/2 passing)

Check Result
test ✅ pass
codecov/patch ✅ pass

Canary

🧪 Running — dispatched run 26692079608, pending completion.

Jira

PILOT-303 — QA/IN-REVIEW (assigned: Teodor Calin). Updated: 2026-05-30T21:51 EEST.

Last operator activity

PR created by matthew-pilot at 18:51 UTC. No operator activity yet.

@matthew-pilot
Copy link
Copy Markdown
Collaborator Author

🔍 PR Explanation — #26 PILOT-303

What this does

Adds a SHA-256 hash chain to the audit log ring buffer, making it tamper-evident. Each audit entry is cryptographically linked to its predecessor so that any modification, deletion, or reordering of the ring buffer is detectable.

The problem

The existing audit.Store ring buffer (audit/audit.go) had no integrity protection. An attacker with filesystem access could modify, delete, or reorder entries in the persisted audit log without detection — undermining the audit trail for compliance and forensics.

Walkthrough: audit/audit.go (+98/−1)

1. Struct changes (lines 34–44)

  • Entry gains two new fields: PrevHash (SHA-256 of the previous entry) and Hash (SHA-256 of this entry encompassing PrevHash + all content fields). Both are omitempty for backward-compat with existing snapshots.
  • Store gains lastHash string to cache the most recent entry hash, avoiding a full ring-buffer read on every Append.

2. Append (line 139) — Chain extension

  • Before locking: computes e.PrevHash = st.lastHash, then e.Hash = hashEntry(...).
  • After appending: st.lastHash = e.Hash — sets up the next link.
  • Net effect: every Append call produces an entry cryptographically linked to all prior entries.

3. RestoreLog (line 170) — Rebuild from snapshot

  • Calls st.rebuildHashChain() after replacing the log buffer, ensuring the chain is consistent after a snapshot restore.

4. New function: hashEntry (line 275) — Deterministic hashing

  • Serialises prevHash + timestamp + action + networkID (uint16 BE) + nodeID (uint32 BE) + details into SHA-256.
  • Uses binary.Write for fixed-width fields — deliberately not JSON to avoid field-ordering, whitespace, or encoding ambiguity across versions.

5. New function: rebuildHashChain (line 294) — Idempotent chain repair

  • Walks the ring buffer from entry 0 to N. For genesis: fills empty hash. For subsequent entries: checks PrevHash matches the previous Hash, and recomputes if broken.
  • Idempotent: if the chain is already valid, it is left untouched.

6. New function: VerifyIntegrity (line 322) — Public verification API

  • Walks the chain end-to-end and returns the index of the first broken link, or −1 if fully intact. Callable externally for on-demand integrity checks.

Why this approach

  • Deterministic binary serialisation prevents hash-chain breakage from JSON field reordering.
  • Idempotent rebuild means existing snapshots without hash fields upgrade cleanly (genesis entry gets an empty PrevHash and computed Hash).
  • Backward-compatibleomitempty fields mean existing serialised entries still parse correctly.

@matthew-pilot
Copy link
Copy Markdown
Collaborator Author

🤖 PR Status Check

PR #26: fix: add SHA-256 hash chain to audit log ring buffer for tamper-evidence (PILOT-303)
State: open | Mergeable: MERGEABLE (clean) ✅
CI: 2/2 ✅ all passing
Changes: +98/−1 in 1 file(s)
Labels: matthew-fix-larger


matthew-pr-worker • 2026-05-31T11:58:00Z

@matthew-pilot
Copy link
Copy Markdown
Collaborator Author

🤖 PR Explanation

fix: add SHA-256 hash chain to audit log ring buffer for tamper-evidence (PILOT-303)

Summary

What

Adds a cryptographic hash chain to the audit log ring buffer in audit/audit.go.

Each Entry now carries two new fields:

  • PrevHash — SHA-256 hex digest of the preceding entry (empty for genesis)
  • Hash — SHA-256 hex digest of this entry, encompassing PrevHash + all payload fields

The chain is maintained by Append() on every write and rebuilt by RestoreLog() when restoring from a pre-chain snapshot. VerifyIntegrity() walks the chain and returns the index of the first tam...

Changes

+98/−1 lines across 1 file(s):

  • audit/audit.go (+98/−1): "crypto/sha256"

Files Changed

audit/audit.go


matthew-pr-worker • 2026-05-31T11:58:00Z

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

canary-failed matthew-fix-larger Medium-scope autonomous fix (≤10 files, ≤200 LoC)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant