Skip to content

Commit

Permalink
feat: Support cookies and query via the protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
blaine-arcjet committed Feb 8, 2024
1 parent c2d3dd0 commit 43fdf71
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 16 deletions.
81 changes: 73 additions & 8 deletions arcjet-next/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,44 @@ export interface ArcjetNextRequest {

ip?: string;

nextUrl?: Partial<{ pathname: string; search: string }>;
nextUrl?: Partial<{ pathname: string; search: string; protocol: string }>;

cookies?:
| {
[Symbol.iterator](): IterableIterator<
[string, { name: string; value: string }]
>;
}
| Partial<{ [key: string]: string }>;
}

function isIterable(val: any): val is Iterable<any> {
return typeof val?.[Symbol.iterator] === "function";
}

function cookiesToArray(
cookies?: ArcjetNextRequest["cookies"],
): { name: string; value: string }[] {
if (typeof cookies === "undefined") {
return [];
}

if (isIterable(cookies)) {
return Array.from(cookies).map(([_, cookie]) => cookie);
} else {
return Object.entries(cookies).map(([name, value]) => ({
name,
value: value ?? "",
}));
}
}

function cookiesToString(cookies?: ArcjetNextRequest["cookies"]): string {
// This is essentially the implementation of `RequestCookies#toString` in
// Next.js but normalized for NextApiRequest cookies object
return cookiesToArray(cookies)
.map((v) => `${v.name}=${encodeURIComponent(v.value)}`)
.join("; ");
}

export interface ArcjetNext<Props extends PlainObject> {
Expand Down Expand Up @@ -180,16 +217,42 @@ export default function arcjetNext<const Rules extends (Primitive | Product)[]>(
const ip = findIP(request, headers);
const method = request.method ?? "";
const host = headers.get("host") ?? "";
let path;
// TODO(#36): nextUrl has formatting logic when you `toString` but we don't account for that here
let path = "";
let query = "";
let protocol = "";
// TODO(#36): nextUrl has formatting logic when you `toString` but
// we don't account for that here
if (typeof request.nextUrl !== "undefined") {
path = request.nextUrl.pathname;
if (request.nextUrl.search !== "") {
path += "?" + request.nextUrl.search;
path = request.nextUrl.pathname ?? "";
if (typeof request.nextUrl.search !== "undefined") {
query = request.nextUrl.search;
}
if (typeof request.nextUrl.protocol !== "undefined") {
protocol = request.nextUrl.protocol
}
} else {
path = request.url ?? "";
// Do some very simple validation, but also try/catch around URL parsing
if (
typeof request.url !== "undefined" &&
request.url !== "" &&
host !== ""
) {
try {
const url = new URL(request.url, `http://${host}`);
path = url.pathname;
query = url.search;
protocol = url.protocol;
} catch {
// If the parsing above fails, just set the path as whatever url we
// received.
// TODO: Maybe add some local logging
path = request.url ?? "";
}
} else {
path = request.url ?? "";
}
}
const cookies = cookiesToString(request.cookies);

const extra: { [key: string]: string } = {};

Expand All @@ -213,10 +276,12 @@ export default function arcjetNext<const Rules extends (Primitive | Product)[]>(
...props,
ip,
method,
protocol: "",
protocol,
host,
path,
headers,
cookies,
query,
...extra,
// TODO(#220): The generic manipulations get really mad here, so we just cast it
} as ArcjetRequest<ExtraProps<Rules>>);
Expand Down
2 changes: 2 additions & 0 deletions arcjet/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,8 @@ export function createRemoteClient(
host: details.host,
path: details.path,
headers: Object.fromEntries(details.headers.entries()),
cookies: details.cookies,
query: details.query,
// TODO(#208): Re-add body
// body: details.body,
extra: extraProps(details),
Expand Down
40 changes: 32 additions & 8 deletions arcjet/test/index.node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1611,6 +1611,8 @@ describe("Primitive > detectBot", () => {
host: "example.com",
path: "/",
headers: new Headers(),
cookies: "",
query: "",
extra: {},
};

Expand Down Expand Up @@ -1658,6 +1660,8 @@ describe("Primitive > detectBot", () => {
"Mozilla/5.0 (Macintosh; Intel Mac OS X 13_5_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Safari/605.1.15",
],
]),
cookies: "",
query: "",
"extra-test": "extra-test-value",
};

Expand Down Expand Up @@ -1708,6 +1712,8 @@ describe("Primitive > detectBot", () => {
"Mozilla/5.0 (Macintosh; Intel Mac OS X 13_5_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Safari/605.1.15",
],
]),
cookies: "",
query: "",
"extra-test": "extra-test-value",
};

