nextjs-secure 0.2.0 → 0.3.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 +115 -0
- package/dist/headers.cjs +277 -7
- package/dist/headers.cjs.map +1 -1
- package/dist/headers.d.cts +162 -25
- package/dist/headers.d.ts +162 -25
- package/dist/headers.js +267 -6
- package/dist/headers.js.map +1 -1
- package/dist/index.cjs +280 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +271 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1178,29 +1178,307 @@ async function validateCSRF(req, config = {}) {
|
|
|
1178
1178
|
return { valid: true };
|
|
1179
1179
|
}
|
|
1180
1180
|
|
|
1181
|
+
// src/middleware/headers/builder.ts
|
|
1182
|
+
function buildCSP(policy) {
|
|
1183
|
+
const directives = [];
|
|
1184
|
+
const directiveMap = {
|
|
1185
|
+
defaultSrc: "default-src",
|
|
1186
|
+
scriptSrc: "script-src",
|
|
1187
|
+
styleSrc: "style-src",
|
|
1188
|
+
imgSrc: "img-src",
|
|
1189
|
+
fontSrc: "font-src",
|
|
1190
|
+
connectSrc: "connect-src",
|
|
1191
|
+
mediaSrc: "media-src",
|
|
1192
|
+
objectSrc: "object-src",
|
|
1193
|
+
frameSrc: "frame-src",
|
|
1194
|
+
childSrc: "child-src",
|
|
1195
|
+
workerSrc: "worker-src",
|
|
1196
|
+
frameAncestors: "frame-ancestors",
|
|
1197
|
+
formAction: "form-action",
|
|
1198
|
+
baseUri: "base-uri",
|
|
1199
|
+
manifestSrc: "manifest-src",
|
|
1200
|
+
reportUri: "report-uri",
|
|
1201
|
+
reportTo: "report-to"
|
|
1202
|
+
};
|
|
1203
|
+
for (const [key, directive] of Object.entries(directiveMap)) {
|
|
1204
|
+
const value = policy[key];
|
|
1205
|
+
if (value !== void 0 && value !== false) {
|
|
1206
|
+
if (Array.isArray(value)) {
|
|
1207
|
+
directives.push(`${directive} ${value.join(" ")}`);
|
|
1208
|
+
} else if (typeof value === "string") {
|
|
1209
|
+
directives.push(`${directive} ${value}`);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
if (policy.upgradeInsecureRequests) {
|
|
1214
|
+
directives.push("upgrade-insecure-requests");
|
|
1215
|
+
}
|
|
1216
|
+
if (policy.blockAllMixedContent) {
|
|
1217
|
+
directives.push("block-all-mixed-content");
|
|
1218
|
+
}
|
|
1219
|
+
return directives.join("; ");
|
|
1220
|
+
}
|
|
1221
|
+
function buildHSTS(config) {
|
|
1222
|
+
let value = `max-age=${config.maxAge}`;
|
|
1223
|
+
if (config.includeSubDomains) {
|
|
1224
|
+
value += "; includeSubDomains";
|
|
1225
|
+
}
|
|
1226
|
+
if (config.preload) {
|
|
1227
|
+
value += "; preload";
|
|
1228
|
+
}
|
|
1229
|
+
return value;
|
|
1230
|
+
}
|
|
1231
|
+
function buildPermissionsPolicy(policy) {
|
|
1232
|
+
const directives = [];
|
|
1233
|
+
const featureMap = {
|
|
1234
|
+
accelerometer: "accelerometer",
|
|
1235
|
+
ambientLightSensor: "ambient-light-sensor",
|
|
1236
|
+
autoplay: "autoplay",
|
|
1237
|
+
battery: "battery",
|
|
1238
|
+
camera: "camera",
|
|
1239
|
+
displayCapture: "display-capture",
|
|
1240
|
+
documentDomain: "document-domain",
|
|
1241
|
+
encryptedMedia: "encrypted-media",
|
|
1242
|
+
fullscreen: "fullscreen",
|
|
1243
|
+
geolocation: "geolocation",
|
|
1244
|
+
gyroscope: "gyroscope",
|
|
1245
|
+
magnetometer: "magnetometer",
|
|
1246
|
+
microphone: "microphone",
|
|
1247
|
+
midi: "midi",
|
|
1248
|
+
payment: "payment",
|
|
1249
|
+
pictureInPicture: "picture-in-picture",
|
|
1250
|
+
publicKeyCredentialsGet: "publickey-credentials-get",
|
|
1251
|
+
screenWakeLock: "screen-wake-lock",
|
|
1252
|
+
syncXhr: "sync-xhr",
|
|
1253
|
+
usb: "usb",
|
|
1254
|
+
webShare: "web-share",
|
|
1255
|
+
xrSpatialTracking: "xr-spatial-tracking"
|
|
1256
|
+
};
|
|
1257
|
+
for (const [key, feature] of Object.entries(featureMap)) {
|
|
1258
|
+
const origins = policy[key];
|
|
1259
|
+
if (origins !== void 0) {
|
|
1260
|
+
if (origins.length === 0) {
|
|
1261
|
+
directives.push(`${feature}=()`);
|
|
1262
|
+
} else {
|
|
1263
|
+
const formatted = origins.map((o) => o === "self" ? "self" : `"${o}"`).join(" ");
|
|
1264
|
+
directives.push(`${feature}=(${formatted})`);
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
return directives.join(", ");
|
|
1269
|
+
}
|
|
1270
|
+
var PRESET_STRICT = {
|
|
1271
|
+
contentSecurityPolicy: {
|
|
1272
|
+
defaultSrc: ["'self'"],
|
|
1273
|
+
scriptSrc: ["'self'"],
|
|
1274
|
+
styleSrc: ["'self'"],
|
|
1275
|
+
imgSrc: ["'self'", "data:"],
|
|
1276
|
+
fontSrc: ["'self'"],
|
|
1277
|
+
objectSrc: ["'none'"],
|
|
1278
|
+
frameAncestors: ["'none'"],
|
|
1279
|
+
formAction: ["'self'"],
|
|
1280
|
+
baseUri: ["'self'"],
|
|
1281
|
+
upgradeInsecureRequests: true
|
|
1282
|
+
},
|
|
1283
|
+
strictTransportSecurity: {
|
|
1284
|
+
maxAge: 31536e3,
|
|
1285
|
+
// 1 year
|
|
1286
|
+
includeSubDomains: true,
|
|
1287
|
+
preload: true
|
|
1288
|
+
},
|
|
1289
|
+
xFrameOptions: "DENY",
|
|
1290
|
+
xContentTypeOptions: true,
|
|
1291
|
+
xDnsPrefetchControl: "off",
|
|
1292
|
+
xDownloadOptions: true,
|
|
1293
|
+
xPermittedCrossDomainPolicies: "none",
|
|
1294
|
+
referrerPolicy: "strict-origin-when-cross-origin",
|
|
1295
|
+
crossOriginOpenerPolicy: "same-origin",
|
|
1296
|
+
crossOriginEmbedderPolicy: "require-corp",
|
|
1297
|
+
crossOriginResourcePolicy: "same-origin",
|
|
1298
|
+
permissionsPolicy: {
|
|
1299
|
+
camera: [],
|
|
1300
|
+
microphone: [],
|
|
1301
|
+
geolocation: [],
|
|
1302
|
+
payment: []
|
|
1303
|
+
},
|
|
1304
|
+
originAgentCluster: true
|
|
1305
|
+
};
|
|
1306
|
+
var PRESET_RELAXED = {
|
|
1307
|
+
contentSecurityPolicy: {
|
|
1308
|
+
defaultSrc: ["'self'"],
|
|
1309
|
+
scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
|
|
1310
|
+
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
1311
|
+
imgSrc: ["'self'", "data:", "blob:", "https:"],
|
|
1312
|
+
fontSrc: ["'self'", "https:", "data:"],
|
|
1313
|
+
connectSrc: ["'self'", "https:", "wss:"],
|
|
1314
|
+
frameSrc: ["'self'"]
|
|
1315
|
+
},
|
|
1316
|
+
strictTransportSecurity: {
|
|
1317
|
+
maxAge: 86400,
|
|
1318
|
+
// 1 day
|
|
1319
|
+
includeSubDomains: false
|
|
1320
|
+
},
|
|
1321
|
+
xFrameOptions: "SAMEORIGIN",
|
|
1322
|
+
xContentTypeOptions: true,
|
|
1323
|
+
referrerPolicy: "no-referrer-when-downgrade"
|
|
1324
|
+
};
|
|
1325
|
+
var PRESET_API = {
|
|
1326
|
+
contentSecurityPolicy: {
|
|
1327
|
+
defaultSrc: ["'none'"],
|
|
1328
|
+
frameAncestors: ["'none'"]
|
|
1329
|
+
},
|
|
1330
|
+
strictTransportSecurity: {
|
|
1331
|
+
maxAge: 31536e3,
|
|
1332
|
+
includeSubDomains: true
|
|
1333
|
+
},
|
|
1334
|
+
xFrameOptions: "DENY",
|
|
1335
|
+
xContentTypeOptions: true,
|
|
1336
|
+
referrerPolicy: "no-referrer",
|
|
1337
|
+
crossOriginResourcePolicy: "same-origin"
|
|
1338
|
+
};
|
|
1339
|
+
function getPreset(name) {
|
|
1340
|
+
switch (name) {
|
|
1341
|
+
case "strict":
|
|
1342
|
+
return PRESET_STRICT;
|
|
1343
|
+
case "relaxed":
|
|
1344
|
+
return PRESET_RELAXED;
|
|
1345
|
+
case "api":
|
|
1346
|
+
return PRESET_API;
|
|
1347
|
+
default:
|
|
1348
|
+
return PRESET_STRICT;
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
function buildHeaders(config) {
|
|
1352
|
+
const headers = new Headers();
|
|
1353
|
+
if (config.contentSecurityPolicy) {
|
|
1354
|
+
const csp = buildCSP(config.contentSecurityPolicy);
|
|
1355
|
+
if (csp) headers.set("Content-Security-Policy", csp);
|
|
1356
|
+
}
|
|
1357
|
+
if (config.strictTransportSecurity) {
|
|
1358
|
+
headers.set("Strict-Transport-Security", buildHSTS(config.strictTransportSecurity));
|
|
1359
|
+
}
|
|
1360
|
+
if (config.xFrameOptions) {
|
|
1361
|
+
headers.set("X-Frame-Options", config.xFrameOptions);
|
|
1362
|
+
}
|
|
1363
|
+
if (config.xContentTypeOptions) {
|
|
1364
|
+
headers.set("X-Content-Type-Options", "nosniff");
|
|
1365
|
+
}
|
|
1366
|
+
if (config.xDnsPrefetchControl) {
|
|
1367
|
+
headers.set("X-DNS-Prefetch-Control", config.xDnsPrefetchControl);
|
|
1368
|
+
}
|
|
1369
|
+
if (config.xDownloadOptions) {
|
|
1370
|
+
headers.set("X-Download-Options", "noopen");
|
|
1371
|
+
}
|
|
1372
|
+
if (config.xPermittedCrossDomainPolicies) {
|
|
1373
|
+
headers.set("X-Permitted-Cross-Domain-Policies", config.xPermittedCrossDomainPolicies);
|
|
1374
|
+
}
|
|
1375
|
+
if (config.referrerPolicy) {
|
|
1376
|
+
const value = Array.isArray(config.referrerPolicy) ? config.referrerPolicy.join(", ") : config.referrerPolicy;
|
|
1377
|
+
headers.set("Referrer-Policy", value);
|
|
1378
|
+
}
|
|
1379
|
+
if (config.crossOriginOpenerPolicy) {
|
|
1380
|
+
headers.set("Cross-Origin-Opener-Policy", config.crossOriginOpenerPolicy);
|
|
1381
|
+
}
|
|
1382
|
+
if (config.crossOriginEmbedderPolicy) {
|
|
1383
|
+
headers.set("Cross-Origin-Embedder-Policy", config.crossOriginEmbedderPolicy);
|
|
1384
|
+
}
|
|
1385
|
+
if (config.crossOriginResourcePolicy) {
|
|
1386
|
+
headers.set("Cross-Origin-Resource-Policy", config.crossOriginResourcePolicy);
|
|
1387
|
+
}
|
|
1388
|
+
if (config.permissionsPolicy) {
|
|
1389
|
+
const pp = buildPermissionsPolicy(config.permissionsPolicy);
|
|
1390
|
+
if (pp) headers.set("Permissions-Policy", pp);
|
|
1391
|
+
}
|
|
1392
|
+
if (config.originAgentCluster) {
|
|
1393
|
+
headers.set("Origin-Agent-Cluster", "?1");
|
|
1394
|
+
}
|
|
1395
|
+
return headers;
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
// src/middleware/headers/middleware.ts
|
|
1399
|
+
function mergeConfigs(base, custom) {
|
|
1400
|
+
return {
|
|
1401
|
+
...base,
|
|
1402
|
+
...custom,
|
|
1403
|
+
// Deep merge CSP if both exist
|
|
1404
|
+
contentSecurityPolicy: custom.contentSecurityPolicy === false ? false : custom.contentSecurityPolicy ? base.contentSecurityPolicy ? { ...base.contentSecurityPolicy, ...custom.contentSecurityPolicy } : custom.contentSecurityPolicy : base.contentSecurityPolicy,
|
|
1405
|
+
// Deep merge HSTS if both exist
|
|
1406
|
+
strictTransportSecurity: custom.strictTransportSecurity === false ? false : custom.strictTransportSecurity ? base.strictTransportSecurity ? { ...base.strictTransportSecurity, ...custom.strictTransportSecurity } : custom.strictTransportSecurity : base.strictTransportSecurity,
|
|
1407
|
+
// Deep merge Permissions-Policy if both exist
|
|
1408
|
+
permissionsPolicy: custom.permissionsPolicy === false ? false : custom.permissionsPolicy ? base.permissionsPolicy ? { ...base.permissionsPolicy, ...custom.permissionsPolicy } : custom.permissionsPolicy : base.permissionsPolicy
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1411
|
+
function withSecurityHeaders(handler, options = {}) {
|
|
1412
|
+
const { preset, config, override = false } = options;
|
|
1413
|
+
let baseConfig = preset ? getPreset(preset) : PRESET_STRICT;
|
|
1414
|
+
if (config) {
|
|
1415
|
+
baseConfig = mergeConfigs(baseConfig, config);
|
|
1416
|
+
}
|
|
1417
|
+
const securityHeaders = buildHeaders(baseConfig);
|
|
1418
|
+
return async (req) => {
|
|
1419
|
+
const response = await handler(req);
|
|
1420
|
+
const newHeaders = new Headers(response.headers);
|
|
1421
|
+
securityHeaders.forEach((value, key) => {
|
|
1422
|
+
if (override || !newHeaders.has(key)) {
|
|
1423
|
+
newHeaders.set(key, value);
|
|
1424
|
+
}
|
|
1425
|
+
});
|
|
1426
|
+
return new Response(response.body, {
|
|
1427
|
+
status: response.status,
|
|
1428
|
+
statusText: response.statusText,
|
|
1429
|
+
headers: newHeaders
|
|
1430
|
+
});
|
|
1431
|
+
};
|
|
1432
|
+
}
|
|
1433
|
+
function createSecurityHeaders(options = {}) {
|
|
1434
|
+
const { preset, config } = options;
|
|
1435
|
+
let baseConfig = preset ? getPreset(preset) : PRESET_STRICT;
|
|
1436
|
+
if (config) {
|
|
1437
|
+
baseConfig = mergeConfigs(baseConfig, config);
|
|
1438
|
+
}
|
|
1439
|
+
return buildHeaders(baseConfig);
|
|
1440
|
+
}
|
|
1441
|
+
function createSecurityHeadersObject(options = {}) {
|
|
1442
|
+
const headers = createSecurityHeaders(options);
|
|
1443
|
+
const obj = {};
|
|
1444
|
+
headers.forEach((value, key) => {
|
|
1445
|
+
obj[key] = value;
|
|
1446
|
+
});
|
|
1447
|
+
return obj;
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1181
1450
|
// src/index.ts
|
|
1182
|
-
var VERSION = "0.
|
|
1451
|
+
var VERSION = "0.3.0";
|
|
1183
1452
|
|
|
1184
1453
|
exports.AuthenticationError = AuthenticationError;
|
|
1185
1454
|
exports.AuthorizationError = AuthorizationError;
|
|
1186
1455
|
exports.ConfigurationError = ConfigurationError;
|
|
1187
1456
|
exports.CsrfError = CsrfError;
|
|
1188
1457
|
exports.MemoryStore = MemoryStore;
|
|
1458
|
+
exports.PRESET_API = PRESET_API;
|
|
1459
|
+
exports.PRESET_RELAXED = PRESET_RELAXED;
|
|
1460
|
+
exports.PRESET_STRICT = PRESET_STRICT;
|
|
1189
1461
|
exports.RateLimitError = RateLimitError;
|
|
1190
1462
|
exports.SecureError = SecureError;
|
|
1191
1463
|
exports.VERSION = VERSION;
|
|
1192
1464
|
exports.ValidationError = ValidationError;
|
|
1193
1465
|
exports.anonymizeIp = anonymizeIp;
|
|
1466
|
+
exports.buildCSP = buildCSP;
|
|
1467
|
+
exports.buildHSTS = buildHSTS;
|
|
1468
|
+
exports.buildPermissionsPolicy = buildPermissionsPolicy;
|
|
1194
1469
|
exports.checkRateLimit = checkRateLimit;
|
|
1195
1470
|
exports.clearAllRateLimits = clearAllRateLimits;
|
|
1196
1471
|
exports.createCSRFToken = createToken;
|
|
1197
1472
|
exports.createMemoryStore = createMemoryStore;
|
|
1198
1473
|
exports.createRateLimiter = createRateLimiter;
|
|
1474
|
+
exports.createSecurityHeaders = createSecurityHeaders;
|
|
1475
|
+
exports.createSecurityHeadersObject = createSecurityHeadersObject;
|
|
1199
1476
|
exports.formatDuration = formatDuration;
|
|
1200
1477
|
exports.generateCSRF = generateCSRF;
|
|
1201
1478
|
exports.getClientIp = getClientIp;
|
|
1202
1479
|
exports.getGeoInfo = getGeoInfo;
|
|
1203
1480
|
exports.getGlobalMemoryStore = getGlobalMemoryStore;
|
|
1481
|
+
exports.getPreset = getPreset;
|
|
1204
1482
|
exports.getRateLimitStatus = getRateLimitStatus;
|
|
1205
1483
|
exports.isLocalhost = isLocalhost;
|
|
1206
1484
|
exports.isPrivateIp = isPrivateIp;
|
|
@@ -1218,5 +1496,6 @@ exports.validateCSRF = validateCSRF;
|
|
|
1218
1496
|
exports.verifyCSRFToken = verifyToken;
|
|
1219
1497
|
exports.withCSRF = withCSRF;
|
|
1220
1498
|
exports.withRateLimit = withRateLimit;
|
|
1499
|
+
exports.withSecurityHeaders = withSecurityHeaders;
|
|
1221
1500
|
//# sourceMappingURL=index.cjs.map
|
|
1222
1501
|
//# sourceMappingURL=index.cjs.map
|