diff --git a/.forge/features/node-security-october-2026/spec.md b/.forge/features/node-security-october-2026/spec.md new file mode 100644 index 000000000..e29447217 --- /dev/null +++ b/.forge/features/node-security-october-2026/spec.md @@ -0,0 +1,23 @@ +# October 2026 Node.js Security Update Spec + +Status: Scoped from the user's security-maintenance request (2026-10-21). + +## Purpose and scope + +Address the Node.js October 21 security release across Forge's development, CI, +and container runtimes. Keep existing application behavior and dependencies. +The user requested a branch from main named `update-vulernerability`, no commits, +and validation results plus commands for committing manually. + +## Acceptance criteria + +- Contributors and CI use a supported, patched Node release. +- Every Node container build and runner stage uses the patched release. +- Existing checks and native dependencies are exercised under that release. +- Results distinguish source changes from production rollout and skipped checks. + +## Non-goals and open questions + +No app/UI, schema, permission, dependency, PHP, or production-service changes. +No PHP source or Composer manifest is tracked. Actual production runtime versions +and deployment completion require operator access; do not infer them from source. diff --git a/.forge/features/node-security-october-2026/srd.md b/.forge/features/node-security-october-2026/srd.md new file mode 100644 index 000000000..4e780f396 --- /dev/null +++ b/.forge/features/node-security-october-2026/srd.md @@ -0,0 +1,49 @@ +# October 2026 Node.js Security Update SRD + +## Evidence and boundary + +The [official October advisory](https://nodejs.org/en/blog/vulnerability/october-2026-security-releases) +identifies Node 24.22.1, 22.23.4, and 26.12.1 as patched releases. It covers +DNS/TLS validation, Brotli memory corruption, HTTP behavior, and bundled libraries. +Before this change `.nvmrc` pinned EOL 25.6.1, root engines admitted Node >=20.16.0, +and 18 Node stages in 13 Dockerfiles used the floating `24-bookworm-slim` tag. +The local executable was 24.21.0. Production versions have not been observed. + +## Technical requirements and compatibility + +- Use 24.22.1, retaining the existing production major and Debian bookworm-slim. +- Pin `.nvmrc`; CI already consumes it via `tooling/github/setup/action.yml`. +- Restrict root engines to `^24.22.1`. An unbounded minimum would still admit + EOL 25.x and unpatched early 26.x versions. +- The engine range is advisory for local commands. Contributors must select the + `.nvmrc` runtime; this patch does not enforce arbitrary manual Node invocations. +- Replace every Node tools/runner base with `node:24.22.1-bookworm-slim`. +- Keep pnpm 9.12.1, Turbo, lockfile, application dependencies, and nginx unchanged. +- Update contributor instructions to agree with the supported runtime. +- Access policy, tRPC, database, authentication, and all app behavior are unchanged. + +These are separate atomic checks of the same runtime invariant, consistent with +[Forge's engineering principles](../../../docs/agentic-development/forge-engineering-principles.md). +No shared application abstraction or new test harness is needed. + +## Verification + +Use an official Node binary with its SHA-256 checksum verified. Check all runtime +references, engine-range boundaries, native solver/image support, and repo gates. +Confirm the Docker tag exists for amd64 and arm64, then exercise Linux runtime +behavior and representative image builds when Docker is available. +A package-manager audit is supplementary; it does not audit Node itself. + +## Rollout and rollback + +Source changes do not patch running containers. After review/merge, rebuild and +redeploy each configured Coolify resource using these Dockerfiles, including +static-site builders, and verify `node --version` in the five Node runners +(Blade, Guild, KH9, TK, cron) is 24.22.1. Static nginx runners contain no Node. +A container restart alone is insufficient. Non-Docker deployments and CI runners +must also use the patched runtime. Do not roll back to the vulnerable versions; +use a supported release carrying the same fixes if compatibility requires a change. + +## Open questions + +Production inventory and deployment execution are outside this local patch. diff --git a/.forge/features/node-security-october-2026/status.md b/.forge/features/node-security-october-2026/status.md new file mode 100644 index 000000000..95e249c3f --- /dev/null +++ b/.forge/features/node-security-october-2026/status.md @@ -0,0 +1,70 @@ +# October 2026 Node.js Security Update Status + +Current phase: Validated locally; ready for user commit and operator rollout. + +## Decisions + +- 2026-10-21: User requested Node security maintenance from main, the branch + `update-vulernerability`, no commit, and manual commit commands. +- Branch starts at freshly fetched `origin/main` (8097334e). Local main is checked + out in another worktree and was left untouched. Existing untracked work is preserved. +- Keep the production Node 24 major; use patched 24.22.1 everywhere, restrict + engines to 24.x, and leave application dependencies/lockfile unchanged. +- Independent read-only investigation confirmed every runtime reference and + installed Next/Vitest/Vite/OR-Tools/Sharp compatibility with Node 24. +- User confirmed: keep dependency remediation out of this Node branch; report + audit findings separately. + +## Progress + +- [x] Verify official advisory and source runtime inventory. +- [x] Create requested branch and feature bundle. +- [x] Align development/CI, engines, documentation, and all Node Docker stages. +- [x] Complete focused runtime/native validation and repository gates. +- [x] Complete independent patch review (no actionable bypass/regression found). +- [x] Prepare readiness report and scoped manual commit commands. + +## Validation + +- Official macOS arm64 24.22.1 archive downloaded; SHA-256 matches SHASUMS256.txt. +- Docker registry manifest confirms 24.22.1-bookworm-slim for amd64 and arm64. +- Runtime-reference assertions: all 18 Node stages, root engines, `.nvmrc`, and + CI version-file consumption agree. Engine range boundary checks passed. +- DNS NUL regression: old local 24.21.0 sent a query for both embedded-NUL and + trailing-NUL names. Patched 24.22.1 rejected both with ERR_INVALID_ARG_VALUE + before any query. Valid local DNS and Brotli round trips passed on macOS, + Linux arm64, and Linux amd64. Other upstream CVEs were not individually reproduced. +- `pnpm --filter @forge/api test src/tests/judging-schedule/solver.test.ts`: + passed, 10 tests. Native Sharp PNG encode/decode passed. +- `pnpm format`: passed, 24 tasks. Targeted Prettier check also covers root JSON, + setup docs, and this feature bundle. +- `TURBO_FORCE=true TURBO_CONCURRENCY=1 pnpm typecheck`: passed, 33 tasks, + no Turbo cache hits. Validation used the verified 24.22.1 binary via PATH. +- `TURBO_FORCE=true TURBO_CONCURRENCY=1 pnpm lint`: passed, 31 tasks, no Turbo + cache hits; existing warnings and no lint errors. +- Docker tools-stage build passed; its isolated container reports Node 24.22.1, + pnpm 9.12.1, and Turbo 2.8.11. Full app container builds were cancelled with the + initial concurrent attempts and were not completed. Linux base/runtime behavior + and the shared tools stage were validated; final application images and running + production containers still require operator rollout verification. +- Initial concurrent lint/typecheck/container attempts were explicitly cancelled + because of host memory pressure; they are not recorded as passing checks. +- Full build passed, 21 tasks, no Turbo cache hits. Exact command, with the verified + Node 24.22.1 binary on PATH: + `CI=1 TURBO_FORCE=true TURBO_CONCURRENCY=1 pnpm exec dotenv -e .env.example -- pnpm build --env-mode=loose --concurrency=1`. + Example values were injected into the build process; existing env files were not + changed. The operator's default local Node installation was not upgraded. +- `pnpm audit --json`: exited 1; metadata reports 6 critical, 69 high, + 77 moderate, and 18 low vulnerability counts. The advisory dictionary contains + 152 distinct records across 45 packages. Critical records involve Next.js and + Handlebars. These registry matches were not individually reproduced; the report + and raw JSON are retained separately outside the repository. Dependencies remain + unchanged at the user's request. + +## Open questions and links + +- Actual production runtime inventory and rollout require operator verification. +- The local DNS regression script, dependency report, and raw audit JSON were + retained through Codex Security artifact storage and linked in the handoff. +- No issue/PR was supplied or created; no commit or push will be performed. +- Source: https://nodejs.org/en/blog/vulnerability/october-2026-security-releases diff --git a/.forge/features/node-security-october-2026/test-cases.md b/.forge/features/node-security-october-2026/test-cases.md new file mode 100644 index 000000000..446fb94df --- /dev/null +++ b/.forge/features/node-security-october-2026/test-cases.md @@ -0,0 +1,25 @@ +# October 2026 Node.js Security Update Test Cases + +## Runtime invariant + +- `.nvmrc`, root engines, and all 18 Node stages agree on patched 24.22.1. +- CI reads `.nvmrc`; no independent old CI version bypasses the pin. +- Engine range accepts 24.22.1 and newer 24.x; rejects 20.x, 22.x, 24.22.0, + EOL 25.x, and early unpatched 26.x. Other supported majors are outside the + deliberately chosen, production-aligned 24.x compatibility contract. +- Docker registry publishes the exact tag for Linux amd64 and arm64. + +## Security and legitimate controls + +- Compare DNS NUL handling with the prior local runtime and patched runtime. +- Valid DNS behavior and a Brotli round trip remain functional. +- Exercise real OR-Tools scheduling and Sharp encode/decode under patched Node. +- Do not claim a CVE reproducer for vulnerabilities without a verified trigger. + Upstream release identity is the evidence for the remaining runtime fixes. + +## Repository and container checks + +Run `pnpm format`, `pnpm lint`, `pnpm typecheck`, and `pnpm build` with the patched +Node at the front of PATH and Turbo cache bypassed. Exercise representative static, +standalone Next, and bot images when practical. Record exact failures and skipped +checks in `status.md`; do not expand this patch to unrelated baseline failures. diff --git a/.nvmrc b/.nvmrc index 385d7caac..474d750b5 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v25.6.1 +v24.22.1 diff --git a/deploy/dockerfiles/blade.Dockerfile b/deploy/dockerfiles/blade.Dockerfile index 35ea46586..07f4960ac 100644 --- a/deploy/dockerfiles/blade.Dockerfile +++ b/deploy/dockerfiles/blade.Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1.7 -FROM node:24-bookworm-slim AS tools +FROM node:24.22.1-bookworm-slim AS tools RUN npm install --global pnpm@9.12.1 turbo@2.8.11 WORKDIR /app @@ -43,7 +43,7 @@ RUN test -n "${FORGE_BUILD_PLACEHOLDER}" \ && test -n "${NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY}" \ && pnpm --filter @forge/blade build -FROM node:24-bookworm-slim AS runner +FROM node:24.22.1-bookworm-slim AS runner RUN apt-get update \ && apt-get install --yes --no-install-recommends ca-certificates curl \ && rm -rf /var/lib/apt/lists/* diff --git a/deploy/dockerfiles/bloomknights.Dockerfile b/deploy/dockerfiles/bloomknights.Dockerfile index 0b62886a7..480058824 100644 --- a/deploy/dockerfiles/bloomknights.Dockerfile +++ b/deploy/dockerfiles/bloomknights.Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1.7 -FROM node:24-bookworm-slim AS tools +FROM node:24.22.1-bookworm-slim AS tools RUN npm install --global pnpm@9.12.1 turbo@2.8.11 WORKDIR /app diff --git a/deploy/dockerfiles/club.Dockerfile b/deploy/dockerfiles/club.Dockerfile index ef89c40a1..4521fc9cb 100644 --- a/deploy/dockerfiles/club.Dockerfile +++ b/deploy/dockerfiles/club.Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1.7 -FROM node:24-bookworm-slim AS tools +FROM node:24.22.1-bookworm-slim AS tools RUN npm install --global pnpm@9.12.1 turbo@2.8.11 WORKDIR /app diff --git a/deploy/dockerfiles/cron.Dockerfile b/deploy/dockerfiles/cron.Dockerfile index 1b9dad33c..6e1c998cf 100644 --- a/deploy/dockerfiles/cron.Dockerfile +++ b/deploy/dockerfiles/cron.Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1.7 -FROM node:24-bookworm-slim AS tools +FROM node:24.22.1-bookworm-slim AS tools RUN npm install --global pnpm@9.12.1 turbo@2.8.11 WORKDIR /app @@ -18,7 +18,7 @@ COPY --from=pruner /app/out/full/ . RUN --mount=type=cache,id=forge-pnpm-store,target=/pnpm/store,sharing=locked \ pnpm --config.node-linker=isolated --filter @forge/cron deploy --prod /prod -FROM node:24-bookworm-slim AS runner +FROM node:24.22.1-bookworm-slim AS runner ENV NODE_ENV=production WORKDIR /app COPY --from=builder --chown=node:node /prod/ ./ diff --git a/deploy/dockerfiles/gemiknights.Dockerfile b/deploy/dockerfiles/gemiknights.Dockerfile index af5c5f2b5..c0158403c 100644 --- a/deploy/dockerfiles/gemiknights.Dockerfile +++ b/deploy/dockerfiles/gemiknights.Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1.7 -FROM node:24-bookworm-slim AS tools +FROM node:24.22.1-bookworm-slim AS tools RUN npm install --global pnpm@9.12.1 turbo@2.8.11 WORKDIR /app diff --git a/deploy/dockerfiles/guild.Dockerfile b/deploy/dockerfiles/guild.Dockerfile index 5c5afd7a0..bd3ba36ee 100644 --- a/deploy/dockerfiles/guild.Dockerfile +++ b/deploy/dockerfiles/guild.Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1.7 -FROM node:24-bookworm-slim AS tools +FROM node:24.22.1-bookworm-slim AS tools RUN npm install --global pnpm@9.12.1 turbo@2.8.11 WORKDIR /app @@ -39,7 +39,7 @@ RUN test -n "${FORGE_BUILD_PLACEHOLDER}" \ && test -n "${NEXT_PUBLIC_TRPC_URL}" \ && pnpm --filter @forge/guild build -FROM node:24-bookworm-slim AS runner +FROM node:24.22.1-bookworm-slim AS runner RUN apt-get update \ && apt-get install --yes --no-install-recommends ca-certificates curl \ && rm -rf /var/lib/apt/lists/* diff --git a/deploy/dockerfiles/kh2020.Dockerfile b/deploy/dockerfiles/kh2020.Dockerfile index 2d4819d71..3039b2cf3 100644 --- a/deploy/dockerfiles/kh2020.Dockerfile +++ b/deploy/dockerfiles/kh2020.Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1.7 -FROM node:24-bookworm-slim AS tools +FROM node:24.22.1-bookworm-slim AS tools RUN npm install --global pnpm@9.12.1 turbo@2.8.11 WORKDIR /app diff --git a/deploy/dockerfiles/kh2021.Dockerfile b/deploy/dockerfiles/kh2021.Dockerfile index a7370f10d..f67b8fb9a 100644 --- a/deploy/dockerfiles/kh2021.Dockerfile +++ b/deploy/dockerfiles/kh2021.Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1.7 -FROM node:24-bookworm-slim AS tools +FROM node:24.22.1-bookworm-slim AS tools RUN npm install --global pnpm@9.12.1 turbo@2.8.11 WORKDIR /app diff --git a/deploy/dockerfiles/kh2023.Dockerfile b/deploy/dockerfiles/kh2023.Dockerfile index 96b2ed61f..aea7ee7e9 100644 --- a/deploy/dockerfiles/kh2023.Dockerfile +++ b/deploy/dockerfiles/kh2023.Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1.7 -FROM node:24-bookworm-slim AS tools +FROM node:24.22.1-bookworm-slim AS tools RUN npm install --global pnpm@9.12.1 turbo@2.8.11 WORKDIR /app diff --git a/deploy/dockerfiles/kh2024.Dockerfile b/deploy/dockerfiles/kh2024.Dockerfile index 7e8eed4a4..49c518fc0 100644 --- a/deploy/dockerfiles/kh2024.Dockerfile +++ b/deploy/dockerfiles/kh2024.Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1.7 -FROM node:24-bookworm-slim AS tools +FROM node:24.22.1-bookworm-slim AS tools RUN npm install --global pnpm@9.12.1 turbo@2.8.11 WORKDIR /app diff --git a/deploy/dockerfiles/kh8.Dockerfile b/deploy/dockerfiles/kh8.Dockerfile index 7b78385f6..2e8084029 100644 --- a/deploy/dockerfiles/kh8.Dockerfile +++ b/deploy/dockerfiles/kh8.Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1.7 -FROM node:24-bookworm-slim AS tools +FROM node:24.22.1-bookworm-slim AS tools RUN npm install --global pnpm@9.12.1 turbo@2.8.11 WORKDIR /app diff --git a/deploy/dockerfiles/kh9.Dockerfile b/deploy/dockerfiles/kh9.Dockerfile index 8129f1402..b543f5509 100644 --- a/deploy/dockerfiles/kh9.Dockerfile +++ b/deploy/dockerfiles/kh9.Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1.7 -FROM node:24-bookworm-slim AS tools +FROM node:24.22.1-bookworm-slim AS tools RUN npm install --global pnpm@9.12.1 turbo@2.8.11 WORKDIR /app @@ -26,7 +26,7 @@ RUN test -n "${BLADE_URL}" \ && test -n "${KHIX_HACKER_PORTAL_ORIGIN}" \ && pnpm --filter @forge/2026 build -FROM node:24-bookworm-slim AS runner +FROM node:24.22.1-bookworm-slim AS runner RUN apt-get update \ && apt-get install --yes --no-install-recommends ca-certificates curl \ && rm -rf /var/lib/apt/lists/* diff --git a/deploy/dockerfiles/tk.Dockerfile b/deploy/dockerfiles/tk.Dockerfile index 0ebbd61d8..25edbb184 100644 --- a/deploy/dockerfiles/tk.Dockerfile +++ b/deploy/dockerfiles/tk.Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1.7 -FROM node:24-bookworm-slim AS tools +FROM node:24.22.1-bookworm-slim AS tools RUN npm install --global pnpm@9.12.1 turbo@2.8.11 WORKDIR /app @@ -18,7 +18,7 @@ COPY --from=pruner /app/out/full/ . RUN --mount=type=cache,id=forge-pnpm-store,target=/pnpm/store,sharing=locked \ pnpm --config.node-linker=isolated --filter @forge/tk deploy --prod /prod -FROM node:24-bookworm-slim AS runner +FROM node:24.22.1-bookworm-slim AS runner ENV NODE_ENV=production WORKDIR /app COPY --from=builder --chown=node:node /prod/ ./ diff --git a/docs/GETTING-STARTED.md b/docs/GETTING-STARTED.md index 69181928e..25653074c 100644 --- a/docs/GETTING-STARTED.md +++ b/docs/GETTING-STARTED.md @@ -12,7 +12,7 @@ This guide will help you set up Forge locally and make your first contribution. Before you begin, ensure you have the following installed: -- [Node.js LTS](https://nodejs.org/en/download/) (v20.16.0 or higher) +- [Node.js LTS](https://nodejs.org/en/download/) (v24.22.1 or a newer 24.x release; use `.nvmrc` to match CI) - [pnpm](https://pnpm.io/installation) (v9.6.0 or higher) - [Docker Desktop](https://docs.docker.com/get-docker/) - A POSIX/Unix terminal (Linux, macOS, or WSL for Windows users) @@ -266,7 +266,7 @@ If a port is already in use, stop the conflicting process or change the port in Make sure you're using the correct versions: ```bash -node --version # Should be >= 20.16.0 +node --version # Should match .nvmrc (v24.22.1), or a newer 24.x release pnpm --version # Should be >= 9.6.0 ``` diff --git a/package.json b/package.json index ff6ebc035..8ddf91ed4 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "forge", "private": true, "engines": { - "node": ">=20.16.0", + "node": "^24.22.1", "pnpm": "^9.6.0" }, "packageManager": "pnpm@9.12.1", diff --git a/packages/api/src/tests/integration/hackathon-portal-configuration.test.ts b/packages/api/src/tests/integration/hackathon-portal-configuration.test.ts index a8a607e37..36bde271c 100644 --- a/packages/api/src/tests/integration/hackathon-portal-configuration.test.ts +++ b/packages/api/src/tests/integration/hackathon-portal-configuration.test.ts @@ -8,6 +8,7 @@ import { provisionDisposableDatabase, } from "@forge/db/testing"; +import { trackTestPoolShutdown } from "../support/close-test-pool"; import { permissionBitstring } from "../support/permissions"; type DatabaseClient = typeof import("@forge/db/client").db; @@ -25,6 +26,7 @@ describe.skipIf(!canRunDatabaseTests())( () => { let disposable: DisposableDatabase | undefined; let client: DatabaseClient; + let closePool: (() => Promise) | undefined; let audit: AuditSchemas; let auth: AuthSchemas; let knightHacks: KnightHacksSchemas; @@ -51,6 +53,7 @@ describe.skipIf(!canRunDatabaseTests())( process.env.DATABASE_URL = disposable.url; ({ db: client } = await import("@forge/db/client")); + closePool = trackTestPoolShutdown(client.$client); audit = await import("@forge/db/schemas/audit"); auth = await import("@forge/db/schemas/auth"); knightHacks = await import("@forge/db/schemas/knight-hacks"); @@ -96,7 +99,7 @@ describe.skipIf(!canRunDatabaseTests())( }, 120_000); afterAll(async () => { - await client.$client.end().catch(() => undefined); + await closePool?.(); await disposable?.drop(); }, 30_000);