import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; const repoRoot = process.cwd(); const dockerfile = fs.readFileSync(path.join(repoRoot, 'apps', 'api', 'Dockerfile'), 'utf8'); assert.match( dockerfile, /ARG NODE_IMAGE=node:20\.20\.2-alpine3\.23@sha256:fb4cd12c85ee03686f6af5362a0b0d56d50c58a04632e6c0fb8363f609372293/, 'API image must pin the reviewed multi-architecture Node/Alpine manifest', ); assert.match(dockerfile, /FROM deps AS production-deps/); assert.match(dockerfile, /npm prune --omit=dev --workspaces --include-workspace-root/); assert.match(dockerfile, /COPY --from=production-deps --chown=node:node \/app\/node_modules \.\/node_modules/); assert.match(dockerfile, /COPY --from=build --chown=node:node \/app\/apps\/api\/dist \.\/apps\/api\/dist/); assert.match(dockerfile, /\nUSER node\n/); assert.match(dockerfile, /CMD \["node", "apps\/api\/dist\/apps\/api\/src\/server\.js"\]/); assert.doesNotMatch( dockerfile.slice(dockerfile.lastIndexOf(`FROM \${NODE_IMAGE} AS runner`)), /package-lock\.json|package\.json|scripts\/import-pocketbase|COPY --from=deps \/app\/node_modules/, 'runtime stage must contain only production dependencies and compiled API files', ); console.log('[PASS] API Docker runtime least-privilege contract');