Skip to content

Enforce Debian 13 across maintained paths#460

Closed
jeremi wants to merge 35 commits into
mainfrom
agent/debian13-boundary
Closed

Enforce Debian 13 across maintained paths#460
jeremi wants to merge 35 commits into
mainfrom
agent/debian13-boundary

Conversation

@jeremi

@jeremi jeremi commented Jul 23, 2026

Copy link
Copy Markdown
Member

What changed

  • Move the maintained registryctl tutorial builder from Bookworm to the exact digest-pinned Trixie builder used by release workflows.
  • Extend the explicit Debian 13 contract to 14 project-owned image and image-policy surfaces, including the tutorial script and its CI cache step.
  • Discover every immediate root workflow and use bounded structured YAML traversal for exact container, image, source_image, target_image, restore_image, and uses: docker:// values. New workflow images are denied unless path, semantic role, and value are explicitly inventoried. Notary source, target, and restore roles have separate exact Alpine sets; Relay retains only its exact image exception.
  • Require the exact two-stage, multi-architecture Dockerfile contracts and effective Distroless runtime directives for Relay and Notary, including canonical health, entrypoint, workdir, and command behavior with no USER or VOLUME override.
  • Normalize bounded Dockerfile logical instructions, require each of the five maintained Dockerfiles to match its exact reviewed RUN and final-stage COPY inventories, and reject COPY --from sources unless they are a declared local stage or an exact reviewed BuildKit named context. Alternate escape directives and unterminated continuations fail closed.
  • Bind the known release, tutorial, and live-journey builder definitions, handoffs, and Docker command prefixes to the approved immutable image.
  • Move the Relay live journey from the floating Debian-default postgres:16 image to the exercised postgres:16.13-alpine image and bind its assignment and both consumers.
  • Add the Notary PostgreSQL conformance script to the maintained boundary with exact active source, target, restore, unsupported-image, direct-literal, and environment-fallback inventories.
  • Reject Bookworm and bounded Debian 12 support/base spellings in inventoried surfaces and dynamically discovered root workflows.
  • Key the tutorial build cache by the tutorial builder script and Cargo.lock, with no fallback that can restore artifacts built by an older base image.
  • Run the contract and its focused mutation suite in CI, with gate-inventory protection.

Why

Issue #380 requires an unambiguous Debian 13 boundary for project-owned 1.0 images and maintained builders. The implementation uses explicit known-surface contracts plus bounded PyYAML traversal of workflow image fields and bounded logical Dockerfile instruction handling. It does not attempt to parse arbitrary Docker, shell, Markdown, or repository YAML, matching the issue's non-goal of creating a generic container-base abstraction.

Implementer impact

Contributors get focused failures when a governed image surface changes its base, stage shape, effective runtime behavior, builder handoff, workflow image inventory, external copy source, live-journey database image, or cache binding. Equivalent bounded forms such as canonical readonly/export shell assignments, reviewed lowercase COPY --from=/--chown= options, Dockerfile continuations, and quoted YAML cache keys remain valid. Maintained RUN, runtime-copy, workflow-role, or active PostgreSQL-default changes require an explicit policy update and review. New root workflow image values require an explicit reviewed path-and-value policy entry.

Validation

  • Debian 13 checker: 45 focused test groups and live repository scan
  • Gate inventory: 44 tests and 82 required gates
  • Full release-script discovery: 306 tests
  • Registryctl tutorial helper tests and dry-run
  • Full source tutorial journey in the exact Trixie builder, including Relay and Notary live flows and expected fail-closed recovery
  • Docs test suite
  • OCI label smoke and release manifest/source-model checks
  • Ruff, Python compilation, actionlint, shell syntax, targeted ShellCheck, and git diff --check
  • Independent reviews of the fixed-surface image-position matrix, workflow YAML variants, Dockerfile logical instruction and source inventory, live-journey bindings, and full bounded contract matrix

Issue status

Progresses #380 but does not close it. Closure still requires exact-candidate scans, vendor PKCS#11 module-loading proof, Relay/Notary runtime acceptance, the released Solmara implementer smoke, and the remaining lifecycle documentation.

