@appwarden/middleware 3.10.1 → 3.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![GitHub](https://img.shields.io/badge/GitHub-appwarden%2Fmiddleware-181717?logo=github&logoColor=white)](https://github.com/appwarden/middleware)
5
5
  [![npm version](https://img.shields.io/npm/v/@appwarden/middleware.svg)](https://www.npmjs.com/package/@appwarden/middleware)
6
6
  [![npm provenance](https://img.shields.io/badge/npm-provenance-green)](https://docs.npmjs.com/generating-provenance-statements)
7
- ![Test Coverage](https://img.shields.io/badge/coverage-92.72%25-brightgreen)
7
+ ![Test Coverage](https://img.shields.io/badge/coverage-93.03%25-brightgreen)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
9
9
 
10
10
  ## Core Features
@@ -51,9 +51,9 @@ function isHTMLRequest(request) {
51
51
  }
52
52
  const normalizedAccept = accept.toLowerCase();
53
53
  const isWildcardOnlyAccept = (value) => {
54
- const mediaRanges = value.split(",");
54
+ const mediaRanges2 = value.split(",");
55
55
  let hasNonEmptyRange = false;
56
- for (const range of mediaRanges) {
56
+ for (const range of mediaRanges2) {
57
57
  const [typeSubtype] = range.split(";");
58
58
  const trimmed = typeSubtype.trim();
59
59
  if (!trimmed) {
@@ -69,7 +69,15 @@ function isHTMLRequest(request) {
69
69
  if (isWildcardOnlyAccept(normalizedAccept)) {
70
70
  return false;
71
71
  }
72
- return normalizedAccept.includes("text/html");
72
+ const mediaRanges = normalizedAccept.split(",");
73
+ for (const range of mediaRanges) {
74
+ const [typeSubtype] = range.split(";");
75
+ const token = typeSubtype.trim();
76
+ if (token === "text/html") {
77
+ return true;
78
+ }
79
+ }
80
+ return false;
73
81
  }
74
82
 
75
83
  // src/schemas/use-content-security-policy.ts
@@ -0,0 +1,86 @@
1
+ import {
2
+ UseCSPInputSchema,
3
+ isHTMLResponse
4
+ } from "./chunk-2WPLLVUI.js";
5
+ import {
6
+ makeCSPHeader
7
+ } from "./chunk-QUVGY2YI.js";
8
+
9
+ // src/middlewares/use-content-security-policy.ts
10
+ var AppendAttribute = (attribute, nonce) => ({
11
+ element: function(element) {
12
+ element.setAttribute(attribute, nonce);
13
+ }
14
+ });
15
+ var useContentSecurityPolicy = (input) => {
16
+ const parsedInput = UseCSPInputSchema.safeParse(input);
17
+ if (!parsedInput.success) {
18
+ throw parsedInput.error;
19
+ }
20
+ const config = parsedInput.data;
21
+ return async (context, next) => {
22
+ await next();
23
+ const { response } = context;
24
+ if (
25
+ // if the csp is disabled
26
+ !["enforced", "report-only"].includes(config.mode)
27
+ ) {
28
+ context.debug("CSP is disabled");
29
+ return;
30
+ }
31
+ if (response.headers.has("Content-Type") && !isHTMLResponse(response)) {
32
+ return;
33
+ }
34
+ const cspNonce = crypto.randomUUID();
35
+ const [cspHeaderName, cspHeaderValue] = makeCSPHeader(
36
+ cspNonce,
37
+ config.directives,
38
+ config.mode
39
+ );
40
+ context.debug(`Applying CSP in ${config.mode} mode`);
41
+ const method = context.request.method.toUpperCase();
42
+ const shouldSkipTransform = !response.body || response.status === 204 || response.status === 304 || method === "HEAD";
43
+ if (shouldSkipTransform) {
44
+ context.debug(
45
+ "Skipping HTMLRewriter transform for response without body or HEAD request"
46
+ );
47
+ const nextResponse2 = new Response(null, response);
48
+ nextResponse2.headers.set(cspHeaderName, cspHeaderValue);
49
+ const originalContentType2 = response.headers.get("content-type");
50
+ if (originalContentType2) {
51
+ if (/charset\s*=/i.test(originalContentType2)) {
52
+ nextResponse2.headers.set("content-type", originalContentType2);
53
+ } else {
54
+ nextResponse2.headers.set(
55
+ "content-type",
56
+ `${originalContentType2}; charset=utf-8`
57
+ );
58
+ }
59
+ } else {
60
+ nextResponse2.headers.set("content-type", "text/html; charset=utf-8");
61
+ }
62
+ context.response = nextResponse2;
63
+ return;
64
+ }
65
+ const nextResponse = new Response(response.clone().body, response);
66
+ nextResponse.headers.set(cspHeaderName, cspHeaderValue);
67
+ const originalContentType = response.headers.get("content-type");
68
+ if (originalContentType) {
69
+ if (/charset\s*=/i.test(originalContentType)) {
70
+ nextResponse.headers.set("content-type", originalContentType);
71
+ } else {
72
+ nextResponse.headers.set(
73
+ "content-type",
74
+ `${originalContentType}; charset=utf-8`
75
+ );
76
+ }
77
+ } else {
78
+ nextResponse.headers.set("content-type", "text/html; charset=utf-8");
79
+ }
80
+ context.response = new HTMLRewriter().on("style", AppendAttribute("nonce", cspNonce)).on("script", AppendAttribute("nonce", cspNonce)).transform(nextResponse);
81
+ };
82
+ };
83
+
84
+ export {
85
+ useContentSecurityPolicy
86
+ };
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  LOCKDOWN_TEST_EXPIRY_MS
3
- } from "./chunk-6O25N45F.js";
3
+ } from "./chunk-2WPLLVUI.js";
4
4
  import {
5
5
  printMessage
6
- } from "./chunk-QGXPAVOA.js";
6
+ } from "./chunk-QUVGY2YI.js";
7
7
 
8
8
  // src/utils/build-lock-page-url.ts
9
9
  function normalizeLockPageSlug(lockPageSlug) {
@@ -210,7 +210,7 @@ var insertErrorLogs = async (context, error) => {
210
210
  { html: true }
211
211
  );
212
212
  }
213
- }).transform(await fetch(context.request));
213
+ }).transform(await fetch(context.request.clone()));
214
214
  };
215
215
 
216
216
  // src/utils/cloudflare/make-csp-header.ts
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  MemoryCache,
3
3
  debug
4
- } from "./chunk-HIGZSGKS.js";
4
+ } from "./chunk-KE2UVIYR.js";
5
5
  import {
6
6
  APPWARDEN_CACHE_KEY,
7
7
  APPWARDEN_TEST_ROUTE
8
- } from "./chunk-6O25N45F.js";
8
+ } from "./chunk-2WPLLVUI.js";
9
9
  import {
10
10
  deleteEdgeValue,
11
11
  getLockValue,
12
12
  store,
13
13
  syncEdgeValue
14
- } from "./chunk-QGXPAVOA.js";
14
+ } from "./chunk-QUVGY2YI.js";
15
15
 
