Skip to main content

Guide

Scanning GitHub Actions, GitLab CI and Dockerfiles

A pipeline misconfiguration is a vulnerability too — a workflow that checks out a fork's code under pull_request_target can leak repo secrets just as effectively as a SQL injection can leak a database. Here is exactly what gets scanned, which part is on by default and which part you have to switch on, with the real rule IDs behind each.

Two separate rule sets, not one

This is the detail most comparisons skip: Dockerfile hygiene and CI/CD pipeline security are two distinct rule categories with different default states. Getting this wrong means either missing coverage you assumed you had, or being surprised a scan didn't report something.

Dockerfile hygiene — on by default

General Dockerfile rules live under the SECURITY category, enabled in every scan with no configuration:

Rule Severity What it catches
dockerfile_root_user HIGH No USER directive — the container runs as root by default.
dockerfile_copy_all MEDIUM COPY . . pulls the entire build context, including .env, credentials, SSH keys, into the image.
dockerfile_unpinned_base MEDIUM Base image not pinned to a specific version.

CI/CD pipeline rules — opt-in, off by default

This is the part worth being precise about: the 38 rules that inspect .github/workflows/*.yml and .gitlab-ci.yml content — expression injection, permission scoping, unpinned actions, and the rule below — are grouped under a separate CICD category that is disabled by default, the same way dependency scanning needs --with-deps. There is no CLI flag for it; it is a config setting.

Enable it in audit.config.json:

"categories": {
  "cicd": { "enabled": true, "weight": 2 }
}

Set this before the first scan of a project (or clear the rules cache with --no-cache once if toggling it on an already-scanned project) — then run normally.

Rule Severity What it catches
pull_request_target_checkout HIGH pull_request_target combined with a checkout of the fork's head — lets a forked PR run code with write access and secrets.
unpinned_action_version MEDIUM An action referenced by branch or tag (@main, @v4) instead of a full commit SHA.
gha_missing_permissions LOW Workflow has no explicit permissions: block, defaulting to broader access than needed.
docker_latest_tag LOW An image: key in CI YAML pinned to :latest — note: this matches the YAML image:/from: key, not a Dockerfile's FROM line (that's a different, Dockerfile-language rule not currently included).
workflow_not_in_codeowners LOW Workflow files not covered by a CODEOWNERS entry, so changes to CI logic don't require a specific reviewer.

38 rules total in this category — the table above is a sample, not the full list.

One real example, start to finish

❌ Vulnerable
on: pull_request_target
jobs:
  build:
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha }}
✅ Clean
on: pull_request
jobs:
  build:
    steps:
      - uses: actions/checkout@v4

The fix here is not a parameter tweak — it's switching event triggers. pull_request_target runs with the base repo's permissions and secrets even for forked PRs; pull_request does not. If privileged operations are genuinely needed after review, the rule's own guidance is to split them into a separate workflow_run job instead of adding a checkout step to the pull_request_target job.

Frequently asked questions

Why is CI/CD scanning opt-in instead of always on?

The same reason dependency scanning needs --with-deps: not every project has a .github/workflows or .gitlab-ci.yml to begin with, and scanning YAML/pipeline files by default on every project would report zero findings (and cost scan time) for the ones that don't use CI/CD at all. Opt-in keeps the default scan focused on application source code.

Does docker_latest_tag catch a Dockerfile's FROM line?

No — its pattern matches a YAML image: or from: key (the syntax used inside .gitlab-ci.yml job definitions), not a Dockerfile's FROM node:latest line, which has no colon after the instruction. This is a real, verified distinction, not a caveat added for effect — the two syntaxes are different enough that one pattern does not cover both.

Does enabling CICD also scan Dockerfiles differently?

No — Dockerfile hygiene rules (root user, COPY . ., unpinned base image) are in the always-on SECURITY category regardless of the CICD setting. Enabling CICD only adds the pipeline-YAML-specific rules on top.

Is this the same detection as GitHub's own workflow linting?

No — GitHub's own tooling checks workflow syntax validity. These rules check for specific, known-dangerous patterns (privilege escalation via pull_request_target, supply-chain risk from unpinned actions) that a syntactically valid workflow can still contain.

See it on a real pipeline

Open the live demo report — no install, no signup — or enable the CICD category on your own project and scan it.

Open the live report See the CLI reference