mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 16:02:26 +00:00
- Added fuzzing scripts for nostrEventSchemas, dmDecryptor, and magnetUtils. - Fixed crashes in nostrEventSchemas related to unsafe stringification of content. - Fixed crash in magnetUtils when options argument is null. - Added safeStringify helper in nostrEventSchemas.js. - Included fuzzing reports and reproducers. Co-authored-by: PR0M3TH3AN <[email protected]>
249 lines
6.0 KiB
JavaScript
249 lines
6.0 KiB
JavaScript
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import {
|
|
buildVideoPostEvent,
|
|
buildVideoMirrorEvent,
|
|
buildRepostEvent,
|
|
buildShareEvent,
|
|
buildRelayListEvent,
|
|
buildDmRelayListEvent,
|
|
buildProfileMetadataEvent,
|
|
buildMuteListEvent,
|
|
buildDeletionEvent,
|
|
buildLegacyDirectMessageEvent,
|
|
buildDmAttachmentEvent,
|
|
buildDmReadReceiptEvent,
|
|
buildDmTypingIndicatorEvent,
|
|
buildViewEvent,
|
|
buildZapRequestEvent,
|
|
buildReactionEvent,
|
|
buildCommentEvent,
|
|
buildWatchHistoryEvent,
|
|
buildSubscriptionListEvent,
|
|
buildBlockListEvent,
|
|
buildHashtagPreferenceEvent,
|
|
sanitizeAdditionalTags,
|
|
} from '../../js/nostrEventSchemas.js';
|
|
|
|
// Setup artifacts and reproducers
|
|
const ARTIFACTS_DIR = 'artifacts';
|
|
const REPRODUCERS_DIR = 'examples/reproducers';
|
|
const REPORT_FILE = path.join(ARTIFACTS_DIR, 'fuzz-report-nostrEventSchemas.json');
|
|
|
|
if (!fs.existsSync(ARTIFACTS_DIR)) fs.mkdirSync(ARTIFACTS_DIR, { recursive: true });
|
|
if (!fs.existsSync(REPRODUCERS_DIR)) fs.mkdirSync(REPRODUCERS_DIR, { recursive: true });
|
|
|
|
const BIG_NAUGHTY_LIST = [
|
|
undefined,
|
|
null,
|
|
0,
|
|
1,
|
|
-1,
|
|
NaN,
|
|
Infinity,
|
|
-Infinity,
|
|
true,
|
|
false,
|
|
'',
|
|
' ',
|
|
'\n',
|
|
'\t',
|
|
'\r',
|
|
'\0',
|
|
'a',
|
|
'0',
|
|
'[]',
|
|
'{}',
|
|
'null',
|
|
'undefined',
|
|
'NaN',
|
|
'Infinity',
|
|
'<>',
|
|
'\'',
|
|
'"',
|
|
'`',
|
|
'\\',
|
|
'/',
|
|
'.',
|
|
',',
|
|
':',
|
|
';',
|
|
'!',
|
|
'?',
|
|
'@',
|
|
'#',
|
|
'$',
|
|
'%',
|
|
'^',
|
|
'&',
|
|
'*',
|
|
'(',
|
|
')',
|
|
'-',
|
|
'_',
|
|
'+',
|
|
'=',
|
|
'[',
|
|
']',
|
|
'{',
|
|
'}',
|
|
'|',
|
|
'~',
|
|
'💩', // Emoji
|
|
'Hello 🌍', // Text with emoji
|
|
'Zalgo text: Hͥ̽ͣ̃ͤ́eͧ̈́lͥlͦo', // Zalgo
|
|
'\uD800', // Lone surrogate
|
|
'\uDFFF',
|
|
'\uD83D\uDCA9', // Valid surrogate pair (Pile of Poo)
|
|
'\u0000', // Null byte
|
|
'\uFFFF',
|
|
'\uFFFD', // Replacement character
|
|
Array(1000).fill('a').join(''), // Long string
|
|
{ toString: () => { throw new Error('Explosion!'); } }, // Object that throws on toString
|
|
{ toJSON: () => { throw new Error('JSON Explosion!'); } }, // Object that throws on toJSON
|
|
Object.create(null), // Null prototype object
|
|
[],
|
|
{},
|
|
[null],
|
|
[undefined],
|
|
[''],
|
|
{ a: 1 },
|
|
];
|
|
|
|
// Add a recursive object
|
|
const recursiveObj = {};
|
|
recursiveObj.self = recursiveObj;
|
|
BIG_NAUGHTY_LIST.push(recursiveObj);
|
|
|
|
const report = {
|
|
target: 'nostrEventSchemas',
|
|
timestamp: new Date().toISOString(),
|
|
failures: [],
|
|
};
|
|
|
|
function logFailure(funcName, args, error) {
|
|
const hash = Math.random().toString(36).substring(2, 10);
|
|
const reproDir = path.join(REPRODUCERS_DIR, `issue-${Date.now()}-${hash}`);
|
|
fs.mkdirSync(reproDir, { recursive: true });
|
|
|
|
const reproScript = `
|
|
import { ${funcName} } from '../../../js/nostrEventSchemas.js';
|
|
|
|
const args = ${JSON.stringify(args, (key, value) => {
|
|
if (value === undefined) return '__UNDEFINED__';
|
|
if (Number.isNaN(value)) return '__NAN__';
|
|
if (value === Infinity) return '__INFINITY__';
|
|
if (value === -Infinity) return '__NEG_INFINITY__';
|
|
if (typeof value === 'object' && value !== null) {
|
|
// Handle circular references for JSON stringify
|
|
const seen = new WeakSet();
|
|
const circularReplacer = (k, v) => {
|
|
if (typeof v === 'object' && v !== null) {
|
|
if (seen.has(v)) {
|
|
return '[Circular]';
|
|
}
|
|
seen.add(v);
|
|
}
|
|
return v;
|
|
};
|
|
// Simple circular check handling for top level if needed, but here we just try-catch or simplify
|
|
try {
|
|
return value;
|
|
} catch(e) {
|
|
return '[Unserializable]';
|
|
}
|
|
}
|
|
return value;
|
|
}, 2).replace(/"__UNDEFINED__"/g, 'undefined').replace(/"__NAN__"/g, 'NaN').replace(/"__INFINITY__"/g, 'Infinity').replace(/"__NEG_INFINITY__"/g, '-Infinity')};
|
|
|
|
console.log('Running reproduction for ${funcName}...');
|
|
try {
|
|
${funcName}(...args);
|
|
console.log('No crash reproduced.');
|
|
} catch (error) {
|
|
console.log('Crash reproduced:');
|
|
console.error(error);
|
|
}
|
|
`;
|
|
|
|
fs.writeFileSync(path.join(reproDir, 'repro.mjs'), reproScript);
|
|
fs.writeFileSync(path.join(reproDir, 'README.md'), `To run:\n\n\`\`\`bash\nnode --import ../../../tests/test-helpers/setup-localstorage.mjs repro.mjs\n\`\`\`\n\nError: ${error.message}\nStack: ${error.stack}`);
|
|
|
|
report.failures.push({
|
|
function: funcName,
|
|
args: args.map(a => {
|
|
try {
|
|
return JSON.stringify(a);
|
|
} catch {
|
|
return '[Circular/Unserializable]';
|
|
}
|
|
}),
|
|
error: {
|
|
message: error.message,
|
|
stack: error.stack,
|
|
},
|
|
reproDir,
|
|
});
|
|
console.error(`FAILURE in ${funcName}: ${error.message}`);
|
|
}
|
|
|
|
function fuzzFunction(func, funcName) {
|
|
console.log(`Fuzzing ${funcName}...`);
|
|
|
|
// Test 1: Single argument fuzzing
|
|
for (const input of BIG_NAUGHTY_LIST) {
|
|
try {
|
|
func(input);
|
|
} catch (error) {
|
|
logFailure(funcName, [input], error);
|
|
}
|
|
}
|
|
|
|
// Test 2: Object property fuzzing (since most accept a params object)
|
|
const baseParams = {};
|
|
for (const input of BIG_NAUGHTY_LIST) {
|
|
// Try to inject the naughty value into common keys
|
|
const keys = ['pubkey', 'created_at', 'content', 'tags', 'additionalTags', 'metadata', 'relays'];
|
|
for (const key of keys) {
|
|
try {
|
|
func({ ...baseParams, [key]: input });
|
|
} catch (error) {
|
|
logFailure(funcName, [{ [key]: input }], error);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const targets = {
|
|
buildVideoPostEvent,
|
|
buildVideoMirrorEvent,
|
|
buildRepostEvent,
|
|
buildShareEvent,
|
|
buildRelayListEvent,
|
|
buildDmRelayListEvent,
|
|
buildProfileMetadataEvent,
|
|
buildMuteListEvent,
|
|
buildDeletionEvent,
|
|
buildLegacyDirectMessageEvent,
|
|
buildDmAttachmentEvent,
|
|
buildDmReadReceiptEvent,
|
|
buildDmTypingIndicatorEvent,
|
|
buildViewEvent,
|
|
buildZapRequestEvent,
|
|
buildReactionEvent,
|
|
buildCommentEvent,
|
|
buildWatchHistoryEvent,
|
|
buildSubscriptionListEvent,
|
|
buildBlockListEvent,
|
|
buildHashtagPreferenceEvent,
|
|
sanitizeAdditionalTags,
|
|
};
|
|
|
|
for (const [name, func] of Object.entries(targets)) {
|
|
fuzzFunction(func, name);
|
|
}
|
|
|
|
fs.writeFileSync(REPORT_FILE, JSON.stringify(report, null, 2));
|
|
console.log(`Fuzzing complete. Report saved to ${REPORT_FILE}`);
|