Security Labels and Metadata

Container labels, embedded metadata, and SBOMs for vulnerability scanning

Hummingbird container images include labels, embedded metadata, and SBOMs that enable security scanners to perform container-first vulnerability reporting.

Overview

Security scanners need a way to determine which vulnerabilities are applicable to a container image. Three mechanisms provide this information:

  1. Container Labels - OCI image labels (name, cpe) set in the Containerfile
  2. Embedded Metadata - A labels.json file written to the container filesystem during build
  3. Software Bill of Materials (SBOM) - An SPDX document listing all packages, attached as an OCI artifact alongside the image

Labels and embedded metadata identify the product; the SBOM identifies the packages within it. Labels and embedded metadata provide the same core information but serve different access paths: labels are accessible via container inspection tools, while embedded metadata is accessible to scanners with only filesystem access.

Applicability

The cpe label is only added to released Hummingbird distro images (no CPE identifier for non-product images). All other labels are added to all images.

The labels.json embedded metadata file contains all Containerfile LABEL values plus auto-computed fields.

SBOMs are attached to all released images (both Rawhide and Hummingbird).

Container Labels

name

  • Type: String
  • Format: <org>/<image> or <org>/<image>-<variant>
  • Examples: hummingbird/nodejs-24, hummingbird/nodejs-24-builder, hummingbird/caddy
  • Description: Canonical name for the container image. This is the name that appears in VEX (Vulnerability Exploitability eXchange) statements and is used by scanners to map vulnerabilities to specific images.

The canonical name is derived from the image directory name (which includes the version, e.g. nodejs-24) and the variant. For default variants, the name is hummingbird/<image> (e.g. hummingbird/nodejs-24). For non-default variants, the variant is appended with a hyphen (e.g. hummingbird/nodejs-24-builder). Note that the canonical name may differ from the registry repository path. For example, nodejs-24 and nodejs-20 are both pushed to the hummingbird/nodejs registry repository with different tags, but their canonical names are hummingbird/nodejs-24 and hummingbird/nodejs-20 respectively.

See container-image-labels.md for the full name label format across all image types.

cpe

  • Type: String (CPE 2.2 formatted)
  • Format: cpe:/a:redhat:hummingbird:1
  • Description: Common Platform Enumeration (CPE) identifier for the container. CPE is a standardized naming scheme for software products that enables correlation with vulnerability databases.

The CPE value is defined globally in images/variables.yml and applied to all Hummingbird images. Containers with the same CPE are considered the same software product for vulnerability reporting purposes.

org.opencontainers.image.created

  • Type: String (RFC3339 timestamp)
  • Format: 2025-01-15T12:00:00Z
  • Description: Creation timestamp of the container image. Used by scanners to determine if an image predates or postdates a vulnerability fix.

If SOURCE_DATE_EPOCH is set during the build, that timestamp is used instead of the actual build time, supporting reproducible builds.

Embedded Metadata (labels.json)

The labels.json file is written to /usr/share/buildinfo/labels.json inside the container filesystem by the inject-source-info script during build. It contains all Containerfile LABEL values plus auto-computed fields like architecture and org.opencontainers.image.created.

Schema

The file follows the embedded_metadata.v1 schema published by Red Hat Product Security.

Fields

The embedded_metadata.v1 schema defines the minimum fields required by security scanners:

Field Type Description
name string Canonical container name (e.g., hummingbird/nodejs-24)
cpe string CPE identifier (e.g., cpe:/a:redhat:hummingbird:1)
architecture string Target architecture (e.g., amd64, arm64)
org.opencontainers.image.created string RFC3339 creation timestamp

The file also includes all other Containerfile LABEL values (e.g., description, summary, vendor, version). See container-image-labels.md for the complete label reference.

Example

{
  "name": "hummingbird/caddy",
  "cpe": "cpe:/a:redhat:hummingbird:1",
  "architecture": "amd64",
  "org.opencontainers.image.created": "2025-01-15T12:00:00Z"
}

Software Bill of Materials (SBOM)

Each released image has a per-architecture SPDX 2.3 SBOM attached as an OCI artifact. See SBOM Generation for how the production SBOMs are built from Syft, Hermeto, and Mobster.

Accessing SBOMs

SBOMs are stored as OCI artifacts alongside each per-architecture image. To download one, resolve the per-arch digest and use cosign:

IMAGE="quay.io/hummingbird/caddy"
TAG="latest"
ARCH="amd64"