16
16
  // src/core/check-lock-status.ts
17
17
  var createContext = async (config) => {
@@ -3,29 +3,29 @@ import {
3
3
  } from "../chunk-XFG6SUSV.js";
4
4
  import {
5
5
  useContentSecurityPolicy
6
- } from "../chunk-ZQNXNGLV.js";
6
+ } from "../chunk-K7ZIT3FM.js";
7
7
  import {
8
8
  getNowMs
9
9
  } from "../chunk-X7WZVYQS.js";
10
10
  import {
11
11
  checkLockStatus
12
- } from "../chunk-TASPCREA.js";
12
+ } from "../chunk-ZOYE6D3A.js";
13
13
  import {
14
14
  TEMPORARY_REDIRECT_STATUS,
15
15
  buildLockPageUrl,
16
16
  createRedirect,
17
17
  debug,
18
18
  isOnLockPage
19
- } from "../chunk-HIGZSGKS.js";
19
+ } from "../chunk-KE2UVIYR.js";
20
20
  import {
21
21
  UseCSPInputSchema,
22
22
  isHTMLRequest
23
- } from "../chunk-6O25N45F.js";
23
+ } from "../chunk-2WPLLVUI.js";
24
24
  import {
25
25
  AppwardenApiTokenSchema,
26
26
  BooleanSchema,
27
27
  printMessage
28
- } from "../chunk-QGXPAVOA.js";
28
+ } from "../chunk-QUVGY2YI.js";
29
29
 
