Add request validator rule names

This commit is contained in:
mini-bomba
2025-04-26 02:01:10 +02:00
parent cbc38c5ac8
commit 9bc4bf8c7b
5 changed files with 205 additions and 129 deletions

View File

@@ -59,7 +59,7 @@ export async function postBranding(req: Request, res: Response) {
const hashedIP = await getHashCache(getIP(req) + config.globalSalt as IPAddress); const hashedIP = await getHashCache(getIP(req) + config.globalSalt as IPAddress);
const isBanned = await checkBanStatus(hashedUserID, hashedIP); const isBanned = await checkBanStatus(hashedUserID, hashedIP);
if (isRequestInvalid({ const matchedRule = isRequestInvalid({
userAgent, userAgent,
userAgentHeader: req.headers["user-agent"], userAgentHeader: req.headers["user-agent"],
videoDuration, videoDuration,
@@ -72,8 +72,9 @@ export async function postBranding(req: Request, res: Response) {
downvote, downvote,
}, },
endpoint: "dearrow-postBranding", endpoint: "dearrow-postBranding",
})) { });
sendNewUserWebhook(config.discordRejectedNewUserWebhookURL, hashedUserID, videoID, userAgent, req, videoDuration, title); if (matchedRule !== null) {
sendNewUserWebhook(config.discordRejectedNewUserWebhookURL, hashedUserID, videoID, userAgent, req, videoDuration, title, matchedRule);
Logger.warn(`Dearrow submission rejected by request validator: ${hashedUserID} ${videoID} ${videoDuration} ${userAgent} ${req.headers["user-agent"]} ${title.title} ${thumbnail.timestamp}`); Logger.warn(`Dearrow submission rejected by request validator: ${hashedUserID} ${videoID} ${videoDuration} ${userAgent} ${req.headers["user-agent"]} ${title.title} ${thumbnail.timestamp}`);
res.status(200).send("OK"); res.status(200).send("OK");
return; return;
@@ -86,7 +87,7 @@ export async function postBranding(req: Request, res: Response) {
res.status(403).send(permission.reason); res.status(403).send(permission.reason);
return; return;
} else if (permission.newUser) { } else if (permission.newUser) {
sendNewUserWebhook(config.discordNewUserWebhookURL, hashedUserID, videoID, userAgent, req, videoDuration, title); sendNewUserWebhook(config.discordNewUserWebhookURL, hashedUserID, videoID, userAgent, req, videoDuration, title, undefined);
} }
if (videoDuration && thumbnail && await checkForWrongVideoDuration(videoID, videoDuration)) { if (videoDuration && thumbnail && await checkForWrongVideoDuration(videoID, videoDuration)) {
@@ -210,7 +211,7 @@ export async function postBranding(req: Request, res: Response) {
} }
} }
function sendNewUserWebhook(webhookUrl: string, hashedUserID: HashedUserID, videoID: VideoID, userAgent: any, req: Request, videoDuration: number, title: TitleSubmission) { function sendNewUserWebhook(webhookUrl: string, hashedUserID: HashedUserID, videoID: VideoID, userAgent: any, req: Request, videoDuration: number, title: TitleSubmission, ruleName: string | undefined) {
if (!webhookUrl) return; if (!webhookUrl) return;
axios.post(webhookUrl, { axios.post(webhookUrl, {
@@ -226,6 +227,9 @@ function sendNewUserWebhook(webhookUrl: string, hashedUserID: HashedUserID, vide
"thumbnail": { "thumbnail": {
"url": getMaxResThumbnail(videoID), "url": getMaxResThumbnail(videoID),
}, },
"footer": {
"text": ruleName === undefined ? `Caught by permission check` : `Caught by rule '${ruleName}'`,
},
}], }],
}) })
.then(res => { .then(res => {

View File

@@ -510,7 +510,7 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
} }
const userID: HashedUserID = await getHashCache(paramUserID); const userID: HashedUserID = await getHashCache(paramUserID);
if (isRequestInvalid({ const matchedRule = isRequestInvalid({
userAgent, userAgent,
userAgentHeader: req.headers["user-agent"], userAgentHeader: req.headers["user-agent"],
videoDuration, videoDuration,
@@ -519,8 +519,9 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
service, service,
segments, segments,
endpoint: "sponsorblock-postSkipSegments" endpoint: "sponsorblock-postSkipSegments"
})) { });
sendNewUserWebhook(config.discordRejectedNewUserWebhookURL, userID, videoID, userAgent, req, videoDurationParam); if (matchedRule !== null) {
sendNewUserWebhook(config.discordRejectedNewUserWebhookURL, userID, videoID, userAgent, req, videoDurationParam, matchedRule);
Logger.warn(`Sponsorblock submission rejected by request validator: ${userID} ${videoID} ${videoDurationParam} ${userAgent} ${req.headers["user-agent"]}`); Logger.warn(`Sponsorblock submission rejected by request validator: ${userID} ${videoID} ${videoDurationParam} ${userAgent} ${req.headers["user-agent"]}`);
return res.status(200).send("OK"); return res.status(200).send("OK");
} }
@@ -572,7 +573,7 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
Logger.warn(`New user trying to submit: ${userID} ${videoID} ${videoDurationParam} ${userAgent} ${req.headers["user-agent"]}`); Logger.warn(`New user trying to submit: ${userID} ${videoID} ${videoDurationParam} ${userAgent} ${req.headers["user-agent"]}`);
return res.status(403).send(permission.reason); return res.status(403).send(permission.reason);
} else if (permission.newUser) { } else if (permission.newUser) {
sendNewUserWebhook(config.discordNewUserWebhookURL, userID, videoID, userAgent, req, videoDurationParam); sendNewUserWebhook(config.discordNewUserWebhookURL, userID, videoID, userAgent, req, videoDurationParam, undefined);
} }
// Will be filled when submitting // Will be filled when submitting
@@ -661,7 +662,7 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
} }
} }
function sendNewUserWebhook(webhookUrl: string, userID: HashedUserID, videoID: any, userAgent: any, req: Request, videoDurationParam: VideoDuration) { function sendNewUserWebhook(webhookUrl: string, userID: HashedUserID, videoID: any, userAgent: any, req: Request, videoDurationParam: VideoDuration, ruleName: string | undefined) {
if (!webhookUrl) return; if (!webhookUrl) return;
axios.post(webhookUrl, { axios.post(webhookUrl, {
@@ -676,6 +677,9 @@ function sendNewUserWebhook(webhookUrl: string, userID: HashedUserID, videoID: a
"thumbnail": { "thumbnail": {
"url": getMaxResThumbnail(videoID), "url": getMaxResThumbnail(videoID),
}, },
"footer": {
"text": ruleName === undefined ? "Caught by permission check" : `Caught by rule '${ruleName}'`,
},
}], }],
}) })
.then(res => { .then(res => {

View File

@@ -43,6 +43,7 @@ export interface CustomPostgresReadOnlyConfig extends CustomPostgresConfig {
export type ValidatorPattern = string | [string, string]; export type ValidatorPattern = string | [string, string];
export interface RequestValidatorRule { export interface RequestValidatorRule {
ruleName?: string;
// mostly universal // mostly universal
userAgent?: ValidatorPattern; userAgent?: ValidatorPattern;
userAgentHeader?: ValidatorPattern; userAgentHeader?: ValidatorPattern;

View File

@@ -24,7 +24,8 @@ export interface RequestValidatorInput {
newUsername?: string; newUsername?: string;
endpoint?: string; endpoint?: string;
} }
export type CompiledValidityCheck = (input: RequestValidatorInput) => boolean; export type CompiledValidityCheck = (input: RequestValidatorInput) => string | null;
type CompiledPatternCheck = (input: RequestValidatorInput) => boolean;
type CompiledSegmentCheck = (input: IncomingSegment) => boolean; type CompiledSegmentCheck = (input: IncomingSegment) => boolean;
type InputExtractor = ( type InputExtractor = (
input: RequestValidatorInput, input: RequestValidatorInput,
@@ -46,7 +47,7 @@ function patternToRegex(pattern: ValidatorPattern): RegExp {
function compilePattern( function compilePattern(
pattern: ValidatorPattern, pattern: ValidatorPattern,
extractor: InputExtractor, extractor: InputExtractor,
): CompiledValidityCheck { ): CompiledPatternCheck {
const regex = patternToRegex(pattern); const regex = patternToRegex(pattern);
return (input: RequestValidatorInput) => { return (input: RequestValidatorInput) => {
@@ -72,11 +73,12 @@ function compileSegmentPattern(
export function compileRules( export function compileRules(
ruleDefinitions: RequestValidatorRule[], ruleDefinitions: RequestValidatorRule[],
): CompiledValidityCheck { ): CompiledValidityCheck {
if (ruleDefinitions.length === 0) return () => false; if (ruleDefinitions.length === 0) return () => null;
const rules: CompiledValidityCheck[] = []; const rules: CompiledValidityCheck[] = [];
let untitledRuleCounter = 0;
for (const ruleDefinition of ruleDefinitions) { for (const ruleDefinition of ruleDefinitions) {
const ruleComponents: CompiledValidityCheck[] = []; const ruleComponents: CompiledPatternCheck[] = [];
const segmentRuleComponents: CompiledSegmentCheck[] = []; const segmentRuleComponents: CompiledSegmentCheck[] = [];
for (const [ruleKey, rulePattern] of Object.entries( for (const [ruleKey, rulePattern] of Object.entries(
ruleDefinition, ruleDefinition,
@@ -217,6 +219,9 @@ export function compileRules(
}); });
break; break;
} }
case "ruleName":
// not a rule component
break;
default: { default: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
const _exhaustive: never = ruleKey; const _exhaustive: never = ruleKey;
@@ -239,22 +244,24 @@ export function compileRules(
return false; return false;
}); });
} }
const ruleName = ruleDefinition.ruleName ?? `Untitled rule ${++untitledRuleCounter}`;
rules.push((input) => { rules.push((input) => {
for (const rule of ruleComponents) { for (const rule of ruleComponents) {
if (!rule(input)) return false; if (!rule(input)) return null;
} }
return true; return ruleName;
}); });
} }
return (input) => { return (input) => {
for (const rule of rules) { for (const rule of rules) {
if (rule(input)) return true; const result = rule(input);
if (result !== null) return result;
} }
return false; return null;
}; };
} }
export function isRequestInvalid(input: RequestValidatorInput) { export function isRequestInvalid(input: RequestValidatorInput): string | null {
compiledRules ??= compileRules(config.requestValidatorRules); compiledRules ??= compileRules(config.requestValidatorRules);
return compiledRules(input); return compiledRules(input);
} }

View File

@@ -21,38 +21,43 @@ describe("Request validator", () => {
}); });
it("simple expected match", () => { it("simple expected match", () => {
assert.ok( assert.equal(
compiledRuleset({ compiledRuleset({
userID: "asdfg", userID: "asdfg",
}), }),
"Untitled rule 1",
); );
}); });
it("case insensitive match", () => { it("case insensitive match", () => {
assert.ok( assert.equal(
compiledRuleset({ compiledRuleset({
userID: "asDfg", userID: "asDfg",
}), }),
"Untitled rule 1",
); );
}); });
it("simple expected no match", () => { it("simple expected no match", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
userID: "125aaa", userID: "125aaa",
}), }),
null,
); );
}); });
it("missing field - no match", () => { it("missing field - no match", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
userAgent: "Mozilla/5.0", userAgent: "Mozilla/5.0",
}), }),
null,
); );
}); });
}); });
describe("single case sensitive rule", () => { describe("single case sensitive rule with name", () => {
const ruleset: RequestValidatorRule[] = [ const ruleset: RequestValidatorRule[] = [
{ {
ruleName: "Testing rule",
// tuple patterns allow setting regex flags // tuple patterns allow setting regex flags
userID: ["^[a-z]+$", ""], userID: ["^[a-z]+$", ""],
}, },
@@ -64,39 +69,44 @@ describe("Request validator", () => {
}); });
it("simple expected match", () => { it("simple expected match", () => {
assert.ok( assert.equal(
compiledRuleset({ compiledRuleset({
userID: "asdfg", userID: "asdfg",
}), }),
"Testing rule",
); );
}); });
it("different casing", () => { it("different casing", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
userID: "asDfg", userID: "asDfg",
}), }),
null,
); );
}); });
it("extra field match", () => { it("extra field match", () => {
assert.ok( assert.equal(
compiledRuleset({ compiledRuleset({
userID: "asdfg", userID: "asdfg",
userAgent: "Mozilla/5.0", userAgent: "Mozilla/5.0",
}), }),
"Testing rule",
); );
}); });
it("simple expected no match", () => { it("simple expected no match", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
userID: "125aaa", userID: "125aaa",
}), }),
null,
); );
}); });
it("missing field - no match", () => { it("missing field - no match", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
userAgent: "Mozilla/5.0", userAgent: "Mozilla/5.0",
}), }),
null,
); );
}); });
}); });
@@ -104,6 +114,7 @@ describe("Request validator", () => {
describe("2-pattern rule", () => { describe("2-pattern rule", () => {
const ruleset: RequestValidatorRule[] = [ const ruleset: RequestValidatorRule[] = [
{ {
ruleName: "Testing rule",
userID: ["^[a-z]+$", ""], userID: ["^[a-z]+$", ""],
userAgent: "^Mozilla/5\\.0", userAgent: "^Mozilla/5\\.0",
}, },
@@ -115,48 +126,54 @@ describe("Request validator", () => {
}); });
it("simple expected match", () => { it("simple expected match", () => {
assert.ok( assert.equal(
compiledRuleset({ compiledRuleset({
userID: "asdfg", userID: "asdfg",
userAgent: "Mozilla/5.0 Chromeium/213.7", userAgent: "Mozilla/5.0 Chromeium/213.7",
}), }),
"Testing rule",
); );
}); });
it("only matching one pattern - fail #1", () => { it("only matching one pattern - fail #1", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
userID: "asDfg", userID: "asDfg",
userAgent: "Mozilla/5.0 Chromeium/213.7", userAgent: "Mozilla/5.0 Chromeium/213.7",
}), }),
null,
); );
}); });
it("only matching one pattern - fail #2", () => { it("only matching one pattern - fail #2", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
userID: "asdfg", userID: "asdfg",
userAgent: "ReVanced/20.07.39", userAgent: "ReVanced/20.07.39",
}), }),
null,
); );
}); });
it("missing one of the fields - fail #1", () => { it("missing one of the fields - fail #1", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
userID: "asdfg", userID: "asdfg",
}), }),
null,
); );
}); });
it("missing one of the fields - fail #2", () => { it("missing one of the fields - fail #2", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
userAgent: "Mozilla/5.0 Chromeium/213.7", userAgent: "Mozilla/5.0 Chromeium/213.7",
}), }),
null,
); );
}); });
it("missing all fields - fail", () => { it("missing all fields - fail", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
videoDuration: 21.37, videoDuration: 21.37,
}), }),
null,
); );
}); });
}); });
@@ -164,6 +181,7 @@ describe("Request validator", () => {
describe("1-pattern segment rule", () => { describe("1-pattern segment rule", () => {
const ruleset: RequestValidatorRule[] = [ const ruleset: RequestValidatorRule[] = [
{ {
ruleName: "Testing rule",
description: "mini_bomba", description: "mini_bomba",
}, },
]; ];
@@ -174,7 +192,7 @@ describe("Request validator", () => {
}); });
it("simple expected match", () => { it("simple expected match", () => {
assert.ok( assert.equal(
compiledRuleset({ compiledRuleset({
segments: [ segments: [
{ {
@@ -185,10 +203,11 @@ describe("Request validator", () => {
}, },
], ],
}), }),
"Testing rule",
); );
}); });
it("match on one of multiple segments", () => { it("match on one of multiple segments", () => {
assert.ok( assert.equal(
compiledRuleset({ compiledRuleset({
segments: [ segments: [
{ {
@@ -205,10 +224,11 @@ describe("Request validator", () => {
}, },
], ],
}), }),
"Testing rule",
); );
}); });
it("match on one of multiple segments with other missing field", () => { it("match on one of multiple segments with other missing field", () => {
assert.ok( assert.equal(
compiledRuleset({ compiledRuleset({
segments: [ segments: [
{ {
@@ -224,11 +244,12 @@ describe("Request validator", () => {
}, },
], ],
}), }),
"Testing rule",
); );
}); });
it("no match with one segment", () => { it("no match with one segment", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
segments: [ segments: [
{ {
segment: ["1", "2"], segment: ["1", "2"],
@@ -238,11 +259,12 @@ describe("Request validator", () => {
}, },
], ],
}), }),
null,
); );
}); });
it("no match with multiple segments", () => { it("no match with multiple segments", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
segments: [ segments: [
{ {
segment: ["1", "2"], segment: ["1", "2"],
@@ -258,11 +280,12 @@ describe("Request validator", () => {
}, },
], ],
}), }),
null,
); );
}); });
it("one segment missing field", () => { it("one segment missing field", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
segments: [ segments: [
{ {
segment: ["1", "2"], segment: ["1", "2"],
@@ -271,11 +294,12 @@ describe("Request validator", () => {
}, },
], ],
}), }),
null,
); );
}); });
it("multiple segments missing field", () => { it("multiple segments missing field", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
segments: [ segments: [
{ {
segment: ["1", "2"], segment: ["1", "2"],
@@ -289,20 +313,23 @@ describe("Request validator", () => {
}, },
], ],
}), }),
null,
); );
}); });
it("zero segments", () => { it("zero segments", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
segments: [], segments: [],
}), }),
null,
); );
}); });
it("missing segments", () => { it("missing segments", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
userAgent: "Mozilla/5.0", userAgent: "Mozilla/5.0",
}), }),
null,
); );
}); });
}); });
@@ -310,6 +337,7 @@ describe("Request validator", () => {
describe("2-pattern segment rule", () => { describe("2-pattern segment rule", () => {
const ruleset: RequestValidatorRule[] = [ const ruleset: RequestValidatorRule[] = [
{ {
ruleName: "Testing rule",
description: "mini_bomba", description: "mini_bomba",
startTime: "\\.\\d", startTime: "\\.\\d",
}, },
@@ -321,7 +349,7 @@ describe("Request validator", () => {
}); });
it("simple expected match", () => { it("simple expected match", () => {
assert.ok( assert.equal(
compiledRuleset({ compiledRuleset({
segments: [ segments: [
{ {
@@ -332,10 +360,11 @@ describe("Request validator", () => {
}, },
], ],
}), }),
"Testing rule",
); );
}); });
it("match on one of multiple segments", () => { it("match on one of multiple segments", () => {
assert.ok( assert.equal(
compiledRuleset({ compiledRuleset({
segments: [ segments: [
{ {
@@ -352,10 +381,11 @@ describe("Request validator", () => {
}, },
], ],
}), }),
"Testing rule",
); );
}); });
it("match on one of multiple segments with other missing field", () => { it("match on one of multiple segments with other missing field", () => {
assert.ok( assert.equal(
compiledRuleset({ compiledRuleset({
segments: [ segments: [
{ {
@@ -371,11 +401,12 @@ describe("Request validator", () => {
}, },
], ],
}), }),
"Testing rule",
); );
}); });
it("no match with one segment #1", () => { it("no match with one segment #1", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
segments: [ segments: [
{ {
segment: ["1", "2"], segment: ["1", "2"],
@@ -385,11 +416,12 @@ describe("Request validator", () => {
}, },
], ],
}), }),
null,
); );
}); });
it("no match with one segment #2", () => { it("no match with one segment #2", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
segments: [ segments: [
{ {
segment: ["1.1", "2"], segment: ["1.1", "2"],
@@ -399,11 +431,12 @@ describe("Request validator", () => {
}, },
], ],
}), }),
null,
); );
}); });
it("no match with one segment #2", () => { it("no match with one segment #2", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
segments: [ segments: [
{ {
segment: ["1", "2"], segment: ["1", "2"],
@@ -413,11 +446,12 @@ describe("Request validator", () => {
}, },
], ],
}), }),
null,
); );
}); });
it("no match with multiple segments", () => { it("no match with multiple segments", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
segments: [ segments: [
{ {
segment: ["1", "2"], segment: ["1", "2"],
@@ -433,11 +467,12 @@ describe("Request validator", () => {
}, },
], ],
}), }),
null,
); );
}); });
it("no match with multiple segments with partial matches", () => { it("no match with multiple segments with partial matches", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
segments: [ segments: [
{ {
segment: ["1.1", "2"], segment: ["1.1", "2"],
@@ -453,11 +488,12 @@ describe("Request validator", () => {
}, },
], ],
}), }),
null,
); );
}); });
it("one segment missing field", () => { it("one segment missing field", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
segments: [ segments: [
{ {
segment: ["1", "2"], segment: ["1", "2"],
@@ -466,11 +502,12 @@ describe("Request validator", () => {
}, },
], ],
}), }),
null,
); );
}); });
it("multiple segments missing field", () => { it("multiple segments missing field", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
segments: [ segments: [
{ {
segment: ["1", "2"], segment: ["1", "2"],
@@ -484,20 +521,23 @@ describe("Request validator", () => {
}, },
], ],
}), }),
null,
); );
}); });
it("zero segments", () => { it("zero segments", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
segments: [], segments: [],
}), }),
null,
); );
}); });
it("missing segments", () => { it("missing segments", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
userAgent: "Mozilla/5.0", userAgent: "Mozilla/5.0",
}), }),
null,
); );
}); });
}); });
@@ -505,6 +545,7 @@ describe("Request validator", () => {
describe("boolean rule", () => { describe("boolean rule", () => {
const ruleset: RequestValidatorRule[] = [ const ruleset: RequestValidatorRule[] = [
{ {
ruleName: "Testing rule",
dearrowDownvote: true, dearrowDownvote: true,
}, },
]; ];
@@ -515,28 +556,31 @@ describe("Request validator", () => {
}); });
it("simple expected match", () => { it("simple expected match", () => {
assert.ok( assert.equal(
compiledRuleset({ compiledRuleset({
dearrow: { dearrow: {
downvote: true, downvote: true,
}, },
}), }),
"Testing rule",
); );
}); });
it("simple expected no match", () => { it("simple expected no match", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
dearrow: { dearrow: {
downvote: false, downvote: false,
}, },
}), }),
null,
); );
}); });
it("missing field - no match", () => { it("missing field - no match", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
userAgent: "Mozilla/5.0", userAgent: "Mozilla/5.0",
}), }),
null,
); );
}); });
}); });
@@ -544,6 +588,7 @@ describe("Request validator", () => {
describe("mixed type rules", () => { describe("mixed type rules", () => {
const ruleset: RequestValidatorRule[] = [ const ruleset: RequestValidatorRule[] = [
{ {
ruleName: "Testing rule",
titleOriginal: true, titleOriginal: true,
title: "mini_bomba", title: "mini_bomba",
}, },
@@ -555,7 +600,7 @@ describe("Request validator", () => {
}); });
it("simple expected match", () => { it("simple expected match", () => {
assert.ok( assert.equal(
compiledRuleset({ compiledRuleset({
dearrow: { dearrow: {
downvote: false, downvote: false,
@@ -565,11 +610,12 @@ describe("Request validator", () => {
}, },
}, },
}), }),
"Testing rule",
); );
}); });
it("simple expected no match", () => { it("simple expected no match", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
dearrow: { dearrow: {
downvote: false, downvote: false,
title: { title: {
@@ -578,11 +624,12 @@ describe("Request validator", () => {
}, },
}, },
}), }),
null,
); );
}); });
it("partial match #1", () => { it("partial match #1", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
dearrow: { dearrow: {
downvote: false, downvote: false,
title: { title: {
@@ -591,11 +638,12 @@ describe("Request validator", () => {
}, },
}, },
}), }),
null,
); );
}); });
it("partial match #2", () => { it("partial match #2", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
dearrow: { dearrow: {
downvote: false, downvote: false,
title: { title: {
@@ -604,34 +652,38 @@ describe("Request validator", () => {
}, },
}, },
}), }),
null,
); );
}); });
it("missing field - no match #1", () => { it("missing field - no match #1", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
userAgent: "Mozilla/5.0", userAgent: "Mozilla/5.0",
}), }),
null,
); );
}); });
it("missing field - no match #2", () => { it("missing field - no match #2", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
dearrow: { dearrow: {
downvote: false, downvote: false,
} },
}), }),
null,
); );
}); });
it("missing field - no match #3", () => { it("missing field - no match #3", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
dearrow: { dearrow: {
downvote: false, downvote: false,
thumbnail: { thumbnail: {
original: true, original: true,
} },
} },
}), }),
null,
); );
}); });
}); });
@@ -639,11 +691,12 @@ describe("Request validator", () => {
describe("two-rule ruleset", () => { describe("two-rule ruleset", () => {
const ruleset: RequestValidatorRule[] = [ const ruleset: RequestValidatorRule[] = [
{ {
ruleName: "Rule one",
titleOriginal: true, titleOriginal: true,
}, },
{ {
title: "mini_bomba", title: "mini_bomba",
} },
]; ];
let compiledRuleset: CompiledValidityCheck; let compiledRuleset: CompiledValidityCheck;
@@ -652,7 +705,7 @@ describe("Request validator", () => {
}); });
it("matches both", () => { it("matches both", () => {
assert.ok( assert.equal(
compiledRuleset({ compiledRuleset({
dearrow: { dearrow: {
downvote: false, downvote: false,
@@ -662,23 +715,11 @@ describe("Request validator", () => {
}, },
}, },
}), }),
"Rule one",
); );
}); });
it("matches 1", () => { it("matches 1", () => {
assert.ok( assert.equal(
compiledRuleset({
dearrow: {
downvote: false,
title: {
title: "mini_bomba gaming",
original: false,
},
},
}),
);
});
it("matches 2", () => {
assert.ok(
compiledRuleset({ compiledRuleset({
dearrow: { dearrow: {
downvote: false, downvote: false,
@@ -688,11 +729,26 @@ describe("Request validator", () => {
}, },
}, },
}), }),
"Rule one",
);
});
it("matches 2", () => {
assert.equal(
compiledRuleset({
dearrow: {
downvote: false,
title: {
title: "mini_bomba gaming",
original: false,
},
},
}),
"Untitled rule 1",
); );
}); });
it("no match", () => { it("no match", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
dearrow: { dearrow: {
downvote: false, downvote: false,
title: { title: {
@@ -701,34 +757,38 @@ describe("Request validator", () => {
}, },
}, },
}), }),
null,
); );
}); });
it("missing both fields #1", () => { it("missing both fields #1", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
userAgent: "Mozilla/5.0", userAgent: "Mozilla/5.0",
}), }),
null,
); );
}); });
it("missing both fields #2", () => { it("missing both fields #2", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
dearrow: { dearrow: {
downvote: false, downvote: false,
} },
}), }),
null,
); );
}); });
it("missing both fields #3", () => { it("missing both fields #3", () => {
assert.ok( assert.equal(
!compiledRuleset({ compiledRuleset({
dearrow: { dearrow: {
downvote: false, downvote: false,
thumbnail: { thumbnail: {
original: true, original: true,
} },
} },
}), }),
null,
); );
}); });
}); });