A SPDX and CycloneDX SBOM (Software Bill of Materials) parser implemented as an mq module.
This is the auditing side: it reads SBOMs produced by other tools/projects — component inventories, license/supplier metadata, and (for CycloneDX) known vulnerabilities — normalizes them into a common shape, and builds a supply-chain security audit report from the result.
- Parses both major SBOM standards from their JSON serialization: SPDX 2.x and CycloneDX 1.x
- Auto-detects the format from the document itself (
spdxVersionvs.bomFormat) - Normalizes SPDX packages and CycloneDX components into one component shape:
{id, name, version, type, purl, licenses, supplier} - Treats SPDX's
NOASSERTION/NONEsentinels as missing data, consistently with CycloneDX's absent fields - License classification: splits SPDX-style license expressions (
"(MIT OR Apache-2.0)","GPL-2.0-only WITH Classpath-exception-2.0") and classifies each component aspermissive,weak-copyleft,copyleft, orunknown, worst-case-first - License policy checks: flag components against a caller-supplied allow-list or deny-list of license ids
- Audit checks: missing license, missing version, missing package URL (purl), missing supplier, copyleft/weak-copyleft licensing, unrecognized licenses, conflicting versions of the same component, and components affected by a declared vulnerability
- Renders a ready-to-share Markdown audit report
Copy sbom.mq to your mq module directory, or place it anywhere and reference it with -L.
cp sbom.mq ~/.local/mq/config/If mq was built with the http-import feature, you can import directly from GitHub without any local setup. This requires the --allow-http-import flag, which is disabled by default:
mq --allow-http-import -I raw 'import "github.com/harehare/sbom.mq" | sbom::sbom_parse(.) | sbom::sbom_audit_report(.)' bom.jsonPin to a specific release with @vX.Y.Z:
mq --allow-http-import -I raw 'import "github.com/harehare/sbom.mq@v1.0.0" | sbom::sbom_parse(.)' bom.jsonmq -L /path/to/modules -I raw \
'import "sbom" | sbom::sbom_parse(.) | sbom::sbom_audit_report(.)' bom.jsonIf you copied it to the mq built-in module directory:
mq -I raw 'import "sbom" | sbom::sbom_parse(.) | sbom::sbom_audit_report(.)' bom.jsonParses an SBOM JSON string (SPDX or CycloneDX) and returns {"format": "spdx" | "cyclonedx", "data": <parsed json>}. Raises an error if the input isn't valid JSON, or isn't a recognizable SPDX/CycloneDX document.
Detects the format ("spdx" or "cyclonedx") of an already-parsed JSON document.
Returns the normalized component list. Each component is:
| Field | Description |
|---|---|
id |
SPDXID (SPDX) or bom-ref/purl (CycloneDX) |
name |
Component name |
version |
Version string, or None if not declared |
type |
Component type (always "package" for SPDX; CycloneDX's type, e.g. "library") |
purl |
Package URL, or None |
licenses |
Array of license identifiers/expressions (empty if none declared) |
supplier |
Supplier name, or None |
Returns document-level metadata: {name, version, namespace, created, tool, spec_version}.
Returns declared vulnerabilities as {id, severity, refs, description}. Only CycloneDX carries a standard vulnerability list; SPDX documents always yield [].
sbom_license_category(component)— classifies one normalized component as"permissive","weak-copyleft","copyleft", or"unknown"(no recognized license id, including no license at all). License expressions are split into individual ids and the most restrictive one wins.sbom_license_summary(parsed)— counts components per category:{"permissive": n, "weak-copyleft": n, "copyleft": n, "unknown": n}.sbom_find_copyleft_components(parsed)— components classifiedcopyleft/weak-copyleft, each enriched with a"category"field.sbom_find_unknown_licenses(parsed)— components that do declare a license, but it isn't a recognized id (typos, custom/non-SPDX license text). Distinct fromsbom_find_missing_license, which is for no license at all.sbom_check_license_policy(parsed, policy)— checks components against a caller-supplied policy:{"allow": [ids]}flags any component whose licenses aren't all in the allow-list.{"deny": [ids]}flags any component using one of the denied ids.- Matching is case-insensitive; components with no declared license are never flagged here (see
sbom_find_missing_license).
sbom_license_policy_report(parsed, policy)— renderssbom_check_license_policy's result as a Markdown section. Not part ofsbom_audit_reportsince the policy is caller-supplied — append it yourself:sbom::sbom_audit_report(parsed) + "\n\n" + sbom::sbom_license_policy_report(parsed, {"deny": ["GPL-3.0-only", "AGPL-3.0-only"]})
Each takes the result of sbom_parse and returns an array of the offending components (or, for the two below noted, differently shaped records):
sbom_find_missing_license(parsed)sbom_find_missing_version(parsed)sbom_find_missing_purl(parsed)sbom_find_missing_supplier(parsed)sbom_find_duplicate_components(parsed)— same name at conflicting versions, as{name, versions}sbom_find_vulnerable_components(parsed)— components matched againstsbom_vulnerabilities, as{component, version, vulnerability, severity}
Runs every check above (license-policy checks excepted, since those need an explicit policy) and returns one report dict: {format, component_count, license_summary, missing_license, missing_version, missing_purl, missing_supplier, copyleft_components, unknown_licenses, duplicate_components, vulnerable_components}.
Renders sbom_audit's result as a Markdown supply-chain security audit report.
Given bom.json (CycloneDX):
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"metadata": { "component": { "name": "my-project", "version": "1.0.0" } },
"components": [
{ "type": "library", "bom-ref": "pkg:npm/foo@1.2.3", "name": "foo", "version": "1.2.3",
"purl": "pkg:npm/foo@1.2.3", "licenses": [{ "license": { "id": "MIT" } }] },
{ "type": "library", "bom-ref": "pkg:npm/bar@0.9.0", "name": "bar", "version": "0.9.0",
"purl": "pkg:npm/bar@0.9.0" }
],
"vulnerabilities": [
{ "id": "CVE-2024-0001", "ratings": [{ "severity": "high" }],
"affects": [{ "ref": "pkg:npm/bar@0.9.0" }] }
]
}mq -L . -I raw 'import "sbom" | sbom::sbom_parse(.) | sbom::sbom_audit_report(.)' bom.json# Supply Chain Security Audit Report
- **Format:** CYCLONEDX
- **Subject:** my-project 1.0.0
- **Components:** 2
### Known Vulnerabilities
- **CVE-2024-0001** (high) affects bar 0.9.0
### License Summary
| Category | Components |
| --- | --- |
| Permissive | 1 |
| Weak Copyleft | 0 |
| Copyleft | 0 |
| Unknown | 1 |
### Copyleft / Weak-Copyleft Licenses
No copyleft or weak-copyleft licensed components were found.
...
### Missing License
- bar (0.9.0)
...Requires mq v0.5 or later (uses the built-in json module).
MIT