30
30
  // src/adapters/astro-cloudflare.ts
31
31
  import { waitUntil } from "cloudflare:workers";
@@ -3,22 +3,22 @@ import {
3
3
  } from "../chunk-X7WZVYQS.js";
4
4
  import {
5
5
  checkLockStatus
6
- } from "../chunk-TASPCREA.js";
6
+ } from "../chunk-ZOYE6D3A.js";
7
7
  import {
8
8
  TEMPORARY_REDIRECT_STATUS,
9
9
  buildLockPageUrl,
10
10
  debug,
11
11
  isOnLockPage
12
- } from "../chunk-HIGZSGKS.js";
12
+ } from "../chunk-KE2UVIYR.js";
13
13
  import {
14
14
  UseCSPInputSchema,
15
15
  isHTMLRequest
16
- } from "../chunk-6O25N45F.js";
16
+ } from "../chunk-2WPLLVUI.js";
17
17
  import {
18
18
  AppwardenApiTokenSchema,
19
19
  BooleanSchema,
20
20
  printMessage
21
- } from "../chunk-QGXPAVOA.js";
21
+ } from "../chunk-QUVGY2YI.js";
22
22
 
23
23
  // src/adapters/nextjs-cloudflare.ts
24
24
  import {
@@ -101,7 +101,7 @@ function createAppwardenMiddleware(configFn) {
101
101
  debugFn(
102
102
  `Applying CSP headers in ${config.contentSecurityPolicy.mode} mode`
103
103
  );
104
- const { makeCSPHeader } = await import("../cloudflare-PE3JKP3X.js");
104
+ const { makeCSPHeader } = await import("../cloudflare-JVRRPVRP.js");
105
105
  const [headerName, headerValue] = makeCSPHeader(
106
106
  "",
107
107
  config.contentSecurityPolicy.directives,
@@ -3,28 +3,28 @@ import {
3
3
  } from "../chunk-XFG6SUSV.js";
4
4
  import {
5
5
  useContentSecurityPolicy
6
- } from "../chunk-ZQNXNGLV.js";
6
+ } from "../chunk-K7ZIT3FM.js";
7
7
  import {
8
8
  getNowMs
9
9
  } from "../chunk-X7WZVYQS.js";
10
10
  import {
11
11
  checkLockStatus
12
- } from "../chunk-TASPCREA.js";
12
+ } from "../chunk-ZOYE6D3A.js";
13
13
  import {
14
14
  buildLockPageUrl,
15
15
  createRedirect,
16
16
  debug,
17
17
  isOnLockPage
18
- } from "../chunk-HIGZSGKS.js";
18
+ } from "../chunk-KE2UVIYR.js";
19
19
  import {
20
20
  UseCSPInputSchema,
21
21
  isHTMLRequest
22
- } from "../chunk-6O25N45F.js";
22
+ } from "../chunk-2WPLLVUI.js";
23
23
  import {
24
24
  AppwardenApiTokenSchema,
25
25
  BooleanSchema,
26
26
  printMessage
27
- } from "../chunk-QGXPAVOA.js";
27
+ } from "../chunk-QUVGY2YI.js";
28
28
 
29
29
  // src/adapters/react-router-cloudflare.ts
30
30
  import { waitUntil } from "cloudflare:workers";
