mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
- Expand fuzzing coverage for `js/nostrEventSchemas.js` to include all builder functions. - Fix a crash in `buildAdminListEvent` where non-array `hexPubkeys` caused a TypeError. - Update `scripts/agent/fuzz-dmDecryptor.mjs` and `scripts/agent/fuzz-magnetUtils.mjs` iterations. - Add reproducer for the `buildAdminListEvent` crash. - Generate fuzz reports in `artifacts/`. Co-authored-by: PR0M3TH3AN <[email protected]>
255 lines
8.7 KiB
JavaScript
255 lines
8.7 KiB
JavaScript
|
|
import { runFuzzer, rng } from "./fuzz-shared.mjs";
|
|
import { TextEncoder, TextDecoder } from "util";
|
|
|
|
// Polyfills
|
|
global.TextEncoder = TextEncoder;
|
|
global.TextDecoder = TextDecoder;
|
|
global.window = {
|
|
bitvidNostrEventOverrides: {}
|
|
};
|
|
|
|
// Import the target module
|
|
// Note: We are in scripts/agent/
|
|
import * as Schemas from "../../js/nostrEventSchemas.js";
|
|
|
|
async function fuzzTest(iteration) {
|
|
// We choose a random function to fuzz in each iteration or fuzz all of them?
|
|
// Let's pick one random target per iteration to keep it simple and isolate crashes.
|
|
|
|
const targets = [
|
|
"buildVideoPostEvent",
|
|
"buildViewEvent",
|
|
"buildCommentEvent",
|
|
"buildReactionEvent",
|
|
"buildZapRequestEvent",
|
|
"sanitizeAdditionalTags",
|
|
"validateEventStructure",
|
|
"setNostrEventSchemaOverrides",
|
|
"buildRepostEvent",
|
|
"buildShareEvent",
|
|
"buildRelayListEvent",
|
|
"buildDmRelayListEvent",
|
|
"buildProfileMetadataEvent",
|
|
"buildMuteListEvent",
|
|
"buildDeletionEvent",
|
|
"buildLegacyDirectMessageEvent",
|
|
"buildDmAttachmentEvent",
|
|
"buildDmReadReceiptEvent",
|
|
"buildDmTypingIndicatorEvent",
|
|
"buildWatchHistoryEvent",
|
|
"buildSubscriptionListEvent",
|
|
"buildBlockListEvent",
|
|
"buildHashtagPreferenceEvent",
|
|
"buildAdminListEvent",
|
|
"buildHttpAuthEvent",
|
|
"buildReportEvent",
|
|
"buildVideoMirrorEvent"
|
|
];
|
|
|
|
const targetName = rng.oneOf(targets);
|
|
let inputs = {};
|
|
|
|
// Helper to generate a comprehensive random params object
|
|
const genParams = () => {
|
|
// 10% chance of being undefined/null/non-object to test robustness
|
|
if (Math.random() < 0.1) return rng.nastyString();
|
|
if (Math.random() < 0.1) return null;
|
|
|
|
// Otherwise generate a rich object
|
|
const params = {
|
|
pubkey: rng.mixedString(64),
|
|
created_at: rng.bool() ? rng.int(0, 2000000000) : rng.nastyString(),
|
|
content: rng.bool() ? rng.mixedString(1000) : rng.recursiveObject(2),
|
|
additionalTags: rng.bool() ? rng.array(() => rng.array(() => rng.mixedString(20), 5), 10) : rng.nastyString(),
|
|
};
|
|
|
|
// Add specific fields that might be used by specific builders
|
|
const extraFields = [
|
|
"dTagValue", "url", "magnet", "thumbnail", "description", "mode", "videoRootId",
|
|
"pointerValue", "pointerTag", "pointerTags", "dedupeTag", "includeSessionTag",
|
|
"videoEventId", "videoEventRelay", "videoDefinitionAddress", "rootIdentifier",
|
|
"parentCommentId", "rootKind", "rootAuthorPubkey", "parentKind",
|
|
"recipientPubkey", "relays", "amountSats", "lnurl", "eventId", "coordinate",
|
|
"targetPointer", "targetAuthorPubkey",
|
|
// Added fields
|
|
"repostKind", "targetKind", "targetEvent", "serializedEvent",
|
|
"video", "metadata", "pTags", "encrypted", "encryptionTag",
|
|
"eventIds", "addresses", "reason", "ciphertext", "attachment",
|
|
"messageKind", "expiresAt", "monthIdentifier", "hexPubkeys",
|
|
"payload", "reportType", "address", "addressRelay", "authorPubkey",
|
|
"eventRelay", "method", "userId", "relayHint"
|
|
];
|
|
|
|
extraFields.forEach(field => {
|
|
if (rng.bool()) {
|
|
params[field] = rng.mixedString(50);
|
|
} else if (rng.bool()) {
|
|
if (field === "targetEvent" || field === "video" || field === "attachment" || field === "metadata") {
|
|
params[field] = rng.recursiveObject(2);
|
|
} else if (field === "relays" || field === "pTags" || field === "eventIds" || field === "addresses" || field === "hexPubkeys") {
|
|
params[field] = rng.array(() => rng.mixedString(64), 5);
|
|
} else {
|
|
params[field] = rng.recursiveObject(1); // Potentially invalid type
|
|
}
|
|
}
|
|
});
|
|
|
|
return params;
|
|
};
|
|
|
|
switch (targetName) {
|
|
case "buildVideoPostEvent":
|
|
inputs = genParams();
|
|
Schemas.buildVideoPostEvent(inputs);
|
|
break;
|
|
|
|
case "buildViewEvent":
|
|
inputs = genParams();
|
|
Schemas.buildViewEvent(inputs);
|
|
break;
|
|
|
|
case "buildCommentEvent":
|
|
inputs = genParams();
|
|
Schemas.buildCommentEvent(inputs);
|
|
break;
|
|
|
|
case "buildReactionEvent":
|
|
inputs = genParams();
|
|
Schemas.buildReactionEvent(inputs);
|
|
break;
|
|
|
|
case "buildZapRequestEvent":
|
|
inputs = genParams();
|
|
Schemas.buildZapRequestEvent(inputs);
|
|
break;
|
|
|
|
case "sanitizeAdditionalTags":
|
|
// sanitizeAdditionalTags expects an array of tags (arrays)
|
|
if (rng.bool()) {
|
|
inputs = rng.array(() => rng.array(() => rng.mixedString(20), 5), 20);
|
|
} else {
|
|
inputs = rng.nastyString(); // or object, etc.
|
|
}
|
|
Schemas.sanitizeAdditionalTags(inputs);
|
|
break;
|
|
|
|
case "validateEventStructure":
|
|
const type = rng.oneOf(Object.values(Schemas.NOTE_TYPES));
|
|
let event = genParams(); // repurpose genParams to make a fake event
|
|
// Ensure event is an object for this specific test setup, unless we want to fuzz that too.
|
|
// validateEventStructure checks: if (!event || typeof event !== "object") return { valid: false ... }
|
|
// So passing a string is valid input for the function (should not crash),
|
|
// but my fuzzer crashes trying to assign .kind to it.
|
|
if (event && typeof event === 'object') {
|
|
event.kind = rng.int(0, 50000);
|
|
event.tags = rng.array(() => rng.array(() => rng.mixedString(10), 3), 5);
|
|
}
|
|
inputs = { type, event };
|
|
Schemas.validateEventStructure(type, event);
|
|
break;
|
|
|
|
case "setNostrEventSchemaOverrides":
|
|
const randomType = rng.oneOf(Object.values(Schemas.NOTE_TYPES));
|
|
if (rng.bool()) {
|
|
// Circular object
|
|
const obj = { foo: "bar" };
|
|
obj.self = obj;
|
|
inputs = { [randomType]: obj };
|
|
} else {
|
|
inputs = { [randomType]: genParams() };
|
|
}
|
|
// If we set overrides, we should also trigger usage of them to see if it crashes later
|
|
Schemas.setNostrEventSchemaOverrides(inputs);
|
|
// Trigger usage
|
|
Schemas.getNostrEventSchema(randomType);
|
|
break;
|
|
|
|
case "buildRepostEvent":
|
|
inputs = genParams();
|
|
Schemas.buildRepostEvent(inputs);
|
|
break;
|
|
case "buildShareEvent":
|
|
inputs = genParams();
|
|
Schemas.buildShareEvent(inputs);
|
|
break;
|
|
case "buildRelayListEvent":
|
|
inputs = genParams();
|
|
Schemas.buildRelayListEvent(inputs);
|
|
break;
|
|
case "buildDmRelayListEvent":
|
|
inputs = genParams();
|
|
Schemas.buildDmRelayListEvent(inputs);
|
|
break;
|
|
case "buildProfileMetadataEvent":
|
|
inputs = genParams();
|
|
Schemas.buildProfileMetadataEvent(inputs);
|
|
break;
|
|
case "buildMuteListEvent":
|
|
inputs = genParams();
|
|
Schemas.buildMuteListEvent(inputs);
|
|
break;
|
|
case "buildDeletionEvent":
|
|
inputs = genParams();
|
|
Schemas.buildDeletionEvent(inputs);
|
|
break;
|
|
case "buildLegacyDirectMessageEvent":
|
|
inputs = genParams();
|
|
Schemas.buildLegacyDirectMessageEvent(inputs);
|
|
break;
|
|
case "buildDmAttachmentEvent":
|
|
inputs = genParams();
|
|
Schemas.buildDmAttachmentEvent(inputs);
|
|
break;
|
|
case "buildDmReadReceiptEvent":
|
|
inputs = genParams();
|
|
Schemas.buildDmReadReceiptEvent(inputs);
|
|
break;
|
|
case "buildDmTypingIndicatorEvent":
|
|
inputs = genParams();
|
|
Schemas.buildDmTypingIndicatorEvent(inputs);
|
|
break;
|
|
case "buildWatchHistoryEvent":
|
|
inputs = genParams();
|
|
Schemas.buildWatchHistoryEvent(inputs);
|
|
break;
|
|
case "buildSubscriptionListEvent":
|
|
inputs = genParams();
|
|
Schemas.buildSubscriptionListEvent(inputs);
|
|
break;
|
|
case "buildBlockListEvent":
|
|
inputs = genParams();
|
|
Schemas.buildBlockListEvent(inputs);
|
|
break;
|
|
case "buildHashtagPreferenceEvent":
|
|
inputs = genParams();
|
|
Schemas.buildHashtagPreferenceEvent(inputs);
|
|
break;
|
|
case "buildAdminListEvent":
|
|
inputs = genParams();
|
|
// buildAdminListEvent(listKey, params)
|
|
const listKey = rng.oneOf(["moderation", "editors", "whitelist", "blacklist", "communityBlacklistSources", rng.nastyString()]);
|
|
Schemas.buildAdminListEvent(listKey, inputs);
|
|
break;
|
|
case "buildHttpAuthEvent":
|
|
inputs = genParams();
|
|
Schemas.buildHttpAuthEvent(inputs);
|
|
break;
|
|
case "buildReportEvent":
|
|
inputs = genParams();
|
|
Schemas.buildReportEvent(inputs);
|
|
break;
|
|
case "buildVideoMirrorEvent":
|
|
inputs = genParams();
|
|
Schemas.buildVideoMirrorEvent(inputs);
|
|
break;
|
|
}
|
|
|
|
return { target: targetName, inputs };
|
|
}
|
|
|
|
runFuzzer("nostrEventSchemas", 10000, fuzzTest).catch(err => {
|
|
console.error("Fatal fuzzer error:", err);
|
|
process.exit(1);
|
|
});
|