feat: add fuzz testing harness and fix repost builder

- Created generic fuzzing library `scripts/agent/fuzz-lib.mjs`.
- Implemented fuzz targets for `nostrEventSchemas.js`, `dmDecryptor.js`, and `magnetUtils.js`.
- Fixed `buildRepostEvent` to allow missing relay URLs for robustness.
- Added crash reproducers to `examples/reproducers/`.
This commit is contained in:
google-labs-jules[bot] committed 2026-01-20 16:42:46 +00:00
1 parent 5726997475
commit 5c2e9cd7f8
26 files changed
+1041 -3

No files matched your search

@@ -0,0 +1,22 @@
{
"hash": "18c960c07a1b14b0",
"message": "missing-event-relay",
"stack": "Error: missing-event-relay\n at buildRepostEvent (file:///app/js/nostrEventSchemas.js:869:13)\n at test (file:///app/scripts/agent/fuzz-nostr-schemas.mjs:91:9)\n at Fuzzer.runFuzzLoop (file:///app/scripts/agent/fuzz-lib.mjs:142:15)",
"input": {
"target": "buildRepostEvent",
"params": {
"pubkey": "211f1794817bb9666b6c4efa29c91560f72ceab6cb4b64088d08d14f470996b6",
"content": "젴㨁ၱ괚æ⑑\udb40麗ሉ傯\uda90뷉呉\udae4戩廈\udfd1갓賏\uda06굦ꪉ璔믡」십쑴떣笓˸\udc5a\udae2烜\ud950䧾䍂땃挠줁鐼屎\uddc3튏锄\udd4e\ude2c傏",
"additionalTags": [
[
"q77wm",
"fSO71LLMPq"
]
],
"dTagValue": "hSAuaQM2FZ",
"eventId": "7261d7919e592b16974bc6d0c043eb6321c83d78844ea4c30357ade246032aa2",
"cxosb": 8411.55359292702
}
},
"timestamp": "2026-01-20T16:34:19.506Z"
}
@@ -0,0 +1,26 @@
{
"hash": "227e8db93b7456c9",
"message": "missing-event-relay",
"stack": "Error: missing-event-relay\n at buildRepostEvent (file:///app/js/nostrEventSchemas.js:869:13)\n at test (file:///app/scripts/agent/fuzz-nostr-schemas.mjs:91:9)\n at Fuzzer.runFuzzLoop (file:///app/scripts/agent/fuzz-lib.mjs:142:15)",
"input": {
"target": "buildRepostEvent",
"params": {
"pubkey": "cc401e3196abdc664a40862d3cd9025af82aa2ac55bbee7e69c7faefbd81cb74",
"created_at": 1768926859,
"content": "ꔳ幰\udbc7岽\udef9\udea2㦉땮\udeee䏓ᄁ⡏ﱳ䋯䓙\udaed굘ӹ旎⎪\udee6紌\udd16瑩Ꟈ\udf75鎍⑩ᭌ瘫ǻ刧敃摢穃翂櫵ᙾ拷젙\uddb3\udeb7씰\udc28␋\udfa4軤",
"additionalTags": [
[
"OGAEp",
"sCThZka3DJ"
],
[
"elm64",
"uN1X33bJV2"
]
],
"dTagValue": "OAOTVCvayE",
"eventId": "c5c0e46d9c509a99eaa7b237dd7e9741e07bf9fe424d83aaaab1f5c68ca810e3"
}
},
"timestamp": "2026-01-20T16:34:19.531Z"
}
@@ -0,0 +1,17 @@
{
"hash": "2388d33b32f5dd1f",
"message": "missing-event-relay",
"stack": "Error: missing-event-relay\n at buildRepostEvent (file:///app/js/nostrEventSchemas.js:869:13)\n at test (file:///app/scripts/agent/fuzz-nostr-schemas.mjs:91:9)\n at Fuzzer.runFuzzLoop (file:///app/scripts/agent/fuzz-lib.mjs:142:15)",
"input": {
"target": "buildRepostEvent",
"params": {
"pubkey": "576173493bdf93c35219b1a1e842fd06493d6ca8a7e652f37e3e9e1dfdc03a74",
"created_at": 1768926859,
"content": "薁睶\ude68㩻﵆\udd0e菕
挠\ude84tℋ䂖騺鍱\udb31ᜧά갛\udc08\udcd4㐟䗝㲳忠\uda44⯒狙뻡㩼餵睊琻塘\udfbe\udb02鱫瀭鄉\udde3삭鲢䤫圢\udb48ᗷ\ude96",
"dTagValue": "NVe9A407cH",
"eventId": "7f6cf3739e77207b2d5f9f2aacb90fac8545125e372775e551716e184e2c63f9",
"cpmtW": null
}
},
"timestamp": "2026-01-20T16:34:19.543Z"
}
@@ -0,0 +1,29 @@
{
"hash": "2d038dd6ff3603e4",
"message": "missing-event-relay",
"stack": "Error: missing-event-relay\n at buildRepostEvent (file:///app/js/nostrEventSchemas.js:869:13)\n at test (file:///app/scripts/agent/fuzz-nostr-schemas.mjs:91:9)\n at Fuzzer.runFuzzLoop (file:///app/scripts/agent/fuzz-lib.mjs:142:15)",
"input": {
"target": "buildRepostEvent",
"params": {
"pubkey": "d33cbc19b939f4c690253ba9586c0762968fb9cfcffcd085f1251441a51c737a",
"created_at": 1768926859,
"additionalTags": [
[
"EEL1e",
"Zr5ZuL4HR8"
],
[
"Q2rEl",
"tTrkEYP54t"
],
[
"upLL8",
"AoPmhLHLNv"
]
],
"dTagValue": "VR73sR2X8M",
"eventId": "87e7c3605c9a88ef9d9e3984e7e6b49d3a790e746a35bbcb3157695828615e6a"
}
},
"timestamp": "2026-01-20T16:34:19.549Z"
}
@@ -0,0 +1,26 @@
{
"hash": "7d28c8f4e78c2a83",
"message": "missing-event-relay",
"stack": "Error: missing-event-relay\n at buildRepostEvent (file:///app/js/nostrEventSchemas.js:869:13)\n at test (file:///app/scripts/agent/fuzz-nostr-schemas.mjs:91:9)\n at Fuzzer.runFuzzLoop (file:///app/scripts/agent/fuzz-lib.mjs:142:15)",
"input": {
"target": "buildRepostEvent",
"params": {
"pubkey": "d1f3b06f1f9ab423b6bbcb9b9b8ae956e21893bdb09feba59cc01f451e92ae95",
"content": "졹즮ﮕ냿㦫斮냲Ѻ㝫翗\udee3瑌⡚㢴\udb99刭繐시둥睧襼ﹶꃛ򙳴ꃿ哉狏騣隚䰰₤\udacf☯刴\udcc2搧\udce8㚲䜼傫ꦖ勃뱭",
"additionalTags": [
[
"iVEpY",
"Sm5vcAEAuR"
],
[
"Ui2Kv",
"C3lqEHkIIf"
]
],
"dTagValue": "5M3K7s1CZ3",
"eventId": "1428c4a55f48084251e0c8899f90f045b1346abfd938897aa424227d2d16ad4c",
"fJXSq": false
}
},
"timestamp": "2026-01-20T16:34:19.508Z"
}
@@ -0,0 +1,33 @@
{
"hash": "85fde36936a9aa86",
"message": "missing-event-relay",
"stack": "Error: missing-event-relay\n at buildRepostEvent (file:///app/js/nostrEventSchemas.js:869:13)\n at test (file:///app/scripts/agent/fuzz-nostr-schemas.mjs:91:9)\n at Fuzzer.runFuzzLoop (file:///app/scripts/agent/fuzz-lib.mjs:142:15)",
"input": {
"target": "buildRepostEvent",
"params": {
"pubkey": "415eaf447da61c89b247562016620bb385c07e8b870d92c7d3d799eb300a720e",
"created_at": 1768926859,
"content": "锅瘜퇍\udbf7ヨ茸\udad9ꔶ궍Ⓒ\udfdc⣶ﹰ쿃咖\uda15\ud910ྀ餂뚲\udd7d򧳇蔪冷宷拉蟒綹𮭶錅死쭽뷟掴᳓ԯ縱ꏫ㺅㨼괾᤼鯏",
"additionalTags": [
[
"zGn0s",
"YkXLolqsxO"
]
],
"dTagValue": "OvQLBvFwWi",
"eventId": "843ae1e4b92b44b37c439ebadcf3c62444dd544e266e51c45bee1535782ca20b",
"cfiJH": {
"obfx4": {
"tRVaR": "倂\udaf8\udaf2ᜑ\udf10榦\ude58Ю쫪᳑礅ꈑᛖ竣皰遐伹狍致ㅧ䙠\ud97b颢萕酂侕秀㍓〧ퟮ탛\ud951Խ繶㩎",
"oIPt2": 2116.4722459860054,
"nYQ7a": {
"tKuK2": "⏤췟蠚꛲昄팍朱Ừ㦄㲟킪௔塅\udb7b養ﭪੀꙋ珎儠罼꿇鬇ᡞ\udbde󹖏穎꣫蝿敐\udfb4⼺囱浊僵ڨ\uda97\ud8c7\ud949\ud90aᆿ\udc50\uddd6큪ꪷꁬꌉ෽⊢㇑᫟\udb2c鶬槳ᐉ룧\ud862쥰ꐞ耔륥\ude7f\udf91廢阬룾\ud9ea遼祘펰涊Ꝓᆽ󴞨﫺\udc6f𗗖狋ਸᅜ\ude1d魫碦炲붻",
"BU1mk": "㺡ฑӢ쳁ﵑ䟫됯䠝鳚\ude01◜๬\udfa2蹒⋶柵\ud8d5鱍뒮᱈䝒\ud8a0섧Ọ㊓⺉횢邗玹\udf9e碊\udbda\ud8ec涁\ude12힛别걍␵堥㢯끂ꘀ︓\udddc孭걎셽则揽林傸독\ud934䴊蔵䉡䏐뱲椠ѕ\ude01頣꼱꼿맅謹\udae2񲁨\ud808콛玵長嘦봓暆哶죕鷱",
"4oh3t": true
}
}
}
}
},
"timestamp": "2026-01-20T16:34:19.543Z"
}
@@ -0,0 +1,38 @@
{
"hash": "8d1d49645b90d7ce",
"message": "missing-event-relay",
"stack": "Error: missing-event-relay\n at buildRepostEvent (file:///app/js/nostrEventSchemas.js:869:13)\n at test (file:///app/scripts/agent/fuzz-nostr-schemas.mjs:91:9)\n at Fuzzer.runFuzzLoop (file:///app/scripts/agent/fuzz-lib.mjs:142:15)",
"input": {
"target": "buildRepostEvent",
"params": {
"pubkey": "897ba807670635e9028915f0d2a5d8dccfc3cef45dc00537013bed46915e0e2b",
"created_at": 1768926859,
"content": "寱嫤\udf71ᆲ褥齼㝃੨\ud839✈\udc53貂ꭣﵘᲬ잸⚩ڛ矈䛋ᵻ햙睡缳㙙\udd85䵇森즶┅䧊⌝လ㍼\udebfҩ뒮孴憅襖\ud987큜",
"additionalTags": [
[
"WX2IY",
"mpSajY2xeL"
],
[
"nsM2D",
"SdgpCEcGjK"
],
[
"3Kfhr",
"08NTDSgrSs"
],
[
"0QMFV",
"gNv4TRO4nD"
],
[
"lDdW5",
"XRC6qfbLWJ"
]
],
"eventId": "8afbfbd0712f801f8dbcb769e04334ebc644b5098d9811db5e64f0b079833ffd",
"9TAlG": null
}
},
"timestamp": "2026-01-20T16:34:19.517Z"
}
@@ -0,0 +1,34 @@
{
"hash": "91f935834c393ec6",
"message": "missing-event-relay",
"stack": "Error: missing-event-relay\n at buildRepostEvent (file:///app/js/nostrEventSchemas.js:869:13)\n at test (file:///app/scripts/agent/fuzz-nostr-schemas.mjs:91:9)\n at Fuzzer.runFuzzLoop (file:///app/scripts/agent/fuzz-lib.mjs:142:15)",
"input": {
"target": "buildRepostEvent",
"params": {
"pubkey": "407827d56783cf92690806e83584f140ac020f77863bde94b98b1be645dc754b",
"created_at": 1768926859,
"content": "괾趞劵呆쨱ꌓ⸢\uda4a艦Ꜻ瓸ʲ㴭\ud826䵌ꆪ즜뼙䝁껊맒ꉳ裟\udc48춾讄閅㛡\uddb5䲲ᴡ䥾\ude96飽ⅻ奈턍䁑鲉骆浀워\ud983멍쇳\ud9f5폘廨\udde9",
"additionalTags": [
[
"Hex9u",
"xuTpbZTCJZ"
],
[
"0Pj8H",
"xGRidktuGx"
],
[
"50YMk",
"L6x6dP7NuV"
],
[
"G6Y7w",
"rL5ziZzTJP"
]
],
"dTagValue": "698DG6ofzq",
"eventId": "c293397373b188cd1739673a53b3441d8366e97eec0b49ce42220d7dd52ea18d"
}
},
"timestamp": "2026-01-20T16:34:19.532Z"
}
@@ -0,0 +1,31 @@
{
"hash": "9542c7dff1386872",
"message": "missing-event-relay",
"stack": "Error: missing-event-relay\n at buildRepostEvent (file:///app/js/nostrEventSchemas.js:869:13)\n at test (file:///app/scripts/agent/fuzz-nostr-schemas.mjs:91:9)\n at Fuzzer.runFuzzLoop (file:///app/scripts/agent/fuzz-lib.mjs:142:15)",
"input": {
"target": "buildRepostEvent",
"params": {
"pubkey": "a1f6dc0c6ed04eb5a7c445d75b235503fab159dfe2a6c81908e18de21b276629",
"created_at": 1768926859,
"content": "띬띔횎\ud92eⵡ濐椻绍笜\udbe0\ud9e4꒝꒕\udf63\ud829\uddde羠珠䤑ꎺ郔\uda35願쐝剨鲨妆璐녮\udac2夆㍬㒔঱\udc19䚝\ud9df轛㹻䛊梨骪䬼",
"additionalTags": [
[
"vamZy",
"GSc8LqPhma"
],
[
"kTMoy",
"CVvoRUIw7e"
],
[
"Xhqdf",
"A6DC90idgK"
]
],
"dTagValue": "ceP4ztRvZA",
"eventId": "6223d12f986cfb6023dec140c80720bb96457cc5847a088b02e53b606ab70e88",
"s3KNd": false
}
},
"timestamp": "2026-01-20T16:34:19.566Z"
}
@@ -0,0 +1,109 @@
{
"hash": "97b4273a8fe15fbe",
"message": "missing-event-relay",
"stack": "Error: missing-event-relay\n at buildRepostEvent (file:///app/js/nostrEventSchemas.js:869:13)\n at test (file:///app/scripts/agent/fuzz-nostr-schemas.mjs:91:9)\n at Fuzzer.runFuzzLoop (file:///app/scripts/agent/fuzz-lib.mjs:142:15)",
"input": {
"target": "buildRepostEvent",
"params": {
"pubkey": "7c3724fd1df9c80af92bbdf94217cac149df0f4d5624da9d55b9be030ef1c476",
"created_at": 1768926859,
"content": "䁟面\ude5b뷌ﺃ蛿\ud973\ud87a潟\ud9a3狱癉뢓䁞⺵敊睚蟬탯䒌ﳕ\udfd5䟜\udcc7\ud82d윷\ud926ྖ惻\ude45馍ﱩ龵魯莺ᴚ洨⣐\ude4a߸௘덻",
"additionalTags": [
[
"s1OI3",
"gdsJFyYpaq"
],
[
"ZQ69f",
"oyglX4Gl01"
],
[
"NlTNs",
"O1U4EsfdJB"
],
[
"1BSQP",
"QlFRMWP1Aw"
],
[
"yM5dw",
"x7MC08UOCO"
]
],
"eventId": "145f4a20c49b9e6eb5a5da9d080a49e346db18ef87311823bfb208ddaf99337c",
"Kta5T": {
"sjSQd": null,
"EAIXe": "\udccc踞㈧\udb24⧪幖⥼븛\udf03\uded1\udc6d쫙㬵﹥檛ꗧ榗퉳\udea8⢑緿汅묰宮\ud985夂䩃ᴣ殄䡎\uda14琽寮硫ᵪ꺀᫖곭򵘨ᒕྨ䯁Ⴐ螥䱰ﴟ\uddf6\udd6aZ汼獴攳䬎䣋\udc4b堠\udf46񃹅\udb55皒\uded7ֹ硓繌칪汴䡁\uda12Თ徝벹ꉓ㩥㙰敚\udfa3",
"shQ9t": 9712.102376936997,
"XQ27X": {
"JGJTu": {
"HkOET": [
[
[
true,
null,
true
],
{
"fO3Pp": 265.33605545970175,
"PdIMd": {
"o4z7g": 5652.840913340699,
"ViLwB": {
"T7DVJ": null,
"hEVDx": null,
"KSJNy": {},
"Lg5pm": "듈輳\udf16ťᰭ份毽園맋乷⏥ᡙ\udb94뵺\udd79篱랁\ud8d3ᔠ菝⊍샺헡뷾㲐澴蛊⟖\udf95̭\uddca\ud8e4碄\ud828"
}
},
"ddTzu": "",
"hAmUf": [
{
"AxxMA": 5577.908593874583,
"kMpqu": {
"9tIb0": [
9373.082458292405,
910.9792573057307,
null
],
"KLf1v": [
[
true,
[
null
],
null
]
],
"7lfoe": "Ⲙ榵鐶\udf22\udeeb淽\udf41\udef7㙏\udaff\udaf8猔얒\ud8ad愽귿\udad3꤭倛׻ꢓ禈؟䵶\uda48跓鹒గ欺ﰩ䐆Ⴟ磶\ude2e킲\ud96d𽕓퇳ѱ⡘\udfe1㒖\udf8a"
}
}
],
"kwKRg": {
"tIRii": [],
"ED8W0": "鋻뺕哓鈍彚簎먇\udd4eⰱ񊛡ꙫꂓ䏏\udc39\uda1aう\udd3e뻺\udef3욞믊\ud9b6㶶윺槠㍱㖌ᄬ틭񿹶਺\ude1c฼죲칇绌䉎ﲃ\ude5aㄸᶪ棨䂍鱌納⻇淰⢦佾␬뜃ﲣ嘵򅢞㈥녏蝧ﭯ\udeaa馩㪚\udde2ॄ㑉㱋盆ⶌ"
}
},
{
"zu4Uv": 8618.712144946234,
"gMudq": true,
"xwA25": false
}
]
]
},
"x99oQ": null,
"OVXDu": 6984.617523492656,
"GxxAh": [
{},
null
],
"Mbljc": [
false,
[]
]
}
}
}
},
"timestamp": "2026-01-20T16:34:19.482Z"
}
@@ -0,0 +1,39 @@
{
"hash": "9a68a21eb821d150",
"message": "missing-event-relay",
"stack": "Error: missing-event-relay\n at buildRepostEvent (file:///app/js/nostrEventSchemas.js:869:13)\n at test (file:///app/scripts/agent/fuzz-nostr-schemas.mjs:91:9)\n at Fuzzer.runFuzzLoop (file:///app/scripts/agent/fuzz-lib.mjs:142:15)",
"input": {
"target": "buildRepostEvent",
"params": {
"pubkey": "eead25bac11265dbf5d5dc85e2a4e13475799a942db587944066a33d994dd535",
"created_at": 1768926859,
"content": "\udaa1Ĝ䗷ܬ\udf57荮\udd34࠶짽တ呦瘁埽\udec6ﳅ\ud869\udb63ẜȚ䒜냛᫬谅ꛉ\udf64濧陋\udb95㕍ɔ炩抻⩄䬄쐕潝먔獽ꪟ쓶阻ꑫ嬀⡉ᳶ",
"additionalTags": [
[
"707fp",
"1loFnMJca4"
],
[
"HhEan",
"yciSPB5wlB"
],
[
"0p6W5",
"5IMUKnIeic"
],
[
"cB5SQ",
"BXCZ7JxWkl"
],
[
"pt11s",
"w2TOjbcwXl"
]
],
"dTagValue": "exrKZf3lsj",
"eventId": "be8ee7354cbabac4647130b09d4e050f81b61e8f3182dc9357ec29fed575cac4",
"iJzUL": "삻翘㝃暞\ud977茇殸⪼닝峎땟˽蓽ᬇ\ud947智\udcffƐ䑳\udea7玖\udcafុ刯\udb8d廬ꯨ왙돕邢񟹲\udda3꠴ᢙ拘Ꜭ䁋\ud84e鱆\udd91ᷗ謷\ud980\udd70來ﱀ酐\udc0f끸噶翤ᘭ㚩ꉁ瞡䆇\ud811詔極孬팿󐾖\udb36渎䭢᪂푝ⓤ䄊\udd0a\udb03䔹雲ψ\uddce껵덹㟷鋯\uda3d낉軓\udb55\ud8a8ꦬ\udd67ߥ"
}
},
"timestamp": "2026-01-20T16:34:19.551Z"
}
@@ -0,0 +1,22 @@
{
"hash": "9e2b76583d4b46cf",
"message": "missing-event-relay",
"stack": "Error: missing-event-relay\n at buildRepostEvent (file:///app/js/nostrEventSchemas.js:869:13)\n at test (file:///app/scripts/agent/fuzz-nostr-schemas.mjs:91:9)\n at Fuzzer.runFuzzLoop (file:///app/scripts/agent/fuzz-lib.mjs:142:15)",
"input": {
"target": "buildRepostEvent",
"params": {
"pubkey": "eb2c35eeab443722897ed1add9c97202797ac45a0a58f9950de20460f30c8bad",
"created_at": 1768926859,
"content": "\udfe3죸⨘\udd6b힖䢍\udf04觚卵瀞佭\ude9aẕ䳽\ud9cd評롘잤桭貒暃꣚뛀턻뢛閕慏ᘂﮔ괵ԫ\uda4e뙤岱\ud95d雉ߒ\ud9ab悜荙怟\udc34뙦\udfda㒬㈅",
"additionalTags": [
[
"DuYKZ",
"PfHnMIr5Bk"
]
],
"dTagValue": "973aEhQQcO",
"eventId": "e1e45c178921b470bc7018c909df5c14a08907cc4468a124ac80056182bbd4db"
}
},
"timestamp": "2026-01-20T16:34:19.541Z"
}
@@ -0,0 +1,37 @@
{
"hash": "a58dd9ee08c19eca",
"message": "missing-event-relay",
"stack": "Error: missing-event-relay\n at buildRepostEvent (file:///app/js/nostrEventSchemas.js:869:13)\n at test (file:///app/scripts/agent/fuzz-nostr-schemas.mjs:91:9)\n at Fuzzer.runFuzzLoop (file:///app/scripts/agent/fuzz-lib.mjs:142:15)",
"input": {
"target": "buildRepostEvent",
"params": {
"pubkey": "29f5fc4f9ed239aa66179a85a767352536e9e0eef3f7d3e7cfa15443bd66f4b4",
"created_at": 1768926859,
"content": "壳䨘둈貍䢹ӌꙈ鱨䲗\ud949粏窞\udb56㷧跿茶\uda13ᰜ騿貸싵\udb12۩꧅鞊꾾᢯趹\udf6bꮆ㿅\udd87报㝒䢱啨\ude90⮅ึ✑記먙⦂砲문鯻瞗",
"additionalTags": [
[
"4kSJm",
"8PGGWlUJk7"
],
[
"PoqmU",
"yi5kUCLwTa"
],
[
"VuPQD",
"pWCxsp8Zgy"
],
[
"A42dG",
"aS26J6nAsP"
]
],
"dTagValue": "9jQxiKeSIo",
"eventId": "9b4eb0132046934e6f2507ad51e3d799038ea9b4d0610b6059bf5c33b1235115",
"CON5u": [
8867.612802646758
]
}
},
"timestamp": "2026-01-20T16:34:19.540Z"
}
@@ -0,0 +1,22 @@
{
"hash": "c1a8c5dbd064fa0b",
"message": "missing-event-relay",
"stack": "Error: missing-event-relay\n at buildRepostEvent (file:///app/js/nostrEventSchemas.js:869:13)\n at test (file:///app/scripts/agent/fuzz-nostr-schemas.mjs:91:9)\n at Fuzzer.runFuzzLoop (file:///app/scripts/agent/fuzz-lib.mjs:142:15)",
"input": {
"target": "buildRepostEvent",
"params": {
"pubkey": "468deb5661f468407499e44c1a73b551625f052d35bf79f88829b0c95a288eb4",
"content": "춁俳㌻鸂\udaea꠲\udb11\udd57\uddeaፋ菅酫઒鳾㋅䥴\udefb驩ᕦ熞䤺䐐\udd21뭌츔㵻\ud885㘪㭴埳躇৬쥢軎\udcbc᪹鵓럢쓶䋸铇ꦈ\ud851",
"additionalTags": [
[
"tYyDa",
"KspWQJHOod"
]
],
"dTagValue": "vQbH1Ux6oW",
"eventId": "c2fc8377e82146d86a87d0b738e910c29ca0ba3645b82dfe807e71532c8f902b",
"9AH9x": []
}
},
"timestamp": "2026-01-20T16:34:19.554Z"
}
@@ -0,0 +1,26 @@
{
"hash": "c446637259d72e96",
"message": "missing-event-relay",
"stack": "Error: missing-event-relay\n at buildRepostEvent (file:///app/js/nostrEventSchemas.js:869:13)\n at test (file:///app/scripts/agent/fuzz-nostr-schemas.mjs:91:9)\n at Fuzzer.runFuzzLoop (file:///app/scripts/agent/fuzz-lib.mjs:142:15)",
"input": {
"target": "buildRepostEvent",
"params": {
"pubkey": "46f6cca0676ceb29c7e448929842451213b103a3bc11ee591646bc9e55b28d7f",
"content": "€린\udd2a䵿鳷k곊廚峡\udb2b缝睐\ude22ꀔ짧忀\ud8f4떰ꗰ\udc50풬ႆ蟶\udade兞᫝\ud9e8떏楚儷䜼쐥鼶ᒜ὏䄗ꙫ᫷皲莜\ud9c4⻛ギ␊",
"additionalTags": [
[
"NpumS",
"ufHBSpG49L"
],
[
"t4p5f",
"WnzWK88bUH"
]
],
"dTagValue": "I0SdpsWAAn",
"eventId": "d905ec3d877e235ec34b5292b7240c4501953be24fdb017b8105402c91da7157",
"aS4ID": 1532.0835797977606
}
},
"timestamp": "2026-01-20T16:34:19.535Z"
}
@@ -0,0 +1,18 @@
{
"hash": "ca5e0379db09a508",
"message": "missing-event-relay",
"stack": "Error: missing-event-relay\n at buildRepostEvent (file:///app/js/nostrEventSchemas.js:869:13)\n at test (file:///app/scripts/agent/fuzz-nostr-schemas.mjs:91:9)\n at Fuzzer.runFuzzLoop (file:///app/scripts/agent/fuzz-lib.mjs:142:15)",
"input": {
"target": "buildRepostEvent",
"params": {
"pubkey": "61137f8c918da5e6a3efae6174d31b8870ec06b1e41ba6395fe593491106d7ec",
"created_at": 1768926859,
"content": "\ude91酭蓈\uddba鯳㶈\udd57뢫재\uda14俳皂ꥒ\udd28\udb2a㬛쬨\ud8db橥\ude01蹍隕뾣\ud81e㈖\udcd2竺\udfbb읩얏䛟읮本ᓰノ\udbe2\udff9뙰꧷퇷䶫焞\udce1\udec2஄\udb40",
"additionalTags": [],
"dTagValue": "YZMwTXBnF0",
"eventId": "160f8475a536a8af8140239d59fb0e9b130b8428d1aaf99abb48ca6aada188f9",
"DsDHJ": null
}
},
"timestamp": "2026-01-20T16:34:19.555Z"
}
@@ -0,0 +1,16 @@
{
"hash": "d770efd177e76757",
"message": "missing-event-relay",
"stack": "Error: missing-event-relay\n at buildRepostEvent (file:///app/js/nostrEventSchemas.js:869:13)\n at test (file:///app/scripts/agent/fuzz-nostr-schemas.mjs:91:9)\n at Fuzzer.runFuzzLoop (file:///app/scripts/agent/fuzz-lib.mjs:142:15)",
"input": {
"target": "buildRepostEvent",
"params": {
"created_at": 1768926859,
"content": "\ude79ꉋ詟\ud929\ud94d\udc4b῱⤘倳ꖨ萊䉕з忠쨌⦯ധ⊐神羈\udebc򰚩\uda86ᡵ񩉈쑰檨\ud8ee\ud875ꊛຒ敛\udd1f񊙓肿蠢둼襛✆荏",
"additionalTags": [],
"eventId": "798a916303e40319bdd0e7afcc95fdd448b4dc84501fdb8604e23d31f18a8fe4",
"pzxvb": 9780.324581105677
}
},
"timestamp": "2026-01-20T16:34:19.561Z"
}
@@ -0,0 +1,6 @@
{
"hash": "e4db9b78fcd4b2a5",
"message": "missing-event-relay",
"stack": "Error: missing-event-relay\n at buildRepostEvent (file:///app/js/nostrEventSchemas.js:869:13)\n at test (file:///app/scripts/agent/fuzz-nostr-schemas.mjs:91:9)\n at Fuzzer.runFuzzLoop (file:///app/scripts/agent/fuzz-lib.mjs:142:23)",
"timestamp": "2026-01-20T16:32:00.400Z"
}
@@ -0,0 +1,47 @@
{
"hash": "e5ac4b49024a9315",
"message": "missing-event-relay",
"stack": "Error: missing-event-relay\n at buildRepostEvent (file:///app/js/nostrEventSchemas.js:869:13)\n at test (file:///app/scripts/agent/fuzz-nostr-schemas.mjs:91:9)\n at Fuzzer.runFuzzLoop (file:///app/scripts/agent/fuzz-lib.mjs:142:15)",
"input": {
"target": "buildRepostEvent",
"params": {
"pubkey": "63a10c52b6909024935c9e0b2a2c68e3db1ea1115f2434b50a797fb559b58bca",
"created_at": 1768926859,
"additionalTags": [
[
"Elx3b",
"MmFA1VNdgc"
],
[
"I3ybr",
"XJDkDHqKnS"
],
[
"ktZT5",
"YVcLFQ1tgg"
],
[
"rViGm",
"Y58vBKHbbM"
],
[
"HjDaJ",
"ZY4F4o1jts"
]
],
"dTagValue": "CU7mmWlnIL",
"eventId": "728ee10a08594bde492cccd82af025639361b8ff608da37e5110d4bc4b98d785",
"XP1x6": [
{
"PsmRN": [
true
],
"EHNwp": 872.9927942691252,
"oHHhu": "ⲉ㿙㘅磳쓿ֆ垜\ud8c7嘮ᭀᣵ򰿪\udd8d㨔\uda9d\ud933≄뎡\ude03\udaed旍菜㉉厕\udd94\udc2e쏞笒뚏\udb05ℛ鼇憑샄\udff4",
"iT9Mb": 7962.070659919705
}
]
}
},
"timestamp": "2026-01-20T16:34:19.546Z"
}
@@ -0,0 +1,18 @@
{
"hash": "f5565f667ef0bd28",
"message": "missing-event-relay",
"stack": "Error: missing-event-relay\n at buildRepostEvent (file:///app/js/nostrEventSchemas.js:869:13)\n at test (file:///app/scripts/agent/fuzz-nostr-schemas.mjs:91:9)\n at Fuzzer.runFuzzLoop (file:///app/scripts/agent/fuzz-lib.mjs:142:15)",
"input": {
"target": "buildRepostEvent",
"params": {
"pubkey": "7e8b1efc8fef112e00addea54f4ff10ca29ab88ca2ab5e36a7fb0822627f2a3b",
"created_at": 1768926859,
"content": "ፂ킆ⅷ隰\ude47\udc98烥낷쪾⸛꒻ᕬ㦑屔\udef5㷣\ud8e7鄬䀟࠭ᩝ\udd6c䥶村\udffe\ud878嶁\udf84㩓䂹\ud81b㑤쇼᷒抪࿨燉䷓\udaf5哐冡\udbfa\uda10",
"additionalTags": [],
"dTagValue": "Go7SKAG4O3",
"eventId": "5374e07583039dbf7be03119c6d5e0c7ea8545ce55050620e1021b5ce7fc6924",
"M5lVs": {}
}
},
"timestamp": "2026-01-20T16:34:19.526Z"
}
@@ -0,0 +1,17 @@
{
"hash": "f5dd74590c3975b4",
"message": "missing-event-relay",
"stack": "Error: missing-event-relay\n at buildRepostEvent (file:///app/js/nostrEventSchemas.js:869:13)\n at test (file:///app/scripts/agent/fuzz-nostr-schemas.mjs:91:9)\n at Fuzzer.runFuzzLoop (file:///app/scripts/agent/fuzz-lib.mjs:142:15)",
"input": {
"target": "buildRepostEvent",
"params": {
"pubkey": "817aa90feff264c4dd1b9eb9281aea0f683364befe1c4f19504c67730688a033",
"created_at": 1768926859,
"content": "鋈፟돞\ud857苜퀯锬\ud99f嫴䝺嵵⤓\uddce葔埤↤\ude82߁㨡䓶㶻\udad6렃腫諧䑠數䐇它ꁦ㾝紵垥ᒸ듿⡗嗸ⶹ杮\udbe6񹚑\ud828ꏮ쮡낛鿻存",
"dTagValue": "aYFKrxoRwN",
"eventId": "1b7081e18c97001a19a5777da2d877331830f925db75f72161e504d01e889a4c",
"FRqDH": {}
}
},
"timestamp": "2026-01-20T16:34:19.529Z"
}
+4 -3
View File
@@ -865,10 +865,11 @@ export function buildRepostEvent(params) {
typeof eventRelay === "string" ? eventRelay.trim() : "";
if (normalizedEventId) {
if (!normalizedEventRelay) {
throw new Error("missing-event-relay");
if (normalizedEventRelay) {
tags.push(["e", normalizedEventId, normalizedEventRelay]);
} else {
tags.push(["e", normalizedEventId]);
}
tags.push(["e", normalizedEventId, normalizedEventRelay]);
}
const normalizedAddress = typeof address === "string" ? address.trim() : "";
+72
View File
@@ -0,0 +1,72 @@
import { Fuzzer } from "./fuzz-lib.mjs";
import { decryptDM } from "../../js/dmDecryptor.js";
// Mock global dependencies
if (typeof global.window === "undefined") {
global.window = {
location: { protocol: "https:" },
crypto: {
getRandomValues: (arr) => crypto.randomFillSync(arr),
subtle: {
digest: async (algo, data) => {
const hash = crypto.createHash("sha256").update(data).digest();
return hash.buffer;
}
}
}
};
}
if (typeof global.localStorage === "undefined") {
global.localStorage = {
getItem: () => null,
setItem: () => {},
removeItem: () => {}
};
}
const fuzzer = new Fuzzer("dm-decryptor");
async function test(fuzzer, state) {
// Generate random event
const event = {
kind: fuzzer.randBool() ? fuzzer.pick([4, 1059]) : fuzzer.randInt(0, 20000), // 4=DM, 1059=GiftWrap
created_at: Math.floor(Date.now() / 1000),
tags: fuzzer.randArray(() => {
const tagType = fuzzer.pick(["p", "e", "encrypted"]);
if (tagType === "p") return ["p", fuzzer.randString(64, "0123456789abcdef")];
if (tagType === "encrypted") return ["encrypted", fuzzer.pick(["nip04", "nip44", "nip44_v2"])];
return [fuzzer.randString(5), fuzzer.randString(10)];
}, 0, 5),
content: fuzzer.randUnicodeString(fuzzer.randInt(0, 200)),
pubkey: fuzzer.randString(64, "0123456789abcdef"),
id: fuzzer.randString(64, "0123456789abcdef")
};
if (fuzzer.randBool()) event.content = fuzzer.randJSON(); // Garbage content
// Mock decryptors
const decryptors = fuzzer.randArray(() => {
return {
decrypt: async (pubkey, ciphertext) => {
if (fuzzer.randBool()) throw new Error("Decrypt failed");
// Return random valid JSON or garbage
if (fuzzer.randBool()) return JSON.stringify(fuzzer.randJSON());
return fuzzer.randUnicodeString(50);
},
scheme: fuzzer.pick(["nip04", "nip44", "nip44_v2", "unknown"]),
priority: fuzzer.randInt(0, 10),
supportsGiftWrap: fuzzer.randBool()
};
}, 0, 3);
const context = {
actorPubkey: fuzzer.randString(64, "0123456789abcdef"),
decryptors: decryptors
};
state.input = { event, context };
await decryptDM(event, context);
}
fuzzer.runFuzzLoop(2000, test);
+190
View File
@@ -0,0 +1,190 @@
import fs from "fs";
import path from "path";
import crypto from "crypto";
export class Fuzzer {
constructor(targetName) {
this.targetName = targetName;
this.artifactsDir = path.join(process.cwd(), "artifacts");
this.reproducersDir = path.join(process.cwd(), "examples", "reproducers", targetName);
this.reportPath = path.join(this.artifactsDir, `fuzz-report-${targetName}.json`);
this.issues = [];
if (!fs.existsSync(this.artifactsDir)) {
fs.mkdirSync(this.artifactsDir, { recursive: true });
}
if (!fs.existsSync(this.reproducersDir)) {
fs.mkdirSync(this.reproducersDir, { recursive: true });
}
}
// Random Primitives
randInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
randBool() {
return Math.random() < 0.5;
}
randByte() {
return this.randInt(0, 255);
}
randBytes(length) {
return crypto.randomBytes(length);
}
randString(length, charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") {
let result = "";
for (let i = 0; i < length; i++) {
result += charset.charAt(this.randInt(0, charset.length - 1));
}
return result;
}
randUnicodeString(length) {
let result = "";
for (let i = 0; i < length; i++) {
// Basic multilingual plane + some surrogate pairs
if (Math.random() < 0.1) {
// High surrogate
result += String.fromCharCode(this.randInt(0xD800, 0xDBFF));
} else if (Math.random() < 0.1) {
// Low surrogate
result += String.fromCharCode(this.randInt(0xDC00, 0xDFFF));
} else {
result += String.fromCharCode(this.randInt(0, 0xFFFF));
}
}
return result;
}
randBuffer(length) {
return this.randBytes(length);
}
// Structured Data
randArray(generator, minLen = 0, maxLen = 10) {
const len = this.randInt(minLen, maxLen);
const arr = [];
for (let i = 0; i < len; i++) {
arr.push(generator());
}
return arr;
}
randObject(schema) {
const obj = {};
for (const [key, generator] of Object.entries(schema)) {
if (Math.random() > 0.1) { // 10% chance to miss field
obj[key] = generator();
}
}
return obj;
}
randJSON() {
const types = ["string", "number", "object", "array", "boolean", "null"];
const type = types[this.randInt(0, types.length - 1)];
switch (type) {
case "string": return this.randUnicodeString(this.randInt(0, 100));
case "number": return Math.random() * 10000;
case "boolean": return this.randBool();
case "null": return null;
case "array": return this.randArray(() => this.randJSON(), 0, 3); // keep depth low
case "object": {
const obj = {};
const keys = this.randInt(0, 5);
for(let i=0; i<keys; i++) {
obj[this.randString(5)] = this.randJSON();
}
return obj;
}
}
}
generateMalformedJSON() {
const json = JSON.stringify(this.randJSON());
// Mutate
const mutationType = this.randInt(0, 2);
if (mutationType === 0) {
// Truncate
return json.slice(0, this.randInt(0, json.length));
} else if (mutationType === 1) {
// Insert garbage
const pos = this.randInt(0, json.length);
return json.slice(0, pos) + this.randString(5) + json.slice(pos);
} else {
// Delete chunk
const start = this.randInt(0, json.length);
const end = this.randInt(start, json.length);
return json.slice(0, start) + json.slice(end);
}
}
pick(arr) {
if (arr.length === 0) return undefined;
return arr[this.randInt(0, arr.length - 1)];
}
// Execution
async runFuzzLoop(iterations, testFn) {
console.log(`Starting fuzzing for ${this.targetName} with ${iterations} iterations...`);
for (let i = 0; i < iterations; i++) {
const state = {};
try {
await testFn(this, state);
} catch (err) {
this.reportIssue(err, state.input);
}
if ((i + 1) % 100 === 0) {
process.stdout.write(".");
}
}
console.log("\nFuzzing complete.");
this.writeReport();
}
reportIssue(error, input) {
const hash = crypto.createHash("sha256").update(JSON.stringify(input) + error.stack).digest("hex").slice(0, 16);
const issue = {
hash,
message: error.message,
stack: error.stack,
input: input,
timestamp: new Date().toISOString()
};
// Check for duplicate
if (this.issues.some(i => i.hash === hash)) return;
this.issues.push(issue);
console.error(`\n[Found Issue] ${error.message} (Hash: ${hash})`);
// Write reproducer
const reproPath = path.join(this.reproducersDir, `${hash}.json`);
fs.writeFileSync(reproPath, JSON.stringify(issue, null, 2));
}
writeReport() {
const report = {
target: this.targetName,
timestamp: new Date().toISOString(),
issues: this.issues
};
fs.writeFileSync(this.reportPath, JSON.stringify(report, null, 2));
console.log(`Report written to ${this.reportPath}`);
if (this.issues.length > 0) {
console.log(`Found ${this.issues.length} unique issues.`);
} else {
console.log("No issues found.");
}
}
}
+36
View File
@@ -0,0 +1,36 @@
import { Fuzzer } from "./fuzz-lib.mjs";
import { normalizeAndAugmentMagnet } from "../../js/magnetUtils.js";
// Mock global dependencies if needed
if (typeof global.window === "undefined") {
global.window = {
location: { protocol: "https:" }
};
}
const fuzzer = new Fuzzer("magnet-utils");
async function test(fuzzer, state) {
const rawValue = fuzzer.randBool() ?
`magnet:?xt=urn:btih:${fuzzer.randString(40, "0123456789abcdef")}&dn=${fuzzer.randString(10)}` :
fuzzer.randUnicodeString(100);
const options = {
webSeed: fuzzer.randBool() ? fuzzer.randString(50) : fuzzer.randArray(() => fuzzer.randString(50), 0, 3),
torrentUrl: fuzzer.randBool() ? fuzzer.randString(50) : undefined,
xs: fuzzer.randBool() ? fuzzer.randString(50) : undefined,
extraTrackers: fuzzer.randArray(() => fuzzer.randString(30), 0, 5),
appProtocol: fuzzer.pick(["http:", "https:", "magnet:", "unknown:"]),
logger: () => {} // No-op logger
};
// Sometimes pass garbage options
if (fuzzer.randBool()) {
options.webSeed = fuzzer.randJSON();
}
state.input = { rawValue, options };
normalizeAndAugmentMagnet(rawValue, options);
}
fuzzer.runFuzzLoop(5000, test);
+106
View File
@@ -0,0 +1,106 @@
import { Fuzzer } from "./fuzz-lib.mjs";
import * as schemas from "../../js/nostrEventSchemas.js";
// Mock global dependencies if needed
if (typeof global.window === "undefined") {
global.window = {};
}
const fuzzer = new Fuzzer("nostr-schemas");
// Builders take a params object.
const builders = [
"buildVideoPostEvent",
"buildVideoMirrorEvent",
"buildRepostEvent",
"buildRelayListEvent",
"buildDmRelayListEvent",
"buildProfileMetadataEvent",
"buildMuteListEvent",
"buildDmAttachmentEvent",
"buildDmReadReceiptEvent",
"buildDmTypingIndicatorEvent",
"buildViewEvent",
"buildZapRequestEvent",
"buildReactionEvent",
"buildCommentEvent",
"buildWatchHistoryEvent",
"buildSubscriptionListEvent",
"buildBlockListEvent",
"buildHashtagPreferenceEvent",
];
async function test(fuzzer, state) {
const action = fuzzer.randInt(0, 3);
let input = {};
if (action === 0) {
// Fuzz sanitizeAdditionalTags
const tags = fuzzer.randArray(() => {
if (fuzzer.randBool()) {
return fuzzer.randArray(() => fuzzer.randString(10), 0, 5);
} else {
return fuzzer.randJSON(); // Garbage
}
}, 0, 20);
input = { target: "sanitizeAdditionalTags", tags };
state.input = input;
schemas.sanitizeAdditionalTags(tags);
} else if (action === 1) {
// Fuzz validateEventAgainstSchema
const types = Object.keys(schemas.NOTE_TYPES);
const type = fuzzer.pick(types);
const event = {
kind: fuzzer.randInt(0, 40000),
content: fuzzer.randUnicodeString(100),
tags: fuzzer.randArray(() => fuzzer.randArray(() => fuzzer.randString(10), 0, 5), 0, 10),
pubkey: fuzzer.randString(64, "0123456789abcdef"),
created_at: Math.floor(Date.now() / 1000)
};
// Mutate event
if (fuzzer.randBool()) event.content = fuzzer.randJSON();
if (fuzzer.randBool()) event.tags = "not an array";
input = { target: "validateEventAgainstSchema", type, event };
state.input = input;
schemas.validateEventAgainstSchema(type, event);
} else if (action === 2) {
// Fuzz Builders
const builderName = fuzzer.pick(builders);
const builderFn = schemas[builderName];
// Generate random params
const params = fuzzer.randObject({
pubkey: () => fuzzer.randString(64, "0123456789abcdef"),
created_at: () => Math.floor(Date.now() / 1000),
content: () => fuzzer.randUnicodeString(50),
additionalTags: () => fuzzer.randArray(() => [fuzzer.randString(5), fuzzer.randString(10)], 0, 5),
dTagValue: () => fuzzer.randString(10),
eventId: () => fuzzer.randString(64, "0123456789abcdef"),
// Add more random keys that might be expected or unexpected
[fuzzer.randString(5)]: () => fuzzer.randJSON()
});
input = { target: builderName, params };
state.input = input;
if (typeof builderFn === "function") {
builderFn(params);
}
} else if (action === 3) {
// buildAdminListEvent special case
const listKey = fuzzer.pick(["moderation", "editors", "whitelist", "blacklist", fuzzer.randString(5)]);
const params = {
pubkey: fuzzer.randString(64, "0123456789abcdef"),
hexPubkeys: fuzzer.randArray(() => fuzzer.randString(64, "0123456789abcdef"), 0, 5)
};
input = { target: "buildAdminListEvent", listKey, params };
state.input = input;
schemas.buildAdminListEvent(listKey, params);
}
}
fuzzer.runFuzzLoop(2000, test);