feat(rule): add rules for unpinned images in Compose and Dockerfile - #95
Open
nozaq wants to merge 1 commit into
Open
feat(rule): add rules for unpinned images in Compose and Dockerfile#95nozaq wants to merge 1 commit into
nozaq wants to merge 1 commit into
Conversation
The reproducibility rules only looked at a devcontainer.json's "image", "features", and "customizations", so the same moving reference went unreported wherever else it is written: a Dockerfile-based or Compose-based configuration escaped image pinning entirely, and a Feature's own dependencies were never checked. - no-dockerfile-image-latest and pin-dockerfile-image-digest read the Dockerfile named by "build.dockerfile" (or the legacy "dockerFile") and judge each FROM. A reference to an earlier stage, "scratch", and one containing a variable are left out: none names an image the configuration pins. - no-compose-image-latest reads the "image" of the Compose service the dev container runs in. A service that builds its own image, an image written as a variable, and a service no declared file defines are left out. - pin-depends-on-version checks a Feature's "dependsOn", where an unpinned reference installs a moving dependency into every project using the Feature, with no way for those projects to pin it. - pin-feature-exact-version requires a full "major.minor.patch", since the "major" and "major.minor" tags are reassigned on release. It stands to pin-feature-version as pin-image-digest stands to no-image-latest. A file another configuration file names is read through the directory the linted file was discovered in, so a path leading outside that boundary reports nothing rather than reaching for it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y7ohBnPuPSezRvwrkzaRaA
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds two new linting rules to detect unpinned container images in Compose-based and Dockerfile-based devcontainer configurations, complementing the existing
no-image-latestrule for direct image references.Key Changes
no-compose-image-latestrule: Reports when a Compose service (the one the dev container runs in) uses an image without an explicit tag or with the "latest" tag. Reads and parses referenced Compose files to extract the service's image configuration.no-dockerfile-image-latestrule: Reports when a Dockerfile that a devcontainer.json builds from has FROM instructions naming images without explicit tags or with "latest". Parses Dockerfile syntax to extract base image references.Shared utilities in
util.go:readConfigFile(): Safely reads referenced configuration files (Compose, Dockerfile) with size limits and path validationociFeatureRefs(): Extracts OCI Feature references from configuration objectsisLocalFeature()andisTarballFeature(): Helpers to identify Feature references that don't need version pinningunpinnedFeatureVersion(): Generates consistent messaging for unpinned Feature versionsdockerfile.go: New module providing Dockerfile parsing utilities:dockerfileRef(): Locates the Dockerfile path from devcontainer.json (handles both top-leveldockerFileand nestedbuild.dockerfile)dockerfileBaseImages(): Extracts base images from Dockerfile, filtering out stage references, scratch, and variable referencesdockerfileBuildImages(): Combines the above to get images with their source locationRefactored
pin_feature_version.go: Extracted common Feature reference handling logic into shared utilities inutil.goto reduce duplication and support the newpin-depends-on-versionandpin-feature-exact-versionrules.Comprehensive test coverage: Added test suites for all new rules covering edge cases like missing files, parse errors, variable references, and file merging behavior.
Implementation Details
Both new image rules follow the same pattern as
no-image-latest: they report at the configuration property that declares the image source (the Compose file path or Dockerfile path), making the finding location clear even though the fix belongs in the referenced file.Compose file handling respects the merge semantics: multiple files are read in order with later ones overriding earlier ones, matching Docker Compose's behavior.
Dockerfile parsing uses the buildkit parser to properly handle multi-stage builds, filtering out stage-to-stage references and variable substitutions that cannot be statically resolved.
File reading is bounded by
maxConfigFileBytes(4 MB) to prevent issues with truncated configuration analysis, and paths are validated to prevent directory traversal.https://claude.ai/code/session_01Y7ohBnPuPSezRvwrkzaRaA