Skip to content

fix(cli): normalize hex ids used in generic tag filters - #5966

Open
Chessing234 wants to merge 4 commits into
block:mainfrom
Chessing234:fix/cli-tag-filter-hex-case
Open

fix(cli): normalize hex ids used in generic tag filters#5966
Chessing234 wants to merge 4 commits into
block:mainfrom
Chessing234:fix/cli-tag-filter-hex-case

Conversation

@Chessing234

Copy link
Copy Markdown
Contributor

Found by reading validate_hex64 against its own doc comment; no issue filed.

validate_hex64 is documented as validating a "64-character lowercase hex string", but is_ascii_hexdigit accepts either case and no caller normalizes. Paste an id in uppercase — from a block explorer, a log line, a colleague's message — and it flows through unchanged.

That is harmless almost everywhere, which is what makes the exceptions quiet:

  • ids and authors are parsed into typed EventId / PublicKey on the relay, so case there doesn't matter.
  • buzz-sdk's builders already lowercase (check_pubkey_hex, check_hex_exact) before writing a tag, so anything published through them is fine.
  • A NIP-01 generic tag filter is not. filter_match_one compares #e/#p values with == against the raw tag string, and every event in the tree writes lowercase hex. An uppercase id there matches nothing.

Two commands build those filters as raw JSON:

buzz messages thread --event <ID> — the #e filter finds no replies while the ids filter beside it still resolves the root. You get the root event and none of its thread, printed as if it were the whole thing. (Same shape as #5800, different cause.) The two filters are lifted into thread_filters so their shape is pinned by a test rather than only by the network.

buzz issues assign — reads the issue's prior assignment events over #e before deciding what to publish. With an uppercase id it reads an empty history and builds its note from that, reporting an assignment against a state it never saw. The ids filter beside it still resolves the issue, which is again what keeps it quiet.

Related: I opened #5963 earlier aiming this at moderation.rs and closed it myself — the SDK already normalizes there, so it changed nothing. This is the version that bites.

Scope. Other validate_hex64 call sites still pass unnormalized values, but most reach typed fields or SDK builders. The remaining raw-JSON tag filters (social.rs, patches.rs, pr.rs) each need their own look at whether the value lands in #e/#a or only in authors; I didn't want to sweep 14 files blind.

Verified locally on the pinned 1.95.0 toolchain:

  • cargo test -p buzz-cli355 passed, 0 failed (349 before, plus the 6 new)
  • cargo clippy --workspace --all-targets -- -D warnings — clean
  • cargo fmt --all -- --check — clean

Note: I'm an outside contributor, so the workflow runs here sit at action_required until a maintainer approves them; only the DCO check reports on its own.

`validate_hex64` is documented as validating a "64-character lowercase hex
string" but accepts either case — `is_ascii_hexdigit` does — and every caller
passes the string through unchanged.

That is harmless where the value reaches a typed field: `ids` and `authors`
are parsed into `EventId`/`PublicKey` on the relay, and `buzz-sdk`'s builders
already lowercase before they write a tag. It is not harmless in a NIP-01
generic tag filter, which is compared as a string.

Add `parse_hex64`, which validates and returns the value lowercased, and
correct the older function's doc to say what it does and when each is right.

No call sites yet.

Signed-off-by: Taksh <takshkothari09@gmail.com>
`buzz messages thread --event <ID>` validated the hex and used it verbatim in
two filters. `ids` is parsed into a typed `EventId` on the relay, so case
there is harmless — but `#e` is a NIP-01 generic tag filter, matched by
string comparison against a tag every event writes lowercase.

Paste an id in uppercase and the command returns the root event and none of
its replies: a partial thread, printed as if it were the whole thing.

Lowercase the id once, at the boundary, and lift the two filters into
`thread_filters` so the shape is pinned by a test instead of only by the
network.

Signed-off-by: Taksh <takshkothari09@gmail.com>
`buzz issues assign` looks up the issue's prior assignment events with an
`#e` filter before deciding what to publish. Same string-compared tag filter
as the thread query: an uppercase issue id finds no prior assignments, so the
command builds its note from an empty history — reporting an assignment
against a state it never actually read.

The `ids` filter beside it is typed and would still resolve the issue itself,
which is what makes the failure quiet.

Signed-off-by: Taksh <takshkothari09@gmail.com>

@themiguelamador themiguelamador left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The new normalization boundary is correct, but the PR leaves five user-facing raw tag filters with the same silent uppercase failure:

  • reactions get and reactions remove still place the validated-but-unnormalized event ID into #e.
  • patches list, pr list, and issues list still interpolate the validated-but-unnormalized repository owner into the string-compared 30617:<owner>:<repo> value used by #a.

An uppercase ID therefore still yields empty reaction results/removal failures, and an uppercase repository owner yields empty NIP-34 lists. Since this PR introduces the normalization helper specifically for generic filters, those remaining call sites should be covered in the same boundary fix.

I prepared a signed fix that normalizes every remaining user-supplied hex component in CLI #e/#a filters and adds shared regression coverage: https://github.com/Complear/buzz/commit/4fcc6659d

Verification on the fix:

  • cargo test -p buzz-cli: 357 passed
  • cargo clippy -p buzz-cli --all-targets -- -D warnings: clean
  • cargo fmt --all -- --check: clean
  • exhaustive CLI #e/#p/#a filter scan completed
  • git diff whitespace check passes

Review feedback: `reactions get` and `reactions remove` build the same kind:7
`#e` filter from the caller's id without normalizing it. `#e` is a generic tag
filter compared as a raw string, so an uppercase id silently returns nothing —
`get` prints an empty reaction list and `remove` reports the reaction as
missing, for an id every other command accepts.

Both query shapes now go through one `reactions_filter` builder fed by
`parse_hex64`, with tests pinning the normalized `#e` value and the optional
author narrowing.

Signed-off-by: Taksh <takshkothari09@gmail.com>
@Chessing234

Copy link
Copy Markdown
Contributor Author

pushed 754842dreactions get and reactions remove now build the same kind:7 filter through one reactions_filter helper fed by parse_hex64, with tests on the normalized #e and on the author narrowing.

on the three #a sites: #5970 is branched off this PR and is exactly that change — repo_coord_a_value applied to patches list, pr list and issues list. i rebased it onto this tip rather than duplicating the commit here, so the two together are the complete boundary fix. happy to squash them into this one if you'd rather review it as a single PR.

verified: cargo test -p buzz-cli 357 passed, cargo clippy -p buzz-cli --all-targets -- -D warnings clean, cargo fmt --all -- --check clean.

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.

2 participants