agentleFS
MartinKChenMartinKChen/harness-claude-code/skills/pattern-reviewer-container/SKILL.md

pattern-reviewer-container

Skill0 starsChanged 5 months agoMITView on GitHub

Docker / compose audit: Dockerfile is multi-stage (`base`/`build`/`final`); tags pinned (no `:latest`) with `docker scout` clean of fixable MEDIUM+ CVEs; non-root user with writable paths redirected; `.dockerignore` excludes secrets/build outputs; backend entrypoint runs `alembic upgrade head` before the server; no-dep `/healthz` exists; frontend nginx puts API `location` blocks ABOVE the SPA `try_files` fallback. Activate on Dockerfile, compose, or `.dockerignore` diffs.

  • Reads credentials
  • Installs packages

The file

We split this skill into the rules it gives an agent

5 rules. About 0 are common, 0 are distinctive, and 5 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 5

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    After loading this skill, also check $MAIN_ROOT/.claude/memory/patterns/pattern-reviewer-container.md in the consuming project (resolve …

    line 15
  2. Very specific:

    Read every stage of each touched Dockerfile top-to-bottom: stage names (AS), base-image tags, USER lines, ENV/ARG values, COPY order, what …

    line 26
  3. Very specific:

    Run docker scout cves (or Trivy) against the slug-tagged image when one is built.

    line 30
  4. Very specific:

    Use ${VAR} in compose environment: pulling from host env / .env (gitignored).

    line 151
  5. Very specific:

    Use the shape in templates/review-comment.md.

    line 173

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

pattern-reviewer-container

When to activate

  • Reviewing a diff that touches Dockerfile, docker-compose.yaml / .yml, compose.yaml / .yml, .dockerignore, or backend entrypoint scripts.
  • A user says "review the Docker setup / image build / compose wiring".

Project memory overlay

After loading this skill, also check $MAIN_ROOT/.claude/memory/patterns/pattern-reviewer-container.md in the consuming project (resolve MAIN_ROOT="$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")"). If present, load it as an additive overlay to the rules below; if absent, skip silently. See memory-convention for the full contract (additivity, severity floor, conflict surfacing).

Iron rules

  • >80% confidence filter. Report only when you are >80% confident. Consolidate similar findings.
  • Cite path/to/file.ext:line. Quote the offending snippet in a BAD block; show the fix in a GOOD block.
  • Severity is load-bearing. CRITICAL / HIGH block the gate; MEDIUM / LOW are informational. Use the per-pattern severity assigned below. The banding: CRITICAL = secret exposure; HIGH = the container fails, races, or leaks in production; MEDIUM = drift that bites later (lost SIGTERM, LAN-exposed port); LOW = build-cache / ergonomics advice that never blocks.
  • Never refer to a finding as #N — GitHub auto-links those to issues. Use a non-numeric handle (quoted title, F1 / F2, Finding 1).

Where to look

  • Read every stage of each touched Dockerfile top-to-bottom: stage names (AS), base-image tags, USER lines, ENV/ARG values, COPY order, what lands in final.
  • ls next to the Dockerfile for .dockerignore; open it and diff against the exclusion list below.
  • In compose files scan ports:, environment:, volumes:, and the migrate service wiring.
  • Open the entrypoint script (whatever ENTRYPOINT names): migration step, exec on the server line.
  • Run docker scout cves (or Trivy) against the slug-tagged image when one is built; quote counts in the finding.
  • For frontend images, open the nginx config and check location order against the BAD/GOOD below.

Patterns to review

Multi-stage build (HIGH)

  • Dockerfile has at least three stages: base, build, final (use AS aliases).
  • Single-stage Dockerfile → flag.
  • final stage carries build tools / dev deps → flag (ship a minimal runtime).

Pinned tags + docker scout (HIGH)

  • :latest anywhere → flag.
  • Pinned major-only tag (node:20) → flag; use full immutable tag (node:20.11.1-alpine).
  • docker scout cves <image> reports MEDIUM+ CVEs that have a fix available → flag; bump to the fixed tag.
  • Production base images can additionally use digest pinning (@sha256:...) — informational.

Non-root user (HIGH)

  • final stage ends with USER <name> (not USER root or no USER) → flag.
  • Process tries to write to root-owned paths (/run/nginx.pid, /var/cache/nginx, /var/run/...) → HIGH. Fixes:
    1. Redirect writable paths to /tmp/... via server config — for nginx, pid /tmp/nginx.pid; + client_body_temp_path /tmp/client_body, etc.
    2. Recursive chown -R app:app /usr/share/nginx/html (or equivalent) in the build stage BEFORE USER.
  • The container starts but dies on first write attempt ([emerg] open() "/run/nginx.pid" failed (13: Permission denied) or equivalent) → flag.

No in-image virtualenvs (MEDIUM)

  • RUN uv venv or python -m venv .venv inside a Dockerfile → flag.
  • uv pip install --system -r requirements.txt (system site-packages) → correct.

.dockerignore mandatory (HIGH)

  • No .dockerignore next to the Dockerfile → flag.
  • .dockerignore doesn't exclude: .git, node_modules, .env*, dist/, build/, coverage/, *.log, .vscode, .idea → MEDIUM.
  • .env accidentally in the build context (not excluded) → CRITICAL (potential secret leak into image history).

Layer ordering (LOW)

  • COPY . . before COPY package*.json + npm ci → flag (dep layer cache never hits on source edits).
# BAD — any source edit invalidates the npm ci layer
COPY . .
RUN npm ci

# GOOD — dep layer cached until the manifests change
COPY package*.json ./
RUN npm ci
COPY . .

Backend entrypoint (HIGH)

  • Backend image owns its DB schema but no migration step in entrypoint → flag.
  • Migration step in CMD instead of ENTRYPOINT → flag (CMD is the server).
  • Migration step in a FastAPI startup hook instead of the entrypoint → flag (the app accepts traffic against a stale schema; N replicas race).
  • Migration CLI (alembic) missing from final stage even though entrypoint calls it (command not found: alembic) → flag; copy the CLI from the build stage or install it in final.
  • Missing exec on the server line in the entrypoint (uvicorn ... instead of exec uvicorn ...) → MEDIUM (SIGTERM doesn't forward to the server).
# BAD — server runs as a child of the shell; SIGTERM stops at the shell
#!/bin/sh
alembic upgrade head
uvicorn app.main:app --host 0.0.0.0

# GOOD — migrations first, then the server replaces the shell
#!/bin/sh
set -e
alembic upgrade head
exec uvicorn app.main:app --host 0.0.0.0

/healthz endpoint (HIGH)

  • Backend exposes no /healthz route → flag; the pre-push hook's container:smoke-health probe can't pass.
  • /healthz requires auth → flag.
  • /healthz touches the DB or an external API → flag; move that to /readyz.
  • /healthz takes >100ms → flag.

Frontend nginx ordering (HIGH)

# BAD — try_files catches /api/* too; backend POSTs receive index.html
server {
  root /usr/share/nginx/html;
  index index.html;
  location / {
    try_files $uri $uri/ /index.html;
  }
  location /api/ {
    proxy_pass http://backend:8000;
  }
}

# GOOD — API location blocks ABOVE the SPA fallback
server {
  location = /healthz {
    proxy_pass http://backend:8000/healthz;
  }
  location /api/ {
    proxy_pass http://backend:8000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
  }
  root /usr/share/nginx/html;
  index index.html;
  location / {
    try_files $uri $uri/ /index.html;
  }
}
  • try_files $uri $uri/ /index.html missing → flag (any SPA route 404s on direct load).
  • try_files without $uri/ → flag (directory-path requests 404).
  • The pre-push hook's container:smoke-api-proxy probe detects this exact misconfig by Content-Type (text/html on an API probe = SPA catch-all intercepted).

Secrets in image (CRITICAL)

  • ENV API_KEY=... / ARG API_KEY=... / COPY .env ./ in Dockerfile → CRITICAL.
  • Hardcoded values in docker-compose.yaml environment: (committed) → CRITICAL.
  • Use ${VAR} in compose environment: pulling from host env / .env (gitignored).

Networking (MEDIUM)

  • ports: on internal services (DB, cache, queue) that the host doesn't need → flag; use expose: (or omit, since compose networks allow all ports between services).
  • Publishing a dev-only port without binding to 127.0.0.1 (e.g. "5432:5432" instead of "127.0.0.1:5432:5432") → flag (reachable on the LAN).

Volumes (LOW)

  • Named volume for persistent state present and declared at top level → correct.
  • Bind mount over an in-image path that has generated content (e.g. node_modules) without an anonymous-volume shield → flag.

Templates

AssetPurpose
templates/DockerfileThree-stage Node Dockerfile reference; compare the project's Dockerfile against this.
templates/docker-compose.yamlapp + db services with healthcheck + named volume reference.
templates/.dockerignoreExclusions reference for .dockerignore audits.

Constructing the finding

Use the shape in templates/review-comment.md.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.