ci: consume the container-scan-summary catalog component

What does this MR do?

Adds the first consumer include from the estate's Continuous Integration/Continuous Deployment (CI/CD) Catalog. .gitlab-ci.yml now includes the container-scan-summary component from gitlab-com/public-sector/pipeline at exact tag v6.1.0, placed directly after the existing security-scanning template: includes:

- component: $CI_SERVER_FQDN/gitlab-com/public-sector/pipeline/container-scan-summary@v6.1.0
  inputs:
    stage: security
    runner_tag: saas-linux-small-amd64

The component reads the artifact already produced by the container_scanning job (from GitLab's own Jobs/Container-Scanning.gitlab-ci.yml template, which this project already includes). It tallies findings by severity and lists the top Critical and High findings into vulnerability-summary.md, which it saves as a job artifact and also prints to the job log -- "paste-friendly for triage," in the component's own description. It does not post to the merge request, and it does not remove the need to open the Security tab for the full report; the benefit is copy-paste-ready text once you already have a reason to look, not a saved click.

Two inputs needed an override:

  • stage: the component's default is scan, which is not in this project's stages: list (build, test, security, deploy, release). Pinned to security.
  • runner_tag: the component's default (saas-linux-medium-amd64) is more capacity than a jq + bash job over one JSON file needs. Pinned to saas-linux-small-amd64.

Every other input (job_name, container_scanning_job, report_path, top_n) matches this project's existing setup with no override needed: container_scanning_job defaults to container_scanning, the exact job name the Container Scanning template produces here, and report_path defaults to gl-container-scanning-report.json, the standard artifact name that template writes.

job_rules does not currently line up with container_scanning's own cadence

The component's default job_rules runs container-scan-summary on merge request pipelines, the default branch, and tags. container_scanning's own merged rules (from GitLab's template, unmodified in this project) run it on any branch push pipeline ($CI_COMMIT_BRANCH set) and, conditionally, on merge request pipelines gated behind $AST_ENABLE_MR_PIPELINES (which this project does not set). It never runs on tag pipelines -- no rule in the template matches $CI_COMMIT_TAG.

That produces two concrete divergences, verified against the merged pipeline configuration:

  • Today, on tag pipelines: container-scan-summary is instantiated (its default job_rules matches $CI_COMMIT_TAG) but container_scanning is not (no rule in the template matches a tag pipeline), so the summary always prints "no report" on a tag pipeline. Follow-up MR !7 (merged) (branch ci/ephemeral-first) fixes this specific divergence: it replaces container_scanning's rules wholesale with an explicit override (a full override, not a merge -- rules: is an array, and GitLab's include-merge semantics replace an array key rather than concatenate it) that adds a $CI_COMMIT_TAG rule. Once it lands, container_scanning runs on tag pipelines and this "no report" case goes away.
  • That same follow-up also widens the changes: scope its own jobs (and, via the override above, container_scanning) gate on, from stig_tools/**/* alone to a shared ci_relevant_paths anchor covering stig_tools/**/*, .gitlab-ci.yml, templates/**/*, and scripts/**/*. That narrows, but does not close, the divergence with container-scan-summary: a merge request touching only paths outside that list will still instantiate container-scan-summary (its default rule matches any merge request event, with no changes: filter) but not container_scanning -- another "no report" case. A plain branch push or a web-triggered pipeline that does run container_scanning (both are unconditional matches in the override) still won't get a container-scan-summary run, because the component's default job_rules has no generic branch or web-source condition -- only default-branch, merge-request, and tag. So the divergences that survive that follow-up landing are: merge requests touching only non-CI paths, plain branch pushes, and web pipelines.

The fix for those surviving divergences is to feed container-scan-summary the same rules this project's own jobs already run under -- but not literally via inputs: job_rules: *full_pipeline_rules: &full_pipeline_rules anchors a mapping ({rules: [...]}), while job_rules is typed as an array input, so aliasing the mapping in directly is a type mismatch, not a working reference. The anchor would instead need to point at the bare array of conditions, the same idiom ci_relevant_paths already uses for the changes: list (an array anchored on its own, not wrapped in a keyed block) -- e.g. a &full_pipeline_rules_array anchor holding just the - if: ... entries, aliased in as inputs: job_rules: *full_pipeline_rules_array. That's left as a follow-up rather than done here for a second reason on top of the type mismatch: today, &full_pipeline_rules (and any array-shaped sibling anchor) is defined below this include: block, and a YAML alias must resolve to an anchor already declared earlier in the same document, so it cannot be referenced from here regardless. Hand-copying the rule list into this include instead would create a second copy that silently drifts the next time the anchor changes. The follow-up is to introduce the array-shaped anchor and move it above the include block, then alias it in.

N/A. Follow-ups, none blocking this MR:

  1. Follow-up MR !7 (merged) (branch ci/ephemeral-first) fixes container_scanning's CS_IMAGE so it scans the image this project actually builds, and gives it an explicit rules: override that adds a $CI_COMMIT_TAG rule and widens its changes: gating from stig_tools/**/* alone to a shared ci_relevant_paths anchor (stig_tools/**/*, .gitlab-ci.yml, templates/**/*, scripts/**/*).
  2. Align container-scan-summary's job_rules with an array-shaped rules anchor (see above -- not full_pipeline_rules itself, which is a mapping and cannot be aliased directly into an array input); depends on (1) landing, that array anchor existing, and it moving above the include: block.
  3. This project has not adopted the ADOPTION.md baseline gate (osv-verdict + automerge-gate, called "L0" in that document); this MR is deliberately the first and smallest possible catalog include, to prove the include mechanics end to end, not an attempt at that fuller profile.

Documentation updates

None required. This is a CI/CD pipeline configuration change with no user-facing feature, input, or behavior change to document in README.md.

CHANGELOG.md updated

Yes. Added a new ### Added - CI/CD Catalog: container-scan-summary component entry under [Unreleased], placed at the tail of the Unreleased section (immediately before the [1.2.4] boundary, the first released section after the upstream absorb) to avoid colliding with other open merge requests adding entries nearer the top of the same section.

Checklist

  • README.md updated (if applicable) -- N/A, no user-facing change
  • CHANGELOG.md updated
  • Security documentation updated (if applicable) -- N/A, no change to authentication, access control, or data handling
  • Usage examples added (for new features) -- N/A, no new input surface for consumers of this project
  • Pipeline passes -- not yet applicable: this branch is pushed to origin but no MR has been opened, so no pipeline has run against it yet. Config lints valid, including the merged component YAML (see Validation below); the pipeline runs once an MR is posted.

Validation

Config-level validation only -- no pipeline has run yet (see checklist above).

glab ci lint .gitlab-ci.yml in a fresh clone of this branch:

Validating...
✓ CI/CD YAML is valid!

Confirmed the include actually resolves against the live catalog (not just a YAML syntax check), and this include's effect on lint output:

  1. Pointing the same include at a tag that does not exist (v999.0.0) fails lint with Component 'gitlab.com/gitlab-com/public-sector/pipeline/container-scan-summary@v999.0.0' - content not found, proving the lint fetches the real component rather than skipping unresolved includes.

  2. The GitLab CI Lint API's merged-YAML output for this branch's .gitlab-ci.yml shows the injected job exactly as expected:

    container-scan-summary:
      stage: security
      image: registry.gitlab.com/gitlab-com/public-sector/pipeline/ci-tools:v6.1.0
      tags:
        - saas-linux-small-amd64
      needs:
        - job: container_scanning
          artifacts: true
          optional: true
      ...
  3. This include adds one lint warning that is not present on master. Linting master's .gitlab-ci.yml unmodified returns zero warnings. Linting this branch returns one:

    retry uses deprecated when value(s): stuck_or_timeout_failure. These match the more specific failure reasons that replaced them; migrate to those reasons. See https://docs.gitlab.com/ci/yaml/#retrywhen

    This originates in the component itself: container-scan-summary's template.yml at v6.1.0 sets retry: when: [runner_system_failure, stuck_or_timeout_failure], and the component exposes no input to override its retry block. Fixing it needs an upstream change in gitlab-com/public-sector/pipeline, not anything on the consumer side.

Honesty check: container_scanning's image path

Today, container_scanning's CS_IMAGE variable is not set in this project (the override exists in .gitlab-ci.yml but is commented out), so it falls back to the Container Scanning template's default. CS_IMAGE defaults to $CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG, and those in turn default to $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG and $CI_COMMIT_SHA (confirmed against GitLab's container scanning documentation) -- so container_scanning actually scans $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA, a per-branch subpath of this project's registry, not the image this pipeline actually builds and pushes ($CI_REGISTRY_IMAGE/stig-analyzer:$CI_COMMIT_SHORT_SHA). This is an observed divergence, not a theoretical one: !5 (merged)'s container_scanning job log shows it scanning .../stig-analyzer/ci-duo-cli-canary:<sha>, an image this pipeline never builds.

Follow-up MR !7 (merged) (branch ci/ephemeral-first) addresses that by setting CS_IMAGE to the image this pipeline actually builds. Until it lands, container-scan-summary degrades gracefully: its script checks for the report file before doing anything else, and emits a short "no report at this stage" markdown summary with exit 0 rather than failing the job. Once that follow-up lands, this same include starts producing a real, populated summary with no further change needed here. That same follow-up is also expected to move container_scanning itself into the security stage; the ordering between it and container-scan-summary stays needs:-driven either way (needs: [{job: container_scanning, artifacts: true, optional: true}]), so pinning stage: security on this include remains correct regardless of which stage container_scanning itself ends up in.

A note on the include form and Renovate

This follows the pipeline project's convention for catalog includes: component: $CI_SERVER_FQDN/gitlab-com/public-sector/pipeline/<name>@vX.Y.Z, with one exact catalog version across the file (there is only one component reference here, so that holds trivially). That specific $CI_SERVER_FQDN-prefixed form comes from the pipeline project's Renovate preset, not from ADOPTION.md's prose (whose examples use the literal gitlab.com/... form). The preset explicitly disables Renovate's native gitlabci manager for these pins, because that manager treats the unresolved $CI_SERVER_FQDN CI variable as the literal registry host and always returns a lookup failure; a custom regex manager in the same preset is what actually tracks these pins (matching gitlab-com/public-sector/pipeline/<name>@vX.Y.Z regardless of what precedes it, resolved via the gitlab-releases datasource). I verified the custom manager's regex matches this pin. I have not verified that Renovate has actually opened an update MR against a pin like this one -- this project has no observable Renovate footprint yet (no Dependency Dashboard issue since enrollment), so whether the automated bump path works end to end here is confirmed only by regex inspection, not by an observed run.

Edited by Andrew Dunn

Merge request reports

Loading
Loading