// CHANGELOG

What's new in aisbom-cli

Releases auto-published from GitHub. Subscribe via RSS to follow.

📡 RSS feed →

52 releases · last updated September 23, 2026

v1.6.0 — CVE statements for pinned dependencies

--vex now contacts a third-party service. When a --vex scan finds exact requirements.txt pins, it sends each pinned package name and version to the public OSV API at api.osv.dev. Nothing else is sent. Pass --no-osv or set AISBOM_NO_OSV=1 to turn this off. The GitHub Action runs --vex whenever its token input is set, so those runs make the lookup too.

Dependency bom-refs are now stable. requirements.txt components in CycloneDX output are given dependency-<n>-<name> instead of a new random value on every run, so scanning the same input twice produces the same references.

New

  • CVE statements for pinned Python dependencies. aisbom scan --vex looks up each exact requirements.txt pin in OSV and adds one statement per advisory to the same OpenVEX and CycloneDX VEX documents that carry the model findings. Statements are keyed on the CVE, or on the GHSA/PYSEC id when an advisory has none. A statement is affected only when OSV and AIsbom's own check of the advisory's version ranges agree; otherwise it is under_investigation. Range specifiers such as torch>=2.0 are skipped and counted in the scan summary. Results are cached for 24 hours in ~/.aisbom/osv_cache.json.

    If OSV can't be reached or returns something unexpected, the scan prints a warning and the documents carry no CVE statements. Exit codes and model findings are unaffected. A scan without --vex never contacts OSV.

Changed

  • Telemetry reports unusable scan targets. A missing path, a broken symlink or a file no scanner recognises now sends a cli_error event with one of three fixed reasons (MissingTarget, UnsupportedFileType, NotAFileOrDirectory), and cli_scan gains a target_error_count field. The path is never sent. These runs now count toward the repeated-failure hint instead of resetting it. AISBOM_NO_TELEMETRY=1 still turns telemetry off.

  • Large Action uploads fall back to the SBOM alone. If the SBOM and its VEX documents together would exceed 1 MiB, the Action uploads only the SBOM and logs that it did, rather than having the whole request rejected.

Fixed

  • The macOS Intel binary is built for Intel. Every aisbom-macos-amd64 asset published before this release, v0.7.0 through v1.5.0, is an arm64 executable and does not run on an Intel Mac. The build ran on an Apple Silicon runner. It now runs on an Intel runner, and a release fails if any binary's architecture doesn't match its asset name. The PyPI package was not affected. Intel Mac users on an earlier release can download the 1.6.0 asset or pip install aisbom-cli.

What's not changing

Model scanning, detection coverage, output formats and the meaning of each exit code are identical to 1.5.0.

v1.5.0 — Exploitability data reaches the dashboard

What's new

The dashboard can now tell you whether a finding is exploitable, not just that it exists.

aisbom scan --vex writes VEX documents stating, per finding, whether each scanned artifact is actually affected. It landed in v1.4.0 without a mention in those release notes, so this is the first time it appears in a changelog.

Those documents stayed on the runner. The Action uploaded only the SBOM, so a connected repo's inventory could show what the repo contained and never whether any of it was actually exploitable. That is the question the EU Cyber Resilience Act and FDA §524B ask about most directly.

Setting the Action's token input now also runs the scan with --vex, and the resulting OpenVEX and CycloneDX VEX documents are uploaded alongside the SBOM in the same request.

- uses: Lab700xOrg/aisbom@v1
  with:
    directory: models/
    token: ${{ secrets.AISBOM_TOKEN }}   # now also uploads VEX
  • Nothing to change in your workflow. If you already set token, the next run carries exploitability data.
  • Nothing changes if you don't use the dashboard. No token means no --vex, no VEX files written into your workspace, and no request. The scan runs exactly as it did in v1.4.0.
  • The CI log says what was sent. The existing upload log group now reports vex-documents=N alongside the trigger and run id.

The VEX documents are derived entirely from findings already described in the SBOM you were uploading anyway. They add no new information about your files. As always, the model files themselves never leave the runner. The README privacy section describes the payload in full.

Fixed: repos with CRITICAL findings were missing from the dashboard

If you use the token input with the default fail-on-risk: true, and a scan found a CRITICAL artifact, that scan never reached your dashboard.

aisbom scan exits 2 on a CRITICAL finding but still writes its SBOM. The Action honoured fail-on-risk before running the upload, so the job stopped and uploaded nothing. The inventory was quietly omitting the repos that most needed to be in it, and it has behaved this way since the dashboard upload shipped.

The upload now runs before the risk gate. Two consequences worth knowing:

  • Repos you thought were clean may not be. A repo whose scans always failed on CRITICAL will now appear in your inventory, possibly for the first time, showing findings that were always there.
  • Exit codes are unchanged for every case that could previously occur. Where a CRITICAL scan also has a failed upload, the job exits 2 rather than 3, so your required check reports the dangerous model rather than the plumbing.

If you do not set token, this does not affect you.

Also in this release

  • A corrupt VEX file no longer costs you the upload. An unreadable or non-object sibling document is skipped with a log line. The SBOM still reaches your inventory, because that is the part you actually need there.
  • An unparseable SBOM is posted as-is rather than wrapped, so the server returns its own specific rejection reason instead of a confusing one.

What's not changing

Scanner behaviour, detection coverage, output formats and the meaning of each exit code are identical to v1.4.0. Sharing stays opt-in and off by default (share: false); --vex never triggers a share upload. aisbom scan --vex on the command line behaves exactly as before. Model weights and file contents have never left your machine and still don't.

v1.4.0 — Action SBOM sharing is now opt-in

These notes were expanded on 2026-09-07, after publication. The original version described only the Action sharing change below. Four user-facing features shipped in this release without being written down; they are documented here now rather than left to be discovered.

Heads up — a behaviour change for GitHub Action users

SBOM sharing is now opt-in and off by default.

Until this release, action/entrypoint.sh ran the scan with --share --share-yes hardcoded. Every run of the Action uploaded the full CycloneDX SBOM to aisbom.io and minted a publicly-readable link that lived for 30 days.

Sharing is now controlled by a new share input, defaulting to false. With it unset, no request reaches aisbom.io and the share-url output is empty.

