Skip to content

Commit b452ed8

Browse files
authored
allow 'non-strict' cookie matching. any token of the format (#25)
for example, any cookie of the format recaptcha-*-t will match in non-struct mode. This allows cross-waf keys to be used, eg. for testing.
1 parent 799a706 commit b452ed8

File tree

3 files changed

+89
-8
lines changed

3 files changed

+89
-8
lines changed

src/createAssessment.ts

+22-4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import * as action from "./action";
2222
import { Assessment, AssessmentSchema, Event, EventSchema, RpcErrorSchema } from "./assessment";
2323
import * as error from "./error";
2424
import { RecaptchaContext } from "./index";
25+
import picomatch from "picomatch";
2526

2627
/**
2728
* Adds reCAPTCHA specific values to an Event strucutre.
@@ -40,13 +41,30 @@ export function createPartialEventWithSiteInfo(
4041
context.log("debug", "siteKind: action");
4142
} else {
4243
const cookieMap = new Map<string, string>();
44+
var sessionToken: string | undefined;
45+
var challengeToken: string | undefined;
4346
for (const cookie of req.headers.get("cookie")?.split(";") ?? []) {
4447
const [key, value] = cookie.split("=");
4548
cookieMap.set(key.trim(), value.trim());
49+
50+
// Non-strict cookie parsing will match any 'recaptcha-*-t' token.
51+
// This is useful for using an existing key in a different WAF than registered
52+
// specifically for testing.
53+
if (!context.config.strict_cookie) {
54+
if (picomatch.isMatch(key.trim(), "recaptcha-*-t")) {
55+
sessionToken = value.trim();
56+
} else if (picomatch.isMatch(key.trim(), "recaptcha-*-e")) {
57+
challengeToken = value.trim();
58+
}
59+
}
4660
}
4761

48-
const sessionToken = cookieMap.get(context.sessionPageCookie);
49-
const challengeToken = cookieMap.get(context.challengePageCookie);
62+
if (!sessionToken) {
63+
sessionToken = cookieMap.get(context.sessionPageCookie);
64+
}
65+
if (!challengeToken) {
66+
challengeToken = cookieMap.get(context.challengePageCookie);
67+
}
5068
if (context.config.debug) {
5169
for (const [key, value] of cookieMap.entries()) {
5270
if (
@@ -65,12 +83,12 @@ export function createPartialEventWithSiteInfo(
6583
}
6684

6785
if (context.config.sessionSiteKey && sessionToken) {
68-
event.token = cookieMap.get(context.sessionPageCookie);
86+
event.token = sessionToken;
6987
event.siteKey = context.config.sessionSiteKey;
7088
event.wafTokenAssessment = true;
7189
context.log("debug", "siteKind: session");
7290
} else if (context.config.challengePageSiteKey && challengeToken) {
73-
event.token = cookieMap.get(context.challengePageCookie);
91+
event.token = challengeToken;
7492
event.siteKey = context.config.challengePageSiteKey;
7593
event.wafTokenAssessment = true;
7694
context.log("debug", "siteKind: challenge");

src/index.test.ts

+66-4
Original file line numberDiff line numberDiff line change
@@ -929,7 +929,7 @@ test("processRequest-raise", async () => {
929929
expect(fetch).toHaveBeenCalledTimes(3);
930930
});
931931

932-
test("insertFeaturesIntoEvent-actionToken", () => {
932+
test("createPartialEventWithSiteInfo-actionToken", () => {
933933
const context = new TestContext(testConfig);
934934
const req = new Request("https://www.example.com/teste2e", {
935935
headers: { "X-Recaptcha-Token": "action-token" },
@@ -949,7 +949,7 @@ test("insertFeaturesIntoEvent-actionToken", () => {
949949
});
950950
});
951951

952-
test("insertFeaturesIntoEvent-sessionToken", () => {
952+
test("createPartialEventWithSiteInfo-sessionToken", () => {
953953
const context = new TestContext(testConfig);
954954
const req = new Request("https://www.example.com/test", {
955955
headers: { cookie: "recaptcha-test-t=session-token" },
@@ -969,7 +969,48 @@ test("insertFeaturesIntoEvent-sessionToken", () => {
969969
});
970970
});
971971

972-
test("insertFeaturesIntoEvent-challengeToken", () => {
972+
test("createPartialEventWithSiteInfo-strictSessionToken", () => {
973+
const context = new TestContext(testConfig);
974+
context.config.strict_cookie = true;
975+
const req = new Request("https://www.example.com/test", {
976+
headers: { cookie: "recaptcha-example-t=session-token" },
977+
});
978+
const site_info = createPartialEventWithSiteInfo(context, req);
979+
const site_features = EventSchema.parse(context.buildEvent(req));
980+
const event = {
981+
...site_info,
982+
...site_features,
983+
};
984+
expect(event).toEqual({
985+
siteKey: "express-site-key",
986+
express: true,
987+
userAgent: "test-user-agent",
988+
userIpAddress: "1.2.3.4",
989+
});
990+
});
991+
992+
test("createPartialEventWithSiteInfo-nonStrictSessionToken", () => {
993+
const context = new TestContext(testConfig);
994+
context.config.strict_cookie = false;
995+
const req = new Request("https://www.example.com/test", {
996+
headers: { cookie: "recaptcha-example-t=session-token" },
997+
});
998+
const site_info = createPartialEventWithSiteInfo(context, req);
999+
const site_features = EventSchema.parse(context.buildEvent(req));
1000+
const event = {
1001+
...site_info,
1002+
...site_features,
1003+
};
1004+
expect(event).toEqual({
1005+
token: "session-token",
1006+
siteKey: "session-site-key",
1007+
userAgent: "test-user-agent",
1008+
wafTokenAssessment: true,
1009+
userIpAddress: "1.2.3.4",
1010+
});
1011+
});
1012+
1013+
test("createPartialEventWithSiteInfo-challengeToken", () => {
9731014
const context = new TestContext(testConfig);
9741015
const req = new Request("https://www.example.com/test", {
9751016
headers: { cookie: "recaptcha-test-e=challenge-token" },
@@ -989,7 +1030,28 @@ test("insertFeaturesIntoEvent-challengeToken", () => {
9891030
});
9901031
});
9911032

992-
test("insertFeaturesIntoEvent-express", () => {
1033+
test("createPartialEventWithSiteInfo-nonStrictChallengeToken", () => {
1034+
const context = new TestContext(testConfig);
1035+
context.config.strict_cookie = false;
1036+
const req = new Request("https://www.example.com/test", {
1037+
headers: { cookie: "recaptcha-example-e=challenge-token" },
1038+
});
1039+
const site_info = createPartialEventWithSiteInfo(context, req);
1040+
const site_features = EventSchema.parse(context.buildEvent(req));
1041+
const event = {
1042+
...site_info,
1043+
...site_features,
1044+
};
1045+
expect(event).toEqual({
1046+
token: "challenge-token",
1047+
siteKey: "challenge-page-site-key",
1048+
userAgent: "test-user-agent",
1049+
wafTokenAssessment: true,
1050+
userIpAddress: "1.2.3.4",
1051+
});
1052+
});
1053+
1054+
test("createPartialEventWithSiteInfo-express", () => {
9931055
const context = new TestContext(testConfig);
9941056
const req = new Request("https://www.example.com/test", {});
9951057
const site_info = createPartialEventWithSiteInfo(context, req);

src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ export interface RecaptchaConfig {
7676
sessionJsInjectPath?: string;
7777
recaptchaEndpoint: string;
7878
debug?: boolean;
79+
strict_cookie?: boolean;
7980
}
8081

8182
export type LogLevel = "debug" | "info" | "warning" | "error";

0 commit comments

Comments
 (0)