Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions .trivyignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@ CVE-2026-26960
CVE-2026-29786
CVE-2026-31802

# vite — dev-only. Reaches the image solely because the schema-deps stage runs a full
# `pnpm install` (devDependencies included) and the runner copies that node_modules.
# The CVE is a `server.fs.deny` bypass via Windows alternate paths in vite's DEV SERVER;
# this image is Linux, serves Next.js standalone, and never starts vite. Exposure is nil.
# Not fixable by a pnpm override — vitest 4.1.4 holds vite at 7.3.2 (7.3.6 exists, but
# neither `overrides` nor `--force` re-resolves it). The real fix is to stop shipping
# devDependencies in the runner stage; see FORK.md.
CVE-2026-53571
# vite — REMOVED (was CVE-2026-53571). vite no longer reaches the image at all: the
# schema-deps stage now installs with `--prod`, so no devDependency tree is copied into
# the runner. The suppression was deleted rather than kept, so that if vite ever reappears
# in a production image Trivy fails loudly instead of staying quiet. vite is still pinned at
# 7.3.2 in the lockfile by vitest's peer range — that surfaces in `pnpm audit`, which is
# advisory-only, not in the image scan that gates the build.

# esbuild Go stdlib (1.23.x) — build tool binary in schema-sync, not a runtime service. Runs once at startup for drizzle-kit push.
# Cannot fix until esbuild ships a release built with Go 1.24.13+.
# NOTE: none of the IDs below appear in a scan of the current image (esbuild is now 0.28.1);
# they are retained only so a base-image or esbuild regression does not turn CI red overnight.
CVE-2025-47912
CVE-2025-58183
CVE-2025-58185
Expand Down
15 changes: 14 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,24 @@ RUN pnpm build

# ---------------------------------------------------------------------------
# Stage 3 — Minimal deps for drizzle-kit
#
# `--prod` is load-bearing for image CVE count, not just size: the runner copies
# this node_modules in for the startup `drizzle-kit push`, so a full install
# shipped vitest/vite/jsdom/undici/typescript into production and Trivy flagged
# every one of them. drizzle-kit, drizzle-orm and postgres all live in
# `dependencies`, and drizzle-kit vendors its own esbuild/tsx, so the schema push
# has everything it needs. drizzle.config.ts already guards its `dotenv` require
# in a try/catch for exactly this case.
#
# The `prepare` script is stripped because it runs husky, a devDependency that
# `--prod` (correctly) does not install. Removing a script does not affect the
# `--frozen-lockfile` check, which compares dependency specifiers only.
# ---------------------------------------------------------------------------
FROM base AS schema-deps
WORKDIR /schema-sync
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
RUN node -e "const fs=require('fs');const p=JSON.parse(fs.readFileSync('package.json','utf8'));delete p.scripts.prepare;fs.writeFileSync('package.json',JSON.stringify(p,null,2))" \
&& pnpm install --frozen-lockfile --prod

# ---------------------------------------------------------------------------
# Stage 4 — Production runner
Expand Down
43 changes: 29 additions & 14 deletions FORK.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,28 +78,43 @@ re-apply these if upstream rewrites the release job. Reviewing that diff is wort
merging upstream runs *their* workflow code with this repo's `contents: write` and `packages: write`
token.

## Known issue: devDependencies ship in the production image
## Fixed: devDependencies no longer ship in the production image

The `schema-deps` Dockerfile stage runs a full `pnpm install` (devDependencies included) and the
runner stage copies that `node_modules` in for the drizzle-kit schema push. So vitest's entire
dependency tree — vite, jsdom, undici — lands in the production image, and Trivy flags it.
**Was:** the `schema-deps` Dockerfile stage ran a full `pnpm install` (devDependencies included) and
the runner stage copied that `node_modules` in for the drizzle-kit schema push. vitest's entire
dependency tree — vite, jsdom, undici, typescript — landed in the production image, and Trivy
flagged all of it.