- uses: Lab700xOrg/aisbom@v1
  with:
    directory: models/
    share: true       # opt in to the public hosted viewer link

What this breaks: if you consume the share-url output, or want the hosted viewer link in your PR comments, you now have to ask for it with share: true. Everything else is unaffected — the SBOM artifact, the PR comment, fail-on-risk, and the token dashboard upload all render from the SBOM on the runner and behave exactly as before.

Also a behaviour change: the default SBOM spec version

--format json now emits CycloneDX 1.7 rather than 1.6.

Model components gain an ML-BOM modelCard block with a typed modelParameters section — task, architecture family, and training datasets — populated from the Hugging Face model card on hf:// scans. modelCard is emitted for 1.7 only; asking for an older schema version keeps the document shape older consumers expect.

If a downstream tool of yours is pinned to CycloneDX 1.6, this is the change to be aware of.

New commands and flags

aisbom score grades an AIBOM for completeness. It answers a different question from scan: not "is this model dangerous" but "is this document good enough to be the compliance artifact you are about to hand someone". A scan can come back perfectly clean and still produce an SBOM that names no licenses, carries no checksums and describes none of the models.

Seven weighted dimensions — component identity, integrity hashes, licenses, model-card coverage, dataset provenance, VEX presence, document provenance — with a letter grade, a per-dimension breakdown, and the specific gaps behind each. --fail-under <n> returns a non-zero exit code so it can gate CI, and --json gives programmatic output. CycloneDX input only.

aisbom scan --vex emits VEX documents. OpenVEX 0.2.0 and CycloneDX VEX written alongside the SBOM, stating per finding whether each scanned artifact is actually affected.

  • --vex-format openvex|cyclonedx|both selects the flavour.
  • --vex-baseline <old-sbom.json> compares against a previous SBOM and unlocks the fixed status.
  • Requires --format json: statements address components by serial number and bom-ref, so a VEX file emitted beside a Markdown or SPDX document would carry a dangling cross-reference.

Statements are keyed on AIsbom finding classes rather than CVEs. What AIsbom detects lives inside a model file, which will never have a CVE, because the file is the payload rather than a component with a patchable defect.

--spdx-version 3.0 emits SPDX 3.0 JSON-LD with the AI Profile. 2.3 remains the default, so existing --format spdx output is unchanged.

Also fixed

  • The PR comment link is gated on the same input. The comment previously recovered the viewer URL by scraping the scan log with a URL-shaped regex, independently of the share setting — so a URL-shaped scan target could have put a link in a comment on a run that shared nothing. It is now gated on the input directly.
  • Two documentation claims corrected. AISBOM_NO_TELEMETRY=1 was documented as disabling the share upload; it does not — it withholds the cli_share_created event only, and dropping --share is what stops the upload. The privacy sections in both READMEs now state exactly which network call each input enables, and describe the telemetry payload separately from the SBOM upload paths rather than lumping them together.
  • Standalone binaries are smoke-tested before release assets are uploaded, so a frozen build that cannot start is caught at build time rather than by whoever downloads it.

What's not changing

Scanner behaviour, detection coverage and exit codes are identical to v1.3.3 — the new spec version changes the shape of the document, not any verdict in it. The CLI's own --share flag is unchanged: it has always been explicit and still prompts unless you pass --share-yes. Model weights and file contents have never left your machine and still don't.

v1.3.3 — Correcting a published limit

Correction to the v1.3.2 notes

v1.3.2 introduced content-based discovery for files no extension claims, and documented one known limit: that a payload could only hide behind a literal larger than the whole 16MB sniff budget.

That figure was wrong. The real threshold was roughly 64KB — about 254× easier to reach than published. This release fixes the underlying gap and makes the documented ceiling the true one.

What was wrong

Discovery reads a 64KB head and re-reads with a larger budget only when that head looks like an unfinished pickle. The signal for "unfinished" was at least one opcode parsed.

A pickle that opens with a single literal bigger than that first read completes zero opcodes — so the larger read never happened, and the file was never discovered at all. Measured on the released v1.3.2:

first-literal    65,000 B: CRITICAL (RCE Detected: os.system)
first-literal    70,000 B: NOT DISCOVERED
first-literal 1,000,000 B: NOT DISCOVERED

Scope: discovery only, and only for files whose extension nothing claims. The same padded payloads under a recognized extension such as .pkl were always caught, as was a payload followed by a large literal. If you scan .pt, .pkl, .safetensors, .gguf or any other recognized format, this never applied to you.

The fix

Discovery now also re-reads when the first opcode declares an argument that runs past the buffer — the one way a genuine pickle yields no opcodes at all.

  • Length-prefixed arguments (BINBYTES, BINUNICODE, BINBYTES8, …) are read exactly from their size prefix.
  • Newline-terminated arguments cannot be measured without the terminator, so they are admitted only for the handful of opcodes that can really begin a pickle (STRING, UNICODE, INT, LONG, FLOAT), only when no newline is already in view, and only when the next byte matches what that opcode's argument must start with.

That last guard is what keeps the walk cheap. A parquet file opens PAR1, and P is the PERSID opcode — but no real pickle begins with a persistent id, so parquet still settles on its first 64KB instead of being re-read to the cap. There is a test asserting exactly that.

Verification

  • Every previously-evading pad is now caught — 70,000 B, 250,000 B, 1,000,000 B and 2,000,000 B, in both the protocol-0 quoted-string and binary length-prefixed shapes.
  • The ceiling is now real: caught at 16,773,120 bytes, not discovered at 16,781,312.
  • Cost unchanged — a 5,101-file node_modules walk still yields 0 artifacts in 0.83s.
  • poetry run pytest: 831 passed, 91.38% coverage.
  • aisbom bypass-scorecard --check: gate green, 9/11 unchanged.

What's not changing

Exit codes, output formats and the CycloneDX/SPDX schemas are identical to v1.3.2. No new dependencies. The bypass scorecard is unmoved at 9/11 — this gap was never one of the eleven published corpus cases, which is its own lesson about what a corpus does and does not cover.

v1.3.2 — Payloads can't hide behind a filename

Heads up

