fix(receipts): reject empty-quote receipts instead of verifying them#513
fix(receipts): reject empty-quote receipts instead of verifying them#513davion-knight wants to merge 2 commits into
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
Walkthrough
ChangesReceipt verification
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/vouch/receipts.py`:
- Line 61: Update the empty-quote guard in verify_evidence to use the same
falsy-quote handling as verify_receipt, so an empty evidence.quote with a
missing source returns the no-receipt result instead of forged. Preserve the
existing behavior for non-empty quotes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: aa88a531-b829-4529-b62f-3c4326e2f63a
📒 Files selected for processing (3)
CHANGELOG.mdsrc/vouch/receipts.pytests/test_receipts.py
verify_evidence carries its own copy of the empty-span guard ahead of the source read (a missing source must never mask "nothing to compare" as forged). it had the same quote is None gap verify_receipt did: quote="" paired with a missing source fell through to FORGED instead of NO_RECEIPT. same fix, one call site up. per coderabbit review on vouchdev#513.
|
fixed — verify_evidence carried its own copy of the same guard ahead of the source-read (so a missing source can't mask "nothing to compare" as forged), and had the identical quote is None gap. same fix applied there: quote="" paired with a missing source now correctly returns NO_RECEIPT instead of FORGED. added test_verify_evidence_no_receipt_for_empty_quote_and_missing_source to cover it. |
|
heads up — the |
verify_evidence carries its own copy of the empty-span guard ahead of the source read (a missing source must never mask "nothing to compare" as forged). it had the same quote is None gap verify_receipt did: quote="" paired with a missing source fell through to FORGED instead of NO_RECEIPT. same fix, one call site up. per coderabbit review on vouchdev#513.
f8b52b6 to
79ff4c4
Compare
verify_evidence carries its own copy of the empty-span guard ahead of the source read (a missing source must never mask "nothing to compare" as forged). it had the same quote is None gap verify_receipt did: quote="" paired with a missing source fell through to FORGED instead of NO_RECEIPT. same fix, one call site up. per coderabbit review on vouchdev#513.
79ff4c4 to
0ab34bd
Compare
verify_receipt()'s guard only checked `quote is None`, not an empty
string. an Evidence with quote="" and byte_start == byte_end decodes
to "", trivially equals the empty quote, and returns VERIFIED --
despite carrying no actual quoted text. this contradicts the
function's own docstring ("no quote to compare" -> NO_RECEIPT) and the
sibling locate_span(), which already refuses to mint a receipt for an
empty quote on the propose path. the verify path had no equivalent
guard, so an empty-quote evidence landing on disk some other way
(bundle import, sync) would mechanically "verify" and clear the
auto-approval receipt gate.
`not quote` catches both None and "" with no other call sites needing
changes, since verify_evidence and evaluate_claim_receipts both
delegate to verify_receipt.
verify_evidence carries its own copy of the empty-span guard ahead of the source read (a missing source must never mask "nothing to compare" as forged). it had the same quote is None gap verify_receipt did: quote="" paired with a missing source fell through to FORGED instead of NO_RECEIPT. same fix, one call site up. per coderabbit review on vouchdev#513.
0ab34bd to
c96546d
Compare
summary
verify_receipt()'s null-guard only checkedquote is None, not an empty string. anEvidencewithquote=""andbyte_start == byte_end(a zero-length span) decodes to"", trivially equals the empty quote, and returnsVERIFIED— despite carrying no actual quoted text.this contradicts the function's own docstring: "Returns
NO_RECEIPTwhen the evidence carries no verifiable span (either offset missing, or no quote to compare)." the siblinglocate_span()already refuses to mint a receipt for an empty quote (if not needle: return None, with its own testtest_locate_span_returns_none_for_empty_quote) — butverify_receipthad no equivalent guard on the verify side.why it matters
Evidence.quotehas no min-length constraint at the model level. the normal propose path can't trigger this today (proposals.pyalways builds evidence viareceipt_for_quote, which delegates tolocate_span's guard) — butbundle.py'simport_applyandsync.py'ssync_applywriteevidence/*.yamlstraight to disk after only pydantic-schema validation, no receipt-content check. a hand-crafted bundle/sync source can land an empty-quote, zero-length-span evidence, and any claim citing it would mechanically clear the auto-approval receipt gate (evaluate_claim_receipts) despite proving nothing about the source.what changed
one line in
src/vouch/receipts.py::verify_receipt:quote is None→not quote, so bothNoneand""hit the sameNO_RECEIPTpath. no other call site needs a change —verify_evidenceandevaluate_claim_receiptsboth delegate toverify_receipt.test plan
new test
test_no_receipt_when_quote_is_empty_stringintests/test_receipts.py— assertsverify_receiptreturnsNO_RECEIPTforquote=""/byte_start == byte_end. fails on the old code (VERIFIED), passes with the fix.all clean. the full suite has 5 pre-existing order-dependent failures (
test_fsck_clean_kb_prints_clean_and_exits_zero,test_search_fts5_backend_label,test_deindex_removes_fts_and_prov, and 2 parametrized cases intest_covered_methods_attach_sidebar_when_kb_has_recent_claims) that reproduce identically on a cleantestcheckout with no changes — confirmed viagit stash+ rerun, unrelated to this PR.Summary by CodeRabbit
Bug Fixes
quote="") with a zero-length byte span as missing, preventing receipts from being marked as verified.Tests