SBOM Guardrails
Enforce Software Bill of Materials standards across your organization. Verify SBOMs are generated, contain license data, use approved formats, keep licenses within an allow-list or out of a denylist, and flag dependencies with blocked geographic origins or disallowed package patterns.
sbom to your lunar-config.yml:uses: github://earthly/lunar-lib/policies/sbom@v1.0.5
Included Guardrails
This policy includes 8 guardrails that enforce standards for your security and compliance.
sbom-exists
Ensures an SBOM was generated, either automatically or detected in CI.
has-licenses
Verifies that SBOM components have license information populated. Fails if license coverage is below the configured threshold.
disallowed-licenses
Checks for disallowed licenses in SBOM components. Matches component licenses against configurable regex patterns.
allowed-licenses
Fails when an SBOM component carries a license that allowed_licenses does not admit. Skips until allowed_licenses is configured.
min-components
Verifies the SBOM contains a minimum number of components. Catches trivially empty SBOMs that may indicate detection failures.
standard-format
Validates the SBOM uses an approved format (CycloneDX, SPDX). Auto-passes if no format restriction is configured.
blocked-origins
Checks for dependencies with license origin mentions from blocked countries. Supports blocklist or allowlist mode. Requires the license-origins collector.
disallowed-packages
Checks for disallowed packages by matching PURL, name, or group against configurable regex patterns (e.g. "ru\.yandex\..", "com\.alibaba\..").
How Guardrails Fit into Lunar
Lunar guardrails define your engineering standards as code. They evaluate data collected by integrations and produce pass/fail checks with actionable feedback.
Policies support gradual enforcement—from silent scoring to blocking PRs or deployments—letting you roll out standards at your own pace without disrupting existing workflows.
Learn How Lunar Works →Required Integrations
This policy evaluates data gathered by one or more of the following integration(s).
Make sure to enable them in your lunar-config.yml.
Configuration
Configure this policy in your lunar-config.yml.
Inputs
| Input | Required | Default | Description |
|---|---|---|---|
disallowed_licenses
|
Required | — | Regex patterns of disallowed licenses. Accepts a comma-separated string (e.g. "GPL.*,AGPL.*") or a JSON array (e.g. '["GPL.*", "AGPL.*"]'). |
allowed_licenses
|
Required | — | Licenses components may use, as SPDX IDs or regex patterns that must match the whole ID, case-insensitively (e.g. '["MIT", "Apache-2.0", "BSD-.*-Clause"]'). Accepts a comma-separated string or a JSON array. Empty skips the allowed-licenses check. |
min_license_coverage
|
Optional |
50
|
Minimum percentage of components that must have license info (0-100) |
min_components
|
Optional |
1
|
Minimum number of components the SBOM must contain |
allowed_formats
|
Required | — | Comma-separated list of allowed SBOM formats (e.g. "cyclonedx,spdx"). Empty means any. |
blocked_countries
|
Required | — | Comma-separated list of blocked countries for license origin checks (e.g. "Russia,China,Iran,North Korea") |
disallowed_packages
|
Required | — | Regex patterns for disallowed packages, matched against PURL, name, and group. Accepts a comma-separated string or a JSON array (e.g. '["ru\\.yandex\\..*", "com\\.alibaba\\..*"]'). |
Documentation
View on GitHubSBOM Guardrails
Enforces SBOM existence, license compliance, completeness, and format standards.
Overview
This policy enforces Software Bill of Materials standards across your organization. It verifies that SBOMs are generated, contain license data, use approved formats, and keep licenses within an allow-list or out of a denylist. It works with data from both auto-generated SBOMs (via the syft collector) and CI-detected SBOMs, enabling vendor-agnostic SBOM governance.
Policies
This policy provides the following guardrails (use include to select a subset):
| Policy | Description | Failure Meaning |
|---|---|---|
sbom-exists |
Checks that an SBOM was generated | No SBOM found from any source |
has-licenses |
Verifies components have license info | License coverage below threshold |
disallowed-licenses |
Checks for disallowed license patterns | Component uses a disallowed license |
allowed-licenses |
Checks component licenses against an allow-list | Component uses a license the allow-list does not admit |
min-components |
Verifies minimum component count | SBOM has too few components |
standard-format |
Validates SBOM format | SBOM uses a non-approved format |
blocked-origins |
Checks for license origin mentions from blocked countries | Dependency has country mention from blocklist |
disallowed-packages |
Checks for disallowed packages by PURL/name/group pattern | Package matches a disallowed pattern |
How allowed-licenses evaluates licenses
- Every license a component carries is checked: CycloneDX
license.id,license.nameandexpression, and SPDXlicenseConcluded(falling back tolicenseDeclared). A component that lists several licenses needs each of them allowed. - SPDX expressions:
A OR Bpasses if either side is allowed andA AND Bneeds both.X WITH exceptionpasses if you list the full pair, or ifXis allowed and the exception is on the SPDX exceptions list, whose entries only relax a license. SoApache-2.0admitsApache-2.0 WITH LLVM-exceptionbut notApache-2.0 WITH Commons-Clause.X+passes ifXis allowed. - Entries match the whole license ID, case-insensitively, as written or as a regex:
MITadmitsMITbut notMIT-0, andBSD-.*-Clauseadmits the BSD clause family. This differs fromdisallowed_licenses, which matches anywhere in the ID. A listedX WITH exceptionpair, and a multi-word name that isn't an SPDX expression such asMIT LicenseorMIT with modifications(operators must be uppercase), match only as written, so a pattern can't stretch across them. - An SPDX
LicenseRef-…is also admitted by the name it resolves to, soCommercialcovers syft's CycloneDXCommercialand SPDXLicenseRef-Commercialalike. - Components with no license data, including SPDX
NOASSERTIONandNONE, are left tohas-licensesrather than failed twice.
Required Data
This policy reads from the following Component JSON paths:
| Path | Type | Provided By |
|---|---|---|
.sbom.auto |
object | syft collector (generate sub-collector) |
.sbom.cicd |
object | syft collector (ci sub-collector) |
.sbom.auto.cyclonedx.components |
array | syft collector |
.sbom.cicd.cyclonedx.components |
array | syft collector |
.sbom.cicd.spdx.packages |
array | syft collector (for allowed-licenses) |
.sbom.license_origins.packages |
array | license-origins collector (for blocked-origins check) |
Note: Ensure the syft collector is configured before enabling this policy. The blocked-origins check additionally requires the license-origins collector.
Installation
Add to your lunar-config.yml:
policies:
- uses: github://earthly/lunar-lib/policies/sbom@main
on: ["domain:engineering"]
enforcement: block-pr
# include: [sbom-exists, disallowed-licenses]
with:
disallowed_licenses: "GPL.*,BSL.*,AGPL.*"
# allowed_licenses: '["MIT", "Apache-2.0", "BSD-2-Clause", "BSD-3-Clause", "ISC"]'
min_license_coverage: "90"
min_components: "1"
# allowed_formats: "cyclonedx"
# disallowed_packages: '["alibabacloud", "aliyun-.*", ".*\\.ru$"]'
Tip:
disallowed_licenses,allowed_licensesanddisallowed_packagesaccept either a comma-separated string ("GPL.*,AGPL.*") or a JSON array string ('["GPL.*", "AGPL.*"]'). JSON arrays are recommended when patterns contain commas or complex regex.
Examples
Passing Example
All components have approved licenses and license coverage meets the threshold:
{
"sbom": {
"auto": {
"source": { "tool": "syft", "integration": "code", "version": "1.19.0" },
"cyclonedx": {
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"components": [
{
"name": "github.com/sirupsen/logrus",
"version": "v1.9.3",
"licenses": [{ "license": { "id": "MIT" } }]
}
]
}
}
}
}
Failing Example
A component uses a disallowed GPL license:
{
"sbom": {
"auto": {
"cyclonedx": {
"components": [
{
"name": "copyleft-lib",
"licenses": [{ "license": { "id": "GPL-3.0" } }]
}
]
}
}
}
}
Failure message: "Component 'copyleft-lib' uses disallowed license 'GPL-3.0' (matches pattern 'GPL.*')"
Failing Example (allowed-licenses)
With allowed_licenses: '["MIT", "Apache-2.0"]', jszip passes because MIT is one of its alternatives, while pako needs Zlib as well:
{
"sbom": {
"auto": {
"cyclonedx": {
"components": [
{ "name": "jszip", "version": "3.10.1", "licenses": [{ "expression": "MIT OR GPL-3.0-or-later" }] },
{ "name": "pako", "version": "1.0.11", "licenses": [{ "expression": "MIT AND Zlib" }] }
]
}
}
}
}
Failure message: "License 'MIT AND Zlib' is not in allowed_licenses: pako@1.0.11"
Remediation
When this policy fails, you can resolve it by:
sbom-existsfailure: Enable thesyftcollector or run Syft in your CI pipeline to generate an SBOMhas-licensesfailure: Ensure Syft has network access for remote license lookups, or add license metadata to your project dependenciesdisallowed-licensesfailure: Replace the disallowed dependency with an alternative that uses an approved license, or update thedisallowed_licensesinputallowed-licensesfailure: Replace the dependency, or add its license toallowed_licensesonce it has been approvedmin-componentsfailure: Verify Syft can detect your project's package manager and dependencies are declared correctlystandard-formatfailure: Configure Syft to output in an approved format (e.g.,cyclonedx-json) or update theallowed_formatsinputblocked-originsfailure: Review the flagged package's license file to confirm the country mention is genuine (not a false positive), then either replace the dependency or update theblocked_countries/allowed_countriesinputsdisallowed-packagesfailure: Replace the disallowed dependency or update thedisallowed_packagesregex patterns
Open Source
This policy is open source and available on GitHub. Contribute improvements, report issues, or fork it for your own use.
Common Use Cases
Explore how individual guardrails work with specific integrations.
Ready to Automate Your Standards?
See how Lunar can turn your AGENTS.md, engineering wiki, compliance docs, or postmortem action items into automated guardrails with our 200+ built-in guardrails.