this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(images): Implement local patterns for images (#740)

authored by

Magnus and committed by
GitHub
d8150d20 b911c3e7

+386 -259
+5
.changeset/shaky-kings-stare.md
··· 1 + --- 2 + "@opennextjs/cloudflare": patch 3 + --- 4 + 5 + feat(images): implement localPatterns for images
+31 -7
examples/playground14/e2e/cloudflare.spec.ts
··· 19 19 await expect(res.json()).resolves.toEqual(expect.objectContaining({ PROCESS_ENV_VAR: "process.env" })); 20 20 }); 21 21 22 - test("fetch an image allowed by remotePatterns", async ({ page }) => { 23 - const res = await page.request.get("/_next/image?url=https://avatars.githubusercontent.com/u/248818"); 24 - expect(res.status()).toBe(200); 25 - expect(res.headers()).toMatchObject({ "content-type": "image/jpeg" }); 22 + test.describe("remotePatterns", () => { 23 + test("fetch an image allowed by remotePatterns", async ({ page }) => { 24 + const res = await page.request.get("/_next/image?url=https://avatars.githubusercontent.com/u/248818"); 25 + expect(res.status()).toBe(200); 26 + expect(res.headers()).toMatchObject({ "content-type": "image/jpeg" }); 27 + }); 28 + 29 + test("400 when fetching an image disallowed by remotePatterns", async ({ page }) => { 30 + const res = await page.request.get("/_next/image?url=https://avatars.githubusercontent.com/u/248817"); 31 + expect(res.status()).toBe(400); 32 + }); 26 33 }); 27 34 28 - test("404 when fetching an image disallowed by remotePatterns", async ({ page }) => { 29 - const res = await page.request.get("/_next/image?url=https://avatars.githubusercontent.com/u/248817"); 30 - expect(res.status()).toBe(400); 35 + test.describe("localPatterns", () => { 36 + test("fetch an image allowed by localPatterns", async ({ page }) => { 37 + const res = await page.request.get("/_next/image?url=/snipp/snipp.webp?iscute=yes"); 38 + expect(res.status()).toBe(200); 39 + expect(res.headers()).toMatchObject({ "content-type": "image/webp" }); 40 + }); 41 + 42 + test("400 when fetching an image disallowed by localPatterns with wrong query parameter", async ({ 43 + page, 44 + }) => { 45 + const res = await page.request.get("/_next/image?url=/snipp/snipp?iscute=no"); 46 + expect(res.status()).toBe(400); 47 + }); 48 + 49 + test("400 when fetching an image disallowed by localPatterns without query parameter", async ({ 50 + page, 51 + }) => { 52 + const res = await page.request.get("/_next/image?url=/snipp/snipp"); 53 + expect(res.status()).toBe(400); 54 + }); 31 55 }); 32 56 });
+6
examples/playground14/next.config.mjs
··· 19 19 pathname: "/u/248818", 20 20 }, 21 21 ], 22 + localPatterns: [ 23 + { 24 + pathname: "/snipp/**", 25 + search: "?iscute=yes", 26 + }, 27 + ], 22 28 }, 23 29 }; 24 30
examples/playground14/public/snipp/snipp.webp

This is a binary file and will not be displayed.

