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-amd64The 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 isscan, which is not in this project'sstages:list (build,test,security,deploy,release). Pinned tosecurity.runner_tag: the component's default (saas-linux-medium-amd64) is more capacity than ajq+bashjob over one JSON file needs. Pinned tosaas-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-summaryis instantiated (its defaultjob_rulesmatches$CI_COMMIT_TAG) butcontainer_scanningis 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) (branchci/ephemeral-first) fixes this specific divergence: it replacescontainer_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_TAGrule. Once it lands,container_scanningruns 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, fromstig_tools/**/*alone to a sharedci_relevant_pathsanchor coveringstig_tools/**/*,.gitlab-ci.yml,templates/**/*, andscripts/**/*. That narrows, but does not close, the divergence withcontainer-scan-summary: a merge request touching only paths outside that list will still instantiatecontainer-scan-summary(its default rule matches any merge request event, with nochanges:filter) but notcontainer_scanning-- another "no report" case. A plain branch push or a web-triggered pipeline that does runcontainer_scanning(both are unconditional matches in the override) still won't get acontainer-scan-summaryrun, because the component's defaultjob_ruleshas 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.
Related issues
N/A. Follow-ups, none blocking this MR:
- Follow-up MR !7 (merged) (branch
ci/ephemeral-first) fixescontainer_scanning'sCS_IMAGEso it scans the image this project actually builds, and gives it an explicitrules:override that adds a$CI_COMMIT_TAGrule and widens itschanges:gating fromstig_tools/**/*alone to a sharedci_relevant_pathsanchor (stig_tools/**/*,.gitlab-ci.yml,templates/**/*,scripts/**/*). - Align
container-scan-summary'sjob_ruleswith an array-shaped rules anchor (see above -- notfull_pipeline_rulesitself, 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 theinclude:block. - 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:
-
Pointing the same include at a tag that does not exist (
v999.0.0) fails lint withComponent '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. -
The GitLab CI Lint API's merged-YAML output for this branch's
.gitlab-ci.ymlshows 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 ... -
This include adds one lint warning that is not present on
master. Lintingmaster's.gitlab-ci.ymlunmodified returns zero warnings. Linting this branch returns one:retry uses deprecated
whenvalue(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/#retrywhenThis originates in the component itself:
container-scan-summary'stemplate.ymlatv6.1.0setsretry: when: [runner_system_failure, stuck_or_timeout_failure], and the component exposes no input to override itsretryblock. Fixing it needs an upstream change ingitlab-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.