Bawbel Scanner
Open-source CLI scanner for agentic AI components — SKILL.md files, MCP servers, system prompts, plugins. Detects AVE vulnerabilities before they reach production.
Run your first scan
# Scan a file
bawbel scan ./my-skill.md
# Full remediation report
bawbel report ./my-skill.md
# Recursive scan, fail on HIGH+
bawbel scan ./skills/ --recursive --fail-on-severity high
# Watch mode
bawbel scan ./skills/ --watch
What is covered
40+ detection rules
Pattern rules mapped 1:1 to AVE records. Plus 3 new rules in v1.2.0: hook hijacking, credentials, delegation chains.
6 detection engines
Pattern, YARA, Semgrep, LLM semantic analysis, Magika file-type verification, Docker behavioral sandbox.
3 output formats
Text for humans, JSON for tooling, SARIF for GitHub Security tab and any SIEM.
Justified suppression
New in v1.2.0. Every suppression requires a reason and reviewer. Accepted risks resurface on expiry.
Installation
Requires Python 3.10+. The base install uses no optional dependencies.
pip install bawbel-scanner # core - pattern engine only
pip install "bawbel-scanner[yara]" # + YARA rules
pip install "bawbel-scanner[semgrep]" # + Semgrep rules
pip install "bawbel-scanner[llm]" # + LLM semantic engine
pip install "bawbel-scanner[all]" # everything
Verify
bawbel version
Bawbel Scanner v1.2.0
Detection Engines:
✓ Pattern 40 rules · always active
✓ YARA v4.5.x · active
✓ Semgrep v1.159.0 · active
✗ LLM not installed
✗ Sandbox disabled
bawbel scan
Scan a component or directory for AVE vulnerabilities.
bawbel scan PATH [OPTIONS]Options
| Option | Default | Description |
|---|---|---|
--format | text | text · json · sarif |
--fail-on-severity | — | Exit 2 if findings at or above: critical high medium low |
--recursive, -r | false | Scan all matching files in a directory tree |
--no-ignore | false | Disable all suppressions — audit mode |
--watch, -w | false | Watch for file changes and re-scan |
Exit codes
| Code | Meaning |
|---|---|
0 | Clean, or findings below threshold |
2 | Findings at or above --fail-on-severity threshold |
bawbel creds & bawbel chain
Focused scans that filter to a specific threat category. Both use the same output format as bawbel scan.
For a full security scan use bawbel scan. bawbel creds and bawbel chain are for targeted triage sessions or specialized CI gates.
bawbel creds
Filters to bawbel-hardcoded-credential (AVE-2026-00047) only. Detects API keys, tokens, passwords, private key blocks, and URL-embedded credentials.
bawbel creds ./skills/ --recursive
bawbel creds ./skills/ --fail-on-any
bawbel creds ./skill.md --format jsonbawbel chain
Filters to bawbel-unsafe-delegation (AVE-2026-00048) only. Flags sub-agent spawning with inherited permissions and no trust boundary.
bawbel chain ./skills/ --recursive
bawbel chain ./skills/ --fail-on-any
bawbel chain ./skill.md --format jsonShared options
| Option | Description |
|---|---|
--recursive, -r | Scan directory recursively |
--no-ignore | Disable all suppressions |
--fail-on-any | Exit 2 if any finding is found |
--format | text (default) or json |
bawbel accept
Mark a finding as a false positive or accepted risk. Inserts a justified suppression comment directly into the source file. The comment is the canonical record — no external database.
# Mark as false positive (permanent suppression)
bawbel accept AVE-2026-00001 ./skill.md --line 7 \
--reason "Internal registry endpoint, not attacker-controlled" \
--type false-positive
# Mark as accepted risk with 90-day expiry
bawbel accept AVE-2026-00047 ./skill.md --line 3 \
--reason "Placeholder value, replaced at deploy time" \
--type accepted-risk \
--expires 90d
# List all accepted findings
bawbel accept --list
# Show findings expiring within 30 days (exits 1 in CI when within 14 days)
bawbel accept --expiring-soon --within 30
# Send anonymous FP signal to PiranhaDB
bawbel accept AVE-2026-00001 ./skill.md --line 7 \
--reason "Internal endpoint" \
--type false-positive \
--reportExpiry formats
| Format | Example |
|---|---|
| Relative days | 90d |
| Relative months | 3m |
| Relative years | 1y |
| ISO date | 2026-08-16 |
Justified suppression
The existing bawbel-ignore silently removes findings. Justified suppression requires a human-readable reason, records a reviewer, and supports expiry dates for accepted risks.
False positive
The finding matched the pattern but the content is not dangerous. Suppressed permanently.
reason: Internal registry endpoint, not attacker-controlled
reviewer: chaksaray
reviewed: 2026-05-16
-->
Accepted risk
The finding is real but the behavior is intentional and reviewed. When the expiry passes, the finding resurfaces as active on the next scan.
reason: Placeholder value, replaced at deploy time by CI pipeline
reviewer: chaksaray
reviewed: 2026-05-16
expires: 2026-08-16
-->
Expired accepted risks resurface automatically as active findings. The finding's suppression_reason will say accepted_risk_expired with the original reviewer and date.
JSON audit trail
Justified suppressions appear in accepted_findings in JSON output — separate from suppressed_findings.
{
"accepted_findings": [{
"ave_id": "AVE-2026-00047",
"suppression_type": "accepted_risk",
"reason": "Placeholder replaced at deploy time",
"reviewer": "chaksaray",
"expires_at": "2026-08-16",
"days_until_expiry": 92,
"is_expired": false
}]
}Suppression
Four mechanisms for suppressing false positives, plus the automatic FP pipeline.
Inline comment
content <!-- bawbel-ignore -->
content <!-- bawbel-ignore: bawbel-external-fetch -->
content <!-- bawbel-ignore: AVE-2026-00001 -->
content # bawbel-ignore
content // bawbel-ignoreBlock suppression
<!-- bawbel-ignore-start -->
... all findings in this section suppressed ...
<!-- bawbel-ignore-end -->.bawbelignore
docs/**
tests/fixtures/**
examples/**Audit mode
bawbel scan ./skills/ --no-ignore
bawbel report ./skill.md --no-ignoreAutomatic FP pipeline
| Layer | Mechanism |
|---|---|
| FP-1 | Code fence stripping — content inside ``` blocks blanked before analysis |
| FP-2 | Negation context — lines preceded by "Never do this:" or "Bad example:" |
| FP-3 | Confidence scoring — docs/ and examples/ paths reduce confidence |
| FP-4 | LLM meta-analyzer — medium-confidence findings reviewed by LLM (optional) |
New AVE records in v1.2.0
| Rule ID | AVE ID | Sev | AIVSS | Description |
|---|---|---|---|---|
bawbel-hook-hijack |
AVE-2026-00046 |
CRITICAL | 9.1 | MCP tool hook hijacking — registers hooks to intercept all tool calls |
bawbel-hardcoded-credential |
AVE-2026-00047 |
HIGH | 7.8 | Hardcoded API keys, tokens, passwords, and URL-embedded credentials |
bawbel-unsafe-delegation |
AVE-2026-00048 |
HIGH | 8.2 | Sub-agent spawning with inherited permissions and no trust boundary |
Detection engines
Six independent engines. Each returns list[Finding]. Results merge and deduplicate before toxic flow analysis.
Pattern — always active
40 regex rules mapped to AVE records. Stdlib only, completes in under 5ms. No install needed.
YARA — pip install "bawbel-scanner[yara]"
39 YARA rules. Multi-condition matching, string combinations, case variations. Catches things regex misses.
Semgrep — pip install "bawbel-scanner[semgrep]"
41 structural rules. Spans multiple lines, context-aware. Runs as external subprocess.
LLM — pip install "bawbel-scanner[llm]" + API key
Semantic analysis via LiteLLM. Catches synonym attacks, multi-paragraph injections, novel patterns. Auto-detects provider from API keys.
Sandbox — Docker + BAWBEL_SANDBOX_ENABLED=true
Dynamic behavioral analysis. Executes the component in a locked-down container and monitors actual runtime behavior.
Every engine skips silently when its dependency is not installed. scan() always completes and always returns a result — it never raises.
CI/CD
# .github/workflows/bawbel.yml
- name: Bawbel scan
uses: bawbel/scanner@v1
with:
path: ./skills/
fail-on-severity: high
format: sarif
output: bawbel.sarif
- name: Upload to GitHub Security
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: bawbel.sarifCheck expiring accepted risks in CI
# Warn when accepted risks expire within 14 days (exits 1)
bawbel accept --expiring-soon --within 14Pre-commit
# .pre-commit-config.yaml
repos:
- repo: https://github.com/bawbel/scanner
rev: v1.2.0
hooks:
- id: bawbel-scan
args: [--fail-on-severity, high]Output formats
bawbel scan ./skills/ --format text # human-readable (default)
bawbel scan ./skills/ --format json # machine-readable, includes accepted_findings
bawbel scan ./skills/ --format sarif # SARIF 2.1.0 for GitHub Security / GHASJSON output includes accepted_findings (new in v1.2.0) alongside the existing findings and suppressed_findings arrays.