The runner stage ships the npm CLI bundled in node:22-alpine, whose
vendored dependencies carry known vulnerabilities that trivy flags on the
published postgresql-latest image:
usr/local/lib/node_modules/npm/node_modules/...
- sigstore 3.1.0 CVE-2026-48815 (HIGH)
- picomatch 4.0.3 CVE-2026-33671 (HIGH)
- @sigstore/core 2.0.0 CVE-2026-48758
- brace-expansion 2.0.2 CVE-2026-33750
- ip-address 10.1.0 CVE-2026-42338
- tar 7.5.11 CVE-2026-53655
npm is only a build-time bootstrap for pnpm; it is not needed at runtime.
Install pnpm with it, then remove npm in the same layer so none of these
vendored packages are present in the final image, and run the container
via pnpm instead of npm. pnpm (and corepack) remain and were never flagged.
The curl/libcurl/c-ares HIGH findings are resolved by rebuilding alone,
since Alpine 3.24 already serves curl 8.21.0-r0 / c-ares 1.34.8-r0 (the
published image is simply stale).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
84 lines
2.5 KiB
Docker
84 lines
2.5 KiB
Docker
ARG NODE_IMAGE_VERSION="22-alpine"
|
|
ARG PNPM_VERSION="10.15.1"
|
|
# Keep in sync with the prisma/@prisma/* versions in package.json
|
|
ARG PRISMA_VERSION="7.8.0"
|
|
|
|
# Install dependencies only when needed
|
|
FROM node:${NODE_IMAGE_VERSION} AS deps
|
|
|
|
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
|
|
RUN apk add --no-cache libc6-compat
|
|
WORKDIR /app
|
|
COPY package.json pnpm-lock.yaml ./
|
|
RUN npm install -g pnpm
|
|
|
|
RUN printf 'strictDepBuilds: false\n' > pnpm-workspace.yaml
|
|
|
|
RUN pnpm install --frozen-lockfile
|
|
|
|
# Rebuild the source code only when needed
|
|
FROM node:${NODE_IMAGE_VERSION} AS builder
|
|
WORKDIR /app
|
|
COPY --from=deps /app/node_modules ./node_modules
|
|
COPY . .
|
|
COPY docker/proxy.ts ./src
|
|
|
|
ARG BASE_PATH
|
|
|
|
ENV BASE_PATH=$BASE_PATH
|
|
ENV NEXT_TELEMETRY_DISABLED=1
|
|
ENV DATABASE_URL="postgresql://user:pass@localhost:5432/dummy"
|
|
|
|
RUN npm run build-docker
|
|
|
|
# Production image, copy all the files and run next
|
|
FROM node:${NODE_IMAGE_VERSION} AS runner
|
|
WORKDIR /app
|
|
|
|
ARG NODE_OPTIONS
|
|
ARG PRISMA_VERSION
|
|
|
|
ENV NODE_ENV=production
|
|
ENV NEXT_TELEMETRY_DISABLED=1
|
|
ENV NODE_OPTIONS=$NODE_OPTIONS
|
|
|
|
RUN addgroup --system --gid 1001 nodejs
|
|
RUN adduser --system --uid 1001 nextjs
|
|
# Bootstrap pnpm with the bundled npm, then remove npm in the same layer so the
|
|
# vulnerable packages vendored inside the npm CLI are not shipped in the final
|
|
# image. pnpm is the only package manager needed at build and runtime.
|
|
RUN set -x \
|
|
&& apk add --no-cache curl libc6-compat \
|
|
&& npm install -g pnpm \
|
|
&& rm -rf /usr/local/lib/node_modules/npm /usr/local/bin/npm /usr/local/bin/npx
|
|
|
|
RUN echo {} > package.json
|
|
|
|
RUN printf "allowBuilds:\n '@prisma/engines': true\n prisma: false\nverifyDepsBeforeRun: false\n" > pnpm-workspace.yaml
|
|
|
|
# Script dependencies
|
|
RUN pnpm add npm-run-all dotenv chalk semver \
|
|
prisma@${PRISMA_VERSION} \
|
|
@prisma/client@${PRISMA_VERSION} \
|
|
@prisma/adapter-pg@${PRISMA_VERSION}
|
|
|
|
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
|
|
COPY --from=builder /app/prisma ./prisma
|
|
COPY --from=builder /app/prisma.config.ts ./prisma.config.ts
|
|
COPY --from=builder /app/scripts ./scripts
|
|
COPY --from=builder /app/generated ./generated
|
|
|
|
# Automatically leverage output traces to reduce image size
|
|
# https://nextjs.org/docs/advanced-features/output-file-tracing
|
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
|
|
|
USER nextjs
|
|
|
|
EXPOSE 3000
|
|
|
|
ENV HOSTNAME=0.0.0.0
|
|
ENV PORT=3000
|
|
|
|
CMD ["pnpm", "run", "start-docker"]
|