@nosecone/sveltekit
Version:
Protect your SvelteKit application with secure headers
97 lines (94 loc) • 3.13 kB
JavaScript
import nosecone, { defaults as defaults$1, CONTENT_SECURITY_POLICY_DIRECTIVES, NoseconeValidationError, QUOTED } from 'nosecone';
export { default, default as nosecone, withVercelToolbar } from 'nosecone';
/**
* Nosecone SvelteKit defaults.
*/
const defaults = {
...defaults$1,
directives: {
...defaults$1.contentSecurityPolicy.directives,
scriptSrc: ["'strict-dynamic'"],
},
};
/**
* Create a SvelteKit hook that sets secure headers on every request.
*
* @param options
* Configuration to provide to Nosecone.
* @returns
* SvelteKit hook that sets secure headers.
*/
function createHook(options = defaults) {
return async ({ event, resolve }) => {
const response = await resolve(event);
const headers = nosecone(options);
for (const [headerName, headerValue] of headers.entries()) {
// Only add headers that aren't already set. For example, SvelteKit will
// likely have added `Content-Security-Policy` if configured with `csp`
if (!response.headers.has(headerName)) {
response.headers.set(headerName, headerValue);
}
}
return response;
};
}
function unquote(value) {
for (const [unquoted, quoted] of QUOTED) {
if (value === quoted) {
return unquoted;
}
}
return value;
}
function resolveValue(v) {
if (typeof v === "function") {
return v();
}
else {
return v;
}
}
function directivesToSvelteKitConfig(directives) {
const sveltekitDirectives = {};
for (const [optionKey, optionValues] of Object.entries(directives)) {
const key = CONTENT_SECURITY_POLICY_DIRECTIVES.get(
// @ts-expect-error because we're validating this option key
optionKey);
if (!key) {
throw new NoseconeValidationError(`${optionKey} is not a Content-Security-Policy directive`);
}
// Skip anything falsey
if (!optionValues) {
continue;
}
// TODO: What do we want to do if array is empty? I think they work differently for some directives
const resolvedValues = Array.isArray(optionValues)
? new Set(optionValues.map(resolveValue))
: new Set();
// TODO: Add validations for SvelteKit CSP directives
const values = Array.from(resolvedValues);
if (key === "upgrade-insecure-requests") {
sveltekitDirectives[key] = true;
}
else {
// @ts-ignore because we're mapping to SvelteKit options
sveltekitDirectives[key] = values.map(unquote);
}
}
return sveltekitDirectives;
}
/**
* Create a SvelteKit Content Security Policy configuration.
*
* @param options
* Configuration.
* @returns
* SvelteKit Content Security Policy configuration.
*/
function csp(options) {
return {
mode: options?.mode ? options.mode : "auto",
directives: directivesToSvelteKitConfig(options?.directives ?? defaults.contentSecurityPolicy.directives) || {},
};
}
export { createHook, csp, defaults };