Expand Down Expand Up @@ -1758,6 +1764,8 @@ describe("Primitive > detectBot", () => {
"Mozilla/5.0 (Macintosh; Intel Mac OS X 13_5_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Safari/605.1.15",
],
]),
cookies: "",
query: "",
"extra-test": "extra-test-value",
};

Expand Down Expand Up @@ -1795,6 +1803,8 @@ describe("Primitive > detectBot", () => {
"Mozilla/5.0 (Macintosh; Intel Mac OS X 13_5_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Safari/605.1.15",
],
]),
cookies: "",
query: "",
"extra-test": "extra-test-value",
};

Expand Down Expand Up @@ -1849,6 +1859,8 @@ describe("Primitive > detectBot", () => {
host: "example.com",
path: "/",
headers: new Headers([["User-Agent", "curl/8.1.2"]]),
cookies: "",
query: "",
"extra-test": "extra-test-value",
};

Expand Down Expand Up @@ -1898,6 +1910,8 @@ describe("Primitive > detectBot", () => {
"Mozilla/5.0 (Macintosh; Intel Mac OS X 13_5_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Safari/605.1.15",
],
]),
cookies: "",
query: "",
"extra-test": "extra-test-value",
};

Expand Down Expand Up @@ -1936,6 +1950,8 @@ describe("Primitive > detectBot", () => {
host: "example.com",
path: "/",
headers: new Headers([["User-Agent", "curl/8.1.2"]]),
cookies: "",
query: "",
"extra-test": "extra-test-value",
};

Expand Down Expand Up @@ -2783,7 +2799,8 @@ describe("Primitive > validateEmail", () => {
host: "example.com",
path: "/",
headers: new Headers(),
extra: {},
cookies: "",
query: "",
email: "[email protected]",
};

Expand Down Expand Up @@ -2813,7 +2830,8 @@ describe("Primitive > validateEmail", () => {
host: "example.com",
path: "/",
headers: new Headers(),
extra: {},
cookies: "",
query: "",
email: "foobarbaz",
};

Expand Down Expand Up @@ -2843,7 +2861,8 @@ describe("Primitive > validateEmail", () => {
host: "example.com",
path: "/",
headers: new Headers(),
extra: {},
cookies: "",
query: "",
email: "foobarbaz@localhost",
};

Expand Down Expand Up @@ -2873,7 +2892,8 @@ describe("Primitive > validateEmail", () => {
host: "example.com",
path: "/",
headers: new Headers(),
extra: {},
cookies: "",
query: "",
email: "foobarbaz@localhost",
};

Expand Down Expand Up @@ -2905,7 +2925,8 @@ describe("Primitive > validateEmail", () => {
host: "example.com",
path: "/",
headers: new Headers(),
extra: {},
cookies: "",
query: "",
email: "@example.com",
};

Expand Down Expand Up @@ -2935,7 +2956,8 @@ describe("Primitive > validateEmail", () => {
host: "example.com",
path: "/",
headers: new Headers(),
extra: {},
cookies: "",
query: "",
email: "foobarbaz@[127.0.0.1]",
};

Expand Down Expand Up @@ -2965,7 +2987,8 @@ describe("Primitive > validateEmail", () => {
host: "example.com",
path: "/",
headers: new Headers(),
extra: {},
cookies: "",
query: "",
email: "foobarbaz@localhost",
};

Expand Down Expand Up @@ -2997,7 +3020,8 @@ describe("Primitive > validateEmail", () => {
host: "example.com",
path: "/",
headers: new Headers(),
extra: {},
cookies: "",
query: "",
email: "foobarbaz@[127.0.0.1]",
};

Expand Down
2 changes: 2 additions & 0 deletions protocol/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,8 @@ export interface ArcjetRequestDetails {
path: string;
// TODO(#215): Allow `Record<string, string>` and `Record<string, string[]>`?
headers: Headers;
cookies: string;
query: string;
}

export type ArcjetRule<Props extends {} = {}> = {
Expand Down

0 comments on commit 43fdf71

Please sign in to comment.