nuxt-security
Version:
🛡️ Security Module for Nuxt based on HTTP Headers and Middleware
43 lines (42 loc) • 1.73 kB
JavaScript
import { defineNitroPlugin } from "nitropack/runtime";
import { resolveSecurityRules } from "../context/index.js";
import { headerStringFromObject } from "../../../utils/headers";
const META_CSP_RE = /<meta\s+http-equiv=["']Content-Security-Policy["'][^>]*>/i;
const HEAD_CHARSET_RE = /<meta\s+charset=["'][^"']*["'][^>]*>/i;
const HEAD_OPEN_RE = /<head\b[^>]*>/i;
export default defineNitroPlugin((nitroApp) => {
if (!import.meta.prerender) {
return;
}
nitroApp.hooks.hook("render:response", (response, { event }) => {
const rules = resolveSecurityRules(event);
if (!rules.enabled) {
return;
}
if (rules.ssg && rules.ssg.meta && rules.headers && rules.headers.contentSecurityPolicy) {
const body = response.body;
if (typeof body !== "string" || !body) {
return;
}
const csp = structuredClone(rules.headers.contentSecurityPolicy);
csp["frame-ancestors"] = false;
const headerValue = headerStringFromObject("contentSecurityPolicy", csp);
const metaTag = `<meta http-equiv="Content-Security-Policy" content="${headerValue}">`;
if (META_CSP_RE.test(body)) {
response.body = body.replace(META_CSP_RE, metaTag);
return;
}
const charsetMatch = HEAD_CHARSET_RE.exec(body);
if (charsetMatch) {
const insertAt = charsetMatch.index + charsetMatch[0].length;
response.body = body.slice(0, insertAt) + metaTag + body.slice(insertAt);
return;
}
const headMatch = HEAD_OPEN_RE.exec(body);
if (headMatch) {
const insertAt = headMatch.index + headMatch[0].length;
response.body = body.slice(0, insertAt) + metaTag + body.slice(insertAt);
}
}
});
});