Skip to main content

agent-harness-aibom

CI PyPI License: MIT

An AI Bill of Materials (AIBOM) generator, security analyzer, and CI gate for AI agent harnesses.

Point it at a running Hermes or OpenClaw installation and it answers the question a general-purpose SBOM scanner can't: what exactly does this agent have access to? Its runtime binary, the model(s) it talks to, the skills and MCP servers and hooks it can reach, the secrets surface around all of that - recorded as a standard, tool-agnostic CycloneDX 1.6 document, then analyzed for risk, attack surface, and blast radius without ever leaving that standard.

pip install agent-harness-aibom
harness-aibom scan --runtime auto -o aibom.json
harness-aibom report aibom.json -o report.html

AIBOM Explorer report - architecture diagram and scan warnings

Why CycloneDX, not a custom format

Every fact this tool collects is real, on-disk state - nothing inferred, nothing scored by an opaque model. It stays inside the official CycloneDX 1.6 schema everywhere a native field exists (purl, hashes, licenses, supplier, externalReferences, the dependency graph), and adds harness-specific facts as a harness-aibom: property namespace alongside them - so a generic CycloneDX/SBOM tool that ignores unknown properties still gets a valid, useful BOM, and this project's own validate/diff/report/policy commands get everything they need on top of it. See SPEC.md for the full data model and the reasoning behind every field.

What it collects

Class What it is
runtime The harness binary itself - version, install method, upstream hash
model Every model behind the configured endpoint, via Ollama's own API - digest, family, parameter size, quantization, context length
model_endpoint / mcp_server Native CycloneDX services - transport, TLS, auth posture, endpoint URLs
tool One per MCP tool a server declares, with a name-based read/write/exec/network risk class
skill Every SKILL.md at any depth, directory-hashed, with deterministic content analysis (referenced servers, URLs, shell commands, env vars) and real YAML frontmatter (name/description/license/allowed-tools)
hook Shell hooks, their approval state, and whether their content has changed since approval
configuration The harness's own config file, fingerprinted
dependency The harness's Python packages and MCP launcher packages, with native purl, licenses, and supplier
secrets_surface .env files, credential-shaped filenames, key/PEM files - location and permissions only, contents never read
prompt_surface AGENTS.md/CLAUDE.md instruction files, fingerprinted
memory_store Chroma/FAISS persistence files - location and permissions only, contents never read

Twelve classes, all cross-referenced into one real dependency graph (harness → mcp_server → tool, harness → model_endpoint → model, …) - not a flat list of everything the harness happens to touch.

Security analysis, built on top of the same document

Nothing here is a second, independent scan - every result below is a pure function over the CycloneDX document scan already produced re-derivable by anyone from the same JSON:

  • Risk observations - a fixed, named set of explainable rules (world-readable secrets at two confidence tiers, plaintext/ unauthenticated MCP transport, unpinned launcher packages, models with no content digest, world-readable memory stores), never a single opaque score.
  • Attack surface & trust zones - every component classified by where it actually sits (filesystem, local process, loopback, network, a credential store, a model provider), then split into what stays on the box versus what crosses a real network boundary.
  • Capability matrix - componentClass × capability × reachability, aggregated - never a fabricated per-asset checklist of capabilities this scanner didn't actually observe.
  • Supply chain & blast radius - for any component, an exact BFS over the real dependency graph in both directions: what it depends on, and what would be affected if it were compromised.
  • Baseline diff & digest drift - diff two scans and see exactly what was added, removed, or changed, with a distinct callout when a model's own tag stays the same while its content digest doesn't.
  • Vulnerability data - real OSV.dev records via scan-vulns (opt-in, the one command that talks to the network), rendered into CycloneDX's native vulnerabilities[] and the report's own Vulnerabilities section.
  • Self-assessed declarations & compliance evidence mapping - a handful of narrow, real, computed coverage claims (CycloneDX declarations) and a mapping of this scanner's own data to real NIST AI RMF / OWASP LLM Top 10 / MITRE ATLAS control IDs - always "evidence collected" or "partial evidence", never a compliance/certification claim.

The AIBOM Explorer

