# Security Labels and Metadata

> Container labels, embedded metadata, and SBOMs for vulnerability scanning

---

LLMS index: [llms.txt](https://hummingbird-project.io/llms.txt) | Full content: [llms-full.txt](https://hummingbird-project.io/llms-full.txt)

---

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](https://hummingbird-project.io/docs/background/containers/container-image-labels/) 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][schema] published by Red Hat
Product Security.

### Fields

The [embedded_metadata.v1 schema][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](https://hummingbird-project.io/docs/background/containers/container-image-labels/) for the complete
label reference.

### Example

```json
{
  "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](https://hummingbird-project.io/docs/background/containers/image-pipeline/#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][]:

```bash
IMAGE="registry.access.redhat.com/hi/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):

```text
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):

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

**Hermeto** (build, source):

```text
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:

```json
{
  "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

## Related Files

| 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          |
| `images/hummingbird-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

- [Container Image Labels](https://hummingbird-project.io/docs/background/containers/container-image-labels/) -- complete reference for
  all image labels across all categories
- [Image Pipeline -- SBOM Generation](https://hummingbird-project.io/docs/background/containers/image-pipeline/#sbom-generation) --
  how the production SBOMs are built from Syft, Hermeto, and Mobster

## References

- [Embedded Metadata Schema][schema]
- [Container-First Vulnerability Reporting][konflux-feature] (Konflux feature
  specification)
- [VEX (Vulnerability Exploitability eXchange)][vex]
- [Syft][syft] -- SBOM generation tool
- [Hermeto][hermeto] -- build-time dependency provenance tool
- [Mobster][mobster] -- SBOM merging tool
- [SPDX Specification][spdx]

[schema]: https://github.com/RedHatProductSecurity/security-data-guidelines/blob/main/schema/embedded_metadata.v1.schema.json
[konflux-feature]: https://issues.redhat.com/browse/KONFLUX-6210
[vex]: https://security.access.redhat.com/data/csaf/v2/vex/
[syft]: https://github.com/anchore/syft
[hermeto]: https://github.com/hermetoproject/hermeto
[mobster]: https://github.com/konflux-ci/mobster
[cosign]: https://github.com/sigstore/cosign
[spdx]: https://spdx.github.io/spdx-spec/v2.3/
