mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-06 12:52:26 +00:00
Harden moderation trust handling
This commit is contained in:
1 parent
0aa28a023d
commit
397ed7c8ce
5 files changed
+661
-8
No files matched your search
@@ -0,0 +1,102 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { ModerationService } from "../js/services/moderationService.js";
|
||||
|
||||
const nip19 = {
|
||||
npubEncode(hex) {
|
||||
if (typeof hex !== "string" || !hex) {
|
||||
throw new Error("invalid hex");
|
||||
}
|
||||
return `npub${hex}`;
|
||||
},
|
||||
decode(value) {
|
||||
if (typeof value !== "string" || !value.startsWith("npub")) {
|
||||
throw new Error("invalid npub");
|
||||
}
|
||||
return { type: "npub", data: value.slice(4) };
|
||||
},
|
||||
};
|
||||
|
||||
if (typeof globalThis.window === "undefined") {
|
||||
globalThis.window = {};
|
||||
}
|
||||
|
||||
test("trusted report summaries respect personal blocks and admin lists", async (t) => {
|
||||
const previousTools = globalThis.NostrTools;
|
||||
const previousWindowTools = globalThis.window?.NostrTools;
|
||||
|
||||
globalThis.NostrTools = { nip19 };
|
||||
if (globalThis.window) {
|
||||
globalThis.window.NostrTools = globalThis.NostrTools;
|
||||
}
|
||||
|
||||
t.after(() => {
|
||||
if (typeof previousTools === "undefined") {
|
||||
delete globalThis.NostrTools;
|
||||
} else {
|
||||
globalThis.NostrTools = previousTools;
|
||||
}
|
||||
|
||||
if (globalThis.window) {
|
||||
if (typeof previousWindowTools === "undefined") {
|
||||
delete globalThis.window.NostrTools;
|
||||
} else {
|
||||
globalThis.window.NostrTools = previousWindowTools;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const trustedHex = "a".repeat(64);
|
||||
const whitelistedHex = "b".repeat(64);
|
||||
const blacklistedHex = "c".repeat(64);
|
||||
const blockedHex = "d".repeat(64);
|
||||
const randomHex = "e".repeat(64);
|
||||
const eventId = "f".repeat(64);
|
||||
|
||||
const userBlocksMock = {
|
||||
async ensureLoaded() {},
|
||||
isBlocked(pubkey) {
|
||||
return pubkey === blockedHex;
|
||||
},
|
||||
};
|
||||
|
||||
const accessControlMock = {
|
||||
getWhitelist() {
|
||||
return [`npub${whitelistedHex}`];
|
||||
},
|
||||
getBlacklist() {
|
||||
return [`npub${blacklistedHex}`];
|
||||
},
|
||||
};
|
||||
|
||||
const service = new ModerationService({
|
||||
logger: () => {},
|
||||
userBlocks: userBlocksMock,
|
||||
accessControl: accessControlMock,
|
||||
});
|
||||
|
||||
service.trustedContacts = new Set([trustedHex]);
|
||||
|
||||
const reports = new Map();
|
||||
reports.set(trustedHex, new Map([["nudity", { created_at: 100 }]]));
|
||||
reports.set(whitelistedHex, new Map([["nudity", { created_at: 400 }]]));
|
||||
reports.set(blacklistedHex, new Map([["nudity", { created_at: 500 }]]));
|
||||
reports.set(blockedHex, new Map([["nudity", { created_at: 600 }]]));
|
||||
reports.set(randomHex, new Map([["nudity", { created_at: 300 }]]));
|
||||
service.reportEvents.set(eventId, reports);
|
||||
|
||||
service.recomputeSummaryForEvent(eventId);
|
||||
|
||||
const summary = service.getTrustedReportSummary(eventId);
|
||||
assert.equal(summary.totalTrusted, 2);
|
||||
assert.equal(summary.types.nudity.total, 3);
|
||||
assert.equal(summary.types.nudity.trusted, 2);
|
||||
assert.equal(summary.types.nudity.latest, 400);
|
||||
|
||||
const reporters = service.getTrustedReporters(eventId, "nudity");
|
||||
assert.deepEqual(
|
||||
reporters.map((entry) => entry.pubkey),
|
||||
[whitelistedHex, trustedHex]
|
||||
);
|
||||
assert.equal(service.trustedReportCount(eventId, "nudity"), 2);
|
||||
});
|
||||
@@ -0,0 +1,105 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { createModerationStage } from "../js/feedEngine/stages.js";
|
||||
|
||||
test("moderation stage enforces admin lists and whitelist bypass", async () => {
|
||||
const whitelistedHex = "1".repeat(64);
|
||||
const blockedHex = "2".repeat(64);
|
||||
const blacklistedHex = "3".repeat(64);
|
||||
const normalHex = "4".repeat(64);
|
||||
|
||||
const service = {
|
||||
async refreshViewerFromClient() {},
|
||||
async setActiveEventIds() {},
|
||||
getAdminListSnapshot() {
|
||||
return {
|
||||
whitelist: new Set([`npub${whitelistedHex}`]),
|
||||
whitelistHex: new Set([whitelistedHex]),
|
||||
blacklist: new Set([`npub${blacklistedHex}`]),
|
||||
blacklistHex: new Set([blacklistedHex]),
|
||||
};
|
||||
},
|
||||
getAccessControlStatus(identifier) {
|
||||
const normalized = typeof identifier === "string" ? identifier.trim().toLowerCase() : "";
|
||||
if (normalized === whitelistedHex) {
|
||||
return { hex: whitelistedHex, whitelisted: true, blacklisted: false };
|
||||
}
|
||||
if (normalized === blacklistedHex) {
|
||||
return { hex: blacklistedHex, whitelisted: false, blacklisted: true };
|
||||
}
|
||||
if (normalized === blockedHex) {
|
||||
return { hex: blockedHex, whitelisted: false, blacklisted: false };
|
||||
}
|
||||
if (normalized === normalHex) {
|
||||
return { hex: normalHex, whitelisted: false, blacklisted: false };
|
||||
}
|
||||
return { hex: normalized, whitelisted: false, blacklisted: false };
|
||||
},
|
||||
getTrustedReportSummary() {
|
||||
return null;
|
||||
},
|
||||
trustedReportCount(videoId) {
|
||||
if (videoId === "c") {
|
||||
return 5;
|
||||
}
|
||||
if (videoId === "d") {
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
getTrustedReporters() {
|
||||
return [];
|
||||
},
|
||||
};
|
||||
|
||||
const stage = createModerationStage({
|
||||
service,
|
||||
reportType: "nudity",
|
||||
autoplayThreshold: 2,
|
||||
blurThreshold: 3,
|
||||
});
|
||||
|
||||
const items = [
|
||||
{ video: { id: "a", pubkey: blockedHex }, metadata: {} },
|
||||
{ video: { id: "b", pubkey: blacklistedHex }, metadata: {} },
|
||||
{ video: { id: "c", pubkey: whitelistedHex }, metadata: {} },
|
||||
{ video: { id: "d", pubkey: normalHex }, metadata: {} },
|
||||
];
|
||||
|
||||
const why = [];
|
||||
const context = {
|
||||
feedName: "discovery",
|
||||
runtime: {
|
||||
isAuthorBlocked: (pubkey) => pubkey === blockedHex,
|
||||
},
|
||||
addWhy(detail) {
|
||||
why.push(detail);
|
||||
return detail;
|
||||
},
|
||||
log() {},
|
||||
};
|
||||
|
||||
const result = await stage(items, context);
|
||||
|
||||
assert.equal(result.length, 2);
|
||||
assert.deepEqual(
|
||||
result.map((entry) => entry.video.id),
|
||||
["c", "d"]
|
||||
);
|
||||
|
||||
const whitelistedItem = result[0];
|
||||
assert.equal(whitelistedItem.video.moderation.blockAutoplay, false);
|
||||
assert.equal(whitelistedItem.video.moderation.blurThumbnail, false);
|
||||
assert.equal(whitelistedItem.video.moderation.adminWhitelist, true);
|
||||
assert.equal(whitelistedItem.video.moderation.adminWhitelistBypass, true);
|
||||
assert.equal(whitelistedItem.metadata.moderation.adminWhitelistBypass, true);
|
||||
|
||||
const normalItem = result[1];
|
||||
assert.equal(normalItem.video.moderation.blockAutoplay, true);
|
||||
assert.equal(normalItem.video.moderation.blurThumbnail, true);
|
||||
|
||||
const reasons = why.map((entry) => entry.reason);
|
||||
assert(reasons.includes("viewer-block"));
|
||||
assert(reasons.includes("admin-blacklist"));
|
||||
assert(reasons.includes("blur"));
|
||||
});
|
||||
Reference in new issue
Block a user