harness-aibom report renders the whole document as a single, offline, static HTML file - no server, no CDN, no external dependency. Live search and class filtering, a click-through Component Inspector for every entry (with evidence chains for confidence-tagged facts and a "View in graph" jump into a collapsed-by-default, per-instance Dependency graph explorer), a baseline-diff view, an Artifact Integrity panel when the document is signed, and a Raw BOM view whose own "bom-ref" lines click straight back into the Component Inspector. See examples/hermes-aibom.example.html for a full, real rendering.

Install

pip install agent-harness-aibom

or, with uv:

uv venv && source .venv/bin/activate && uv pip install agent-harness-aibom

For development, from a checkout:

uv sync --extra dev

Usage

# Auto-detect and scan whatever's installed under $HOME
harness-aibom scan --runtime auto -o aibom.json

# Scan a specific runtime explicitly
harness-aibom scan --runtime hermes -o hermes-aibom.json
harness-aibom scan --runtime openclaw -o openclaw-aibom.json

# Check a document's shape and internal integrity (duplicate bom-refs,
# dangling dependency edges, malformed hashes, orphaned components)
harness-aibom validate aibom.json

# Compare two scans -- e.g. before/after a suspected skill compromise
harness-aibom diff before.json after.json

# ...or diff only the named security findings that changed
harness-aibom diff before.json after.json --security --exit-code

# Render a document as a single, offline, static HTML file
harness-aibom report aibom.json -o report.html

# ...with a baseline, to render what changed inline
harness-aibom report after.json --baseline before.json -o report.html

# Gate CI on the built-in risk rules (or your own policy-as-code rules)
harness-aibom policy aibom.json --fail-on high
harness-aibom policy aibom.json --policy-file policy.yaml
harness-aibom policy aibom.json --format sarif -o results.sarif

# Sign and verify an AIBOM with cosign, key-based and fully offline
harness-aibom scan --deterministic -o aibom.json
harness-aibom sign aibom.json --key cosign.key
harness-aibom verify-signature aibom.json --key cosign.pub

# Enrich with real OSV.dev vulnerability data -- opt-in, requires network,
# never part of `scan` itself
harness-aibom scan-vulns aibom.json -o aibom-with-vulns.json

# A narrow, real evidence mapping toward a published framework's own
# control IDs -- NEVER a compliance or certification claim
harness-aibom compliance aibom.json --framework nist-ai-rmf
harness-aibom compliance aibom.json --framework owasp-llm-top10-2025
harness-aibom compliance aibom.json --framework mitre-atlas

Run harness-aibom <command> --help for the full set of flags on any subcommand.

