Skip to content
🎉 Launch sale:50% off everything over $22 — automatically applied at checkout· ends Aug 2Shop the sale →
DevOps AI ToolKit
Newsletter

Rule catalog

All 50 deterministic checks the auditor runs, grouped by category. Rule set 1.0.0+50r.

Security

DPA-SEC-001 High dockerfile

Container runs as root (no non-root USER)

Why it matters: A root process that escapes the container, or a compromised app, has far more power on the host. Running as an unprivileged user is a baseline hardening control.

Fix: Create and switch to a non-root user in the final stage: `RUN adduser -D app` then `USER app`.

Reference ↗
DPA-SEC-002 High dockerfile

Final stage explicitly runs as root

Why it matters: Explicitly selecting root in the runtime stage removes the isolation benefit of an unprivileged user and is rarely necessary for a running service.

Fix: Drop back to a non-root user after any root-only setup: end the stage with `USER app`.

DPA-SEC-003 High dockerfile

Secret passed through a build `ARG`

Why it matters: ARG values are visible in image history and build logs. Build secrets leak this way constantly.

Fix: Use BuildKit secrets (`RUN --mount=type=secret …`) instead of ARG for credentials.

DPA-SEC-004 High dockerfile

Secret baked into an `ENV`

Why it matters: ENV values are baked into the image and visible via `docker inspect` and image history to anyone who can pull the image.

Fix: Inject secrets at runtime (Docker/Compose secrets, orchestrator secret store), never in the image.

DPA-SEC-005 Critical dockerfile

Sensitive file copied into the image

Why it matters: Copying secrets into an image bakes them into a distributable artifact — anyone who pulls the image gets the credential.

Fix: Remove the file from the build context (add it to `.dockerignore`) and inject the secret at runtime.

DPA-SEC-006 Medium dockerfile

`sudo` installed or used in build

Why it matters: sudo inside a container is an anti-pattern: it enables privilege escalation and is unnecessary when you control the USER directly.

Fix: Perform root-only steps before switching USER; remove sudo from the image.

DPA-SEC-007 Medium dockerfile

`ADD` fetches a remote URL

Why it matters: ADD-from-URL fetches unverified remote content at build time with no checksum, and does not use the build cache well.

Fix: Use `RUN curl -fsSL <url> -o file && echo "<sha> file" | sha256sum -c` or fetch via a package manager.

DPA-SEC-008 Medium dockerfile

World-writable permissions (`chmod 777`)

Why it matters: World-writable files let any process (or attacker) in the container modify code and data, undermining integrity.

Fix: Grant the least permission needed (e.g. `chmod 750`) and set ownership with `COPY --chown`.

DPA-SEC-009 Medium dockerfile

`.git` directory copied into the image

Why it matters: A shipped `.git` leaks full source history, branch names, and sometimes committed secrets, and bloats the image.

Fix: Add `.git` to `.dockerignore`; never COPY it explicitly.

DPA-SEC-101 Critical compose

Service runs in privileged mode

Why it matters: Privileged mode disables almost all container isolation — the container can access every device and effectively act as root on the host. A compromise becomes a host compromise.

Fix: Remove `privileged: true`. Grant only the specific capabilities you need via `cap_add`.

DPA-SEC-102 Critical compose

Docker socket mounted into a container

Why it matters: Access to the Docker socket is root-equivalent on the host: a process with the socket can start a privileged container and take over the machine.

Fix: Avoid mounting the socket. If unavoidable, use a read-only, filtered socket proxy that exposes only the endpoints needed.

DPA-SEC-103 High compose

Sensitive host path bind-mounted

Why it matters: Mounting host system paths lets the container read or modify host configuration and secrets, breaking isolation.

Fix: Mount only the specific sub-path you need, read-only (`:ro`) where possible; prefer named volumes.

DPA-SEC-104 High compose

Service uses host network mode

Why it matters: Host networking removes network namespace isolation: the container shares the host’s interfaces and can bind any port, bypassing Compose’s network controls.

Fix: Use a user-defined bridge network and publish only the ports you need.

DPA-SEC-105 High compose

Service shares host PID or IPC namespace

Why it matters: Sharing the host PID/IPC namespace lets the container see and signal host processes and access host shared memory — a serious isolation break.

Fix: Remove `pid: host` / `ipc: host` unless a specific, reviewed need exists.

DPA-SEC-106 High compose

Dangerous Linux capability added

Why it matters: Powerful capabilities can be used to escape the container or tamper with the host. `SYS_ADMIN` in particular is close to full root.

Fix: Grant the narrowest capability that works, or redesign to avoid it. Never use `cap_add: [ALL]`.

DPA-SEC-107 Low compose

Missing `no-new-privileges`

Why it matters: Without this flag, a setuid binary inside the container can still gain privileges. Setting it is a cheap, broadly-safe hardening default.

Fix: Add `security_opt: ["no-new-privileges:true"]` to the service.

DPA-SEC-108 High compose

Hard-coded secret in environment

Why it matters: Secrets committed in Compose files leak through version control, CI logs, and `docker inspect`. Anyone with the repo has the credential.