Your SBOM may gain components. Five serialization extensions that were previously skipped are now scanned, and files carrying no recognized extension are now opened when their bytes are a pickle. A repo containing either will produce more components than before and may newly exit 2.

Nothing that was already scanned changes verdict: the old-vs-new differential across the bypass corpus and 134 fixture verdicts is empty in both modes.

Security — a payload can no longer hide behind its filename

Discovery decided what to open from a file's suffix alone. That is the whole of CVE-2025-1889: a pickle named config.p — or weights.dat, or a file with no extension at all — was never opened, so the scan reported No AI models found and exited 0. A clean bill of health on a directory carrying a reverse shell.

Files that nothing claims by name are now identified by content.

Adding .p to the extension list was deliberately rejected. The technique is any unexpected suffix — the attacker picks the name — so that would have flipped our published scorecard case while protecting nobody.

Two details worth stating, because they are where this kind of check usually goes wrong:

  • It validates the pickle stack, not just the opcode syntax. . is the STOP opcode, so a weaker "does it reach STOP" rule treats every stylesheet opening with a class selector as a one-opcode pickle. Measured against a real node_modules of 5,123 files, that rule claimed 11: seven JavaScript files, a TypeScript declaration, a stylesheet and two man pages. Those shapes are now regression tests.
  • Validation runs on the prefix ending at the first STOP, not the whole buffer — so a payload followed by a corrupt tail (the nullifAI shape) is still caught rather than thrown out along with its own garbage.

Reads are bounded: 64KB per unclaimed file, escalating only for a stream that parses cleanly and has not yet ended, capped at the same budget the inspector itself uses. A walk over that tree takes 1.52s and claims nothing.

Known limit, stated rather than hidden: a pickle whose first opcode carries an argument larger than that entire budget is not discovered by content.

⚠️ Correction — the limit above was wrong as published

The threshold was not 16MB. Because escalation required at least one opcode to parse, a pickle opening with a single literal larger than the first 64KB read completed no opcodes and was never re-read — so a payload could hide behind roughly 64KB, about 254× easier than stated. Measured on this release: a 65,000-byte pad was caught, a 70,000-byte pad was not.

This affected discovery only, and only files whose extension nothing claims. Padded payloads under a recognized extension (.pt, .pkl, .safetensors, …) were always caught.

Fixed in v1.3.3, where the 16MB ceiling becomes the real one. Please upgrade.

Bypass scorecard: 8/11 → 9/11

cve-2025-1889-nonstandard-extension moves from missed to detected in both scan modes, and the regression floor is raised to lock it in. Run it yourself:

pip install aisbom-cli py7zr
aisbom bypass-scorecard

py7zr builds the 7z corpus case; the scanner itself never unpacks 7z, which is why that case scores a partial. Without it the command exits 1 before printing the table.

The two remaining ⚠️ partials are documented, not accidental: a 7z-repacked model is refused by container rather than by payload (unpacking it would put a native dependency in every install), and the ShadowPickle allowlist-abuse case is indistinguishable from an ordinary OrderedDict checkpoint by static analysis.

New formats scanned

  • joblib, dill, NumPy object arrays and bare pickles.pkl, .pickle, .joblib, .dill, .npy, .npz. Every one is a pickle stream underneath, carrying exactly the arbitrary-code-execution risk of a .pt. .pkl is the sharpest case: the README has always documented aisbom scan model.pkl --strict, and that command scanned nothing.
  • joblib's compression is opened, whichever codec it chose — zlib, gzip, bz2, lzma/xz and the legacy ZF container, all via the standard library. lz4 and zstd are named but not opened (MEDIUM (Unscanned Container: lz4)), on the same reasoning as 7z. No runtime dependency is added: joblib, dill and numpy are not imported by the scanner.
  • dill's code-reconstruction globals are flagged_create_function, _create_code, _import_module, _get_attr are CRITICAL. dill's type and array helpers are deliberately not flagged, so a .dill holding only data still scans clean.

Pickle detection

  • A raw array block no longer ends the scan. joblib splices raw buffers mid-pickle, which stopped disassembly ~226 bytes into a 552-byte file while the payload sat at byte 516. Unreachable bytes now get a second pass that recovers globals directly.
  • A .npy header no longer decides whether to look. descr is attacker-supplied, so a pickle behind a header claiming '<f8' would have been a one-line evasion.
  • STACK_GLOBAL operands arriving via the pickle memo resolve correctly — reading only literals resolved numpy.dtype as dtype.dtype, a strict-mode false positive on every ordinary joblib model.
  • .npz members are read even when the archive fights back — a tampered CRC or header name goes through the same raw local-header read the PyTorch path uses.
  • No scan limit ends in a clean verdict. Every bound that leaves bytes unexamined now reports MEDIUM (Pickle Scan Incomplete) rather than passing a prefix off as the whole file.

Fixed

  • hf:// scans resolve the new formats. The resolver's extension list is now derived from the scanner's own dispatch sets rather than restated, so the two cannot drift again.
  • The decompression budget follows the read budget. It came from a default argument, which binds once at import — so a remote scan that fetched 2MB could expand 16MB.

What's not changing

Exit codes, output formats, and the CycloneDX/SPDX schemas are identical to v1.3.1. No new runtime dependencies.

v1.3.1 — Scan target correctness

Heads up: two exit codes changed

Two cases that previously exited 0 now exit non-zero. Neither was scanning anything before, so nothing that genuinely worked is affected — but a pipeline that was silently green on a bad path will now fail, which is the point of the fix.

What's new

Scanning a single model file works. aisbom scan model.pt discovered nothing and exited 0, because local discovery only ever enumerated the contents of a directory. A malicious file named directly on the command line reported "No AI models found" and passed clean — while the identical file scanned via its parent directory was correctly flagged CRITICAL.

  • This is the form the README documents for both --strict and --lint, so the two examples a security-conscious user is most likely to copy were the two that silently did nothing.
  • Single files are now first-class targets for every supported format, and a file gets the same verdict whichever way it is reached.

