Skip to content

fix: remove proof-of-iron pickle fallback#5030

Open
galpetame wants to merge 1 commit into
Scottcjn:mainfrom
galpetame:codex-fix-proof-of-iron-pickle-rce-4885
Open

fix: remove proof-of-iron pickle fallback#5030
galpetame wants to merge 1 commit into
Scottcjn:mainfrom
galpetame:codex-fix-proof-of-iron-pickle-rce-4885

Conversation

@galpetame
Copy link
Copy Markdown

Fixes #4885.

Summary

  • Removes the live pickle.loads() fallback from Proof-of-Iron feature cache loading.
  • Keeps JSON feature-cache loading intact.
  • Rejects non-JSON legacy cache rows instead of deserializing them in the service path.
  • Updates the migration/security regression so a malicious pickle payload is not executed.

Validation

  • C:\Users\prian\.openclaw\workspace\crypto-revenue\.venv-rustchain-tests\Scripts\python.exe -m pytest test_pickle_to_json_migration.py -q -> 5 passed
  • C:\Users\prian\.openclaw\workspace\crypto-revenue\.venv-rustchain-tests\Scripts\python.exe -m pytest issue2307_boot_chime\tests\test_boot_chime.py -q -> 30 passed
  • python -m py_compile issue2307_boot_chime\src\proof_of_iron.py test_pickle_to_json_migration.py -> passed
  • git diff --check origin/main...HEAD -- issue2307_boot_chime\src\proof_of_iron.py test_pickle_to_json_migration.py -> passed
  • python tools\bcos_spdx_check.py --base-ref origin/main -> BCOS SPDX check: OK

No production service, live wallet, private key, or destructive request was used.

@github-actions github-actions Bot added BCOS-L1 Beacon Certified Open Source tier BCOS-L1 (required for non-doc PRs) tests Test suite changes size/M PR: 51-200 lines labels May 13, 2026
@508704820
Copy link
Copy Markdown
Contributor

Code Review: Remove proof-of-iron pickle fallback

Summary

Same fix as #4886 (our PR) — removes the pickle.loads() fallback in _load_features() that enables RCE via crafted feature cache data.

Comparison with #4886

Aspect #4886 (Xeophon) #5030 (galpetame)
Removes pickle Yes Yes
SafeUnpickler Yes (whitelist approach) No (complete removal)
Migration support SafeUnpickler for legacy data No migration — corrupt data = re-derive

Analysis

#5030 takes a more aggressive approach: completely removes pickle deserialization instead of using a SafeUnpickler whitelist. This is simpler and more secure — if the JSON decode fails, the features are re-derived from scratch rather than falling back to unsafe pickle.

The trade-off: existing deployments with pickle-cached features will need to re-derive on first run after upgrade. But this is a one-time cost, and re-derivation is safer than deserializing untrusted data.

Both PRs fix the same vulnerability. #5030 is simpler and more secure.

Review quality: Comparative review

Copy link
Copy Markdown
Contributor

@lavishsaluja lavishsaluja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved current head 4358b894258ec9d8b1d0733fc10b45ad38528df6.

This removes the unsafe live pickle.loads() fallback from the Proof-of-Iron feature-cache loader and keeps JSON cache loading intact. Invalid or non-JSON legacy rows now return None instead of being deserialized in the service path, and the regression uses a malicious pickle payload to prove the old fallback no longer executes.

Validation performed locally:

  • python -m pytest test_pickle_to_json_migration.py -q -> 5 passed
  • python -m pytest issue2307_boot_chime/tests/test_boot_chime.py -q -> 30 passed
  • python -m py_compile issue2307_boot_chime/src/proof_of_iron.py test_pickle_to_json_migration.py -> passed
  • git diff --check origin/main...HEAD -- issue2307_boot_chime/src/proof_of_iron.py test_pickle_to_json_migration.py -> passed
  • python tools/bcos_spdx_check.py --base-ref origin/main -> OK

No production service, live wallet, private key, or destructive testing was used.

Copy link
Copy Markdown
Contributor

@Asti1982 Asti1982 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved. I reviewed PR #5030 on head 4358b894258ec9d8b1d0733fc10b45ad38528df6 and the patch removes the unsafe live pickle.loads() fallback from Proof-of-Iron feature cache loading while keeping JSON cache reads intact.

What I checked:

  • _load_features() now tries JSON and returns None for non-JSON legacy rows instead of deserializing arbitrary pickle bytes in the service path.
  • The regression test stores a malicious pickle payload and verifies that _load_features('old_hash') rejects it without setting the exploit marker environment variable.
  • rg 'pickle\.loads|import pickle' issue2307_boot_chime\src\proof_of_iron.py test_pickle_to_json_migration.py only finds pickle usage in the test file, not the service module.

Validation performed:

  • python -m pytest test_pickle_to_json_migration.py -q -> 5 passed
  • python -m pytest issue2307_boot_chime\tests\test_boot_chime.py -q -> 30 passed
  • python -m py_compile issue2307_boot_chime\src\proof_of_iron.py test_pickle_to_json_migration.py -> passed
  • git diff --check origin/main...HEAD -- issue2307_boot_chime\src\proof_of_iron.py test_pickle_to_json_migration.py -> passed
  • python tools\bcos_spdx_check.py --base-ref origin/main -> BCOS SPDX check: OK
  • .venv-nomad-pr5030\Scripts\python -m ruff check issue2307_boot_chime\src\proof_of_iron.py test_pickle_to_json_migration.py --select E9,F821,F811,F841 -> passed
  • gh pr checks 5030 -> label, size-label, welcome passed

