diff --git a/scripts/agent/fuzz-dmDecryptor.mjs b/scripts/agent/fuzz-dmDecryptor.mjs index 6622e9fb..5fedb8d8 100644 --- a/scripts/agent/fuzz-dmDecryptor.mjs +++ b/scripts/agent/fuzz-dmDecryptor.mjs @@ -1,6 +1,6 @@ import { decryptDM } from '../../js/dmDecryptor.js'; -import { getRandomFuzzInput, randomHex } from './fuzz-utils.mjs'; +import { getRandomFuzzInput, randomHex, randomString } from './fuzz-utils.mjs'; import fs from 'fs'; import path from 'path'; @@ -47,17 +47,29 @@ function runFuzz(name, fn, inputGenerator, iterations = 1000) { } } -// Mock decryptor -const mockDecryptor = { - scheme: 'nip44_v2', - decrypt: async (pubkey, ciphertext) => { - // sometimes succeed, sometimes fail - if (Math.random() > 0.5) { - return JSON.stringify({ content: 'decrypted', pubkey: randomHex(), created_at: Date.now()/1000 }); - } else { - throw new Error('Decrypt failed'); +// Deterministic Mock decryptor factory based on input seed +const createMockDecryptor = (seedValue) => { + return { + scheme: 'nip44_v2', + decrypt: async (pubkey, ciphertext, options) => { + // Use seed to determine behavior deterministically + // seedValue comes from input. + const mode = (typeof seedValue === 'number' ? seedValue : 0) % 10; + + if (mode > 6) { + // Success + return JSON.stringify({ content: 'decrypted', pubkey: randomHex(), created_at: Date.now()/1000 }); + } else if (mode > 3) { + // Garbage JSON + return "{ bad json"; + } else if (mode > 1) { + // Not JSON + return "Just a string"; + } else { + throw new Error('Decrypt failed'); + } } - } + }; }; const throwingDecryptor = { @@ -69,27 +81,41 @@ const wrapper = async (input) => { // input is fuzzy junk. // We construct a call to decryptDM(event, context) - let event = input; - let context = { - actorPubkey: randomHex(), - decryptors: [mockDecryptor, throwingDecryptor] - }; + // Deterministic randomness from input if object + let seed = 0; + if (input && typeof input === 'object') { + seed = Object.keys(input).length; + } else if (typeof input === 'string') { + seed = input.length; + } else if (typeof input === 'number') { + seed = Math.floor(input); + } + let event = input; + + // Deterministically force supported kinds based on seed if (typeof input === 'object' && input !== null && !Array.isArray(input)) { - // If input is an object, treat it as the event structure, but potentially corrupted + const kinds = [4, 1059, input.kind]; event = { - kind: input.kind !== undefined ? input.kind : 4, + ...input, + kind: kinds[seed % kinds.length], content: input.content || 'ciphertext', pubkey: input.pubkey || randomHex(), tags: input.tags || [['p', randomHex()]], - created_at: input.created_at || Date.now()/1000, - ...input + created_at: input.created_at || Date.now()/1000 }; + } - // Also fuzz context sometimes - if (Math.random() > 0.8) { - context = input.context || getRandomFuzzInput(); - } + const mockDec = createMockDecryptor(seed); + + let context = { + actorPubkey: randomHex(), + decryptors: [mockDec, throwingDecryptor] + }; + + // Deterministically fuzz context + if ((seed % 10) > 8) { + context = input.context || getRandomFuzzInput(); } await decryptDM(event, context); diff --git a/scripts/agent/fuzz-magnetUtils.mjs b/scripts/agent/fuzz-magnetUtils.mjs index 99e31f84..a6332b97 100644 --- a/scripts/agent/fuzz-magnetUtils.mjs +++ b/scripts/agent/fuzz-magnetUtils.mjs @@ -64,6 +64,7 @@ const wrapper = (input) => { }; } + // Deterministic execution: input fully defines execution path. normalizeAndAugmentMagnet(rawValue, options); }; diff --git a/scripts/agent/fuzz-nostrEventSchemas.mjs b/scripts/agent/fuzz-nostrEventSchemas.mjs index 74d6dd29..16a2bf02 100644 --- a/scripts/agent/fuzz-nostrEventSchemas.mjs +++ b/scripts/agent/fuzz-nostrEventSchemas.mjs @@ -12,9 +12,10 @@ import { buildBlockListEvent, buildHashtagPreferenceEvent, buildAdminListEvent, - buildStorageChallengeEvent, sanitizeAdditionalTags, - getNostrEventSchema + getNostrEventSchema, + setNostrEventSchemaOverrides, + NOTE_TYPES } from '../../js/nostrEventSchemas.js'; import { getRandomFuzzInput, randomString, randomHex } from './fuzz-utils.mjs'; import fs from 'fs'; @@ -66,8 +67,6 @@ function runFuzz(name, fn, inputGenerator, iterations = 1000) { // Wrap functions that take multiple arguments or objects const wrappers = { buildVideoPostEvent: (input) => { - // Input is expected to be an object, but we fuzz it with anything. - // If it is an object, we fuzz its properties too. if (typeof input === 'object' && input !== null && !Array.isArray(input)) { buildVideoPostEvent({ pubkey: input.pubkey || getRandomFuzzInput(), @@ -205,22 +204,29 @@ const wrappers = { buildAdminListEvent(key, input); } }, - buildStorageChallengeEvent: (input) => { - if (typeof input === 'object' && input !== null) { - buildStorageChallengeEvent({ - ...input, - pubkey: input.pubkey || getRandomFuzzInput(), - created_at: input.created_at || getRandomFuzzInput() - }); - } else { - buildStorageChallengeEvent(input); - } - }, sanitizeAdditionalTags: (input) => { sanitizeAdditionalTags(input); }, getNostrEventSchema: (input) => { getNostrEventSchema(input); + }, + setNostrEventSchemaOverrides: (input) => { + try { + setNostrEventSchemaOverrides(input); + // Deterministically pick a type based on input properties if possible + const types = Object.values(NOTE_TYPES); + let index = 0; + if (typeof input === 'object' && input !== null) { + index = Object.keys(input).length % types.length; + } else if (typeof input === 'string') { + index = input.length % types.length; + } + const type = types[index]; + getNostrEventSchema(type); + } finally { + // Clean up global state to avoid pollution + setNostrEventSchemaOverrides({}); + } } };