An unusable scan target now fails instead of passing. A path that does not exist — or a broken symlink, or a named file no scanner can read — previously produced an empty SBOM and exit 0, indistinguishable from a genuinely clean repo. A typo'd path in CI turned the gate green permanently.

  • These now report what went wrong and exit 1.
  • --no-fail-on-risk does not suppress it: that flag governs risk findings, not a broken target.
  • An empty directory is still a clean scan and still exits 0.

The bypass scorecard now publishes why each uncaught case is uncaught. Cases that are not fully caught carry a limitation note, rendered into docs/bypass-scorecard.md alongside the verdict: what AIsbom actually reports, why that is the wrong reason, and what closing the gap would take. No case's expected verdict changes — every evasion technique in the corpus remains one a correct scanner should catch, so the gate keeps counting all three against us. The note explains a gap; it never excuses one.

Exit codes

Exit Meaning
0 Scan completed, no CRITICAL risk
1 Scan could not be completed — target missing/unreadable, parse failure, or remote fetch failure
2 CRITICAL risk found (suppress with --no-fail-on-risk)

Only the 1 row is new for local targets; remote fetch failures have behaved this way since v1.1.0.

What's not changing

CycloneDX and SPDX output are byte-for-byte identical to v1.3.0 — verified by diffing the emitted documents across the whole CLI surface. Detection behavior is unchanged, and the bypass scorecard holds at 8/11 with no case moving.

Dependencies

typer 0.27.1, cyclonedx-python-lib 11.11.1, packaging 26.3, and pyinstaller 6.22.0 (build-only). Each was verified individually and in combination against an unchanged CLI-output baseline.

v1.3.0 — Keras, ONNX & GGUF template scanning + bypass scorecard

AIsbom now scans three more model formats, closes several documented ways of sneaking a malicious pickle past a scanner, and ships a command that lets you check those claims yourself.

Three new formats scanned

Keras (.keras, .h5, .hdf5) — a Lambda layer stores an arbitrary Python callable in the model config as a marshalled code object, and load_model runs it. Lambda layers and embedded code objects are now flagged CRITICAL. The payload is identified from its header bytes and never unmarshalled. Both containers Keras writes are handled — the .keras zip and legacy HDF5 — without adding an HDF5 library to your install.

ONNX (.onnx) — the protobuf is walked directly; no ONNX runtime is imported and the graph is never executed. Alongside producer, opset, IR version and operator inventory, two signals are surfaced: operators from a non-standard domain, and external-data paths pointing outside the model directory, which turn load_model into an arbitrary-file read. Subgraphs carried by If, Loop and Scan are walked too.

GGUF chat templates — the embedded Jinja chat_template is extracted into the SBOM component and checked statically for sandbox-escape constructs. The template is never rendered, because rendering it is the vulnerability.

Pickle evasions closed

  • Concatenated streams are all walked. A legacy torch.save file hides its object behind several header pickles, so stopping at the first STOP meant never reaching the payload.
  • Non-standard containers are flagged. Packing a model with 7z instead of the ZIP PyTorch expects previously meant the archive was never opened; it now reports CRITICAL (Non-Standard Container: …). The container is named, not unpacked — no native dependency enters your install.
  • Broken and truncated streams are scanned, not abandoned. The pickle VM runs sequentially, so a payload at the front executes before a corrupt tail is ever reached. Damaging a file is no longer a way to hide one.
  • Files are disassembled before their type is decided. A printable protocol-0 pickle could previously pass as a text config file and be reported safe.
  • Indirect-execution gadgets are caught in both modesbdb.Bdb.run, the asyncio gadget chain, and the import-mechanism primitives (sys.modules, importlib, runpy, pkgutil, builtins.__import__). Strict mode now judges a global by its resolved module and attribute, so a submodule no longer inherits an allowlisted parent's trust.
  • hf:// scans list the new extensions, so a repo containing a backdoored .keras, .h5 or .onnx model no longer resolves to zero artifacts.

An unfinished scan is never reported as a clean one

Two new risk levels you may see in output. MEDIUM (Pickle Scan Incomplete) appears when a file is so unusual that the scan reaches its work limit. MEDIUM (Unreadable Pickle Member) appears when an archive member cannot be read at all — a loader that does not verify integrity the way AIsbom does would still run it.

Both exist for the same reason: a scanner that quietly gives up and returns a clean bill is worse than no scanner, because you would act on the clean bill.

New: aisbom bypass-scorecard

Builds inert replicas of publicly documented scanner-evasion techniques — each traceable to a published CVE or research paper — scans them in both modes, and prints what AIsbom catches and what it misses. Nothing in the corpus is ever executed.

8 of 11 evasion cases are now caught, up from 5 of 11. The other three are published with the same detail as the wins — what AIsbom actually reports on each, why that reason is the wrong one, and what closing the gap would take:

  • shadowpickle-allowlist-overwrite — AIsbom resolves the STACK_GLOBAL pair and sees collections.OrderedDict, which is legitimately allowlisted because real state_dicts are OrderedDicts. Both modes return only MEDIUM (Pickle Present), the baseline every pickle gets. This is the ceiling on static allowlist analysis: the call is indistinguishable from a legitimate call to an allowlisted global, and closing it needs evidence beyond the resolved name.
  • nullifai-7z-container — reported CRITICAL (Non-Standard Container: 7z). Right severity, earned from the container rather than the payload, which is never disassembled. Unpacking 7z would put a native dependency in every install to cover one evasion class.
  • cve-2025-1889-nonstandard-extension — the one outright miss. A pickle named config.p is never opened, so the scan reports No AI models found and emits zero artifacts. picklescan closed this in 0.0.22 and AIsbom has not.

None of the three is written off. All eleven stay marked as cases a correct scanner should catch, so the gate keeps counting them against us.

--check is a release gate and runs in CI on every push. It cannot be satisfied by regenerating the scorecard — improving detection is the only way to raise the floor.

What's not changing

Exit codes, CycloneDX and SPDX output formats, and every existing verdict are unchanged. Verified against the pre-release baseline: no previously-reported risk level moves in either scan mode.

Full details in CHANGELOG.md.

v1.2.1 — SPDX export correctness

What's new