# Get per-architecture digests from the image index
skopeo inspect --raw "docker://${IMAGE}:${TAG}" \
  | jq '.manifests[] | {digest, platform}'

# Download the SBOM for the chosen architecture
ARCH_DIGEST="$(skopeo inspect --raw "docker://${IMAGE}:${TAG}" \
  | jq -r --arg arch "${ARCH}" \
    '.manifests[] | select(.platform.architecture == $arch) | .digest')"
cosign download sbom "${IMAGE}@${ARCH_DIGEST}" > sbom.json

Entry Sources

The merged SBOM contains entries from two tools:

Source Content Scope
Syft Installed binary RPMs + non-RPM packages Image architecture only
Hermeto Build-time dependencies from lockfiles All architectures + source RPMs

A package installed in the final image may have entries from both Syft and Hermeto (with different metadata), since it was both a build dependency and is present at runtime.

Distinguishing Entry Sources

After the Mobster merge, Syft and Hermeto entries carry mutually exclusive markers:

Marker Syft Hermeto
sourceInfo field present
licenseDeclared set
CPE references
upstream= in PURL
Annotation containing “hermeto”
repository_id= in PURL

As a general rule, any entry with an annotation containing the word “hermeto” (case-insensitive) originates from Hermeto (build-time provenance). All other entries originate from Syft (runtime image scan).

PURL Format

Syft and Hermeto use different PURL qualifier sets for the same package. Examples from caddy:latest (Hummingbird, amd64):

Syft (runtime):

pkg:rpm/hummingbird/caddy@2.10.2-1.hum1?arch=x86_64&distro=hummingbird-20251124&upstream=caddy-2.10.2-1.hum1.src.rpm

Hermeto (build, binary):

pkg:rpm/caddy@2.10.2-1.hum1?arch=x86_64&checksum=sha256:ca02a0...&repository_id=public-hummingbird-x86_64-rpms

Hermeto (build, source):

pkg:rpm/caddy@2.10.2-1.hum1?arch=src&checksum=sha256:42912d...&repository_id=public-hummingbird-source-rpms

Key differences:

  • Distro namespace: Syft includes the distro in the PURL path (pkg:rpm/hummingbird/...). Hermeto omits it (pkg:rpm/...).
  • EVR: Syft populates versionInfo with the full epoch:version-release. Hermeto sets versionInfo to the bare upstream version; the full EVR is only in the PURL @version.
  • Architecture: Syft entries match the image architecture. Hermeto entries carry an arch= PURL qualifier that may be the image arch, a different arch (cross-arch), noarch, or src (source RPMs).
  • Source RPM: Syft entries carry upstream=<srpm> in the PURL. Hermeto entries have separate arch=src entries instead.

Hermeto Annotation Format

Hermeto entries carry annotations with JSON-encoded metadata:

{
  "annotationDate": "2026-03-02T11:58:12Z",
  "annotationType": "OTHER",
  "annotator": "Tool: hermeto:jsonencoded",
  "comment": "{\"name\": \"hermeto:found_by\", \"value\": \"hermeto\"}"
}

Entry Breakdown Example

Typical counts for caddy:latest (Hummingbird, amd64); exact counts vary as image dependencies change:

Source Typical count Content
Syft ~53 Binary RPMs (x86_64 + noarch)
Syft 1 Go module (stdlib)
Syft 2 OCI image metadata
Hermeto ~41 Binary RPMs (x86_64)
Hermeto ~41 Binary RPMs (aarch64, cross-arch)
Hermeto ~11 Binary RPMs (noarch)
Hermeto ~36 Source RPMs (arch=src)

How Scanners Use This Metadata

  1. CPE Matching: Scanners use the cpe value to look up applicable VEX statements for the product
  2. Name Matching: The name identifies which specific container the VEX statements apply to
  3. Version Comparison: The creation timestamp enables comparison between the scanned image and fixed versions reported in VEX statements
  4. Package Enumeration: Scanners use the SBOM to enumerate all packages in the image and correlate them with vulnerability databases via PURLs and CPEs
File Purpose
documentation/background/container-image-labels.md Complete reference for all image labels
documentation/background/image-pipeline.md SBOM generation pipeline (Stage 3)
images/variables.yml Defines the global cpe value
ci/images/builder/inject-source-info.sh Script that creates labels.json
macros/inject_source_info_labels.yml.j2 Macro that adds LABEL to Containerfile
macros/install_newroot.yml.j2 Macro that invokes inject-source-info

See Also

References