@@ -3,28 +3,28 @@ import {
3
3
  } from "../chunk-XFG6SUSV.js";
4
4
  import {
5
5
  useContentSecurityPolicy
6
- } from "../chunk-ZQNXNGLV.js";
6
+ } from "../chunk-K7ZIT3FM.js";
7
7
  import {
8
8
  getNowMs
9
9
  } from "../chunk-X7WZVYQS.js";
10
10
  import {
11
11
  checkLockStatus
12
- } from "../chunk-TASPCREA.js";
12
+ } from "../chunk-ZOYE6D3A.js";
13
13
  import {
14
14
  buildLockPageUrl,
15
15
  createRedirect,
16
16
  debug,
17
17
  isOnLockPage
18
- } from "../chunk-HIGZSGKS.js";
18
+ } from "../chunk-KE2UVIYR.js";
19
19
  import {
20
20
  UseCSPInputSchema,
21
21
  isHTMLRequest
22
- } from "../chunk-6O25N45F.js";
22
+ } from "../chunk-2WPLLVUI.js";
23
23
  import {
24
24
  AppwardenApiTokenSchema,
25
25
  BooleanSchema,
26
26
  printMessage
27
- } from "../chunk-QGXPAVOA.js";
27
+ } from "../chunk-QUVGY2YI.js";
28
28
 
29
29
  // src/adapters/tanstack-start-cloudflare.ts
30
30
  import { waitUntil } from "cloudflare:workers";
@@ -11,7 +11,7 @@ import {
11
11
  makeCSPHeader,
12
12
  store,
13
13
  syncEdgeValue
14
- } from "./chunk-QGXPAVOA.js";
14
+ } from "./chunk-QUVGY2YI.js";
15
15
  export {
16
16
  CSP_KEYWORDS,
17
17
  autoQuoteCSPDirectiveArray,
package/cloudflare.d.ts CHANGED
@@ -260,8 +260,10 @@ declare const UseAppwardenInputSchema: z.ZodObject<{
260
260
  "require-trusted-types-for"?: string | boolean | string[] | undefined;
261
261
  };
262
262
  }>>>;
