Reject non-string proof metadata keys#252
Conversation
tolga-tom-nook
left a comment
There was a problem hiding this comment.
No blockers found. I reviewed the proof metadata sanitization path in app/ledger/service.py and the new regression in tests/test_security.py.
Evidence:
- Confirmed
_clean_proof_metadata()now rejects non-string metadata keys before proof/ledger entry creation. - Confirmed the regression exercises
pay_bounty()with a mixed string/intverifier_resultand verifies no award count or account balance mutation afterLedgerError. - Ran
./.venv/bin/python -m pytest tests/test_security.py -q— 33 passed. - Ran
./.venv/bin/python -m ruff check app/ledger/service.py tests/test_security.py— passed. - Ran
./.venv/bin/python -m mypy app— passed.
weilixiong
left a comment
There was a problem hiding this comment.
Review ✅ APPROVED
QUALITY_GATE: LOW — 2f/14+, reject non-string proof metadata keys.
- Validates proof metadata keys are strings before storage
- Clean type checking + error handling
- Test coverage confirmed by author
Bounty: MW #219 (round 7, 40 MRWK)
AugmentSecurity
left a comment
There was a problem hiding this comment.
I found one remaining canonical-JSON validation gap before this is complete.
The PR rejects non-string verifier_result keys, but non-JSON-serializable values still pass _clean_proof_metadata() and fail later in canonical_json(clean_verifier_result) with a raw TypeError. That happens after the bounty award counter update has already run in the current session, so the failure is still not a controlled ledger validation error.
Repro on this PR head:
pay_bounty(..., verifier_result={"label": "mrwk:accepted", "raw": object()})
TypeError: Object of type object is not JSON serializable
awards_paid=1
alice_balance=0
Suggested fix: validate verifier_result for canonical JSON serializability before any payout mutation, or explicitly restrict metadata values to JSON primitives, lists, and dicts with string keys. The regression should assert a LedgerError and that both bounty.awards_paid and the recipient balance remain unchanged.
Evidence checked:
- Inspected
_clean_proof_metadata()and the latercanonical_json(clean_verifier_result)/ proof payload serialization path inapp/ledger/service.py. - Confirmed the new regression only covers non-string metadata keys.
- Direct-probed this PR head with an object-valued metadata entry and observed the raw
TypeErrorplus in-sessionawards_paid=1.
No secrets, wallet material, private deployment values, private vulnerability details, or MRWK price claims were used.
Thanhdn1984
left a comment
There was a problem hiding this comment.
Reviewed PR #252 on current head.
Summary: no content blockers found for rejecting non-string proof metadata keys. I inspected app/ledger/service.py and tests/test_security.py; _clean_proof_metadata() now rejects non-string keys before formatting the verifier_result.<key> error path, and the regression covers a mixed string/int-key metadata dict without paying the bounty or crediting the recipient.
Local validation:
uv run --python 3.12 --extra dev python -m pytest tests/test_security.py::test_bounty_payment_proof_rejects_non_string_metadata_keys -q-> 1 passeduv run --python 3.12 --extra dev ruff check app/ledger/service.py tests/test_security.py-> all checks passed
Caveat: GitHub currently reports no hosted checks for this branch via gh pr checks 252, so I only verified the focused local path and lint, not hosted CI.
No secrets, wallet material, private deployment values, private vulnerability details, payout credentials, or MRWK price claims were used.
9d00327 to
b2f5ff2
Compare
|
Updated this PR to address the review finding about non-JSON-serializable verifier metadata values. What changed:
Validation:
|
Kleenex-ultrasoft
left a comment
There was a problem hiding this comment.
No blockers found on the updated head. I specifically rechecked the earlier canonical-JSON gap from AugmentSecurity's review. The current pay_bounty() path now cleans verifier metadata, applies the accepted_by override, and then calls _ensure_json_serializable(clean_verifier_result, "verifier_result") before validate_public_url(), duplicate-submission lookup, award counter mutation, ledger entry creation, or proof creation. That keeps both non-string keys and object-valued metadata on controlled LedgerError paths before payout state can change.
Evidence checked:
- Inspected
app/ledger/service.pyaround_clean_proof_metadata(),_ensure_json_serializable(), and the start ofpay_bounty(). - Inspected
tests/test_security.pyregressions for non-string metadata keys, unserializable metadata values, and the existing control-character metadata case. /tmp/mergework-pr265-venv/bin/python -m pytest tests/test_security.py::test_bounty_payment_proof_rejects_non_string_metadata_keys tests/test_security.py::test_bounty_payment_proof_rejects_unserializable_metadata_values tests/test_security.py::test_bounty_payment_proof_rejects_control_character_metadata -q-> 3 passed./tmp/mergework-pr265-venv/bin/python -m pytest tests/test_security.py -q-> 34 passed./tmp/mergework-pr265-venv/bin/ruff check app/ledger/service.py tests/test_security.py-> passed./tmp/mergework-pr265-venv/bin/ruff format --check app/ledger/service.py tests/test_security.py-> 2 files already formatted./tmp/mergework-pr265-venv/bin/python -m mypy app/ledger/service.py-> success.git diff --check origin/main...HEAD-> clean.
Hosted checks are still unavailable for this branch: gh pr checks reports no checks on codex/reject-proof-metadata-keys.
Summary
LedgerErrorwithout changing payout stateWhy
pay_bounty()copiesverifier_resultand later serializes it through canonical JSON with sorted keys. If a service caller passes a metadata dict with non-string keys, serialization can raise a rawTypeErrorinstead of returning a controlled ledger validation error.Validation
.\.venv\Scripts\python.exe -m pytest -q(206 passed, 2 warnings).\.venv\Scripts\python.exe -m ruff check app\ledger\service.py tests\test_security.py.\.venv\Scripts\python.exe -m ruff format --check app\ledger\service.py tests\test_security.pyBounty #228