Skip to content

Latest commit

 

History

History
531 lines (417 loc) · 22.5 KB

File metadata and controls

531 lines (417 loc) · 22.5 KB

Chainguard OSV v2 Feed Specification

This document describes the Chainguard v2 security feed format, officially rolled out on April 20, 2026. The v2 feed extends the Open Source Vulnerability (OSV) schema v1.7.5 with per-component ecosystem specific vulnerability data. Unlike the original Chainguard OSV feed, which only publishes resolved advisories (fixed and false positive), the v2 feed publishes all advisory statuses, including detections, pending upstream fixes, and other unresolved states, giving scanners complete visibility into Chainguard's vulnerability analysis. For details on the individual advisory statuses, see latest_event_status later in this document or Chainguard's summary of advisory statuses for broader context.

Note: This document assumes you're already familiar with the material covered in Foundational Concepts, including Chainguard's OSV feed. If you are migrating from the original Chainguard OSV feed, see Differences from the original feed for a summary of changes, including new architecture granularity and per-advisory component data.

Feed URLs

The v2 feed uses path-based versioning. The version identifier (v2) is part of the URL path, making it possible to consume a specific feed version without ambiguity. Future feed versions will follow the same convention (e.g., /v3/osv/).

Resource URL
Index https://advisories.cgr.dev/chainguard/v2/osv/all.json
Individual record https://advisories.cgr.dev/chainguard/v2/osv/<CGA-ID>.json

Note: The v2 feed is served from advisories.cgr.dev, the new dedicated domain for Chainguard's security advisory feeds. The legacy packages.cgr.dev domain used by the original feed will eventually be deprecated. New integrations should use advisories.cgr.dev exclusively.

Consuming the feed is a two-step process:

Step 1: Fetch the index

Request the index to get the list of all available vulnerability records:

GET https://advisories.cgr.dev/chainguard/v2/osv/all.json

The response is a JSON array of id and modified pairs, sorted by id:

[
    { "id": "CGA-224q-ccj5-2p53", "modified": "2026-01-07T00:00:00Z" },
    { "id": "CGA-2255-2h2p-73q2", "modified": "2026-01-07T00:00:00Z" },
    { "id": "CGA-2266-xcqq-qpf4", "modified": "2026-01-26T20:04:09.447Z" },
    ...
]

Each id is a parent CGA identifier that groups one or more advisory records for the same upstream vulnerability. Use the modified timestamps to detect changes since your last sync.

Step 2: Fetch individual records

Use an id from the index to request the full vulnerability record:

GET https://advisories.cgr.dev/chainguard/v2/osv/CGA-224q-ccj5-2p53.json

This returns the complete record for CGA-224q-ccj5-2p53, including all affected packages, version ranges, component details, severity, and references. The full record schema is described below.

The feed is refreshed hourly. To keep your scanner's data current, poll the index at least once per hour and re-fetch any records whose modified timestamp has changed.

Record Schema

The following schema shows the structure of a vulnerability record. Each field is described in the sections that follow.

{
    "schema_version": string,
    "id": string,
    "published": string,
    "modified": string,
    "severity": [ { "type": string, "score": string } ],
    "upstream": [ string ],
    "references": [ { "type": string, "url": string } ],
    "affected": [ {
        "package": {
            "ecosystem": string,
            "name": string,
            "purl": string
        },
        "ranges": [ {
            "type": string,
            "events": [ { "introduced": string, "fixed": string } ]
        } ],
        "ecosystem_specific": {
            "components": [ {
                "advisory_id": string,
                "architecture": string,
                "component_name": string,
                "component_version": string,
                "component_type": string,
                "component_location": string,
                "component_purl": string,
                "latest_event_status": string,
                "latest_event_timestamp": string
            } ]
        }
    } ]
}

For the official definitions of top-level fields, see the OSV schema specification. The sections below describe how Chainguard uses them.

schema_version field

{
    "schema_version": "1.7.5"
}

The schema_version field identifies which version of the OSV schema the record conforms to. All v2 feed records use schema version 1.7.5.

id field

{
    "id": "CGA-2mh2-vjwv-24h5"
}

The id field is the identifier for the vulnerability record. It is a Chainguard Advisory (CGA) identifier in the format CGA-xxxx-xxxx-xxxx.

published, modified fields

{
    "published": "2026-01-15T10:00:00Z",
    "modified": "2026-06-15T12:00:00Z"
}

The published field indicates the time the vulnerability was first recorded, as an RFC 3339 timestamp in UTC. It is derived from the earliest created_at timestamp across all advisories in the group. If no creation timestamp is available, the field is omitted.

