hono
Version:
Web framework built on Web Standards
154 lines (153 loc) • 5.64 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var secure_headers_exports = {};
__export(secure_headers_exports, {
NONCE: () => NONCE,
secureHeaders: () => secureHeaders
});
module.exports = __toCommonJS(secure_headers_exports);
var import_encode = require("../../utils/encode");
const HEADERS_MAP = {
crossOriginEmbedderPolicy: ["Cross-Origin-Embedder-Policy", "require-corp"],
crossOriginResourcePolicy: ["Cross-Origin-Resource-Policy", "same-origin"],
crossOriginOpenerPolicy: ["Cross-Origin-Opener-Policy", "same-origin"],
originAgentCluster: ["Origin-Agent-Cluster", "?1"],
referrerPolicy: ["Referrer-Policy", "no-referrer"],
strictTransportSecurity: ["Strict-Transport-Security", "max-age=15552000; includeSubDomains"],
xContentTypeOptions: ["X-Content-Type-Options", "nosniff"],
xDnsPrefetchControl: ["X-DNS-Prefetch-Control", "off"],
xDownloadOptions: ["X-Download-Options", "noopen"],
xFrameOptions: ["X-Frame-Options", "SAMEORIGIN"],
xPermittedCrossDomainPolicies: ["X-Permitted-Cross-Domain-Policies", "none"],
xXssProtection: ["X-XSS-Protection", "0"]
};
const DEFAULT_OPTIONS = {
crossOriginEmbedderPolicy: false,
crossOriginResourcePolicy: true,
crossOriginOpenerPolicy: true,
originAgentCluster: true,
referrerPolicy: true,
strictTransportSecurity: true,
xContentTypeOptions: true,
xDnsPrefetchControl: true,
xDownloadOptions: true,
xFrameOptions: true,
xPermittedCrossDomainPolicies: true,
xXssProtection: true,
removePoweredBy: true
};
const generateNonce = () => {
const buffer = new Uint8Array(16);
crypto.getRandomValues(buffer);
return (0, import_encode.encodeBase64)(buffer);
};
const NONCE = (ctx) => {
const nonce = ctx.get("secureHeadersNonce") || (() => {
const newNonce = generateNonce();
ctx.set("secureHeadersNonce", newNonce);
return newNonce;
})();
return `'nonce-${nonce}'`;
};
const secureHeaders = (customOptions) => {
const options = { ...DEFAULT_OPTIONS, ...customOptions };
const headersToSet = getFilteredHeaders(options);
const callbacks = [];
if (options.contentSecurityPolicy) {
const [callback, value] = getCSPDirectives(options.contentSecurityPolicy);
if (callback) {
callbacks.push(callback);
}
headersToSet.push(["Content-Security-Policy", value]);
}
if (options.reportingEndpoints) {
headersToSet.push(["Reporting-Endpoints", getReportingEndpoints(options.reportingEndpoints)]);
}
if (options.reportTo) {
headersToSet.push(["Report-To", getReportToOptions(options.reportTo)]);
}
return async function secureHeaders2(ctx, next) {
const headersToSetForReq = callbacks.length === 0 ? headersToSet : callbacks.reduce((acc, cb) => cb(ctx, acc), headersToSet);
await next();
setHeaders(ctx, headersToSetForReq);
if (options?.removePoweredBy) {
ctx.res.headers.delete("X-Powered-By");
}
};
};
function getFilteredHeaders(options) {
return Object.entries(HEADERS_MAP).filter(([key]) => options[key]).map(([key, defaultValue]) => {
const overrideValue = options[key];
return typeof overrideValue === "string" ? [defaultValue[0], overrideValue] : defaultValue;
});
}
function getCSPDirectives(contentSecurityPolicy) {
const callbacks = [];
const resultValues = [];
for (const [directive, value] of Object.entries(contentSecurityPolicy)) {
const valueArray = Array.isArray(value) ? value : [value];
valueArray.forEach((value2, i) => {
if (typeof value2 === "function") {
const index = i * 2 + 2 + resultValues.length;
callbacks.push((ctx, values) => {
values[index] = value2(ctx, directive);
});
}
});
resultValues.push(
directive.replace(
/[A-Z]+(?![a-z])|[A-Z]/g,
(match, offset) => offset ? "-" + match.toLowerCase() : match.toLowerCase()
),
...valueArray.flatMap((value2) => [" ", value2]),
"; "
);
}
resultValues.pop();
return callbacks.length === 0 ? [void 0, resultValues.join("")] : [
(ctx, headersToSet) => headersToSet.map((values) => {
if (values[0] === "Content-Security-Policy") {
const clone = values[1].slice();
callbacks.forEach((cb) => {
cb(ctx, clone);
});
return [values[0], clone.join("")];
} else {
return values;
}
}),
resultValues
];
}
function getReportingEndpoints(reportingEndpoints = []) {
return reportingEndpoints.map((endpoint) => `${endpoint.name}="${endpoint.url}"`).join(", ");
}
function getReportToOptions(reportTo = []) {
return reportTo.map((option) => JSON.stringify(option)).join(", ");
}
function setHeaders(ctx, headersToSet) {
headersToSet.forEach(([header, value]) => {
ctx.res.headers.set(header, value);
});
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
NONCE,
secureHeaders
});
;