diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index af0b7038..be61e48f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -26,7 +26,7 @@ technical problem on its own terms and the private tracker links to it. ## Set up the repository -The monorepo recommends Node.js 24 LTS and uses pnpm 11.20.0. Node.js 22 is +The monorepo recommends Node.js 24 LTS and uses pnpm 11.28.2. Node.js 22 is also supported from 22.13.0. Docker is required for the local platform stack and workspace end-to-end tests. For nvm users, the repository's `.nvmrc` selects the recommended Node.js 24 line with `nvm use`. @@ -34,7 +34,7 @@ the recommended Node.js 24 line with `nvm use`. ```bash git clone https://github.com/theam/facility.git cd facility -corepack install --global pnpm@11.20.0 +corepack install --global pnpm@11.28.2 pnpm dev ``` diff --git a/Dockerfile b/Dockerfile index d24c1854..e444accd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ ENV PNPM_HOME=/pnpm ENV PATH=/pnpm:$PATH # Keep a digest-pinned base while still making a reviewed Debian security # refresh invalidate BuildKit's cached package layer. -ARG DEBIAN_SECURITY_REFRESH=20260915 +ARG DEBIAN_SECURITY_REFRESH=20261005 # The slim base has Node's bundled trust roots but not the system CA package. # Bootstrap HTTPS with those roots; apt then installs the maintained OS bundle. RUN node -e "const fs = require('node:fs'); fs.mkdirSync('/etc/ssl/certs', {recursive:true}); fs.writeFileSync('/etc/ssl/certs/ca-certificates.crt', require('node:tls').rootCertificates.join('\\n'))" \ diff --git a/README.md b/README.md index 45ff233d..17f44fbd 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ The project manifest also lets you [choose CPU and memory for new story workspac ## Quick start: run Facility -You need Docker, Node.js 24 LTS, and pnpm 11.20.0. Node.js 22 is also supported +You need Docker, Node.js 24 LTS, and pnpm 11.28.2. Node.js 22 is also supported from 22.13.0. Start the application locally, then connect a repository to run Claude Code or Codex in a story workspace. @@ -71,7 +71,7 @@ Claude Code or Codex in a story workspace. ```bash git clone https://github.com/theam/facility.git cd facility -corepack install --global pnpm@11.20.0 +corepack install --global pnpm@11.28.2 pnpm dev ``` @@ -161,7 +161,7 @@ behavior and boundaries can be agreed on. ```bash git clone https://github.com/theam/facility.git cd facility -corepack install --global pnpm@11.20.0 +corepack install --global pnpm@11.28.2 pnpm install --frozen-lockfile pnpm verify ``` diff --git a/apps/docs/docs/reference/reference-fixture.md b/apps/docs/docs/reference/reference-fixture.md index d1bad731..56049063 100644 --- a/apps/docs/docs/reference/reference-fixture.md +++ b/apps/docs/docs/reference/reference-fixture.md @@ -12,7 +12,7 @@ sample builder and scheduled-security agents. It has no network or credential de From a clean checkout, run: ```bash -corepack install --global pnpm@11.20.0 +corepack install --global pnpm@11.28.2 pnpm install --frozen-lockfile docker build -f runner/Dockerfile -t facility-runner:dev . FACILITY_E2E_DOCKER=1 \ diff --git a/apps/docs/docs/self-host/local-development.md b/apps/docs/docs/self-host/local-development.md index 65f99e60..b5d47162 100644 --- a/apps/docs/docs/self-host/local-development.md +++ b/apps/docs/docs/self-host/local-development.md @@ -9,10 +9,10 @@ This guide covers work on the Facility monorepo. If you only want to run the pro ## Toolchain -Install Node.js 24 LTS and pnpm 11.20.0 before running the repository: +Install Node.js 24 LTS and pnpm 11.28.2 before running the repository: ```bash -corepack install --global pnpm@11.20.0 +corepack install --global pnpm@11.28.2 ``` The root `package.json` also accepts Node.js 22 from 22.13.0. Use the repository `.nvmrc` when diff --git a/apps/docs/docs/self-host/quickstart.md b/apps/docs/docs/self-host/quickstart.md index 3c12ee69..477cd829 100644 --- a/apps/docs/docs/self-host/quickstart.md +++ b/apps/docs/docs/self-host/quickstart.md @@ -18,7 +18,7 @@ nested Docker images, and persistent volumes. ## Start the local stack ```bash -corepack install --global pnpm@11.20.0 +corepack install --global pnpm@11.28.2 pnpm install --frozen-lockfile cp .env.example .env docker build -f runner/Dockerfile -t facility-runner:dev . diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile index 50bbb140..9882b31d 100644 --- a/apps/web/Dockerfile +++ b/apps/web/Dockerfile @@ -3,7 +3,7 @@ # docker build -f apps/web/Dockerfile -t facility/web . FROM node:24-trixie-slim@sha256:50c3b2f6988dfc307b86e5301d69611af31f4789bdf232863b07d3b02fe55ae0 AS base ENV PNPM_HOME=/pnpm PATH=/pnpm:$PATH -ARG DEBIAN_SECURITY_REFRESH=20260915 +ARG DEBIAN_SECURITY_REFRESH=20261005 RUN test -n "$DEBIAN_SECURITY_REFRESH" \ && apt-get update \ && DEBIAN_FRONTEND=noninteractive apt-get upgrade -y \ diff --git a/package.json b/package.json index c9aad01f..db197e0d 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "engines": { "node": "^22.13.0 || ^24.0.0" }, - "packageManager": "pnpm@11.20.0", + "packageManager": "pnpm@11.28.2", "scripts": { "build": "turbo run build", "clean:outputs": "node scripts/clean-outputs.mjs", diff --git a/patches/js-yaml@3.15.2.patch b/patches/js-yaml@3.15.2.patch new file mode 100644 index 00000000..8bb8871b --- /dev/null +++ b/patches/js-yaml@3.15.2.patch @@ -0,0 +1,21 @@ +diff --git a/bin/js-yaml.js b/bin/js-yaml.js +index e79186be6f56f1d92de9bcd4fa4b10fff9564795..e1c0a90287763ec0bb06117c14bff8d5cb0e9e99 100755 +--- a/bin/js-yaml.js ++++ b/bin/js-yaml.js +@@ -23,10 +23,15 @@ var yaml = require('..'); + + var cli = new argparse.ArgumentParser({ + prog: 'js-yaml', +- version: require('../package.json').version, + addHelp: true + }); + ++cli.addArgument([ '-v', '--version' ], { ++ action: 'version', ++ version: require('../package.json').version, ++ help: "show program's version number and exit" ++}); ++ + + cli.addArgument([ '-c', '--compact' ], { + help: 'Display errors in compact mode', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 475eb20d..b66e2494 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,16 @@ settings: excludeLinksFromLockfile: false overrides: + proxy-addr@>=1.1.0 <2.0.8: 2.0.8 + tinypool@<2.1.2: 2.1.2 + source-map-js@>=1.0.0 <1.2.2: 1.2.2 + compression@<1.8.2: 1.8.2 + postcss-selector-parser@<7.1.6: 7.1.6 + prosemirror-view@<1.42.3: 1.42.3 + vue@>=3.0.0 <3.5.42: 3.5.42 + katex@>=0.11.0 <0.18.2: 0.18.2 + js-yaml@3>argparse: 2.0.1 + http-cache-semantics@<4.3.0: 4.3.0 browserslist@<4.28.7: 4.28.7 esbuild@>=0.27.3 <0.28.1: 0.28.1 postcss@<8.5.23: 8.5.23 @@ -33,11 +43,12 @@ overrides: dompurify@<3.4.16: 3.4.16 nanoid@>=3.0.0 <3.3.18: 3.3.18 colord@<2.9.4: 2.9.4 - joi@>=16.0.0 <17.13.7: 17.13.7 + joi@>=16.0.0 <17.13.8: 17.13.8 baseline-browser-mapping@>=2.0.0 <2.11.0: 2.11.0 patchedDependencies: image-size@2.0.2: 91c116f6f63aa8ff1be47af798f33721d884b495bae00fdf2adfe313bfbad42a + js-yaml@3.15.2: 1610aa3fa9f60a71602f33f5bf14f5b03af00fd3182fa6dc79e0e946a66ba001 importers: @@ -103,13 +114,13 @@ importers: version: link:../../packages/ui '@milkdown/crepe': specifier: ^7.21.3 - version: 7.21.3(prosemirror-model@1.25.11)(prosemirror-state@1.4.4)(prosemirror-view@1.42.1)(supports-color@10.2.2)(typescript@5.9.3) + version: 7.21.3(prosemirror-model@1.25.11)(prosemirror-state@1.4.4)(prosemirror-view@1.42.3)(supports-color@10.2.2)(typescript@5.9.3) '@milkdown/kit': specifier: 7.21.3 version: 7.21.3(@codemirror/language@6.12.4)(@codemirror/state@6.7.1)(@codemirror/view@6.43.6)(supports-color@10.2.2)(typescript@5.9.3) '@milkdown/react': specifier: 7.21.3 - version: 7.21.3(@codemirror/language@6.12.4)(@codemirror/state@6.7.1)(@codemirror/view@6.43.6)(prosemirror-model@1.25.11)(prosemirror-state@1.4.4)(prosemirror-view@1.42.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(supports-color@10.2.2)(typescript@5.9.3) + version: 7.21.3(@codemirror/language@6.12.4)(@codemirror/state@6.7.1)(@codemirror/view@6.43.6)(prosemirror-model@1.25.11)(prosemirror-state@1.4.4)(prosemirror-view@1.42.3)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(supports-color@10.2.2)(typescript@5.9.3) framer-motion: specifier: ^12.42.2 version: 12.42.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -651,6 +662,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.29.9': + resolution: {integrity: sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7': resolution: {integrity: sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==} engines: {node: '>=6.9.0'} @@ -1115,6 +1131,10 @@ packages: resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + '@balena/dockerignore@1.0.2': resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==} @@ -1598,13 +1618,13 @@ packages: resolution: {integrity: sha512-mf1LEW0tJLKfWyvn5KdDrhpxHyuxpbNwTIwOYLIvsTffeyOf85j5oIzfG0yosxDgx/sswlqBnESYUcQH0vgZ0g==} engines: {node: '>=18'} peerDependencies: - postcss-selector-parser: ^7.0.0 + postcss-selector-parser: 7.1.6 '@csstools/selector-specificity@5.0.0': resolution: {integrity: sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==} engines: {node: '>=18'} peerDependencies: - postcss-selector-parser: ^7.0.0 + postcss-selector-parser: 7.1.6 '@csstools/utilities@2.0.0': resolution: {integrity: sha512-5VdOr0Z71u+Yp3ozOx8T11N703wIFGVRgOWbOZMKgglPJsWA54MRIoMNVMa7shUToIhx5J8vX4sOZgD2XiihiQ==} @@ -3545,32 +3565,32 @@ packages: '@vitest/utils@4.1.11': resolution: {integrity: sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==} - '@vue/compiler-core@3.5.40': - resolution: {integrity: sha512-39E8IgOhTbVDnoJFMKc2DvYnypcZwUqgUhQkccva/0m6FUwtIKSGV7n1hpVmYcFaoRAwf9pBcwnKlCEsN63ZEQ==} + '@vue/compiler-core@3.5.42': + resolution: {integrity: sha512-2Ye1ilMtKXxl8qZUrQ5j0CdgenFp/HFQmta6rfRyfEsTG69L6Wk+tWuNoHYHMx9E8tF2Slvdg1FuwDvAXdy1LQ==} - '@vue/compiler-dom@3.5.40': - resolution: {integrity: sha512-pwkx4vqlqOspFstrcmzwkKLePVMD3PT65imRzLhanU2V1Fj4K13g6OXjanOyzw3aTAuRk84BOmY8f3rEHqPaVA==} + '@vue/compiler-dom@3.5.42': + resolution: {integrity: sha512-qbhQZEFmycr+ni/qyuccS4sucNN7VAbDfbkvNxWOX2VfgFm90MNs3/UhRNKoPMEIVn0F8gdlYjLPvqxHwHeQOA==} - '@vue/compiler-sfc@3.5.40': - resolution: {integrity: sha512-gIf497P4kpuALcvs5n3AEg1Vdn0pSY4XbjASIfHNYF1/MP3T2Mf2STERTubysBxCRxzJGJYtF/O7vwJrxFB3Vw==} + '@vue/compiler-sfc@3.5.42': + resolution: {integrity: sha512-fkCAFB4okcAANGMThboWnScp/gzWjU0ZSkVnjTIiplmMDq2uq0tIB3j+xVu4rhv5rvOgBySCysudmbMd6xRRqw==} - '@vue/compiler-ssr@3.5.40': - resolution: {integrity: sha512-rrE5xiXG663+vHCHa3J9p2z5OcBRjXmoqenprJxAFQxg5pSshzeBiCE6pu46axapRJ2Adk0YDA2BRZVjiHXnhg==} + '@vue/compiler-ssr@3.5.42': + resolution: {integrity: sha512-xmLk3wLkbizPAiLyomjgFFosf2ys9b5Ghb+oh/k2tnvipNz8OFrQOiTcWCzyK7MpBp9KkyGtfvgfLUivbmuGYA==} - '@vue/reactivity@3.5.40': - resolution: {integrity: sha512-B7ot9UlUZOi1zbq61/LvE88ZLTV8IlajTdiZTAEiDQgrnIMIZoPr9kGw0Zw46ObW62O9+H/Be3kMbfb7kYPQZA==} + '@vue/reactivity@3.5.42': + resolution: {integrity: sha512-TzNNfKpb7hDxbQltwAut8VDQA5YP+BuRlxntHUuRjyKwlMvmAPbs3unhCvieijifY6vFfVBwsS7wG/C7uq+bEQ==} - '@vue/runtime-core@3.5.40': - resolution: {integrity: sha512-KAZLweuZ6uUJPK1PMSQPgBU5gCjgrrfjUhSglmU9NhH+Zjepa8cnwSydPWDWHDwOgY4g3VcZ+PljbiHlURNCbw==} + '@vue/runtime-core@3.5.42': + resolution: {integrity: sha512-9uACtuHs7vJGkm5Bp3xu4xRDLFTIYy5DgxpToVjqGIAhAEKwQfsaLvKINhM6nFVp6bZPRFGdDqd1g52MqKsotA==} - '@vue/runtime-dom@3.5.40': - resolution: {integrity: sha512-ZfrX8ssZQds900L9pr8AuK05ddnMsR4MPMZr8cPN9GoqoPWcXLhjvvbIA2SMv+7a97sJ1vv9pj/zxK0Cq/eEFQ==} + '@vue/runtime-dom@3.5.42': + resolution: {integrity: sha512-rsCmhiWLaRxGltLwhlCWyYkFn7WAbKRh0q17eZ1A6Dq6eqc2ACQ61IIryxz0LrsvCzHSilLA9JHovVwM8CNE2g==} - '@vue/server-renderer@3.5.40': - resolution: {integrity: sha512-XNJym9WpevhTVt1HuwOrCRJ5Q+9z4BjTMrDtjTrvx74SmUll8spNTw6whWJa9mEkO4PKn5TihI/bm/8ds2QVJw==} + '@vue/server-renderer@3.5.42': + resolution: {integrity: sha512-2++5dUyYS4gvo7xQXSECUDhB7TS0aOl5SeVfC5qSq1Jgfhjvegw1zqhwTIR3imZ+QYPJQw9gfcFvXGAjGZ7ajQ==} - '@vue/shared@3.5.40': - resolution: {integrity: sha512-WxnBtruIqOoV3rA4jeKDWzrYI5h7Cp4+pjwDi8kWGHz+IslhiN+wguLVVhtv2l8VoU02rzDCVfDjgCl1lNpZVg==} + '@vue/shared@3.5.42': + resolution: {integrity: sha512-2rPxex1jQf4jvl9MOHl6YaXCPcrNqz/FstMOEh3QWY+/OME9nQTvl9WYeCwhW7AFjaR0SnngZGlp/wkR6rkI6g==} '@webassemblyjs/ast@1.14.1': resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} @@ -3756,9 +3776,6 @@ packages: resolution: {integrity: sha512-zHPGN3S55sihSQo0dBbK0A5qpi2R31z7HZDZnry3ifOyj8bZZnpZND2gpmhnRGO1V/d555RwBqIK5W4Mrmv3ig==} engines: {node: '>=16.17.0'} - argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -4155,8 +4172,8 @@ packages: resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} engines: {node: '>= 0.6'} - compression@1.8.1: - resolution: {integrity: sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==} + compression@1.8.2: + resolution: {integrity: sha512-o8vI5RE5A6EVVOd9o41jKp41aJom+QTEO/Bx8MYNjexMo/Bv2WOjUfZr+aL0WnYSgymUy6zeguqLTsIhV0gMvQ==} engines: {node: '>= 0.8.0'} concat-map@0.0.1: @@ -5260,8 +5277,8 @@ packages: resolution: {integrity: sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==} engines: {node: '>= 0.8'} - http-cache-semantics@4.2.0: - resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + http-cache-semantics@4.3.0: + resolution: {integrity: sha512-M5t5LlJpS1UHMjvwRQVdFHvPISGeLAxNcrWuJkeGh0KxsqCHZ1O3NXZU/8x7cD0BDcGW8kapxMKTvwlqrNkHkA==} http-deceiver@1.2.7: resolution: {integrity: sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==} @@ -5552,8 +5569,8 @@ packages: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true - joi@17.13.7: - resolution: {integrity: sha512-MF80Dm5Y2veNy8QWVx9Bj3ui4mo7+VPSPsR1M+oaHXV0Gx6zGX9a2F+OZG3Blby9tOlzU9Rs5FUimlEhbKtfnQ==} + joi@17.13.8: + resolution: {integrity: sha512-iPKOGmiRw1jxf/JOPwxmCcUQAOdF359mdzYiP2DJ+TMX0YK2zjK3D+zYOaGjpumWxOFF/l2xVWjRVK5bGSLdEw==} jose@6.2.3: resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} @@ -5636,12 +5653,8 @@ packages: jsonlines@0.1.1: resolution: {integrity: sha512-ekDrAGso79Cvf+dtm+mL8OBI2bmAOt3gssYs833De/C9NmIpWDWyUO4zPgB5x2/OhY366dkhgfPMYfwZF7yOZA==} - katex@0.16.47: - resolution: {integrity: sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==} - hasBin: true - - katex@0.17.0: - resolution: {integrity: sha512-Vdw0ATsQ9V+LuegM/BTwQqV/6cTl5lbGcIrU+BCgLxyf6bo38ybOr372tuSIxir3CN720flu1meYR6XzNMwQnw==} + katex@0.18.2: + resolution: {integrity: sha512-3snve4y0SXTMequLim1FMiPvLGElySam0blN5xQZBqEY3lOJz1TnuaaiugnBZBqHzlSAGmFTYL7MCQkORuLKCA==} hasBin: true keygrip@1.1.0: @@ -6953,12 +6966,8 @@ packages: peerDependencies: postcss: 8.5.23 - postcss-selector-parser@6.1.4: - resolution: {integrity: sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==} - engines: {node: '>=4'} - - postcss-selector-parser@7.1.4: - resolution: {integrity: sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==} + postcss-selector-parser@7.1.6: + resolution: {integrity: sha512-7qASPzhKF2l2KLboRZux8CCTRMdGiV08vWmyKzPz22qZ7ZjQBOeY7rNzNoCLSUiftJ7HUq0GERHmxw/t0dCdMw==} engines: {node: '>=4'} postcss-sort-media-queries@5.2.0: @@ -7092,15 +7101,15 @@ packages: prosemirror-transform@1.12.0: resolution: {integrity: sha512-GxboyN4AMIsoHNtz5uf2r2Ru551i5hWeCMD6E2Ib4Eogqoub0NflniaBPVQ4MrGE5yZ8JV9tUHg9qcZTTrcN4w==} - prosemirror-view@1.42.1: - resolution: {integrity: sha512-rRqzZnRgkyh69XoOMrfFJHwauHscLBmHbq772kwbic1ymQAM8gXjzEbJse5j1ep2UO2HRIAQL0bY3kZ/RoqjVw==} + prosemirror-view@1.42.3: + resolution: {integrity: sha512-oTN7EtH+CpwxU9NrwEYWd0UZ4JUx7l048l5A2Xppm4p/60isZYLnth9QVQmC3VRIvdrIWCxwZSd+Uz791G31/w==} prosemirror-virtual-cursor@0.4.2: resolution: {integrity: sha512-pUMKnIuOhhnMcgIJUjhIQTVJruBEGxfMBVQSrK0g2qhGPDm1i12KdsVaFw15dYk+29tZcxjMeR7P5VDKwmbwJg==} peerDependencies: prosemirror-model: ^1.0.0 prosemirror-state: ^1.0.0 - prosemirror-view: ^1.0.0 + prosemirror-view: 1.42.3 peerDependenciesMeta: prosemirror-model: optional: true @@ -7116,8 +7125,8 @@ packages: resolution: {integrity: sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==} engines: {node: '>=12.0.0'} - proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + proxy-addr@2.0.8: + resolution: {integrity: sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==} engines: {node: '>= 0.10'} pump@3.0.4: @@ -7630,8 +7639,8 @@ packages: resolution: {integrity: sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA==} engines: {node: '>= 6.3.0'} - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + source-map-js@1.2.2: + resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==} engines: {node: '>=0.10.0'} source-map-support@0.5.21: @@ -7662,9 +7671,6 @@ packages: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - srcset@4.0.0: resolution: {integrity: sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==} engines: {node: '>=12'} @@ -7909,9 +7915,9 @@ packages: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} - tinypool@1.1.1: - resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} - engines: {node: ^18.0.0 || >=20.0.0} + tinypool@2.1.2: + resolution: {integrity: sha512-9YodfrxS9g9IbFr/KOjE5bAeJ0p61n3bW6mqvy0jtoeKd1kTW1Cxm0oulm6KX2lyM9Gl6WIe8nEbY7LWv5ZJww==} + engines: {node: ^20.0.0 || >=22.0.0} tinyrainbow@3.1.0: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} @@ -8275,8 +8281,8 @@ packages: jsdom: optional: true - vue@3.5.40: - resolution: {integrity: sha512-+8PJ4SJXdn/cHGImF4CKdxlWHIN5Dkt7DoufRREM6h6uVCx2m7QxgcEQmmzyOK8A9mcafg7sFbJFYsdFVubTig==} + vue@3.5.42: + resolution: {integrity: sha512-4RyHQTbQvOPs3MfvUO1Sg0YRrKNnA0mAVtvpd12Tg1fKDN7OHBUl1IqSn8zGJjK9nI3NkNp8cgTpVrSZC5TTcA==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -8846,6 +8852,10 @@ snapshots: dependencies: '@babel/types': 7.29.7 + '@babel/parser@7.29.9': + dependencies: + '@babel/types': 7.29.8 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: '@babel/core': 7.29.7(supports-color@10.2.2) @@ -9446,6 +9456,11 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@balena/dockerignore@1.0.2': {} '@biomejs/biome@2.5.2': @@ -9814,9 +9829,9 @@ snapshots: '@csstools/postcss-cascade-layers@5.0.2(postcss@8.5.23)': dependencies: - '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.4) + '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.6) postcss: 8.5.23 - postcss-selector-parser: 7.1.4 + postcss-selector-parser: 7.1.6 '@csstools/postcss-color-function-display-p3-linear@1.0.1(postcss@8.5.23)': dependencies: @@ -9922,9 +9937,9 @@ snapshots: '@csstools/postcss-is-pseudo-class@5.0.3(postcss@8.5.23)': dependencies: - '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.4) + '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.6) postcss: 8.5.23 - postcss-selector-parser: 7.1.4 + postcss-selector-parser: 7.1.6 '@csstools/postcss-light-dark-function@2.0.11(postcss@8.5.23)': dependencies: @@ -10026,7 +10041,7 @@ snapshots: '@csstools/postcss-scope-pseudo-class@4.0.1(postcss@8.5.23)': dependencies: postcss: 8.5.23 - postcss-selector-parser: 7.1.4 + postcss-selector-parser: 7.1.6 '@csstools/postcss-sign-functions@1.1.4(postcss@8.5.23)': dependencies: @@ -10070,13 +10085,13 @@ snapshots: dependencies: postcss: 8.5.23 - '@csstools/selector-resolve-nested@3.1.0(postcss-selector-parser@7.1.4)': + '@csstools/selector-resolve-nested@3.1.0(postcss-selector-parser@7.1.6)': dependencies: - postcss-selector-parser: 7.1.4 + postcss-selector-parser: 7.1.6 - '@csstools/selector-specificity@5.0.0(postcss-selector-parser@7.1.4)': + '@csstools/selector-specificity@5.0.0(postcss-selector-parser@7.1.6)': dependencies: - postcss-selector-parser: 7.1.4 + postcss-selector-parser: 7.1.6 '@csstools/utilities@2.0.0(postcss@8.5.23)': dependencies: @@ -10223,7 +10238,7 @@ snapshots: react-router-dom: 5.3.4(react@18.3.1) semver: 7.8.5 serve-handler: 6.1.7 - tinypool: 1.1.1 + tinypool: 2.1.2 tslib: 2.8.1 update-notifier: 6.0.2 webpack: 5.108.3(clean-css@5.3.3)(cssnano@6.1.2(postcss@8.5.23))(csso@5.0.5)(esbuild@0.28.1)(html-minifier-terser@7.2.0)(lightningcss@1.32.0)(postcss@8.5.23) @@ -10901,7 +10916,7 @@ snapshots: '@types/mdast': 4.0.4 '@types/react': 19.2.17 commander: 5.1.0 - joi: 17.13.7 + joi: 17.13.8 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-helmet-async: '@slorber/react-helmet-async@1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)' @@ -10952,7 +10967,7 @@ snapshots: '@docusaurus/utils': 3.10.1(clean-css@5.3.3)(cssnano@6.1.2(postcss@8.5.23))(csso@5.0.5)(esbuild@0.28.1)(html-minifier-terser@7.2.0)(lightningcss@1.32.0)(postcss@8.5.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@10.2.2) '@docusaurus/utils-common': 3.10.1(clean-css@5.3.3)(cssnano@6.1.2(postcss@8.5.23))(csso@5.0.5)(esbuild@0.28.1)(html-minifier-terser@7.2.0)(lightningcss@1.32.0)(postcss@8.5.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@10.2.2) fs-extra: 11.3.6 - joi: 17.13.7 + joi: 17.13.8 js-yaml: 4.3.2 lodash: 4.18.1 tslib: 2.8.1 @@ -11705,7 +11720,7 @@ snapshots: lodash-es: 4.18.1 nanoid: 5.1.16 unist-util-visit: 5.1.0 - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.42(typescript@5.9.3) transitivePeerDependencies: - supports-color - typescript @@ -11722,7 +11737,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@milkdown/crepe@7.21.3(prosemirror-model@1.25.11)(prosemirror-state@1.4.4)(prosemirror-view@1.42.1)(supports-color@10.2.2)(typescript@5.9.3)': + '@milkdown/crepe@7.21.3(prosemirror-model@1.25.11)(prosemirror-state@1.4.4)(prosemirror-view@1.42.3)(supports-color@10.2.2)(typescript@5.9.3)': dependencies: '@codemirror/commands': 6.10.4 '@codemirror/language': 6.12.4 @@ -11735,12 +11750,12 @@ snapshots: clsx: 2.1.1 codemirror: 6.0.2 dompurify: 3.4.16 - katex: 0.17.0 + katex: 0.18.2 lodash-es: 4.18.1 - prosemirror-virtual-cursor: 0.4.2(prosemirror-model@1.25.11)(prosemirror-state@1.4.4)(prosemirror-view@1.42.1) + prosemirror-virtual-cursor: 0.4.2(prosemirror-model@1.25.11)(prosemirror-state@1.4.4)(prosemirror-view@1.42.3) remark-math: 6.0.0(supports-color@10.2.2) unist-util-visit: 5.1.0 - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.42(typescript@5.9.3) transitivePeerDependencies: - prosemirror-model - prosemirror-state @@ -11944,11 +11959,11 @@ snapshots: prosemirror-state: 1.4.4 prosemirror-tables: 1.8.5 prosemirror-transform: 1.12.0 - prosemirror-view: 1.42.1 + prosemirror-view: 1.42.3 - '@milkdown/react@7.21.3(@codemirror/language@6.12.4)(@codemirror/state@6.7.1)(@codemirror/view@6.43.6)(prosemirror-model@1.25.11)(prosemirror-state@1.4.4)(prosemirror-view@1.42.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(supports-color@10.2.2)(typescript@5.9.3)': + '@milkdown/react@7.21.3(@codemirror/language@6.12.4)(@codemirror/state@6.7.1)(@codemirror/view@6.43.6)(prosemirror-model@1.25.11)(prosemirror-state@1.4.4)(prosemirror-view@1.42.3)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(supports-color@10.2.2)(typescript@5.9.3)': dependencies: - '@milkdown/crepe': 7.21.3(prosemirror-model@1.25.11)(prosemirror-state@1.4.4)(prosemirror-view@1.42.1)(supports-color@10.2.2)(typescript@5.9.3) + '@milkdown/crepe': 7.21.3(prosemirror-model@1.25.11)(prosemirror-state@1.4.4)(prosemirror-view@1.42.3)(supports-color@10.2.2)(typescript@5.9.3) '@milkdown/kit': 7.21.3(@codemirror/language@6.12.4)(@codemirror/state@6.7.1)(@codemirror/view@6.43.6)(supports-color@10.2.2)(typescript@5.9.3) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -12618,7 +12633,7 @@ snapshots: jiti: 2.7.0 lightningcss: 1.32.0 magic-string: 0.30.21 - source-map-js: 1.2.1 + source-map-js: 1.2.2 tailwindcss: 4.3.2 '@tailwindcss/oxide-android-arm64@4.3.2': @@ -12967,59 +12982,59 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.0 - '@vue/compiler-core@3.5.40': + '@vue/compiler-core@3.5.42': dependencies: - '@babel/parser': 7.29.7 - '@vue/shared': 3.5.40 + '@babel/parser': 7.29.9 + '@vue/shared': 3.5.42 entities: 7.0.1 estree-walker: 2.0.2 - source-map-js: 1.2.1 + source-map-js: 1.2.2 - '@vue/compiler-dom@3.5.40': + '@vue/compiler-dom@3.5.42': dependencies: - '@vue/compiler-core': 3.5.40 - '@vue/shared': 3.5.40 + '@vue/compiler-core': 3.5.42 + '@vue/shared': 3.5.42 - '@vue/compiler-sfc@3.5.40': + '@vue/compiler-sfc@3.5.42': dependencies: - '@babel/parser': 7.29.7 - '@vue/compiler-core': 3.5.40 - '@vue/compiler-dom': 3.5.40 - '@vue/compiler-ssr': 3.5.40 - '@vue/shared': 3.5.40 + '@babel/parser': 7.29.9 + '@vue/compiler-core': 3.5.42 + '@vue/compiler-dom': 3.5.42 + '@vue/compiler-ssr': 3.5.42 + '@vue/shared': 3.5.42 estree-walker: 2.0.2 magic-string: 0.30.21 postcss: 8.5.23 - source-map-js: 1.2.1 + source-map-js: 1.2.2 - '@vue/compiler-ssr@3.5.40': + '@vue/compiler-ssr@3.5.42': dependencies: - '@vue/compiler-dom': 3.5.40 - '@vue/shared': 3.5.40 + '@vue/compiler-dom': 3.5.42 + '@vue/shared': 3.5.42 - '@vue/reactivity@3.5.40': + '@vue/reactivity@3.5.42': dependencies: - '@vue/shared': 3.5.40 + '@vue/shared': 3.5.42 - '@vue/runtime-core@3.5.40': + '@vue/runtime-core@3.5.42': dependencies: - '@vue/reactivity': 3.5.40 - '@vue/shared': 3.5.40 + '@vue/reactivity': 3.5.42 + '@vue/shared': 3.5.42 - '@vue/runtime-dom@3.5.40': + '@vue/runtime-dom@3.5.42': dependencies: - '@vue/reactivity': 3.5.40 - '@vue/runtime-core': 3.5.40 - '@vue/shared': 3.5.40 + '@vue/reactivity': 3.5.42 + '@vue/runtime-core': 3.5.42 + '@vue/shared': 3.5.42 csstype: 3.2.3 - '@vue/server-renderer@3.5.40': + '@vue/server-renderer@3.5.42': dependencies: - '@vue/compiler-ssr': 3.5.40 - '@vue/runtime-dom': 3.5.40 - '@vue/shared': 3.5.40 + '@vue/compiler-ssr': 3.5.42 + '@vue/runtime-dom': 3.5.42 + '@vue/shared': 3.5.42 - '@vue/shared@3.5.40': {} + '@vue/shared@3.5.42': {} '@webassemblyjs/ast@1.14.1': dependencies: @@ -13227,10 +13242,6 @@ snapshots: node-addon-api: 8.9.0 node-gyp-build: 4.8.4 - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - argparse@2.0.1: {} array-flatten@1.1.1: {} @@ -13468,7 +13479,7 @@ snapshots: dependencies: '@types/http-cache-semantics': 4.2.0 get-stream: 6.0.1 - http-cache-semantics: 4.2.0 + http-cache-semantics: 4.3.0 keyv: 4.5.4 mimic-response: 4.0.0 normalize-url: 8.1.1 @@ -13651,11 +13662,12 @@ snapshots: dependencies: mime-db: 1.54.0 - compression@1.8.1(supports-color@10.2.2): + compression@1.8.2(supports-color@10.2.2): dependencies: bytes: 3.1.2 compressible: 2.0.18 debug: 2.6.9(supports-color@10.2.2) + destroy: 1.2.0 negotiator: 0.6.4 on-headers: 1.1.0 safe-buffer: 5.2.1 @@ -13779,7 +13791,7 @@ snapshots: css-blank-pseudo@7.0.1(postcss@8.5.23): dependencies: postcss: 8.5.23 - postcss-selector-parser: 7.1.4 + postcss-selector-parser: 7.1.6 css-declaration-sorter@7.4.0(postcss@8.5.23): dependencies: @@ -13787,9 +13799,9 @@ snapshots: css-has-pseudo@7.0.3(postcss@8.5.23): dependencies: - '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.4) + '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.6) postcss: 8.5.23 - postcss-selector-parser: 7.1.4 + postcss-selector-parser: 7.1.6 postcss-value-parser: 4.2.0 css-loader@6.11.0(webpack@5.108.3(clean-css@5.3.3)(cssnano@6.1.2(postcss@8.5.23))(csso@5.0.5)(esbuild@0.28.1)(html-minifier-terser@7.2.0)(lightningcss@1.32.0)(postcss@8.5.23)): @@ -13843,17 +13855,17 @@ snapshots: css-tree@2.2.1: dependencies: mdn-data: 2.0.28 - source-map-js: 1.2.1 + source-map-js: 1.2.2 css-tree@2.3.1: dependencies: mdn-data: 2.0.30 - source-map-js: 1.2.1 + source-map-js: 1.2.2 css-tree@3.2.1: dependencies: mdn-data: 2.27.1 - source-map-js: 1.2.1 + source-map-js: 1.2.2 css-what@6.2.2: {} @@ -14356,7 +14368,7 @@ snapshots: on-finished: 2.4.1 parseurl: 1.3.3 path-to-regexp: 0.1.13 - proxy-addr: 2.0.7 + proxy-addr: 2.0.8 qs: 6.16.0 range-parser: 1.2.1 safe-buffer: 5.2.1 @@ -14391,7 +14403,7 @@ snapshots: on-finished: 2.4.1 once: 1.4.0 parseurl: 1.3.3 - proxy-addr: 2.0.7 + proxy-addr: 2.0.8 qs: 6.16.0 range-parser: 1.3.0 router: 2.2.0(supports-color@10.2.2) @@ -14680,7 +14692,7 @@ snapshots: gray-matter@4.0.3: dependencies: - js-yaml: 3.15.2 + js-yaml: 3.15.2(patch_hash=1610aa3fa9f60a71602f33f5bf14f5b03af00fd3182fa6dc79e0e946a66ba001) kind-of: 6.0.3 section-matter: 1.0.0 strip-bom-string: 1.0.0 @@ -14884,7 +14896,7 @@ snapshots: deep-equal: 1.0.1 http-errors: 1.8.1 - http-cache-semantics@4.2.0: {} + http-cache-semantics@4.3.0: {} http-deceiver@1.2.7: {} @@ -15124,7 +15136,7 @@ snapshots: jiti@2.7.0: {} - joi@17.13.7: + joi@17.13.8: dependencies: '@hapi/hoek': 9.3.0 '@hapi/topo': 5.1.0 @@ -15140,9 +15152,9 @@ snapshots: js-tokens@4.0.0: {} - js-yaml@3.15.2: + js-yaml@3.15.2(patch_hash=1610aa3fa9f60a71602f33f5bf14f5b03af00fd3182fa6dc79e0e946a66ba001): dependencies: - argparse: 1.0.10 + argparse: 2.0.1 esprima: 4.0.1 js-yaml@4.3.2: @@ -15240,11 +15252,7 @@ snapshots: jsonlines@0.1.1: {} - katex@0.16.47: - dependencies: - commander: 8.3.0 - - katex@0.17.0: + katex@0.18.2: dependencies: commander: 8.3.0 @@ -15760,7 +15768,7 @@ snapshots: dependencies: '@types/katex': 0.16.8 devlop: 1.1.0 - katex: 0.16.47 + katex: 0.18.2 micromark-factory-space: 2.0.1 micromark-util-character: 2.1.1 micromark-util-symbol: 2.0.1 @@ -16449,12 +16457,12 @@ snapshots: postcss-attribute-case-insensitive@7.0.1(postcss@8.5.23): dependencies: postcss: 8.5.23 - postcss-selector-parser: 7.1.4 + postcss-selector-parser: 7.1.6 postcss-calc@9.0.1(postcss@8.5.23): dependencies: postcss: 8.5.23 - postcss-selector-parser: 6.1.4 + postcss-selector-parser: 7.1.6 postcss-value-parser: 4.2.0 postcss-clamp@4.1.0(postcss@8.5.23): @@ -16520,12 +16528,12 @@ snapshots: '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) '@csstools/css-tokenizer': 3.0.4 postcss: 8.5.23 - postcss-selector-parser: 7.1.4 + postcss-selector-parser: 7.1.6 postcss-dir-pseudo-class@9.0.1(postcss@8.5.23): dependencies: postcss: 8.5.23 - postcss-selector-parser: 7.1.4 + postcss-selector-parser: 7.1.6 postcss-discard-comments@6.0.2(postcss@8.5.23): dependencies: @@ -16546,7 +16554,7 @@ snapshots: postcss-discard-unused@6.0.5(postcss@8.5.23): dependencies: postcss: 8.5.23 - postcss-selector-parser: 6.1.4 + postcss-selector-parser: 7.1.6 postcss-double-position-gradients@6.0.4(postcss@8.5.23): dependencies: @@ -16558,12 +16566,12 @@ snapshots: postcss-focus-visible@10.0.1(postcss@8.5.23): dependencies: postcss: 8.5.23 - postcss-selector-parser: 7.1.4 + postcss-selector-parser: 7.1.6 postcss-focus-within@9.0.1(postcss@8.5.23): dependencies: postcss: 8.5.23 - postcss-selector-parser: 7.1.4 + postcss-selector-parser: 7.1.6 postcss-font-variant@5.0.0(postcss@8.5.23): dependencies: @@ -16630,7 +16638,7 @@ snapshots: caniuse-api: 3.0.0 cssnano-utils: 4.0.2(postcss@8.5.23) postcss: 8.5.23 - postcss-selector-parser: 6.1.4 + postcss-selector-parser: 7.1.6 postcss-minify-font-values@6.1.0(postcss@8.5.23): dependencies: @@ -16654,7 +16662,7 @@ snapshots: postcss-minify-selectors@6.0.4(postcss@8.5.23): dependencies: postcss: 8.5.23 - postcss-selector-parser: 6.1.4 + postcss-selector-parser: 7.1.6 postcss-modules-extract-imports@3.1.0(postcss@8.5.23): dependencies: @@ -16664,13 +16672,13 @@ snapshots: dependencies: icss-utils: 5.1.0(postcss@8.5.23) postcss: 8.5.23 - postcss-selector-parser: 7.1.4 + postcss-selector-parser: 7.1.6 postcss-value-parser: 4.2.0 postcss-modules-scope@3.2.1(postcss@8.5.23): dependencies: postcss: 8.5.23 - postcss-selector-parser: 7.1.4 + postcss-selector-parser: 7.1.6 postcss-modules-values@4.0.0(postcss@8.5.23): dependencies: @@ -16679,10 +16687,10 @@ snapshots: postcss-nesting@13.0.2(postcss@8.5.23): dependencies: - '@csstools/selector-resolve-nested': 3.1.0(postcss-selector-parser@7.1.4) - '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.4) + '@csstools/selector-resolve-nested': 3.1.0(postcss-selector-parser@7.1.6) + '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.6) postcss: 8.5.23 - postcss-selector-parser: 7.1.4 + postcss-selector-parser: 7.1.6 postcss-normalize-charset@6.0.2(postcss@8.5.23): dependencies: @@ -16831,7 +16839,7 @@ snapshots: postcss-pseudo-class-any-link@10.0.1(postcss@8.5.23): dependencies: postcss: 8.5.23 - postcss-selector-parser: 7.1.4 + postcss-selector-parser: 7.1.6 postcss-reduce-idents@6.0.3(postcss@8.5.23): dependencies: @@ -16856,14 +16864,9 @@ snapshots: postcss-selector-not@8.0.1(postcss@8.5.23): dependencies: postcss: 8.5.23 - postcss-selector-parser: 7.1.4 - - postcss-selector-parser@6.1.4: - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 + postcss-selector-parser: 7.1.6 - postcss-selector-parser@7.1.4: + postcss-selector-parser@7.1.6: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 @@ -16882,7 +16885,7 @@ snapshots: postcss-unique-selectors@6.0.4(postcss@8.5.23): dependencies: postcss: 8.5.23 - postcss-selector-parser: 6.1.4 + postcss-selector-parser: 7.1.6 postcss-value-parser@4.2.0: {} @@ -16894,7 +16897,7 @@ snapshots: dependencies: nanoid: 3.3.18 picocolors: 1.1.1 - source-map-js: 1.2.1 + source-map-js: 1.2.2 postgres-array@2.0.0: {} @@ -16959,26 +16962,26 @@ snapshots: '@ocavue/utils': 1.7.0 prosemirror-model: 1.25.11 prosemirror-state: 1.4.4 - prosemirror-view: 1.42.1 + prosemirror-view: 1.42.3 prosemirror-dropcursor@1.8.3: dependencies: prosemirror-state: 1.4.4 prosemirror-transform: 1.12.0 - prosemirror-view: 1.42.1 + prosemirror-view: 1.42.3 prosemirror-gapcursor@1.4.1: dependencies: prosemirror-keymap: 1.2.3 prosemirror-model: 1.25.11 prosemirror-state: 1.4.4 - prosemirror-view: 1.42.1 + prosemirror-view: 1.42.3 prosemirror-history@1.5.0: dependencies: prosemirror-state: 1.4.4 prosemirror-transform: 1.12.0 - prosemirror-view: 1.42.1 + prosemirror-view: 1.42.3 rope-sequence: 1.3.4 prosemirror-inputrules@1.5.1: @@ -16998,7 +17001,7 @@ snapshots: prosemirror-safari-ime-span@1.0.2: dependencies: prosemirror-state: 1.4.4 - prosemirror-view: 1.42.1 + prosemirror-view: 1.42.3 prosemirror-schema-list@1.5.1: dependencies: @@ -17010,7 +17013,7 @@ snapshots: dependencies: prosemirror-model: 1.25.11 prosemirror-transform: 1.12.0 - prosemirror-view: 1.42.1 + prosemirror-view: 1.42.3 prosemirror-tables@1.8.5: dependencies: @@ -17018,23 +17021,23 @@ snapshots: prosemirror-model: 1.25.11 prosemirror-state: 1.4.4 prosemirror-transform: 1.12.0 - prosemirror-view: 1.42.1 + prosemirror-view: 1.42.3 prosemirror-transform@1.12.0: dependencies: prosemirror-model: 1.25.11 - prosemirror-view@1.42.1: + prosemirror-view@1.42.3: dependencies: prosemirror-model: 1.25.11 prosemirror-state: 1.4.4 prosemirror-transform: 1.12.0 - prosemirror-virtual-cursor@0.4.2(prosemirror-model@1.25.11)(prosemirror-state@1.4.4)(prosemirror-view@1.42.1): + prosemirror-virtual-cursor@0.4.2(prosemirror-model@1.25.11)(prosemirror-state@1.4.4)(prosemirror-view@1.42.3): optionalDependencies: prosemirror-model: 1.25.11 prosemirror-state: 1.4.4 - prosemirror-view: 1.42.1 + prosemirror-view: 1.42.3 proto-list@1.2.4: {} @@ -17052,7 +17055,7 @@ snapshots: '@types/node': 24.13.2 long: 5.3.2 - proxy-addr@2.0.7: + proxy-addr@2.0.8: dependencies: forwarded: 0.2.0 ipaddr.js: 1.9.1 @@ -17769,7 +17772,7 @@ snapshots: sort-css-media-queries@2.2.0: {} - source-map-js@1.2.1: {} + source-map-js@1.2.2: {} source-map-support@0.5.21: dependencies: @@ -17807,8 +17810,6 @@ snapshots: split2@4.2.0: {} - sprintf-js@1.0.3: {} - srcset@4.0.0: {} ssh2@1.17.0: @@ -17908,7 +17909,7 @@ snapshots: dependencies: browserslist: 4.28.7 postcss: 8.5.23 - postcss-selector-parser: 6.1.4 + postcss-selector-parser: 7.1.6 sucrase@3.35.1: dependencies: @@ -18036,7 +18037,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - tinypool@1.1.1: {} + tinypool@2.1.2: {} tinyrainbow@3.1.0: {} @@ -18377,13 +18378,13 @@ snapshots: transitivePeerDependencies: - msw - vue@3.5.40(typescript@5.9.3): + vue@3.5.42(typescript@5.9.3): dependencies: - '@vue/compiler-dom': 3.5.40 - '@vue/compiler-sfc': 3.5.40 - '@vue/runtime-dom': 3.5.40 - '@vue/server-renderer': 3.5.40 - '@vue/shared': 3.5.40 + '@vue/compiler-dom': 3.5.42 + '@vue/compiler-sfc': 3.5.42 + '@vue/runtime-dom': 3.5.42 + '@vue/server-renderer': 3.5.42 + '@vue/shared': 3.5.42 optionalDependencies: typescript: 5.9.3 @@ -18450,7 +18451,7 @@ snapshots: bonjour-service: 1.4.2 chokidar: 3.6.0 colorette: 2.0.20 - compression: 1.8.1(supports-color@10.2.2) + compression: 1.8.2(supports-color@10.2.2) connect-history-api-fallback: 2.0.0 express: 4.22.2(supports-color@10.2.2) graceful-fs: 4.2.11 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index b2c1d4d6..08656027 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,6 +6,22 @@ packages: # Workspace-wide security floors for transitive dependencies. overrides: + "proxy-addr@>=1.1.0 <2.0.8": 2.0.8 + # Tinypool 2 drops Node 18; Facility already requires Node 22.13+/24. + # Exercise both worker pollution vectors and the real docs build in tests. + "tinypool@<2.1.2": 2.1.2 + "source-map-js@>=1.0.0 <1.2.2": 1.2.2 + "compression@<1.8.2": 1.8.2 + "postcss-selector-parser@<7.1.6": 7.1.6 + "prosemirror-view@<1.42.3": 1.42.3 + # Keep Vue's renderer/compiler/runtime on its coordinated patch release. + "vue@>=3.0.0 <3.5.42": 3.5.42 + "katex@>=0.11.0 <0.18.2": 0.18.2 + # Argparse 2 retains the legacy CLI API and removes sprintf-js entirely. + # Scope the major override to js-yaml 3; exercise its CLI in regression tests. + # The CLI-only patch below preserves the legacy --version output. + "js-yaml@3>argparse": 2.0.1 + "http-cache-semantics@<4.3.0": 4.3.0 "browserslist@<4.28.7": 4.28.7 "esbuild@>=0.27.3 <0.28.1": 0.28.1 "postcss@<8.5.23": 8.5.23 @@ -36,7 +52,7 @@ overrides: "dompurify@<3.4.16": 3.4.16 "nanoid@>=3.0.0 <3.3.18": 3.3.18 "colord@<2.9.4": 2.9.4 - "joi@>=16.0.0 <17.13.7": 17.13.7 + "joi@>=16.0.0 <17.13.8": 17.13.8 "baseline-browser-mapping@>=2.0.0 <2.11.0": 2.11.0 # Postinstall build-script allowlist — nothing builds unless named here. @@ -62,12 +78,17 @@ auditConfig: - GHSA-mh99-v99m-4gvg - GHSA-w3rx-r6r6-pgpr - GHSA-5p2g-fcmc-qvqq + # Temporary accepted risk: docs tooling uses braces 3.0.3; no official fix. + # Owner: @alvaroloes; human review by 2026-10-13 (not an automatic expiry). + # Remove after an official fix/advisory resolution: https://github.com/theam/facility/issues/450 + - GHSA-vfj7-8cjw-p6xm minimumReleaseAgeExclude: - '@vercel/sandbox@3.0.0' patchedDependencies: image-size@2.0.2: patches/image-size@2.0.2.patch + js-yaml@3.15.2: patches/js-yaml@3.15.2.patch # Filtered production-image installs intentionally omit the docs-only patched # dependency. Keep full repository installs strict and open this exception only diff --git a/runner/Dockerfile b/runner/Dockerfile index 488403db..2096b31e 100644 --- a/runner/Dockerfile +++ b/runner/Dockerfile @@ -219,8 +219,8 @@ FROM node:24-trixie-slim@sha256:50c3b2f6988dfc307b86e5301d69611af31f4789bdf23286 # Bump this audited epoch only when a scanner identifies a distro fix that the # official image has not incorporated yet; using it in the command makes that # refresh explicit and reproducible instead of disabling the whole build cache. -ARG DEBIAN_SECURITY_REFRESH=20260924 -ARG CHROMIUM_MIN_VERSION=153.0.8010.52-1~deb13u1 +ARG DEBIAN_SECURITY_REFRESH=20261005 +ARG CHROMIUM_MIN_VERSION=154.0.8037.57-1~deb13u1 RUN test -n "$DEBIAN_SECURITY_REFRESH" \ && apt-get update \ && DEBIAN_FRONTEND=noninteractive apt-get upgrade -y \ @@ -285,10 +285,10 @@ RUN test -n "$DEBIAN_SECURITY_REFRESH" \ ARG NPM_VERSION=12.0.2 RUN npm install --global "npm@${NPM_VERSION}" \ && npm install --global --omit=dev --ignore-scripts \ - brace-expansion@5.0.9 \ - ip-address@10.3.1 \ + brace-expansion@5.0.12 \ + ip-address@10.7.1 \ tar@7.5.21 \ - undici@6.28.0 \ + undici@6.28.1 \ && rm -rf \ /usr/local/lib/node_modules/npm/node_modules/brace-expansion \ /usr/local/lib/node_modules/npm/node_modules/ip-address \ @@ -334,10 +334,12 @@ RUN corepack enable # Seed the repository's pinned package-manager release into an immutable layer. COPY package.json /tmp/facility-root-package.json +COPY runner/verify-package-security.mjs /tmp/verify-package-security.mjs RUN package_manager="$(node -p "require('/tmp/facility-root-package.json').packageManager")" \ && COREPACK_HOME=/opt/facility-corepack corepack install --global --cache-only "$package_manager" \ + && node /tmp/verify-package-security.mjs /usr/local/lib/node_modules/npm /opt/facility-corepack \ && chmod -R a-w /opt/facility-corepack \ - && rm -f /tmp/facility-root-package.json + && rm -f /tmp/facility-root-package.json /tmp/verify-package-security.mjs COPY --chown=root:root --chmod=0555 runner/facility-git-credential.mjs /usr/local/bin/facility-git-credential COPY --chown=root:root --chmod=0555 runner/facility-preview-gateway.mjs /usr/local/bin/facility-preview-gateway diff --git a/runner/verify-package-security.mjs b/runner/verify-package-security.mjs new file mode 100644 index 00000000..89891483 --- /dev/null +++ b/runner/verify-package-security.mjs @@ -0,0 +1,51 @@ +import assert from "node:assert/strict"; +import { readdirSync, readFileSync } from "node:fs"; +import { createRequire } from "node:module"; +import { join, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +export function assertPatchedVersion(version, minimum, label) { + assert.match(version, /^\d+\.\d+\.\d+$/, `${label}: invalid version`); + const actual = version.split(".").map(Number); + const floor = minimum.split(".").map(Number); + assert.equal(actual[0], floor[0], `${label}: review a new major before accepting it`); + assert.ok( + actual[1] > floor[1] || (actual[1] === floor[1] && actual[2] >= floor[2]), + `${label}: ${version} is below security floor ${minimum}`, + ); +} + +export function verifyRunnerPackages(npmRoot, corepackRoot) { + const npmRequire = createRequire(join(resolve(npmRoot), "package.json")); + for (const [name, minimum] of [ + ["brace-expansion", "5.0.12"], + ["ip-address", "10.7.1"], + ["tar", "7.5.21"], + ["undici", "6.28.1"], + ]) { + // Resolve as npm does: an old nested copy must not shadow the global overlay. + const manifest = JSON.parse(readFileSync(npmRequire.resolve(`${name}/package.json`), "utf8")); + assertPatchedVersion(manifest.version, minimum, `npm ${name}`); + } + + let copies = 0; + function visit(directory) { + for (const entry of readdirSync(directory, { withFileTypes: true })) { + if (!entry.isDirectory()) continue; + const path = join(directory, entry.name); + if (entry.name === "undici") { + const manifest = JSON.parse(readFileSync(join(path, "package.json"), "utf8")); + assertPatchedVersion(manifest.version, "6.28.1", path); + copies += 1; + } + visit(path); + } + } + visit(corepackRoot); + assert.ok(copies > 0, "No cached pnpm undici found; review the package layout"); +} + +if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + verifyRunnerPackages(process.argv[2], process.argv[3]); + console.log("Runner npm and cached pnpm security floors verified"); +} diff --git a/scripts/critical-dependencies-security.test.mjs b/scripts/critical-dependencies-security.test.mjs new file mode 100644 index 00000000..e1f6ff3b --- /dev/null +++ b/scripts/critical-dependencies-security.test.mjs @@ -0,0 +1,89 @@ +import assert from "node:assert/strict"; +import { spawnSync } from "node:child_process"; +import { once } from "node:events"; +import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { createServer } from "node:http"; +import { createRequire } from "node:module"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import test from "node:test"; +import { pathToFileURL } from "node:url"; + +const docsRequire = createRequire(new URL("../apps/docs/package.json", import.meta.url)); +const coreRequire = createRequire(docsRequire.resolve("@docusaurus/core/package.json")); +const serverRequire = createRequire(coreRequire.resolve("webpack-dev-server")); +const expressRequire = createRequire(serverRequire.resolve("express")); +const proxyaddr = expressRequire("proxy-addr"); +const poolURL = pathToFileURL(coreRequire.resolve("tinypool")).href; + +test("IPv4 addresses cannot match malformed or unmapped IPv6 trust subnets", () => { + for (const subnet of ["::ffff:10.0.0.0/8", "::/1"]) { + const trust = proxyaddr.compile(subnet); + assert.equal(trust("203.0.113.10"), false); + assert.equal(trust("127.0.0.1"), false); + } + for (const subnet of ["10.0.0.0/8", "::ffff:10.0.0.0/104"]) { + const trust = proxyaddr.compile(subnet); + assert.equal(trust("10.1.2.3"), true); + assert.equal(trust("203.0.113.10"), false); + } +}); + +test("real HTTP requests cannot spoof their IP through a malformed trust subnet", async (t) => { + const trust = proxyaddr.compile("::ffff:10.0.0.0/8"); + const server = createServer((request, response) => response.end(proxyaddr(request, trust))); + t.after(() => new Promise((resolve, reject) => { + server.closeAllConnections(); + server.close((error) => error ? reject(error) : resolve()); + })); + server.listen(0, "127.0.0.1"); + await once(server, "listening"); + const response = await fetch(`http://127.0.0.1:${server.address().port}`, { + headers: { "x-forwarded-for": "198.51.100.20" }, + signal: AbortSignal.timeout(2000), + }); + assert.equal(await response.text(), "127.0.0.1"); +}); + +for (const vector of ["none", "execArgv", "env", "filename"]) { + test(`real worker pool ignores inherited ${vector} options`, () => { + const directory = mkdtempSync(join(tmpdir(), "facility-worker-security-")); + try { + const worker = join(directory, "worker.mjs"); + const payload = join(directory, "payload.cjs"); + const replacement = join(directory, "replacement.mjs"); + writeFileSync(worker, "export default n => ({ value: n * 2, poisoned: process.env.FACILITY_TEST_POISONED === 'yes' });"); + writeFileSync(payload, "process.env.FACILITY_TEST_POISONED = 'yes';"); + writeFileSync(replacement, "export default () => ({ value: -1, poisoned: true });"); + // Prototype mutation is confined to a disposable child process. Fixtures + // only set a synthetic marker, never access credentials or external hosts. + const code = ` + import assert from 'node:assert/strict'; + import Tinypool from ${JSON.stringify(poolURL)}; + const vector = ${JSON.stringify(vector)}; + if (vector === 'execArgv') Object.prototype.execArgv = ['--require', ${JSON.stringify(payload)}]; + if (vector === 'env') Object.prototype.env = { NODE_OPTIONS: '--require ' + ${JSON.stringify(payload)} }; + const pool = new Tinypool({ filename: ${JSON.stringify(worker)}, minThreads: 1, maxThreads: 1 }); + try { + if (vector === 'filename') Object.prototype.filename = ${JSON.stringify(replacement)}; + const result = await pool.run(21, { signal: new AbortController().signal }); + assert.deepEqual(result, { value: 42, poisoned: false }); + } finally { + delete Object.prototype.execArgv; + delete Object.prototype.env; + delete Object.prototype.filename; + await pool.destroy(); + } + `; + const probe = join(directory, "probe.mjs"); + writeFileSync(probe, code); + const result = spawnSync(process.execPath, [probe], { + encoding: "utf8", timeout: 10000, + }); + assert.equal(result.error, undefined, String(result.error)); + assert.equal(result.status, 0, result.stderr); + } finally { + rmSync(directory, { recursive: true, force: true }); + } + }); +} diff --git a/scripts/http-cache-security.test.mjs b/scripts/http-cache-security.test.mjs new file mode 100644 index 00000000..b67a10fd --- /dev/null +++ b/scripts/http-cache-security.test.mjs @@ -0,0 +1,67 @@ +import assert from "node:assert/strict"; +import { once } from "node:events"; +import { readdirSync } from "node:fs"; +import { createServer } from "node:http"; +import { createRequire } from "node:module"; +import { join } from "node:path"; +import test from "node:test"; +import { fileURLToPath, pathToFileURL } from "node:url"; + +// Exercise the installed documentation HTTP client and its actual dependency +// resolution, not a separately installed test-only copy of the cache policy. +const store = fileURLToPath(new URL("../node_modules/.pnpm/", import.meta.url)); +const clients = readdirSync(store).filter((name) => /^got@12\./.test(name)); +assert.equal(clients.length, 1, "Review the documentation HTTP client dependency layout"); +const clientRoot = join(store, clients[0], "node_modules/got"); +const clientRequire = createRequire(join(clientRoot, "package.json")); +const cacheRequire = createRequire(clientRequire.resolve("cacheable-request")); +const CachePolicy = cacheRequire("http-cache-semantics"); +const { default: got } = await import(pathToFileURL(join(clientRoot, "dist/source/index.js"))); + +test("the documentation cache consumer resolves the reviewed official release", () => { + assert.equal(cacheRequire("http-cache-semantics/package.json").version, "4.3.0"); +}); + +test("private and no-store responses are excluded from shared cache storage", () => { + const request = { method: "GET", url: "http://fixture.test/", headers: { host: "fixture.test" } }; + for (const directive of ["private", "no-store"]) { + const policy = new CachePolicy(request, { + status: 200, + headers: { "cache-control": `${directive}, max-age=60`, "set-cookie": "session=synthetic" }, + }, { shared: true }); + // The documented cache contract checks storable() before saving a response. + // Revalidation alone is not a substitute for that storage gate. + assert.equal(policy.storable(), false); + } +}); + +test("the real HTTP client preserves private isolation and public caching", async (t) => { + let hits = 0; + const server = createServer((request, response) => { + hits++; + const directive = request.url.slice(1); + response.setHeader("Cache-Control", `${directive}, max-age=60`); + response.setHeader("Set-Cookie", `session=synthetic-${hits}`); + response.end(String(hits)); + }); + t.after(() => new Promise((resolve, reject) => { + server.closeAllConnections(); + server.close((error) => error ? reject(error) : resolve()); + })); + server.listen(0, "127.0.0.1"); + await once(server, "listening"); + const client = got.extend({ cache: new Map(), retry: { limit: 0 }, timeout: { request: 2000 } }); + for (const directive of ["private", "no-store", "public"]) { + const url = `http://127.0.0.1:${server.address().port}/${directive}`; + const first = await client(url); + const second = await client(url, { headers: { "cache-control": "max-stale=999999" } }); + if (directive === "public") { + assert.equal(second.isFromCache, true); + assert.equal(second.body, first.body); + } else { + assert.equal(second.isFromCache, false); + assert.notEqual(second.body, first.body); + assert.notDeepEqual(second.headers["set-cookie"], first.headers["set-cookie"]); + } + } +}); diff --git a/scripts/images.test.mjs b/scripts/images.test.mjs index 5385c905..c9e33012 100644 --- a/scripts/images.test.mjs +++ b/scripts/images.test.mjs @@ -323,7 +323,7 @@ test("Vercel runner supports SDK user switching without granting node sudo privi }); test("runner rejects Chromium packages older than the reviewed security fix", () => { - assert.match(runnerDockerfile, /ARG CHROMIUM_MIN_VERSION=153\.0\.8010\.52-1~deb13u1/); + assert.match(runnerDockerfile, /ARG CHROMIUM_MIN_VERSION=154\.0\.8037\.57-1~deb13u1/); for (const name of ["chromium", "chromium-common"]) { assert.ok( runnerDockerfile.includes( @@ -332,3 +332,19 @@ test("runner rejects Chromium packages older than the reviewed security fix", () ); } }); + +test("image security refreshes invalidate cached distro layers and audit the cached package manager", () => { + for (const path of ["Dockerfile", "apps/web/Dockerfile", "runner/Dockerfile"]) { + const dockerfile = readFileSync(new URL(`../${path}`, import.meta.url), "utf8"); + assert.match(dockerfile, /ARG DEBIAN_SECURITY_REFRESH=20261005/); + assert.match(dockerfile, /test -n "\$DEBIAN_SECURITY_REFRESH"/); + } + assert.match(runnerDockerfile, /brace-expansion@5\.0\.12/); + assert.match(runnerDockerfile, /undici@6\.28\.1/); + const seed = runnerDockerfile.indexOf( + 'corepack install --global --cache-only "$package_manager"', + ); + const audit = runnerDockerfile.indexOf("node /tmp/verify-package-security.mjs"); + const freeze = runnerDockerfile.indexOf("chmod -R a-w /opt/facility-corepack"); + assert.ok(seed > 0 && audit > seed && freeze > audit); +}); diff --git a/scripts/runner-package-security.test.mjs b/scripts/runner-package-security.test.mjs new file mode 100644 index 00000000..cbb8f0ec --- /dev/null +++ b/scripts/runner-package-security.test.mjs @@ -0,0 +1,52 @@ +import assert from "node:assert/strict"; +import { spawnSync } from "node:child_process"; +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import test from "node:test"; +import { fileURLToPath } from "node:url"; +import { assertPatchedVersion } from "../runner/verify-package-security.mjs"; + +test("security floors accept patches and reject old, malformed and unreviewed major versions", () => { + for (const v of ["6.28.1", "6.28.2", "6.29.0"]) assertPatchedVersion(v, "6.28.1", "fixture"); + for (const v of ["6.28.0", "6.27.99", "garbage", "6.28.1-beta", "7.0.0"]) + assert.throws(() => assertPatchedVersion(v, "6.28.1", "fixture")); +}); + +test("image audit executes against real nested npm and both cached pnpm layouts", (t) => { + const root = mkdtempSync(join(tmpdir(), "facility-package-security-")); + t.after(() => rmSync(root, { recursive: true, force: true })); + const globals = join(root, "node_modules"); + const npmRoot = join(globals, "npm"); + const cache = join(root, "corepack"); + function pkg(path, version) { + mkdirSync(path, { recursive: true }); + writeFileSync(join(path, "package.json"), JSON.stringify({ version })); + } + pkg(npmRoot, "12.0.2"); + for (const [name, version] of [ + ["brace-expansion", "5.0.12"], ["ip-address", "10.7.1"], + ["tar", "7.5.21"], ["undici", "6.28.1"], + ]) pkg(join(globals, name), version); + const copies = ["dist/node_modules/undici", "artifacts/exe/dist/node_modules/undici"] + .map((path) => join(cache, "v1/pnpm/fixture", path)); + for (const path of copies) pkg(path, "6.28.1"); + const run = () => spawnSync(process.execPath, [ + fileURLToPath(new URL("../runner/verify-package-security.mjs", import.meta.url)), npmRoot, cache, + ], { encoding: "utf8" }); + assert.equal(run().status, 0); + for (const path of copies) { + pkg(path, "6.28.0"); + const rejected = run(); + assert.notEqual(rejected.status, 0); + assert.match(rejected.stderr, /below security floor/); + pkg(path, "6.28.1"); + } + pkg(join(npmRoot, "node_modules/undici"), "6.28.0"); + assert.match(run().stderr, /npm undici: 6.28.0 is below security floor/); + pkg(join(npmRoot, "node_modules/undici"), "6.28.1"); + assert.equal(run().status, 0); + rmSync(cache, { recursive: true }); + mkdirSync(cache); + assert.match(run().stderr, /No cached pnpm undici found/); +}); diff --git a/scripts/toolchain.test.mjs b/scripts/toolchain.test.mjs index 47e395c1..8e87bc52 100644 --- a/scripts/toolchain.test.mjs +++ b/scripts/toolchain.test.mjs @@ -7,7 +7,7 @@ import { fileURLToPath } from "node:url"; const root = join(dirname(fileURLToPath(import.meta.url)), ".."); // Add future even-numbered LTS lines only after they have explicit CI coverage. const nodeRange = "^22.13.0 || ^24.0.0"; -const packageManager = "pnpm@11.20.0"; +const packageManager = "pnpm@11.28.2"; async function read(path) { return readFile(join(root, path), "utf8"); @@ -65,7 +65,7 @@ test("setup documentation installs the package-manager pin", async () => { "apps/docs/docs/self-host/local-development.md", ]) { const documentation = await read(path); - assert.match(documentation, /pnpm(?:@| )11\.20\.0/); - assert.match(documentation, /corepack install --global pnpm@11\.20\.0/); + assert.match(documentation, /pnpm(?:@| )11\.28\.2/); + assert.match(documentation, /corepack install --global pnpm@11\.28\.2/); } }); diff --git a/scripts/yaml-cli-security.test.mjs b/scripts/yaml-cli-security.test.mjs new file mode 100644 index 00000000..6068fc0d --- /dev/null +++ b/scripts/yaml-cli-security.test.mjs @@ -0,0 +1,41 @@ +import assert from "node:assert/strict"; +import { spawnSync } from "node:child_process"; +import { readFileSync } from "node:fs"; +import { createRequire } from "node:module"; +import { dirname, join } from "node:path"; +import test from "node:test"; + +const docsRequire = createRequire(new URL("../apps/docs/package.json", import.meta.url)); +const coreRequire = createRequire(docsRequire.resolve("@docusaurus/core/package.json")); +const utilsRequire = createRequire(coreRequire.resolve("@docusaurus/utils")); +const matterRequire = createRequire(utilsRequire.resolve("gray-matter")); +const yamlRequire = createRequire(matterRequire.resolve("js-yaml/package.json")); +const yaml = matterRequire("js-yaml"); +const cli = join(dirname(matterRequire.resolve("js-yaml/package.json")), "bin/js-yaml.js"); + +test("legacy YAML consumer uses official argparse 2 without sprintf-js", () => { + assert.equal(yamlRequire("argparse/package.json").version, "2.0.1"); + const lockfile = readFileSync(new URL("../pnpm-lock.yaml", import.meta.url), "utf8"); + assert.doesNotMatch(lockfile, /^\s+sprintf-js(?:@|:)/m); + assert.deepEqual(yaml.safeLoad("title: Example\nitems: [one, two]\n"), { + title: "Example", items: ["one", "two"], + }); +}); + +test("real js-yaml CLI retains input, help, version and invalid-option behavior", () => { + const run = (args, input = "") => spawnSync(process.execPath, [cli, ...args], { + input, encoding: "utf8", timeout: 5000, + }); + const converted = run(["--to-json"], "title: Example\nitems: [one, two]\n"); + assert.equal(converted.status, 0, converted.stderr); + assert.deepEqual(JSON.parse(converted.stdout), { title: "Example", items: ["one", "two"] }); + const help = run(["--help"]); + assert.equal(help.status, 0, help.stderr); + assert.match(help.stdout, /--compact/); + const version = run(["--version"]); + assert.equal(version.status, 0, version.stderr); + assert.match(version.stdout, /^3\.15\.2\s*$/); + const invalid = run(["--not-a-real-option"]); + assert.notEqual(invalid.status, 0); + assert.match(invalid.stderr, /unrecognized arguments/); +});