License Policy Gate

build_sbom records each component’s license name, but nothing ever judged it — a copyleft or otherwise-disallowed license could ship unnoticed. This adds the policy gate: normalize the SBOM’s license strings to SPDX ids, evaluate them against an allowlist / denylist (with a built-in strong-copyleft set), and emit violations that bridge into the existing SARIF exporter — the license-compliance lane beside the OSV vulnerability lane.

Pure standard library (re); fully offline; imports no PySide6.

Headless API

from je_auto_control import (
    build_sbom, evaluate_sbom, evaluate_license,
    license_findings_to_sarif, write_sarif, DEFAULT_COPYLEFT)

sbom = build_sbom("je_auto_control")

# Allowlist mode: anything outside the list is a violation.
violations = evaluate_sbom(sbom["components"],
                           allow=["MIT", "Apache-2.0", "BSD-3-Clause"])

# Or denylist mode using the built-in strong-copyleft set.
violations = evaluate_sbom(sbom["components"], deny=DEFAULT_COPYLEFT)

write_sarif(license_findings_to_sarif(violations), "licenses.sarif",
            tool_name="AutoControl-License")

normalize_spdx maps loose names ("MIT License"MIT, "Apache 2.0"Apache-2.0) to SPDX ids. evaluate_license returns allowed / denied / unknown: deny takes precedence; an empty allow means “not constrained”; a missing license is unknown. SPDX expressions are understood — "MIT OR GPL-3.0-only" is a choice (allowed if any operand is allowed), while "MIT AND GPL-3.0-only" requires every operand. Each violation is {name, version, license, status}; denied maps to a SARIF error and unknown to a warning.

Executor command

AC_check_licenses takes components (a component list, a full SBOM dict, or a JSON string) and optional allow / deny lists; it returns {violations, count}. The same operation is exposed as the MCP tool ac_check_licenses and as a Script Builder command under Security.