Fix: Reference secrets via `${VAR}` from an untracked `.env`, or use Docker/Compose `secrets:` backed by an external store.

DPA-SEC-109 High compose

Database/backend port published to all interfaces

Why it matters: Publishing a database port to 0.0.0.0 exposes it to the network (and often the internet). Backends should be reachable only over the internal Compose network.

Fix: Remove the host port mapping and let other services reach it over the Compose network, or bind to `127.0.0.1` only.

DPA-SEC-110 Low compose

Root filesystem is writable

Why it matters: A read-only root filesystem blocks a large class of tampering and persistence techniques; combine it with tmpfs for the few writable paths a service needs.

Fix: Set `read_only: true` and add `tmpfs:` (or named volumes) for directories that must be writable.

DPA-SEC-201 Medium dockerignore

`.dockerignore` missing sensitive exclusions

Why it matters: An incomplete ignore file still lets secrets (`.env`) and history (`.git`) slip into the image via `COPY . .`.

Fix: Add the missing entries to `.dockerignore` (`.git`, `.env`, `.env.*`, `node_modules`).

Reliability

DPA-REL-001 Medium dockerfile

No HEALTHCHECK defined

Why it matters: Without a health check, orchestrators cannot tell a hung-but-running container from a healthy one, so traffic keeps flowing to a broken instance.

Fix: Add a HEALTHCHECK that probes a real readiness endpoint, e.g. `HEALTHCHECK CMD curl -f http://localhost:8080/health || exit 1`.

Reference ↗
DPA-REL-002 Low dockerfile

HEALTHCHECK explicitly disabled

Why it matters: Disabling the health check removes the orchestrator’s only signal that the container is actually serving.

Fix: Replace `HEALTHCHECK NONE` with a real probe unless a sidecar owns health checking.

DPA-REL-003 Medium dockerfile

Shell-form ENTRYPOINT/CMD breaks signal handling

Why it matters: Shell form runs your process as a child of `/bin/sh -c`, which does not forward SIGTERM — so graceful shutdown and zero-downtime deploys break.

Fix: Use exec (JSON-array) form: `ENTRYPOINT ["node", "server.js"]`.

DPA-REL-004 Medium dockerfile

No default command (CMD/ENTRYPOINT)

Why it matters: An image with no default command relies on callers always supplying one, which is fragile and undocumented.

Fix: Declare the process the image should run with CMD or ENTRYPOINT (exec form).

DPA-REL-101 Medium compose

No restart policy

Why it matters: Without a restart policy a crashed container stays down until someone notices — the opposite of production self-healing.

Fix: Add `restart: unless-stopped` (or `always`) for long-running services.

DPA-REL-102 Medium compose

No healthcheck

Why it matters: Without a health check, `depends_on: condition: service_healthy` cannot work and orchestrators route traffic to not-yet-ready or hung containers.

Fix: Add a `healthcheck:` that probes a readiness endpoint or a CLI check for the service.

DPA-REL-103 Low compose

`depends_on` without health conditions

Why it matters: Short `depends_on` waits for the container to START, not to be READY, so apps routinely crash on boot racing a not-yet-ready database.

Fix: Use the long form with `condition: service_healthy` and give the dependency a healthcheck.

DPA-REL-104 Medium compose

Stateful service without a named volume

Why it matters: Without persistent named storage, a container recreate wipes the data — catastrophic for a database.

Fix: Mount a named volume at the data path (e.g. `pgdata:/var/lib/postgresql/data`) and declare it under top-level `volumes:`.

Performance

DPA-PERF-001 Medium dockerfile

`apt-get update` not chained with `install`

Why it matters: Separate update/install layers cause stale package caches (the classic Docker caching bug) and can pull outdated or missing packages.

Fix: Chain them: `RUN apt-get update && apt-get install -y --no-install-recommends … && rm -rf /var/lib/apt/lists/*`.

DPA-PERF-002 Low dockerfile

Package cache not cleaned

Why it matters: Leftover package lists and caches bloat every downstream layer permanently — they cannot be removed by a later RUN.

Fix: Append cache cleanup to the install layer (`rm -rf /var/lib/apt/lists/*`, `apk --no-cache`, `yum clean all`).

DPA-PERF-003 Low dockerfile

Missing `--no-install-recommends`

Why it matters: Recommended packages pull in extra, often unneeded software that enlarges the image and widens the attack surface.

Fix: Add `--no-install-recommends` to `apt-get install`.

DPA-PERF-004 Low dockerfile

Single-stage build ships build tooling

Why it matters: Compilers, dev headers, and package caches in the final image bloat it and expand the attack surface. A multi-stage build ships only the artifact.

Fix: Split into a `build` stage and a slim runtime stage that `COPY --from=build` only the artifacts.

DPA-PERF-101 Medium compose

No memory limit

Why it matters: An unbounded container can consume all host memory and OOM-kill its neighbours — one leaky service takes down the box.

Fix: Set a memory limit sized to the workload (e.g. `mem_limit: 512m`).

DPA-PERF-102 Low compose

No CPU limit

Why it matters: Without a CPU limit a busy service can starve everything else on the host of CPU time.

Fix: Set a CPU limit (e.g. `cpus: "1.5"`).

Observability

DPA-OBS-001 Low dockerfile

Missing OCI image labels

Why it matters: Standard labels (source, revision, version, description) make images traceable back to code and are used by registries and scanners.

Fix: Add OCI labels, e.g. `LABEL org.opencontainers.image.source="https://github.com/org/repo"`.

Reference ↗
DPA-OBS-101 Low compose

Unbounded container logging

Why it matters: The default json-file driver grows without bound and can fill the host disk, taking every container down with it.

Fix: Configure log rotation: `logging: { driver: json-file, options: { max-size: "10m", max-file: "3" } }`.

DPA-OBS-102 Low compose

Missing service labels

Why it matters: Labels make containers discoverable and groupable by monitoring, log pipelines, and cost tooling.

Fix: Add labels such as `com.example.project`, `environment`, and `owner`.

Maintainability

DPA-MAINT-001 Low dockerfile

Deprecated `MAINTAINER` instruction

Why it matters: MAINTAINER is deprecated in favour of a LABEL, which is structured and queryable.

Fix: Replace with `LABEL org.opencontainers.image.authors="name <email>"`.

DPA-MAINT-002 Low dockerfile

No WORKDIR set

Why it matters: Relying on `/` or an implicit directory makes relative paths ambiguous and encourages accidental writes to the root filesystem.

Fix: Set an explicit `WORKDIR /app` before COPY/RUN steps.

DPA-MAINT-101 Medium dockerignore

Broad `COPY . .` without a `.dockerignore`

Why it matters: Copying everything drags in `.git`, `node_modules`, local env files, and build junk — bloating the image, busting the cache on every change, and risking secret leakage.

Fix: Add a `.dockerignore` excluding `.git`, `node_modules`, `.env*`, build output, and other non-runtime files.

Deployment Readiness

DPA-DEPLOY-001 Medium dockerfile

Development/debug mode in the image

Why it matters: Development servers are single-threaded, insecure, and slow, and debug modes can leak internals. They should never run in production.

Fix: Use the production runtime (e.g. `gunicorn`, `node server.js`, a compiled server) and set `NODE_ENV=production`.

DPA-DEPLOY-002 Low dockerfile

Privileged port exposed while running non-root

Why it matters: Binding ports < 1024 requires extra privileges (CAP_NET_BIND_SERVICE). A non-root process may fail to bind, causing a confusing startup failure.

Fix: Listen on a high port (e.g. 8080) and map it at publish time, or grant the capability explicitly.

DPA-DEPLOY-101 Medium compose

Source-code bind mount (development pattern)

Why it matters: Source bind mounts are a development convenience that, in production, overwrite the image’s code with whatever is on the host — undermining immutable, reproducible deploys.

Fix: Bake code into the image; keep bind mounts in a separate `compose.override.yml` used only for local development.

DPA-DEPLOY-102 Low compose

Fixed `container_name` prevents scaling

Why it matters: A fixed container name means the service cannot be scaled to more than one replica and collides across environments on the same host.

Fix: Remove `container_name` and let Compose name containers from the project + service + replica index.

Supply Chain

DPA-SUPPLY-001 High dockerfile

Base image uses a mutable `latest` tag

Why it matters: Mutable tags make builds non-reproducible: the same Dockerfile can produce different images over time, defeating rollbacks and making incidents hard to reproduce.

Fix: Pin an explicit version tag (e.g. `node:20.11-alpine`) and, ideally, a digest.

Reference ↗
DPA-SUPPLY-002 Medium dockerfile

Base image has no explicit tag

Why it matters: An untagged image is an implicit `latest` — the same reproducibility problem, only less visible.

Fix: Add an explicit version tag to every FROM that pulls a remote image.

DPA-SUPPLY-003 Low dockerfile

Base image is not pinned by digest

Why it matters: Even version tags can be re-pushed. A `@sha256:` digest guarantees the exact image bytes, which matters for supply-chain integrity and reproducible builds.

Fix: Append a digest: `FROM node:20.11-alpine@sha256:…`. Automate updates with a tool like Renovate.

DPA-SUPPLY-004 High dockerfile

Remote script piped straight into a shell

Why it matters: Curl-pipe-shell runs unreviewed, unpinned remote code at build time — a classic supply-chain and tampering risk. There is no integrity check on what gets executed.

Fix: Download to a file, verify a checksum or signature, then execute. Prefer official packages.

DPA-SUPPLY-005 Low dockerfile

`ADD` used where `COPY` is safer

Why it matters: ADD has surprising behaviour (URL fetches, tar auto-extraction). COPY is explicit and predictable; reserve ADD for cases that need its features.

Fix: Replace `ADD <local> <dest>` with `COPY <local> <dest>` unless you rely on ADD’s extraction.

DPA-SUPPLY-101 High compose

Service image uses `latest`

Why it matters: Mutable image tags make deployments non-reproducible and break rollbacks — you can’t redeploy the exact image that was running.

Fix: Pin an explicit version tag (and ideally a digest) for every service image.