HoneyBadger
Security scanner for skills, tools, and MCP servers used by AI assistant runtimes.
HoneyBadger don't care. HoneyBadger checks anyway.
What it does
Before anything gets installed on a family home server running AI assistants, HoneyBadger checks it. HoneyBadger performs static analysis only -- it reads source code and metadata but never executes the scanned code.
Install
# Go install (requires Go 1.22+)
go install github.com/famclaw/honeybadger/cmd/honeybadger@latest
# Binary download (Linux amd64)
curl -fsSL https://github.com/famclaw/honeybadger/releases/latest/download/honeybadger-linux-amd64 \
-o honeybadger && chmod +x honeybadger
# Docker
docker pull ghcr.io/famclaw/honeybadger:latest
All platforms: Releases — Linux (amd64, arm64, armv7), macOS (arm64, amd64). Verify downloads: see SECURITY.md.
Usage
CLI
honeybadger scan <repo-url> [flags]
where <repo-url> supports HTTPS, SSH/git@ (e.g., [email protected]:user/repo), and local paths.
Flags:
--paranoia string off|minimal|family|strict|paranoid (default: family)
--format string ndjson|text|sarif (default: ndjson)
--llm string LLM endpoint override
--db string Path to audit trail file
--installed-sha string SHA256 of installed version
--installed-tool-hash SHA256 of installed MCP tool definitions
--tool-manifest string Path to MCP tools/list JSON for tool-definition analysis
--tool-baseline string Path to approved tools/list JSON for rug-pull diffing
--force Skip scan, exit 0
--offline Skip network calls, scan local only
--path string Subdirectory within repo to scan
See [docs/sarif-output.md] for details on SARIF output.
MCP Server
honeybadger --mcp-server
Speaks MCP JSON-RPC over stdio. Exposes honeybadger_scan tool.
Piped input
cat SKILL.md | honeybadger scan -
Reads from stdin and scans it as a single file (SKILL.md by default).
Input is capped at 10 MB.
Suppressing findings
Place a .honeybadgerignore file in your repository root. Each line suppresses
findings by rule ID, optionally constrained by a glob pattern or snippet SHA256:
# Suppress all findings for a rule
SECRET_IN_CODE
# Suppress only in test fixtures
SECRET_IN_CODE *.test.yaml
# Suppress a specific snippet by SHA256
SECRET_IN_CODE sha256:<64-hex-digit-sha256-of-the-snippet>
Suppressed findings are excluded from the verdict. A suppression_summary
NDJSON event is emitted when findings are suppressed. In text mode, a summary
line is printed after the verdict.
Rules CLI
honeybadger rules list
honeybadger rules explain <rule-id>
What it checks
| Check | Scanner | Description |
|---|---|---|
| Secrets | gitleaks v8 | 800+ credential patterns, noise reduction for test files |
| CVEs | osv.dev | Batch API across Go, npm, PyPI, Rust, Ruby, Maven (8 lockfile formats) |
| curl|bash | supplychain | Downloads and executes remote scripts |
| eval remote | supplychain | Evaluates remotely fetched code |
| Reverse shell | supplychain | nc/netcat/bash reverse shell patterns |
| Crypto mining | supplychain | Coinhive, xmrig, stratum+tcp patterns |
| Data exfil | supplychain | Webhook/requestbin exfiltration endpoints |
| Typosquat | supplychain | Edit-distance check against popular package names |
| SKILL.md fields | meta | Required fields and format validation |
| Capability drift | capability | Declared requires.* vs actual code: network/filesystem/bins/env reads (family+) |
| Build provenance | attestation | GitHub Attestation API + workflow check (strict+) |
| Cosign/SHA256 | attestation | Cosign signatures and checksum files present (strict+) |
| Prompt injection | skillsafety | Override phrases in 11 languages, across SKILL.md and any referenced text files (family+) |
| Homoglyphs | skillsafety | Mixed-script words (Latin+Cyrillic/Greek/Armenian) (family+) |
| Zero-width chars | skillsafety | Hidden Unicode characters in skill content (family+) |
| RTL override | skillsafety | Right-to-left text direction manipulation (family+) |
| Data exfil intent | skillsafety | Sensitive paths + external/webhook URLs correlation (family+) |
| Multi-language hiding | skillsafety | Unexpected script blocks in primary-language skills (family+) |
| MCP tool injection | mcptool | Prompt injection in MCP tool/param descriptions, titles, defaults, enums, and the SKILL.md body (family+) |
| MCP read-from-other-file | mcptool | SKILL.md directing the agent to read/see/consult a separate instruction file (family+) |
| MCP tool obfuscation | mcptool | Zero-width / homoglyph / RTL / Tags-block chars in tool definitions (family+) |
| MCP cross-tool shadowing | mcptool | One tool's description redefining another tool's behavior (family+) |
| MCP capability mismatch | mcptool | Tool declares readOnlyHint but params/source show writes (family+) |
| MCP rug pull | mcptool | Tool definitions changed since the approved baseline (family+) |
Binary Detection
HoneyBadger implements robust binary file detection to prevent scanning of non-text content. Two approaches are used:
- Null-byte detection - Checks for null bytes (0x00) in the first 512 bytes of file content
- UTF-8 validation - Ensures content is valid UTF-8 encoded text
Binary files are automatically skipped during scanning to avoid processing executables, libraries, or compiled code that wouldn't benefit from security scanning rules.
Why HoneyBadger
HoneyBadger analyzes MCP tool definitions from a caller-supplied manifest and never executes the server, unlike scanners that call tools/list on a live server.
| HoneyBadger | Cisco MCP Scanner | Snyk agent-scan | Proximity | |
|---|---|---|---|---|
| Single binary | Go | Python | Python | Python |
| Offline mode | yes | partial (static) | no | partial (local Ollama) |
| MCP server mode | yes (JSON-RPC) | scans MCP servers | scans MCP servers | scans MCP servers |
| Paranoia levels | 5 tiers | no | no | no |
| SKILL.md scanning | yes | no | yes | yes |
| CVE scanning | 8 lockfile formats | no | no | no |
| Secrets detection | gitleaks 800+ | Yara | yes (skills mode) | yes (skill scanning) |
| Supply chain | yes | no | no | no |
| Attestation | yes | no | no | no |
| No cloud dependency | yes | partial | no (needs Snyk API) | partial (Ollama ok) |
| Runs on ARM/RPi | yes | no | no | no |
| Audit trail | JSONL | no | no | no |
Integrations
| Platform | Type | Guide |
|---|---|---|
| Claude Code | Skill + MCP + Hook | docs/CLAUDE_CODE.md |
| OpenAI Codex CLI | Hook | docs/integrations/codex-cli.md |
| FamClaw | Built-in pipeline | docs/INSTALLATION.md |
| OpenClaw | Skill | docs/INSTALLATION.md |
| PicoClaw | Skill | docs/INSTALLATION.md |
| NanoBot | Skill | docs/INSTALLATION.md |
| CI/CD | CLI | docs/EXAMPLES.md |
| MCP | JSON-RPC stdio | docs/EXAMPLES.md |
Paranoia levels
| Level | Scanners | LLM | Blocks on |
|---|---|---|---|
| off | None | No | Nothing |
| minimal | secrets, cve | No | CRITICAL |
| family | secrets, cve, supplychain, meta, capability, skillsafety | Yes | HIGH+ |
| strict | family + attestation | Yes | MEDIUM+ (WARN=FAIL) |
| paranoid | family + attestation + allowlist | Yes | LOW+ |
Output
Newline-delimited JSON streamed to stdout. Events: progress, finding, cve, health, attestation, sandbox, suppression_summary, result.
Findings include rule metadata when available: rule_id, more_info_url, and references from the source YAML rule.
In text mode, the severity tag shows [SEVERITY rule_id] and a → url line links to further documentation.
CVE severity is graded from the CVSS v3/v4 score, fetching the full osv.dev record when the batch response omits it. When no CVSS score is available, severity falls back to MEDIUM and the finding carries reason: severity_unknown so a fallback MEDIUM is distinguishable from a graded one.
Exit codes: 0=PASS, 1=WARN, 2=FAIL, 3=error.
Project structure
honeybadger/
├── cmd/honeybadger/
│ ├── main.go # CLI entry point — full pipeline wiring
│ ├── mcp.go # MCP server mode — JSON-RPC over stdio
│ ├── mcp_test.go # MCP server tests via in-process client
│ ├── integration_test.go # CLI + MCP integration tests (build tag: integration)
│ └── e2e_test.go # E2E stdio MCP server subprocess tests
├── internal/
│ ├── engine/
│ │ ├── engine.go # Verdict computation, tier/sandbox detection, scanner list builder
│ │ └── engine_test.go
│ ├── fetch/
│ │ ├── fetch.go # Repo type, Route(), Fetcher interface
│ │ ├── fetch_test.go
│ │ ├── github.go # GitHub fetcher
│ │ ├── gitlab.go # GitLab fetcher
│ │ ├── stdin.go # Stdin fetcher (piped input via -)
│ │ ├── stdin_test.go
│ │ └── tarball.go # Tarball fetcher
│ ├── ignore/
│ │ ├── ignore.go # .honeybadgerignore parser and finding filter
│ │ └── ignore_test.go
│ ├── report/
│ │ ├── types.go # Emitter interface
│ │ ├── ndjson.go # NDJSON streaming emitter
│ │ ├── ndjson_test.go
│ │ ├── text.go # Human-readable text emitter
│ │ ├── text_test.go
│ │ ├── llm.go # LLM prompt assembly + verdict calling
│ │ └── llm_test.go
│ ├── scan/
│ │ ├── finding.go # Finding struct, severity constants, ParanoiaLevel, Options
│ │ ├── finding_test.go
│ │ ├── scan.go # ScanFunc type, RunAll (concurrent runner with fan-in)
│ │ ├── scan_test.go
│ │ ├── helpers.go # WalkCode, IsPlaceholder, Redact, EditDistance, IsBinaryFile
│ │ ├── fileclass.go # File-role classifier (Code/Test/Doc/Config/Rules), finding re-weighting
│ │ ├── fileclass_test.go
│ │ ├── markdown.go # Markdown code-block vs prose discrimination
│ │ └── markdown_test.go
│ │ ├── binary.go # Shared binary detection functions
│ ├── scanner/
│ │ ├── secrets/
│ │ │ ├── secrets.go # Secrets scanner (gitleaks-powered)
│ │ │ └── secrets_test.go
│ │ ├── supplychain/
│ │ │ ├── supplychain.go # Supply chain risk patterns + typosquat detection
│ │ │ └── supplychain_test.go
│ │ ├── cve/
│ │ │ ├── cve.go # CVE scanner via osv.dev API
│ │ │ ├── deps.go # Dependency parser (8 lockfile formats)
│ │ │ ├── cve_test.go
│ │ │ └── deps_test.go
│ │ ├── meta/
│ │ │ ├── meta.go # SKILL.md meta scanner
│ │ │ └── meta_test.go
│ │ ├── capability/
│ │ │ ├── capability.go # Capability drift scanner (requires vs code)
│ │ │ ├── network.go # Network capability evidence detection
│ │ │ ├── filesystem.go # Filesystem capability evidence detection
│ │ │ ├── bins.go # Executable invocation detection
│ │ │ ├── env.go # Environment variable read detection
│ │ │ └── capability_test.go
│ │ ├── skillsafety/
│ │ │ ├── scanner.go # Skill-safety scanner entry point (extract → evaluate → findings)
│ │ │ ├── signals.go # Structured signal extraction from skill files
│ │ │ ├── extract.go # Prompt-injection override-phrase extraction
│ │ │ ├── evaluate.go # Safety-rule evaluation over extracted signals
│ │ │ ├── language.go # Primary-language / script-block detection
│ │ │ ├── unicode.go # Zero-width / homoglyph / RTL codepoint detection
│ │ │ ├── evaluate_test.go
│ │ │ ├── extract_test.go
│ │ │ ├── language_test.go
│ │ │ ├── scanner_test.go
│ │ │ └── unicode_test.go
│ │ ├── attestation/
│ │ │ ├── attestation.go # Attestation verification scanner
│ │ │ └── attestation_test.go
│ │ └── mcptool/
│ │ ├── mcptool.go # MCP tool scanner entry point
│ │ ├── manifest.go # tools/list JSON loader
│ │ ├── model.go # MCP tool definition types
│ │ ├── detect.go # Top-level detection dispatcher
│ │ ├── extract.go # Source-based tool extraction (no manifest)
│ │ ├── textfields.go # Text field enumeration across tool structs
│ │ ├── injection.go # Prompt-injection pattern detection
│ │ ├── unicode.go # Zero-width / homoglyph / RTL / Tags-block detection
│ │ ├── shadowing.go # Cross-tool shadowing detection
│ │ ├── capability.go # readOnlyHint vs params/source mismatch detection
│ │ ├── rugpull.go # Baseline diff / rug-pull detection
│ │ ├── mcptool_test.go
│ │ ├── manifest_test.go
│ │ ├── detect_test.go
│ │ ├── extract_test.go
│ │ ├── textfields_test.go
│ │ ├── injection_test.go
│ │ ├── unicode_test.go
│ │ ├── shadowing_test.go
│ │ ├── capability_test.go
│ │ ├── rugpull_test.go
│ │ └── integration_test.go
│ ├── store/
│ │ ├── audit.go # JSONL audit trail writer
│ │ └── audit_test.go
│ └── testfixture/
│ ├── fixtures.go # Builder functions returning *fetch.Repo with in-memory files
│ ├── fixtures_test.go # Smoke tests for all fixtures
│ └── mock_osv.go # Mock osv.dev server for testing CVE scanner
├── rules/
│ ├── supplychain/ # Supply-chain detection YAML rules
│ ├── skillsafety/ # Skill-safety detection YAML rules
│ └── mcptool/
│ ├── concealment.yaml # Concealment / obfuscation prompt-injection patterns
│ ├── threat-framing.yaml # Threat-framing prompt-injection patterns
│ └── silent-redirect.yaml # Silent-redirect prompt-injection patterns
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ ├── ci.yml
│ ├── codeql.yml
│ └── release.yml
├── docs/
│ ├── INSTALLATION.md # Installation guide for all runtimes
│ ├── CLAUDE_CODE.md # Claude Code integration guide (MCP config, hooks)
│ ├── EXAMPLES.md # CLI and MCP usage examples
│ └── docs_test.go # Doc validation tests (11 functions, 35 checks)
├── .gitignore
├── .goreleaser.yml # GoReleaser config: builds, signing, Docker, SBOM, changelog
├── Dockerfile # Multi-stage distroless image (local dev)
├── Dockerfile.goreleaser # GoReleaser Docker image (pre-built binary)
├── go.mod
├── go.sum
├── Makefile
├── README.md
├── SECURITY.md
└── SKILL.md # AgentSkills manifest for skill registries
Status
v0.5.1 released -- download binaries
Eight scanners: secrets, cve, supplychain, meta, capability, skillsafety,
attestation, mcptool. The mcptool scanner analyzes MCP tool definitions for
poisoning, cross-tool shadowing, capability mismatch, and rug-pull drift.
Detection rules are YAML-defined and runtime-extensible. Binaries signed with
Sigstore cosign, SPDX SBOMs attached to every release.
File-role classification distinguishes a threat described in documentation or defined in a rule corpus from one present in executable code, so HoneyBadger passes its own scan cleanly at every paranoia tier.
See CHANGELOG.md for version history.
Building
make build # current platform
make cross # all 5 targets (linux arm64/armv7/amd64, darwin arm64/amd64)
make test # run all tests
make self-check # scan ourselves at strict paranoia (requires prior release)
make self-check-bootstrap # scan at minimal paranoia (for initial releases only)
make release-dry # test GoReleaser locally (snapshot, no publish)
Extending with custom rules
Detection rules are YAML files embedded in the binary. Add custom rules at
runtime by dropping .yaml files into ~/.honeybadger/rules/ (or set
HONEYBADGER_RULES_DIR):
mkdir -p ~/.honeybadger/rules/custom
cat > ~/.honeybadger/rules/custom/my-rule.yaml << 'EOF'
id: my_custom_check
kind: pattern
scanner: supplychain
category: custom
severity: HIGH
signal: file_content
patterns:
- regex: 'SOME_DANGEROUS_PATTERN'
description: "My custom detection"
message: "Custom rule matched"
EOF
honeybadger scan ./my-project # custom rule will fire
See rules/README.md for the full format spec.
Release Checklist
make self-checkpasses at strict paranoia (orself-check-bootstrapfor first release)- Tag:
git tag vX.Y.Z && git push origin vX.Y.Z - GoReleaser builds, signs, and publishes via
.github/workflows/release.yml - Verify release: see SECURITY.md
- Set GitHub topics:
security,mcp,supply-chain,scanner,agentskills,golang
No comments yet
Be the first to share your take.