Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

bezoar

The open-source security scanner for the AI-agent supply chain — skills, MCP servers, plugins, subagents, and CLI add-ons across every major agent platform.

Bezoar: the legendary stone believed to neutralize any poison. This one neutralizes poisoned agent add-ons.

bezoar scan audits an add-on before it enters your agent's context: static detectors, four verdict bands, and a policy layer that turns those bands into decisions. Think npm audit for agent add-ons — offline-first, deterministic, CI-friendly, SARIF/JSON output.

License PyPI CI SARIF

1.0.0a4 ships the scanner plus the self-hosted console: inventory, finding lifecycle, CI ingest, notifications, policy history, frameworks, the local guard daemon, MCP gateway, install quarantine, and an agent-readable surface. The commands below and the guides in docs/ describe that shipped surface.

60-second quickstart

Requires Python ≥ 3.11.

# From this checkout (works before PyPI)
uv tool install .

# After the 1.0.0a2 tag is published
# uv tool install bezoar==1.0.0a2

bezoar version
bezoar scan tests/corpus/benign/claude_code/skill-markdown-lint --fail-on unsafe

Point bezoar scan at any skill directory or repo root. Flags: --format text|json|sarif (reports go to stdout; there is no --output), --fail-on, --config, --changed-since, --baseline. Exit 0 = gate passed, 1 = band ≥ --fail-on, 2 usage, 3 incomplete, 4 internal.

See Getting started for reading findings.

Illustrative text report

The live text reporter prints a band table, then per-component rows. The excerpt below is the finding shape: band is a fact; decision is policy; locations are raw file:line:column; rule ids use <family>/<rule>.

✗  band=malicious  decision=block  components=1  findings=4

generic:skill:pdf-optimizer@<hash12>  malicious → block  (band_default)

  [critical] harness_forgery/control-tag-close      SKILL.md:16:1  [hidden]
             Close of a trusted harness wrapper — the breakout primitive
             evidence: </system-reminder>

  [    high] harness_forgery/control-tag-open       SKILL.md:17:1  [hidden]
             Open of a reminder-role control tag with an imperative body
             evidence: <system-reminder priority="critical">

  [    high] harness_forgery/prose-terminator       SKILL.md:22:1  [hidden]
             Prose-form function-end marker (semantic escape)
             evidence: [Function results halted by user]

  [critical] prompt_injection/instruction-override  SKILL.md:27:1
             Instruction-override language
             evidence: Ignore all previous instructions

--format json and --format sarif are the stable machine interfaces. JSON/SARIF are byte-identical across runs (modulo stats.duration_ms).

Bands and decisions

Band is a fact about the component. Decision is what the org does. No exception, pin, baseline, or vendor trust rewrites a band.

Band (fact) Default decision (policy)
safe allow
potential_risk allow_with_notice
unsafe alert
malicious block

Four bands, four decisions. The three enforcement outcomes people quote (allow / alert / block) are the same table: allow_with_notice is the fourth decision — allow, but surface the risk. Capability-only findings (permissions, unpinned-only supply-chain, metadata) cap the band at potential_risk unless an intent-bearing finding corroborates them. Details: Policy.

Why bezoar

Enterprises deploy coding agents with no seatbelt. Skills and MCPs are executable supply-chain artifacts that load straight into an agent's context. Public scanners catch generic jailbreak phrases and leaked keys; they miss the class that actually steers the model — harness-envelope forgery (content impersonating the host harness's own control syntax). bezoar maps findings to the OWASP Agentic Skills Top 10 and adds that missing family. See Harness-envelope forgery.

A default scan performs no network requests and never executes scanned artifacts. Same inputs → same JSON/SARIF.

Comparison

Public posture as of 2026-09. “Harness-envelope forgery” means a first-class, per-platform vocabulary of harness control syntax plus structural-sequence matchers — not generic “ignore previous instructions” or “system message” regexes.

Offline Deterministic Harness-forgery detection Policy-as-code SARIF Price
bezoar Yes (default) Yes Yes Yes (.bezoar.yml) Yes Apache-2.0
Snyk agent-scan No (SNYK_TOKEN + hosted analysis) No No No (Snyk org / API) No (JSON) Free Snyk account required; commercial platform
Cisco skill-scanner Partial (patterns/YARA offline; LLM optional) Partial (LLM judge optional) No Yes (YAML presets) Yes Apache-2.0
invariantlabs mcp-scan No (Invariant API or OpenAI; connects to MCP servers) No No Partial (proxy guardrails) No Apache-2.0 + cloud / OpenAI
AIR No (inline firewall + cloud) No No Yes (product policy) Not documented Commercial

invariantlabs-ai/mcp-scan now redirects into Snyk agent-scan; the column reflects the historical Invariant CLI that still appears in comparisons. AIR is the commercial category reference — see FAQ.

Docs

