Skip to content

v0.797.0

Choose a tag to compare

@github-actions github-actions released this 01 Jul 06:13
v0.797.0
137637d

v0.797.0 — audit: VerifyChain reports a poisoned success value as a chain break

Fixed (safety — audit tamper-evidence robustness)

  • VerifyChain scanned the success column into a bare Go bool. Legitimate rows
    always store 0/1 (schema DEFAULT 1), but success and entry_hash are
    independent columns an attacker with direct SQLite write can set separately.
    Setting success to NULL or an out-of-range integer (e.g. 2) makes rows.Scan
    fail ("couldn't convert 2 into type bool"), so VerifyChain returns an error
    and ABORTS the entire walk instead of the designed "chain broken at row N"
    verdict. A single UPDATE ... SET success=NULL turns the forensic integrity
    check into a self-inflicted DoS.

    This is fail-loud, not fail-open — it can never read a tampered log as
    Valid=true — but it degrades tamper-evidence: an operator gets an opaque type
    error instead of a pinpointed break, and the verifier can't report which rows
    are broken. Fix: scan success as sql.NullInt64 (matching the sibling
    duration_ms) and feed success.Int64 == 1 into chainHash. Behaviour-preserving
    for legitimate 0/1 rows; any poisoned value now flows into the hash recompute,
    fails the comparison, and is correctly reported as a chain break
    (Valid=false, FirstBrokenID=id).

Found via the internal audit sweep of the query DSL + hash chain. The rest
verified sound: chain pre-image injectivity (length-prefixed fields), verify
recomputes rather than trusting the stored hash, reorder/deletion/truncation +
CheckpointAnchor detection, fully-parameterized WHERE builder, capped LIMIT,
writeMu + flock single-writer, CSV formula-injection guard.

Verified

  • task ci green: golangci-lint 0 issues, go vet, build, task test:full (full
    suite, -race) 0 fail, govulncheck clean. task eval 17/17. All five release
    cross-compile targets build green.
  • TestVerifyChain_PoisonedSuccessReportsBreak (success=2 and success=NULL):
    reports a break at the poisoned row without erroring; proven to fail pre-fix
    (both aborted with a bool scan error).

Follow-up (LOW, banked): Query/QueryFiltered share the bare-bool scan, where a
NULL-success row is silently dropped from results; barely reachable, tracked
separately.