263
+ debug: z.ZodOptional<z.ZodEffects<z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodBoolean]>>, boolean, string | boolean | undefined>>;
263
264
  }, "strip", z.ZodTypeAny, {
264
265
  lockPageSlug: string;
266
+ debug?: boolean | undefined;
265
267
  contentSecurityPolicy?: {
266
268
  mode: "disabled" | "report-only" | "enforced";
267
269
  directives?: {
@@ -295,6 +297,7 @@ declare const UseAppwardenInputSchema: z.ZodObject<{
295
297
  } | undefined;
296
298
  }, {
297
299
  lockPageSlug: string;
300
+ debug?: string | boolean | undefined;
298
301
  contentSecurityPolicy?: {
299
302
  mode: "disabled" | "report-only" | "enforced";
300
303
  directives: string | {
@@ -335,6 +338,7 @@ declare const UseAppwardenInputSchema: z.ZodObject<{
335
338
  lockPageSlug?: string | undefined;
336
339
  multidomainConfig?: Record<string, {
337
340
  lockPageSlug: string;
341
+ debug?: boolean | undefined;
338
342
  contentSecurityPolicy?: {
339
343
  mode: "disabled" | "report-only" | "enforced";
340
344
  directives?: {
@@ -374,6 +378,7 @@ declare const UseAppwardenInputSchema: z.ZodObject<{
374
378
  lockPageSlug?: string | undefined;
375
379
  multidomainConfig?: Record<string, {
376
380
  lockPageSlug: string;
381
+ debug?: string | boolean | undefined;
377
382
  contentSecurityPolicy?: {
378
383
  mode: "disabled" | "report-only" | "enforced";
379
384
  directives: string | {
package/cloudflare.js CHANGED
@@ -1,25 +1,25 @@
1
1
  import {
2
2
  useContentSecurityPolicy
3
- } from "./chunk-ZQNXNGLV.js";
3
+ } from "./chunk-K7ZIT3FM.js";
4
4
  import {
5
5
  checkLockStatus
6
- } from "./chunk-TASPCREA.js";
6
+ } from "./chunk-ZOYE6D3A.js";
7
7
  import {
8
8
  buildLockPageUrl,
9
9
  createRedirect,
10
10
  debug,
11
11
  isOnLockPage
12
- } from "./chunk-HIGZSGKS.js";
12
+ } from "./chunk-KE2UVIYR.js";
13
13
  import {
14
14
  UseCSPInputSchema,
15
15
  isHTMLRequest
16
- } from "./chunk-6O25N45F.js";
16
+ } from "./chunk-2WPLLVUI.js";
17
17
  import {
18
18
  AppwardenApiTokenSchema,
19
19
  BooleanSchema,
20
20
  insertErrorLogs,
21
21
  printMessage
22
- } from "./chunk-QGXPAVOA.js";
22
+ } from "./chunk-QUVGY2YI.js";
23
23
 
24
24
  // src/runners/appwarden-on-cloudflare.ts
25
25
  import { ZodError } from "zod";
@@ -33,7 +33,8 @@ var AppwardenMultidomainConfigSchema = z.record(
33
33
  z.string(),
34
34
  z.object({
35
35
  lockPageSlug: z.string(),
36
- contentSecurityPolicy: z.lazy(() => UseCSPInputSchema).optional()
36
+ contentSecurityPolicy: z.lazy(() => UseCSPInputSchema).optional(),
37
+ debug: BooleanSchema.optional()
37
38
  })
38
39
  );
39
40
  var UseAppwardenInputSchema = z.object({
@@ -81,6 +82,9 @@ var useAppwarden = (input) => async (context, next) => {
81
82
  let shouldCallNext = true;
82
83
  try {
83
84
  const requestUrl = new URL(request.url);
85
+ if (request.method.toUpperCase() === "OPTIONS") {
86
+ return;
87
+ }
84
88
  if (!isHTMLRequest(request)) {
85
89
  return;
86
90
  }
@@ -121,11 +125,7 @@ var useAppwarden = (input) => async (context, next) => {
121
125
 
122
126
  // src/middlewares/use-fetch-origin.ts
123
127
  var useFetchOrigin = () => async (context, next) => {
124
- context.response = await fetch(
125
- new Request(context.request, {
126
- redirect: "manual"
127
- })
128
- );
128
+ context.response = await fetch(new Request(context.request));
129
129
  await next();
130
130
  };
131
131
 
@@ -147,16 +147,20 @@ var appwardenOnCloudflare = (inputFn) => async (request, env, ctx) => {
147
147
  return insertErrorLogs(tempContext, parsedInput.error);
148
148
  }
149
149
  const input = parsedInput.data({ env, ctx, cf: {} });
150
+ const domainDebug = input.multidomainConfig?.[requestUrl.hostname]?.debug ?? input.debug ?? false;
150
151
  const context = {
151
152
  request,
152
153
  hostname: requestUrl.hostname,
153
154
  response: new Response("Unhandled response"),
154
155
  // https://developers.cloudflare.com/workers/observability/errors/#illegal-invocation-errors
155
156
  waitUntil: (fn) => ctx.waitUntil(fn),
156
- debug: debug(input.debug ?? false)
157
+ debug: debug(domainDebug)
157
158
  };
158
159
  try {
159
- const pipeline = [useAppwarden(input), useFetchOrigin()];
160
+ const pipeline = [
161
+ useAppwarden({ ...input, debug: domainDebug }),
162
+ useFetchOrigin()
163
+ ];
160
164
  const cspConfig = input.multidomainConfig?.[requestUrl.hostname]?.contentSecurityPolicy;
161
165
  if (cspConfig) {
162
166
  pipeline.push(useContentSecurityPolicy(cspConfig));
package/index.js CHANGED
@@ -5,14 +5,14 @@ import {
5
5
  } from "./chunk-QEFORWCW.js";
6
6
  import {
7
7
  useContentSecurityPolicy
8
- } from "./chunk-ZQNXNGLV.js";
8
+ } from "./chunk-K7ZIT3FM.js";
9
9
  import {
10
10
  APPWARDEN_CACHE_KEY,
11
11
  CSPDirectivesSchema,
12
12
  CSPModeSchema,
13
13
  LOCKDOWN_TEST_EXPIRY_MS
14
- } from "./chunk-6O25N45F.js";
15
- import "./chunk-QGXPAVOA.js";
14
+ } from "./chunk-2WPLLVUI.js";
15
+ import "./chunk-QUVGY2YI.js";
16
16
  export {
17
17
  APPWARDEN_CACHE_KEY,
18
18
  CSPDirectivesSchema,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appwarden/middleware",
3
- "version": "3.10.1",
3
+ "version": "3.11.0",
4
4
  "description": "Instantly disable all user interaction with your app deployed on Cloudflare or Vercel",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/vercel.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  buildLockPageUrl,
9
9
  debug,
10
10
  isOnLockPage
11
- } from "./chunk-HIGZSGKS.js";
11
+ } from "./chunk-KE2UVIYR.js";
12
12
  import {
13
13
  APPWARDEN_CACHE_KEY,
14
14
  CSPDirectivesSchema,
@@ -16,13 +16,13 @@ import {
16
16
  errors,
17
17
  globalErrors,
18
18
  isHTMLRequest
19
- } from "./chunk-6O25N45F.js";
19
+ } from "./chunk-2WPLLVUI.js";
20
20
  import {
21
21
  LockValue,
22
22
  getErrors,
23
23
  makeCSPHeader,
24
24
  printMessage
25
- } from "./chunk-QGXPAVOA.js";
25
+ } from "./chunk-QUVGY2YI.js";
26
26
 
27
27
  // src/runners/appwarden-on-vercel.ts
28
28
  import { waitUntil } from "@vercel/functions";
package/chunk-ZQNXNGLV.js DELETED
@@ -1,50 +0,0 @@
1
- import {
2
- UseCSPInputSchema,
3
- isHTMLResponse
4
- } from "./chunk-6O25N45F.js";
5
- import {
6
- makeCSPHeader
7
- } from "./chunk-QGXPAVOA.js";
8
-
9
- // src/middlewares/use-content-security-policy.ts
10
- var AppendAttribute = (attribute, nonce) => ({
11
- element: function(element) {
12
- element.setAttribute(attribute, nonce);
13
- }
14
- });
15
- var useContentSecurityPolicy = (input) => {
16
- const parsedInput = UseCSPInputSchema.safeParse(input);
17
- if (!parsedInput.success) {
18
- throw parsedInput.error;
19
- }
20
- const config = parsedInput.data;
21
- return async (context, next) => {
22
- await next();
23
- const { response } = context;
24
- if (
25
- // if the csp is disabled
26
- !["enforced", "report-only"].includes(config.mode)
27
- ) {
28
- context.debug("CSP is disabled");
29
- return;
30
- }
31
- if (response.headers.has("Content-Type") && !isHTMLResponse(response)) {
32
- return;
33
- }
34
- const cspNonce = crypto.randomUUID();
35
- const [cspHeaderName, cspHeaderValue] = makeCSPHeader(
36
- cspNonce,
37
- config.directives,
38
- config.mode
39
- );
40
- context.debug(`Applying CSP in ${config.mode} mode`);
41
- const nextResponse = new Response(response.body, response);
42
- nextResponse.headers.set(cspHeaderName, cspHeaderValue);
43
- nextResponse.headers.set("content-type", "text/html; charset=utf-8");
44
- context.response = new HTMLRewriter().on("style", AppendAttribute("nonce", cspNonce)).on("script", AppendAttribute("nonce", cspNonce)).transform(nextResponse);
45
- };
46
- };
47
-
48
- export {
49
- useContentSecurityPolicy
50
- };