feat: plan 16 P2 batch 2 (SEC-12 drift, SEC-29 audit fail-closed, SEC-30 replay, SEC-15 low)
- SEC-12: drift-detect adds semantic invariants — negation-flip detection (a dropped "not" now FAILS despite high char-similarity) + env must-keep patterns. - SEC-29 (X-05): governance-check audit write fails CLOSED — an unwritable audit log denies the action and empties the output (no unaudited output). - SEC-30 (X-06): approval-verify records a one-time-use nonce (sha of token/sig) and rejects replays (enforced mode / when a nonce ledger is set); dev unchanged. - SEC-15 (low): typosquat distance<=2 with the levenshtein length-sentinel bug fixed (no false positives); tool-exec fails closed with no timeout backend in enforced mode; validate-tool-input now validates nested objects/arrays recursively. Verify: new SEC suites all green via gate, run-casan4 0-FAIL, adversarial 44/44, track-c 29/0, h5-approval 12/0, no regressions. Plan-16 P2 remaining: infra-gated only (SEC-14/22/23/24/25/26). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
8c06a55aed
commit
d695a598ee
@@ -155,7 +155,10 @@ TRACE_FILE="$TRACE_DIR/governance-$TRACE_ID.json"
|
||||
# raw, so a value containing `"` + newline could inject a SECOND forged audit record
|
||||
# (a fabricated "approved" decision). The record_hash is still computed from
|
||||
# RECORD_CORE above, so verify-audit-chain.sh recomputes and matches unchanged.
|
||||
CASAN_GC_REASONS="$REASONS_JSON" python - "$TRACE_FILE" "$AUDIT_LOG" \
|
||||
# SEC-29 (X-05): the audit write must FAIL CLOSED. If the audit log cannot be
|
||||
# written (disk full, read-only, quota), there must be NO governed action without
|
||||
# its accountability record — deny and empty the output rather than proceed.
|
||||
if ! CASAN_GC_REASONS="$REASONS_JSON" python - "$TRACE_FILE" "$AUDIT_LOG" \
|
||||
"$TIMESTAMP" "$TRACE_ID" "$ACTION_NAME" "$ACTOR" "$RISK_LEVEL" "$DECISION" \
|
||||
"$APPROVAL_STATUS" "$APPROVER" "$INPUT_HASH" "$OUTPUT_HASH" "$PREV_HASH" "$RECORD_HASH" <<'PY'
|
||||
import json, os, sys
|
||||
@@ -180,7 +183,14 @@ with open(audit_log, "a", encoding="utf-8") as f:
|
||||
# Compact separators: the chain line is regex-parsed elsewhere and must match
|
||||
# the original printf format (no space after ':' / ',').
|
||||
f.write(json.dumps(rec, separators=(",", ":")) + "\n")
|
||||
f.flush()
|
||||
os.fsync(f.fileno())
|
||||
PY
|
||||
then
|
||||
: > "$OUTPUT_FILE" 2>/dev/null || true
|
||||
echo "GOVERNANCE_DENIED trace_id=$TRACE_ID reason=audit_unwritable (fail-closed: no governed action without an audit record)" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# --- External anchor: cryptographically sign the new chain head ---
|
||||
# A re-forged chain (recomputed hashes) changes the head; without the private
|
||||
|
||||
Reference in New Issue
Block a user