Skip to content

fix: HIGH-tier batch 2 — receipt/audit-chain determinism, command injection, Solidity overflow#6

Closed
New1Direction wants to merge 4 commits into
fix/high-tierfrom
fix/high-tier-2
Closed

fix: HIGH-tier batch 2 — receipt/audit-chain determinism, command injection, Solidity overflow#6
New1Direction wants to merge 4 commits into
fix/high-tierfrom
fix/high-tier-2

Conversation

@New1Direction
Copy link
Copy Markdown
Owner

Second (final) batch of HIGH-severity audit fixes — completes the tier. Stacked on #5; base is fix/high-tier.

Fixed in this PR

  • Mesh receipt hash folded wall-clock telemetry (determinism) — ExecutionReceipt.compute_hash included resource_usage (cpu_ms) and status_detail (which for TIMEOUT embeds "Elapsed {ms}ms > max ..."). Receipts are chained + verify_chain recomputes, so machine-speed jitter changed the whole ledger chain. Hash now covers only logical identity (status enum kept; usage/detail stay as stored metadata).
  • Production audit hash chain wasn't reproducible (determinism) — AuditLogger.log folded the wall-clock audit_id (time.time()*1e6) and a datetime.now() timestamp into the hashed payload, so replaying the same logical sequence produced a different audit_hash chain. The chain now hashes only the logical event + prev_hash; audit_id/timestamp remain stored columns.
  • BashCommandHook command injection (security) — interpolated context values (incl. agent-controlled tool_input) were spliced into a subprocess.run(shell=True) string with no escaping. Each interpolated value is now shlex.quote-d, so a value with ;, |, $(...), backticks is a single literal argument.
  • Solidity sentry i64 overflow (Rust parity) — parse::<i64>().unwrap() panicked on an oversized version component (e.g. a 20-digit major); Python's int() is arbitrary-precision. Oversized/unparseable components → sentinel that can't match the small buggy-release constants, reproducing Python's "not flagged" outcome with no panic. (The panic was already caught by PR fix: resolve 3 critical security findings (sandbox RCE, unauth console, Rust panic fail-safe) #3's run_agent_safe; this removes it at the source and restores byte-parity.) Also refactored run_agent_safe to an agent-independent catch_panic helper so its test no longer depends on this bug.

Verification

  • Full CI Python suite: 1278 passed, 0 failed (PYTHONHASHSEED=0) · Rust: 784 + 13 passed · ruff check + format clean.
  • Each fix is RED→GREEN; the two determinism ones assert reproducibility across independent runs / hash seeds.

HIGH tier status

With this PR, all HIGH findings from the audit are addressed (the prior 4 in #5; these 4 here). The one remaining audit item — pi-semantic-governance.yml "dependency-confusion gate" — was found to be untracked/never-committed (a latent, not live, risk), documented in #5.

🤖 Generated with Claude Code

PI Platform added 4 commits June 1, 2026 12:54
ExecutionReceipt.compute_hash folded in resource_usage (cpu_ms, a wall-clock
float) and status_detail (which for TIMEOUT embeds 'Elapsed {ms}ms > max ...').
Receipts are chained + verify_chain recomputes the hash, so machine-speed jitter
changed the whole ledger chain. compute_hash now covers only logical identity
(status enum kept; usage/detail stay as stored metadata). + determinism tests.
…ock from hash)

AuditLogger.log folded the wall-clock audit_id (time.time()*1e6) and a
datetime.now() timestamp into the hashed payload, so replaying the same logical
sequence produced a different audit_hash chain — the 'immutable, replayable audit
ledger' was not reproducible. The chained hash now covers only the logical event
+ prev_hash; audit_id/timestamp remain stored columns. + reproducibility test.
execute() interpolated context values (incl. agent-controlled tool_input) into a
string run under subprocess.run(shell=True) with no escaping, so a value with
shell metacharacters (;, |, $(), backticks) executed arbitrary commands. Each
interpolated value is now shlex.quote-d (_interpolate gains quote_values=True),
so untrusted values become a single literal argument. + injection tests.
… panic)

PiSolidityCompilerBugsSentry did parse::<i64>().unwrap() on each version
component, so an oversized component (e.g. a 20-digit major in
'pragma solidity 99999999999999999999.8.13;') overflowed and panicked — a
Rust<->Python divergence (Python's int() is arbitrary-precision) and, pre-fix, a
DoS that only fell back via run_agent_safe. The components are only compared to
small buggy-release constants, so unparseable/oversized -> sentinel(-1) that can
never match, reproducing Python's 'not flagged' outcome without panicking.

Also refactor run_agent_safe to delegate to an agent-independent catch_panic()
helper, so the panic-safety test no longer depends on this (now-fixed) overflow.
@New1Direction
Copy link
Copy Markdown
Owner Author

Consolidated. All commits from this PR are now in rust-core-loadbearing (fast-forwarded through the full stack), together with the 17 previously-uncommitted source files the package needed to import. The integration branch now imports cleanly, the committed test suite passes, and ruff/mypy/parse gates are green on a fresh checkout. Closing as superseded — history is preserved on rust-core-loadbearing.

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