Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

sbom.mq

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.

Features

  • Parses both major SBOM standards from their JSON serialization: SPDX 2.x and CycloneDX 1.x
  • Auto-detects the format from the document itself (spdxVersion vs. bomFormat)
  • Normalizes SPDX packages and CycloneDX components into one component shape: {id, name, version, type, purl, licenses, supplier}
  • Treats SPDX's NOASSERTION / NONE sentinels 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 as permissive, weak-copyleft, copyleft, or unknown, 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

Installation

Copy sbom.mq to your mq module directory, or place it anywhere and reference it with -L.

cp sbom.mq ~/.local/mq/config/

HTTP Import (no local installation needed)

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.json

Pin 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.json

Usage

mq -L /path/to/modules -I raw \
  'import "sbom" | sbom::sbom_parse(.) | sbom::sbom_audit_report(.)' bom.json

If you copied it to the mq built-in module directory:

mq -I raw 'import "sbom" | sbom::sbom_parse(.) | sbom::sbom_audit_report(.)' bom.json

API

sbom_parse(input)

Parses 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.

sbom_format(data)

Detects the format ("spdx" or "cyclonedx") of an already-parsed JSON document.

sbom_components(parsed)

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

sbom_metadata(parsed)

Returns document-level metadata: {name, version, namespace, created, tool, spec_version}.

sbom_vulnerabilities(parsed)

Returns declared vulnerabilities as {id, severity, refs, description}. Only CycloneDX carries a standard vulnerability list; SPDX documents always yield [].

License checks

  • 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 classified copyleft/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 from sbom_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) — renders sbom_check_license_policy's result as a Markdown section. Not part of sbom_audit_report since 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"]})
    

Other audit checks

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 against sbom_vulnerabilities, as {component, version, vulnerability, severity}

sbom_audit(parsed)

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}.

sbom_audit_report(parsed)

Renders sbom_audit's result as a Markdown supply-chain security audit report.

Example

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)

...

Compatibility

Requires mq v0.5 or later (uses the built-in json module).

License

MIT

About

A SPDX and CycloneDX SBOM (Software Bill of Materials) parser implemented as an mq module.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors