UNPKG

astro

Version:

Astro is a modern site builder with web best practices, performance, and DX front-of-mind.

117 lines (116 loc) • 4.31 kB
import { readFileSync } from "node:fs"; import { fileURLToPath } from "node:url"; import astroIslandPrebuilt from "../../runtime/server/astro-island.prebuilt.js"; import astroIslandPrebuiltDev from "../../runtime/server/astro-island.prebuilt-dev.js"; import { ISLAND_STYLES } from "../../runtime/server/astro-island-styles.js"; import { generateCspDigest } from "../encryption.js"; export function shouldTrackCspHashes(csp) { return csp === true || typeof csp === "object"; } export function getAlgorithm(csp) { if (csp === true) { return "SHA-256"; } return csp.algorithm; } export function getScriptResources(csp) { if (csp === true) { return []; } return csp.scriptDirective?.resources ?? []; } export function getScriptHashes(csp) { if (csp === true) { return []; } return csp.scriptDirective?.hashes ?? []; } export function getStyleResources(csp) { if (csp === true) { return []; } return csp.styleDirective?.resources ?? []; } export function getStyleHashes(csp) { if (csp === true) { return []; } return csp.styleDirective?.hashes ?? []; } // Unlike other helpers like getStyleResources, getDirectives has more logic // because it has to collect and deduplicate font resources from both the user // config and the vite plugin for fonts export function getDirectives(settings) { const { csp } = settings.config.security; if (!shouldTrackCspHashes(csp)) { return []; } const userDirectives = csp === true ? [] : [...csp.directives ?? []]; const fontResources = Array.from(settings.injectedCsp.fontResources.values()); if (fontResources.length === 0) { // If no font resources, just return user directives return userDirectives; } const fontSrcIndex = userDirectives.findIndex((e) => e.startsWith("font-src")); if (fontSrcIndex === -1) { // Add new font-src directive return [...userDirectives, `font-src ${fontResources.join(" ")}`]; } // Merge and deduplicate font-src resources const existing = userDirectives[fontSrcIndex].split(/\s+/).slice(1).filter(Boolean); const merged = Array.from(new Set([...existing, ...fontResources])); userDirectives[fontSrcIndex] = `font-src ${merged.join(" ")}`; return userDirectives; } export function getStrictDynamic(csp) { if (csp === true) { return false; } return csp.scriptDirective?.strictDynamic ?? false; } export async function trackStyleHashes({ internals, settings, algorithm, clientRoot }) { const clientStyleHashes = []; for (const [_, page] of internals.pagesByViteID.entries()) { for (const style of page.styles) { if (style.sheet.type === "inline") { clientStyleHashes.push(await generateCspDigest(style.sheet.content, algorithm)); } } } for (const clientAsset in internals.clientChunksAndAssets) { const contents = readFileSync(fileURLToPath(new URL(clientAsset, clientRoot)), "utf-8"); if (clientAsset.endsWith(".css") || clientAsset.endsWith(".css")) { clientStyleHashes.push(await generateCspDigest(contents, algorithm)); } } if (settings.renderers.length > 0) { clientStyleHashes.push(await generateCspDigest(ISLAND_STYLES, algorithm)); } return clientStyleHashes; } export async function trackScriptHashes({ internals, settings, algorithm, clientRoot }) { const clientScriptHashes = []; for (const script of internals.inlinedScripts.values()) { clientScriptHashes.push(await generateCspDigest(script, algorithm)); } for (const directiveContent of Array.from(settings.clientDirectives.values())) { clientScriptHashes.push(await generateCspDigest(directiveContent, algorithm)); } for (const clientAsset in internals.clientChunksAndAssets) { const contents = readFileSync(fileURLToPath(new URL(clientAsset, clientRoot)), "utf-8"); if (clientAsset.endsWith(".js") || clientAsset.endsWith(".mjs")) { clientScriptHashes.push(await generateCspDigest(contents, algorithm)); } } for (const script of settings.scripts) { const { content, stage } = script; if (stage === "head-inline" || stage === "before-hydration") { clientScriptHashes.push(await generateCspDigest(content, algorithm)); } } if (settings.renderers.length > 0) { clientScriptHashes.push(await generateCspDigest(astroIslandPrebuilt, algorithm)); clientScriptHashes.push(await generateCspDigest(astroIslandPrebuiltDev, algorithm)); } return clientScriptHashes; }