+315 -250
packages/cloudflare/src/cli/templates/images.spec.ts
··· 1 - /** 2 - * See https://github.com/vercel/next.js/blob/64702a9/test/unit/image-optimizer/match-remote-pattern.test.ts 3 - */ 4 - 5 1 import pm from "picomatch"; 6 2 import { describe, expect, it } from "vitest"; 7 3 8 - import { matchRemotePattern as m } from "./images.js"; 4 + import type { LocalPattern } from "./images.js"; 5 + import { matchLocalPattern, matchRemotePattern as mRP } from "./images.js"; 9 6 7 + /** 8 + * See https://github.com/vercel/next.js/blob/64702a9/test/unit/image-optimizer/match-remote-pattern.test.ts 9 + */ 10 10 describe("matchRemotePattern", () => { 11 11 it("should match literal hostname", () => { 12 12 const p = { hostname: pm.makeRe("example.com") } as const; 13 - expect(m(p, new URL("https://example.com"))).toBe(true); 14 - expect(m(p, new URL("https://example.com.uk"))).toBe(false); 15 - expect(m(p, new URL("https://example.net"))).toBe(false); 16 - expect(m(p, new URL("https://sub.example.com"))).toBe(false); 17 - expect(m(p, new URL("https://com"))).toBe(false); 18 - expect(m(p, new URL("https://example.com/path"))).toBe(true); 19 - expect(m(p, new URL("https://example.com/path/to"))).toBe(true); 20 - expect(m(p, new URL("https://example.com/path/to/file"))).toBe(true); 21 - expect(m(p, new URL("https://example.com:81/path/to/file"))).toBe(true); 22 - expect(m(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(true); 23 - expect(m(p, new URL("http://example.com:81/path/to/file"))).toBe(true); 13 + expect(mRP(p, new URL("https://example.com"))).toBe(true); 14 + expect(mRP(p, new URL("https://example.com.uk"))).toBe(false); 15 + expect(mRP(p, new URL("https://example.net"))).toBe(false); 16 + expect(mRP(p, new URL("https://sub.example.com"))).toBe(false); 17 + expect(mRP(p, new URL("https://com"))).toBe(false); 18 + expect(mRP(p, new URL("https://example.com/path"))).toBe(true); 19 + expect(mRP(p, new URL("https://example.com/path/to"))).toBe(true); 20 + expect(mRP(p, new URL("https://example.com/path/to/file"))).toBe(true); 21 + expect(mRP(p, new URL("https://example.com:81/path/to/file"))).toBe(true); 22 + expect(mRP(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(true); 23 + expect(mRP(p, new URL("http://example.com:81/path/to/file"))).toBe(true); 24 24 }); 25 25 26 26 it("should match literal protocol and hostname", () => { 27 27 const p = { protocol: "https", hostname: pm.makeRe("example.com") } as const; 28 - expect(m(p, new URL("https://example.com"))).toBe(true); 29 - expect(m(p, new URL("https://example.com.uk"))).toBe(false); 30 - expect(m(p, new URL("https://sub.example.com"))).toBe(false); 31 - expect(m(p, new URL("https://com"))).toBe(false); 32 - expect(m(p, new URL("https://example.com/path/to"))).toBe(true); 33 - expect(m(p, new URL("https://example.com/path/to/file"))).toBe(true); 34 - expect(m(p, new URL("https://example.com/path/to/file"))).toBe(true); 35 - expect(m(p, new URL("https://example.com:81/path/to/file"))).toBe(true); 36 - expect(m(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(true); 37 - expect(m(p, new URL("http://example.com:81/path/to/file"))).toBe(false); 38 - expect(m(p, new URL("ftp://example.com:81/path/to/file"))).toBe(false); 28 + expect(mRP(p, new URL("https://example.com"))).toBe(true); 29 + expect(mRP(p, new URL("https://example.com.uk"))).toBe(false); 30 + expect(mRP(p, new URL("https://sub.example.com"))).toBe(false); 31 + expect(mRP(p, new URL("https://com"))).toBe(false); 32 + expect(mRP(p, new URL("https://example.com/path/to"))).toBe(true); 33 + expect(mRP(p, new URL("https://example.com/path/to/file"))).toBe(true); 34 + expect(mRP(p, new URL("https://example.com/path/to/file"))).toBe(true); 35 + expect(mRP(p, new URL("https://example.com:81/path/to/file"))).toBe(true); 36 + expect(mRP(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(true); 37 + expect(mRP(p, new URL("http://example.com:81/path/to/file"))).toBe(false); 38 + expect(mRP(p, new URL("ftp://example.com:81/path/to/file"))).toBe(false); 39 39 }); 40 40 41 41 it("should match literal protocol, hostname, no port", () => { 42 42 const p = { protocol: "https", hostname: pm.makeRe("example.com"), port: "" } as const; 43 - expect(m(p, new URL("https://example.com"))).toBe(true); 44 - expect(m(p, new URL("https://example.com.uk"))).toBe(false); 45 - expect(m(p, new URL("https://sub.example.com"))).toBe(false); 46 - expect(m(p, new URL("https://com"))).toBe(false); 47 - expect(m(p, new URL("https://example.com/path/to/file"))).toBe(true); 48 - expect(m(p, new URL("https://example.com/path/to/file?q=1"))).toBe(true); 49 - expect(m(p, new URL("http://example.com/path/to/file"))).toBe(false); 50 - expect(m(p, new URL("ftp://example.com/path/to/file"))).toBe(false); 51 - expect(m(p, new URL("https://example.com:81/path/to/file"))).toBe(false); 52 - expect(m(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(false); 53 - expect(m(p, new URL("http://example.com:81/path/to/file"))).toBe(false); 43 + expect(mRP(p, new URL("https://example.com"))).toBe(true); 44 + expect(mRP(p, new URL("https://example.com.uk"))).toBe(false); 45 + expect(mRP(p, new URL("https://sub.example.com"))).toBe(false); 46 + expect(mRP(p, new URL("https://com"))).toBe(false); 47 + expect(mRP(p, new URL("https://example.com/path/to/file"))).toBe(true); 48 + expect(mRP(p, new URL("https://example.com/path/to/file?q=1"))).toBe(true); 49 + expect(mRP(p, new URL("http://example.com/path/to/file"))).toBe(false); 50 + expect(mRP(p, new URL("ftp://example.com/path/to/file"))).toBe(false); 51 + expect(mRP(p, new URL("https://example.com:81/path/to/file"))).toBe(false); 52 + expect(mRP(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(false); 53 + expect(mRP(p, new URL("http://example.com:81/path/to/file"))).toBe(false); 54 54 }); 55 55 56 56 it("should match literal protocol, hostname, no port, no search", () => { ··· 60 60 port: "", 61 61 search: "", 62 62 } as const; 63 - expect(m(p, new URL("https://example.com"))).toBe(true); 64 - expect(m(p, new URL("https://example.com.uk"))).toBe(false); 65 - expect(m(p, new URL("https://sub.example.com"))).toBe(false); 66 - expect(m(p, new URL("https://com"))).toBe(false); 67 - expect(m(p, new URL("https://example.com/path/to/file"))).toBe(true); 68 - expect(m(p, new URL("https://example.com/path/to/file?q=1"))).toBe(false); 69 - expect(m(p, new URL("http://example.com/path/to/file"))).toBe(false); 70 - expect(m(p, new URL("ftp://example.com/path/to/file"))).toBe(false); 71 - expect(m(p, new URL("https://example.com:81/path/to/file"))).toBe(false); 72 - expect(m(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(false); 73 - expect(m(p, new URL("http://example.com:81/path/to/file"))).toBe(false); 63 + expect(mRP(p, new URL("https://example.com"))).toBe(true); 64 + expect(mRP(p, new URL("https://example.com.uk"))).toBe(false); 65 + expect(mRP(p, new URL("https://sub.example.com"))).toBe(false); 66 + expect(mRP(p, new URL("https://com"))).toBe(false); 67 + expect(mRP(p, new URL("https://example.com/path/to/file"))).toBe(true); 68 + expect(mRP(p, new URL("https://example.com/path/to/file?q=1"))).toBe(false); 69 + expect(mRP(p, new URL("http://example.com/path/to/file"))).toBe(false); 70 + expect(mRP(p, new URL("ftp://example.com/path/to/file"))).toBe(false); 71 + expect(mRP(p, new URL("https://example.com:81/path/to/file"))).toBe(false); 72 + expect(mRP(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(false); 73 + expect(mRP(p, new URL("http://example.com:81/path/to/file"))).toBe(false); 74 74 }); 75 75 76 76 it("should match literal protocol, hostname, port 42", () => { ··· 79 79 hostname: pm.makeRe("example.com"), 80 80 port: "42", 81 81 } as const; 82 - expect(m(p, new URL("https://example.com:42"))).toBe(true); 83 - expect(m(p, new URL("https://example.com.uk:42"))).toBe(false); 84 - expect(m(p, new URL("https://sub.example.com:42"))).toBe(false); 85 - expect(m(p, new URL("https://com:42"))).toBe(false); 86 - expect(m(p, new URL("https://example.com:42/path/to/file"))).toBe(true); 87 - expect(m(p, new URL("https://example.com:42/path/to/file?q=1"))).toBe(true); 88 - expect(m(p, new URL("http://example.com:42/path/to/file"))).toBe(false); 89 - expect(m(p, new URL("ftp://example.com:42/path/to/file"))).toBe(false); 90 - expect(m(p, new URL("https://example.com"))).toBe(false); 91 - expect(m(p, new URL("https://example.com.uk"))).toBe(false); 92 - expect(m(p, new URL("https://sub.example.com"))).toBe(false); 93 - expect(m(p, new URL("https://com"))).toBe(false); 94 - expect(m(p, new URL("https://example.com/path/to/file"))).toBe(false); 95 - expect(m(p, new URL("https://example.com/path/to/file?q=1"))).toBe(false); 96 - expect(m(p, new URL("http://example.com/path/to/file"))).toBe(false); 97 - expect(m(p, new URL("ftp://example.com/path/to/file"))).toBe(false); 98 - expect(m(p, new URL("https://example.com:81"))).toBe(false); 99 - expect(m(p, new URL("https://example.com:81/path/to/file"))).toBe(false); 100 - expect(m(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(false); 82 + expect(mRP(p, new URL("https://example.com:42"))).toBe(true); 83 + expect(mRP(p, new URL("https://example.com.uk:42"))).toBe(false); 84 + expect(mRP(p, new URL("https://sub.example.com:42"))).toBe(false); 85 + expect(mRP(p, new URL("https://com:42"))).toBe(false); 86 + expect(mRP(p, new URL("https://example.com:42/path/to/file"))).toBe(true); 87 + expect(mRP(p, new URL("https://example.com:42/path/to/file?q=1"))).toBe(true); 88 + expect(mRP(p, new URL("http://example.com:42/path/to/file"))).toBe(false); 89 + expect(mRP(p, new URL("ftp://example.com:42/path/to/file"))).toBe(false); 90 + expect(mRP(p, new URL("https://example.com"))).toBe(false); 91 + expect(mRP(p, new URL("https://example.com.uk"))).toBe(false); 92 + expect(mRP(p, new URL("https://sub.example.com"))).toBe(false); 93 + expect(mRP(p, new URL("https://com"))).toBe(false); 94 + expect(mRP(p, new URL("https://example.com/path/to/file"))).toBe(false); 95 + expect(mRP(p, new URL("https://example.com/path/to/file?q=1"))).toBe(false); 96 + expect(mRP(p, new URL("http://example.com/path/to/file"))).toBe(false); 97 + expect(mRP(p, new URL("ftp://example.com/path/to/file"))).toBe(false); 98 + expect(mRP(p, new URL("https://example.com:81"))).toBe(false); 99 + expect(mRP(p, new URL("https://example.com:81/path/to/file"))).toBe(false); 100 + expect(mRP(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(false); 101 101 }); 102 102 103 103 it("should match literal protocol, hostname, port, pathname", () => { ··· 107 107 port: "42", 108 108 pathname: pm.makeRe("/path/to/file", { dot: true }), 109 109 } as const; 110 - expect(m(p, new URL("https://example.com:42"))).toBe(false); 111 - expect(m(p, new URL("https://example.com.uk:42"))).toBe(false); 112 - expect(m(p, new URL("https://sub.example.com:42"))).toBe(false); 113 - expect(m(p, new URL("https://example.com:42/path"))).toBe(false); 114 - expect(m(p, new URL("https://example.com:42/path/to"))).toBe(false); 115 - expect(m(p, new URL("https://example.com:42/file"))).toBe(false); 116 - expect(m(p, new URL("https://example.com:42/path/to/file"))).toBe(true); 117 - expect(m(p, new URL("https://example.com:42/path/to/file?q=1"))).toBe(true); 118 - expect(m(p, new URL("http://example.com:42/path/to/file"))).toBe(false); 119 - expect(m(p, new URL("ftp://example.com:42/path/to/file"))).toBe(false); 120 - expect(m(p, new URL("https://example.com"))).toBe(false); 121 - expect(m(p, new URL("https://example.com.uk"))).toBe(false); 122 - expect(m(p, new URL("https://sub.example.com"))).toBe(false); 123 - expect(m(p, new URL("https://example.com/path"))).toBe(false); 124 - expect(m(p, new URL("https://example.com/path/to"))).toBe(false); 125 - expect(m(p, new URL("https://example.com/path/to/file"))).toBe(false); 126 - expect(m(p, new URL("https://example.com/path/to/file?q=1"))).toBe(false); 127 - expect(m(p, new URL("http://example.com/path/to/file"))).toBe(false); 128 - expect(m(p, new URL("ftp://example.com/path/to/file"))).toBe(false); 129 - expect(m(p, new URL("https://example.com:81/path/to/file"))).toBe(false); 130 - expect(m(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(false); 110 + expect(mRP(p, new URL("https://example.com:42"))).toBe(false); 111 + expect(mRP(p, new URL("https://example.com.uk:42"))).toBe(false); 112 + expect(mRP(p, new URL("https://sub.example.com:42"))).toBe(false); 113 + expect(mRP(p, new URL("https://example.com:42/path"))).toBe(false); 114 + expect(mRP(p, new URL("https://example.com:42/path/to"))).toBe(false); 115 + expect(mRP(p, new URL("https://example.com:42/file"))).toBe(false); 116 + expect(mRP(p, new URL("https://example.com:42/path/to/file"))).toBe(true); 117 + expect(mRP(p, new URL("https://example.com:42/path/to/file?q=1"))).toBe(true); 118 + expect(mRP(p, new URL("http://example.com:42/path/to/file"))).toBe(false); 119 + expect(mRP(p, new URL("ftp://example.com:42/path/to/file"))).toBe(false); 120 + expect(mRP(p, new URL("https://example.com"))).toBe(false); 121 + expect(mRP(p, new URL("https://example.com.uk"))).toBe(false); 122 + expect(mRP(p, new URL("https://sub.example.com"))).toBe(false); 123 + expect(mRP(p, new URL("https://example.com/path"))).toBe(false); 124 + expect(mRP(p, new URL("https://example.com/path/to"))).toBe(false); 125 + expect(mRP(p, new URL("https://example.com/path/to/file"))).toBe(false); 126 + expect(mRP(p, new URL("https://example.com/path/to/file?q=1"))).toBe(false); 127 + expect(mRP(p, new URL("http://example.com/path/to/file"))).toBe(false); 128 + expect(mRP(p, new URL("ftp://example.com/path/to/file"))).toBe(false); 129 + expect(mRP(p, new URL("https://example.com:81/path/to/file"))).toBe(false); 130 + expect(mRP(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(false); 131 131 }); 132 132 133 133 it("should match literal protocol, hostname, port, pathname, search", () => { ··· 138 138 pathname: pm.makeRe("/path/to/file", { dot: true }), 139 139 search: "?q=1&a=two&s=!@$^&-_+/()[]{};:~", 140 140 } as const; 141 - expect(m(p, new URL("https://example.com:42"))).toBe(false); 142 - expect(m(p, new URL("https://example.com.uk:42"))).toBe(false); 143 - expect(m(p, new URL("https://sub.example.com:42"))).toBe(false); 144 - expect(m(p, new URL("https://example.com:42/path"))).toBe(false); 145 - expect(m(p, new URL("https://example.com:42/path/to"))).toBe(false); 146 - expect(m(p, new URL("https://example.com:42/file"))).toBe(false); 147 - expect(m(p, new URL("https://example.com:42/path/to/file"))).toBe(false); 148 - expect(m(p, new URL("http://example.com:42/path/to/file"))).toBe(false); 149 - expect(m(p, new URL("ftp://example.com:42/path/to/file"))).toBe(false); 150 - expect(m(p, new URL("https://example.com"))).toBe(false); 151 - expect(m(p, new URL("https://example.com.uk"))).toBe(false); 152 - expect(m(p, new URL("https://sub.example.com"))).toBe(false); 153 - expect(m(p, new URL("https://example.com/path"))).toBe(false); 154 - expect(m(p, new URL("https://example.com/path/to"))).toBe(false); 155 - expect(m(p, new URL("https://example.com/path/to/file"))).toBe(false); 156 - expect(m(p, new URL("https://example.com/path/to/file?q=1"))).toBe(false); 157 - expect(m(p, new URL("http://example.com/path/to/file"))).toBe(false); 158 - expect(m(p, new URL("ftp://example.com/path/to/file"))).toBe(false); 159 - expect(m(p, new URL("https://example.com:81/path/to/file"))).toBe(false); 160 - expect(m(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(false); 161 - expect(m(p, new URL("https://example.com:42/path/to/file?q=1"))).toBe(false); 162 - expect(m(p, new URL("https://example.com:42/path/to/file?q=1&a=two"))).toBe(false); 163 - expect(m(p, new URL("https://example.com:42/path/to/file?q=1&a=two&s"))).toBe(false); 164 - expect(m(p, new URL("https://example.com:42/path/to/file?q=1&a=two&s="))).toBe(false); 165 - expect(m(p, new URL("https://example.com:42/path/to/file?q=1&a=two&s=!@"))).toBe(false); 166 - expect(m(p, new URL("https://example.com:42/path/to/file?q=1&a=two&s=!@$^&-_+/()[]{};:~"))).toBe(true); 167 - expect(m(p, new URL("https://example.com:42/path/to/file?q=1&s=!@$^&-_+/()[]{};:~&a=two"))).toBe(false); 168 - expect(m(p, new URL("https://example.com:42/path/to/file?a=two&q=1&s=!@$^&-_+/()[]{};:~"))).toBe(false); 141 + expect(mRP(p, new URL("https://example.com:42"))).toBe(false); 142 + expect(mRP(p, new URL("https://example.com.uk:42"))).toBe(false); 143 + expect(mRP(p, new URL("https://sub.example.com:42"))).toBe(false); 144 + expect(mRP(p, new URL("https://example.com:42/path"))).toBe(false); 145 + expect(mRP(p, new URL("https://example.com:42/path/to"))).toBe(false); 146 + expect(mRP(p, new URL("https://example.com:42/file"))).toBe(false); 147 + expect(mRP(p, new URL("https://example.com:42/path/to/file"))).toBe(false); 148 + expect(mRP(p, new URL("http://example.com:42/path/to/file"))).toBe(false); 149 + expect(mRP(p, new URL("ftp://example.com:42/path/to/file"))).toBe(false); 150 + expect(mRP(p, new URL("https://example.com"))).toBe(false); 151 + expect(mRP(p, new URL("https://example.com.uk"))).toBe(false); 152 + expect(mRP(p, new URL("https://sub.example.com"))).toBe(false); 153 + expect(mRP(p, new URL("https://example.com/path"))).toBe(false); 154 + expect(mRP(p, new URL("https://example.com/path/to"))).toBe(false); 155 + expect(mRP(p, new URL("https://example.com/path/to/file"))).toBe(false); 156 + expect(mRP(p, new URL("https://example.com/path/to/file?q=1"))).toBe(false); 157 + expect(mRP(p, new URL("http://example.com/path/to/file"))).toBe(false); 158 + expect(mRP(p, new URL("ftp://example.com/path/to/file"))).toBe(false); 159 + expect(mRP(p, new URL("https://example.com:81/path/to/file"))).toBe(false); 160 + expect(mRP(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(false); 161 + expect(mRP(p, new URL("https://example.com:42/path/to/file?q=1"))).toBe(false); 162 + expect(mRP(p, new URL("https://example.com:42/path/to/file?q=1&a=two"))).toBe(false); 163 + expect(mRP(p, new URL("https://example.com:42/path/to/file?q=1&a=two&s"))).toBe(false); 164 + expect(mRP(p, new URL("https://example.com:42/path/to/file?q=1&a=two&s="))).toBe(false); 165 + expect(mRP(p, new URL("https://example.com:42/path/to/file?q=1&a=two&s=!@"))).toBe(false); 166 + expect(mRP(p, new URL("https://example.com:42/path/to/file?q=1&a=two&s=!@$^&-_+/()[]{};:~"))).toBe(true); 167 + expect(mRP(p, new URL("https://example.com:42/path/to/file?q=1&s=!@$^&-_+/()[]{};:~&a=two"))).toBe(false); 168 + expect(mRP(p, new URL("https://example.com:42/path/to/file?a=two&q=1&s=!@$^&-_+/()[]{};:~"))).toBe(false); 169 169 }); 170 170 171 171 it("should match hostname pattern with single asterisk by itself", () => { 172 172 const p = { hostname: pm.makeRe("avatars.*.example.com") } as const; 173 - expect(m(p, new URL("https://com"))).toBe(false); 174 - expect(m(p, new URL("https://example.com"))).toBe(false); 175 - expect(m(p, new URL("https://sub.example.com"))).toBe(false); 176 - expect(m(p, new URL("https://example.com.uk"))).toBe(false); 177 - expect(m(p, new URL("https://sub.example.com.uk"))).toBe(false); 178 - expect(m(p, new URL("https://avatars.example.com"))).toBe(false); 179 - expect(m(p, new URL("https://avatars.sfo1.example.com"))).toBe(true); 180 - expect(m(p, new URL("https://avatars.iad1.example.com"))).toBe(true); 181 - expect(m(p, new URL("https://more.avatars.iad1.example.com"))).toBe(false); 173 + expect(mRP(p, new URL("https://com"))).toBe(false); 174 + expect(mRP(p, new URL("https://example.com"))).toBe(false); 175 + expect(mRP(p, new URL("https://sub.example.com"))).toBe(false); 176 + expect(mRP(p, new URL("https://example.com.uk"))).toBe(false); 177 + expect(mRP(p, new URL("https://sub.example.com.uk"))).toBe(false); 178 + expect(mRP(p, new URL("https://avatars.example.com"))).toBe(false); 179 + expect(mRP(p, new URL("https://avatars.sfo1.example.com"))).toBe(true); 180 + expect(mRP(p, new URL("https://avatars.iad1.example.com"))).toBe(true); 181 + expect(mRP(p, new URL("https://more.avatars.iad1.example.com"))).toBe(false); 182 182 }); 183 183 184 184 it("should match hostname pattern with single asterisk at beginning", () => { 185 185 const p = { hostname: pm.makeRe("avatars.*1.example.com") } as const; 186 - expect(m(p, new URL("https://com"))).toBe(false); 187 - expect(m(p, new URL("https://example.com"))).toBe(false); 188 - expect(m(p, new URL("https://sub.example.com"))).toBe(false); 189 - expect(m(p, new URL("https://example.com.uk"))).toBe(false); 190 - expect(m(p, new URL("https://sub.example.com.uk"))).toBe(false); 191 - expect(m(p, new URL("https://avatars.example.com"))).toBe(false); 192 - expect(m(p, new URL("https://avatars.sfo1.example.com"))).toBe(true); 193 - expect(m(p, new URL("https://avatars.iad1.example.com"))).toBe(true); 194 - expect(m(p, new URL("https://more.avatars.iad1.example.com"))).toBe(false); 195 - expect(m(p, new URL("https://avatars.sfo2.example.com"))).toBe(false); 196 - expect(m(p, new URL("https://avatars.iad2.example.com"))).toBe(false); 197 - expect(m(p, new URL("https://avatars.1.example.com"))).toBe(true); 186 + expect(mRP(p, new URL("https://com"))).toBe(false); 187 + expect(mRP(p, new URL("https://example.com"))).toBe(false); 188 + expect(mRP(p, new URL("https://sub.example.com"))).toBe(false); 189 + expect(mRP(p, new URL("https://example.com.uk"))).toBe(false); 190 + expect(mRP(p, new URL("https://sub.example.com.uk"))).toBe(false); 191 + expect(mRP(p, new URL("https://avatars.example.com"))).toBe(false); 192 + expect(mRP(p, new URL("https://avatars.sfo1.example.com"))).toBe(true); 193 + expect(mRP(p, new URL("https://avatars.iad1.example.com"))).toBe(true); 194 + expect(mRP(p, new URL("https://more.avatars.iad1.example.com"))).toBe(false); 195 + expect(mRP(p, new URL("https://avatars.sfo2.example.com"))).toBe(false); 196 + expect(mRP(p, new URL("https://avatars.iad2.example.com"))).toBe(false); 197 + expect(mRP(p, new URL("https://avatars.1.example.com"))).toBe(true); 198 198 }); 199 199 200 200 it("should match hostname pattern with single asterisk in middle", () => { 201 201 const p = { hostname: pm.makeRe("avatars.*a*.example.com") } as const; 202 - expect(m(p, new URL("https://com"))).toBe(false); 203 - expect(m(p, new URL("https://example.com"))).toBe(false); 204 - expect(m(p, new URL("https://sub.example.com"))).toBe(false); 205 - expect(m(p, new URL("https://example.com.uk"))).toBe(false); 206 - expect(m(p, new URL("https://sub.example.com.uk"))).toBe(false); 207 - expect(m(p, new URL("https://avatars.example.com"))).toBe(false); 208 - expect(m(p, new URL("https://avatars.sfo1.example.com"))).toBe(false); 209 - expect(m(p, new URL("https://avatars.iad1.example.com"))).toBe(true); 210 - expect(m(p, new URL("https://more.avatars.iad1.example.com"))).toBe(false); 211 - expect(m(p, new URL("https://avatars.sfo2.example.com"))).toBe(false); 212 - expect(m(p, new URL("https://avatars.iad2.example.com"))).toBe(true); 213 - expect(m(p, new URL("https://avatars.a.example.com"))).toBe(true); 202 + expect(mRP(p, new URL("https://com"))).toBe(false); 203 + expect(mRP(p, new URL("https://example.com"))).toBe(false); 204 + expect(mRP(p, new URL("https://sub.example.com"))).toBe(false); 205 + expect(mRP(p, new URL("https://example.com.uk"))).toBe(false); 206 + expect(mRP(p, new URL("https://sub.example.com.uk"))).toBe(false); 207 + expect(mRP(p, new URL("https://avatars.example.com"))).toBe(false); 208 + expect(mRP(p, new URL("https://avatars.sfo1.example.com"))).toBe(false); 209 + expect(mRP(p, new URL("https://avatars.iad1.example.com"))).toBe(true); 210 + expect(mRP(p, new URL("https://more.avatars.iad1.example.com"))).toBe(false); 211 + expect(mRP(p, new URL("https://avatars.sfo2.example.com"))).toBe(false); 212 + expect(mRP(p, new URL("https://avatars.iad2.example.com"))).toBe(true); 213 + expect(mRP(p, new URL("https://avatars.a.example.com"))).toBe(true); 214 214 }); 215 215 216 216 it("should match hostname pattern with single asterisk at end", () => { 217 217 const p = { hostname: pm.makeRe("avatars.ia*.example.com") } as const; 218 - expect(m(p, new URL("https://com"))).toBe(false); 219 - expect(m(p, new URL("https://example.com"))).toBe(false); 220 - expect(m(p, new URL("https://sub.example.com"))).toBe(false); 221 - expect(m(p, new URL("https://example.com.uk"))).toBe(false); 222 - expect(m(p, new URL("https://sub.example.com.uk"))).toBe(false); 223 - expect(m(p, new URL("https://avatars.example.com"))).toBe(false); 224 - expect(m(p, new URL("https://avatars.sfo1.example.com"))).toBe(false); 225 - expect(m(p, new URL("https://avatars.iad1.example.com"))).toBe(true); 226 - expect(m(p, new URL("https://more.avatars.iad1.example.com"))).toBe(false); 227 - expect(m(p, new URL("https://avatars.sfo2.example.com"))).toBe(false); 228 - expect(m(p, new URL("https://avatars.iad2.example.com"))).toBe(true); 229 - expect(m(p, new URL("https://avatars.ia.example.com"))).toBe(true); 218 + expect(mRP(p, new URL("https://com"))).toBe(false); 219 + expect(mRP(p, new URL("https://example.com"))).toBe(false); 220 + expect(mRP(p, new URL("https://sub.example.com"))).toBe(false); 221 + expect(mRP(p, new URL("https://example.com.uk"))).toBe(false); 222 + expect(mRP(p, new URL("https://sub.example.com.uk"))).toBe(false); 223 + expect(mRP(p, new URL("https://avatars.example.com"))).toBe(false); 224 + expect(mRP(p, new URL("https://avatars.sfo1.example.com"))).toBe(false); 225 + expect(mRP(p, new URL("https://avatars.iad1.example.com"))).toBe(true); 226 + expect(mRP(p, new URL("https://more.avatars.iad1.example.com"))).toBe(false); 227 + expect(mRP(p, new URL("https://avatars.sfo2.example.com"))).toBe(false); 228 + expect(mRP(p, new URL("https://avatars.iad2.example.com"))).toBe(true); 229 + expect(mRP(p, new URL("https://avatars.ia.example.com"))).toBe(true); 230 230 }); 231 231 232 232 it("should match hostname pattern with double asterisk", () => { 233 233 const p = { hostname: pm.makeRe("**.example.com") } as const; 234 - expect(m(p, new URL("https://com"))).toBe(false); 235 - expect(m(p, new URL("https://example.com"))).toBe(false); 236 - expect(m(p, new URL("https://sub.example.com"))).toBe(true); 237 - expect(m(p, new URL("https://deep.sub.example.com"))).toBe(true); 238 - expect(m(p, new URL("https://example.com.uk"))).toBe(false); 239 - expect(m(p, new URL("https://sub.example.com.uk"))).toBe(false); 240 - expect(m(p, new URL("https://avatars.example.com"))).toBe(true); 241 - expect(m(p, new URL("https://avatars.sfo1.example.com"))).toBe(true); 242 - expect(m(p, new URL("https://avatars.iad1.example.com"))).toBe(true); 243 - expect(m(p, new URL("https://more.avatars.iad1.example.com"))).toBe(true); 234 + expect(mRP(p, new URL("https://com"))).toBe(false); 235 + expect(mRP(p, new URL("https://example.com"))).toBe(false); 236 + expect(mRP(p, new URL("https://sub.example.com"))).toBe(true); 237 + expect(mRP(p, new URL("https://deep.sub.example.com"))).toBe(true); 238 + expect(mRP(p, new URL("https://example.com.uk"))).toBe(false); 239 + expect(mRP(p, new URL("https://sub.example.com.uk"))).toBe(false); 240 + expect(mRP(p, new URL("https://avatars.example.com"))).toBe(true); 241 + expect(mRP(p, new URL("https://avatars.sfo1.example.com"))).toBe(true); 242 + expect(mRP(p, new URL("https://avatars.iad1.example.com"))).toBe(true); 243 + expect(mRP(p, new URL("https://more.avatars.iad1.example.com"))).toBe(true); 244 244 }); 245 245 246 246 it("should match pathname pattern with single asterisk by itself", () => { ··· 248 248 hostname: pm.makeRe("example.com"), 249 249 pathname: pm.makeRe("/act123/*/pic.jpg", { dot: true }), 250 250 } as const; 251 - expect(m(p, new URL("https://com"))).toBe(false); 252 - expect(m(p, new URL("https://example.com"))).toBe(false); 253 - expect(m(p, new URL("https://sub.example.com"))).toBe(false); 254 - expect(m(p, new URL("https://example.com.uk"))).toBe(false); 255 - expect(m(p, new URL("https://example.com/act123"))).toBe(false); 256 - expect(m(p, new URL("https://example.com/act123/usr4"))).toBe(false); 257 - expect(m(p, new URL("https://example.com/act123/usr4/pic"))).toBe(false); 258 - expect(m(p, new URL("https://example.com/act123/usr4/picsjpg"))).toBe(false); 259 - expect(m(p, new URL("https://example.com/act123/usr4/pic.jpg"))).toBe(true); 260 - expect(m(p, new URL("https://example.com/act123/usr5/pic.jpg"))).toBe(true); 261 - expect(m(p, new URL("https://example.com/act123/usr6/pic.jpg"))).toBe(true); 262 - expect(m(p, new URL("https://example.com/act123/team/pic.jpg"))).toBe(true); 263 - expect(m(p, new URL("https://example.com/act456/team/pic.jpg"))).toBe(false); 264 - expect(m(p, new URL("https://example.com/act123/.a/pic.jpg"))).toBe(true); 265 - expect(m(p, new URL("https://example.com/act123/team/usr4/pic.jpg"))).toBe(false); 266 - expect(m(p, new URL("https://example.com/team/pic.jpg"))).toBe(false); 251 + expect(mRP(p, new URL("https://com"))).toBe(false); 252 + expect(mRP(p, new URL("https://example.com"))).toBe(false); 253 + expect(mRP(p, new URL("https://sub.example.com"))).toBe(false); 254 + expect(mRP(p, new URL("https://example.com.uk"))).toBe(false); 255 + expect(mRP(p, new URL("https://example.com/act123"))).toBe(false); 256 + expect(mRP(p, new URL("https://example.com/act123/usr4"))).toBe(false); 257 + expect(mRP(p, new URL("https://example.com/act123/usr4/pic"))).toBe(false); 258 + expect(mRP(p, new URL("https://example.com/act123/usr4/picsjpg"))).toBe(false); 259 + expect(mRP(p, new URL("https://example.com/act123/usr4/pic.jpg"))).toBe(true); 260 + expect(mRP(p, new URL("https://example.com/act123/usr5/pic.jpg"))).toBe(true); 261 + expect(mRP(p, new URL("https://example.com/act123/usr6/pic.jpg"))).toBe(true); 262 + expect(mRP(p, new URL("https://example.com/act123/team/pic.jpg"))).toBe(true); 263 + expect(mRP(p, new URL("https://example.com/act456/team/pic.jpg"))).toBe(false); 264 + expect(mRP(p, new URL("https://example.com/act123/.a/pic.jpg"))).toBe(true); 265 + expect(mRP(p, new URL("https://example.com/act123/team/usr4/pic.jpg"))).toBe(false); 266 + expect(mRP(p, new URL("https://example.com/team/pic.jpg"))).toBe(false); 267 267 }); 268 268 269 269 it("should match pathname pattern with single asterisk at the beginning", () => { ··· 271 271 hostname: pm.makeRe("example.com"), 272 272 pathname: pm.makeRe("/act123/*4/pic.jpg", { dot: true }), 273 273 } as const; 274 - expect(m(p, new URL("https://com"))).toBe(false); 275 - expect(m(p, new URL("https://example.com"))).toBe(false); 276 - expect(m(p, new URL("https://sub.example.com"))).toBe(false); 277 - expect(m(p, new URL("https://example.com.uk"))).toBe(false); 278 - expect(m(p, new URL("https://example.com/act123"))).toBe(false); 279 - expect(m(p, new URL("https://example.com/act123/usr4"))).toBe(false); 280 - expect(m(p, new URL("https://example.com/act123/usr4/pic"))).toBe(false); 281 - expect(m(p, new URL("https://example.com/act123/usr4/picsjpg"))).toBe(false); 282 - expect(m(p, new URL("https://example.com/act123/usr4/pic.jpg"))).toBe(true); 283 - expect(m(p, new URL("https://example.com/act123/usr5/pic.jpg"))).toBe(false); 284 - expect(m(p, new URL("https://example.com/act123/team4/pic.jpg"))).toBe(true); 285 - expect(m(p, new URL("https://example.com/act456/team5/pic.jpg"))).toBe(false); 286 - expect(m(p, new URL("https://example.com/team/pic.jpg"))).toBe(false); 287 - expect(m(p, new URL("https://example.com/act123/4/pic.jpg"))).toBe(true); 274 + expect(mRP(p, new URL("https://com"))).toBe(false); 275 + expect(mRP(p, new URL("https://example.com"))).toBe(false); 276 + expect(mRP(p, new URL("https://sub.example.com"))).toBe(false); 277 + expect(mRP(p, new URL("https://example.com.uk"))).toBe(false); 278 + expect(mRP(p, new URL("https://example.com/act123"))).toBe(false); 279 + expect(mRP(p, new URL("https://example.com/act123/usr4"))).toBe(false); 280 + expect(mRP(p, new URL("https://example.com/act123/usr4/pic"))).toBe(false); 281 + expect(mRP(p, new URL("https://example.com/act123/usr4/picsjpg"))).toBe(false); 282 + expect(mRP(p, new URL("https://example.com/act123/usr4/pic.jpg"))).toBe(true); 283 + expect(mRP(p, new URL("https://example.com/act123/usr5/pic.jpg"))).toBe(false); 284 + expect(mRP(p, new URL("https://example.com/act123/team4/pic.jpg"))).toBe(true); 285 + expect(mRP(p, new URL("https://example.com/act456/team5/pic.jpg"))).toBe(false); 286 + expect(mRP(p, new URL("https://example.com/team/pic.jpg"))).toBe(false); 287 + expect(mRP(p, new URL("https://example.com/act123/4/pic.jpg"))).toBe(true); 288 288 }); 289 289 290 290 it("should match pathname pattern with single asterisk in the middle", () => { ··· 292 292 hostname: pm.makeRe("example.com"), 293 293 pathname: pm.makeRe("/act123/*sr*/pic.jpg", { dot: true }), 294 294 } as const; 295 - expect(m(p, new URL("https://com"))).toBe(false); 296 - expect(m(p, new URL("https://example.com"))).toBe(false); 297 - expect(m(p, new URL("https://sub.example.com"))).toBe(false); 298 - expect(m(p, new URL("https://example.com.uk"))).toBe(false); 299 - expect(m(p, new URL("https://example.com/act123"))).toBe(false); 300 - expect(m(p, new URL("https://example.com/act123/usr4"))).toBe(false); 301 - expect(m(p, new URL("https://example.com/act123/usr4/pic"))).toBe(false); 302 - expect(m(p, new URL("https://example.com/act123/usr4/picsjpg"))).toBe(false); 303 - expect(m(p, new URL("https://example.com/act123/usr4/pic.jpg"))).toBe(true); 304 - expect(m(p, new URL("https://example.com/act123/usr5/pic.jpg"))).toBe(true); 305 - expect(m(p, new URL("https://example.com/act123/.sr6/pic.jpg"))).toBe(true); 306 - expect(m(p, new URL("https://example.com/act123/team4/pic.jpg"))).toBe(false); 307 - expect(m(p, new URL("https://example.com/act123/team5/pic.jpg"))).toBe(false); 308 - expect(m(p, new URL("https://example.com/team/pic.jpg"))).toBe(false); 309 - expect(m(p, new URL("https://example.com/act123/sr/pic.jpg"))).toBe(true); 295 + expect(mRP(p, new URL("https://com"))).toBe(false); 296 + expect(mRP(p, new URL("https://example.com"))).toBe(false); 297 + expect(mRP(p, new URL("https://sub.example.com"))).toBe(false); 298 + expect(mRP(p, new URL("https://example.com.uk"))).toBe(false); 299 + expect(mRP(p, new URL("https://example.com/act123"))).toBe(false); 300 + expect(mRP(p, new URL("https://example.com/act123/usr4"))).toBe(false); 301 + expect(mRP(p, new URL("https://example.com/act123/usr4/pic"))).toBe(false); 302 + expect(mRP(p, new URL("https://example.com/act123/usr4/picsjpg"))).toBe(false); 303 + expect(mRP(p, new URL("https://example.com/act123/usr4/pic.jpg"))).toBe(true); 304 + expect(mRP(p, new URL("https://example.com/act123/usr5/pic.jpg"))).toBe(true); 305 + expect(mRP(p, new URL("https://example.com/act123/.sr6/pic.jpg"))).toBe(true); 306 + expect(mRP(p, new URL("https://example.com/act123/team4/pic.jpg"))).toBe(false); 307 + expect(mRP(p, new URL("https://example.com/act123/team5/pic.jpg"))).toBe(false); 308 + expect(mRP(p, new URL("https://example.com/team/pic.jpg"))).toBe(false); 309 + expect(mRP(p, new URL("https://example.com/act123/sr/pic.jpg"))).toBe(true); 310 310 }); 311 311 312 312 it("should match pathname pattern with single asterisk at the end", () => { ··· 314 314 hostname: pm.makeRe("example.com"), 315 315 pathname: pm.makeRe("/act123/usr*/pic.jpg", { dot: true }), 316 316 } as const; 317 - expect(m(p, new URL("https://com"))).toBe(false); 318 - expect(m(p, new URL("https://example.com"))).toBe(false); 319 - expect(m(p, new URL("https://sub.example.com"))).toBe(false); 320 - expect(m(p, new URL("https://example.com.uk"))).toBe(false); 321 - expect(m(p, new URL("https://example.com/act123"))).toBe(false); 322 - expect(m(p, new URL("https://example.com/act123/usr4"))).toBe(false); 323 - expect(m(p, new URL("https://example.com/act123/usr4/pic"))).toBe(false); 324 - expect(m(p, new URL("https://example.com/act123/usr4/picsjpg"))).toBe(false); 325 - expect(m(p, new URL("https://example.com/act123/usr4/pic.jpg"))).toBe(true); 326 - expect(m(p, new URL("https://example.com/act123/usr5/pic.jpg"))).toBe(true); 327 - expect(m(p, new URL("https://example.com/act123/usr/pic.jpg"))).toBe(true); 328 - expect(m(p, new URL("https://example.com/act123/team4/pic.jpg"))).toBe(false); 329 - expect(m(p, new URL("https://example.com/act456/team5/pic.jpg"))).toBe(false); 330 - expect(m(p, new URL("https://example.com/team/pic.jpg"))).toBe(false); 331 - expect(m(p, new URL("https://sub.example.com/act123/usr6/pic.jpg"))).toBe(false); 317 + expect(mRP(p, new URL("https://com"))).toBe(false); 318 + expect(mRP(p, new URL("https://example.com"))).toBe(false); 319 + expect(mRP(p, new URL("https://sub.example.com"))).toBe(false); 320 + expect(mRP(p, new URL("https://example.com.uk"))).toBe(false); 321 + expect(mRP(p, new URL("https://example.com/act123"))).toBe(false); 322 + expect(mRP(p, new URL("https://example.com/act123/usr4"))).toBe(false); 323 + expect(mRP(p, new URL("https://example.com/act123/usr4/pic"))).toBe(false); 324 + expect(mRP(p, new URL("https://example.com/act123/usr4/picsjpg"))).toBe(false); 325 + expect(mRP(p, new URL("https://example.com/act123/usr4/pic.jpg"))).toBe(true); 326 + expect(mRP(p, new URL("https://example.com/act123/usr5/pic.jpg"))).toBe(true); 327 + expect(mRP(p, new URL("https://example.com/act123/usr/pic.jpg"))).toBe(true); 328 + expect(mRP(p, new URL("https://example.com/act123/team4/pic.jpg"))).toBe(false); 329 + expect(mRP(p, new URL("https://example.com/act456/team5/pic.jpg"))).toBe(false); 330 + expect(mRP(p, new URL("https://example.com/team/pic.jpg"))).toBe(false); 331 + expect(mRP(p, new URL("https://sub.example.com/act123/usr6/pic.jpg"))).toBe(false); 332 332 }); 333 333 334 334 it("should match pathname pattern with double asterisk", () => { ··· 336 336 hostname: pm.makeRe("example.com"), 337 337 pathname: pm.makeRe("/act123/**", { dot: true }), 338 338 } as const; 339 - expect(m(p, new URL("https://com"))).toBe(false); 340 - expect(m(p, new URL("https://example.com"))).toBe(false); 341 - expect(m(p, new URL("https://sub.example.com"))).toBe(false); 342 - expect(m(p, new URL("https://example.com.uk"))).toBe(false); 343 - expect(m(p, new URL("https://example.com/act123"))).toBe(true); 344 - expect(m(p, new URL("https://example.com/act123/usr4"))).toBe(true); 345 - expect(m(p, new URL("https://example.com/act123/usr4/pic"))).toBe(true); 346 - expect(m(p, new URL("https://example.com/act123/usr4/picsjpg"))).toBe(true); 347 - expect(m(p, new URL("https://example.com/act123/usr4/pic.jpg"))).toBe(true); 348 - expect(m(p, new URL("https://example.com/act123/usr5/pic.jpg"))).toBe(true); 349 - expect(m(p, new URL("https://example.com/act123/usr6/pic.jpg"))).toBe(true); 350 - expect(m(p, new URL("https://example.com/act123/team/pic.jpg"))).toBe(true); 351 - expect(m(p, new URL("https://example.com/act123/.a/pic.jpg"))).toBe(true); 352 - expect(m(p, new URL("https://example.com/act123/team/.pic.jpg"))).toBe(true); 353 - expect(m(p, new URL("https://example.com/act456/team/pic.jpg"))).toBe(false); 354 - expect(m(p, new URL("https://example.com/team/pic.jpg"))).toBe(false); 355 - expect(m(p, new URL("https://sub.example.com/act123/team/pic.jpg"))).toBe(false); 339 + expect(mRP(p, new URL("https://com"))).toBe(false); 340 + expect(mRP(p, new URL("https://example.com"))).toBe(false); 341 + expect(mRP(p, new URL("https://sub.example.com"))).toBe(false); 342 + expect(mRP(p, new URL("https://example.com.uk"))).toBe(false); 343 + expect(mRP(p, new URL("https://example.com/act123"))).toBe(true); 344 + expect(mRP(p, new URL("https://example.com/act123/usr4"))).toBe(true); 345 + expect(mRP(p, new URL("https://example.com/act123/usr4/pic"))).toBe(true); 346 + expect(mRP(p, new URL("https://example.com/act123/usr4/picsjpg"))).toBe(true); 347 + expect(mRP(p, new URL("https://example.com/act123/usr4/pic.jpg"))).toBe(true); 348 + expect(mRP(p, new URL("https://example.com/act123/usr5/pic.jpg"))).toBe(true); 349 + expect(mRP(p, new URL("https://example.com/act123/usr6/pic.jpg"))).toBe(true); 350 + expect(mRP(p, new URL("https://example.com/act123/team/pic.jpg"))).toBe(true); 351 + expect(mRP(p, new URL("https://example.com/act123/.a/pic.jpg"))).toBe(true); 352 + expect(mRP(p, new URL("https://example.com/act123/team/.pic.jpg"))).toBe(true); 353 + expect(mRP(p, new URL("https://example.com/act456/team/pic.jpg"))).toBe(false); 354 + expect(mRP(p, new URL("https://example.com/team/pic.jpg"))).toBe(false); 355 + expect(mRP(p, new URL("https://sub.example.com/act123/team/pic.jpg"))).toBe(false); 356 356 }); 357 357 358 358 it("should throw when hostname is missing", () => { 359 359 const p = { protocol: "https" } as const; 360 360 // @ts-ignore testing invalid input 361 - expect(m(p, new URL("https://example.com"))).toBe(false); 361 + expect(mRP(p, new URL("https://example.com"))).toBe(false); 362 + }); 363 + }); 364 + 365 + /** 366 + * See https://github.com/vercel/next.js/blob/64702a9/test/unit/image-optimizer/match-local-pattern.test.ts 367 + */ 368 + describe("matchLocalPattern", () => { 369 + const mLP = (p: LocalPattern, urlPathAndQuery: string) => 370 + matchLocalPattern(p, new URL(urlPathAndQuery, "http://n")); 371 + 372 + it("should match anything when no pattern is defined", () => { 373 + const p = {} as const; 374 + expect(mLP(p, "/")).toBe(true); 375 + expect(mLP(p, "/path")).toBe(true); 376 + expect(mLP(p, "/path/to")).toBe(true); 377 + expect(mLP(p, "/path/to/file")).toBe(true); 378 + expect(mLP(p, "/path/to/file.txt")).toBe(true); 379 + expect(mLP(p, "/path/to/file?q=1")).toBe(true); 380 + expect(mLP(p, "/path/to/file?q=1&a=two")).toBe(true); 381 + }); 382 + 383 + it("should match any path without a search query string", () => { 384 + const p = { 385 + search: "", 386 + } as const; 387 + expect(mLP(p, "/")).toBe(true); 388 + expect(mLP(p, "/path")).toBe(true); 389 + expect(mLP(p, "/path/to")).toBe(true); 390 + expect(mLP(p, "/path/to/file")).toBe(true); 391 + expect(mLP(p, "/path/to/file.txt")).toBe(true); 392 + expect(mLP(p, "/path/to/file?q=1")).toBe(false); 393 + expect(mLP(p, "/path/to/file?q=1&a=two")).toBe(false); 394 + expect(mLP(p, "/path/to/file.txt?q=1&a=two")).toBe(false); 395 + }); 396 + 397 + it("should match literal pathname and any search query string", () => { 398 + const p = { 399 + pathname: pm.makeRe("/path/to/file", { 400 + dot: true, 401 + }), 402 + } as const; 403 + expect(mLP(p, "/")).toBe(false); 404 + expect(mLP(p, "/path")).toBe(false); 405 + expect(mLP(p, "/path/to")).toBe(false); 406 + expect(mLP(p, "/path/to/file")).toBe(true); 407 + expect(mLP(p, "/path/to/file.txt")).toBe(false); 408 + expect(mLP(p, "/path/to/file?q=1")).toBe(true); 409 + expect(mLP(p, "/path/to/file?q=1&a=two")).toBe(true); 410 + expect(mLP(p, "/path/to/file.txt?q=1&a=two")).toBe(false); 411 + }); 412 + 413 + it("should match pathname with double asterisk", () => { 414 + const p = { 415 + pathname: pm.makeRe("/path/to/**", { 416 + dot: true, 417 + }), 418 + } as const; 419 + expect(mLP(p, "/")).toBe(false); 420 + expect(mLP(p, "/path")).toBe(false); 421 + expect(mLP(p, "/path/to")).toBe(true); 422 + expect(mLP(p, "/path/to/file")).toBe(true); 423 + expect(mLP(p, "/path/to/file.txt")).toBe(true); 424 + expect(mLP(p, "/path/to/file?q=1")).toBe(true); 425 + expect(mLP(p, "/path/to/file?q=1&a=two")).toBe(true); 426 + expect(mLP(p, "/path/to/file.txt?q=1&a=two")).toBe(true); 362 427 }); 363 428 });
+29 -2
packages/cloudflare/src/cli/templates/images.ts
··· 7 7 search?: string; 8 8 }; 9 9 10 + export type LocalPattern = { 11 + // pathname is always set in the manifest 12 + pathname: string; 13 + search?: string; 14 + }; 15 + 10 16 /** 11 17 * Fetches an images. 12 18 * ··· 22 28 // Local 23 29 if (imageUrl.startsWith("/")) { 24 30 let pathname: string; 31 + let url: URL; 25 32 try { 26 - const url = new URL(imageUrl, "http://n"); 33 + // We only need pathname and search 34 + url = new URL(imageUrl, "http://n"); 27 35 pathname = decodeURIComponent(url.pathname); 28 36 } catch { 29 37 return getUrlErrorResponse(); 30 38 } 39 + 31 40 if (/\/_next\/image($|\/)/.test(pathname)) { 32 41 return getUrlErrorResponse(); 33 42 } 34 43 44 + // If localPatterns are not defined all local images are allowed. 45 + if ( 46 + __IMAGES_LOCAL_PATTERNS__.length > 0 && 47 + !__IMAGES_LOCAL_PATTERNS__.some((p: LocalPattern) => matchLocalPattern(p, url)) 48 + ) { 49 + return getUrlErrorResponse(); 50 + } 51 + 35 52 return fetcher?.fetch(`http://assets.local${imageUrl}`); 36 53 } 37 54 ··· 47 64 return getUrlErrorResponse(); 48 65 } 49 66 67 + // The remotePatterns is used to allow images from specific remote external paths and block all others. 50 68 if (!__IMAGES_REMOTE_PATTERNS__.some((p: RemotePattern) => matchRemotePattern(p, url))) { 51 69 return getUrlErrorResponse(); 52 70 } ··· 83 101 return true; 84 102 } 85 103 104 + export function matchLocalPattern(pattern: LocalPattern, url: URL): boolean { 105 + // https://github.com/vercel/next.js/blob/d76f0b1/packages/next/src/shared/lib/match-local-pattern.ts 106 + if (pattern.search !== undefined && pattern.search !== url.search) { 107 + return false; 108 + } 109 + 110 + return new RegExp(pattern.pathname).test(url.pathname); 111 + } 112 + 86 113 /** 87 114 * @returns same error as Next.js when the url query parameter is not accepted. 88 115 */ ··· 93 120 /* eslint-disable no-var */ 94 121 declare global { 95 122 var __IMAGES_REMOTE_PATTERNS__: RemotePattern[]; 96 - var __IMAGES_LOCAL_PATTERNS__: unknown[]; 123 + var __IMAGES_LOCAL_PATTERNS__: LocalPattern[]; 97 124 } 98 125 /* eslint-enable no-var */