@middy/http-cors 7.0.3 → 7.1.1

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
@@ -1,5 +1,5 @@
1
1
  <div align="center">
2
- <h1>Middy CORS middleware</h1>
2
+ <h1>Middy `http-cors` middleware</h1>
3
3
  <img alt="Middy logo" src="https://raw.githubusercontent.com/middyjs/middy/main/docs/img/middy-logo.svg"/>
4
4
  <p><strong>CORS middleware for the middy framework, the stylish Node.js middleware engine for AWS Lambda</strong></p>
5
5
  <p>
package/index.d.ts CHANGED
@@ -12,9 +12,10 @@ export interface Options {
12
12
  origins?: string[];
13
13
  exposeHeaders?: string;
14
14
  maxAge?: number | string;
15
- requestHeaders?: string;
16
- requestMethods?: string;
15
+ requestHeaders?: string[];
16
+ requestMethods?: string[];
17
17
  cacheControl?: string;
18
+ vary?: string;
18
19
  }
19
20
 
20
21
  declare function httpCors(options?: Options): middy.MiddlewareObj;
package/index.js CHANGED
@@ -2,6 +2,35 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  import { normalizeHttpResponse } from "@middy/util";
4
4
 
5
+ const hostnameToPunycode = (hostname) => {
6
+ const placeholder = "-_ANY_-";
7
+ const tempHostname = hostname.replace(/\*/g, placeholder);
8
+ try {
9
+ const url = new URL(`https://${tempHostname}`);
10
+ return url.host.replaceAll(placeholder.toLowerCase(), "*");
11
+ } catch {
12
+ return hostname;
13
+ }
14
+ };
15
+
16
+ const originToPunycode = (origin) => {
17
+ if (!origin || origin === "*") return origin;
18
+ const match = origin.match(/^(https?:\/\/)(.+)$/);
19
+ if (!match) return origin;
20
+ const [, protocol, host] = match;
21
+ return protocol + hostnameToPunycode(host);
22
+ };
23
+
24
+ // CORS-safelisted request headers
25
+ // https://developer.mozilla.org/en-US/docs/Glossary/CORS-safelisted_request_header
26
+ const corsSafelistedRequestHeaders = [
27
+ "accept",
28
+ "accept-language",
29
+ "content-language",
30
+ "content-type",
31
+ "range",
32
+ ];
33
+
5
34
  const defaults = {
6
35
  disableBeforePreflightResponse: true,
7
36
  getOrigin: undefined, // default inserted below
@@ -12,6 +41,8 @@ const defaults = {
12
41
  origins: [],
13
42
  exposeHeaders: undefined,
14
43
  maxAge: undefined,
44
+ requestHeaders: undefined,
45
+ requestMethods: undefined,
15
46
  cacheControl: undefined,
16
47
  vary: undefined,
17
48
  };
@@ -46,15 +77,19 @@ const httpCorsMiddleware = (opts = {}) => {
46
77
  ...opts,
47
78
  };
48
79
 
80
+ options.requestHeaders = options.requestHeaders?.map((v) => v.toLowerCase());
81
+ options.requestMethods = options.requestMethods?.map((v) => v.toUpperCase());
82
+
49
83
  let originAny = false;
50
84
  let originMany = options.origins.length > 1;
51
85
  const originStatic = {};
52
86
  const originDynamic = [];
53
87
 
54
- for (const origin of [options.origin, ...options.origins]) {
88
+ for (let origin of [options.origin, ...options.origins]) {
55
89
  if (!origin) {
56
90
  continue;
57
91
  }
92
+ origin = originToPunycode(origin);
58
93
  // All
59
94
  if (origin === "*") {
60
95
  originAny = true;
@@ -67,7 +102,6 @@ const httpCorsMiddleware = (opts = {}) => {
67
102
  }
68
103
  originMany = true;
69
104
  // Dynamic
70
- // TODO: IDN -> puncycode not handled, add in if requested
71
105
  const regExpStr = origin
72
106
  .replace(/[.+?^${}()|[\]\\]/g, "\\$&")
73
107
  .replaceAll("*", "[^.]*");
@@ -149,6 +183,40 @@ const httpCorsMiddleware = (opts = {}) => {
149
183
  );
150
184
  if (method === "OPTIONS") {
151
185
  normalizeHttpResponse(request);
186
+ const eventHeaders = request.event.headers ?? {};
187
+ const requestMethod =
188
+ eventHeaders["Access-Control-Request-Method"] ??
189
+ eventHeaders["access-control-request-method"];
190
+
191
+ if (options.requestMethods?.length && requestMethod) {
192
+ if (!options.requestMethods.includes(requestMethod)) {
193
+ request.response.statusCode = 204;
194
+ request.response.headers = {};
195
+ return request.response;
196
+ }
197
+ }
198
+
199
+ const requestHeadersValue =
200
+ eventHeaders["Access-Control-Request-Headers"] ??
201
+ eventHeaders["access-control-request-headers"];
202
+
203
+ if (options.requestHeaders?.length && requestHeadersValue) {
204
+ const requestedHeaders = requestHeadersValue
205
+ .split(",")
206
+ .map((h) => h.trim().toLowerCase());
207
+ const nonSafelistedHeaders = requestedHeaders.filter(
208
+ (h) => !corsSafelistedRequestHeaders.includes(h),
209
+ );
210
+ const hasDisallowedHeader = nonSafelistedHeaders.some(
211
+ (h) => !options.requestHeaders.includes(h),
212
+ );
213
+ if (hasDisallowedHeader) {
214
+ request.response.statusCode = 204;
215
+ request.response.headers = {};
216
+ return request.response;
217
+ }
218
+ }
219
+
152
220
  const headers = {};
153
221
  modifyHeaders(headers, options, request);
154
222
  request.response.headers = headers;
@@ -178,7 +246,7 @@ const getVersionHttpMethod = {
178
246
  "2.0": (event) => event.requestContext.http.method,
179
247
  };
180
248
 
181
- // header in offical name, lowercase varient handeled
249
+ // header in official name, lowercase variant handled
182
250
  const addHeaderPart = (headers, header, value) => {
183
251
  if (!value) return;
184
252
  const headerLower = header.toLowerCase();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@middy/http-cors",
3
- "version": "7.0.3",
3
+ "version": "7.1.1",
4
4
  "description": "CORS (Cross-Origin Resource Sharing) middleware for the middy framework",
5
5
  "type": "module",
6
6
  "engines": {
@@ -65,9 +65,10 @@
65
65
  },
66
66
  "gitHead": "7a6c0fbb8ab71d6a2171e678697de9f237568431",
67
67
  "dependencies": {
68
- "@middy/util": "7.0.3"
68
+ "@middy/util": "7.1.1"
69
69
  },
70
70
  "devDependencies": {
71
- "@middy/core": "7.0.3"
71
+ "@middy/core": "7.1.1",
72
+ "@types/aws-lambda": "^8.0.0"
72
73
  }
73
74
  }