New Captcha Package added
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
import type {
|
||||
CaptchaPolicyOptions,
|
||||
CaptchaRiskResult,
|
||||
CaptchaRiskSignals,
|
||||
} from "./types.ts";
|
||||
|
||||
export function evaluateCaptchaRisk(
|
||||
signals: CaptchaRiskSignals,
|
||||
threshold = 50,
|
||||
): CaptchaRiskResult {
|
||||
let score = Math.max(0, Math.min(100, signals.customScore ?? 0));
|
||||
const reasons: string[] = [];
|
||||
const add = (points: number, reason: string): void => {
|
||||
score = Math.min(100, score + points);
|
||||
reasons.push(reason);
|
||||
};
|
||||
if ((signals.failedAttempts ?? 0) > 0) add(Math.min(35, (signals.failedAttempts ?? 0) * 10), "failed-attempts");
|
||||
if ((signals.requestsInWindow ?? 0) > 20) add(Math.min(35, ((signals.requestsInWindow ?? 0) - 20) * 2), "request-rate");
|
||||
if (signals.completionMs !== undefined && signals.completionMs < 700) add(25, "too-fast");
|
||||
if (signals.missingBrowserSignals) add(20, "missing-browser-signals");
|
||||
if (signals.suspiciousHeaders) add(20, "suspicious-headers");
|
||||
if (signals.tokenReuse) add(70, "token-reuse");
|
||||
if (signals.knownBadIp) add(60, "known-bad-ip");
|
||||
return { score, challenge: score >= threshold, reasons };
|
||||
}
|
||||
|
||||
export function shouldRequireCaptcha(
|
||||
action: string,
|
||||
options: CaptchaPolicyOptions = {},
|
||||
signals: CaptchaRiskSignals = {},
|
||||
): CaptchaRiskResult {
|
||||
if (options.neverForActions?.includes(action) || options.mode === "never") {
|
||||
return { score: 0, challenge: false, reasons: ["policy-never"] };
|
||||
}
|
||||
if (options.alwaysForActions?.includes(action) || options.mode === "always") {
|
||||
return { score: 100, challenge: true, reasons: ["policy-always"] };
|
||||
}
|
||||
if ((options.mode ?? "adaptive") === "session") {
|
||||
return { score: 100, challenge: true, reasons: ["session-unverified"] };
|
||||
}
|
||||
return evaluateCaptchaRisk(signals, options.threshold ?? 50);
|
||||
}
|
||||
|
||||
export interface CaptchaSessionGrant {
|
||||
action: string;
|
||||
routeGroup?: string;
|
||||
expiresAt: number;
|
||||
provider: string;
|
||||
}
|
||||
|
||||
export function validCaptchaGrant(
|
||||
grants: CaptchaSessionGrant[] | undefined,
|
||||
action: string,
|
||||
now: number,
|
||||
routeGroup?: string,
|
||||
): CaptchaSessionGrant | undefined {
|
||||
return grants?.find(
|
||||
(grant) =>
|
||||
grant.expiresAt > now &&
|
||||
(grant.action === action || grant.action === "*") &&
|
||||
(!routeGroup || !grant.routeGroup || grant.routeGroup === routeGroup),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user