scan runs entirely against the local filesystem and local subprocesses/ HTTP calls (hermes/openclaw CLIs, Ollama's /api/tags and, opportunistically, /api/show for prompt-template/tokenizer metadata). To scan a remote box, install the package there (or SSH in and run it) - there's no built-in remote transport. scan-vulns is the one command that talks to a third-party network service (the public OSV.dev API) - deliberately separate, so scan itself stays fully offline.

Missing pieces are never fatal: if hermes isn't on PATH, or Ollama isn't reachable, the scan still completes and prints a warning[...] line to stderr explaining what it skipped, so the resulting document is never mistaken for a complete one.

Example output

examples/hermes-aibom.example.json and examples/openclaw-aibom.example.json were generated by running scan against the fixtures in tests/fixtures/ and are schema-validated against the real CycloneDX 1.6 JSON Schema on every test run. Their report renderings are examples/hermes-aibom.example.html and examples/openclaw-aibom.example.html.

Project layout

src/harness_aibom/
├── model.py           # Component / HarnessDocument -- the in-memory data model
├── cyclonedx.py        # model.py -> CycloneDX 1.6 JSON
├── security.py          # architecture graph, risk rules, attack surface, blast radius
├── report.py             # the AIBOM Explorer HTML report
├── validate.py            # structural + referential-integrity checks
├── diff.py                 # before/after comparison
├── policy_yaml.py           # policy-as-code rule evaluation
├── sarif.py                  # SARIF 2.1.0 output for code-scanning UIs
├── sign.py                    # cosign sign/verify wrapper
├── vex.py                      # OSV.dev vulnerability enrichment (scan-vulns)
├── compliance.py                # NIST AI RMF / OWASP LLM Top 10 / MITRE ATLAS evidence mapping
├── paths.py                    # relPath / symlink-escape helpers
├── fingerprint.py                # sha256 helpers
├── cli.py                         # `harness-aibom` entrypoint
└── collectors/
    ├── base.py                      # Collector ABC
    ├── ollama.py                     # shared: model discovery via Ollama's HTTP API
    ├── mcp.py                         # shared: MCP server extraction from a config dict
    ├── deps.py                         # shared: Python dependency inventory
    ├── secrets.py                       # shared: secrets-surface discovery (paths/perms only)
    ├── skills.py                         # shared: SKILL.md discovery, frontmatter, content analysis
    ├── prompt_surface.py                  # shared: AGENTS.md/CLAUDE.md discovery
    ├── memory_store.py                     # shared: Chroma/FAISS discovery
    ├── hermes.py                             # Hermes collector
    └── openclaw.py                           # OpenClaw collector

CI and publishing

  • .github/workflows/ci.yml runs the test suite (including real, non-mocked cosign sign/verify tests) and a CLI smoke test on every push and pull request, on Python 3.10, 3.11, and 3.12.
  • .github/workflows/publish.yml builds and publishes the package to PyPI when a GitHub Release is published, via PyPI Trusted Publishing - no password stored in this repo.

To ship a new version: bump version in pyproject.toml and src/harness_aibom/__init__.py, commit, push, then publish a GitHub Release with a matching tag (e.g. v0.9.0). The release triggers publish.yml, which builds and uploads it automatically.

Testing

uv run pytest -q

Collector tests run entirely against fixtures under tests/fixtures/ (hermes_home/, openclaw_home/) - no real hermes/openclaw/ollama needed. test_cyclonedx_schema.py validates real output against the official CycloneDX 1.6 JSON Schema; test_sarif.py does the same for SARIF 2.1.0; test_sign.py and the sign/verify-signature tests in test_cli.py run against a real, locally installed cosign binary and skip themselves (never mock it) when one isn't on PATH.

What's not here yet

This project only ever ships a feature once it's been verified against real behavior, not because it looks reasonable - that discipline is documented in full, including every deliberate deferral and the reasoning behind it, in SPEC.md. As of v0.9.0: vulnerability/ VEX data (real OSV.dev records, scan-vulns), CycloneDX declarations (narrow, self-assessed coverage claims), a compliance evidence mapping (NIST AI RMF, OWASP Top 10 for LLM Applications 2025, MITRE ATLAS - real, independently-verified control/technique IDs, always "evidence collected"/"partial evidence"/"not assessed", never a certification claim), narrow observed/inferred confidence tags plus evidence chains in the Component Inspector, optional Ollama /api/show prompt-template/ tokenizer metadata (verified against Ollama's own published API doc, not against a live server - none was available), and a collapsed-by-default per-instance Dependency graph explorer with Raw-BOM-to-Inspector cross-navigation are all in. Still open: ISO/IEC 42001 and SLSA in the compliance mapping (no real, freely fetchable canonical source for the former; the latter doesn't have a meaningful evidence source for what this scanner actually reads), CycloneDX attestations/affirmation (no real requirements catalog to map to), and a live-Ollama-verified /api/show - each waiting on either a real data source or its own design pass, not attempted half-way.

License

MIT - see LICENSE.

Release files for agent-harness-aibom 0.9.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for agent-harness-aibom 0.9.1
File Size Uploaded
agent_harness_aibom-0.9.1.tar.gz 121.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for agent-harness-aibom 0.9.1
File Interpreter ABI Platform
agent_harness_aibom-0.9.1-py3-none-any.whl Python 3 none any Details

Total release size: 257.8 kB

Release files / agent_harness_aibom-0.9.1.tar.gz

Download URL agent_harness_aibom-0.9.1.tar.gz
Size 121.3 kB
Tags Source
SHA-256 checksum
How to use checksums
400f7bed4e1bc8060e1f983c8cdddc1b6bfd76a8059f071f7d9d230553764c6f
BLAKE2b-256 checksum
How to use checksums
220639b5cece0d8f83dd45385a403ee093662a2a6a2b52d6ca077215be6f395e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log

Release files / agent_harness_aibom-0.9.1-py3-none-any.whl

Download URL agent_harness_aibom-0.9.1-py3-none-any.whl
Size 136.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5f86be3312de7250511a52d30385618f8d5d18e6a0a04d38d202237389dd8dc8
BLAKE2b-256 checksum
How to use checksums
b6ace012e63a723460fadf2d896860784b8a5e8717139d86c26a9b23e800bc7c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page