SPDX export correctnessaisbom scan --format spdx now produces a compliance artifact worth handing to an auditor. Every SPDX document the CLI produced previously named all artifacts unknown-model, reported Type: unknown, and carried no checksums.

  • Real filenames and formats: models appear under their actual filename with their real format (pickle, safetensors, gguf).
  • SHA256 checksums: SPDX packages now carry the checksum of the scanned file — the single most load-bearing field in a security SBOM. Remotely-scanned artifacts, whose bytes are never fully read, omit the field rather than assert a placeholder.
  • Deterministic documents: package identifiers are now derived from file content instead of a process memory address, so scanning unchanged inputs twice produces identical SPDX identifiers. SPDX SBOMs can now be diffed meaningfully in CI.
  • Correct tool attribution: creators reports the running CLI version instead of a hardcoded aisbom-cli-0.1.0.

Fixes

  • --format spdx no longer crashes on a requirements.txt that repeats a pin, or that uses a wildcard version such as torch>=2.0.*.
  • --format spdx no longer crashes on a scan that finds no models and no requirements.

All three previously produced an invalid document and an unhandled traceback.

What's not changing

Scanner behavior, exit codes, and CycloneDX output — all identical to v1.2.0. CycloneDX output is byte-for-byte unchanged; this release touches the SPDX path only.

v1.2.0 — Hosted dashboard generally available

What's new

