Skip to content
File

Blob: tests/worker/security/same-origin.test.ts

typescript43 lines
1import { env } from "cloudflare:workers";
2import { Hono } from "hono";
3import { describe, expect, it } from "vitest";
4 
5import type { AppEnv } from "@/worker/hono";
6import { HttpError, toErrorResponse } from "@/worker/http";
7import { requireSameOrigin } from "@/worker/security/same-origin";
8 
9const buildApp = () => {
10 const app = new Hono<AppEnv>();
11 app.use("*", requireSameOrigin);
12 app.all("/guarded", (c) => c.json({ ok: true }));
13 app.onError((error, c) => toErrorResponse(c, error instanceof Error ? error : new HttpError(500, "error", "error")));
14 return app;
15};
16 
17const fetchGuarded = async (method: string, headers?: HeadersInit): Promise<Response> =>
18 await buildApp().fetch(new Request("https://example.com/guarded", { method, headers }), env);
19 
20describe("same-origin guard", () => {
21 it("skips safe methods", async () => {
22 expect((await fetchGuarded("GET")).status).toBe(200);
23 });
24 
25 it("allows same-origin Origin and trusted Fetch Metadata", async () => {
26 expect((await fetchGuarded("POST", { origin: "https://example.com" })).status).toBe(200);
27 expect((await fetchGuarded("POST", { "sec-fetch-site": "none" })).status).toBe(200);
28 });
29 
30 it("rejects missing and cross-origin unsafe requests", async () => {
31 const missingOrigin = await fetchGuarded("POST");
32 expect(missingOrigin.status).toBe(403);
33 
34 const crossOrigin = await fetchGuarded("POST", { origin: "https://evil.example" });
35 expect(crossOrigin.status).toBe(403);
36 await expect(crossOrigin.json()).resolves.toMatchObject({
37 error: {
38 code: "cross_origin_blocked",
39 },
40 });
41 });
42});