The modified field indicates the time the record was last updated, as an RFC 3339 timestamp in UTC. It is derived from the latest updated_at timestamp across all advisories in the group. Given two records with the same id, the one with the later modified time should be considered authoritative.

severity field

{
    "severity": [
        { "type": "CVSS_V3", "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H" }
    ]
}

The severity field lists severity scores for the vulnerability. The type field identifies the scoring system. The score field contains the score string. The defined type values are CVSS_V2, CVSS_V3, and CVSS_V4.

If no severity data is available, the field is an empty array.

upstream field

{
    "upstream": ["CVE-2024-0001", "GHSA-abcd-efgh-ijkl"]
}

The upstream field lists identifiers for the upstream vulnerability that this record describes. These are typically CVE and GHSA identifiers. The relationship is unidirectional (asymmetric). This record references the listed upstream vulnerabilities, but those upstream records do not reference this CGA.

references field

{
    "references": [
        { "type": "WEB", "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-0001" }
    ]
}

The references field lists URLs for the vulnerability. Each entry has a type field and a url field. The defined type values follow the OSV specification: ADVISORY, ARTICLE, DETECTION, DISCUSSION, REPORT, FIX, INTRODUCED, PACKAGE, EVIDENCE, and WEB.

If no reference data is available, the field is an empty array.

affected[] field

{
    "affected": [ ... ]
}

The affected field lists packages affected by the vulnerability. Each entry represents one combination of package name, architecture, and ecosystem.

affected[].package field

{
    "package": {
        "ecosystem": "Chainguard",
        "name": "curl",
        "purl": "pkg:apk/chainguard/curl?arch=x86_64"
    }
}

The package field identifies the affected package.

The ecosystem field identifies the package ecosystem. A package may appear under one or both of the following ecosystems:

Ecosystem Description
Chainguard Packages built and distributed by Chainguard.
Wolfi Packages from the Wolfi repository. Wolfi packages receive both a Chainguard and a Wolfi affected entry with the same version range.

The name field is the package name as it appears in the APK repository (e.g., curl, openssl, go-1.23).

The purl field is a Package URL identifying the package. It includes an ?arch= qualifier specifying the CPU architecture (e.g., x86_64, aarch64). The PURL does not include a version component, since the affected version range is expressed in the ranges field.

affected[].ranges[] field

{
    "ranges": [
        {
            "type": "ECOSYSTEM",
            "events": [
                { "introduced": "0" },
                { "fixed": "8.5.0-r1" }
            ]
        }
    ]
}

The ranges field lists the affected version ranges. Each range contains a type and a list of events.

The type field is always ECOSYSTEM. Versions are compared using APK version ordering.

The events list contains version markers. Each event object has one property:

  • introduced is the version at which the vulnerability was introduced. The value "0" represents a version that sorts before all other versions.
  • fixed is the version at which the vulnerability was fixed. Versions from introduced up to but not including fixed are affected. The value "0" is a special convention indicating a false positive determination (resulting in an empty affected range).

There is one range per affected entry. The events are determined by the status aggregation rules.

affected[].ecosystem_specific field

{
    "ecosystem_specific": {
        "components": [ ... ]
    }
}

While the ranges field defines the vulnerability status for the package as a whole, the ecosystem_specific field provides the granular, per-component data that informed that status. It contains a single components array holding per-component data.

affected[].ecosystem_specific.components[] fields

{
    "advisory_id": "CGA-abcd-efgh-ijkl",
    "architecture": "x86_64",
    "component_name": "libcurl",
    "component_version": "8.4.0",
    "component_type": "apk",
    "component_location": "/usr/lib/libcurl.so.4",
    "component_purl": "pkg:apk/libcurl@8.4.0",
    "latest_event_status": "fixed",
    "latest_event_timestamp": "2024-06-15T12:00:00Z"
}

Each component object maps 1:1 to a Chainguard advisory record. The fields are:

The advisory_id field is the child CGA identifier for this specific advisory, rolling up to the parent group identified by the top-level id.

The architecture field is the CPU architecture of the package build (e.g., x86_64, aarch64).

The component_name field is the name of the component where the vulnerability was detected (e.g., libcurl, golang.org/x/crypto). This may differ from the package name when a package bundles other libraries.

The component_version field is the version of the component at the time of detection. This value is extracted from the advisory's detection scan data.

The component_type field identifies the component's package type using Syft type identifiers (e.g., apk, java-archive, go-module, python, npm, gem, rust-crate, php-composer, dotnet).

The component_location field is the filesystem path where the component was found within the package (e.g., /usr/lib/libcurl.so.4, /usr/share/java/netty.jar).

The component_purl field is a Package URL for the component, including its version. The PURL type is derived from the component_type. If the component type cannot be mapped to a PURL type, this field is empty.

