# Keep in sync with ui/.nvmrc. FROM node:24.18.1-alpine@sha256:f70403e87646dc51b45295f4b8b70cdad0b63d2297c4c9899119b03f7af7a6b3 AS base LABEL maintainer="https://github.com/prowler-cloud" # The build uses pnpm via corepack, so npm is unused — remove it (and npx) to drop # the bundled-npm CVE surface from every stage, incl. prod. # No blanket apk upgrade: it resolves against Alpine's live repo, so the digest pin # above would not make the image reproducible. Move the digest forward instead, or # take a named package as the targeted exception below. RUN corepack enable && rm -rf /usr/local/lib/node_modules/npm /usr/local/bin/npm /usr/local/bin/npx # High CVEs fixed in Alpine 3.24 but not yet in the pinned base image: # libcrypto3/libssl3 3.5.8-r0 CVE-2026-14456, CVE-2026-14457, CVE-2026-18798, # CVE-2026-54874, CVE-2026-63072, CVE-2026-63075, # CVE-2026-63076 (image ships 3.5.7-r0) # The base image pins node 24.18.1, which has not been rebuilt since that package # was published, so the upgrade is taken here rather than by moving the pin -- the # newest published node:24-alpine (24.19.0, built 2026-08-03) predates the # 2026-08-13 advisory and carries the same vulnerable version. libcrypto3 and # libssl3 are both built from openssl and are flagged separately, so both are named. # `>=` rather than `=`: Alpine keeps only the newest build of a package in a # branch's index, so an exact pin breaks this build the day 3.5.8-r0 is superseded. # Drop this once the base image ships 3.5.8-r0 or later. RUN apk add --no-cache --upgrade \ "libcrypto3>=3.5.8-r0" \ "libssl3>=3.5.8-r0" # Install dependencies only when needed FROM base AS deps # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. #hadolint ignore=DL3018 RUN apk add --no-cache libc6-compat WORKDIR /app # Install dependencies based on the preferred package manager COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ COPY scripts ./scripts ENV NODE_OPTIONS=--max-old-space-size=4096 RUN corepack install && pnpm install --frozen-lockfile # Rebuild the source code only when needed FROM base AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . # Install pinned pnpm so build uses the exact version from package.json. # Alternative: move COPY package.json + corepack install to base stage to avoid # re-downloading, at the cost of invalidating all stages on any package.json change. RUN corepack install # Next.js collects completely anonymous telemetry data about general usage. # Learn more here: https://nextjs.org/telemetry # Uncomment the following line in case you want to disable telemetry during the build. ENV NEXT_TELEMETRY_DISABLED=1 ARG NEXT_PUBLIC_PROWLER_RELEASE_VERSION ENV NEXT_PUBLIC_PROWLER_RELEASE_VERSION=${NEXT_PUBLIC_PROWLER_RELEASE_VERSION} # GTM / API base+docs URLs are runtime container env (prod stage), not build ARGs. RUN pnpm run build # Development stage FROM base AS dev WORKDIR /app # Set up environment for development ENV NODE_ENV=development ENV NEXT_TELEMETRY_DISABLED=1 COPY --from=builder /app /app # Run development server with hot-reloading CMD ["pnpm", "run", "dev"] # Production stage FROM base AS prod WORKDIR /app # Set up environment for production ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 RUN addgroup --system --gid 1001 nodejs &&\ adduser --system --uid 1001 nextjs COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static COPY --from=builder --chown=nextjs:nodejs /app/public ./public USER nextjs EXPOSE 3000 ENV PORT=3000 ENV HOSTNAME="0.0.0.0" # Runtime configuration is read by `node server.js` at container start and is # NOT baked into the image. Supply it via your orchestrator (docker-compose, # Helm/K8s): # - required: UI_API_BASE_URL, AUTH_URL, AUTH_SECRET (missing ⇒ fail fast at boot) # - optional: UI_API_DOCS_URL # - optional: UI_CLOUD_ENABLED ("true" only in Prowler Cloud deployments) # - optional: UI_SELF_REGISTRATION_ENABLED (Prowler Cloud only; "false" hides sign-up, invited users can still register) # - optional: UI_REGISTRY_ENABLED ("true" only after the Registry dependency, # Cloud role grant, and controlled acceptance are ready; unset/false hides Registry) # - gated integrations (load only when *_ENABLED="true"; the value is then # required or boot fails). Their legacy names (NEXT_PUBLIC_SENTRY_*, # NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID, POSTHOG_KEY/HOST) still work: # UI_SENTRY_ENABLED + UI_SENTRY_DSN (+ optional UI_SENTRY_ENVIRONMENT) # UI_GOOGLE_TAG_MANAGER_ENABLED + UI_GOOGLE_TAG_MANAGER_ID # UI_POSTHOG_ENABLED + UI_POSTHOG_KEY + UI_POSTHOG_HOST (feedback survey) # - reserved: REO_DEV_CLIENT_ID (no consumer yet) # server.js is created by next build from the standalone output # https://nextjs.org/docs/pages/api-reference/next-config-js/output CMD ["node", "server.js"]