No blocking findings from this review.

Copy link
Copy Markdown
Contributor

@jaxint jaxint left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks for contributing. Approved.

Copy link
Copy Markdown
Contributor

@loganoe loganoe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved. _load_features() now treats non-JSON cache rows as invalid instead of deserializing pickle, which removes the live RCE surface while keeping JSON cache reads intact. The added regression uses a crafted pickle payload and verifies it is not executed.

Validation performed:

  • git diff --check origin/main...HEAD passed
  • python3 -m py_compile issue2307_boot_chime/src/proof_of_iron.py test_pickle_to_json_migration.py passed
  • python3 -m pytest test_pickle_to_json_migration.py issue2307_boot_chime/tests/test_boot_chime.py -q passed (35 passed, one existing SciPy/NumPy warning)

Copy link
Copy Markdown
Contributor

@jaxint jaxint left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks for contributing. Approved.

Copy link
Copy Markdown

@TJCurnutte TJCurnutte left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RustChain bounty review — APPROVED

PR: #5030 — fix: remove proof-of-iron pickle fallback
Scope: Reviewed issue2307_boot_chime/src/proof_of_iron.py, test_pickle_to_json_migration.py; diff covers regression test coverage, bugfix logic.

Local validation

  • git diff --check origin/main...HEAD — PASS
  • python3 tools/bcos_spdx_check.py --base-ref origin/main — PASS
  • python3 -B -m py_compile issue2307_boot_chime/src/proof_of_iron.py test_pickle_to_json_migration.py — PASS
  • python3 -B -m pytest test_pickle_to_json_migration.py -q — PASS
  • Added-line secret-pattern scan — PASS

I did not find a blocking correctness, security, or packaging issue in this focused diff. The changed files line up with the PR's stated security/bugfix scope, and the focused local gates above passed.

Boundary: this approval covers the reviewed PR diff and focused local checks; it is not a payout or production-deploy assertion.

Copy link
Copy Markdown
Contributor

@himanalot himanalot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the Proof-of-Iron pickle fallback removal.

The live _load_features() path now parses JSON only and returns None for legacy pickle/non-JSON cache rows instead of deserializing them. _save_features() remains JSON-based, and malformed cache rows fail closed through the existing None return path. The regression test inserts a malicious pickle payload and verifies _load_features() does not execute it, plus source checks that pickle.loads is no longer present in the load path.

I do not see a blocking issue in this PR.

@Scottcjn
Copy link
Copy Markdown
Owner

Wave-5 cluster pay sent (4 RTC/PR). Please rebase against current main to clear conflicts — multiple wave-5 PRs touch overlapping files.

Copy link
Copy Markdown
Contributor

@jaxint jaxint left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary

LGTM ✅ — Solid contribution to the RustChain ecosystem.

Code Quality

  • Implementation follows project conventions
  • Security considerations adequately addressed
  • Error handling appears robust

Testing

  • Test coverage is appropriate for the changes
  • Edge cases covered

Approved. Ready to merge. 🚀

Copy link
Copy Markdown
Contributor

@jaxint jaxint left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review

Approved

  • Code is correct
  • No obvious issues
  • Good contribution

Thanks! 🙏

Reviewed by jaxint

@Scottcjn
Copy link
Copy Markdown
Owner

Codex audit review (would award high-tier (50 RTC) once fixed):

Issue: Removes unsafe pickle fallback from Proof-of-Iron feature-cache loading; worth merging after rebase.

Address the specific point and ping for re-review.

Copy link
Copy Markdown
Contributor

@jaxint jaxint left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks for contributing. Approved.

Copy link
Copy Markdown

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed PR 5030. Standard review.

Copy link
Copy Markdown

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks for contributing!

Copy link
Copy Markdown

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Copy link
Copy Markdown
Contributor

@508704820 508704820 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove proof-of-iron pickle fallback. Good - removing insecure fallback eliminates the RCE vector. Verify no other pickle fallbacks exist in the codebase. - Xeophon (security review, pickle)

@508704820
Copy link
Copy Markdown
Contributor

Security Review ✅ CRITICAL FIX

Removes pickle.loads() fallback from Proof-of-Iron cache. Pickle deserialization of untrusted data is arbitrary code execution — a crafted cache row could execute any Python code on the node. Fix correctly rejects non-JSON legacy rows instead of deserializing them.

Reviewed by Xeophon - Solana: Lt9nERv6VHsojw15LpFeiaabuphAggzfLF9sM9UXRrZ

Copy link
Copy Markdown

@kekehanshujun kekehanshujun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewing this as part of the RustChain code review bounty.

This is the stricter fix for #4885: the live _load_features() path no longer deserializes legacy pickle rows at all. Invalid JSON/legacy cache rows now return None, which avoids RCE risk and pushes any legacy conversion into an offline migration path instead of the service request path.

The regression test is useful because it uses a pickle payload that would mutate process environment if executed, calls _load_features(), and verifies the payload did not run. It also asserts that pickle.loads is absent from the source loader. I do not see a blocking issue.

One maintenance note: this intentionally sacrifices automatic migration of plain legacy pickle dictionaries. That is the right default for a critical RCE class, but release notes should mention that old cache rows may need offline regeneration or migration.

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

Labels

BCOS-L1 Beacon Certified Open Source tier BCOS-L1 (required for non-doc PRs) size/M PR: 51-200 lines tests Test suite changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: pickle.loads() in proof_of_iron enables arbitrary code execution