Emdash source with visual editor image upload fix
Fixes: 1. media.ts: wrap placeholder generation in try-catch 2. toolbar.ts: check r.ok, display error message in popover
This commit is contained in:
625
packages/core/tests/unit/plugins/request-meta.test.ts
Normal file
625
packages/core/tests/unit/plugins/request-meta.test.ts
Normal file
@@ -0,0 +1,625 @@
|
||||
/**
|
||||
* Request Metadata Extraction Tests
|
||||
*
|
||||
* Tests for extractRequestMeta():
|
||||
* - IP resolution: CF-Connecting-IP (only with cf object), X-Forwarded-For fallback, null
|
||||
* - IP validation: rejects non-IP values (XSS payloads, garbage)
|
||||
* - Geo extraction from Cloudflare `cf` object on request
|
||||
* - User agent and referer header reads (trimmed)
|
||||
* - IPv6 support
|
||||
*/
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
import { _resetTrustedProxyHeadersCache } from "../../../src/auth/trusted-proxy.js";
|
||||
import {
|
||||
extractRequestMeta,
|
||||
sanitizeHeadersForSandbox,
|
||||
} from "../../../src/plugins/request-meta.js";
|
||||
|
||||
// Keep the env-derived trusted-header cache from leaking between tests
|
||||
// (and between this file and others in the same vitest worker).
|
||||
const ORIGINAL_TRUSTED_ENV = process.env.EMDASH_TRUSTED_PROXY_HEADERS;
|
||||
beforeEach(() => {
|
||||
delete process.env.EMDASH_TRUSTED_PROXY_HEADERS;
|
||||
_resetTrustedProxyHeadersCache();
|
||||
});
|
||||
afterEach(() => {
|
||||
if (ORIGINAL_TRUSTED_ENV === undefined) {
|
||||
delete process.env.EMDASH_TRUSTED_PROXY_HEADERS;
|
||||
} else {
|
||||
process.env.EMDASH_TRUSTED_PROXY_HEADERS = ORIGINAL_TRUSTED_ENV;
|
||||
}
|
||||
_resetTrustedProxyHeadersCache();
|
||||
});
|
||||
|
||||
/**
|
||||
* Helper to create a Request with optional headers and cf properties.
|
||||
*/
|
||||
function createRequest(
|
||||
opts: {
|
||||
headers?: Record<string, string>;
|
||||
cf?: { country?: string; region?: string; city?: string };
|
||||
} = {},
|
||||
): Request {
|
||||
const req = new Request("http://localhost/test", {
|
||||
headers: opts.headers,
|
||||
});
|
||||
|
||||
// Attach cf object if provided (simulates Cloudflare Workers runtime)
|
||||
if (opts.cf) {
|
||||
(req as unknown as { cf: typeof opts.cf }).cf = opts.cf;
|
||||
}
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
describe("extractRequestMeta", () => {
|
||||
describe("IP resolution", () => {
|
||||
it("trusts CF-Connecting-IP when cf object is present", () => {
|
||||
const req = createRequest({
|
||||
headers: {
|
||||
"cf-connecting-ip": "1.2.3.4",
|
||||
"x-forwarded-for": "5.6.7.8, 9.10.11.12",
|
||||
},
|
||||
cf: { country: "US" },
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.ip).toBe("1.2.3.4");
|
||||
});
|
||||
|
||||
it("ignores CF-Connecting-IP and XFF when no cf object (spoofed headers)", () => {
|
||||
const req = createRequest({
|
||||
headers: {
|
||||
"cf-connecting-ip": "1.2.3.4",
|
||||
"x-forwarded-for": "5.6.7.8, 9.10.11.12",
|
||||
},
|
||||
// No cf object — not on Cloudflare, XFF is untrusted
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
// Neither CF-Connecting-IP nor XFF should be trusted without cf object
|
||||
expect(meta.ip).toBeNull();
|
||||
});
|
||||
|
||||
it("returns null when CF-Connecting-IP is spoofed and no XFF", () => {
|
||||
const req = createRequest({
|
||||
headers: {
|
||||
"cf-connecting-ip": "1.2.3.4",
|
||||
},
|
||||
// No cf object
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.ip).toBeNull();
|
||||
});
|
||||
|
||||
it("falls back to X-Forwarded-For when behind Cloudflare (cf object present)", () => {
|
||||
const req = createRequest({
|
||||
headers: {
|
||||
"x-forwarded-for": "5.6.7.8, 9.10.11.12",
|
||||
},
|
||||
cf: {},
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.ip).toBe("5.6.7.8");
|
||||
});
|
||||
|
||||
it("ignores X-Forwarded-For without cf object (standalone deployment)", () => {
|
||||
const req = createRequest({
|
||||
headers: {
|
||||
"x-forwarded-for": "5.6.7.8, 9.10.11.12",
|
||||
},
|
||||
// No cf object — standalone deployment, XFF is spoofable
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.ip).toBeNull();
|
||||
});
|
||||
|
||||
it("handles single IP in X-Forwarded-For with cf object", () => {
|
||||
const req = createRequest({
|
||||
headers: {
|
||||
"x-forwarded-for": "5.6.7.8",
|
||||
},
|
||||
cf: {},
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.ip).toBe("5.6.7.8");
|
||||
});
|
||||
|
||||
it("trims whitespace from X-Forwarded-For entries", () => {
|
||||
const req = createRequest({
|
||||
headers: {
|
||||
"x-forwarded-for": " 5.6.7.8 , 9.10.11.12",
|
||||
},
|
||||
cf: {},
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.ip).toBe("5.6.7.8");
|
||||
});
|
||||
|
||||
it("trims whitespace from CF-Connecting-IP", () => {
|
||||
const req = createRequest({
|
||||
headers: {
|
||||
"cf-connecting-ip": " 1.2.3.4 ",
|
||||
},
|
||||
cf: {},
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.ip).toBe("1.2.3.4");
|
||||
});
|
||||
|
||||
it("returns null when no IP headers present", () => {
|
||||
const req = createRequest();
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.ip).toBeNull();
|
||||
});
|
||||
|
||||
it("returns null for empty CF-Connecting-IP with no X-Forwarded-For", () => {
|
||||
const req = createRequest({
|
||||
headers: {
|
||||
"cf-connecting-ip": "",
|
||||
},
|
||||
cf: {},
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.ip).toBeNull();
|
||||
});
|
||||
|
||||
it("falls back to X-Forwarded-For when CF-Connecting-IP is empty", () => {
|
||||
const req = createRequest({
|
||||
headers: {
|
||||
"cf-connecting-ip": "",
|
||||
"x-forwarded-for": "5.6.7.8",
|
||||
},
|
||||
cf: {},
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.ip).toBe("5.6.7.8");
|
||||
});
|
||||
});
|
||||
|
||||
describe("IPv6 support", () => {
|
||||
it("handles IPv6 loopback in X-Forwarded-For with cf object", () => {
|
||||
const req = createRequest({
|
||||
headers: { "x-forwarded-for": "::1" },
|
||||
cf: {},
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.ip).toBe("::1");
|
||||
});
|
||||
|
||||
it("handles full IPv6 address in X-Forwarded-For with cf object", () => {
|
||||
const req = createRequest({
|
||||
headers: { "x-forwarded-for": "2001:db8::1, 10.0.0.1" },
|
||||
cf: {},
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.ip).toBe("2001:db8::1");
|
||||
});
|
||||
|
||||
it("handles IPv6 in CF-Connecting-IP with cf object", () => {
|
||||
const req = createRequest({
|
||||
headers: { "cf-connecting-ip": "2001:db8:85a3::8a2e:370:7334" },
|
||||
cf: {},
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.ip).toBe("2001:db8:85a3::8a2e:370:7334");
|
||||
});
|
||||
});
|
||||
|
||||
describe("IP validation", () => {
|
||||
it("rejects XSS payload in X-Forwarded-For", () => {
|
||||
const req = createRequest({
|
||||
headers: { "x-forwarded-for": "<script>alert(1)</script>" },
|
||||
cf: {},
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.ip).toBeNull();
|
||||
});
|
||||
|
||||
it("rejects non-IP text in X-Forwarded-For", () => {
|
||||
const req = createRequest({
|
||||
headers: { "x-forwarded-for": "not-an-ip, 1.2.3.4" },
|
||||
cf: {},
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
// First entry is "not-an-ip" which fails validation
|
||||
expect(meta.ip).toBeNull();
|
||||
});
|
||||
|
||||
it("rejects XSS payload in CF-Connecting-IP", () => {
|
||||
const req = createRequest({
|
||||
headers: { "cf-connecting-ip": "<img onerror=alert(1)>" },
|
||||
cf: {},
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.ip).toBeNull();
|
||||
});
|
||||
|
||||
it("rejects empty-looking IP values with only whitespace", () => {
|
||||
const req = createRequest({
|
||||
headers: { "x-forwarded-for": " " },
|
||||
cf: {},
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.ip).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("geo extraction", () => {
|
||||
it("extracts geo from cf object", () => {
|
||||
const req = createRequest({
|
||||
cf: { country: "US", region: "CA", city: "San Francisco" },
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.geo).toEqual({
|
||||
country: "US",
|
||||
region: "CA",
|
||||
city: "San Francisco",
|
||||
});
|
||||
});
|
||||
|
||||
it("returns null geo when no cf object", () => {
|
||||
const req = createRequest();
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.geo).toBeNull();
|
||||
});
|
||||
|
||||
it("handles partial geo data", () => {
|
||||
const req = createRequest({
|
||||
cf: { country: "GB" },
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.geo).toEqual({
|
||||
country: "GB",
|
||||
region: null,
|
||||
city: null,
|
||||
});
|
||||
});
|
||||
|
||||
it("returns null geo when cf object has no geo fields", () => {
|
||||
const req = createRequest({
|
||||
cf: {},
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.geo).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("user agent", () => {
|
||||
it("extracts user agent from header", () => {
|
||||
const req = createRequest({
|
||||
headers: {
|
||||
"user-agent": "Mozilla/5.0 (Test)",
|
||||
},
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.userAgent).toBe("Mozilla/5.0 (Test)");
|
||||
});
|
||||
|
||||
it("returns null when no user agent header", () => {
|
||||
const req = createRequest();
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.userAgent).toBeNull();
|
||||
});
|
||||
|
||||
it("returns null for empty user agent header", () => {
|
||||
const req = createRequest({
|
||||
headers: { "user-agent": "" },
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.userAgent).toBeNull();
|
||||
});
|
||||
|
||||
it("trims whitespace from user agent", () => {
|
||||
const req = createRequest({
|
||||
headers: { "user-agent": " TestBot/1.0 " },
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.userAgent).toBe("TestBot/1.0");
|
||||
});
|
||||
});
|
||||
|
||||
describe("referer", () => {
|
||||
it("extracts referer from header", () => {
|
||||
const req = createRequest({
|
||||
headers: {
|
||||
referer: "https://example.com/page",
|
||||
},
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.referer).toBe("https://example.com/page");
|
||||
});
|
||||
|
||||
it("returns null when no referer header", () => {
|
||||
const req = createRequest();
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.referer).toBeNull();
|
||||
});
|
||||
|
||||
it("returns null for empty referer header", () => {
|
||||
const req = createRequest({
|
||||
headers: { referer: "" },
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.referer).toBeNull();
|
||||
});
|
||||
|
||||
it("trims whitespace from referer", () => {
|
||||
const req = createRequest({
|
||||
headers: { referer: " https://example.com " },
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta.referer).toBe("https://example.com");
|
||||
});
|
||||
});
|
||||
|
||||
describe("sanitizeHeadersForSandbox", () => {
|
||||
it("strips cookie header", () => {
|
||||
const headers = new Headers({ cookie: "session=abc123", "content-type": "text/html" });
|
||||
const result = sanitizeHeadersForSandbox(headers);
|
||||
expect(result).not.toHaveProperty("cookie");
|
||||
expect(result["content-type"]).toBe("text/html");
|
||||
});
|
||||
|
||||
it("strips set-cookie header", () => {
|
||||
const headers = new Headers({ "set-cookie": "token=xyz", accept: "application/json" });
|
||||
const result = sanitizeHeadersForSandbox(headers);
|
||||
expect(result).not.toHaveProperty("set-cookie");
|
||||
expect(result.accept).toBe("application/json");
|
||||
});
|
||||
|
||||
it("strips authorization header", () => {
|
||||
const headers = new Headers({ authorization: "Bearer secret-token", host: "example.com" });
|
||||
const result = sanitizeHeadersForSandbox(headers);
|
||||
expect(result).not.toHaveProperty("authorization");
|
||||
expect(result.host).toBe("example.com");
|
||||
});
|
||||
|
||||
it("strips proxy-authorization header", () => {
|
||||
const headers = new Headers({ "proxy-authorization": "Basic abc", host: "example.com" });
|
||||
const result = sanitizeHeadersForSandbox(headers);
|
||||
expect(result).not.toHaveProperty("proxy-authorization");
|
||||
});
|
||||
|
||||
it("strips Cloudflare Access headers", () => {
|
||||
const headers = new Headers({
|
||||
"cf-access-jwt-assertion": "jwt-token",
|
||||
"cf-access-client-id": "client-id",
|
||||
"cf-access-client-secret": "client-secret",
|
||||
"cf-ray": "abc123",
|
||||
});
|
||||
const result = sanitizeHeadersForSandbox(headers);
|
||||
expect(result).not.toHaveProperty("cf-access-jwt-assertion");
|
||||
expect(result).not.toHaveProperty("cf-access-client-id");
|
||||
expect(result).not.toHaveProperty("cf-access-client-secret");
|
||||
expect(result["cf-ray"]).toBe("abc123");
|
||||
});
|
||||
|
||||
it("strips x-emdash-request CSRF header", () => {
|
||||
const headers = new Headers({ "x-emdash-request": "1", "x-custom": "safe" });
|
||||
const result = sanitizeHeadersForSandbox(headers);
|
||||
expect(result).not.toHaveProperty("x-emdash-request");
|
||||
expect(result["x-custom"]).toBe("safe");
|
||||
});
|
||||
|
||||
it("passes through safe headers unchanged", () => {
|
||||
const headers = new Headers({
|
||||
"content-type": "application/json",
|
||||
accept: "text/html",
|
||||
"user-agent": "TestBot/1.0",
|
||||
"x-forwarded-for": "1.2.3.4",
|
||||
"cf-connecting-ip": "5.6.7.8",
|
||||
});
|
||||
const result = sanitizeHeadersForSandbox(headers);
|
||||
expect(result["content-type"]).toBe("application/json");
|
||||
expect(result.accept).toBe("text/html");
|
||||
expect(result["user-agent"]).toBe("TestBot/1.0");
|
||||
expect(result["x-forwarded-for"]).toBe("1.2.3.4");
|
||||
expect(result["cf-connecting-ip"]).toBe("5.6.7.8");
|
||||
});
|
||||
|
||||
it("returns empty object for headers that are all sensitive", () => {
|
||||
const headers = new Headers({
|
||||
cookie: "session=abc",
|
||||
authorization: "Bearer token",
|
||||
});
|
||||
const result = sanitizeHeadersForSandbox(headers);
|
||||
expect(Object.keys(result)).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("returns empty object for empty headers", () => {
|
||||
const headers = new Headers();
|
||||
const result = sanitizeHeadersForSandbox(headers);
|
||||
expect(Object.keys(result)).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("full extraction", () => {
|
||||
it("extracts all metadata from a fully-populated request", () => {
|
||||
const req = createRequest({
|
||||
headers: {
|
||||
"cf-connecting-ip": "203.0.113.50",
|
||||
"user-agent": "TestBot/1.0",
|
||||
referer: "https://example.com",
|
||||
},
|
||||
cf: { country: "DE", region: "BE", city: "Berlin" },
|
||||
});
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta).toEqual({
|
||||
ip: "203.0.113.50",
|
||||
userAgent: "TestBot/1.0",
|
||||
referer: "https://example.com",
|
||||
geo: {
|
||||
country: "DE",
|
||||
region: "BE",
|
||||
city: "Berlin",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("returns all nulls for a bare request", () => {
|
||||
const req = createRequest();
|
||||
|
||||
const meta = extractRequestMeta(req);
|
||||
expect(meta).toEqual({
|
||||
ip: null,
|
||||
userAgent: null,
|
||||
referer: null,
|
||||
geo: null,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Trusted proxy headers — operator-declared headers for self-hosted
|
||||
// deployments behind a reverse proxy they control.
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
describe("trusted proxy headers", () => {
|
||||
it("reads the IP from a trusted header on a non-CF deployment", () => {
|
||||
const req = createRequest({
|
||||
headers: { "x-real-ip": "203.0.113.50" },
|
||||
});
|
||||
const meta = extractRequestMeta(req, { trustedProxyHeaders: ["x-real-ip"] });
|
||||
expect(meta.ip).toBe("203.0.113.50");
|
||||
});
|
||||
|
||||
it("tries trusted headers in order", () => {
|
||||
const req = createRequest({
|
||||
headers: {
|
||||
"x-real-ip": "203.0.113.50",
|
||||
"fly-client-ip": "198.51.100.7",
|
||||
},
|
||||
});
|
||||
const meta = extractRequestMeta(req, {
|
||||
trustedProxyHeaders: ["fly-client-ip", "x-real-ip"],
|
||||
});
|
||||
expect(meta.ip).toBe("198.51.100.7");
|
||||
});
|
||||
|
||||
it("falls through to the next trusted header when the first is missing", () => {
|
||||
const req = createRequest({
|
||||
headers: { "x-real-ip": "203.0.113.50" },
|
||||
});
|
||||
const meta = extractRequestMeta(req, {
|
||||
trustedProxyHeaders: ["fly-client-ip", "x-real-ip"],
|
||||
});
|
||||
expect(meta.ip).toBe("203.0.113.50");
|
||||
});
|
||||
|
||||
it("treats trusted XFF-style headers as comma-separated and takes the first entry", () => {
|
||||
const req = createRequest({
|
||||
headers: {
|
||||
"x-forwarded-for": "203.0.113.50, 10.0.0.1, 10.0.0.2",
|
||||
},
|
||||
});
|
||||
const meta = extractRequestMeta(req, {
|
||||
trustedProxyHeaders: ["x-forwarded-for"],
|
||||
});
|
||||
expect(meta.ip).toBe("203.0.113.50");
|
||||
});
|
||||
|
||||
it("rejects non-IP-shaped values in trusted headers", () => {
|
||||
const req = createRequest({
|
||||
headers: { "x-real-ip": "<script>alert(1)</script>" },
|
||||
});
|
||||
const meta = extractRequestMeta(req, { trustedProxyHeaders: ["x-real-ip"] });
|
||||
expect(meta.ip).toBeNull();
|
||||
});
|
||||
|
||||
it("matches header names case-insensitively", () => {
|
||||
const req = createRequest({
|
||||
headers: { "X-Real-IP": "203.0.113.50" },
|
||||
});
|
||||
const meta = extractRequestMeta(req, { trustedProxyHeaders: ["x-real-ip"] });
|
||||
expect(meta.ip).toBe("203.0.113.50");
|
||||
});
|
||||
|
||||
it("does not read from headers that are not on the trusted list", () => {
|
||||
// "x-client-ip" is not declared trusted — must not be used.
|
||||
const req = createRequest({
|
||||
headers: { "x-client-ip": "203.0.113.50" },
|
||||
});
|
||||
const meta = extractRequestMeta(req, { trustedProxyHeaders: ["x-real-ip"] });
|
||||
expect(meta.ip).toBeNull();
|
||||
});
|
||||
|
||||
it("CF-Connecting-IP wins over trusted headers on Cloudflare", () => {
|
||||
// On CF, cf-connecting-ip is cryptographically trustworthy (CF edge
|
||||
// overwrites any client-supplied value). Operator-declared trusted
|
||||
// headers only apply as a fallback, not an override, so a wrong
|
||||
// entry in the config can't regress CF deployments.
|
||||
const req = createRequest({
|
||||
headers: {
|
||||
"cf-connecting-ip": "1.1.1.1",
|
||||
"x-real-ip": "203.0.113.50",
|
||||
},
|
||||
cf: {},
|
||||
});
|
||||
const meta = extractRequestMeta(req, { trustedProxyHeaders: ["x-real-ip"] });
|
||||
expect(meta.ip).toBe("1.1.1.1");
|
||||
});
|
||||
|
||||
it("trusted headers are used as fallback when CF headers are absent", () => {
|
||||
const req = createRequest({
|
||||
headers: {
|
||||
"x-real-ip": "203.0.113.50",
|
||||
},
|
||||
cf: {},
|
||||
});
|
||||
const meta = extractRequestMeta(req, { trustedProxyHeaders: ["x-real-ip"] });
|
||||
expect(meta.ip).toBe("203.0.113.50");
|
||||
});
|
||||
|
||||
it("falls back to the CF path when no trusted header matches", () => {
|
||||
const req = createRequest({
|
||||
headers: { "cf-connecting-ip": "1.1.1.1" },
|
||||
cf: {},
|
||||
});
|
||||
const meta = extractRequestMeta(req, { trustedProxyHeaders: ["x-real-ip"] });
|
||||
expect(meta.ip).toBe("1.1.1.1");
|
||||
});
|
||||
|
||||
it("validates a pre-resolved string[] of trusted headers", () => {
|
||||
// Passing the array form (used by the plugin runtime with a list
|
||||
// pre-resolved from the config) must not trust an invalid header
|
||||
// name — headers.get() would throw a TypeError.
|
||||
const req = createRequest({
|
||||
headers: { "x-real-ip": "203.0.113.50" },
|
||||
});
|
||||
const meta = extractRequestMeta(req, ["bad name", " x-real-ip ", "bad:colon"]);
|
||||
expect(meta.ip).toBe("203.0.113.50");
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user