mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 16:02:26 +00:00
The hex-filter sanitizer covered ids/authors/#e/#p/#q but not the NIP-22 uppercase root-scope tags #E (root event id) and #P (root author pubkey). Relays hex-decode those too, and the kind-1111 comment subscriptions query by them, so an odd-length value bypassed the sanitizer and got the whole REQ rejected with "uneven size input to from_hex" (dropping that subscription's results). Add #E/#P to the sanitized fields. Co-Authored-By: Claude Opus 4.8 <[email protected]>
82 lines
3.1 KiB
JavaScript
82 lines
3.1 KiB
JavaScript
// Sanitizing hex-only filter fields (ids/authors/#e/#p/#q) before they reach a
|
|
// relay. One odd-length/non-hex value otherwise makes strict relays reject the
|
|
// whole REQ ("uneven size input to from_hex"), silently dropping results.
|
|
|
|
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
import { sanitizeHexFilterFields } from "../js/nostr/toolkit.js";
|
|
|
|
const HEX_A = "a".repeat(64);
|
|
const HEX_B = "b".repeat(64);
|
|
const ODD = "abc"; // odd length -> from_hex failure
|
|
const SHORT = "a".repeat(63); // wrong length
|
|
|
|
test("strips invalid hex from authors but keeps valid entries", () => {
|
|
const out = sanitizeHexFilterFields({ kinds: [0], authors: [HEX_A, ODD, HEX_B] });
|
|
assert.deepEqual(out.authors, [HEX_A, HEX_B]);
|
|
assert.deepEqual(out.kinds, [0], "non-hex fields untouched");
|
|
});
|
|
|
|
test("lowercases and trims valid hex", () => {
|
|
const out = sanitizeHexFilterFields({ ids: [` ${HEX_A.toUpperCase()} `] });
|
|
assert.deepEqual(out.ids, [HEX_A]);
|
|
});
|
|
|
|
test("drops the whole filter when a populated hex field becomes empty", () => {
|
|
// If every author was invalid, removing the key would broaden the query to
|
|
// "all authors" — drop the filter instead.
|
|
const out = sanitizeHexFilterFields({ kinds: [1], authors: [ODD, SHORT] });
|
|
assert.equal(out, null);
|
|
});
|
|
|
|
test("sanitizes event/pubkey tag filters (#e/#p/#q)", () => {
|
|
const out = sanitizeHexFilterFields({
|
|
kinds: [1984],
|
|
"#e": [HEX_A, ODD],
|
|
"#p": [HEX_B],
|
|
});
|
|
assert.deepEqual(out["#e"], [HEX_A]);
|
|
assert.deepEqual(out["#p"], [HEX_B]);
|
|
});
|
|
|
|
test("leaves non-hex tag fields (#a/#d/#t) alone", () => {
|
|
const filter = {
|
|
kinds: [21, 22],
|
|
"#a": ["30078:" + HEX_A + ":some-d-tag"],
|
|
"#d": ["subscriptions"],
|
|
"#t": ["music"],
|
|
};
|
|
const out = sanitizeHexFilterFields(filter);
|
|
assert.deepEqual(out["#a"], filter["#a"]);
|
|
assert.deepEqual(out["#d"], filter["#d"]);
|
|
assert.deepEqual(out["#t"], filter["#t"]);
|
|
});
|
|
|
|
test("returns the same object when nothing needs sanitizing (no needless copy)", () => {
|
|
const filter = { kinds: [0], authors: [HEX_A] };
|
|
const out = sanitizeHexFilterFields(filter);
|
|
assert.equal(out, filter);
|
|
});
|
|
|
|
test("ignores empty/absent hex fields", () => {
|
|
const out = sanitizeHexFilterFields({ kinds: [1], authors: [] });
|
|
assert.deepEqual(out, { kinds: [1], authors: [] });
|
|
});
|
|
|
|
test("sanitizes NIP-22 uppercase root tags #E/#P (kind-1111 comments)", () => {
|
|
// Relays hex-decode #E (root event id) and #P (root author pubkey) too. An
|
|
// odd-length value there previously bypassed the sanitizer and got the whole
|
|
// comment REQ rejected ("uneven size input to from_hex").
|
|
const out = sanitizeHexFilterFields({
|
|
kinds: [1111],
|
|
"#E": [HEX_A, ODD],
|
|
"#P": [HEX_B],
|
|
"#K": ["30078"], // a non-hex tag must be left untouched
|
|
"#A": ["30078:" + HEX_A + ":dtag"], // address tag is not hex-decoded
|
|
});
|
|
assert.deepEqual(out["#E"], [HEX_A], "drops the odd-length root event id");
|
|
assert.deepEqual(out["#P"], [HEX_B]);
|
|
assert.deepEqual(out["#K"], ["30078"], "#K (kind) untouched");
|
|
assert.deepEqual(out["#A"], ["30078:" + HEX_A + ":dtag"], "#A (address) untouched");
|
|
});
|