feat(agent): add fuzz testing scripts for nostr schemas, DM decryptor, and magnet utils

This commit is contained in:
google-labs-jules[bot] committed 2026-01-26 16:53:30 +00:00
1 parent f2344a25d6
commit a1cb6377a6
5 files changed
+365 -503

No files matched your search

+149 -95
View File
@@ -5,133 +5,187 @@ import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const REPRODUCERS_DIR = path.resolve(__dirname, "../../examples/reproducers");
const ARTIFACTS_DIR = path.resolve(__dirname, "../../artifacts");
const REPRODUCER_DIR = path.join(process.cwd(), "examples/reproducers");
const ARTIFACTS_DIR = path.join(process.cwd(), "artifacts");
// Ensure directories exist
if (!fs.existsSync(REPRODUCERS_DIR)) {
fs.mkdirSync(REPRODUCERS_DIR, { recursive: true });
if (!fs.existsSync(REPRODUCER_DIR)) {
fs.mkdirSync(REPRODUCER_DIR, { recursive: true });
}
if (!fs.existsSync(ARTIFACTS_DIR)) {
fs.mkdirSync(ARTIFACTS_DIR, { recursive: true });
}
export function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
export const rng = {
bool: () => Math.random() > 0.5,
int: (min = 0, max = 100) => Math.floor(Math.random() * (max - min + 1)) + min,
float: (min = 0, max = 100) => Math.random() * (max - min) + min,
oneOf: (arr) => arr[Math.floor(Math.random() * arr.length)],
export function randomString(length) {
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 randomHex(length) {
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 randomBoolean() {
return Math.random() < 0.5;
}
export function randomItem(array) {
return array[Math.floor(Math.random() * array.length)];
}
export function randomJSON(depth = 3, maxFields = 5) {
if (depth <= 0) {
const type = randomInt(0, 3);
if (type === 0) return randomString(10);
if (type === 1) return randomInt(-1000, 1000);
if (type === 2) return randomBoolean();
return null;
}
const type = randomInt(0, 5);
if (type === 0) {
// Array
const arr = [];
const len = randomInt(0, maxFields);
string: (maxLength = 100) => {
const len = Math.floor(Math.random() * maxLength);
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let result = "";
for (let i = 0; i < len; i++) {
arr.push(randomJSON(depth - 1, maxFields));
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return arr;
} else if (type === 1) {
// Object
const obj = {};
const len = randomInt(0, maxFields);
return result;
},
nastyString: () => {
const candidates = [
"",
"null",
"undefined",
"NaN",
"\x00",
"\n",
"\r",
"\t",
" ",
" ",
"https://example.com",
"wss://relay.example.com",
"<script>alert(1)</script>",
"' OR 1=1 --",
"javascript:void(0)",
"data:text/plain;base64,SGVsbG8=",
"😀",
"ñ",
"\uD83D\uDCA9", // Pile of poo
"\uD800", // Lone surrogate (high)
"\uDFFF", // Lone surrogate (low)
"A".repeat(1000), // Long string
"A".repeat(10000), // Very long string
JSON.stringify({ foo: "bar" }),
"{}",
"[]"
];
return candidates[Math.floor(Math.random() * candidates.length)];
},
mixedString: (maxLength = 100) => {
if (Math.random() < 0.3) return rng.nastyString();
return rng.string(maxLength);
},
array: (generator, maxLen = 10) => {
const len = rng.int(0, maxLen);
const result = [];
for (let i = 0; i < len; i++) {
obj[randomString(5)] = randomJSON(depth - 1, maxFields);
result.push(generator());
}
return result;
},
object: (keyGen, valGen, maxKeys = 10) => {
const len = rng.int(0, maxKeys);
const result = {};
for (let i = 0; i < len; i++) {
result[keyGen()] = valGen();
}
return result;
},
recursiveObject: (depth = 3) => {
if (depth <= 0) return rng.mixedString();
const type = rng.int(0, 4);
if (type === 0) return rng.int(-1000, 1000);
if (type === 1) return rng.mixedString();
if (type === 2) return rng.bool();
if (type === 3) return null;
if (type === 4) {
// Array
const len = rng.int(0, 5);
const arr = [];
for(let i=0; i<len; i++) arr.push(rng.recursiveObject(depth - 1));
return arr;
}
// Object
const len = rng.int(0, 5);
const obj = {};
for(let i=0; i<len; i++) {
obj[rng.string(10)] = rng.recursiveObject(depth - 1);
}
return obj;
} else {
// Scalar
return randomJSON(0);
}
}
};
export function saveReproducer(target, input, error) {
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
const filename = `crash-${target}-${timestamp}.json`;
const filepath = path.join(REPRODUCERS_DIR, filename);
export function saveReproducer(targetName, input, error) {
const timestamp = Date.now();
const filename = `fuzz-${targetName}-${timestamp}.json`;
const filepath = path.join(REPRODUCER_DIR, filename);
const report = {
target,
const content = {
target: targetName,
timestamp: new Date().toISOString(),
error: {
message: error.message,
stack: error.stack,
name: error.name
message: error.message,
stack: error.stack,
code: error.code,
name: error.name
},
input
};
fs.writeFileSync(filepath, JSON.stringify(report, null, 2));
fs.writeFileSync(filepath, JSON.stringify(content, null, 2));
console.error(`[FUZZ] Saved reproducer to ${filepath}`);
return filename;
return filepath;
}
export async function runFuzzer(name, fuzzFn, iterations = 1000) {
console.log(`[FUZZ] Starting fuzzer for ${name} with ${iterations} iterations...`);
const crashes = [];
const start = Date.now();
export function saveReport(targetName, stats) {
const filepath = path.join(ARTIFACTS_DIR, `fuzz-report-${targetName}.json`);
fs.writeFileSync(filepath, JSON.stringify(stats, null, 2));
console.log(`[FUZZ] Saved report to ${filepath}`);
}
export async function runFuzzer(targetName, iterations, testFn) {
console.log(`[FUZZ] Starting fuzzer for ${targetName} with ${iterations} iterations...`);
const stats = {
target: targetName,
iterations: 0,
passed: 0,
failed: 0,
crashes: [], // List of { input, error, reproducerPath }
startTime: Date.now(),
endTime: null
};
for (let i = 0; i < iterations; i++) {
if (i % 100 === 0) process.stdout.write(".");
let currentInput = null;
stats.iterations++;
let input = null;
try {
const input = await fuzzFn(i);
currentInput = input;
} catch (error) {
process.stdout.write("E");
const filename = saveReproducer(name, currentInput, error);
crashes.push({
input = await testFn(i);
stats.passed++;
} catch (err) {
stats.failed++;
const reproducerPath = saveReproducer(targetName, input, err);
stats.crashes.push({
iteration: i,
error: error.message,
reproducer: filename
error: err.message,
reproducerPath
});
// Optionally stop on first crash or continue
// For now, we continue but log it.
}
if (i % 100 === 0 && i > 0) {
process.stdout.write(".");
}
}
process.stdout.write("\n");
console.log("\n");
const duration = (Date.now() - start) / 1000;
console.log(`[FUZZ] Finished ${name} in ${duration.toFixed(2)}s. Crashes: ${crashes.length}`);
stats.endTime = Date.now();
stats.duration = stats.endTime - stats.startTime;
const reportPath = path.join(ARTIFACTS_DIR, `fuzz-report-${name}.json`);
const report = {
target: name,
iterations,
duration,
crashes
};
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
console.log(`[FUZZ] Saved report to ${reportPath}`);
console.log(`[FUZZ] Finished. ${stats.passed} passed, ${stats.failed} failed.`);
saveReport(targetName, stats);
return stats;
}