diff --git a/_11ty/apply-csp.js b/_11ty/apply-csp.js index 5759f96..e855b60 100644 --- a/_11ty/apply-csp.js +++ b/_11ty/apply-csp.js @@ -19,7 +19,6 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -const { JSDOM } = require("jsdom"); const cspHashGen = require("csp-hash-generator"); const syncPackage = require("browser-sync/package.json"); @@ -49,32 +48,36 @@ const addCspHash = async (rawContent, outputPath) => { let content = rawContent; if (outputPath && outputPath.endsWith(".html")) { - const dom = new JSDOM(content); - const cspAble = [ - ...dom.window.document.querySelectorAll("script[csp-hash]"), - ]; - - const hashes = cspAble.map((element) => { - const hash = cspHashGen(element.textContent); - element.setAttribute("csp-hash", hash); - return quote(hash); - }); - if (isDevelopmentMode()) { - hashes.push.apply(hashes, AUTO_RELOAD_SCRIPTS); + // Fast fail if no CSP meta tag is present (check for the header value) + if (!/Content-Security-Policy/i.test(content)) { + return content; } - const csp = dom.window.document.querySelector( - "meta[http-equiv='Content-Security-Policy']" + const hashes = []; + + // Find and replace script tags with csp-hash attribute + content = content.replace( + /`; + } ); - if (!csp) { - return content; + + if (isDevelopmentMode()) { + hashes.push.apply(hashes, AUTO_RELOAD_SCRIPTS); } - csp.setAttribute( - "content", - csp.getAttribute("content").replace("HASHES", hashes.join(" ")) - ); - content = dom.serialize(); + // Replace HASHES in the CSP meta tag + // Finds the meta tag with http-equiv="Content-Security-Policy" and replaces HASHES inside it + const metaTagRegex = /]*http-equiv=["']Content-Security-Policy["'][^>]*>/i; + content = content.replace(metaTagRegex, (match) => { + if (match.includes("HASHES")) { + return match.replace("HASHES", hashes.join(" ")); + } + return match; + }); } return content; diff --git a/test/test-csp.js b/test/test-csp.js new file mode 100644 index 0000000..939e506 --- /dev/null +++ b/test/test-csp.js @@ -0,0 +1,84 @@ + +const assert = require("assert"); +const applyCsp = require("../_11ty/apply-csp.js"); + +describe("CSP Transform", () => { + let addCspHashFn; + + before(() => { + const mockEleventyConfig = { + addTransform: (name, fn) => { + if (name === "csp") addCspHashFn = fn; + } + }; + applyCsp.configFunction(mockEleventyConfig); + }); + + const htmlWithCsp = ` + +
+ + + + + + +