Skip to content
BETAGuardrails are in beta. The library, the configuration format and the CLI command can still change.

Guardrails / Collectors

Dockerfiles

Every Dockerfile in the tree, parsed without a tool: the stages it builds, the image each one starts from and how that image is pinned, the user the image ends as, the ports it exposes, the paths it copies in and the names of the build arguments and environment variables it declares, enriched with the findings of a hadolint report when the build already left one behind.

Facts keydocker
Versionv1
Scriptdocker.py
Timeout60 seconds

Inputs

InputDescriptionDefaultEnvironment
dockerfilesComma separated globs, matched against the path and the name of every Dockerfile found anywhere in the working directory, narrowing which of them are described. Discovery itself is fixed, so a glob here only ever takes files away.Dockerfile,Dockerfile.*,*.DockerfileGUARDRAIL_INPUT_DOCKERFILES
reportsComma separated globs naming the hadolint SARIF reports to read. A report the build produced is read, never rerun, and a file that is no SARIF document is passed over.hadolint*.sarif,hadolint*.json,*hadolint*.sarifGUARDRAIL_INPUT_REPORTS
maxFindingsMost lint findings to carry in the facts. lint.total stays whole when findings are dropped.200GUARDRAIL_INPUT_MAXFINDINGS

A guardrail that declares an input of the same name passes it through, so the guardrail's configuration in buildnote.json is what decides these values.

A guardrail asks for these facts by name, and reads them back the same way:

json
{
  "collect": ["docker"]
}
python
docker = guardrail.facts("docker")

Facts

The document docker collects. A path carrying [] is an entry of the list before it, and one carrying [path] is a key of the object before it.

FactMeaning
dockerfilesEvery Dockerfile that was parsed, in path order. Parsing is the collector's own, so these facts are there whether or not a linter ever ran.
dockerfiles[].pathPath of the Dockerfile, relative to the directory the CLI runs in.
dockerfiles[].stagesEvery stage the file builds, in the order its FROM instructions declare them.
dockerfiles[].stages[].nameName the stage was given with AS, or null when it was given none.
dockerfiles[].stages[].imageThe FROM argument exactly as it is written, so a stage based on $BASE_IMAGE or on an earlier stage reads as it does in the file.
dockerfiles[].stages[].registryRegistry host the image names, or null when it names none and Docker Hub is implied.
dockerfiles[].stages[].repositoryRepository within that registry, without the tag or the digest.
dockerfiles[].stages[].tagTag the FROM names, or null when it names none, which Docker resolves as latest.
dockerfiles[].stages[].digestDigest the FROM pins the image to, or null when it pins none.
dockerfiles[].stages[].platformPlatform given with --platform, or null when the stage builds for the host's.
dockerfiles[].userArgument of the last USER instruction, or null when the file sets none and the image runs as root.
dockerfiles[].exposedPortsEvery port EXPOSE declares, as written, so 8080/tcp keeps its protocol.
dockerfiles[].healthcheckWhether the file declares a HEALTHCHECK. HEALTHCHECK NONE disables one, and reads as false.
dockerfiles[].buildArgsNames of the arguments ARG declares. Names only, never the defaults beside them, because a default can be a credential and these facts are evidence.
dockerfiles[].envKeysNames of the variables ENV sets. Names only, never the values beside them, for the same reason.
dockerfiles[].copiedPathsEvery source path COPY and ADD name, without the destination they are copied to.
dockerfiles[].instructionsHow many times each instruction appears, by instruction name.
dockerfiles[].unparsedWhich lines could not be read as an instruction, present only when a line could not be. Line numbers only, never what the line said.
sourcereport when a hadolint report was read, none when there was none. The Dockerfile facts above are collected either way.
reasonWhy no findings were read, present only when source is none.
reportsEvery hadolint report that was read, in path order.
reports[].pathPath of the report, relative to the directory the CLI runs in.
reports[].modifiedWhen the report was last written, ISO 8601, so a stale report is recognisable.
reports[].formatFormat it was read as, always sarif.
reports[].producer.nameLinter that wrote it, as the report names itself, or null when it names none.
reports[].producer.versionVersion of that linter, or null when the report carries none.
lint.findingsThe findings the reports carried, up to maxFindings.
lint.findings[].idRule the finding breaks, such as DL3008.
lint.findings[].severitycritical, high, medium, low, info or unknown, as the report rated it.
lint.findings[].messageWhat the linter said, truncated to 500 characters.
lint.findings[].pathDockerfile it was found in, or null.
lint.findings[].lineLine it was found on, or null.
lint.totalFindings across every report that was read, whole even when findings were dropped.
lint.droppedFindings left out because maxFindings was reached.

A collector that cannot finish prints what it had along with an incomplete key saying why, so the facts below the point it stopped are absent. A check that cares reads incomplete before the rest.

Example facts

The example facts collected by docker collector:

json
{
  "dockerfiles": [
    {
      "path": "Dockerfile",
      "stages": [
        {
          "name": "build",
          "image": "gradle:8.14-jdk21",
          "registry": null,
          "repository": "gradle",
          "tag": "8.14-jdk21",
          "digest": null,
          "platform": null
        },
        {
          "name": "runtime",
          "image": "eclipse-temurin:21.0.3_9-jre-alpine@sha256:4f2b3c1e9a0d5c7b8e6f1a2d3c4b5a69788899aabbccddeeff00112233445566",
          "registry": null,
          "repository": "eclipse-temurin",
          "tag": "21.0.3_9-jre-alpine",
          "digest": "sha256:4f2b3c1e9a0d5c7b8e6f1a2d3c4b5a69788899aabbccddeeff00112233445566",
          "platform": null
        }
      ],
      "user": "widget",
      "exposedPorts": [
        "8080/tcp"
      ],
      "healthcheck": true,
      "buildArgs": [
        "WIDGET_VERSION"
      ],
      "envKeys": [
        "WIDGET_PORT",
        "JAVA_TOOL_OPTIONS"
      ],
      "copiedPaths": [
        "settings.gradle.kts",
        "build.gradle.kts",
        "service",
        "/home/gradle/widget/service/build/install/service"
      ],
      "instructions": {
        "FROM": 2,
        "WORKDIR": 2,
        "COPY": 3,
        "RUN": 2,
        "ARG": 1,
        "ENV": 1,
        "EXPOSE": 1,
        "USER": 1,
        "HEALTHCHECK": 1,
        "ENTRYPOINT": 1
      }
    }
  ],
  "source": "none",
  "reason": "no hadolint report matched hadolint*.sarif, hadolint*.json, *hadolint*.sarif",
  "reports": []
}

Collected for

GuardrailCategoryInputs
iac/dockerfile-base-pinnediacnone
iac/dockerfile-healthcheckiacnone
iac/dockerfile-lint-cleaniacnone
iac/dockerfile-no-build-secretsiacnone
iac/dockerfile-nonroot-useriacnone

All collectors

Buildnote Limited
Registered in England and Wales, Reg: 16140412