Doc What it covers
Getting started Install, first scan, reading findings, exit codes
Policy .bezoar.yml, bands vs decisions, exceptions, vendor trust
Rules authoring Rule-pack schema, severity / disposition / confidence, bezoar rules lint
CI GitHub Action, baselines, SARIF, shipping reports to a console
CI ingest POST /ingest envelope, scoped tokens, HMAC
Web console Static JSON viewer and bezoar serve
Inventory and lifecycle Assets, hosts, finding status / owner / exceptions
Notifications Slack, Teams, email, webhook
Frameworks OWASP / ATLAS / ATT&CK mappings and coverage
Guard Desktop daemon, shims, ceilings, ship-to-serve
MCP gateway stdio wrap + HTTP proxy, tool poisoning
Fetch / trust / quarantine bezoar fetch, promote, trust ledger
Agents llms.txt, well-known, bezoar mcp-server
Self-hosting One-click deploys, compose, /data, upgrade, backup
Serve config bezoar-serve.yml keys, types, env vars
SSO and RBAC token / OIDC / proxy, human and machine roles
Audit and retention Hash-chained JSONL, report pruning
Harness-envelope forgery The differentiator, per-platform vocabularies, tuning
Platforms Discovery matrix and per-platform limits
FAQ Offline, determinism, guard privacy, AIR, license, SSO

Web console

The console never computes security facts — it renders bezoar.scan/v1 JSON from the CLI.

  • Viewer: open the static export (console/apps/web/out/index.html or bezoar_console.dist_path() after pip install bezoar-console). Drag-drop a JSON report; it stays in IndexedDB. SARIF is rejected — re-scan with --format json.
  • Serve: bezoar serve (loopback + /api/v1). Needs the console extra:
pip install 'bezoar[console]'   # bezoar-console==1.0.0a2
bezoar serve

Details: docs/console.md.

Self-host the console — enterprise features are a config file

Everything the AI-agent security vendors charge enterprise prices for — SSO, role-based access, a tamper-evident audit log, retention — ships in bezoar as optional YAML. No SKU, no sales call, no per-seat pricing. One container, one volume, Apache-2.0.

Deploy to Render

# docker compose (any VPS)
curl -fsSLO https://raw.githubusercontent.com/IsmailKharoub/bezoar/v1.0.0a3/deploy/compose/docker-compose.yml
BEZOAR_SERVE_TOKEN="$(openssl rand -hex 32)" docker compose up -d
# → http://127.0.0.1:8080  (add the `tls` profile + BEZOAR_DOMAIN for HTTPS via Caddy)

# Fly.io
fly launch --from https://github.com/IsmailKharoub/bezoar --no-deploy
fly secrets set BEZOAR_SERVE_TOKEN="$(openssl rand -hex 32)" && fly deploy

Then turn on the "enterprise" bits by editing bezoar-serve.yml:

auth:
  mode: oidc
  oidc: { issuer: https://login.example.com, client_id: bezoar, client_secret_file: /run/secrets/oidc }
  roles:
    default: viewer
    groups: { admin: [security-leads], editor: [security-eng] }
audit: { enabled: true }
retention: { reports: { max_age_days: 180 } }
bezoar AIR Alice (Caterpillar)
Self-hosted console Yes — one container No (cloud-only) No
SSO (OIDC) Yes, config Enterprise contract
RBAC Yes, config (viewer / editor / admin) Enterprise contract
Audit log Yes — hash-chained JSONL, bezoar audit verify Enterprise contract
Pricing Apache-2.0 Demo-gated, annual SaaS

Default posture is unchanged: bezoar serve still binds loopback with a random bearer token and touches no network. Every feature above is opt-in. → Self-hosting

GitHub Action

# permissions: { contents: read, security-events: write }
- uses: IsmailKharoub/bezoar@v1.0.0a2
  with:
    path: .
    fail-on: malicious
    upload-sarif: true

The Action always writes bezoar.sarif from bezoar scan --format sarif (stdout; the CLI has no --output), uploads it to Code Scanning when upload-sarif is true — including after a failed gate — then re-raises the CLI exit code.

License

Apache-2.0. Rule-pack pattern attributions (gitleaks) are in NOTICE and src/bezoar/data/NOTICE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bezoar-1.0.0a4.tar.gz (682.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

bezoar-1.0.0a4-py3-none-any.whl (517.0 kB view details)

Uploaded Python 3

File details

Details for the file bezoar-1.0.0a4.tar.gz.

File metadata

  • Download URL: bezoar-1.0.0a4.tar.gz
  • Upload date:
  • Size: 682.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for bezoar-1.0.0a4.tar.gz
Algorithm Hash digest
SHA256 e70c537ca94625184a0b4eb09b7eefe75eadabf7f6bd4133beb4e9cc8a13d606
MD5 885ea289f34398bec33c883a6bbd2f33
BLAKE2b-256 9e7c5bb9a2824cb29de09822dbd53e5b87a2acee9956c3697add18c4dd2e2adb

See more details on using hashes here.

Provenance

The following attestation bundles were made for bezoar-1.0.0a4.tar.gz:

Publisher: release.yml on IsmailKharoub/bezoar

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file bezoar-1.0.0a4-py3-none-any.whl.

File metadata

  • Download URL: bezoar-1.0.0a4-py3-none-any.whl
  • Upload date:
  • Size: 517.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for bezoar-1.0.0a4-py3-none-any.whl
Algorithm Hash digest
SHA256 80b6cf5a4a8072f889e335440e1788bd312e77fbf06d81dbd257f2e06e933b91
MD5 dac6b8cce86e020831e910c49808a452
BLAKE2b-256 0fd975ff937f9193191c61034413a418a35a918348de725e1ecfc7e70786800a

See more details on using hashes here.

Provenance

The following attestation bundles were made for bezoar-1.0.0a4-py3-none-any.whl:

Publisher: release.yml on IsmailKharoub/bezoar

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

1.0.0a4 This release

2 files

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