decolint

module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 12, 2026 License: MIT

README

decolint

CI Attestation Checks

decolint is a linter for Dev Container configuration files. Following file types are supported:

  • Dev Container definition (devcontainer.json)
  • Feature (devcontainer-feature.json)
  • Template (devcontainer-template.json)

It checks for common mistakes, security issues, and best practices in these files. See Rules for the list of checks decolint performs.

Installation

decolint can be installed as a prebuilt binary, as a container image, or from source with Go.

Download a prebuilt binary from the releases page. Release artifacts are signed and carry build provenance; see Verifying release artifacts.

Docker
docker run --rm -v "$PWD:/workspace" ghcr.io/bare-devcontainer/decolint [directory ...]

Images are published for linux/amd64 and linux/arm64 and tagged latest, <major>, <major>.<minor>, and <major>.<minor>.<patch>. They carry the same build provenance attestation as the binaries; see Verifying release artifacts.

Install with Go
GOEXPERIMENT=jsonv2 go install github.com/bare-devcontainer/decolint/cmd/decolint@latest

GOEXPERIMENT=jsonv2 is required because decolint uses the still experimental encoding/json/v2 standard library package.

Usage

decolint [directory ...]

Each directory is detected as one of the following based on its layout, and the configuration files it contains are linted:

  • Dev container definition — the devcontainer.json files at the locations defined by the devcontainer specification: .devcontainer/devcontainer.json, .devcontainer.json, and .devcontainer/<folder>/devcontainer.json
  • Feature (contains devcontainer-feature.json) — that file
  • Template (contains devcontainer-template.json) — that file, plus the dev container configuration the template ships

With no arguments, the current directory is linted.

decolint supports the following flags; run decolint -help for the full list.

Flag Description
-platform comma-separated list of platforms to also lint against (see Target platforms)
-format output format: text (default), json, or github (see Output formats)
-deny-warnings also exit non-zero on warn-severity findings (see Exit codes)
-config path to a config file overriding rule severities (see Config file)
Target platforms

Each rule optionally targets specific platforms (vscode, codespaces, ...); a rule with no target platform applies to every platform and always runs. By default, only those platform-agnostic rules run; pass -platform with a comma-separated list to also run rules scoped to specific platforms:

decolint -platform=vscode,codespaces
Output formats

By default, findings are printed one per line, with the rule's severity (error or warn):

.devcontainer/devcontainer.json:4:12: warn: image "ubuntu:latest" uses the "latest" tag; pin a specific version (no-image-latest)

Pass -format to select a different output format:

  • text (default) — the one-line-per-finding format shown above.
  • json — a JSON array of finding objects, for scripting:
    [{"path":".devcontainer/devcontainer.json","line":4,"col":12,"ruleId":"no-image-latest","message":"image \"ubuntu:latest\" uses the \"latest\" tag; pin a specific version","severity":"warn"}]
    
  • githubGitHub Actions workflow commands, so findings show up as inline annotations on pull request diffs when decolint is run from a GitHub Actions workflow:
    ::warning file=.devcontainer/devcontainer.json,line=4,col=12,title=no-image-latest::image "ubuntu:latest" uses the "latest" tag; pin a specific version
    
Exit codes
  • 0 — no error-severity findings (there may still be warn findings)
  • 1 — at least one error-severity finding was reported
  • 2 — an error occurred (e.g. a file could not be parsed)

Pass -deny-warnings to also fail (exit code 1) on warn-severity findings. Exit codes are unaffected by -format.

Config file

Rule severities can be overridden per project with a JSON/JSONC config file:

// .decolint.jsonc
{
  "rules": {
    "no-image-latest": "error",
    "pin-image-digest": "warn",
    "require-non-root": "off"
  }
}

Each entry under rules overrides that rule's default severity to error, warn, or off (see Rules for the list of rule IDs and their defaults); rules with no entry keep their default.

decolint looks for .decolint.jsonc, then .decolint.json, in the current directory; the first one found is used. Pass -config <path> to use a file at a different location instead. It is an error (exit code 2) if -config points at a file that doesn't exist or fails to parse, or if the config references an unknown rule ID. If no -config flag is given and neither default file exists, decolint proceeds with every rule at its default severity.

Rules

Each rule has a default severity, either error or warn. A rule can also be set to off to disable it entirely. Severities can be overridden per project with a config file.

Each rule also optionally targets specific platforms (see Target platforms); a rule with no target platform applies to all platforms.