The hosted dashboard is generally available — the AIsbom Platform at app.aisbom.io is now open to everyone (previously private early access).

  • One-input integration: set the GitHub Action's optional token input and every PR scan posts its SBOM to a continuous inventory across your repos. Get a per-repo token at app.aisbom.io/connect.
  • Documented data flow: the README's new "Hosted dashboard (optional)" and "Data flow & privacy" sections spell out exactly what is sent (the CycloneDX SBOM + branch name, never weights or file contents), where it's stored (EU), and how to stay purely local (leave token unset).
  • platform-url default visible: action.yml now shows the default (https://app.aisbom.io) instead of resolving it internally — behavior unchanged.
  • Launch post: https://aisbom.io/blog/aisbom-platform-launch

What's not changing

Scanner behavior, exit codes, output formats, and the default Action behavior (no token → nothing uploaded) — all identical to v1.1.0. No scanner code changed in this release.

v1.1.0 — Private & gated Hugging Face model scanning

What's new

Private & gated Hugging Face model scanning — authenticate with an environment token.

  • Token auth: set HF_TOKEN (or HUGGING_FACE_HUB_TOKEN) and AIsbom can scan private and gated hf:// models. The token is sent only to huggingface.co, is dropped on the redirect to the LFS CDN, and is never written to logs or telemetry.
  • Clearer fetch errors: auth, network, and not-found failures now print a concise, status-aware message (no traceback) and exit non-zero, instead of silently reporting zero artifacts.
  • CI guidance: README now shows the secrets.HF_TOKEN usage pattern and the egress requirement (HTTPS to huggingface.co and its LFS CDN).

What's not changing

Scanner behavior, exit codes, and output formats (CycloneDX / SPDX / Markdown) are identical to v1.0.7. Public-model scans still work with no token. Telemetry remains opt-out via AISBOM_NO_TELEMETRY=1; the only token-related field collected is a token_present boolean — never the value.

v1.0.7 — Structured risk & legal in the SBOM

What's new

Structured risk & legal in the SBOM — each scanned model component now carries aisbom:risk and aisbom:legal CycloneDX properties alongside the existing aisbom:* format/findings properties, so downstream consumers can read the risk level and legal status structurally instead of parsing the description string.

What's not changing

Scanner behavior, exit codes, and all output formats are identical to v1.0.6. The description string is byte-for-byte unchanged — these properties are purely additive and backward-compatible. aisbom diff is unaffected (it ignores properties[]), so existing CI/CD drift checks see no change.

v1.0.6 — Scanned Branch in Dashboard Uploads

What's new

Scanned branch in dashboard uploads — When the GitHub Action posts an SBOM to a connected dashboard, it now includes the branch or tag that was actually scanned (X-Aisbom-Ref, sourced from GITHUB_REF_NAME). The dashboard can attribute each scan to the correct ref instead of falling back to a placeholder.

  • Honest fallback: the header is sent only when the ref is known. For local runs or older Action versions where it's unset, the header is omitted entirely — the receiver shows an honest "—" rather than a guessed branch.
  • Opt-in only: this affects the platform upload path that already requires an explicit token. Default CLI-only scanning is unchanged.

What's not changing

All scanner rules, exit codes, command signatures, and SBOM output schemas are byte-for-byte identical to v1.0.5. The new header is purely additive and only appears on the already opt-in dashboard upload.

v1.0.5 — Structured Per-Format Findings in SBOM Output

What's new

Structured Per-Format Findings in SBOM Output — Each machine-learning-model component in the CycloneDX SBOM now carries namespaced aisbom:* properties, so downstream tools (including the aisbom.io viewer) can render per-format scan detail without re-parsing the description string.

  • Pickle / PyTorch: the names of any dangerous globals detected (e.g. os.system, subprocess.Popen) plus an opcode count.
  • SafeTensors: tensor count, dtype set, and header keys.
  • GGUF: architecture, quantization, and metadata key names.
  • Transparency: the Telemetry & Privacy section of the README now documents exactly what the --share upload contains.

What's not changing

All scanner rules, exit codes, command signatures, and the existing SBOM description strings are byte-for-byte identical to v1.0.4. The new properties are purely additive — existing consumers (and the platform's risk regex) are unaffected.

v1.0.4 — Disable Rich Auto-Highlighting + Dependency Upgrades

What's new

  • Disable Rich Auto-Highlighting: Fixed a visual formatting bug where Rich's default Console parser automatically colorized path-like substrings, IP addresses, and numbers inside plain f-strings (e.g. coloring parts of "aisbom 1.0.4" or "(CycloneDX v1.6)" cyan). Setting highlight=False ensures CLI output displays exactly as authored, while manual Rich formatting markup tags remain supported.
  • Upgraded Dependencies: Integrates the 5 recently merged Dependabot dependency and security updates (including click, torch, packaging, typer, and pyinstaller).

What's not changing

All core scanner rules, exit codes, SBOM output schemas, and command signatures are identical to v1.0.3.

v1.0.3 — Fix Early-Exception Crashes + Security Update

What's new

  • Fix Early-Exception Crashes: Fixed a bug where a failed file open operation (due to permission errors, broken symlinks, or file deletions) during PyTorch, SafeTensors, or GGUF inspections triggered an UnboundLocalError inside their clean-up code. The scanner now gracefully logs the file system error in the scan metadata instead of crashing and throwing a false cli_error event.
  • Security Update: Updated the transitive dependency idna to version 3.16 to resolve a medium-severity vulnerability (CVE-2026-45409 / GHSA-65pc-fj4g-8rjx).
  • Aligned Versioning: Formally aligned the CLI versioning under the v1.x.x release line to match the GitHub Action's releases, preventing version order issues on GitHub and the website changelog.
  • Release Guidelines: Added a formalized release process document in docs/release-process.md.

What's not changing

All core scanner rules, exit codes, SBOM output schemas, and command signatures are identical to v0.10.0.

v1.0.2 — Fix positional-args migration + PIPESTATUS bash dep

Patch release. Two e2e-discovered bugs in v1.0.1 fixed: inputs are now passed positionally so we don't depend on POSIX-illegal env var names with hyphens, and the entrypoint uses bash so PIPESTATUS captures the scan's exit code (not tee's). The fail-on-risk gate works correctly now.

v1.0.1 — Fix hyphenated INPUT_* env vars for Docker actions

Patch release. Action correctly reads inputs.github-token (and the other hyphenated inputs) from the Docker container env. v1.0.0's comment-posting step silently no-op'd because the token resolved to empty. No API or behavior changes beyond fixing the comment post.

v1.0.0 — AIsbom Action: first Marketplace release

First Marketplace release of the AIsbom Security Scanner Action.

What it does

Scans ML model artifacts (.pt, .safetensors, .gguf) in your PRs for pickle-bomb malware, license risk, and silent drift. Posts a single idempotent comment to the PR summarizing findings, with a link to the hosted viewer at aisbom.io. Re-runs update the same comment in place via a hidden marker — you'll never see stacked AIsbom comments on the same PR.

Uses aisbom-cli 0.10.0 under the hood, installed inside the Action's Docker image.

Quick start

See README_ACTION.md for the full workflow snippet, inputs/outputs reference, permissions block, and troubleshooting.

Minimum permissions in the consuming workflow:

permissions:
  contents: read
  pull-requests: write

Pin via Lab700xOrg/aisbom@v1 (floating) or Lab700xOrg/aisbom@v1.0.0 (immutable).

Telemetry

Two new events (github_action_run and github_action_comment_posted) fire from the Action container. Honors AISBOM_NO_TELEMETRY=1 like the CLI.

v0.10.0

What's new

Two changes you'll see on every successful scan.

  • Acquisition footer. Every aisbom scan now ends with a "Next steps" panel pointing at the right place to view your SBOM (your hosted share URL if you used --share, otherwise the drag-and-drop offline viewer) and at the rolling AISBOM advisories page for the AI/ML supply chain. Recurring re-engagement vector; no scan output formats changed.
  • Friendlier help surface. Several small --help improvements based on a coverage audit of recently shipped features:
    • New top-level --version / -V flag — print the installed version without spinning up aisbom info.
    • Top-level help now documents the AISBOM_NO_TELEMETRY=1 env-var opt-out directly (no more "read the README" friction).
    • --share help text now explicitly calls out that uploaded SBOMs are publicly viewable and expire after 30 days. --share-yes is flagged as CI/CD-only with a warning about interactive use.
    • aisbom info adds a Telemetry: line showing current state (enabled vs opted-out) — one canonical place to confirm whether events are firing.
    • scan target arg spells out all three forms with concrete examples (local dir, HTTP(S) URL, hf:// slug).

Privacy: attribution tagging

The new footer URLs include ?ref=cli so we can measure CLI → web conversion in GA4 Acquisition. This tag is automatically stripped when AISBOM_NO_TELEMETRY=1 is set — opt-out users still see the URLs (still useful), just without attribution.

What's not changing

Scanner behavior, exit codes, output formats, the --share flow, and what data is collected — all identical to 0.9.x. The footer replaces the previous "Visualize this report" panel; everything else in scan output is unchanged.

Opting out

# Permanent
export AISBOM_NO_TELEMETRY=1

# One invocation
AISBOM_NO_TELEMETRY=1 aisbom scan ./my-project

v0.9.2

What's new

Friendlier first-touch experience. Two small changes that make the first 30 seconds with AIsbom click.

  • Concrete-example default command. Running aisbom with no arguments now prints a one-screen quickstart with a working example (aisbom scan hf://google-bert/bert-base-uncased) instead of Typer's auto-generated help dump. aisbom --help still shows the full command reference for power users.

  • Zero-install path documented. New "Zero-Install (pipx run)" section in the README and a third install option on aisbom.io. Try AIsbom without committing to a system install:

    pipx run --spec aisbom-cli aisbom scan hf://google-bert/bert-base-uncased
    

Housekeeping

  • CI: bumped softprops/action-gh-release from v2 to v3 in the binary-release workflow.

What's not changing

Scanner behavior, exit codes, output formats, the --share flow, telemetry events, and what data is collected — all identical to 0.9.1. The default-command change is presentation-only; scripts that parse aisbom no-args output should already be relying on --help instead.

v0.9.1

What's new

Telemetry default-flip — Anonymous CLI usage telemetry is now on by default.

  • The AISBOM_TELEMETRY_V2=1 opt-in introduced in 0.8.x has been retired after a successful soak. The single lever from now on is the opt-out: AISBOM_NO_TELEMETRY=1.
  • No new events, no new fields, no change to what's collected — the schema shipped in 0.8.x continues unchanged. See the Telemetry & Privacy section in the README for the full list.
  • If you previously set AISBOM_TELEMETRY_V2=1 in your shell rc or CI environment, you can remove it; it's now a no-op.

What's not changing

Scanner behavior, exit codes, output formats, the --share flow shipped in 0.9.0, and what data is collected — all identical to 0.9.0.

Opting out

# Permanent
export AISBOM_NO_TELEMETRY=1

# One invocation
AISBOM_NO_TELEMETRY=1 aisbom scan ./my-project

v0.9.0 — Shareable SBOM URLs

What's new

Shareable SBOM URLs — Instantly generate a secure, hosted viewer link for your SBOM by appending the --share flag to your scan.

  • Privacy-first: Includes a strict confirmation prompt before uploading, unless explicitly bypassed with --share-yes for CI/CD pipelines.
  • Telemetry: Added cli_share_created event tracking the has_share_yes parameter to measure bypass usage.
  • Safe Empty Scans: Automatically aborts upload if the target path is empty and no artifacts or dependencies are discovered.
  • Documentation: Added a new "Share Your SBOM" section to the README and updated the Telemetry & Privacy section to reflect the opt-in upload behavior (data retained for 30 days).

What's not changing

Scanner behavior, exit codes, output formats — all identical to v0.8.x.

Telemetry behavior remains the same as v0.8.0.

v0.8.0 — CLI telemetry preview

What's new

  • CLI telemetry preview — opt-in via AISBOM_TELEMETRY_V2=1. Off by default in this release while we soak the pipeline.
  • New events: cli_install_first_seen, cli_scan (with target_type, model_format, risk_level_max, scan_duration_ms, file_count, parse_error_count, strict_mode), cli_scan_critical_found, cli_strict_mode, cli_diff, cli_error (exception class name only).
  • Anonymous user_id (SHA-256 of MAC + salt, 16 hex chars) stored in ~/.aisbom/config.json for returning-user analytics.
  • New "Telemetry & Privacy" section in README documenting the full schema, where data goes, and how to opt out.

Privacy

Set AISBOM_NO_TELEMETRY=1 to disable telemetry entirely. This setting wins over every other gate. The opt-out is forward-compatible — it'll work the same way after the next release flips the default.

What's not changing

  • Scanner behavior, exit codes, output formats — all identical to v0.7.x.
  • aisbom-cli 0.7.x clients keep working unchanged against the deployed Worker.

Next release

Will flip telemetry to default-on, with AISBOM_NO_TELEMETRY=1 documented as the opt-out.

v0.7.8 - Stability Release (macOS CI & Publish Fixes)

Summary This release stabilizes the CI/CD pipeline, ensuring reliable multi-architecture builds and automated publishing.

Changelog

  • Fix(CI): Implemented "Rosetta Strategy" for macOS Intel builds (macos-14 host + x64 python architecture).
  • Fix(CI): Resolved Resource not accessible by integration error in publish.yml by replacing API calls with authenticated git commands for tagging.
  • Fix(CI): Corrected YAML syntax error (duplicate key) in binaries.yml.

Artifacts

  • aisbom-linux-amd64
  • aisbom-macos-amd64 (Intel compatible)
  • aisbom-macos-arm64 (Apple Silicon native)

v0.7.7 - macOS CI Architecture Fix

Summary This release revamps the macOS build pipeline to ensure reliable delivery of Intel binaries by leveraging Apple Silicon runners with Rosetta 2.

Changelog

  • Fix(CI): Switched the build strategy to use macos-14 (Apple Silicon) for ALL macOS builds.
    • Intel (amd64): Built using python-architecture: x64, running via Rosetta 2 on the M1/M2 host.
    • Silicon (arm64): Built natively.
    • Why? This bypasses the instability/unavailability of legacy Intel runners on GitHub Actions.

Artifacts

  • aisbom-linux-amd64
  • aisbom-macos-amd64 (Intel compatible)
  • aisbom-macos-arm64 (Apple Silicon native)

v0.7.6: Addresses build pipeline failure for macOS

This patch release addresses a build pipeline failure for macOS Intel binaries.

Changelog

Fix(CI): Downgraded the macOS Intel runner from macos-13 to macos-12 to resolve "configuration not supported" errors in GitHub Actions.

Artifacts This release should correctly generate:

aisbom-linux-amd64 aisbom-macos-amd64 (Intel) aisbom-macos-arm64 (Apple Silicon)

v0.7.5 - Multi-Architecture Standalone Binaries

This release introduces full support for Air-Gapped / "USB Test" scenarios across all major platforms, answering the demand for secure, offline usage.

Key Features:

  • Multi-Arch Standalone Binaries: We now ship three distinct executables attached to this release:
    • aisbom-linux-amd64 (Linux x86_64)
    • aisbom-macos-amd64 (macOS Intel)
    • aisbom-macos-arm64 (macOS Silicon M1/M2/M3)
  • Documentation Upgrade: Added comprehensive instructions for "Standalone Binary" usage, including a critical Troubleshooting guide for macOS Gatekeeper permissions (xattr -d).

How to Install:

Pip: pip install aisbom-cli==0.7.5 Binary: Download the appropriate file below, chmod +x it, and run!

v0.7.0: Standalone Binaries ("The USB Test")

Features

  • Standalone Binaries: You can now run aisbom without Python installed! Linux (x86_64) and macOS (ARM64) builds are automatically attached to this release. Perfect for air-gapped environments or quick audits ("The USB Test"). Just download, chmod +x, and run ./aisbom scan ..

Improvements

  • Build System: Added PyInstaller integration and GitHub Actions workflow for automated binary releases.
  • Documentation: Updated README with Quick Start guide for binaries.

Fixes

  • Compatibility: Resolved Python < 3.15 requirement for PyInstaller.
  • Data Files: Fixed missing CycloneDX and SPDX schemas in frozen builds.

v0.6.0: Sandbox Wrapper Scripts

This follow-up release improves the usability of the "Defense in Depth" runtime sandbox by including official wrapper scripts.

New Features

  • Sandbox Wrapper (scripts/asb-wrapper.sh): A convenience shell script to run commands inside the amazing-sandbox via uvx without typing the full command string.
  • Safe Loader Template (scripts/safe_loader.py): An example Python script demonstrating how to safely load legacy models that require weights_only=False within the isolated environment.

Documentation

Updated Sandboxed Execution Guide to reference the new scripts.

v0.5.0: Defense in Depth & Linter Hardening

This release solidifies the security architecture with a "Defense in Depth" strategy, aligning AIsbom with the upcoming PyTorch 2.6+ security defaults (weights_only=True).

Security & Linter Improvements

  • Arbitrary Execution Detection: The Migration Linter (--lint) now explicitly flags REDUCE, BUILD, and INST opcodes as WARNINGs. This highlights models that require dynamic object construction (a key vector for RCE) while acknowledging that some legacy models rely on this behavior. Logic is now fully aligned with PyTorch's safe_globals validation strategy.

Documentation & Strategy

  • Defense in Depth Guide: Added Sandboxed Execution Docs, detailing how to combine Static Analysis (AIsbom) with Runtime Isolation (uvx + amazing-sandbox).
  • README Expansion: Updated the README to articulate the two-layer security approach (Layer 1: Static, Layer 2: Runtime).

Maintenance

  • Repo Cleanup: Removed temporary staging directories (repro_stage) and moved internal maintenance documentation to .github/MAINTAINERS.md
  • Tests: Extended test suite coverage for opcode warnings.

v0.4.2: Telemetry & Version Checks

🚀 Features

  • CLI Version Check: The CLI now checks for updates in the background and notifies you if a newer version is available.
  • Ethical Telemetry: Collects minimal, anonymous usage data (OS, Python version, CI status) to help improve the tool.
    • Privacy: Set AISBOM_NO_TELEMETRY=1 to completely disable this behavior.
  • SPDX Support: The offline viewer now supports SPDX 2.2/2.3 JSON files.

🐛 Fixes

  • Fixed CI build failure by synchronizing poetry.lock.
  • Improved documentation for the scan commands.

📦 Installation

pip install aisbom-cli==0.4.2

v0.4.1 - Diff, SPDX 2.3, and Telemetry

This release introduces powerful new compliance and drift detection features, along with standardized output formats.

🚀 New Features

  • Diff Command (aisbom diff):
    • Compare two SBOMs to detect silent regressions (risk increases, license changes, hash drift).
    • CI/CD Integration: Automatically exits with code 1 if critical regressions are found, functioning as a quality gate.
  • SPDX 2.3 Support:
    • New --format spdx option generates industry-standard SPDX 2.3 JSON artifacts.
    • Fully compliant with ISO/IEC 5962:2021.
  • Enhanced Safety Heuristics:
    • Improved detection of malicious patterns in pickle files (e.g., specific posix.system and subprocess calls).
  • CLI Telemetry:
    • The "Visualize this report" link now includes version and source tracking to help us improve the platform.

🛠 Improvements

  • Output Standardization: Unified the "Visualize this report" panel behavior, ensuring it appears consistently for both CycloneDX (default) and SPDX formats.
  • Cleaner Terminal Output: Links are now masked behind clickable text (👉 Open Secure Viewer) to reduce visual clutter.
  • Corrected README.md

v0.4.0 - Diff, SPDX 2.3, and Telemetry

This release introduces powerful new compliance and drift detection features, along with standardized output formats.

🚀 New Features

  • Diff Command (aisbom diff):
    • Compare two SBOMs to detect silent regressions (risk increases, license changes, hash drift).
    • CI/CD Integration: Automatically exits with code 1 if critical regressions are found, functioning as a quality gate.
  • SPDX 2.3 Support:
    • New --format spdx option generates industry-standard SPDX 2.3 JSON artifacts.
    • Fully compliant with ISO/IEC 5962:2021.
  • Enhanced Safety Heuristics:
    • Improved detection of malicious patterns in pickle files (e.g., specific posix.system and subprocess calls).
  • CLI Telemetry:
    • The "Visualize this report" link now includes version and source tracking to help us improve the platform.

🛠 Improvements

  • Output Standardization: Unified the "Visualize this report" panel behavior, ensuring it appears consistently for both CycloneDX (default) and SPDX formats.
  • Cleaner Terminal Output: Links are now masked behind clickable text (👉 Open Secure Viewer) to reduce visual clutter.

v0.3.0 - Remote Scanning

Remote Scanning (New!)

You can now scan models hosted on Hugging Face without downloading them.

aisbom scan hf://google-bert/bert-base-uncased
  • Uses HTTP Range requests to stream only headers and metadata.
  • Saves bandwidth and disk space.

v0.2.7 - bug fix

Robust handling of GLOBAL opcodes that uses space-separated module

v0.2.6 - The DevOps Update

🛡️ Strict Mode (Allowlisting)

New --strict flag. Instead of looking for known malware, block any import that isn't on a safe list (torch, numpy, etc.). Essential for high-security environments.

📝 CI/CD Markdown Reporting

New --format markdown option. Generate beautiful risk tables directly in your GitHub Actions pipeline to post as PR comments.

🧠 GGUF Support

Now scans .gguf headers for License risks (e.g. CC-BY-NC).

🚦 CI Exit Codes

Scanner now returns exit(1) when CRITICAL risks are found, allowing you to block builds automatically.

v0.2.5 - Strict allowlist option now supported

🚀 Features:

  • Pickle scanning now has a strict allowlist option (--strict/--no-strict); unknown imports are flagged as UNSAFE_IMPORT, and high-risk scans exit non-zero (controlled by --fail-on-risk).
  • GGUF model support added; mock GGUF artifact generated; README updated to mention GGUF and strict mode.
  • Test suite expanded (strict mode, GGUF parsing edge cases, generator utilities, legacy .pt, CLI behaviors); coverage up to ~85%.

v0.1.9 - feat: package mock malware generation

🚀 Updates

  • Mock malware and legal risk generation: CLI now provides a packaged way to generate test artifacts.
  • No need to git clone entire repo just for testing.

v0.1.7 - Web Viewer Integration

🚀 Updates

  • Dashboard Link: CLI now provides a direct link to the new Offline SBOM Viewer.
  • UX Improvements: Better console output formatting.

v0.1.5 - License Radar & Safety Check

🚀 New Features

  • Legal Risk Scanning: Now detects "Non-Commercial" (CC-BY-NC) and restrictive licenses in Safetensors metadata.
  • Verification Scripts: Added generate_restricted_model.py to allow users to verify legal risk detection locally.
  • Documentation: Complete rewrite of README.

v0.1.1 - Artifact Hashing

Full Changelog: Added SHA256 streaming hash calculation for large model artifacts.