mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-05 04:12:25 +00:00
feat(forms): validate submitter npub on submission forms (#22)
The embed submission forms accepted any free-text identifier and published it, so they were an easy spam vector. Add a shared, testable NIP-19 validator and require a well-formed npub before a submission is sent. - js/utils/validateNpub.js: validateNpubHex() decodes via nostr-tools nip19 and returns the 64-char hex pubkey, or null for anything that isn't a real npub (free text, wrong prefix, bad checksum, wrong NIP-19 entity). Also exposed as window.validateNpubHex for the classic iframe form scripts. - Application form (required npub): submit is blocked unless valid; the decoded hex is written into the application body for canonical storage/matching. - Feature-request / bug-fix / feedback (optional npub): stays optional, but a non-empty value must be a valid npub or submit is blocked with an inline error. - Tests: tests/validate-npub.test.mjs (real nostr-tools, no implementation mirror). Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
fdcf034464
commit
e4e5c552c7
7 files changed
+203
-5
No files matched your search
@@ -0,0 +1,56 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import * as nostrTools from "nostr-tools";
|
||||
|
||||
import { validateNpubHex } from "../js/utils/validateNpub.js";
|
||||
|
||||
// Use the real nostr-tools nip19 so we exercise actual bech32 decoding, not a
|
||||
// mirror of the implementation. Generate a real key pair for the happy path.
|
||||
const nip19 = nostrTools.nip19;
|
||||
const secret = nostrTools.generateSecretKey();
|
||||
const hex = nostrTools.getPublicKey(secret);
|
||||
const npub = nip19.npubEncode(hex);
|
||||
|
||||
test("accepts a well-formed npub and returns its lowercase hex pubkey", () => {
|
||||
const result = validateNpubHex(npub, { nip19 });
|
||||
assert.equal(result, hex);
|
||||
assert.match(result, /^[0-9a-f]{64}$/);
|
||||
});
|
||||
|
||||
test("tolerates surrounding whitespace", () => {
|
||||
assert.equal(validateNpubHex(` ${npub}\n`, { nip19 }), hex);
|
||||
});
|
||||
|
||||
test("rejects free-text spam that is not an npub", () => {
|
||||
for (const junk of [
|
||||
"",
|
||||
" ",
|
||||
"hello world",
|
||||
"[email protected]",
|
||||
"not-an-npub",
|
||||
npub.replace("npub1", "note1"), // wrong NIP-19 prefix
|
||||
]) {
|
||||
assert.equal(validateNpubHex(junk, { nip19 }), null, `should reject: ${junk}`);
|
||||
}
|
||||
});
|
||||
|
||||
test("rejects an npub with a corrupted checksum", () => {
|
||||
// Flip the last character — the bech32 checksum no longer validates.
|
||||
const tampered = npub.slice(0, -1) + (npub.endsWith("q") ? "p" : "q");
|
||||
assert.equal(validateNpubHex(tampered, { nip19 }), null);
|
||||
});
|
||||
|
||||
test("rejects a wrong NIP-19 entity (nprofile) even though it decodes", () => {
|
||||
const nprofile = nip19.nprofileEncode({ pubkey: hex, relays: [] });
|
||||
assert.equal(validateNpubHex(nprofile, { nip19 }), null);
|
||||
});
|
||||
|
||||
test("rejects non-string input", () => {
|
||||
for (const value of [null, undefined, 42, {}, [], true]) {
|
||||
assert.equal(validateNpubHex(value, { nip19 }), null);
|
||||
}
|
||||
});
|
||||
|
||||
test("returns null (instead of throwing) when no decoder is available", () => {
|
||||
assert.equal(validateNpubHex(npub, { nip19: null }), null);
|
||||
});
|
||||
Reference in new issue
Block a user