Upstream already works around symptoms of this (see the esbuild block in `.trivyignore`). The real
fix is to install only what schema-sync needs in that stage. **Not attempted yet**: schema-sync runs
at container startup via `docker-entrypoint.sh`, so getting it wrong breaks deploys, not just builds.
**Now:** that stage installs with `--prod`. `drizzle-kit`, `drizzle-orm` and `postgres` all live in
`dependencies`, and drizzle-kit vendors its own esbuild/tsx, so the startup schema push still has
everything it needs. `drizzle.config.ts` already guarded its `dotenv` require in a try/catch for
exactly this case. The stage also strips the `prepare` script before installing, because `prepare`
runs husky — a devDependency that `--prod` correctly does not install.

Interim: `undici` is pinned to a patched `^7.28.0` via `pnpm.overrides`. `vite` could not be moved
the same way — vitest 4.1.4 holds it at 7.3.2 and neither `overrides` nor `--force` re-resolves it —
so its CVE is documented in `.trivyignore` instead. That one is genuinely inert here: it is a
dev-server bug on Windows, and this image is Linux running Next.js standalone.
Verified by building the image and running `docker-entrypoint.sh` against a throwaway Postgres:
16 tables created, `/api/health` returned `{"status":"ok","db":"connected"}`. Image dropped
866 MB to 671 MB, and the runner's schema-sync tree went from the full dependency graph to 29
top-level packages.

Consequence worth remembering: anything that must exist at container startup has to be a real
`dependency`. A new startup requirement that lives in `devDependencies` will now break deploys, not
just builds.

Known non-blocking CI failures on this fork:

- **Scan Dependencies** (`dependency-review-action`) — needs Dependency Graph, which GitHub disables
by default on forks. Enable under Settings → Code security, or ignore.
- **Trivy CVEs** in `undici` and `vite`, inherited from upstream's lockfile. Not introduced here.
Real exposure is low for this deployment (LAN-only, no SOCKS proxy configured, not Windows), but
they should clear when upstream bumps deps.
- **`pnpm audit`** still reports `vite` CVE-2026-53571, because vitest 4.1.4 holds vite at 7.3.2 in
the lockfile and neither `overrides` nor `--force` re-resolves it. Advisory-only — the gate that
matters is the Trivy image scan, and vite is no longer in the image.

### Known upstream quirk: sharp is not resolvable at runtime

`next build` with `output: "standalone"` traces sharp into `/app/node_modules/.pnpm/`, but never
creates the top-level `node_modules/sharp` symlink, so `require("sharp")` fails inside the container
and Next silently falls back to serving `/_next/image` requests unoptimised. Pre-existing and
version-independent — not caused by the sharp override. Fixing it means copying sharp explicitly in
the runner stage; nothing depends on it today.

## One-time setup gotcha

Expand Down
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "private-tracker-tracker",
"version": "2.8.9-homelab.2",
"version": "2.8.9-homelab.3",
"description": "Self-hosted dashboard for monitoring private tracker stats over time",
"license": "GPL-3.0",
"repository": {
Expand All @@ -20,6 +20,8 @@
],
"overrides": {
"esbuild": ">=0.25.0",
"postcss": "^8.5.24",
"sharp": "^0.35.3",
"undici": "^7.28.0",
"vite": "^7.3.6"
}
Expand Down Expand Up @@ -63,7 +65,7 @@
"emoji-picker-react": "^4.18.0",
"https-proxy-agent": "^9.0.0",
"jose": "^6.2.2",
"next": "16.2.2",
"next": "16.2.12",
"node-cron": "^4.2.1",
"node-html-parser": "^7.1.0",
"otpauth": "^9.5.0",
Expand Down Expand Up @@ -97,7 +99,7 @@
"husky": "^9.1.7",
"jsdom": "^29.0.2",
"knip": "^6.3.1",
"postcss": "^8.5.9",
"postcss": "^8.5.24",
"prettier": "^3.8.2",
"tailwindcss": "^4.2.2",
"tsx": "^4.21.0",
Expand Down
Loading
Loading