@server/next 0.33.0 → 0.34.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/index.d.ts +12 -1
- package/index.js +46 -5
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -129,9 +129,20 @@ type Logger = {
|
|
|
129
129
|
};
|
|
130
130
|
type SecurityOptions = {
|
|
131
131
|
trustProxy?: boolean;
|
|
132
|
+
frameguard?: boolean | string;
|
|
133
|
+
noSniff?: boolean;
|
|
134
|
+
referrerPolicy?: boolean | string;
|
|
135
|
+
hsts?: boolean | string;
|
|
136
|
+
xssProtection?: boolean;
|
|
137
|
+
csp?: boolean | string;
|
|
138
|
+
coop?: boolean | string;
|
|
139
|
+
corp?: boolean | string;
|
|
140
|
+
permissionsPolicy?: string;
|
|
132
141
|
};
|
|
133
142
|
type SecuritySettings = {
|
|
134
143
|
trustProxy: boolean;
|
|
144
|
+
headers: Record<string, string>;
|
|
145
|
+
hsts: string | null;
|
|
135
146
|
};
|
|
136
147
|
type OnError = (error: Error, ctx: Context) => Response | Promise<Response>;
|
|
137
148
|
type Options = {
|
|
@@ -150,7 +161,7 @@ type Options = {
|
|
|
150
161
|
onError?: OnError;
|
|
151
162
|
log?: LogLevel | boolean;
|
|
152
163
|
favicon?: string | Bucket;
|
|
153
|
-
security?: SecurityOptions;
|
|
164
|
+
security?: boolean | SecurityOptions;
|
|
154
165
|
body?: BodyMode;
|
|
155
166
|
};
|
|
156
167
|
type Settings = {
|
package/index.js
CHANGED
|
@@ -1358,6 +1358,47 @@ function createLogger(level) {
|
|
|
1358
1358
|
};
|
|
1359
1359
|
}
|
|
1360
1360
|
|
|
1361
|
+
// src/helpers/security.ts
|
|
1362
|
+
function resolveSecurity(security) {
|
|
1363
|
+
const off = security === false;
|
|
1364
|
+
const o = security && typeof security === "object" ? security : {};
|
|
1365
|
+
const val = (v, def) => v === false ? null : v === true || v == null ? def : v;
|
|
1366
|
+
const map2 = off ? {} : {
|
|
1367
|
+
"x-frame-options": val(o.frameguard, "SAMEORIGIN"),
|
|
1368
|
+
"x-content-type-options": o.noSniff === false ? null : "nosniff",
|
|
1369
|
+
"referrer-policy": val(
|
|
1370
|
+
o.referrerPolicy,
|
|
1371
|
+
"strict-origin-when-cross-origin"
|
|
1372
|
+
),
|
|
1373
|
+
"x-xss-protection": o.xssProtection === false ? null : "0",
|
|
1374
|
+
// Opt-in: default off
|
|
1375
|
+
"content-security-policy": val(o.csp, null),
|
|
1376
|
+
"cross-origin-opener-policy": val(o.coop, null),
|
|
1377
|
+
"cross-origin-resource-policy": val(o.corp, null),
|
|
1378
|
+
"permissions-policy": o.permissionsPolicy ?? null
|
|
1379
|
+
};
|
|
1380
|
+
const headers2 = {};
|
|
1381
|
+
for (const key in map2) {
|
|
1382
|
+
const value = map2[key];
|
|
1383
|
+
if (value) headers2[key] = value;
|
|
1384
|
+
}
|
|
1385
|
+
return {
|
|
1386
|
+
trustProxy: o.trustProxy ?? true,
|
|
1387
|
+
headers: headers2,
|
|
1388
|
+
hsts: off ? null : val(o.hsts, "max-age=15552000; includeSubDomains")
|
|
1389
|
+
};
|
|
1390
|
+
}
|
|
1391
|
+
function applySecurity(res, ctx) {
|
|
1392
|
+
const security = ctx.options.security;
|
|
1393
|
+
if (!security) return;
|
|
1394
|
+
for (const key in security.headers) {
|
|
1395
|
+
if (!res.headers.has(key)) res.headers.set(key, security.headers[key]);
|
|
1396
|
+
}
|
|
1397
|
+
if (security.hsts && ctx.platform.production && !res.headers.has("strict-transport-security")) {
|
|
1398
|
+
res.headers.set("strict-transport-security", security.hsts);
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1361
1402
|
// src/helpers/config.ts
|
|
1362
1403
|
function config(options = {}) {
|
|
1363
1404
|
const env2 = globalThis.env;
|
|
@@ -1371,11 +1412,9 @@ function config(options = {}) {
|
|
|
1371
1412
|
// How request bodies are read: parsed into ctx.body by default; `raw` keeps
|
|
1372
1413
|
// the Buffer, `stream` hands the handler the unread web ReadableStream.
|
|
1373
1414
|
body: options.body ?? "parse",
|
|
1374
|
-
//
|
|
1375
|
-
//
|
|
1376
|
-
security:
|
|
1377
|
-
trustProxy: options.security?.trustProxy ?? true
|
|
1378
|
-
}
|
|
1415
|
+
// Secure-by-default response headers + trustProxy for ctx.ip. `false` turns
|
|
1416
|
+
// the added headers off; see resolveSecurity for the defaults.
|
|
1417
|
+
security: resolveSecurity(options.security)
|
|
1379
1418
|
};
|
|
1380
1419
|
options.cors = options.cors || env2.CORS || null;
|
|
1381
1420
|
if (options.cors) {
|
|
@@ -1600,6 +1639,7 @@ async function parseResponse(out, ctx) {
|
|
|
1600
1639
|
throw new Error(`Invalid response type ${out}`);
|
|
1601
1640
|
}
|
|
1602
1641
|
applyCors(out, ctx);
|
|
1642
|
+
applySecurity(out, ctx);
|
|
1603
1643
|
if (ctx.time?.times?.length > 1) {
|
|
1604
1644
|
out.headers.set("Server-Timing", ctx.time.headers());
|
|
1605
1645
|
}
|
|
@@ -1756,6 +1796,7 @@ async function getResponse(app, ctx) {
|
|
|
1756
1796
|
} catch (error) {
|
|
1757
1797
|
const res = await ctx.options.onError(error, ctx);
|
|
1758
1798
|
applyCors(res, ctx);
|
|
1799
|
+
applySecurity(res, ctx);
|
|
1759
1800
|
return res;
|
|
1760
1801
|
}
|
|
1761
1802
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@server/next",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.0",
|
|
4
4
|
"description": "A fully-fledged web server with routing, file uploads, sessions, static files, schema validation, websockets, testing, etc.",
|
|
5
5
|
"homepage": "https://server-js.com/",
|
|
6
6
|
"repository": "github:franciscop/server-next",
|