The latest_event_status field is the resolution status of this advisory as a lowercase string. The defined values are:

Status Classification Mapping to UI Status Description
fixed Resolved Fixed A fix has been applied to the affected component and the vulnerability is no longer present.
false_positive_determination Resolved Not affected The vulnerability does not affect this component.
detection Unresolved Under investigation The vulnerability has been detected for the component and is awaiting further investigation to determine its impact.
true_positive_determination Unresolved Affected The vulnerability has been confirmed ro be present and affect the component.
pending_upstream_fix Unresolved Pending upstream fix Remediating the vulnerability is not possible until a fix is made available from the upstream project.
fix_not_planned Unresolved Fix not planned There are no plans to address the vulnerability in the component at this time.

The latest_event_timestamp field is the time of the most recent advisory event, as an RFC 3339 timestamp in UTC.

Status Aggregation

The package-level version range in affected[].ranges is determined by aggregating the status of each component in the affected group. Each component's latest event is classified as resolved or unresolved:

Classification Event Types
Resolved fixed, false_positive_determination
Unresolved detection, true_positive_determination, analysis_not_planned, fix_not_planned, pending_upstream_fix

These statuses combine using the following precedence (highest wins):

  1. Any component unresolved: the package is affected
  2. All resolved, at least one fixed: the package has a fix
  3. All resolved, all false positives: the vulnerability does not affect this package

Range Output

Outcome ranges[].events Meaning
Affected (unresolved) [{"introduced": "0"}] At least one component is unresolved. No fix version is present.
Fixed [{"introduced": "0"}, {"fixed": "<version>"}] All components are resolved and at least one has a fix. The fixed version comes from the advisory's fixed event.
Not affected (false positive) [{"introduced": "0"}, {"fixed": "0"}] All components are resolved and all are false positives.

Decision Matrix

The following matrix shows how two component statuses combine. All unresolved event types behave the same.

            FIXED         FALSE_POS     UNRESOLVED
FIXED       Fixed         Fixed         Affected
FALSE_POS   Fixed         Not affected  Affected
UNRESOLVED  Affected      Affected      Affected

Flowchart

For each (package, architecture, ecosystem) group:

  Any component unresolved?
  +-- YES: Affected (no fix version)
  +-- NO (all resolved):
       Any component fixed?
       +-- YES: Fixed at the advisory's version
       +-- NO:  Not affected (all false positives)

Examples

The following examples are taken from the live v2 feed. Click the record links to see the full data.

Fixed vulnerability

From CGA-224q-ccj5-2p53: a vulnerability in haproxy-2.2 (CVE-2025-32464) fixed in version 2.2.34-r0.

{
  "package": {
    "ecosystem": "Chainguard",
    "name": "haproxy-2.2",
    "purl": "pkg:apk/chainguard/haproxy-2.2?arch=x86_64"
  },
  "ranges": [
    {
      "type": "ECOSYSTEM",
      "events": [
        { "introduced": "0" },
        { "fixed": "2.2.34-r0" }
      ]
    }
  ],
  "ecosystem_specific": {
    "components": [
      {
        "advisory_id": "CGA-hxhw-2p27-xpg6",
        "architecture": "x86_64",
        "component_name": "haproxy-2.2",
        "component_version": "2.2.33-r40",
        "component_type": "apk",
        "component_location": "/.PKGINFO",
        "component_purl": "pkg:apk/haproxy-2.2@2.2.33-r40",
        "latest_event_status": "fixed",
        "latest_event_timestamp": "2025-04-25T13:01:15Z"
      }
    ]
  }
}

The range [{"introduced": "0"}, {"fixed": "2.2.34-r0"}] indicates that all versions before 2.2.34-r0 are affected. A scanner seeing haproxy-2.2 at version 2.2.33-r40 should flag this vulnerability.

False positive

From CGA-6f85-jvrm-qq3w: CVE-2026-32288 was determined to be a false positive for the Go stdlib component in actions-runner-controller.

{
  "package": {
    "ecosystem": "Chainguard",
    "name": "actions-runner-controller",
    "purl": "pkg:apk/chainguard/actions-runner-controller?arch=aarch64"
  },
  "ranges": [
    {
      "type": "ECOSYSTEM",
      "events": [
        { "introduced": "0" },
        { "fixed": "0" }
      ]
    }
  ],
  "ecosystem_specific": {
    "components": [
      {
        "advisory_id": "CGA-32qj-p4rc-7jv7",
        "architecture": "aarch64",
        "component_name": "stdlib",
        "component_version": "go1.26.1",
        "component_type": "go-module",
        "component_location": "/usr/bin/ghalistener",
        "component_purl": "pkg:golang/stdlib@go1.26.1",
        "latest_event_status": "false_positive_determination",
        "latest_event_timestamp": "2026-04-10T20:56:35Z"
      }
    ]
  }
}

The range [{"introduced": "0"}, {"fixed": "0"}] uses "0" as both the introduced and fixed version, resulting in an empty affected range. A scanner should not flag this vulnerability for this package.

Unresolved vulnerability

From the same record CGA-224q-ccj5-2p53: the same CVE-2025-32464 vulnerability affects haproxy-2.9, but a fix is pending from the upstream project.

{
  "package": {
    "ecosystem": "Chainguard",
    "name": "haproxy-2.9",
    "purl": "pkg:apk/chainguard/haproxy-2.9?arch=x86_64"
  },
  "ranges": [
    {
      "type": "ECOSYSTEM",
      "events": [
        { "introduced": "0" }
      ]
    }
  ],
  "ecosystem_specific": {
    "components": [
      {
        "advisory_id": "CGA-72p4-pqqf-9q8x",
        "architecture": "x86_64",
        "component_name": "haproxy-2.9",
        "component_version": "2.9.10-r40",
        "component_type": "apk",
        "component_location": "/.PKGINFO",
        "component_purl": "pkg:apk/haproxy-2.9@2.9.10-r40",
        "latest_event_status": "pending_upstream_fix",
        "latest_event_timestamp": "2025-04-14T20:11:36Z"
      }
    ]
  }
}

The range contains only [{"introduced": "0"}] with no fixed event. No fix version is available because the vulnerability is still unresolved. A scanner should flag this. The latest_event_status of pending_upstream_fix tells you why no fix exists yet.

Note that all unresolved statuses (detection, true_positive_determination, pending_upstream_fix, fix_not_planned) produce the same range output. The latest_event_status in components is the only way to distinguish between them.

Mixed component statuses

When a single affected entry contains multiple components with different statuses, the decision matrix determines the package-level range. Any unresolved component keeps the entire package affected, even if other components are fixed:

{
  "package": {
    "ecosystem": "Chainguard",
    "name": "nginx",
    "purl": "pkg:apk/chainguard/nginx?arch=x86_64"
  },
  "ranges": [
    {
      "type": "ECOSYSTEM",
      "events": [
        { "introduced": "0" }
      ]
    }
  ],
  "ecosystem_specific": {
    "components": [
      {
        "advisory_id": "CGA-comp-fixed",
        "architecture": "x86_64",
        "component_name": "libxml2",
        "component_version": "2.11.0",
        "component_type": "apk",
        "component_location": "/usr/lib/libxml2.so.2",
        "component_purl": "pkg:apk/libxml2@2.11.0",
        "latest_event_status": "fixed",
        "latest_event_timestamp": "2024-05-20T11:00:00Z"
      },
      {
        "advisory_id": "CGA-comp-detected",
        "architecture": "x86_64",
        "component_name": "pcre2",
        "component_version": "10.42",
        "component_type": "apk",
        "component_location": "/usr/lib/libpcre2-8.so.0",
        "component_purl": "pkg:apk/pcre2@10.42",
        "latest_event_status": "detection",
        "latest_event_timestamp": "2024-04-01T09:00:00Z"
      }
    ]
  }
}

The libxml2 component is fixed, but the unresolved pcre2 component keeps the package-level range as [{"introduced": "0"}] with no fix. The components array shows which components are resolved and which are not.

Differences from the original feed

The v2 feed differs from the original Chainguard OSV feed (https://packages.cgr.dev/chainguard/osv/) in the following ways. Note that the v2 feed is also served from a new domain, advisories.cgr.dev. The packages.cgr.dev domain will eventually be deprecated.

Aspect Original feed v2 feed
Feed URL packages.cgr.dev/chainguard/osv/all.json advisories.cgr.dev/chainguard/v2/osv/all.json
Record URL packages.cgr.dev/chainguard/osv/<CGA-ID>.json advisories.cgr.dev/chainguard/v2/osv/<CGA-ID>.json
Affected entry granularity One entry per (package, ecosystem) One entry per (package, architecture, ecosystem)
Architecture Not represented Included as ?arch= PURL qualifier
Alias field related upstream
Component detail Not present Per-advisory data in ecosystem_specific.components
Fixed version Highest version across all advisories for a package Aggregate fixed version across advisory events for (package, architecture, ecosystem)
Advisory coverage Only resolved advisories (fixed, false positive) All advisory statuses published in a single feed, including detections, pending upstream fixes, and other unresolved states
Severity and references Not present From vulnerability metadata when available
Schema version Not set 1.7.5

The original feed will eventually be archived. New integrations should use the v2 feed or later.

Related Resources