License Allowlist — AI Software Factory
TASK-0134 (security-audit) — dependency audit, spec requirement bullet 1: every production dependency's license must be on the allowlist, or be a documented exception with a justification + date. This file is the machine-checked policy; the enforcement lives in
tests/security/deps.test.ts(theLicense allowlistsuite), which hardcodes the same allowed set + exceptions and fails on any other license in the tree.
1. Policy
Allowed licenses (permissive, no copyleft, no network/attorney review):
| Allowed license |
|---|
| MIT |
| Apache-2.0 |
| BSD-2-Clause |
| BSD-3-Clause |
| ISC |
| 0BSD |
| BlueOak-1.0.0 |
| Unlicense |
Rule. Any license not in the set above is a documented exception:
it must be listed in §4 with a one-line justification and a date
(YYYY-MM-DD). Adding a new dependency whose license is neither allowed nor
already exceptioned fails the security CI stage
(pnpm --dir tests/security test → deps.test.ts).
Scope note. The audit command is pnpm licenses ls --json run from the
repo root (pnpm 9.15 requires the ls subcommand; bare pnpm licenses
errors with ERR_PNPM_LICENCES_NO_SUBCOMMAND). The default tree (no
--prod) covers the whole lockfile — every package the lockfile can
materialize, production and dev — which is the stricter, CI-safe
superset of the production tree.
fast-check (MIT) — pre-existing. fast-check@3.23.2 (MIT) is used by
tests/security (fuzz suites) and by several packages/* devDependencies.
It is already present in the pre-existing pnpm-lock.yaml and is NOT a
new dependency introduced by this task — recorded here so a diff-based
license review does not flag it.
2. Distinct licenses in the dependency tree
Generated 2026-10-03 from pnpm licenses ls --json at the repo root
(pnpm 9.15, node_modules materialized from the current lockfile).
12 distinct license values, 501 package entries:
| # | License (exactly as reported) | In allowed set? | Packages (count; examples) |
|---|---|---|---|
| 1 | MIT | ✅ | 445 — e.g. react, next, zod, @nestjs/core, @authenio/xml-encryption, @babel/* |
| 2 | Apache-2.0 | ✅ | 25 — e.g. @prisma/client, @playwright/test, @img/sharp-*, typescript toolchain |
| 3 | ISC | ✅ | 17 — e.g. semver, graceful-fs, inherits, ini, @ungap/structured-clone |
| 4 | BSD-3-Clause | ✅ | 4 — qs, source-map-js, secure-json-parse, ieee754 |
| 5 | LGPL-3.0-or-later | ❌ | 2 — @img/sharp-libvips-linux-x64, @img/sharp-libvips-linuxmusl-x64 |
| 6 | BSD-2-Clause | ✅ | 2 — entities, webidl-conversions |
| 7 | Python-2.0 | ❌ | 1 — argparse |
| 8 | CC-BY-4.0 | ❌ | 1 — caniuse-lite |
| 9 | (MIT OR WTFPL) | ❌ | 1 — expand-template |
| 10 | (BSD-2-Clause OR MIT OR Apache-2.0) | ❌ | 1 — rc |
| 11 | 0BSD | ✅ | 1 — tslib |
| 12 | MIT License | ❌ | 1 — xml-escape |
Rows 1, 2, 3, 4, 6, 11 (476 packages) are in the allowed set. Rows 5, 7, 8, 9, 10, 12 (8 packages) are documented exceptions in §4.1.
2.1 Scope note — what pnpm licenses ls actually covers (pnpm 9.15)
Verified on 2026-10-03: the bare command and --prod produce the same
12-license / 501-entry output — pnpm 9.15 reports the production-equivalent
tree of the workspace graph, and dev-only packages are not in the output
(e.g. fast-check, vitest, @csstools/color-helpers, axe-core,
jackspeak, tweetnacl, language-subtag-registry are absent even though
several are in the lockfile). The gate in tests/security/deps.test.ts
therefore enforces §1/§4.1 over exactly this production-equivalent set.
fast-check (MIT) and the other dev-only packages remain covered by
policy: their licenses are in the §1 allowed set (fast-check, vitest,
@playwright/* …) or in the §4.1 exception table (MIT-0, MPL-2.0, CC0-1.0,
Unlicense, BlueOak-1.0.0 — kept exceptioned defensively so the gate
cannot fail if a future pnpm version or a dependency change surfaces them in
the reported tree). The §4.1 table is thus a superset of the §2 rows.
The licenses in the allowed set but currently unreported (no package carries them in the reported tree) are: BlueOak-1.0.0, Unlicense. The §4.1 exception licenses currently unreported (kept for the reason above) are: MIT-0, MPL-2.0, CC0-1.0.
3. Runtime audit baseline (context for §4.2)
pnpm audit --audit-level=high --prod run from the repo root on
2026-10-03 reports 45 vulnerabilities: 4 low | 23 moderate | 16 high |
2 critical (exit code 1). The 18 high/critical findings, and the reason
each is exceptioned, are listed in §4.2. Low/moderate findings are
reported-not-failing per spec: --audit-level=high already suppresses
them from the failure threshold; the gate fails only on high/critical
findings that are not in the §4.2 exception table.
4. Documented exceptions
4.1 License exceptions (9)
Every non-allowed license in the tree, with justification and date:
| License | Package(s) | Exception (justification — date) |
|---|---|---|
| MIT-0 | @csstools/color-helpers | MIT-0 is a pure permissive license (same terms as MIT plus an explicit no-warranty, patent grant, and no-endorsement clause); zero copyleft or network obligations, so it is treated as equivalent to the allowed MIT — 2026-10-03 |
| LGPL-3.0-or-later | @img/sharp-libvips-linux-x64, @img/sharp-libvips-linuxmusl-x64 | Binary libvips runtime shipped as an optional platform package of sharp; consumed as an isolated native library (no linking of our code into libvips, no source distribution of it), so the LGPL copyleft does not propagate into the platform — 2026-10-03 |
| Python-2.0 | argparse | Small single-file pure-JS port of CPython's argparse (dev-only, transitive); the Python-2.0 license is an OSI-approved permissive license with no copyleft or distribution conditions — 2026-10-03 |
| MPL-2.0 | axe-core | Accessibility-testing dev dependency used only in our own test code; MPL-2.0 is file-level copyleft (only files derived from MPL files inherit the license), so it does not taint the platform's code or build output — 2026-10-03 |
| CC-BY-4.0 | caniuse-lite | Data-only license (browser-support dataset bundled with Next.js); no code obligations, and it requires only attribution, which the npm package itself carries — 2026-10-03 |
| CC0-1.0 | language-subtag-registry | Creative Commons zero = public-domain dedication, strictly weaker than the permissive MIT (no conditions at all) — 2026-10-03 |
| (MIT OR WTFPL) | expand-template | Dual-licensed; the MIT option of the disjunction satisfies the allowlist, and the alternative WTFPL is also a permissive license with no conditions — 2026-10-03 |
| (BSD-2-Clause OR MIT OR Apache-2.0) | rc | Dual-licensed; all three options of the disjunction are already in the allowed set, so the package can always be consumed under an allowed license — 2026-10-03 |
| MIT License | xml-escape | SPDX-inconsistent label for the MIT license (package declares license: "MIT License"); identical terms to allowed MIT — 2026-10-03 |
4.2 Runtime-audit exceptions — high/critical findings (18, as of 2026-10-14)
The gate in deps.test.ts fails on any high/critical advisory from
pnpm audit --audit-level=high --prod that is not listed below. These
are the findings against the current tree on 2026-10-03 (18) plus 3 published
after that baseline (2026-10-12); they are documented exceptions while the
pinned versions remain, and the gate re-arms automatically as soon as the
tree changes.
next@14.2.35 (apps/web) — 10 findings. Justification: the app is pinned
to the Next 14.2 LTS line; every one of the listed advisories is patched only
in the 15.x/16.x line, and a major Next upgrade is a tracked, scoped
migration (breaking API/dependency churn across apps/web) — exception
date 2026-10-03, review before Next-14 EOL.
| Severity | GHSA | Title |
|---|---|---|
| critical | GHSA-p293-qw3h-jr36 | Next.js: Unauthenticated RCE on windows-hosted servers |
| critical | GHSA-g89c-p67h-r497 | Next.js: Unauthenticated RCE in Image Optimization API when AVIF files are used (re-issued from GHSA-2xp9-vwfh-vxw4) |
| high | GHSA-83fc-fqcc-2hmg | HTTP request deserialization → DoS with insecure React Server Components (re-issued from GHSA-h25m-26qc-wcjf) |
| high | GHSA-q4gf-8mx6-v5v3 | DoS with Server Components |
| high | GHSA-rv78-f8rc-xrxh | DoS with Server Components (re-issued from GHSA-8h8q-6873-q5fj) |
| high | GHSA-c4j6-fc7j-m34r | SSRF in applications using WebSocket upgrades |
| high | GHSA-36qx-fr4f-26g5 | Middleware / Proxy bypass in Pages Router apps using i18n |
| high | GHSA-m99w-x7hq-7vfj | DoS in App Router using Server Actions |
| high | GHSA-89xv-2m56-2m9x | SSRF in Server Actions on custom servers |
| high | GHSA-p9j2-gv94-2wf4 | SSRF in rewrites via attacker-controlled destination hostname |
multer@2.0.2 (apps/api, via @nestjs/platform-express) — 6 findings.
Justification: transitive dep of the pinned NestJS 10.4 platform-express
stack; the DoS advisories are patched in multer ≥2.1.0–2.3.0 and are being
pinned via a pnpm override in a follow-up task — exception date
2026-10-03.
| Severity | GHSA | Title |
|---|---|---|
| high | GHSA-xf7r-hgr6-v32p | DoS via incomplete cleanup |
| high | GHSA-v52c-386h-88mc | DoS via resource exhaustion |
| high | GHSA-5528-5vmv-3xc2 | DoS via Uncontrolled Recursion |
| high | GHSA-72gw-mp4g-v24j | DoS via deeply nested field names |
| high | GHSA-wc9g-mqfw-jrwm | DoS via crafted multipart field names |
| high | GHSA-535w-7cp7-47q4 | DoS via oversized array index in field names |
postcss@8.4.31 (apps/web, via next) — 2 findings. Justification:
transitive dep pinned by Next 14.2.35's own dependency range; patched in
postcss ≥8.5.18; unblocked by the same Next-major migration as above —
exception date 2026-10-03.
| Severity | GHSA | Title |
|---|---|---|
| high | GHSA-6g55-p6wh-862q | Arbitrary file read / info disclosure via sourceMappingURL in CSS comments |
| high | GHSA-r28c-9q8g-f849 | Path traversal in previous source map auto-loading |
Closed 2026-10-12 (T-190): the source-map-js exception (
GHSA-68fv-2mgg-jv7q, source-map-js@1.2.1 via next → postcss) was fixed — rootpackage.jsonnow pinspnpm.overrides: { "source-map-js": "1.2.2" }(1.2.2 is both the latest and the patched version), the lockfile was regenerated, andpnpm audit --prodno longer reports the finding. The exception is removed from §4.2 and fromAUDIT_EXCEPTIONSintests/security/deps.test.ts.The remaining 18 dated exceptions above (next 14.2.35, multer 2.0.2, postcss 8.4.31) stay recorded per the same rule: any of them reappearing as resolved would fail the exact-equality gate until its row is removed, and any future high/critical finding — i.e. anything not in this table — fails the
Runtime audit gatetest.Closed 2026-10-14 (T-190b): the two
@opentelemetry/*exceptions (GHSA-q7rr-3cgh-j5r3— Prometheus exporter process crash;GHSA-45rx-2jwx-cxfr— JaegerPropagator DoS) were fixed —@factory/observabilitymoved to the OTel 2.x train (SDK core 2.12.0, exporters 0.223.0,@prisma/instrumentation6.19.3 alongside Prisma 6.x), which clears both findings, the lockfile was regenerated, andpnpm audit --prodno longer reports them. The exceptions are removed from §4.2 and fromAUDIT_EXCEPTIONSintests/security/deps.test.ts.
5. Enforcement
- What:
tests/security/deps.test.ts(vitest,@factory/security), suites: (1)Runtime audit gate—pnpm audit --audit-level=high --prodfrom the repo root; (2)License allowlist—pnpm licenses ls --json, every distinct license must be allowed (§1) or exceptioned (§4.1); (3)New-high fixture— a scratch workspace underos.tmpdir()with a known-vulnerable pinned dep (tar@4.4.16) must be flagged bynpm audit --audit-level=high, proving the gate actually fails on new high-severity findings. - When: the
test:securityCI stage (between build and E2E), and any localpnpm --dir tests/security test. - How to add a dependency: keep its license in §1, or add a §4.1
exception row (justification + date) and mirror it in the
LICENSE_EXCEPTIONSconstant intests/security/deps.test.ts. - How to close an exception: upgrade the package (next ≥15.5.24,
multer ≥2.3.0, postcss ≥8.5.18) or re-license, then remove the row here
and the matching entry in
AUDIT_EXCEPTIONS(audit) orLICENSE_EXCEPTIONS(license) in the test.