jeremi added 3 commits July 23, 2026 15:53
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
@jeremi
jeremi marked this pull request as ready for review July 23, 2026 13:58

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: db99552c98

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +209 to +213
return (
image_name == "debian"
or "debian" in image_name
or any(marker in lowered_tag for marker in generation_tags)
or re.search(r"(?:^|[-_.])debian-?1[0-9](?:$|[-_.])", lowered_tag)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject version-only tags for Debian-default images

When a maintained script or YAML uses a mutable version-only tag such as rust:1.95, python:3.13, or node:22, this predicate returns false because it only treats the repository name debian or tags that spell out trixie/bookworm/debianNN as Debian-derived. The same image families are already listed in DEBIAN_DEFAULT_IMAGE_NAMES for bare references, so adding a tagged default builder can bypass the new digest gate even though it is still Debian-derived and unpinned.

Useful? React with 👍 / 👎.

Comment on lines +270 to +273
if action == "image" and action_index + 1 < len(tokens):
action_index += 1
action = tokens[action_index]
if action not in CONTAINER_COMMANDS:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle Docker container subcommands in the scan

The scanner skips the docker image ... management subcommand but not docker container ...; Docker's CLI reference documents docker container run [OPTIONS] IMAGE... as a supported form (https://docs.docker.com/reference/cli/docker/container/run/). With tokens like docker container run --rm debian, this branch leaves action == "container" and returns None, so a maintained script can still use a bare Debian image without the new CI gate failing.

Useful? React with 👍 / 👎.

Comment on lines +101 to +104
IMAGE_ASSIGNMENT_RE = re.compile(
r"^\s*(?:export\s+)?"
r"(?P<name>[A-Za-z_][A-Za-z0-9_-]*)\s*(?:=|:)\s*"
r"(?P<reference>[^#\s]+)",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Catch bare images in JS declarations

In maintained JS/TS script surfaces, a common declaration like const BUILDER_IMAGE = "rust"; is not matched here because the regex only accepts assignments that start directly with the variable name, and the generic container-reference regex cannot see bare names without a tag. That lets the exact bare implicit-latest image assignment this checker is meant to reject bypass the new CI gate whenever the script uses const/let/var declarations.

Useful? React with 👍 / 👎.

RELAY_TUTORIAL="$SITE_ROOT/src/content/docs/tutorials/publish-spreadsheet-secured-registry-api.mdx"
NOTARY_TUTORIAL="$SITE_ROOT/src/content/docs/tutorials/verify-claim-registry-api.mdx"
BUILDER_IMAGE="rust:1.95-bookworm@sha256:4c2fd73ef19c5ef9d54bee03b06b2839a392604fbfcd578ed948b71b37c1d7fb"
BUILDER_IMAGE="rust:1.95-trixie@sha256:f49565f188ee00bc2a18dd418183f2c5f23ef7d6e691890517ed341a598f67c3"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Key the tutorial cache by builder image

After this line moves the tutorial build container from Bookworm to Trixie, the registryctl-tutorials workflow still restores target/registryctl-tutorial-linux-amd64 with the unchanged cache key registryctl-tutorial-${{ runner.os }}-rust-1.95.0-... in .github/workflows/ci.yml. On a cache hit from before this change, Cargo can consider the existing binaries fresh because the source and Rust version did not change, so this tutorial gate can keep packaging Bookworm-built artifacts instead of exercising the new Debian 13 builder until the cache naturally misses.

Useful? React with 👍 / 👎.

Comment on lines +283 to +286
if "=" in candidate or candidate in CONTAINER_BOOLEAN_OPTIONS:
index += 1
else:
index += 2

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Treat Docker boolean flags as valueless

The option skipper treats every unknown docker run flag without = as consuming the next token, but Docker documents boolean run flags such as --no-healthcheck, --oom-kill-disable, and --sig-proxy (https://docs.docker.com/reference/cli/docker/container/run/). A maintained command like docker run --no-healthcheck debian ... therefore skips debian as if it were an option argument and returns no image reference, letting another bare Debian image bypass the new gate.

Useful? React with 👍 / 👎.

Comment on lines +320 to +324
line_start = text.rfind("\n", 0, match.start()) + 1
line_end = text.find("\n", match.end())
if line_end < 0:
line_end = len(text)
return SCRIPT_IMAGE_CONTEXT_RE.search(text[line_start:line_end]) is not None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve image-assignment context across wrapped strings

For Python/JS/TS maintained scripts, script_reference_is_consumed only checks the physical line containing the matched image reference. If an image constant is wrapped in the style already used by this checker, e.g. BUILDER_IMAGE = (\n "rust:1.95-trixie"\n), the reference line no longer contains image/base/docker, so the unpinned Debian-derived image is ignored even though it is the assigned value the gate is trying to police.

Useful? React with 👍 / 👎.

jeremi added 2 commits July 23, 2026 21:49
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6631ba6072

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +430 to +431
if ":" in reference:
return is_debian_derived(reference) or is_debian_default_version_only(reference)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Parse registry ports before deciding an image is tagged

When a maintained command uses an untagged image from a private registry with a port, such as docker run localhost:5000/debian, this raw colon check treats the registry port as a tag and routes the reference through the tagged parsers, which then see 5000/debian as the tag and return false. Docker supports private-registry references with host:port and omitted tags default to latest (see Docker's image tag and run reference docs), so implicit-latest Debian-derived images from internal registries can bypass the digest gate.

Useful? React with 👍 / 👎.

Comment on lines +323 to +324
if index < len(tokens):
return tokens[index]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Strip shell separators before checking image names

If a maintained shell/YAML run line terminates the Docker command with a same-line shell separator, for example docker run --rm debian; echo ok, shlex.split returns debian; and this branch passes that value through verbatim. The untagged-reference regex then rejects the semicolon-bearing candidate, so a bare Debian image can bypass the new CI gate whenever the command is followed by ; on the same line.

Useful? React with 👍 / 👎.

for relative in DOCKERFILES:
for relative in dockerfiles:
text = texts[relative]
bases = FROM_RE.findall(text)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scan Dockerfile COPY --from image sources

When a maintained Dockerfile copies directly from an external image, for example COPY --from=debian:bookworm /etc/os-release /os-release, Docker supports resolving that --from value as an image source (see the Dockerfile reference at https://docs.docker.com/reference/dockerfile/), but this loop only inspects FROM bases. That leaves another executable Dockerfile image reference able to use retired or unpinned Debian images without the new contract failing.

Useful? React with 👍 / 👎.

Comment on lines +302 to +305
action_index = container_index + 1
if action_index >= len(tokens):
continue
action = tokens[action_index]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip Docker global options before reading subcommands

When a maintained script uses Docker global options, such as docker --context ci run --rm debian true, Docker treats --context as applying before the subcommand (https://docs.docker.com/engine/manage-resources/contexts/), but this parser assumes the token immediately after docker is the action. In that context-specific invocation, action becomes --context, the command is ignored, and the bare Debian image bypasses the digest gate.

Useful? React with 👍 / 👎.

Comment on lines +421 to +423
return sorted(
set(image_assignment_references(relative, text))
| set(command_image_references(relative, text))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Check Docker-image actions in workflows

For maintained GitHub Actions workflows, steps can run a Docker image directly with uses: docker://debian:bookworm (GitHub documents this syntax at https://docs.github.com/actions/using-workflows/workflow-syntax-for-github-actions), but image_references only combines image assignments and Docker/Podman commands. A workflow can therefore introduce a retired or unpinned Debian-derived Docker action without this repository-wide contract reporting it.

Useful? React with 👍 / 👎.

Comment on lines +104 to +106
r"^\s*(?:export\s+)?"
r"(?P<name>[A-Za-z_][A-Za-z0-9_-]*)\s*(?:=|:)\s*"
r"(?P<reference>[^#\s]+)",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Accept shell declaration prefixes on image assignments

Inside shell functions, a maintained script can declare an image with local BUILDER_IMAGE="rust:1.95-bookworm" or readonly BUILDER_IMAGE=debian, then pass $BUILDER_IMAGE to docker run; this regex only permits an optional export, so the literal assignment is never checked and the command parser only sees the variable expansion. That lets local/readonly shell image constants bypass the new retired-generation and digest checks.

Useful? React with 👍 / 👎.

Comment on lines +295 to +299
if any(
item not in {"-", "command", "env", "sudo"}
and "=" not in item
and not item.endswith(":")
for item in prefix

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scan Docker commands after shell list operators

When a maintained script prefixes the Docker invocation with a shell command list, such as cd "$REPO_ROOT" && docker run --rm debian true or echo ok; docker run --rm debian true, shlex.split still exposes the later docker token but this prefix guard rejects it because earlier tokens are not in the allowlist. Those are ordinary shell forms for running commands from a directory or after setup, so the gate misses bare or retired images unless Docker is the first simple command on the line.

Useful? React with 👍 / 👎.

Comment on lines +104 to +106
r"^\s*(?:export\s+)?"
r"(?P<name>[A-Za-z_][A-Za-z0-9_-]*)\s*(?:=|:)\s*"
r"(?P<reference>[^#\s]+)",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Accept YAML list-item image keys

YAML and Kubernetes-style manifests commonly write container entries as list items, for example - image: debian followed by name, but this assignment regex only matches keys that start directly after whitespace. Since maintained YAML is discovered and scanned, a manifest under a maintained path can still carry an implicit-latest Debian image in a list item without a failure.

Useful? React with 👍 / 👎.

Comment on lines +193 to +195
is_dockerfile(relative)
or is_active_script(relative)
or relative.suffix in YAML_SUFFIXES

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scan Markdown and MDX code examples

Markdown and MDX files are included in maintained text surfaces, but this predicate omits them from the image-reference surfaces, so fenced tutorial or ops commands such as docker run --rm debian true are only read as UTF-8 and never passed to command_image_references. Active docs are part of the maintained boundary in this checker, so stale Debian examples in published instructions can drift while CI reports the image contract as passing.

Useful? React with 👍 / 👎.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f6b4c580df

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


def retired_generation_marker(reference: str) -> str | None:
lowered = reference.casefold()
for marker in ("book" + "worm", "debian" + "12"):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject colon-form Debian 12 tags

When a maintained surface uses the valid Docker form debian:12@sha256:..., this marker check only searches for the contiguous text debian12, and the digest means reference_requires_digest will not report it either. That lets an explicitly Debian 12, digest-pinned base or external COPY --from image pass a gate intended to enforce Debian 13.

Useful? React with 👍 / 👎.

image_name = repository.rsplit("/", 1)[-1].casefold()
return (
image_name in DEBIAN_DEFAULT_IMAGE_NAMES
and VERSION_ONLY_TAG_RE.fullmatch(tag) is not None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Treat slim default-family tags as Debian-derived

After the current fix, this branch only rejects default-family images when the tag is purely numeric, so common mutable Debian-based variants such as python:3.13-slim, node:22-slim, or rust:1.95-slim in a maintained script or YAML surface are ignored instead of requiring a digest pin. That leaves another Debian-derived builder/runtime form outside the new contract.

Useful? React with 👍 / 👎.

Comment on lines +128 to +135
DEBIAN_DEFAULT_IMAGE_NAMES = {
"buildpack-deps",
"debian",
"golang",
"node",
"python",
"rust",
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include PostgreSQL defaults in the Debian image set

This allow-list omits postgres, so the new repository-wide gate still passes the existing crates/registry-relay/scripts/run-live-consultation-journey.sh:9 value POSTGRES_IMAGE="postgres:16" and the two docker run calls that consume it. Docker Hub currently lists postgres:16 among the Debian/bookworm aliases, so this maintained live-journey dependency remains a mutable Debian-derived image outside the digest/Debian 13 contract.

Useful? React with 👍 / 👎.

Comment on lines +522 to +523
if relative.suffix not in SHELL_SUFFIXES | YAML_SUFFIXES and relative.suffix:
return []

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scan Docker invocations in non-shell scripts

For discovered Python/JS/TS files under scripts/, this guard returns before running the Docker command parser, so executable code such as subprocess.run(["docker", "run", "--rm", "debian"]) in a maintained release script is accepted unless the image also happens to be assigned to an *IMAGE variable. Since is_active_script includes those files, the contract leaves an entire maintained script class able to run unpinned Debian images without failing CI.

Useful? React with 👍 / 👎.

Comment on lines +105 to +106
r"(?P<name>[A-Za-z_][A-Za-z0-9_-]*)\s*(?:=|:)\s*"
r"(?P<reference>[^#\s]+)",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Parse YAML flow-style image keys

This regex only recognizes an image key when it starts the line (after optional list punctuation), so valid flow-style YAML such as container: { image: debian } in a workflow or helper: { image: debian:bookworm } in a compose file is treated as a non-image assignment and no reference is checked. Maintained YAML can therefore still carry unpinned or retired Debian images while the new contract passes.

Useful? React with 👍 / 👎.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0401069842

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


def retired_generation_marker(reference: str) -> str | None:
lowered = reference.casefold()
for marker in ("book" + "worm", "debian" + "12"):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject all retired Debian generation tags

When a maintained Dockerfile, script, or YAML pins an older Debian generation by digest, such as debian:bullseye@sha256:... or rust:1.95-buster@sha256:..., this marker list does not report it, and reference_requires_digest() also returns false because the digest is present. The same file already treats bullseye and buster as Debian-derived tags, so these digest-pinned pre-Debian-13 references can bypass the new Debian 13 contract entirely.

Useful? React with 👍 / 👎.

Comment on lines +481 to +482
if stripped and is_untagged_debian_derived(stripped):
candidates.append(stripped)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Inspect image defaults inside parameter expansion

When a maintained shell script or Compose/workflow YAML uses a defaulted image expression such as ${BUILDER_IMAGE:-debian} in docker run or an image: key, the scanner does not extract the default image: the tagged-reference regex cannot see a bare image inside the expansion, and this branch only adds untagged Debian-derived candidates when the entire value is the image reference. With the environment variable unset, that default is the image that actually runs, so unpinned Debian defaults can bypass the new contract.

Useful? React with 👍 / 👎.

r"\"(?P<double>(?:\\.|[^\"\\])*)\"|'(?P<single>(?:\\.|[^'\\])*)'"
)
GITHUB_ACTIONS_DOCKER_USES_RE = re.compile(
r"^\s*(?:-\s*)?uses:\s*docker://(?P<reference>[^#\s]+)",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Accept quoted docker:// action references

Although the new scanner handles unquoted uses: docker://... workflow steps, YAML permits the same scalar to be quoted, and this regex only matches when docker:// appears immediately after uses:. A maintained workflow step such as uses: "docker://debian:bookworm@sha256:..." therefore produces no image candidate, letting a retired Debian Docker action bypass the contract.

Useful? React with 👍 / 👎.

Comment on lines +630 to +631
if DIGEST_PIN_RE.search(reference):
return False

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require a Debian 13 marker on digest pins

Because digest-pinned references return before any generation check, maintained references such as debian:stable-slim@sha256:..., debian@sha256:..., or python:3.13@sha256:... pass even though the reference text does not establish that the pinned manifest is Debian 13. That leaves generation-ambiguous Debian-family images outside a gate whose contract is to enforce Debian 13, not only immutable digests.

Useful? React with 👍 / 👎.

Comment on lines +605 to +607
if language in MARKDOWN_SHELL_FENCE_LANGS:
block_relative = Path("example.sh")
block_references = command_image_references(block_relative, block)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve root prompts in console examples

For Markdown console fences, a leading # is commonly a root prompt, but this path sends console blocks through the same shell parser that treats # as a comment introducer. A maintained doc block like ````console

docker run --rm debian true


Useful? React with 👍 / 👎.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8eddb6cfcd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

in_block = False
for line_number, line in enumerate(text.splitlines(), 1):
stripped = line.strip()
if stripped.startswith("```"):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Parse tilde-fenced Markdown examples

When maintained Markdown/MDX uses ~~~sh, ~~~yaml, or ~~~dockerfile fences, this helper never opens the block because it only toggles on lines starting with backticks. The docs tree already uses tilde fences, so a future executable example such as ~~~sh\ndocker run --rm debian true\n~~~ would bypass the new Debian image contract even though backtick-fenced examples are scanned.

Useful? React with 👍 / 👎.

@lru_cache(maxsize=1)
def compose_yaml(text: str) -> Node | None:
try:
return yaml.compose(text, Loader=yaml.SafeLoader)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scan multi-document YAML streams

When a maintained YAML file contains multiple --- documents, as Kubernetes manifests commonly do, yaml.compose is the single-document API and raises instead of returning a root node; this helper catches that and returns None, so both declarative image: keys and executable run/command values in the entire stream are skipped. A multi-resource manifest can therefore add image: debian or uses: docker://debian:bookworm... without the new gate reporting it.

Useful? React with 👍 / 👎.

Comment on lines +963 to +964
for line_number, line in logical_lines(text):
match = COPY_RE.match(line)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scan RUN --mount image sources

When a maintained Dockerfile uses BuildKit mounts such as RUN --mount=type=bind,from=debian:bookworm@sha256:...,target=/src ..., this code never inspects the line because it only enters the parser for COPY; Docker's Dockerfile reference documents from on RUN --mount as accepting a build stage, context, or image name. That leaves another Dockerfile image source able to use retired or unpinned Debian images while the contract passes.

Useful? React with 👍 / 👎.

return (
relative.name == "Dockerfile"
or relative.name.startswith("Dockerfile.")
or relative.name.endswith(".Dockerfile")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Discover lower-case Dockerfile suffixes

When a maintained build uses a lower-case alternate file such as images/relay.dockerfile via docker buildx build -f ..., Docker treats -f as selecting the Dockerfile path (https://docs.docker.com/reference/cli/docker/buildx/build/#file), but this discovery only accepts Dockerfile, Dockerfile.*, and *.Dockerfile. That file is not scanned as a maintained surface, so a FROM debian:bookworm in it can bypass the new repository-wide contract.

Useful? React with 👍 / 👎.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Comment thread release/scripts/test_check_debian13_images.py Fixed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2ccd55688c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

"--tty",
"-d",
"-i",
"-it",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Parse grouped Docker boolean shorthands

When a maintained script or Markdown example uses grouped short booleans other than this literal -it special case, such as docker run -itd debian true, the option skipper treats -itd as an unknown value-taking flag and skips debian, so the bare Debian image bypasses the contract. Docker's CLI reference says "single character command line options can be combined" (https://docs.docker.com/reference/cli/docker/); fresh evidence here is that the current allow-list special-cases -it but still does not parse general grouped boolean shorthands.

Useful? React with 👍 / 👎.

Comment on lines +689 to +692
len(option) == 2
and option.startswith("-")
and option[1] in SHELL_COMMAND_SHORT_OPTIONS
):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Continue past valid bash -o launcher options

If a maintained YAML/script wraps the container command as bash -euo pipefail -c 'docker run --rm debian true', this parser stops before reaching -c because o is not accepted as an invocation option, and the nested Docker command is never scanned. I checked bash --help, which lists -o option under shell invocation options, so common pipefail launchers can hide unpinned Debian images from this gate.

Useful? React with 👍 / 👎.

Comment on lines +1068 to +1071
"compose_image",
"image",
"kubernetes_image",
"workflow_uses",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scan Compose inline Dockerfiles

When a maintained Compose file builds a service with build: dockerfile_inline: | and a FROM debian:bookworm or unpinned Debian-family base, this allowed-context list never treats that scalar as Dockerfile content, so the embedded base image is ignored. Docker's Compose Build Specification says dockerfile_inline defines Dockerfile content (https://docs.docker.com/reference/compose-file/build/#dockerfile_inline), so this is another active build surface that can bypass the Debian 13 contract.

Useful? React with 👍 / 👎.

Comment on lines +955 to +956
if compose and len(path) == 3 and path[0] == "services":
return f"compose_{path[2]}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve Compose merge keys before classifying images

If a maintained Compose file keeps a reusable service block under an x- extension and merges it with <<: *common, an inherited image: debian is not seen at the direct services.<name>.image path this branch requires, so the service can still run an unpinned Debian image. Docker documents Compose extensions as usable with anchors and aliases, including << merges (https://docs.docker.com/reference/compose-file/extension/), so the scanner needs to flatten or otherwise inspect merged service fields.

Useful? React with 👍 / 👎.

Comment on lines +966 to +970
"image"
if key == "IMAGE"
or lowered.endswith("_image")
or key != lowered and lowered.endswith("image")
else ""

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scan Docker action metadata images

When a maintained action.yml/action.yaml defines a Docker container action with runs.image: docker://debian:bookworm or a direct Docker Hub image, this fallback ignores the lowercase image key outside workflow/Compose/Kubernetes contexts, so the action runtime image is never checked. GitHub's metadata syntax documents runs.image as the Docker image used to run the action (https://docs.github.com/en/actions/reference/workflows-and-actions/metadata-syntax#runsimage), so a local action can still introduce an unpinned or retired Debian image while the new gate passes.

Useful? React with 👍 / 👎.

if context == "workflow_run" and isinstance(value_node, ScalarNode):
commands.extend(yaml_scalar_commands(value_node, lines))
continue
if context in {"compose_command", "compose_entrypoint"}:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Combine Compose entrypoint with command

When a maintained Compose service splits a Docker invocation across fields, for example entrypoint: [docker] and command: [run, --rm, debian], both scalars are scanned independently: docker has no subcommand and run --rm debian does not start with Docker, so the image is missed. Docker Compose documents entrypoint together with command as the executable plus command arguments for the service (https://docs.docker.com/reference/compose-file/services/#entrypoint), so these fields need to be evaluated together before deciding there is no image reference.

Useful? React with 👍 / 👎.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7b77d1047c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

OCI_RE = re.compile(
r"(?<![A-Za-z0-9._/@+-])(?P<ref>(?:docker://)?"
r"(?:[A-Za-z0-9.-]+(?::[0-9]+)?/)*[A-Za-z0-9._-]+"
r"(?::[A-Za-z0-9_][A-Za-z0-9._-]*|@sha256:[0-9a-fA-F]{64})"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject untagged default-family image names

For maintained YAML image keys, a valid reference such as image: rust or container: python produces no candidate because this regex only extracts refs that already contain a tag or digest; Docker documents that an omitted tag defaults to latest (https://docs.docker.com/reference/cli/docker/image/tag/). Since the checker already treats rust:latest/python:latest as Debian-family images, the untagged equivalent can still run a mutable Debian-derived image while the new contract reports clean.

Useful? React with 👍 / 👎.

DEFAULT_DEBIAN_FAMILIES = {"golang", "node", "python", "rust"}
OCI_RE = re.compile(
r"(?<![A-Za-z0-9._/@+-])(?P<ref>(?:docker://)?"
r"(?:[A-Za-z0-9.-]+(?::[0-9]+)?/)*[A-Za-z0-9._-]+"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Recognize underscores in image namespaces

When a maintained YAML/Compose surface uses a valid repository path component with an underscore, such as image: registry.local/team_name/debian:trixie, this character class stops the OCI match before the image name; in a YAML image key that means references() returns no candidate and scan_surface() reports clean. The distribution/reference parser allows one or two underscores in name-component separators (https://github.com/distribution/reference/blob/main/regexp.go), so private-registry Debian images can bypass the digest/Trixie check.

Useful? React with 👍 / 👎.

variables = {
match.group("name").casefold() for match in IMAGE_VAR_RE.finditer(line)
}
if not any(re.search("[A-Za-z]", repository_and_tag(item)[0]) for item in references(line)) and not BARE_DEBIAN_RE.search(line) and not variables & (resolved | declared):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require consumed image variables to resolve

If a maintained shell script declares a non-policy image variable and then consumes it, e.g. APP_IMAGE="$(select-image)" followed by docker run "$APP_IMAGE", this condition treats the mere declaration as sufficient and suppresses the unresolved-consumer error even though the assignment never resolved to a literal. When the selector returns debian or a mutable Debian-family tag, the new gate passes without checking the image that actually runs.

Useful? React with 👍 / 👎.

Comment thread .github/workflows/ci.yml
echo "registryctl_tutorial=${registryctl_tutorial}"
} >> "${GITHUB_OUTPUT}"

- name: Check Debian 13 image contract

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sign off this commit

This reviewed commit has no Signed-off-by: trailer in git show -s --format=full 532223d9510028a20423654ee854600af302b921, so it violates the repo's DCO requirement and needs to be recommitted or squashed with git commit -s before it satisfies the contribution rules.

Useful? React with 👍 / 👎.

BARE_DEBIAN_RE = re.compile(
r"(?<![A-Za-z0-9._@+-])debian(?![A-Za-z0-9._/@+:-])"
)
DIGEST_RE = re.compile(r"@sha256:[0-9a-f]{64}$")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Accept uppercase digest hex as pinned

This pin check only accepts lowercase hex, but the scanner's own OCI matcher accepts [0-9a-fA-F] and the container reference parser treats hex digits case-insensitively (for example, distribution/reference uses [:xdigit:] in its digest pattern: https://github.com/distribution/reference/blob/main/regexp.go). A maintained reference like rust:1.95-trixie@sha256:AAAA... is therefore extracted as a valid image reference and then incorrectly reported as unpinned, so valid digest-pinned images can fail the new CI gate until the digest is lowercased.

Useful? React with 👍 / 👎.

jeremi added 3 commits July 24, 2026 01:24
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 21363b59be

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci.yml
echo "registryctl_tutorial=${registryctl_tutorial}"
} >> "${GITHUB_OUTPUT}"

- name: Check Debian 13 image contract

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sign off this commit

This reviewed commit (git show -s --format=full 855be52238896bf469ec03d8050dd9e12d4dac8d) still has no Signed-off-by: trailer, so it does not satisfy the repository's DCO requirement and will need to be recommitted or squashed with sign-off before it can pass contribution policy.

Useful? React with 👍 / 👎.

declared.add(canonical)
if has_literal and not computed or positional:
resolved.add(canonical)
bare = BARE_DEFAULT_FAMILY_RE.search(line)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Parse the actual Docker image token

When a Docker command contains a default-family word as an option value or as the command after a non-Debian image, such as docker run --name postgres alpine true or docker run alpine python -V, this whole-line bare search reports postgres/python as an unpinned image even though Docker's documented form is docker run [OPTIONS] IMAGE[:TAG|@DIGEST] [COMMAND] [ARG...] (https://docs.docker.com/engine/containers/run/#general-form). That makes the new CI gate block valid non-Debian container commands unless contributors avoid common names or commands.

Useful? React with 👍 / 👎.

re.search("[A-Za-z]", repository_and_tag(item)[0])
for item in references(line)
)
and not BARE_DEFAULT_FAMILY_RE.search(line)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Check bare images on joined shell commands

When a workflow or script splits the image onto a continued line, for example run: | docker run --rm \ followed by debian true, the per-line pass never emits the bare-image failure for debian, and this joined-line condition then treats the bare match as enough to skip the unresolved-consumer error. The gate can therefore miss an implicit-latest Debian image in common multiline Docker/Podman commands.

Useful? React with 👍 / 👎.

)
lines, failures = text.splitlines(), []
markdown_flags = markdown_code_flags(path, text.splitlines())
code_file = is_dockerfile(path) or path.suffix in CODE_SUFFIXES

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Treat extensionless build helpers as code

When a maintained executable has no suffix, such as the tracked release/scripts/registry-release helper, code_file is false, so a line like docker run --rm debian true is not treated as a container consumer and produces no image-reference failure. That leaves extensionless release/build helpers able to introduce unpinned Debian images while this repository-wide gate reports clean.

Useful? React with 👍 / 👎.

Comment on lines +80 to +84
OCI_RE = re.compile(
r"(?<![A-Za-z0-9._/@+-])(?P<ref>(?:docker://)?"
r"(?:[A-Za-z0-9._-]+(?::[0-9]+)?/)*[A-Za-z0-9._-]+"
r"(?::[A-Za-z0-9_][A-Za-z0-9._-]*|@sha256:[0-9a-fA-F]{64})"
r"(?:@sha256:[0-9a-fA-F]{64})?)(?![A-Za-z0-9._/@+-])"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scan buildx image contexts

When a maintained build script uses Buildx's image-backed additional contexts, for example docker buildx build --build-context base=docker-image://debian ., the scanner reports no failure because this pattern only recognizes docker:// references and build is not treated as an image-consuming action. Docker documents --build-context and says the docker-image:// scheme is used for container-image contexts (https://docs.docker.com/reference/cli/docker/buildx/build/#use-a-container-image), so unpinned or retired Debian build inputs can bypass the new gate.

Useful? React with 👍 / 👎.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6f6c37e712

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

)
reference_context = not comment and (
is_dockerfile(path)
or path.suffix in {".yaml", ".yml"}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Limit YAML reference checks to image fields

When a maintained YAML file has ordinary metadata or prose with an image-looking value, such as container_note: rust:1.95-trixie, this path.suffix branch sends the line through references() even though assignment() rejected the key as non-image. The new CI gate then reports the unpinned Debian-family tag and blocks a change that did not configure an image, so YAML scanning should be limited to image/container keys and executable consumers.

Useful? React with 👍 / 👎.

if (
not comment
and bare
and (is_dockerfile(path) or consumer or bare_assignment)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Limit Dockerfile bare checks to image instructions

When a Dockerfile line merely contains a default-family word, this is_dockerfile(path) branch turns the whole line into a bare-image context. For example, an internal stage alias like FROM alpine:3.22@sha256:... AS rust or a RUN command mentioning python is reported as an unpinned Debian image even though Docker is not pulling that word as an image, which makes the new gate reject valid Dockerfiles.

Useful? React with 👍 / 👎.

Comment thread .github/workflows/ci.yml
echo "registryctl_tutorial=${registryctl_tutorial}"
} >> "${GITHUB_OUTPUT}"

- name: Check Debian 13 image contract

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sign off this commit

Fresh evidence for this review is that git show -s --format=%B 868deaa57d3598ca4f6cabab8670895d9ae8c821 contains no Signed-off-by: trailer, so this commit still cannot satisfy the repository DCO policy until it is recommitted or squashed with sign-off.

AGENTS.md reference: AGENTS.md:L70-L70

Useful? React with 👍 / 👎.

r'<!--\s*debian13-policy:\s*allow-prose\s+reason="[^"]{8,}"\s*-->'
)
MARKDOWN_SUFFIXES = {".md", ".mdx"}
CODE_SUFFIXES = set(".bash .js .mjs .py .sh .ts .yaml .yml".split())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scan YAML templates as image surfaces

Because classification only uses the final suffixes in this set, tracked Compose templates ending in .yaml.tmpl are read but never get YAML image-key scanning. The active crates/registryctl/src/templates/notary_addon/compose-fragment.yaml.tmpl already contains image: postgres@sha256:...; copied into generated Compose this remains generation-ambiguous, but the new gate passes unless YAML templates are treated like YAML surfaces.

Useful? React with 👍 / 👎.

Comment thread .github/workflows/ci.yml
echo "registryctl_tutorial=${registryctl_tutorial}"
} >> "${GITHUB_OUTPUT}"

- name: Check Debian 13 image contract

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sign off this commit

Fresh evidence for this review is that git show -s --format=%B 868deaa57d3598ca4f6cabab8670895d9ae8c821 contains no Signed-off-by: trailer, so this commit still cannot satisfy the repository DCO policy until it is recommitted or squashed with sign-off.

Useful? React with 👍 / 👎.

"debian" + "10",
"debian-10",
)
DEFAULT_DEBIAN_FAMILIES = {"golang", "node", "postgres", "python", "rust"}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include nginx defaults in Debian-family detection

When a maintained Dockerfile uses the existing release/conformance/openid/nginx.Dockerfile base nginx:1.27.3@sha256:bc2f..., this allow-list does not treat nginx as a Debian-family default, even though Docker Hub lists the same index digest under nginx:1.27.3-bookworm. The new repository-wide gate therefore passes an existing Debian 12 conformance input, so nginx needs to be covered or derived from image metadata before this enforces the Debian 13 boundary.

Useful? React with 👍 / 👎.

)
DIGEST_RE = re.compile(r"@sha256:[0-9a-f]{64}$", re.IGNORECASE)
FROM_RE = re.compile(
r"^FROM\s+(?:--platform=\S+\s+)?(\S+)(?:\s+AS\s+(\S+))?",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Match indented Dockerfile FROM instructions

Docker's reference says leading whitespace before instructions is ignored, but this regex only matches FROM at column 0. In a multi-stage Dockerfile with one normal first stage and a later indented FROM alpine:3.22 AS helper, Docker still builds the helper while this checker sees only the first stage and reports clean, bypassing the new digest-pinning check for that upstream base.

Useful? React with 👍 / 👎.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 34ab475fea

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci.yml
echo "registryctl_tutorial=${registryctl_tutorial}"
} >> "${GITHUB_OUTPUT}"

- name: Check Debian 13 image contract

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sign off this commit

Fresh evidence for this review is that git show -s --format=%B 87d2703dadebad6059289f3b06e542bc2edf786e has no Signed-off-by: trailer, so this commit cannot satisfy the repository DCO policy until it is recommitted or squashed with sign-off.

AGENTS.md reference: AGENTS.md:L70-L70

Useful? React with 👍 / 👎.

Comment thread .github/workflows/ci.yml
echo "registryctl_tutorial=${registryctl_tutorial}"
} >> "${GITHUB_OUTPUT}"

- name: Check Debian 13 image contract

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sign off this commit

Fresh evidence for this review is that git show -s --format=%B 87d2703dadebad6059289f3b06e542bc2edf786e has no Signed-off-by: trailer, so this commit cannot satisfy the repository DCO policy until it is recommitted or squashed with sign-off.

Useful? React with 👍 / 👎.

Comment on lines +96 to +99
ASSIGN_RE = re.compile(
r"^\s*(?:-\s*)?(?:(?:export|local|readonly|const|let|var)\b[^A-Za-z_]*)?"
r"(?P<name>[A-Za-z_][A-Za-z0-9_$-]*)(?:\s*:[^=]+)?"
r"\s*=\s*(?P<value>.+)$"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Parse Bash declaration options

When a maintained bash helper uses a valid readonly local declaration such as local -r APP_IMAGE="alpine:3.22" or declare -r APP_IMAGE=..., this regex does not recognize the assignment because it only skips non-letter characters after local and does not handle declare. The image variable is therefore never marked resolved, so a later docker run "$APP_IMAGE" fails the new gate even though the image was assigned from a static literal.

Useful? React with 👍 / 👎.

Comment on lines +666 to +667
if ((has_literal or has_template) and not computed) or positional:
resolved.add(canonical)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Track image variable resolution in order

Fresh evidence beyond the earlier unresolved-variable concern is that the current implementation records a name in a file-wide resolved set as soon as any literal assignment is seen and never invalidates it. In a maintained script like APP_IMAGE="alpine:3.22"; APP_IMAGE="$(select-image)"; docker run "$APP_IMAGE", the first line keeps app_image resolved, so the computed value can later select debian or a mutable Debian-family tag without this gate reporting it.

Useful? React with 👍 / 👎.

jeremi added 2 commits July 24, 2026 02:44
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d97b33adcf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci.yml
echo "registryctl_tutorial=${registryctl_tutorial}"
} >> "${GITHUB_OUTPUT}"

- name: Check Debian 13 image contract

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sign off this commit

Fresh evidence for this review is that git show -s --format=%B faef5ed58c71b0f41e46dfc17b741a284fc7e209 contains no Signed-off-by: trailer, so this commit still violates the repository DCO requirement and needs to be recommitted or squashed with sign-off before it can satisfy contribution policy.

Useful? React with 👍 / 👎.

r"\b(?P<name>[A-Za-z_][A-Za-z0-9_$-]*(?:_image|Image)|IMAGE)\b",
re.IGNORECASE,
)
CLI_RE = re.compile(r"(?:^|[\s;&|({])(?:\S*/)?(?:docker|podman)\b(?P<tail>[^\n]*)")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scan later Docker invocations on the same shell line

When a maintained script writes a diagnostic Docker command before the image consumer on the same shell line, e.g. docker ps && docker run --rm debian true, this regex consumes the entire remainder of the line as the first match's tail. command_tokens() then keeps only the tokens before &&, and because finditer() has no second match to inspect, the bare Debian image is not reported by the new CI gate; split the tail at shell separators or make the match non-greedy enough to scan each invocation.

Useful? React with 👍 / 👎.

Comment on lines +395 to +399
and (
not tag
or tag in {"latest", "slim"}
or re.fullmatch(r"[0-9]+(?:[._][0-9]+)*(?:-slim)?", tag) is not None
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject Node LTS Debian-family tags

If a maintained workflow or script uses a moving official Node tag such as container: node:lts or docker run node:lts, this predicate returns false because default-family tags are only treated as Debian-derived when they are empty/latest/slim or numeric. Docker Hub documents the default Node image as based on buildpack-deps, which contains common Debian packages, so common LTS tags can still introduce mutable Debian-derived images while this gate reports clean.

Useful? React with 👍 / 👎.

),
}
MARKDOWN_SUFFIXES = {".md", ".mdx"}
CODE_SUFFIXES = set(".bash .js .mjs .py .sh .ts .yaml .yml".split())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Treat Rust image constants as scanned code

When maintained Rust code embeds an image default or generated-template value, the file is discovered but code_file remains false because .rs is omitted here, so neither image assignments nor reference checks run. A Rust-owned constant such as const BUILDER_IMAGE: &str = "rust:1.95-trixie"; therefore passes the new repository-wide gate even though crates like registryctl can own generated Compose/image inputs.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 98da966fab

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci.yml
Comment on lines +170 to +171
- name: Check Debian 13 image contract
run: python3 release/scripts/check-debian13-images.py

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sign off the reviewed commit

Fresh evidence for this review is that git show -s --format=%B a5ab69375fa1040175c203c1e2c2102c52a77181 contains no Signed-off-by: trailer, so this commit cannot satisfy the repository DCO requirement until it is recommitted or squashed with sign-off.

Useful? React with 👍 / 👎.

Comment on lines +830 to +831
if image_assignment_name(name):
names.append(name)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow mapping-form job containers

When a workflow needs container credentials, env, ports, or options, GitHub Actions supports the mapping form container:\n image: ... (https://docs.github.com/actions/using-jobs/running-jobs-in-a-container#defining-the-container-image), but this branch treats the parent container: line as an unsupported image key before the nested image: scalar is validated. That makes the new repository-wide gate reject a valid digest-pinned job container configuration even though the actual image line can still be checked.

Useful? React with 👍 / 👎.

Comment on lines +672 to +673
def build_context_references(text: str) -> list[str]:
return [match.group("ref") for match in DOCKER_CONTEXT_RE.finditer(text)]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scan registry cache image refs

When a maintained build script imports a registry cache with docker buildx build --cache-from=rust . or --cache-from type=registry,ref=rust, Docker documents --cache-from as an external cache source and says an omitted type uses the registry reference (https://docs.docker.com/reference/cli/docker/buildx/build/#use-an-external-cache-source-for-a-build---cache-from). This helper only extracts docker-image:// build contexts, and bare cache refs are not otherwise passed through append_bare_failures, so the new gate can still pass an implicit-latest Debian-family cache image.

Useful? React with 👍 / 👎.

Comment on lines +1105 to +1107
scalar_yaml = (
path.suffix in {".yaml", ".yml"} and yaml_scalar_flags[number - 1]
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scan Compose inline Dockerfiles

When a maintained Compose file uses build.dockerfile_inline with FROM debian, Compose treats that block as Dockerfile content (https://docs.docker.com/reference/compose-file/build/#dockerfile_inline), but these lines mark the block body as YAML scalar text and the scanner never applies Dockerfile FROM/bare-image checks to it. That lets an inline Dockerfile pull an implicit mutable Debian image while the new repository-wide gate reports clean.

Useful? React with 👍 / 👎.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4399a04dfb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci.yml
Comment on lines +170 to +171
- name: Check Debian 13 image contract
run: python3 release/scripts/check-debian13-images.py

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sign off the reviewed commit

Fresh evidence for this review is that git show -s --format=%B a6931c2055322e885360acc68edc50daeedc74c2 contains no Signed-off-by: trailer, so this commit cannot satisfy the repository DCO requirement until it is recommitted or squashed with sign-off.

Useful? React with 👍 / 👎.

)
require(
tutorial_check,
f'BUILDER_IMAGE="{RUST_BUILDER}"',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate the active tutorial builder assignment

When the tutorial script leaves this exact BUILDER_IMAGE text in a comment or dead string but changes the executable assignment, this whole-file substring check still passes; for example, a commented pinned value followed by BUILDER_IMAGE="rust:1.95-trixie" produces no checker failure. Since this is the new CI enforcement for the registryctl tutorial builder, parse the live assignment instead of searching the entire file.

Useful? React with 👍 / 👎.

RELAY_TUTORIAL="$SITE_ROOT/src/content/docs/tutorials/publish-spreadsheet-secured-registry-api.mdx"
NOTARY_TUTORIAL="$SITE_ROOT/src/content/docs/tutorials/verify-claim-registry-api.mdx"
BUILDER_IMAGE="rust:1.95-bookworm@sha256:4c2fd73ef19c5ef9d54bee03b06b2839a392604fbfcd578ed948b71b37c1d7fb"
BUILDER_IMAGE="rust:1.95-trixie@sha256:f49565f188ee00bc2a18dd418183f2c5f23ef7d6e691890517ed341a598f67c3"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Split docs and release changes

This same commit changes a docs/site tutorial script while also adding release checker/tests and root CI wiring, which crosses the docs and release ownership boundaries. The repository guidance asks changes to stay scoped to one owning area, so this should be split or narrowed before review can apply the right owner checks.

Useful? React with 👍 / 👎.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 84d5b62259

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci.yml
echo "registryctl_tutorial=${registryctl_tutorial}"
} >> "${GITHUB_OUTPUT}"

- name: Check Debian 13 image contract

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sign off the reviewed commit

Fresh evidence for this reviewed commit is that git show -s --format=%B 90ca17727f7f3c629c57f6c666ea922ad88e000c contains no Signed-off-by: trailer, so this commit cannot satisfy the repository DCO requirement until it is recommitted or squashed with sign-off.

AGENTS.md reference: AGENTS.md:L70-L70

Useful? React with 👍 / 👎.

jeremi added 2 commits July 24, 2026 04:29
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 62a71b1f2a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@@ -96,70 +184,73 @@ def check_repository(root: Path = ROOT) -> list[str]:
for relative in MAINTAINED_TEXT_PATHS
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate image references, not just retired markers

When a maintained text surface uses an active Debian-family image that is not the old bookworm/debian12 spelling, this loop reports nothing because it only searches for retired-marker words. I verified a fixture with .github/workflows/ci.yml containing container: rust:1.95-trixie returns [], so the new CI gate can still accept an unpinned mutable Debian 13 image on one of the newly added maintained workflow surfaces; the checker needs to parse image/container references and require digests there, not just reject old codenames.

Useful? React with 👍 / 👎.

Comment on lines +47 to 48
Path(".github/workflows/ci.yml"),
Path(".github/workflows/release.yml"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include the other root workflows in the image gate

Adding only ci.yml and release.yml leaves the rest of the tracked root workflows outside this always-on image contract check; I checked rg --files .github/workflows and found conformance/nightly/docs/codeql workflows that are not in MAINTAINED_TEXT_PATHS. If one of those workflows adds container: debian:12 or another Debian-family image, this gate never reads the file, so the repository-wide workflow image boundary can be bypassed by editing a root workflow other than these two.

Useful? React with 👍 / 👎.

Comment thread .github/workflows/ci.yml
Comment on lines +170 to +171
- name: Check Debian 13 image contract
run: python3 release/scripts/check-debian13-images.py

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sign off the reviewed commit

Fresh evidence for this review is that git show -s --format=%B ac79237bf310cbc0cf589ee8b3ff88e5647dc8c0 contains no Signed-off-by: trailer, so this commit cannot satisfy the repository DCO requirement until it is recommitted or squashed with sign-off.

AGENTS.md reference: AGENTS.md:L70-L70

Useful? React with 👍 / 👎.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9e586721d9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci.yml
Comment on lines +170 to +171
- name: Check Debian 13 image contract
run: python3 release/scripts/check-debian13-images.py

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sign off the reviewed commit

Fresh evidence for this review is that git show -s --format=%B 588f026b959d658d131bb82a839f5c35a7659ae3 contains no Signed-off-by: trailer, so this commit cannot satisfy the repository DCO requirement until it is recommitted or squashed with sign-off.

AGENTS.md reference: AGENTS.md:L70-L70

Useful? React with 👍 / 👎.

jeremi added 4 commits July 24, 2026 04:56
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 542910df7e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci.yml
echo "registryctl_tutorial=${registryctl_tutorial}"
} >> "${GITHUB_OUTPUT}"

- name: Check Debian 13 image contract

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sign off the reviewed commit

Fresh evidence for this review is that git show -s --format=%B 13dc99d1401a0e2e7598a58330ac42c6e9b62d5f contains no Signed-off-by: trailer, but the repository requires every commit to be signed off, so this PR will keep failing the contribution policy until the commit is recommitted or squashed with git commit -s.

AGENTS.md reference: AGENTS.md:L70-L70

Useful? React with 👍 / 👎.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b1a797dd4f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci.yml
} >> "${GITHUB_OUTPUT}"

- name: Check Debian 13 image contract
run: python3 release/scripts/check-debian13-images.py

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sign off the reviewed commit

Fresh evidence for this review is that git show -s --format=%B f25350624a4fa90c32c17e4818f1622e07447413 has no Signed-off-by: trailer, so this commit cannot satisfy the repository DCO policy until it is recommitted or squashed with sign-off.

AGENTS.md reference: AGENTS.md:L70-L70

Useful? React with 👍 / 👎.

Comment on lines +306 to +307
elif key == "image":
add_image(child, child_location)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Inventory workflow matrix image values

When a workflow keeps Docker references in matrix fields instead of GitHub's image: schema, this collector never records them because only exact container, image, and docker:// uses keys are added. The active .github/workflows/notary-postgres-conformance.yml uses source_image/target_image matrix values that are exported to NOTARY_POSTGRES_*_IMAGE and pulled by products/notary/scripts/postgresql-conformance.sh; changing one to a mutable Debian-family tag such as postgres:18 would still produce no allowlist failure, so the new always-on gate can miss a maintained workflow image.

Useful? React with 👍 / 👎.

Comment thread .github/workflows/ci.yml
} >> "${GITHUB_OUTPUT}"

- name: Check Debian 13 image contract
run: python3 release/scripts/check-debian13-images.py

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sign off the reviewed commit

Fresh evidence for this review is that git show -s --format=%B f25350624a4fa90c32c17e4818f1622e07447413 has no Signed-off-by: trailer, so this commit cannot satisfy the repository DCO policy until it is recommitted or squashed with sign-off.

Useful? React with 👍 / 👎.

f"{relative}: Dockerfile stage sequence must be exactly "
f"{expected_stages!r}; found {stages!r}"
)
for base, _alias, _platform in stages:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scan Dockerfile external copy sources

If a maintained Dockerfile adds COPY --from=debian:trixie-slim ... or another external image source, the build can still pull that image while the checker passes because only bases collected from FROM stages are validated here. Docker's Dockerfile reference documents COPY [--from=<image|stage|context>], so the exact two-stage check should also reject or allowlist external COPY --from image references before this can enforce the image boundary.

Useful? React with 👍 / 👎.

Comment on lines +701 to +704
require_unique_active_line(
live_journey,
RUST_BUILDER,
(LIVE_JOURNEY_BUILDER,),
LIVE_JOURNEY_BUILDER_RE,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate the live-journey Postgres image

This live-journey section only pins the Rust runner, but the same maintained script has readonly POSTGRES_IMAGE="postgres:16" and uses it in earlier docker run calls to prepare and start PostgreSQL. Since the Docker Hub Postgres default tags are Debian-based unless an Alpine variant is selected, the new contract currently passes an active mutable Debian-family image in a governed path; include this image in the exact contract or move it to a bounded allowlist.

Useful? React with 👍 / 👎.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5b88e978a3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci.yml
} >> "${GITHUB_OUTPUT}"

- name: Check Debian 13 image contract
run: python3 release/scripts/check-debian13-images.py

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sign off the reviewed commit

Fresh evidence for this reviewed commit is that git show -s --format=%B 95d3fd36af3663bcf8db492a0d2315b2ba4cfa28 has no Signed-off-by: trailer, so this contribution cannot satisfy the repository DCO requirement until it is recommitted or squashed with sign-off.

Useful? React with 👍 / 👎.

jeremi added 2 commits July 24, 2026 05:54
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 46dc3965aa

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if alias is not None
}
named_contexts = DOCKERFILE_NAMED_CONTEXTS[relative]
for source in collect_dockerfile_copy_sources(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Inspect BuildKit RUN mount sources

When a maintained Dockerfile uses BuildKit bind mounts such as RUN --mount=type=bind,from=debian:trixie-slim,target=/mnt true, Docker's Dockerfile reference defines from as a build stage, context, or image name (https://docs.docker.com/reference/dockerfile/#run---mounttypebind). This loop only validates COPY --from sources, so that external Debian-family image is never compared with declared stages or reviewed contexts and the new Debian 13 gate can pass a mutable image pull during the build.

Useful? React with 👍 / 👎.

Comment thread .github/workflows/ci.yml
echo "registryctl_tutorial=${registryctl_tutorial}"
} >> "${GITHUB_OUTPUT}"

- name: Check Debian 13 image contract

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sign off the reviewed commit

Fresh evidence for this reviewed commit is that git show -s --format=%B 7b5c68df55136ac7e248ee6d9e878cdb07da9211 has no Signed-off-by: trailer, so this contribution cannot satisfy the repository DCO requirement until it is recommitted or squashed with sign-off.

Useful? React with 👍 / 👎.

failures.append(
f"{relative}: final Distroless runtime contains 'RUN'"
)
for forbidden in ("apt-get", "/bin/sh", "curl ", "wget "):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Forbid copied alternate shells in runtime

When a maintained Dockerfile copies another shell into the final stage, for example COPY --from=builder /bin/bash /bin/bash, this check still passes because the forbidden runtime scan only looks for /bin/sh plus a few tool names and COPY --from=builder is an approved source. That reintroduces a shell into the Distroless runtime even though this gate is now the CI contract for keeping those images shell-free; include alternate shell paths or reject shell-carrying copies/directives in the final stage.

Useful? React with 👍 / 👎.

Path("products/notary/docs/security-assurance.md"),
)

NOTARY_POSTGRES_WORKFLOW_IMAGES = (

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Cover Notary conformance script defaults

This inventory covers the Notary workflow's source_image/target_image matrix values, but the invoked products/notary/scripts/postgresql-conformance.sh also pulls default_restore_image and unsupported_postgres_image defaults that are not in any maintained surface here. If that script is later changed to a mutable Debian-family restore or negative-test image, the always-on Debian 13 gate still reports clean because it never reads the active Docker pulls outside the workflow matrix.

Useful? React with 👍 / 👎.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 63da446cf0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

final_copy_instructions = tuple(
" ".join(instruction.split())
for instruction in instructions[final_from_indexes[-1] + 1 :]
if COPY_INSTRUCTION_RE.match(instruction)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include ADD in final-stage inventory

If a governed Dockerfile adds an active final-stage ADD, for example ADD https://example.invalid/bash /bin/bash, Docker will place that file in the runtime image, but this inventory only collects COPY instructions and the current forbidden-token scan does not catch /bin/bash. I verified such a mutation against release/docker/Dockerfile.registry-relay still returns no checker failure, so the new image contract can pass extra runtime contents that bypass the exact reviewed copy inventory (Dockerfile ADD reference: https://docs.docker.com/reference/dockerfile/#add).

Useful? React with 👍 / 👎.

Comment thread .github/workflows/ci.yml
} >> "${GITHUB_OUTPUT}"

- name: Check Debian 13 image contract
run: python3 release/scripts/check-debian13-images.py

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Install PyYAML before running the contract

In the always-run changes job, this new step executes before any setup or dependency installation, but check-debian13-images.py now imports the third-party yaml module at import time. On runners or fresh checkouts without PyYAML (there is no requirements/venv setup in this job), python3 release/scripts/check-debian13-images.py exits with ModuleNotFoundError before producing outputs, so even unrelated PRs can fail CI before the path filters are usable.

Useful? React with 👍 / 👎.

Comment thread .github/workflows/ci.yml
echo "registryctl_tutorial=${registryctl_tutorial}"
} >> "${GITHUB_OUTPUT}"

- name: Check Debian 13 image contract

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sign off the reviewed commit

Fresh evidence for this review is that git show -s --format=%B 99d901bb47d88493c75d3f5466455153a44635c6 contains no Signed-off-by: trailer, so this commit cannot satisfy the repository DCO requirement until it is recommitted or squashed with sign-off.

Useful? React with 👍 / 👎.

Comment on lines +611 to +612
elif key in WORKFLOW_IMAGE_KEYS:
add_image(child, child_location)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Inventory setup-buildx driver images

In the release workflow I inspected, docker/setup-buildx-action is configured with driver-opts: image=${{ env.RELEASE_BUILDKIT_IMAGE }}, and Docker documents that this image option pins the BuildKit container (https://docs.docker.com/build/ci/github-actions/configure-builder/#version-pinning). This traversal only records scalar keys named image, source_image, or target_image, so the active BuildKit image hidden inside driver-opts is missed; I verified changing RELEASE_BUILDKIT_IMAGE to debian:trixie-slim produces no Debian 13 checker failure, allowing a maintained workflow image pull to bypass the new contract.

Useful? React with 👍 / 👎.

jeremi added 2 commits July 24, 2026 06:42
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e6289df635

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci.yml
Comment on lines +170 to +171
- name: Check Debian 13 image contract
run: python3 release/scripts/check-debian13-images.py

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sign off the reviewed commit

Fresh evidence for this review is that git show -s --format=%B 8dc9e3365da6599d1b9c8272d11f5ec4d7025089 contains no Signed-off-by: trailer, so this commit still cannot satisfy the repository DCO requirement until it is recommitted or squashed with sign-off.

AGENTS.md reference: AGENTS.md:L70-L70

Useful? React with 👍 / 👎.

Comment on lines +181 to +183
Path(".github/workflows/relay-postgres-conformance.yml"),
"image",
"postgres:${{ matrix.postgresql }}-alpine",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bind Relay Postgres matrix values

In the Relay conformance workflow I checked, the service image is postgres:${{ matrix.postgresql }}-alpine while the actual versions come from .github/workflows/relay-postgres-conformance.yml's matrix.postgresql values. Since this allowlist records only the literal template, changing that matrix to something like latest still produces the same collected image string and remains allowlisted, so the new gate does not actually freeze the reviewed Relay PostgreSQL image set.

Useful? React with 👍 / 👎.

Comment on lines +705 to +706
elif key in WORKFLOW_IMAGE_KEYS or key in WORKFLOW_DEFAULT_IMAGE_KEYS:
add_image(child, str(key), child_location)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate Notary image env overrides

The active Notary workflow passes the images that the script pulls through NOTARY_POSTGRES_SOURCE_IMAGE and NOTARY_POSTGRES_TARGET_IMAGE, but this traversal only records the lower-case matrix/default keys. If those env entries are changed directly to postgres:16 while the matrix values remain allowlisted, the checker reports clean and the conformance job pulls a mutable Debian-based image, so the gate needs to inspect these override keys or bind the env handoff explicitly.

Useful? React with 👍 / 👎.

jeremi commented Jul 24, 2026

Copy link
Copy Markdown
Member Author

Superseded by #468 after taking a step back on scope. The replacement keeps only the remaining tutorial builder migration, the existing explicit Debian 13 guard, CI wiring, and cache isolation. It intentionally drops the generalized Docker, shell, and workflow parsing added here. Closing this PR so review and CI can focus on the smaller implementation.

@jeremi jeremi closed this Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants