update-owasp.mjs
72 lines and 5 functions in DrDustinEdwards/site-runtime, at commit 89e9d58 on 2026-10-24. This file on GitHub.
Functions
| Function | Lines | Complexity | Covered | CRAP | Mutation |
|---|---|---|---|---|---|
| here | 25 to 25 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
| read | 28 to 32 | 2 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
| <anonymous:41> | 41 to 41 | 2 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
| <anonymous:44> | 44 to 44 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
| <anonymous:48> | 48 to 48 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
Imports
It imports no other module of this repo. No module of this repo imports it.
Source
#!/usr/bin/env node
/**
* Refreshes the package's copy of the OWASP Secure Headers Project data from the project itself, so no default is
* typed from memory:
*
* node update-owasp.mjs
*
* It reads, from the project's repository (https://github.com/OWASP/www-project-secure-headers, the source of
* https://owasp.org/www-project-secure-headers/):
* - ci/headers_add.json the headers OSHP recommends a response carry, with its `last_update_utc`
* - ci/headers_remove.json the headers OSHP recommends a response not carry
* - subprojects/validator/tests_suite.yml OSHP's own header test suite (a venom suite), kept verbatim
*
* and writes owasp-headers.mjs and oshp-tests-suite.yml beside this file, and the "Source" block of README.md with
* the page, the files and the date they were read. Exits non-zero when a file cannot be read or has an unexpected
* shape, so a failed refresh never leaves a half-written set.
*/
import { readFile, writeFile } from "node:fs/promises";
import { fileURLToPath } from "node:url";
const RAW = "https://raw.githubusercontent.com/OWASP/www-project-secure-headers/master";
const PAGE = "https://owasp.org/www-project-secure-headers/";
const REPO = "https://github.com/OWASP/www-project-secure-headers";
const here = (/** @type {string} */ name) => fileURLToPath(new URL(name, import.meta.url));
/** @param {string} path */
async function read(path) {
const res = await fetch(`${RAW}/${path}`, { headers: { "user-agent": "security-headers update" } });
if (!res.ok) throw new Error(`${RAW}/${path} answered ${res.status}`);
return res.text();
}
const [addText, removeText, suite] = await Promise.all([
read("ci/headers_add.json"),
read("ci/headers_remove.json"),
read("subprojects/validator/tests_suite.yml"),
]);
const add = JSON.parse(addText);
const remove = JSON.parse(removeText);
if (!Array.isArray(add.headers) || add.headers.length === 0 || add.headers.some((/** @type {any} */ h) => typeof h?.name !== "string" || typeof h?.value !== "string")) {
throw new Error("headers_add.json is not a list of { name, value }");
}
if (!Array.isArray(remove.headers) || remove.headers.some((/** @type {any} */ h) => typeof h !== "string")) throw new Error("headers_remove.json is not a list of names");
if (!suite.includes("testcases:")) throw new Error("tests_suite.yml has no testcases");
const fetchedAt = new Date().toISOString().slice(0, 10);
const headers = Object.fromEntries(add.headers.map((/** @type {{ name: string, value: string }} */ h) => [h.name, h.value]));
const module = `// GENERATED by update-owasp.mjs from the OWASP Secure Headers Project. Do not edit: run the script.
// Page: ${PAGE}
// Data: ${REPO}/tree/master/ci (headers_add.json, headers_remove.json), read ${fetchedAt}.
/** Where these values were read, and when. */
export const OWASP_SOURCE = Object.freeze({
page: ${JSON.stringify(PAGE)},
data: ${JSON.stringify(`${REPO}/tree/master/ci`)},
readOn: ${JSON.stringify(fetchedAt)},
lastUpdateUtc: ${JSON.stringify(add.last_update_utc ?? null)},
});
/** The headers OSHP recommends a response carry, as OSHP writes them. */
export const OWASP_RECOMMENDED = Object.freeze(${JSON.stringify(headers, null, 2)});
/** The headers OSHP recommends a response not carry (they name the software that served it). */
export const OWASP_REMOVE = Object.freeze(${JSON.stringify(remove.headers, null, 2)});
`;
await writeFile(here("owasp-headers.mjs"), module, "utf8");
await writeFile(here("oshp-tests-suite.yml"), suite, "utf8");
const block = [
"<!-- owasp:begin -->",
`The defaults are read from the OWASP Secure Headers Project, not typed here: page ${PAGE}, data ${REPO}/tree/master/ci`,
`(\`headers_add.json\`, \`headers_remove.json\`, last updated by OWASP ${add.last_update_utc ?? "(not stated)"} UTC), and its header test suite`,
`\`subprojects/validator/tests_suite.yml\`, all read on **${fetchedAt}**. Refresh with \`node update-owasp.mjs\`.`,
"<!-- owasp:end -->",
].join("\n");
const readmePath = here("README.md");
const readme = await readFile(readmePath, "utf8");
if (!readme.includes("<!-- owasp:begin -->")) throw new Error("README.md has no owasp:begin marker");
await writeFile(readmePath, readme.replace(/<!-- owasp:begin -->[\s\S]*?<!-- owasp:end -->/, block), "utf8");
console.log(`read ${Object.keys(headers).length} recommended headers, ${remove.headers.length} to remove, and the suite, on ${fetchedAt}`);