Reject nul bearing input in str_to_bytes32 - #294
Conversation
Closes #233. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Warning Review limit reachedNext included review available in 25 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
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 |
|
@coderabbitai assess this PR size classification for the totality of the PR with the following criterias and report it in your comment: S/M/L PR Classification Guidelines:This guide helps classify merged pull requests by effort and complexity rather than just line count. The goal is to assess the difficulty and scope of changes after they have been completed. Small (S)Characteristics:
Review Effort: Would have taken 5-10 minutes Examples:
Medium (M)Characteristics:
Review Effort: Would have taken 15-30 minutes Examples:
Large (L)Characteristics:
Review Effort: Would have taken 45+ minutes Examples:
Additional Factors to ConsiderWhen deciding between sizes, also consider:
Notes:
|
Closes #233.
The defect
str_to_bytes32right pads with0u8andbytes32_to_strends the string atthe first
0u8. The padding byte and the terminator are the same byte, so a nulin the input is not representable — but nothing rejected it:
The adjudication
#233 offers two: reject embedded nuls, or document the truncation as intended.
Taken as reject, because the alternative is not writable as a true document.
bytes32_to_strcannot change: right padded, nul terminatedbytes32is theon-chain word encoding these two functions exist to speak (
AuthoringMetaV2Solcarries
bytes32 wordand its assembly reader depends on that layout), and thedecode side has to read whatever the chain holds. So the only place the
ambiguity can be resolved is on the way in. Documenting it would have to say
that
str_to_bytes32returns abytes32that this crate's own decoder readsback as a different string — which is a defect description, not a contract.
Rejecting is a tightening only. Every input that already round tripped
unchanged is nul free and still accepted; the inputs that now error are exactly
the ones whose result the crate could not read back.
""still maps to the zeroword and still round trips to
"".Neither validated path narrows, either: words go through
REGEX_RAIN_SYMBOL(
^[a-z][0-9a-z-]*$) andabi_encode_validate, and no nul survives that. Theunvalidated
AuthoringMetaItem::abi_encodeis the path that could reach here,and it is the one that now errors instead of silently truncating.
What changed
str_to_bytes32errors on any0u8in the input, after the existing lengthcheck, with a new
Error::NulByteInInput("unexpected nul byte in input") —a fixed string variant alongside
BiggerThan32Bytes, the other rejection thissame function makes.
test_str_to_bytes32_rejects_nulcovers the nul in every position (alone,leading, embedded, trailing, and trailing in a full 32 byte input), including
the
"a"/"a\0"pair the issue collides.test_str_to_bytes32_round_trippins the property the issue is about:everything
str_to_bytes32accepts comes back out ofbytes32_to_strunchanged, and no two accepted inputs share a
bytes32.Out of scope
bytes32_to_strstill accepts abytes32carrying nonzero bytes after thefirst nul and truncates there. That direction is many to one by construction —
bytes32has more states than the strings it encodes — and it is the on-chaindata as written, which a decoder has to keep reading.
AuthoringMetaV2::abi_decodeopen codes the same truncation and is untouched.#233's two claims are both about
str_to_bytes32's domain and both close here.Relation to the other open PRs on this file
The five open PRs touching
crates/cli/src/meta/mod.rsare all inimpl Storeor its tests: #239 (
update, ~line 821), #254 (set_deployer, ~746), #256(dotrain removal, ~789-896), #241 (
search_deployer, ~680) and #286 (theDeserializevisitor, ~360). This PR touches the two free functions at ~910-930and adds tests at ~1290. No hunk overlaps, and
Storeis untouched here.crates/cli/src/error/mod.rsis shared with #285 (CorruptRecord,SubgraphError), #287 (AmbiguousSubject) and #288 (MetaNestingTooDeep),which all add variants too.
NulByteInInputis inserted afterBiggerThan32Bytesand itsDisplayarm after that variant's arm, above wherethose three insert, with unchanged lines between — so they append rather than
collide. Whichever lands second may still want a trivial rebase; the variants
themselves are independent.
#253 (#155) tightened
REGEX_RAIN_STRINGfrom^[\s!-~]*$to^[\t\n\x0B\x0C\r !-~]*$. It does not reach this code:REGEX_RAIN_STRINGguards
description, which is ABIstring, notbytes32. It does confirm thedirection, though — nul is outside both the old and the new class, so nothing
that was ever a valid Rain string or symbol is refused by this change.
QA
meta::tests::test_str_to_bytes32_rejects_nulandmeta::tests::test_str_to_bytes32_round_trip—rejects_nulfails on base,verified by mutation 1 below, which restores the base
str_to_bytes32bodybyte for byte;
round_tripfails on mutation 2.crates/cli/src/meta/mod.rsstr_to_bytes32,each run over
cargo test -p rain-metadata --lib -- str_to_bytes32 bytes32_to_str authoring(42 cases).passed / 1 failed, killed only by
test_str_to_bytes32_rejects_nul(
nul bearing input "\0" acceptedat mod.rs:1300). Every pre-existing testin that set survives it — that is the gap this PR closes.
if !bytes.contains(&0u8)) → 24 passed / 18 failed, killedby
test_str_to_bytes32,test_str_to_bytes32_round_trip, the authoringv1 encode/decode tests and the
get_authoring_metaquery tests, so therejection cannot degenerate into refusing valid words.
rather than from the implementation — right padding with the same byte the
decoder terminates on means the representable strings are exactly the nul free
ones. Expected values in
test_str_to_bytes32_round_tripare the inputstrings themselves, compared after a round trip; the collision check compares
the produced words to each other, not to anything recomputed by the code under
test.
loss on the round trip, and no collision between distinct inputs. Covered:
both hold for every accepted input, and the rejected set is characterised by
nul position rather than by one example. The issue's third sentence (document
instead) is answered in "The adjudication" above.
Not run locally: the full test suite. Sibling agents are building in this
tree's neighbourhood, so verification was scoped to the filters above plus
error::tests(12 passed) andcargo fmt --all -- --check(clean); the rest isleft to CI.
🤖 Generated with Claude Code