{"id":"sec-dev-dockerfile-baseline-review","audience":"developers","category":"security","track":"security","technique":"constraints","title":"Check a Dockerfile against your container baseline","free":false,"task":"Review this Dockerfile against your team's container baseline: pinned base image version (no 'latest'), no secrets in ENV or ARG, the app doesn't run as root, and only the app's port is exposed. List only the lines that break the baseline, numbered, as 'line N: problem -> fix', in line order. Plain text, under 80 words.","materialLabel":"Dockerfile and notes (attached to your prompt automatically)","material":"Dockerfile (service: invoice-api)\n 1  FROM node:latest AS build\n 2  WORKDIR /src\n 3  COPY package*.json ./\n 4  RUN npm ci\n 5  COPY . .\n 6  RUN npm run build\n 7\n 8  FROM node:20.18-slim\n 9  WORKDIR /app\n10  COPY --from=build /src/dist ./dist\n11  COPY --from=build /src/node_modules ./node_modules\n12  ENV NODE_ENV=production\n13  ENV PAYMENTS_KEY=EXAMPLE-SECRET-DO-NOT-USE\n14  EXPOSE 3000\n15  EXPOSE 22\n16  CMD [\"node\", \"dist/server.js\"]\n\nNotes:\n- The app listens on port 3000.\n- Port 22 was added \"so we can SSH in to debug\". We debug with the platform's exec command now.\n- The node:20.18-slim image runs as root unless you set USER. It includes a built-in non-root user called \"node\".\n- Multi-stage builds are encouraged by the team.","rules":[{"type":"numberedItems","count":4},{"type":"plainText"},{"type":"maxWords","max":80}],"mistakes":[{"title":"Not giving the baseline","why":"The AI suggests every Docker best practice it knows, like healthchecks and image scanning.","fix":"Paste the baseline and say: \"Only flag what breaks it.\"","checks":["r1"]},{"title":"Missing what isn't there","why":"The AI reviews lines that exist and forgets the missing USER line.","fix":"Add: \"Check whether the app runs as root.\"","checks":["r0"]},{"title":"Vague fixes","why":"The AI says 'avoid secrets' without how.","fix":"Show the shape: \"line N: problem -> fix.\"","checks":["r2","numberedItems"]},{"title":"Trusting the AI to count words","why":"AI is bad at counting. Ask for \"under 80 words\" and it often lands well over.","fix":"Ask for less than the limit and cap each line: \"each line under 15 words, under 60 words total.\"","checks":["maxWords"]},{"title":"Letting markdown in","why":"AI adds bold and headings by default, and in the PR comment they show up as stray symbols.","fix":"Add: \"Plain text only, no bold, no headings.\"","checks":["plainText"]}],"promptHints":[{"label":"Gives the baseline","patterns":["baseline","latest","root","secret","port"]},{"label":"Lists only failures","patterns":["only (the )?(lines|failures)","break","violat"]},{"label":"Shows the line shape","patterns":["line n","->","problem","fix"]},{"label":"Sets a word limit","patterns":["\\d+\\s*words","word (limit|count)"]},{"label":"Asks for plain text","patterns":["plain","no bold","no markdown","no formatting","no headings"]}],"hint":"A baseline review checks a few rules, including things that are missing. Give the AI the rules, ask only for failures, and show the line format.","rubricLabels":["Lists exactly these 4, in line order: line 1 (node:latest), line 13 (secret in ENV), line 15 (port 22 exposed), and the missing USER before CMD (line 16, running as root)","Does not flag the multi-stage build, NODE_ENV on line 12, or EXPOSE 3000","Each line gives a concrete fix (pin a version like node:20.18-slim, pass the key at runtime from the secret store, remove EXPOSE 22, add USER node)"]}