chore(ci): gate deploys on new advisories, restore lint and typecheck #32

Merged
allan merged 1 commits from chore/audit-gate-and-lint-gates into main 2026-10-28 12:32:02 +00:00
11 changed files with 1072 additions and 16 deletions
Showing only changes of commit 2eb5d74f59 - Show all commits

No files matched your search

+17 -8
View File
@@ -26,14 +26,23 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: npm ci --legacy-peer-deps run: npm ci --legacy-peer-deps
# Blocking gate, at CRITICAL only. `npm audit` fails on the severity you # Blocking gate. `npm audit --audit-level=<x>` is all-or-nothing and this
# name, so `--audit-level=low` (or the default) would stop every deploy # tree cannot pass a flat gate at any level: the Gatsby 5 build toolchain
# today: the Gatsby 5 tree carries ~100 advisories at low/moderate/high # carries ~100 advisories with no non-breaking fix, so gating on the tree
# that have no non-breaking fix, and publishing would simply never run. # as a whole stops publishing permanently. scripts/audit-gate.mjs instead
# Critical is the line worth stopping a deploy for, and the tree is clear # accepts the known set (scripts/audit-baseline.json, with an owner and a
# of it - keep it that way rather than gating on noise nobody will action. # reason) and FAILS on any new critical/high advisory.
- name: Audit for critical vulnerabilities - name: Audit for new critical/high advisories
run: npm audit --audit-level=critical run: node scripts/audit-gate.mjs
# The prettier and type checks used to live in a GitHub workflow that
# never ran on this forge; keep them ahead of the build so a type or
# formatting error blocks the deploy rather than shipping.
- name: Typecheck
run: npm run typecheck
- name: Check prettier offences
run: npm run lint
- name: Build - name: Build
run: npm run build run: npm run build
+940
View File
@@ -0,0 +1,940 @@
{
"$comment": "Advisories at critical/high accepted for this tree. scripts/audit-gate.mjs fails the build on anything not listed here, so removing an entry re-arms the gate for that advisory. Regenerate with: node scripts/audit-gate.mjs --write-baseline",
"owner": "Site maintainers (allan@4gl.io)",
"reason": "Gatsby 5 build toolchain: no non-breaking fix available upstream, and these are build-time dependencies of a statically generated site. Re-check on each Gatsby major bump.",
"accepted": [
{
"pkg": "@ardatan/relay-compiler",
"severity": "high",
"advisory": "transitive"
},
{
"pkg": "@babel/plugin-transform-modules-systemjs",
"severity": "high",
"advisory": "GHSA-fv7c-fp4j-7gwp",
"title": "@babel/plugin-transform-modules-systemjs generates arbitrary code when compiling malicious input",
"range": ">=7.12.0 <=7.29.3"
},
{
"pkg": "@graphql-codegen/add",
"severity": "high",
"advisory": "transitive"
},
{
"pkg": "@graphql-codegen/core",
"severity": "high",
"advisory": "transitive"
},
{
"pkg": "@graphql-codegen/plugin-helpers",
"severity": "high",
"advisory": "transitive"
},
{
"pkg": "@graphql-codegen/schema-ast",
"severity": "high",
"advisory": "transitive"
},
{
"pkg": "@graphql-codegen/typescript-operations",
"severity": "high",
"advisory": "transitive"
},
{
"pkg": "@graphql-codegen/typescript",
"severity": "high",
"advisory": "transitive"
},
{
"pkg": "@graphql-codegen/visitor-plugin-common",
"severity": "high",
"advisory": "transitive"
},
{
"pkg": "@graphql-tools/code-file-loader",
"severity": "high",
"advisory": "transitive"
},
{
"pkg": "@graphql-tools/graphql-tag-pluck",
"severity": "high",
"advisory": "transitive"
},
{
"pkg": "@graphql-tools/load",
"severity": "high",
"advisory": "transitive"
},
{
"pkg": "@graphql-tools/merge",
"severity": "high",
"advisory": "transitive"
},
{
"pkg": "@graphql-tools/relay-operation-optimizer",
"severity": "high",
"advisory": "transitive"
},
{
"pkg": "@graphql-tools/schema",
"severity": "high",
"advisory": "transitive"
},
{
"pkg": "@graphql-tools/utils",
"severity": "high",
"advisory": "GHSA-7mx3-vvmw-hjmv",
"title": "GraphQL Tools has prototype pollution in well-established utility function `mergeDeep`",
"range": "<=12.0.0"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-35jp-ww65-95wh",
"title": "axios Vulnerable to Full Man-in-the-Middle via Prototype Pollution Gadget in `config.proxy`",
"range": ">=1.0.0 <1.16.0"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-3g43-6gmg-66jw",
"title": "axios Vulnerable to Credential Theft and Response Hijacking via Prototype Pollution Gadget in Config Merge",
"range": ">=1.0.0 <1.15.2"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-3p68-rc4w-qgx5",
"title": "Axios has a NO_PROXY Hostname Normalization Bypass that Leads to SSRF",
"range": ">=1.0.0 <1.15.0"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-3pq3-5fj3-cg6v",
"title": "Axios: HTTP/2 adapter bypasses configured DNS lookup and proxy controls",
"range": ">=1.13.0 <1.20.0"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-3w6x-2g7m-8v23",
"title": "Axios: Invisible JSON Response Tampering via Prototype Pollution Gadget in `parseReviver`",
"range": ">=1.0.0 <1.15.2"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-42h9-826w-cgv3",
"title": "Axios: Excessive recursion in formDataToJSON can cause denial of service",
"range": ">=1.0.0 <1.18.0"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-43fc-jf86-j433",
"title": "Axios is Vulnerable to Denial of Service via __proto__ Key in mergeConfig",
"range": ">=1.0.0 <=1.13.4"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-445q-vr5w-6q77",
"title": "Axios: CRLF Injection in multipart/form-data body via unsanitized blob.type in formDataToStream",
"range": ">=1.0.0 <1.15.1"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-4hqw-qxg8-jxx2",
"title": "Axios: Fetch Adapter Header Injection via Inherited FormData getHeaders",
"range": ">=1.12.0 <1.20.0"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-542g-h47m-68v8",
"title": "Axios: Denial of Service via Unhandled 'error' Event in HTTP/2 ClientHttp2Session Initialization",
"range": ">=1.13.0 <1.20.0"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-5c9x-8gcm-mpgx",
"title": "Axios' HTTP adapter-streamed uploads bypass maxBodyLength when maxRedirects: 0",
"range": ">=1.0.0 <1.15.1"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-62hf-57xw-28j9",
"title": "Axios: unbounded recursion in toFormData causes DoS via deeply nested request data",
"range": ">=1.0.0 <1.15.1"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-6chq-wfr3-2hj9",
"title": "Axios: Header Injection via Prototype Pollution",
"range": ">=1.0.0 <1.15.1"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-777c-7fjr-54vf",
"title": "Allocation of Resources Without Limits or Throttling in Axios",
"range": ">=1.7.0 <1.16.0"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-7q8q-rj6j-mhjq",
"title": "Axios: Nested axios option objects can consume polluted prototype values",
"range": ">=1.0.0 <1.18.0"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-898c-q2cr-xwhg",
"title": "axios has DoS & Header Injection via Prototype Pollution Read-Side Gadgets in axios merge functions",
"range": ">=1.0.0 <1.16.0"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-9fr6-4gfg-395g",
"title": "Axios: Prototype-Pollution Gadget in the Default Instance Allows Inherited Object.prototype.method to Override HTTP Method",
"range": ">=1.0.0 <1.20.0"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-fvcv-3m26-pcqx",
"title": "Axios has Unrestricted Cloud Metadata Exfiltration via Header Injection Chain",
"range": ">=1.0.0 <1.15.0"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-hfxv-24rg-xrqf",
"title": "Axios: Regular Expression Denial of Service (ReDoS) via Cookie Name Injection",
"range": ">=1.0.0 <1.16.0"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-j5f8-grm9-p9fc",
"title": "Axios: Proxy-Authorization header leaks to redirect target when proxy is re-evaluated to direct connection",
"range": ">=1.0.0 <1.16.0"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-j8rh-479h-cp32",
"title": "Axios: Header Injection via Inherited headers After Minimal Interceptor",
"range": ">=1.0.0 <1.20.0"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-jqh4-m9w3-8hp9",
"title": "Axios: Fetch adapter `ReadableStream` uploads bypass `maxBodyLength`",
"range": ">=1.7.0 <1.18.0"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-m7pr-hjqh-92cm",
"title": "Axios: no_proxy bypass via IP alias allows SSRF",
"range": ">=1.0.0 <1.15.1"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-mmx7-hfxf-jppx",
"title": "Axios: Prototype pollution gadgets can alter axios request construction",
"range": ">=1.0.0 <1.18.0"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-mwf2-3pr3-8698",
"title": "Axios: HTTP/2 streamed uploads bypass `maxBodyLength`",
"range": ">=1.13.0 <1.18.0"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-p92q-9vqr-4j8v",
"title": "Axios: Proxy-Authorization Credential Leak to Origin Server Across HTTP-to-HTTPS Redirect in Axios Node.js HTTP Adapter",
"range": ">=1.0.0 <1.16.0"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-pf86-5x62-jrwf",
"title": "Axios: Prototype Pollution Gadgets - Response Tampering, Data Exfiltration, and Request Hijacking",
"range": ">=1.0.0 <1.15.1"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-pmv8-rq9r-6j72",
"title": "Axios: Deep formToJSON Key Recursion Can Cause Denial of Service",
"range": ">=1.0.0 <1.18.0"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-pmwg-cvhr-8vh7",
"title": "Axios: Incomplete Fix for CVE-2025-62718 — NO_PROXY Protection Bypassed via RFC 1122 Loopback Subnet (127.0.0.0/8) in Axios 1.15.0",
"range": ">=1.0.0 <1.15.1"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-q8qp-cvcw-x6jj",
"title": "Axios has prototype pollution read-side gadgets in HTTP adapter that allow credential injection and request hijacking",
"range": ">=1.0.0 <1.15.2"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-vf2m-468p-8v99",
"title": "Axios: HTTP adapter streamed responses bypass maxContentLength",
"range": ">=1.0.0 <1.15.1"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-vh66-26gq-q6x8",
"title": "Axios: Prototype pollution gadget in fetch adapter can alter outbound requests",
"range": ">=1.7.0 <1.20.0"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-w9j2-pvgh-6h63",
"title": "Axios: Authentication Bypass via Prototype Pollution Gadget in `validateStatus` Merge Strategy",
"range": ">=1.0.0 <1.15.1"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-xhjh-pmcv-23jw",
"title": "Axios: Null Byte Injection via Reverse-Encoding in AxiosURLSearchParams",
"range": ">=1.0.0 <1.15.1"
},
{
"pkg": "axios",
"severity": "high",
"advisory": "GHSA-xx6v-rp6x-q39c",
"title": "Axios: XSRF Token Cross-Origin Leakage via Prototype Pollution Gadget in `withXSRFToken` Boolean Coercion",
"range": ">=1.0.0 <1.15.1"
},
{
"pkg": "body-parser",
"severity": "high",
"advisory": "GHSA-27r3-jxmh-p828",
"title": "body-parser vulnerable to Denial of Service via trailing bytes after a compressed request body",
"range": "<1.20.9"
},
{
"pkg": "body-parser",
"severity": "high",
"advisory": "GHSA-qwcr-r2fm-qrc7",
"title": "body-parser vulnerable to denial of service when url encoding is enabled",
"range": "<1.20.3"
},
{
"pkg": "body-parser",
"severity": "high",
"advisory": "GHSA-v422-hmwv-36x6",
"title": "body-parser vulnerable to denial of service when invalid limit value silently disables size enforcement",
"range": "<1.20.6"
},
{
"pkg": "brace-expansion",
"severity": "high",
"advisory": "GHSA-3jxr-9vmj-r5cp",
"title": "brace-expansion: DoS via exponential-time expansion of consecutive non-expanding {} groups",
"range": "<1.1.16"
},
{
"pkg": "brace-expansion",
"severity": "high",
"advisory": "GHSA-6j4f-fj2g-mc7p",
"title": "brace-expansion: DoS via uncontrolled recursion in parseCommaParts causing stack exhaustion",
"range": "<1.1.19"
},
{
"pkg": "brace-expansion",
"severity": "high",
"advisory": "GHSA-f886-m6hf-6m8v",
"title": "brace-expansion: Zero-step sequence causes process hang and memory exhaustion",
"range": "<1.1.13"
},
{
"pkg": "brace-expansion",
"severity": "high",
"advisory": "GHSA-mh99-v99m-4gvg",
"title": "brace-expansion: DoS via unbounded expansion length causing an out-of-memory process crash",
"range": "<1.1.17"
},
{
"pkg": "brace-expansion",
"severity": "high",
"advisory": "GHSA-q2hr-2g5m-vwhr",
"title": "brace-expansion: Quadratic-time expansion of the `{a},b}` rewrite causes CPU denial of service",
"range": "<1.1.21"
},
{
"pkg": "brace-expansion",
"severity": "high",
"advisory": "GHSA-qhr7-859c-m2p7",
"title": "brace-expansion: DoS via uncontrolled recursion on nested brace groups causing stack exhaustion",
"range": "<1.1.20"
},
{
"pkg": "brace-expansion",
"severity": "high",
"advisory": "GHSA-rgw5-rvv9-x895",
"title": "brace-expansion: DoS via unbounded intermediate arrays, bypassing the CVE-2026-14257 mitigation",
"range": "<1.1.18"
},
{
"pkg": "brace-expansion",
"severity": "high",
"advisory": "GHSA-v6h2-p8h4-qcjw",
"title": "brace-expansion Regular Expression Denial of Service vulnerability",
"range": ">=1.0.0 <=1.1.11"
},
{
"pkg": "browserslist",
"severity": "high",
"advisory": "GHSA-73wf-gq98-2v4g",
"title": "Browserslist: Uncaught crash / prototype write via untrusted browserslist-stats.json custom stats (normalizeStats)",
"range": "<=4.28.6"
},
{
"pkg": "browserslist",
"severity": "high",
"advisory": "GHSA-c83g-rgw3-j3cx",
"title": "Browserslist: Unbounded memory growth (no cache eviction) via distinct query results, leading to eventual OOM",
"range": "<=4.28.6"
},
{
"pkg": "compression",
"severity": "high",
"advisory": "GHSA-vc2v-76pw-4v95",
"title": "compression vulnerable to Denial of Service via memory leak on premature response close",
"range": "<1.8.2"
},
{
"pkg": "cross-spawn",
"severity": "high",
"advisory": "GHSA-3xgq-45jj-v275",
"title": "Regular Expression Denial of Service (ReDoS) in cross-spawn",
"range": ">=7.0.0 <7.0.5"
},
{
"pkg": "engine.io",
"severity": "high",
"advisory": "GHSA-2gc4-cqfq-p2gv",
"title": "Socket.IO: Engine.IO Protocol Revision Mismatch DoS",
"range": ">=6.6.0 <6.6.10"
},
{
"pkg": "engine.io",
"severity": "high",
"advisory": "GHSA-gr94-w7qr-f4j3",
"title": "Socket.IO: Engine.IO WebTransport SID DoS",
"range": ">=6.5.0 <6.6.7"
},
{
"pkg": "engine.io",
"severity": "high",
"advisory": "GHSA-r635-g3xr-vw7x",
"title": "Socket.IO: Engine.IO Polling Transport Connection Exhaustion",
"range": ">=4.1.0 <6.6.7"
},
{
"pkg": "express",
"severity": "high",
"advisory": "GHSA-qw6h-vgh9-j6wx",
"title": "express vulnerable to XSS via response.redirect()",
"range": "<4.20.0"
},
{
"pkg": "flatted",
"severity": "high",
"advisory": "GHSA-25h7-pfq9-p65f",
"title": "flatted vulnerable to unbounded recursion DoS in parse() revive phase",
"range": "<3.4.0"
},
{
"pkg": "flatted",
"severity": "high",
"advisory": "GHSA-rf6f-7fwh-wjgh",
"title": "Prototype Pollution via parse() in NodeJS flatted",
"range": "<=3.4.1"
},
{
"pkg": "form-data",
"severity": "high",
"advisory": "GHSA-hmw2-7cc7-3qxx",
"title": "form-data: CRLF injection in form-data via unescaped multipart field names and filenames",
"range": ">=4.0.0 <4.0.6"
},
{
"pkg": "gatsby-plugin-manifest",
"severity": "high",
"advisory": "transitive"
},
{
"pkg": "gatsby-plugin-sharp",
"severity": "high",
"advisory": "transitive"
},
{
"pkg": "gatsby-plugin-utils",
"severity": "high",
"advisory": "transitive"
},
{
"pkg": "gatsby-sharp",
"severity": "high",
"advisory": "transitive"
},
{
"pkg": "gatsby-transformer-sharp",
"severity": "high",
"advisory": "transitive"
},
{
"pkg": "gatsby",
"severity": "high",
"advisory": "transitive"
},
{
"pkg": "http-cache-semantics",
"severity": "high",
"advisory": "GHSA-ch52-4w7c-c8xp",
"title": "http-cache-semantics max-stale handling can disclose cross-user cached responses",
"range": "<=4.2.0"
},
{
"pkg": "immutable",
"severity": "high",
"advisory": "GHSA-v56q-mh7h-f735",
"title": "Immutable.js `List` 32-bit trie overflow → unrecoverable DoS",
"range": "<3.8.4"
},
{
"pkg": "immutable",
"severity": "high",
"advisory": "GHSA-wf6x-7x77-mvgw",
"title": "Immutable is vulnerable to Prototype Pollution",
"range": "<3.8.3"
},
{
"pkg": "immutable",
"severity": "high",
"advisory": "GHSA-xvcm-6775-5m9r",
"title": "Immutable: Hash-collision algorithmic complexity denial of service in Immutable.Map/Set",
"range": "<3.8.4"
},
{
"pkg": "joi",
"severity": "high",
"advisory": "GHSA-6h2x-m376-mqjq",
"title": "joi: Quadratic regular-expression backtracking in `Joi.string().isoDate()`",
"range": ">=17.2.0 <17.13.7"
},
{
"pkg": "joi",
"severity": "high",
"advisory": "GHSA-6w3j-5fw6-r9vr",
"title": "joi: Prototype pollution via a `__proto__` language key in custom messages",
"range": ">=17.2.0 <17.13.6"
},
{
"pkg": "joi",
"severity": "high",
"advisory": "GHSA-gg4h-3hg2-grpc",
"title": "joi: object().rename() with a template target can set the validated object's prototype",
"range": ">=16.0.0 <17.13.5"
},
{
"pkg": "joi",
"severity": "high",
"advisory": "GHSA-wr44-6hxh-3jwq",
"title": "joi messages compilation allows prototype replacement through __proto__ error codes",
"range": "<17.13.8"
},
{
"pkg": "js-yaml",
"severity": "high",
"advisory": "GHSA-2883-xcg3-v3hh",
"title": "js-yaml: maxTotalMergeKeys does not limit CPU use for empty merge sources",
"range": ">=3.0.0 <3.15.2"
},
{
"pkg": "js-yaml",
"severity": "high",
"advisory": "GHSA-52cp-r559-cp3m",
"title": "js-yaml: YAML merge-key chains can force quadratic CPU consumption",
"range": ">=3.0.0 <3.15.0"
},
{
"pkg": "js-yaml",
"severity": "high",
"advisory": "GHSA-5p4m-2wfm-xmqj",
"title": "JS-YAML: Quadratic CPU consumption in !!omap resolution (3.x and 4.x) — CVE-2026-59870 fix not backported",
"range": ">=3.0.0 <3.15.1"
},
{
"pkg": "js-yaml",
"severity": "high",
"advisory": "GHSA-h67p-54hq-rp68",
"title": "JS-YAML: Quadratic-complexity DoS in merge key handling via repeated aliases",
"range": "<3.15.0"
},
{
"pkg": "js-yaml",
"severity": "high",
"advisory": "GHSA-mh29-5h37-fv8m",
"title": "js-yaml has prototype pollution in merge (<<)",
"range": "<3.14.2"
},
{
"pkg": "lodash",
"severity": "high",
"advisory": "GHSA-f23m-r3pf-42rh",
"title": "lodash vulnerable to Prototype Pollution via array path bypass in `_.unset` and `_.omit`",
"range": "<=4.17.23"
},
{
"pkg": "lodash",
"severity": "high",
"advisory": "GHSA-r5fr-rjxr-66jc",
"title": "lodash vulnerable to Code Injection via `_.template` imports key names",
"range": ">=4.0.0 <=4.17.23"
},
{
"pkg": "lodash",
"severity": "high",
"advisory": "GHSA-xxjr-mmjv-4gpg",
"title": "Lodash has Prototype Pollution Vulnerability in `_.unset` and `_.omit` functions",
"range": ">=4.0.0 <=4.17.22"
},
{
"pkg": "minimatch",
"severity": "high",
"advisory": "GHSA-23c5-xmqv-rm74",
"title": "minimatch ReDoS: nested *() extglobs generate catastrophically backtracking regular expressions",
"range": "<3.1.4"
},
{
"pkg": "minimatch",
"severity": "high",
"advisory": "GHSA-3ppc-4f35-3m26",
"title": "minimatch has a ReDoS via repeated wildcards with non-matching literal in pattern",
"range": "<3.1.3"
},
{
"pkg": "minimatch",
"severity": "high",
"advisory": "GHSA-7r86-cg39-jmmj",
"title": "minimatch has ReDoS: matchOne() combinatorial backtracking via multiple non-adjacent GLOBSTAR segments",
"range": "<3.1.3"
},
{
"pkg": "multer",
"severity": "high",
"advisory": "GHSA-3p4h-7m6x-2hcm",
"title": "Multer vulnerable to Denial of Service via incomplete cleanup of aborted uploads",
"range": ">=2.0.0-alpha.1 <2.2.0"
},
{
"pkg": "multer",
"severity": "high",
"advisory": "GHSA-535w-7cp7-47q4",
"title": "multer vulnerable to Denial of Service via oversized array index in field names",
"range": "<2.3.0"
},
{
"pkg": "multer",
"severity": "high",
"advisory": "GHSA-5528-5vmv-3xc2",
"title": "Multer Vulnerable to Denial of Service via Uncontrolled Recursion",
"range": "<2.1.1"
},
{
"pkg": "multer",
"severity": "high",
"advisory": "GHSA-72gw-mp4g-v24j",
"title": "Multer vulnerable to Denial of Service via deeply nested field names",
"range": ">=1.0.0 <2.2.0"
},
{
"pkg": "multer",
"severity": "high",
"advisory": "GHSA-q7qp-rjg7-q26m",
"title": "multer vulnerable to Denial of Service via a pending asynchronous fileFilter after request teardown",
"range": ">=2.0.0 <2.5.0"
},
{
"pkg": "multer",
"severity": "high",
"advisory": "GHSA-qvfw-j98x-7q72",
"title": "multer vulnerable to file size limit bypass via async fileFilter race condition",
"range": "<2.3.0"
},
{
"pkg": "multer",
"severity": "high",
"advisory": "GHSA-v52c-386h-88mc",
"title": "Multer vulnerable to Denial of Service via resource exhaustion",
"range": "<2.1.0"
},
{
"pkg": "multer",
"severity": "high",
"advisory": "GHSA-wc9g-mqfw-jrwm",
"title": "multer vulnerable to Denial of Service via crafted multipart field names",
"range": "<2.3.0"
},
{
"pkg": "multer",
"severity": "high",
"advisory": "GHSA-xf7r-hgr6-v32p",
"title": "Multer vulnerable to Denial of Service via incomplete cleanup",
"range": "<2.1.0"
},
{
"pkg": "nanoid",
"severity": "high",
"advisory": "GHSA-28wg-ghj8-5hjv",
"title": "nanoid: non-secure generators can loop indefinitely with negative size",
"range": "<3.3.16"
},
{
"pkg": "nanoid",
"severity": "high",
"advisory": "GHSA-2v37-7h3g-55p8",
"title": "nanoid: custom generators can loop indefinitely when size is zero",
"range": "<3.3.18"
},
{
"pkg": "nanoid",
"severity": "high",
"advisory": "GHSA-mwcw-c2x4-8c55",
"title": "Predictable results in nanoid generation when given non-integer values",
"range": "<3.3.8"
},
{
"pkg": "nanoid",
"severity": "high",
"advisory": "GHSA-xwg4-73v4-xw9w",
"title": "nanoid: Integer Overflow or Wraparound",
"range": "<3.3.12"
},
{
"pkg": "path-to-regexp",
"severity": "high",
"advisory": "GHSA-37ch-88jc-xwx2",
"title": "path-to-regexp vulnerable to Regular Expression Denial of Service via multiple route parameters",
"range": "<0.1.13"
},
{
"pkg": "path-to-regexp",
"severity": "high",
"advisory": "GHSA-9wv6-86v2-598j",
"title": "path-to-regexp outputs backtracking regular expressions",
"range": "<0.1.10"
},
{
"pkg": "path-to-regexp",
"severity": "high",
"advisory": "GHSA-rhx6-c78j-4q9w",
"title": "path-to-regexp contains a ReDoS",
"range": "<0.1.12"
},
{
"pkg": "picomatch",
"severity": "high",
"advisory": "GHSA-3v7f-55p6-f55p",
"title": "Picomatch: Method Injection in POSIX Character Classes causes incorrect Glob Matching",
"range": "<2.3.2"
},
{
"pkg": "picomatch",
"severity": "high",
"advisory": "GHSA-c2c7-rcm5-vvqj",
"title": "Picomatch has a ReDoS vulnerability via extglob quantifiers",
"range": "<2.3.2"
},
{
"pkg": "postcss",
"severity": "high",
"advisory": "GHSA-6g55-p6wh-862q",
"title": "PostCSS: Arbitrary file read and information disclosure via attacker-controlled sourceMappingURL in CSS comments",
"range": "<=8.5.11"
},
{
"pkg": "postcss",
"severity": "high",
"advisory": "GHSA-fxqj-rqcc-2cmp",
"title": "PostCSS: incomplete fix of GHSA-6g55-p6wh-862q — attacker-controlled sourceMappingURL reads arbitrary .map files when `from` is unset",
"range": "<=8.5.22"
},
{
"pkg": "postcss",
"severity": "high",
"advisory": "GHSA-qx2v-qp2m-jg93",
"title": "PostCSS has XSS via Unescaped </style> in its CSS Stringify Output",
"range": "<8.5.10"
},
{
"pkg": "postcss",
"severity": "high",
"advisory": "GHSA-r28c-9q8g-f849",
"title": "PostCSS: Path Traversal in Previous Source Map Auto-Loading (sourceMappingURL) leads to Arbitrary .map File Disclosure",
"range": "<=8.5.17"
},
{
"pkg": "probe-image-size",
"severity": "high",
"advisory": "GHSA-gjj5-9665-rwrc",
"title": "probe-image-size: Quadratic-time Denial of Service in the SVG Parser",
"range": "<=7.3.0"
},
{
"pkg": "serialize-javascript",
"severity": "high",
"advisory": "GHSA-5c6j-r48x-rmvq",
"title": "Serialize JavaScript is Vulnerable to RCE via RegExp.flags and Date.prototype.toISOString()",
"range": "<=7.0.2"
},
{
"pkg": "serialize-javascript",
"severity": "high",
"advisory": "GHSA-m5mm-p8h7-m29v",
"title": "Serialize JavaScript: Cross-site scripting (XSS) via unescaped </script at the end of a serialized function body",
"range": "<7.1.3"
},
{
"pkg": "serialize-javascript",
"severity": "high",
"advisory": "GHSA-qj8w-gfj5-8c6v",
"title": "Serialize JavaScript has CPU Exhaustion Denial of Service via crafted array-like objects",
"range": ">=5.0.0 <7.0.5"
},
{
"pkg": "sharp",
"severity": "high",
"advisory": "GHSA-f88m-g3jw-g9cj",
"title": "sharp inherited vulnerabilities in libvips: CVE-2026-33327, CVE-2026-33328, CVE-2026-35590, CVE-2026-35591",
"range": "<0.35.0"
},
{
"pkg": "sharp",
"severity": "high",
"advisory": "GHSA-rgj7-g3m4-5g8c",
"title": "sharp: Vulnerabilities in libheif: GHSA-g89c-p67h-r497 and GHSA-2jg2-4ch7-h545",
"range": "<0.35.4"
},
{
"pkg": "sharp",
"severity": "high",
"advisory": "GHSA-wq5f-xc86-pv6w",
"title": "sharp : Vulnerability in librsvg dependency CVE-2026-96889",
"range": "<0.35.5"
},
{
"pkg": "socket.io-parser",
"severity": "high",
"advisory": "GHSA-2m8v-j782-fhvr",
"title": "Socket.IO: Zero-attachment Memory Exhaustion",
"range": ">=4.0.0 <4.2.7"
},
{
"pkg": "socket.io-parser",
"severity": "high",
"advisory": "GHSA-677m-j7p3-52f9",
"title": "socket.io allows an unbounded number of binary attachments",
"range": ">=4.0.0 <4.2.6"
},
{
"pkg": "source-map-js",
"severity": "high",
"advisory": "GHSA-68fv-2mgg-jv7q",
"title": "source-map-js allows event-loop denial of service through indexed source-map section offsets",
"range": ">=1.0.0 <1.2.2"
},
{
"pkg": "svgo",
"severity": "high",
"advisory": "GHSA-2p49-hgcm-8545",
"title": "SVGO removeScripts plugin leaves some executable scripts intact",
"range": ">=1.0.0 <2.8.3"
},
{
"pkg": "svgo",
"severity": "high",
"advisory": "GHSA-4vpr-x523-8j87",
"title": "SVGO: removeScripts incompletely sanitizes executable HTML in SVG foreignObject elements",
"range": ">=1.0.0 <2.8.4"
},
{
"pkg": "svgo",
"severity": "high",
"advisory": "GHSA-w27v-7q3p-w38r",
"title": "SVGO: removeScripts allows executable links through namespace and control-character bypasses",
"range": ">=1.0.0 <2.8.4"
},
{
"pkg": "svgo",
"severity": "high",
"advisory": "GHSA-xpqw-6gx7-v673",
"title": "SVGO DoS through entity expansion in DOCTYPE (Billion Laughs)",
"range": ">=2.1.0 <2.8.1"
},
{
"pkg": "tmp",
"severity": "high",
"advisory": "GHSA-52f5-9888-hmc6",
"title": "tmp allows arbitrary temporary file / directory write via symbolic link `dir` parameter",
"range": "<=0.2.3"
},
{
"pkg": "tmp",
"severity": "high",
"advisory": "GHSA-ph9p-34f9-6g65",
"title": "tmp has Path Traversal via unsanitized prefix/postfix that enables directory escape",
"range": "<0.2.6"
},
{
"pkg": "webpack-dev-middleware",
"severity": "high",
"advisory": "GHSA-g84c-rxfj-3j2c",
"title": "webpack-dev-middleware vulnerable to Path Traversal via non-slash-terminated publicPath",
"range": "<7.4.5"
},
{
"pkg": "ws",
"severity": "high",
"advisory": "GHSA-58qx-3vcg-4xpx",
"title": "ws: Uninitialized memory disclosure",
"range": ">=8.0.0 <8.20.1"
},
{
"pkg": "ws",
"severity": "high",
"advisory": "GHSA-96hv-2xvq-fx4p",
"title": "ws: Memory exhaustion DoS from tiny fragments and data chunks",
"range": ">=8.0.0 <8.21.0"
}
]
}
+107
View File
@@ -0,0 +1,107 @@
#!/usr/bin/env node
/**
* Audit gate: fail the build on any advisory that is not a tracked exception.
*
* `npm audit --audit-level=<x>` is all-or-nothing, and this tree cannot pass a
* flat gate: the Gatsby 5 build toolchain carries ~100 advisories at
* low/moderate/high with no non-breaking fix, because gatsby is a runtime
* dependency of the build. Blocking on any of those stops publishing
* permanently; ignoring the tree lets a genuinely new advisory through. So the
* known set is committed to audit-baseline.json with an owner and a reason,
* and this script fails only when something NEW appears at or above the
* threshold.
*
* Usage: node scripts/audit-gate.mjs [--write-baseline]
*/
import { execFileSync } from 'node:child_process';
import { readFileSync, writeFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
const HERE = dirname(fileURLToPath(import.meta.url));
const BASELINE = join(HERE, 'audit-baseline.json');
const BLOCKING = new Set(['critical', 'high']);
/** npm audit exits non-zero when it finds anything, so ignore the status. */
function auditReport() {
let out;
try {
out = execFileSync('npm', ['audit', '--json', '--omit=optional'], {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'ignore'],
maxBuffer: 32 * 1024 * 1024
});
} catch (err) {
out = err.stdout;
if (!out) throw err;
}
return JSON.parse(out);
}
/** Flatten the report to one key per package/severity/advisory triple. */
function currentFindings(report) {
const findings = new Map();
for (const [pkg, v] of Object.entries(report.vulnerabilities ?? {})) {
if (!BLOCKING.has(v.severity)) continue;
const advisories = (v.via ?? []).filter((x) => typeof x === 'object');
if (advisories.length === 0) {
// via is a chain of parent package names - attribute to the package.
findings.set(`${pkg}|${v.severity}|transitive`, { pkg, severity: v.severity, advisory: 'transitive' });
continue;
}
for (const a of advisories) {
const id = (a.url ?? '').split('/').pop() || a.title || 'unknown';
findings.set(`${pkg}|${v.severity}|${id}`, { pkg, severity: v.severity, advisory: id, title: a.title, range: a.range });
}
}
return findings;
}
const report = auditReport();
const current = currentFindings(report);
if (process.argv.includes('--write-baseline')) {
const accepted = [...current.values()].sort((a, b) => `${a.pkg}${a.advisory}`.localeCompare(`${b.pkg}${b.advisory}`));
writeFileSync(
BASELINE,
JSON.stringify(
{
$comment:
'Advisories at critical/high accepted for this tree. scripts/audit-gate.mjs fails the build on anything not listed here, so removing an entry re-arms the gate for that advisory. Regenerate with: node scripts/audit-gate.mjs --write-baseline',
owner: 'Site maintainers (allan@4gl.io)',
reason:
'Gatsby 5 build toolchain: no non-breaking fix available upstream, and these are build-time dependencies of a statically generated site. Re-check on each Gatsby major bump.',
accepted
},
null,
2
) + '\n'
);
console.log(`wrote ${accepted.length} accepted advisories to ${BASELINE}`);
process.exit(0);
}
const baseline = JSON.parse(readFileSync(BASELINE, 'utf8'));
const accepted = new Set((baseline.accepted ?? []).map((a) => `${a.pkg}|${a.severity}|${a.advisory}`));
const fresh = [...current.entries()].filter(([key]) => !accepted.has(key));
const resolved = [...accepted].filter((key) => !current.has(key));
for (const [, f] of fresh) {
console.error(`NEW ${f.severity.toUpperCase()}: ${f.pkg} - ${f.title ?? f.advisory} (${f.advisory})`);
}
if (resolved.length > 0) {
// Not a failure: a bumped or removed dependency closes advisories, and the
// baseline should be trimmed in the same change that closes them.
console.log(`${resolved.length} baseline entries no longer present - consider re-running with --write-baseline:`);
for (const key of resolved) console.log(` stale: ${key}`);
}
if (fresh.length > 0) {
console.error(`\n${fresh.length} untracked advisory/ies at critical or high.`);
console.error('Fix it, or add it to scripts/audit-baseline.json with a reason and an owner.');
process.exit(1);
}
console.log(`audit gate: no new critical/high advisories (${current.size} tracked in baseline)`);
+1 -1
View File
@@ -14,7 +14,7 @@ const NotFound: React.FC<PageProps<unknown, PageContext>> = ({
pageContext pageContext
}) => ( }) => (
<Layout location={location} heroSection={false}> <Layout location={location} heroSection={false}>
<Section color="black" bgColor="white" bottomArrow={false}> <Section color="black" bgColor="white" bottomArrow={false}>
<SideBar <SideBar
pageContext={pageContext} pageContext={pageContext}
location={location} location={location}
+1 -1
View File
@@ -18,7 +18,7 @@ const About: React.FC<PageProps<unknown>> = ({ location }) => {
heading="Behind Data Controller" heading="Behind Data Controller"
desc="Data Controller is a product of 4GL, a brand of Bowe IO Ltd" desc="Data Controller is a product of 4GL, a brand of Bowe IO Ltd"
> >
<Section color="black" bgColor="white"> <Section color="black" bgColor="white">
<div className="row"> <div className="row">
<div className="offset-md-2 col-md-8"> <div className="offset-md-2 col-md-8">
<SectionHeading center="no">About Data Controller</SectionHeading> <SectionHeading center="no">About Data Controller</SectionHeading>
+1 -1
View File
@@ -58,7 +58,7 @@ const Contact: React.FC<PageProps<unknown>> = ({ location }) => {
heading="Book a Demo" heading="Book a Demo"
desc="Schedule a demonstration of the Data Controller to see our latest features and explore use cases" desc="Schedule a demonstration of the Data Controller to see our latest features and explore use cases"
> >
<Section color="black" bgColor="white" bottomArrow={false}> <Section color="black" bgColor="white" bottomArrow={false}>
<div className="row"> <div className="row">
<div className="col-md-6"> <div className="col-md-6">
<SectionHeading center="no">Schedule a Call</SectionHeading> <SectionHeading center="no">Schedule a Call</SectionHeading>
+1 -1
View File
@@ -20,7 +20,7 @@ const Faq: React.FC<PageProps<unknown>> = ({ location }) => {
heading="Data Controller – FAQ" heading="Data Controller – FAQ"
desc="Most common user questions and answers regarding the Data Controller" desc="Most common user questions and answers regarding the Data Controller"
> >
<Section color="black" bgColor="white"> <Section color="black" bgColor="white">
<div className="row"> <div className="row">
<div className="col-md-8"> <div className="col-md-8">
<FAQ <FAQ
+1 -1
View File
@@ -68,7 +68,7 @@ const Home: React.FC<PageProps<IndexPageData>> = ({ data, location }) => {
heading="Flexible and Secure Data Modification" heading="Flexible and Secure Data Modification"
desc="Data Controller for SAS® enables business users to update IT owned source tables, secured by review / approve workflow and full audit trail." desc="Data Controller for SAS® enables business users to update IT owned source tables, secured by review / approve workflow and full audit trail."
> >
<Section color="black" bgColor="white"> <Section color="black" bgColor="white">
<div className="row"> <div className="row">
<div className="col-md-3"> <div className="col-md-3">
<Art src={leftArt} info="Business Owner" /> <Art src={leftArt} info="Business Owner" />
+1 -1
View File
@@ -69,7 +69,7 @@ const BlogListTemplate: React.FC<PageProps<BlogListData, PageContext>> = ({
heading="Data Controller’s Knowledge Base" heading="Data Controller’s Knowledge Base"
desc="A section dedicated to news, updates and educational pieces." desc="A section dedicated to news, updates and educational pieces."
> >
<Section color="black" bgColor="white" bottomArrow={false}> <Section color="black" bgColor="white" bottomArrow={false}>
<div className="row"> <div className="row">
<div className="col-md-7"> <div className="col-md-7">
<div className="row"> <div className="row">
+1 -1
View File
@@ -71,7 +71,7 @@ const BlogSearchTemplate: React.FC<PageProps<SearchData, PageContext>> = ({
heading="Data Controller’s Knowledge Base" heading="Data Controller’s Knowledge Base"
desc="A section dedicated to news, updates and educational pieces." desc="A section dedicated to news, updates and educational pieces."
> >
<Section color="black" bgColor="white" bottomArrow={false}> <Section color="black" bgColor="white" bottomArrow={false}>
<div className="row"> <div className="row">
<div className="col-md-7"> <div className="col-md-7">
<div className="row"> <div className="row">
+1 -1
View File
@@ -68,7 +68,7 @@ const FeedListTemplate: React.FC<PageProps<FeedListData, PageContext>> = ({
heading="Data Controller’s Feed" heading="Data Controller’s Feed"
desc="Announcements and social media style updates from the Data Controller team." desc="Announcements and social media style updates from the Data Controller team."
> >
<Section color="black" bgColor="white" bottomArrow={false}> <Section color="black" bgColor="white" bottomArrow={false}>
<div className="row"> <div className="row">
<div className="col-md-7"> <div className="col-md-7">
<div className="row"> <div className="row">