ID Platform Default severity Description
id-dir-mismatch (all) error disallow a Feature's or Template's id that does not match the name of its containing directory
invalid-semver (all) error disallow a Feature's or Template's version that is not a valid semantic version
missing-build-dockerfile (all) error disallow a devcontainer.json build object that is missing dockerfile
missing-compose-service (all) error disallow a devcontainer.json that sets dockerComposeFile without service
missing-container-def (all) error disallow a devcontainer.json that defines none of image, build, or dockerComposeFile
missing-required-props (all) error disallow a Feature's or Template's metadata that is missing a required property (id, version, or name)
missing-workspace-mount-folder (all) error disallow a devcontainer.json using image or build that sets only one of workspaceMount or workspaceFolder
no-app-port (all) warn disallow the legacy appPort property in favor of forwardPorts
no-cap-add-all (all) warn disallow granting all Linux capabilities via an ALL entry in a devcontainer.json's or Feature's capAdd property, or a --cap-add=ALL entry in a devcontainer.json's runArgs
no-docker-socket-mount (all) warn disallow bind-mounting the host's Docker socket via a devcontainer.json's mounts or runArgs, which grants the container root-equivalent control over the host
no-image-latest (all) warn disallow container images without an explicit tag or with the latest tag
no-privileged-container (all) warn disallow running the container in privileged mode via a devcontainer.json's or Feature's privileged property, or a --privileged entry in a devcontainer.json's runArgs
no-seccomp-unconfined (all) warn disallow disabling seccomp confinement via a devcontainer.json's or Feature's securityOpt property, or a --security-opt seccomp=unconfined entry in a devcontainer.json's runArgs
pin-feature-version (all) warn disallow a Feature reference without an explicit version or with the latest version
no-seccomp-override (all) off disallow overriding the container runtime's default seccomp profile via a devcontainer.json's or Feature's securityOpt property, or a --security-opt seccomp=... entry in a devcontainer.json's runArgs
pin-image-digest (all) off disallow an image property that does not pin the image by content digest (e.g. image@sha256:...)
require-cap-drop-all (all) off require an ALL entry in a devcontainer.json's capDrop property, or a --cap-drop=ALL entry in runArgs, dropping every Linux capability
require-no-new-privileges (all) off require no-new-privileges to be set via a devcontainer.json's securityOpt property, or a --security-opt no-new-privileges... entry in runArgs
require-non-root (all) off require remoteUser or, if unset, containerUser to be set to a non-root user
codespaces-no-host-port-format codespaces error disallow host:port entries in forwardPorts and portsAttributes, which GitHub Codespaces does not support
codespaces-no-bind-mount codespaces warn disallow bind type entries in mounts, which GitHub Codespaces silently ignores except for the Docker socket
pin-extension-version vscode, codespaces warn disallow a customizations.vscode.extensions entry without an explicit pinned version

Suppressing findings

Findings can be suppressed with comments in the configuration files:

  • decolint-ignore-line — suppress findings on the same line, typically as a trailing comment
  • decolint-ignore-next-line — suppress findings on the next line
  • decolint-ignore-file — suppress findings in the whole file

Each directive optionally takes rule IDs, separated by commas or spaces; omitting them suppresses all rules. Block comments (/* ... */) work the same way.

// decolint-ignore-file no-app-port
{
  // decolint-ignore-next-line no-image-latest
  "image": "ubuntu:latest",
  "privileged": true // decolint-ignore-line
}

Verifying release artifacts

Each release includes a decolint-<version>-checksums.txt file listing the SHA-256 checksum of every binary, plus a decolint-<version>-checksums.txt.sigstore.json file: a Sigstore bundle containing the cosign keyless signature (signed via GitHub Actions OIDC) and its Rekor transparency log entry.

To verify a downloaded binary:

cosign verify-blob \
  --bundle decolint-<version>-checksums.txt.sigstore.json \
  --certificate-identity-regexp '^https://github\.com/bare-devcontainer/decolint/\.github/workflows/release\.yml@.*$' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  decolint-<version>-checksums.txt

sha256sum --ignore-missing -c decolint-<version>-checksums.txt

The first command confirms the checksums file was signed by this repository's release workflow; the second confirms the downloaded binary matches a checksum in that file.

Each binary's provenance can also be verified with gh attestation verify, using the build provenance attested during the release:

gh attestation verify decolint_<version>_<os>_<arch>.tar.gz \
  --repo bare-devcontainer/decolint

The container image carries the same kind of attestation:

gh attestation verify oci://ghcr.io/bare-devcontainer/decolint:<version> \
  --repo bare-devcontainer/decolint

Contributing

Rules are plain Go code and new ones are easy to add; see CONTRIBUTING.md for the development workflow and a walkthrough of adding a rule.

License

MIT

Directories

Path Synopsis
cmd
decolint command
Command decolint lints devcontainer configuration files (devcontainer.json and friends).
Command decolint lints devcontainer configuration files (devcontainer.json and friends).
Package format implements the output formats decolint can write lint issues in: one-line-per-issue text, a JSON array, and GitHub Actions workflow command annotations.
Package format implements the output formats decolint can write lint issues in: one-line-per-issue text, a JSON array, and GitHub Actions workflow command annotations.
Package linter implements the decolint engine: it determines what kind of devcontainer directory a path is (a dev container definition, a Feature, or a Template), locates the configuration files it contains, parses them as HuJSON (JSONC), runs lint rules against the syntax tree, and filters findings suppressed by ignore comments.
Package linter implements the decolint engine: it determines what kind of devcontainer directory a path is (a dev container definition, a Feature, or a Template), locates the configuration files it contains, parses them as HuJSON (JSONC), runs lint rules against the syntax tree, and filters findings suppressed by ignore comments.
Package rules provides the built-in lint rules bundled with decolint.
Package rules provides the built-in lint rules bundled with decolint.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL