Software Bill of Materials

Current OpenIAP release workflows attach a machine-readable inventory of direct runtime components and dependency contracts, including first-party OpenIAP native contracts, to each GitHub Release as a CycloneDX 1.6 JSON file. A daily repair job fills missed latest-stable-release assets; prereleases rely on their release-time dispatch. Exact application exposure comes from the consumer's resolved dependency graph.

Finding one#

SBOMs are release assets, named after the package and version they describe:

<package>-<version>.cdx.json

react-native-iap-16.3.0.cdx.json
openiap-google-3.3.0.cdx.json
flutter_inapp_purchase-10.3.0.cdx.json

Download one from its release:

gh release download react-native-iap-16.3.0 \
  --repo hyodotdev/openiap -p '*.cdx.json'

One SBOM is published per releasable component — the SDKs, the native packages, and the specification — rather than one for the whole monorepo, because a repository-wide document would describe something nobody installs.

Verifying it#

The SBOM carries a build provenance attestation, so you can confirm OpenIAP's CI produced it rather than trusting the file on sight:

CERT_IDENTITY=https://github.com/hyodotdev/openiap
CERT_IDENTITY="$CERT_IDENTITY/.github/workflows/sbom.yml@refs/heads/main"
gh attestation verify react-native-iap-16.3.0.cdx.json \
  --repo hyodotdev/openiap --cert-identity "$CERT_IDENTITY" \
  --deny-self-hosted-runners

And validate it against the CycloneDX schema:

cyclonedx validate --input-file react-native-iap-16.3.0.cdx.json \
  --input-format json --input-version v1_6 --fail-on-errors

Every tool below is independent of this repository, so verification never requires trusting our tooling:

ToolRoleLicense
gh attestation verifyConfirm CI provenance against SigstoreMIT
cyclonedx-cliValidate against the published schemaApache-2.0
sbomqsScore quality and NTIA complianceApache-2.0
osv-scannerMatch components against the OSV databaseApache-2.0
grypeMatch components against vulnerability feedsApache-2.0

What is inside#

Each document records:

  • The component's name, version, and package URL (purl), matching the published package
  • The repository URL and the exact commit the release was built from
  • The exact generator commit used to create the SBOM
  • The release tag, so an artifact and its inventory cannot be mismatched
  • Every direct runtime dependency, with version or constraint, purl, and available supplier and license data

Deliberately excluded: test and build-only dependencies. Peer dependencies from JavaScript manifests are not counted as bundled npm runtime code, while native CocoaPods and Maven contracts shipped through framework SDKs are listed separately. Operating-system frameworks such as StoreKit are not distributed packages and are likewise absent.

Vulnerability analysis (VEX)#

When a scanner flags a CVE against a dependency, the useful question is whether it is reachable through OpenIAP's use of that dependency. Where that has been analysed, the judgement travels in the same SBOM as a CycloneDX vulnerabilities entry, with a state such as not_affected and a required justification.

If a release's SBOM has no vulnerabilities section, it means no CVE has been analysed for that release — not that a scan was run and came back clean.

Standards and tooling#

Everything here is an open standard, most of it maintained under the Linux Foundation or OWASP.

ConcernStandard
Document formatCycloneDX 1.6
Component identityPackage URL (purl)
License identitySPDX identifiers
Required data fieldsNTIA minimum elements
Vulnerability analysisCycloneDX VEX
ProvenanceSLSA in in-toto statements, signed via Sigstore

Current limits#

Stated plainly, so you can judge the evidence:

  • Transitive dependencies are included only where the ecosystem's resolver output is available. Release SBOMs otherwise describe the documented direct-dependency source.
  • Licenses and suppliers combine reviewed local metadata with point-in-time registry enrichment. Unavailable metadata is omitted, and pub.dev and some NuGet packages do not expose a standard value.
  • Version constraints remain constraints when a library manifest does not select one exact version. Use the consuming application lockfile for exact CVE matching.
  • KMP target scope uses the published canonical/common KMP POM plus exact Apple, Play, Horizon, and Amazon contracts. A single-platform application should use its resolved target graph.
  • MAUI target scope is the union of the published nuspec's target-framework groups. Grouped dependencies remain optional and platform-labelled; use the consuming application's resolved graph to narrow the aggregate.

Where a dependency cannot be resolved, generation fails rather than emitting a shorter list — an inventory that silently omits something is worse than none, because it is trusted.