mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-06 12:52:26 +00:00
Add fuzz robustness tests for nostr schemas, dm decryptor, and magnet utils
- Created `scripts/agent/fuzz-utils.mjs` with shared random data generators. - Created `scripts/agent/fuzz-nostrEventSchemas.mjs` to fuzz event builders and validators, including `sanitizeAdditionalTags`. - Created `scripts/agent/fuzz-dmDecryptor.mjs` to fuzz DM decryption with mocked decryptors and custom reproducer generation. - Created `scripts/agent/fuzz-magnetUtils.mjs` to fuzz magnet URI normalization. - Verified that all fuzz targets run without crashes. - Saved execution reports to `artifacts/`.
This commit is contained in:
1 parent
f40b067d86
commit
f2d3650873
4 files changed
+477
-695
No files matched your search
+101
-68
@@ -1,94 +1,127 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import { randomBytes } from 'crypto';
|
||||
|
||||
export function randomString(length = 100) {
|
||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:,.<>?/`~';
|
||||
let result = '';
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||
}
|
||||
return result;
|
||||
export function fuzzBoolean() {
|
||||
return Math.random() < 0.5;
|
||||
}
|
||||
|
||||
export function randomUnicodeString(length = 100) {
|
||||
let result = '';
|
||||
for (let i = 0; i < length; i++) {
|
||||
// Random printable unicode range (roughly)
|
||||
result += String.fromCharCode(Math.floor(Math.random() * 0xFFFF));
|
||||
}
|
||||
return result;
|
||||
export function fuzzInt(min = 0, max = 100) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
export function randomSurrogateString(length = 100) {
|
||||
export function pickOne(options) {
|
||||
if (!Array.isArray(options) || options.length === 0) return undefined;
|
||||
return options[Math.floor(Math.random() * options.length)];
|
||||
}
|
||||
|
||||
export function fuzzString(length = 10) {
|
||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let result = '';
|
||||
for (let i = 0; i < length; i++) {
|
||||
// Generate random surrogates
|
||||
const code = Math.random() > 0.5
|
||||
? Math.floor(0xD800 + Math.random() * (0xDBFF - 0xD800 + 1))
|
||||
: Math.floor(0xDC00 + Math.random() * (0xDFFF - 0xDC00 + 1));
|
||||
result += String.fromCharCode(code);
|
||||
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function randomInt(max = 10000) {
|
||||
return Math.floor(Math.random() * max);
|
||||
export function fuzzHexString(length = 64) {
|
||||
const chars = '0123456789abcdef';
|
||||
let result = '';
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function randomBool() {
|
||||
return Math.random() > 0.5;
|
||||
export function fuzzSurrogatePairString(length = 10) {
|
||||
// Generate a string that might contain unpaired surrogates
|
||||
let result = '';
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (Math.random() < 0.1) {
|
||||
// High surrogate
|
||||
result += String.fromCharCode(fuzzInt(0xD800, 0xDBFF));
|
||||
} else if (Math.random() < 0.1) {
|
||||
// Low surrogate
|
||||
result += String.fromCharCode(fuzzInt(0xDC00, 0xDFFF));
|
||||
} else {
|
||||
result += fuzzString(1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function randomObject(depth = 2) {
|
||||
if (depth <= 0) return randomString(10);
|
||||
export function fuzzJSON(depth = 2) {
|
||||
if (depth <= 0) {
|
||||
const type = pickOne(['string', 'number', 'boolean', 'null']);
|
||||
switch (type) {
|
||||
case 'string': return fuzzString();
|
||||
case 'number': return fuzzInt();
|
||||
case 'boolean': return fuzzBoolean();
|
||||
case 'null': return null;
|
||||
}
|
||||
}
|
||||
|
||||
const type = pickOne(['object', 'array']);
|
||||
if (type === 'object') {
|
||||
const obj = {};
|
||||
const keys = randomInt(5) + 1;
|
||||
const keys = fuzzInt(1, 5);
|
||||
for (let i = 0; i < keys; i++) {
|
||||
obj[randomString(5)] = Math.random() > 0.5 ? randomString(10) : randomObject(depth - 1);
|
||||
obj[fuzzString(5)] = fuzzJSON(depth - 1);
|
||||
}
|
||||
return obj;
|
||||
} else {
|
||||
const arr = [];
|
||||
const len = fuzzInt(1, 5);
|
||||
for (let i = 0; i < len; i++) {
|
||||
arr.push(fuzzJSON(depth - 1));
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
|
||||
export function randomHex(length = 64) {
|
||||
return randomBytes(length / 2).toString('hex');
|
||||
export function saveFuzzReport(targetName, findings) {
|
||||
const reportDir = 'artifacts';
|
||||
if (!fs.existsSync(reportDir)) {
|
||||
fs.mkdirSync(reportDir, { recursive: true });
|
||||
}
|
||||
const reportPath = path.join(reportDir, `fuzz-report-${targetName}.json`);
|
||||
const report = {
|
||||
target: targetName,
|
||||
date: new Date().toISOString(),
|
||||
findings: findings
|
||||
};
|
||||
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
|
||||
console.log(`Fuzz report saved to ${reportPath}`);
|
||||
}
|
||||
|
||||
export function pickRandom(arr) {
|
||||
return arr[Math.floor(Math.random() * arr.length)];
|
||||
}
|
||||
export function saveReproducer(targetName, id, input, error) {
|
||||
const reproDir = path.join('examples', 'reproducers', `fuzz-${targetName}-${id}`);
|
||||
if (!fs.existsSync(reproDir)) {
|
||||
fs.mkdirSync(reproDir, { recursive: true });
|
||||
}
|
||||
|
||||
export function fuzzGenerators() {
|
||||
const generators = [
|
||||
() => null,
|
||||
() => undefined,
|
||||
() => "",
|
||||
() => " ",
|
||||
() => 0,
|
||||
() => 1,
|
||||
() => -1,
|
||||
() => NaN,
|
||||
() => Infinity,
|
||||
() => -Infinity,
|
||||
() => [],
|
||||
() => [null],
|
||||
() => {},
|
||||
() => randomString(1000),
|
||||
() => randomUnicodeString(100),
|
||||
() => randomSurrogateString(20),
|
||||
() => randomObject(),
|
||||
() => randomHex(64),
|
||||
() => "magnet:?xt=urn:btih:" + randomHex(40),
|
||||
() => "https://" + randomString(20) + ".com",
|
||||
() => {
|
||||
const obj = {};
|
||||
obj.self = obj;
|
||||
return obj; // Circular
|
||||
}
|
||||
];
|
||||
return generators;
|
||||
}
|
||||
const readmeContent = `# Fuzz Reproducer: ${targetName} - ${id}
|
||||
|
||||
export function getRandomFuzzInput() {
|
||||
const gens = fuzzGenerators();
|
||||
return pickRandom(gens)();
|
||||
## Error
|
||||
\`\`\`
|
||||
${error.stack || error.message}
|
||||
\`\`\`
|
||||
|
||||
## Input
|
||||
See \`input.json\`
|
||||
`;
|
||||
|
||||
fs.writeFileSync(path.join(reproDir, 'README.md'), readmeContent);
|
||||
fs.writeFileSync(path.join(reproDir, 'input.json'), JSON.stringify(input, null, 2));
|
||||
|
||||
// Create a minimal reproduction script
|
||||
const scriptContent = `
|
||||
import fs from 'fs';
|
||||
// You would need to adjust imports based on the target
|
||||
// This is a placeholder template
|
||||
const input = JSON.parse(fs.readFileSync('input.json', 'utf8'));
|
||||
console.log('Running reproduction with input:', input);
|
||||
`;
|
||||
fs.writeFileSync(path.join(reproDir, 'repro.mjs'), scriptContent);
|
||||
|
||||
console.log(`Reproducer saved to ${reproDir}`);
|
||||
}
|
||||
Reference in new issue
Block a user