commit 909e28338d405d3de8cbfc988a82b743c3f077c8
parent 3c12fd47100c9748d45208b64e443ae42f69eb99
Author: William Casarin <jb55@jb55.com>
Date: Thu, 1 Aug 2024 13:37:11 -0700
fix note content parsing bug with damus.io urls
Changelog-Fixed: Fixed bug where non-bech32 damus io urls would cause corruption
Signed-off-by: William Casarin <jb55@jb55.com>
Diffstat:
M | src/nostr_bech32.c | | | 33 | ++++++++++++++++++++++++--------- |
M | test.c | | | 95 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- |
2 files changed, 117 insertions(+), 11 deletions(-)
diff --git a/src/nostr_bech32.c b/src/nostr_bech32.c
@@ -52,25 +52,25 @@ int parse_nostr_bech32_type(const char *prefix, enum nostr_bech32_type *type) {
// Parse type
if (strncmp(prefix, "note", 4) == 0) {
*type = NOSTR_BECH32_NOTE;
- return 1;
+ return 4;
} else if (strncmp(prefix, "npub", 4) == 0) {
*type = NOSTR_BECH32_NPUB;
- return 1;
+ return 4;
} else if (strncmp(prefix, "nsec", 4) == 0) {
*type = NOSTR_BECH32_NSEC;
- return 1;
+ return 4;
} else if (strncmp(prefix, "nprofile", 8) == 0) {
*type = NOSTR_BECH32_NPROFILE;
- return 1;
+ return 8;
} else if (strncmp(prefix, "nevent", 6) == 0) {
*type = NOSTR_BECH32_NEVENT;
- return 1;
+ return 6;
} else if (strncmp(prefix, "nrelay", 6) == 0) {
*type = NOSTR_BECH32_NRELAY;
- return 1;
+ return 6;
} else if (strncmp(prefix, "naddr", 5) == 0) {
*type = NOSTR_BECH32_NADDR;
- return 1;
+ return 5;
}
return 0;
@@ -254,11 +254,26 @@ int parse_nostr_bech32_buffer(struct cursor *cur,
int parse_nostr_bech32_str(struct cursor *bech32, enum nostr_bech32_type *type) {
- if (!parse_nostr_bech32_type((const char *)bech32->p, type))
+ unsigned char *start = bech32->p;
+ unsigned char *data_start;
+ int n;
+
+ if (!(n = parse_nostr_bech32_type((const char *)bech32->p, type))) {
+ bech32->p = start;
return 0;
+ }
- if (!consume_until_non_alphanumeric(bech32, 1))
+ data_start = start + n;
+ if (!consume_until_non_alphanumeric(bech32, 1)) {
+ bech32->p = start;
+ return 0;
+ }
+
+ // must be at least 60 chars for the data part
+ if (bech32->p - data_start < 60) {
+ bech32->p = start;
return 0;
+ }
return 1;
}
diff --git a/test.c b/test.c
@@ -866,6 +866,34 @@ static void test_parse_nevent() {
assert(ok == 3);
}
+static void test_single_url_parsing() {
+ unsigned char buf[4096];
+ const char *content = "https://damus.io/notedeck";
+
+ struct ndb_blocks *blocks;
+ struct ndb_block *block;
+ struct ndb_str_block *str;
+ struct ndb_block_iterator iterator, *iter;
+
+ iter = &iterator;
+ assert(ndb_parse_content(buf, sizeof(buf), content, strlen(content), &blocks));
+ assert(blocks->num_blocks == 1);
+
+ ndb_blocks_iterate_start(content, blocks, iter);
+ int i = 0;
+ while ((block = ndb_blocks_iterate_next(iter))) {
+ str = ndb_block_str(block);
+ switch (++i) {
+ case 1:
+ assert(ndb_get_block_type(block) == BLOCK_URL);
+ assert(!strncmp(str->str, "https://damus.io/notedeck", str->len));
+ break;
+ }
+ }
+
+ assert(i == 1);
+}
+
static void test_comma_url_parsing() {
unsigned char buf[4096];
const char *content = "http://example.com,http://example.com";
@@ -884,7 +912,7 @@ static void test_comma_url_parsing() {
while ((block = ndb_blocks_iterate_next(iter))) {
str = ndb_block_str(block);
switch (++i) {
- case 1:
+ case 1:
assert(ndb_get_block_type(block) == BLOCK_URL);
assert(!strncmp(str->str, "http://example.com", str->len));
break;
@@ -922,7 +950,7 @@ static void test_url_parsing() {
while ((block = ndb_blocks_iterate_next(iter))) {
str = ndb_block_str(block);
switch (++i) {
- case 1:
+ case 1:
assert(ndb_get_block_type(block) == BLOCK_URL);
assert(!strncmp(str->str, DAMUSIO, str->len));
break;
@@ -1417,7 +1445,70 @@ static void test_subscriptions()
ndb_destroy(ndb);
}
+static void test_weird_note_corruption() {
+ struct ndb *ndb;
+ struct ndb_config config;
+ struct ndb_blocks *blocks;
+ struct ndb_block *block;
+ struct ndb_str_block *str;
+ struct ndb_block_iterator iterator, *iter = &iterator;
+ struct ndb_filter filter, *f = &filter;
+ uint64_t subid;
+ uint64_t note_id = 0;
+ struct ndb_txn txn;
+ struct ndb_note *note;
+ ndb_default_config(&config);
+
+ const char *ev = "[\"EVENT\",\"a\",{\"content\":\"https://damus.io/notedeck\",\"created_at\":1722537589,\"id\":\"1876ca8cd29afba5805e698cf04ac6611d50e5e5a22e1efb895816a4c5790a1b\",\"kind\":1,\"pubkey\":\"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245\",\"sig\":\"2478aac6e9e66e5a04938d54544928589b55d2a324a7229ef7e709903b5dd12dc9a279abf81ed5753692cd30f62213fd3e0adf8b835543616a60e2d7010f0627\",\"tags\":[[\"e\",\"423fdf3f6e438fded84fe496643008eada5c1db7ba80428521c2c098f1173b83\",\"\",\"root\"],[\"p\",\"406a61077fe67f0eda4be931572c522f937952ddb024c87673e3de6b37e9a98f\"]]}]";
+ assert(ndb_init(&ndb, test_dir, &config));
+
+ assert(ndb_filter_init(f));
+ assert(ndb_filter_start_field(f, NDB_FILTER_KINDS));
+ assert(ndb_filter_add_int_element(f, 1));
+ ndb_filter_end_field(f);
+ ndb_filter_end(f);
+
+ assert((subid = ndb_subscribe(ndb, f, 1)));
+ assert(ndb_process_event(ndb, ev, strlen(ev)));
+
+ assert(ndb_wait_for_notes(ndb, subid, ¬e_id, 1) == 1);
+ assert(note_id > 0);
+ assert(ndb_begin_query(ndb, &txn));
+
+ assert((note = ndb_get_note_by_key(&txn, note_id, NULL)));
+ assert(!strcmp(ndb_note_content(note), "https://damus.io/notedeck"));
+ assert(ndb_note_content_length(note) == 25);
+
+ assert(ndb_num_subscriptions(ndb) == 1);
+ assert(ndb_unsubscribe(ndb, subid));
+ assert(ndb_num_subscriptions(ndb) == 0);
+
+ blocks = ndb_get_blocks_by_key(ndb, &txn, note_id);
+
+ ndb_blocks_iterate_start(ndb_note_content(note), blocks, iter);
+ //printf("url num blocks: %d\n", blocks->num_blocks);
+ assert(blocks->num_blocks == 1);
+ int i = 0;
+ while ((block = ndb_blocks_iterate_next(iter))) {
+ str = ndb_block_str(block);
+ //printf("block (%d): %d:'%.*s'\n", ndb_get_block_type(block), str->len, str->len, str->str);
+ switch (++i) {
+ case 1:
+ assert(ndb_get_block_type(block) == BLOCK_URL);
+ assert(str->len == 25);
+ assert(!strncmp(str->str, "https://damus.io/notedeck", str->len));
+ break;
+ }
+ }
+ assert(i == 1);
+
+ ndb_end_query(&txn);
+ ndb_destroy(ndb);
+}
+
int main(int argc, const char *argv[]) {
+ test_single_url_parsing();
+ test_weird_note_corruption();
test_query();
test_tag_query();
test_parse_content();