generate-dockerfile — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited generate-dockerfile (Agent Skill) and scored it 96/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 1 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Produce a production-quality, secure Dockerfile (or enhancement plan for an existing one) for a single module. The output is a complete Dockerfile that the user can write to disk — not a description of one.
| Field | Required | Description | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
modulePath | yes | Absolute path of the module to containerize (one of the modules from analyze-repo). | ||||||||
language | yes | java \ | dotnet \ | javascript \ | typescript \ | python \ | go \ | rust \ | php \ | ruby. |
languageVersion | recommended | E.g. 21, 8.0, 20, 3.11. Substituted into the base image tag. | ||||||||
framework | optional | E.g. spring-boot, express, next, django, flask, asp.net-core, gin. | ||||||||
environment | optional | production (default) or development. | ||||||||
targetPlatform | optional | linux/amd64 (default) or linux/arm64. | ||||||||
detectedDependencies | optional | Used to choose extra apt/apk packages (e.g. native modules). | ||||||||
detectedEnvVars | optional | List from analyze-repo. Used to add ENV instructions for config/database vars and to WARN about secret-classified ones (never bake secrets in). | ||||||||
existingDockerfile | optional | If a Dockerfile is already at <modulePath>/Dockerfile, read its content and follow the Enhancement path instead of the Generation path. |
This skill needs language, framework, detectedDependencies, and detectedEnvVars from the analyze-repo skill. If you don't already have that output for <modulePath> from earlier in this conversation, follow the analyze-repo skill against <modulePath> first, then come back.
<modulePath>/Dockerfile exists.Enhancement path (Step E2–E4).
Use multi-stage if any of these:
language ∈ {java, go, rust, dotnet}language is typescript AND build output is required (tsc, next build, vite build, etc.)buildSystem.type ∈ {maven, gradle}Otherwise use single-stage.
The reason this matters: multi-stage drops compilers, SDKs, and source code from the final image — typically 70–90% smaller and a smaller attack surface.
Prefer the first matching row. Substitute the major version into the tag.
| Language / Stack | Build stage | Runtime stage |
|---|---|---|
java (Spring Boot, generic) | mcr.microsoft.com/openjdk/jdk:<LV>-azurelinux or eclipse-temurin:<LV>-jdk | mcr.microsoft.com/openjdk/jdk:<LV>-distroless or eclipse-temurin:<LV>-jre-alpine |
dotnet (ASP.NET Core) | mcr.microsoft.com/dotnet/sdk:<LV>-azurelinux | mcr.microsoft.com/dotnet/aspnet:<LV>-azurelinux |
dotnet (worker / CLI) | mcr.microsoft.com/dotnet/sdk:<LV>-azurelinux | mcr.microsoft.com/dotnet/runtime:<LV>-azurelinux |
javascript / typescript | mcr.microsoft.com/azurelinux/base/nodejs:<LV> or node:<LV>-alpine | mcr.microsoft.com/azurelinux/distroless/nodejs:<LV> or node:<LV>-alpine |
python | mcr.microsoft.com/azurelinux/base/python:<LV> or python:<LV>-slim | mcr.microsoft.com/azurelinux/distroless/python:<LV> or python:<LV>-slim |
go | golang:<LV>-alpine | gcr.io/distroless/static-debian12:nonroot (CGO disabled) or gcr.io/distroless/base-debian12:nonroot (CGO enabled) |
rust | rust:<LV>-slim | gcr.io/distroless/cc-debian12:nonroot |
php (web) | composer:2 | php:<LV>-fpm-alpine or php:<LV>-apache |
ruby (rails) | ruby:<LV>-slim | ruby:<LV>-slim |
Defaults if languageVersion is missing: java=21, dotnet=8.0, node=20, python=3.11, go=1.22, rust=1.75, php=8.3, ruby=3.3.
Apply every rule in this checklist:
Layer/structure
WORKDIR early (use /app); never use cd in RUN.preserve layer cache.
RUN chaining with && to keep layers small. Aim for ≤ 6 RUNinstructions.
AS build, AS runtime).Security baseline (mandatory)
USER instruction set to a non-root user.nonroot, appuser), useit; otherwise create one with two separate instructions (USER is a Dockerfile instruction, not a shell command — it cannot be chained after RUN ... &&):
RUN adduser -D -u 10001 appuser
USER appuser RUN useradd -m -u 10001 appuser
USER appuserENV:PASSWORD, PASSWD, TOKEN, SECRET, CREDENTIAL, API_KEY, PRIVATE_KEY, ACCESS_KEY, *_DSN, DATABASE_URL, CONNECTION_STRING, REDIS_URL, MONGODB_URI. These get injected at runtime via K8s Secrets.
apt-get upgrade / apt-get dist-upgrade.apt-get install, clean up:apt-get update && apt-get install -y --no-install-recommends <pkgs> && rm -rf /var/lib/apt/lists/*.
:21, :8.0, :3.11), never :latest andnever floating patch versions like 21.0.3.
Quality baseline
HEALTHCHECK for long-running services (web servers, workers).Pick the simplest probe the runtime image actually supports:
curl: HEALTHCHECK CMD curl -fsS http://localhost:<port>/health || exit 1HEALTHCHECK CMD wget --spider -q http://localhost:<port>/health || exit 1# HEALTHCHECK: skipped — distroless image has no shell. Use Kubernetes liveness/readiness probe.EXPOSE <port> for the primary listener.CMD in exec form (["binary","arg"]), required for distroless.Env variables
detectedEnvVars classified as config or database:if it has a defaultValue, emit ENV NAME=<value>; otherwise emit # ENV NAME=<set at runtime> as a comment.
secret: emit `# SECRET: NAME — inject viaKubernetes Secret at runtime, do NOT bake into the image`.
Required labels (attribution)
Always emit:
LABEL com.azure.containerizationassist.createdby="containerization-assist"If you can read the current containerization-assist package version from the environment (e.g. its package.json), also emit:
LABEL com.azure.containerizationassist.version="<version>"Otherwise omit the version label — never hard-code a stale version string. Do NOT substitute either key for org.opencontainers.image.*.
Framework-specific tweaks
| Framework | Add |
|---|---|
| Spring Boot | ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0", copy fat JAR from target/*.jar, ENTRYPOINT ["java","-jar","/app/app.jar"] |
| Next.js | Build with npm run build; copy .next/standalone, .next/static, public; CMD ["node","server.js"] |
| Django | RUN python manage.py collectstatic --noinput; run with gunicorn |
| FastAPI | CMD ["uvicorn","app.main:app","--host","0.0.0.0","--port","8000"] |
| ASP.NET Core | dotnet publish -c Release -o /out; runtime stage runs dotnet <App>.dll |
| Go | CGO_ENABLED=0 go build -ldflags="-s -w" -o /out/app; copy single binary into distroless |
| Rust | cargo build --release; copy target/release/<bin> into distroless |
Before emitting, confirm:
USER in the final stageENV:latest tag, no apt-get upgradeWORKDIR set, no cd in RUNEXPOSE matches detected listener portLABEL com.azure.containerizationassist.createdby is presentCMD/ENTRYPOINT in exec formIf any item fails, fix the Dockerfile before showing it to the user.
Read the file. Compute:
baseImages = every line starting with FROM (after FROM , take thefirst whitespace-delimited token).
isMultistage = baseImages.length > 1.hasHealthCheck = any line starts with HEALTHCHECK (case-insensitive).hasNonRootUser = any USER line whose argument is not root or 0.instructionCount = number of lines whose first token is one of:FROM, RUN, CMD, LABEL, EXPOSE, ENV, ADD, COPY, ENTRYPOINT, VOLUME, USER, WORKDIR, ARG, ONBUILD, STOPSIGNAL, HEALTHCHECK, SHELL.
complexity = complex if instructionCount > 20 or isMultistage;else moderate if > 10; else simple.
securityPosture = good if hasNonRootUser && hasHealthCheck; poorif !hasNonRootUser && !hasHealthCheck; else needs-improvement.
HEALTHCHECK if present, non-root USER if present, existing base image unless it's :latest or unsupported.
violates (root user, hardcoded secrets, apt-get upgrade, missing cleanup, cd in RUN, floating tags, > 6 RUN instructions when combinable, missing labels).
image has a shell, required LABELs if absent, WORKDIR if absent.
| Conditions | Strategy |
|---|---|
securityPosture == "poor" OR (improve.length + addMissing.length > 5) | major-overhaul |
securityPosture == "needs-improvement" OR (improve.length + addMissing.length > 2) | moderate-refactor |
| Otherwise | minor-tweaks |
Compose the full updated Dockerfile (do not show a diff). Apply every relevant item from G3 to bring it to baseline. Preserve the listed items verbatim. Re-run the G4 self-check before writing.
As soon as the Dockerfile passes the G4 self-check, write it to `<modulePath>/Dockerfile`. Do not ask the user for confirmation first; do not stage it in the response. If a Dockerfile already exists, overwrite it (the Enhancement path already incorporates anything worth preserving).
Do NOT print the full Dockerfile content in the chat response. The user can open the file on disk. Only include excerpts when:
Use exactly these sections, in order. Keep the response short — the file on disk is the artifact.
**Dockerfile <generation|enhancement>** — written to `<modulePath>/Dockerfile`
### Plan
- **Strategy:** <multi-stage build | single-stage build | minor-tweaks | moderate-refactor | major-overhaul>
- **Base image (build):** `<image>` <only if multi-stage>
- **Base image (runtime):** `<image>`
- **User:** `appuser` (UID 10001) <or "preserved from existing">
- **Healthcheck:** <yes/no — reason if no>
### Env vars
- `<NAME>` *<config|database>* → emitted as `ENV`
- `<NAME>` *secret* → **runtime injection only** (Kubernetes Secret)
- ...
### Next steps
1. Build locally: `docker buildx build --platform=<targetPlatform> -t <module>:dev <modulePath>`
2. Run **fix-dockerfile** to lint and remediate the file against the built-in security and best-practice rules.
3. Once validated, scan the built image and proceed to `generate-k8s-manifests`.If the result was an enhancement, also include this section between Plan and Env vars:
### Changes from existing
- **Preserve:** <bullet list>
- **Improve:** <bullet list>
- **Add:** <bullet list>(no placeholders, no <TODO> markers, no truncation).
FROM <image>:latest.insists, refuse and explain why.
apt-get upgrade or apt-get dist-upgrade.root.com.azure.containerizationassist.createdby label, and NEVER replace either attribution label key with org.opencontainers.image.*.it to disk and reference the path. Print only when the user asks or to show a short excerpt.
constraint conflicts with a user instruction, surface the conflict explicitly and ask before proceeding.
| Symptom | Action |
|---|---|
modulePath doesn't exist | Echo the path, ask user to provide a valid one. STOP. |
No language provided and no manifest file at modulePath | Ask the user for the language, OR suggest running analyze-repo first. STOP. |
| Existing Dockerfile is empty or unreadable | Treat as Generation path, note this in Plan. |
| Existing Dockerfile already passes the G4 self-check | Strategy = minor-tweaks; report "no changes required"; leave the file untouched. |
Write to <modulePath>/Dockerfile fails (permissions, read-only FS) | Surface the error and the Dockerfile content for manual save. |
| User has a custom registry / non-standard base image preference | Use the user's choice as base image, still apply all G3 baseline rules. |
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.