mirror of
https://github.com/ajayyy/SponsorBlockServer.git
synced 2025-12-16 08:26:59 +03:00
Add request validator rule names
This commit is contained in:
@@ -59,7 +59,7 @@ export async function postBranding(req: Request, res: Response) {
|
||||
const hashedIP = await getHashCache(getIP(req) + config.globalSalt as IPAddress);
|
||||
const isBanned = await checkBanStatus(hashedUserID, hashedIP);
|
||||
|
||||
if (isRequestInvalid({
|
||||
const matchedRule = isRequestInvalid({
|
||||
userAgent,
|
||||
userAgentHeader: req.headers["user-agent"],
|
||||
videoDuration,
|
||||
@@ -72,8 +72,9 @@ export async function postBranding(req: Request, res: Response) {
|
||||
downvote,
|
||||
},
|
||||
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}`);
|
||||
res.status(200).send("OK");
|
||||
return;
|
||||
@@ -86,7 +87,7 @@ export async function postBranding(req: Request, res: Response) {
|
||||
res.status(403).send(permission.reason);
|
||||
return;
|
||||
} 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)) {
|
||||
@@ -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;
|
||||
|
||||
axios.post(webhookUrl, {
|
||||
@@ -226,6 +227,9 @@ function sendNewUserWebhook(webhookUrl: string, hashedUserID: HashedUserID, vide
|
||||
"thumbnail": {
|
||||
"url": getMaxResThumbnail(videoID),
|
||||
},
|
||||
"footer": {
|
||||
"text": ruleName === undefined ? `Caught by permission check` : `Caught by rule '${ruleName}'`,
|
||||
},
|
||||
}],
|
||||
})
|
||||
.then(res => {
|
||||
|
||||
@@ -510,7 +510,7 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
|
||||
}
|
||||
const userID: HashedUserID = await getHashCache(paramUserID);
|
||||
|
||||
if (isRequestInvalid({
|
||||
const matchedRule = isRequestInvalid({
|
||||
userAgent,
|
||||
userAgentHeader: req.headers["user-agent"],
|
||||
videoDuration,
|
||||
@@ -519,8 +519,9 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
|
||||
service,
|
||||
segments,
|
||||
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"]}`);
|
||||
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"]}`);
|
||||
return res.status(403).send(permission.reason);
|
||||
} 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
|
||||
@@ -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;
|
||||
|
||||
axios.post(webhookUrl, {
|
||||
@@ -676,6 +677,9 @@ function sendNewUserWebhook(webhookUrl: string, userID: HashedUserID, videoID: a
|
||||
"thumbnail": {
|
||||
"url": getMaxResThumbnail(videoID),
|
||||
},
|
||||
"footer": {
|
||||
"text": ruleName === undefined ? "Caught by permission check" : `Caught by rule '${ruleName}'`,
|
||||
},
|
||||
}],
|
||||
})
|
||||
.then(res => {
|
||||
|
||||
@@ -43,6 +43,7 @@ export interface CustomPostgresReadOnlyConfig extends CustomPostgresConfig {
|
||||
|
||||
export type ValidatorPattern = string | [string, string];
|
||||
export interface RequestValidatorRule {
|
||||
ruleName?: string;
|
||||
// mostly universal
|
||||
userAgent?: ValidatorPattern;
|
||||
userAgentHeader?: ValidatorPattern;
|
||||
|
||||
@@ -24,7 +24,8 @@ export interface RequestValidatorInput {
|
||||
newUsername?: 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 InputExtractor = (
|
||||
input: RequestValidatorInput,
|
||||
@@ -46,7 +47,7 @@ function patternToRegex(pattern: ValidatorPattern): RegExp {
|
||||
function compilePattern(
|
||||
pattern: ValidatorPattern,
|
||||
extractor: InputExtractor,
|
||||
): CompiledValidityCheck {
|
||||
): CompiledPatternCheck {
|
||||
const regex = patternToRegex(pattern);
|
||||
|
||||
return (input: RequestValidatorInput) => {
|
||||
@@ -72,11 +73,12 @@ function compileSegmentPattern(
|
||||
export function compileRules(
|
||||
ruleDefinitions: RequestValidatorRule[],
|
||||
): CompiledValidityCheck {
|
||||
if (ruleDefinitions.length === 0) return () => false;
|
||||
if (ruleDefinitions.length === 0) return () => null;
|
||||
|
||||
const rules: CompiledValidityCheck[] = [];
|
||||
let untitledRuleCounter = 0;
|
||||
for (const ruleDefinition of ruleDefinitions) {
|
||||
const ruleComponents: CompiledValidityCheck[] = [];
|
||||
const ruleComponents: CompiledPatternCheck[] = [];
|
||||
const segmentRuleComponents: CompiledSegmentCheck[] = [];
|
||||
for (const [ruleKey, rulePattern] of Object.entries(
|
||||
ruleDefinition,
|
||||
@@ -217,6 +219,9 @@ export function compileRules(
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "ruleName":
|
||||
// not a rule component
|
||||
break;
|
||||
default: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const _exhaustive: never = ruleKey;
|
||||
@@ -239,22 +244,24 @@ export function compileRules(
|
||||
return false;
|
||||
});
|
||||
}
|
||||
const ruleName = ruleDefinition.ruleName ?? `Untitled rule ${++untitledRuleCounter}`;
|
||||
rules.push((input) => {
|
||||
for (const rule of ruleComponents) {
|
||||
if (!rule(input)) return false;
|
||||
if (!rule(input)) return null;
|
||||
}
|
||||
return true;
|
||||
return ruleName;
|
||||
});
|
||||
}
|
||||
return (input) => {
|
||||
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);
|
||||
return compiledRules(input);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user