Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Discovery ──> Scanning ──> Enrichment ──> Blast Radius ──> Compl

| Entry point | File | Purpose |
|---|---|---|
| CLI | `cli.py` | Click-based CLI with 15+ commands |
| CLI | `cli.py` | Click-based CLI with 22+ commands and groups |
| MCP Server | `mcp_server.py` | FastMCP server with 22 tools |
| Proxy | `proxy.py` | MCP JSON-RPC proxy with runtime enforcement |
| API | `api/server.py` | FastAPI REST server with job queue |
Expand Down Expand Up @@ -70,10 +70,11 @@ Each module exports `tag_blast_radius(br: BlastRadius)` to annotate findings.
│ # Prometheus metrics, JSONL audit trail, webhook alerts
├── runtime/
│ ├── __init__.py # Public exports
│ ├── detectors.py # 6 detectors: ToolDrift, ArgumentAnalyzer, CredentialLeak,
│ │ # RateLimit, SequenceAnalyzer, ResponseInspector
│ ├── detectors.py # 7 detectors: ToolDrift, ArgumentAnalyzer, CredentialLeak,
│ │ # RateLimit, SequenceAnalyzer, ResponseInspector,
│ │ # VectorDBInjectionDetector (RAG/cache-poison, CRITICAL severity)
│ └── patterns.py # Regex patterns for credentials, args, cloaking, SVG, Unicode
├── enforcement.py # Tool poisoning detection (10 checks): injection scanning,
├── enforcement.py # Tool poisoning detection (8 checks): injection scanning,
│ # inputSchema analysis, capability combos, CVE exposure,
│ # drift detection, config analysis, over-permission
├── mcp_introspect.py # Live MCP server connection — tools/list, resources/list, drift
Expand Down Expand Up @@ -125,8 +126,8 @@ Each module exports `tag_blast_radius(br: BlastRadius)` to annotate findings.
```
├── api/
│ ├── server.py # FastAPI REST server with job queue
│ ├── auth.py # JWT authentication
│ ├── audit_log.py # API audit logging
│ ├── auth.py # scrypt KDF API keys, RBAC roles (admin/analyst/viewer)
│ ├── audit_log.py # HMAC-SHA256 signed audit log (InMemory + SQLite backends)
│ ├── store.py # Base data store abstraction
│ ├── postgres_store.py # PostgreSQL backend
│ ├── snowflake_store.py # Snowflake backend
Expand Down Expand Up @@ -213,7 +214,7 @@ cli.py / mcp_server.py / api/server.py
| Test functions | 3,419 (3,480 collected by pytest) |
| MCP tools | 22 |
| Compliance frameworks | 10 |
| Runtime detectors | 6 |
| Runtime detectors | 7 |
| Cloud providers | 12 |
| SAST CWE mappings | 52 |

Expand Down
80 changes: 80 additions & 0 deletions AUDIT.md
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,80 @@ ScanRequest → _run_scan_sync() [ThreadPoolExecutor]

---

---

## Area 7: CLI (cli.py)

### Commands verified (22 total)

`scan`, `inventory`, `validate`, `where`, `check`, `verify`, `history`, `diff`, `policy-template`, `serve`, `api`, `mcp-server`, `completions`, `apply`, `schedule` (group: add/list/remove), `registry` (group: list/search/update/enrich/sync), `proxy`, `guard`, `protect`, `watch`, `analytics`, `dashboard`

**`serve` vs `api`**: Not duplicates. `serve` = API + bundled Next.js UI, simpler flags. `api` = production server with `--workers`, `--api-key`, `--rate-limit`. Different extras: `[ui]` vs `[api]`.

**`--introspect`**: Flag on `scan`, wired to `mcp_introspect.py`. Not a standalone command.

### Fixed in this audit

| # | File | Fix |
|---|------|-----|
| F13 | `cli.py` | `api` command had no `--log-level` or `--log-json` (inconsistent with `serve`). Added both; `setup_logging()` now called; uvicorn log level wired to user input. |

### Open issues

| # | Severity | Issue |
|---|----------|-------|
| O5 | LOW | `guard` command (pre-install CVE check wrapping pip/npm) not mentioned in README. Functional, tested, just undocumented externally. |

---

## Area 8: Discovery + Peripheral Runtime Modules

### Discovery (discovery/__init__.py)

- **20 named MCP clients** in `CONFIG_LOCATIONS` (+ `CUSTOM` = 21 `AgentType` enum values). README "20" is accurate. ARCHITECTURE.md "21" also accurate (counts all enum values). No gap.
- `sanitize_env_vars()` called on all env blocks. `validate_path()` used before reading config files. PASS.
- Docker MCP Toolkit handled separately (Docker socket path, not `CONFIG_LOCATIONS`). Correct.

### Runtime peripheral modules

| Module | Lines | Wired to CLI | Tests | Status |
|--------|-------|-------------|-------|--------|
| `watch.py` | 304 | `watch` command | `test_watch.py` indirect | PASS |
| `mcp_introspect.py` | 360 | `scan --introspect` | `test_discovery.py` | PASS |
| `enforcement.py` | 759 | `scan` pipeline | `test_enforcement.py` | PASS |

`enforcement.py`: 8 check functions (`check_cve_exposure`, `check_drift`, `check_claude_config`, `check_agentic_search_risk`, `check_over_permission`, `check_tool_name_collisions` + 2 more). ARCHITECTURE.md previously said "10 checks" — corrected to 8 (F11).

---

## Area 9: Accuracy (ARCHITECTURE.md + SVGs + README)

### Fixed in this audit

| # | File | Fix |
|---|------|-----|
| F7 | `ARCHITECTURE.md` | `auth.py — JWT authentication` → `scrypt KDF API keys, RBAC roles (admin/analyst/viewer)`. No JWT anywhere in codebase. |
| F8 | `ARCHITECTURE.md` | `Runtime detectors: 6` → `7` in Module Stats |
| F9 | `ARCHITECTURE.md` | Detector list now includes `VectorDBInjectionDetector` |
| F10 | `ARCHITECTURE.md` | `CLI with 15+ commands` → `22+ commands and groups` |
| F11 | `ARCHITECTURE.md` | `enforcement.py — 10 checks` → `8 checks` |
| F12 | SVGs (7 files) | `6 detectors` → `7 detectors`: engine-internals, modes-flow, offerings-map, scanner-architecture (dark + light variants) |

### Verified accurate (no changes needed)

| Claim | Verified |
|-------|---------|
| "20 MCP clients" (README) | ✓ 20 named AgentType values |
| "22 MCP tools" | ✓ meta-test enforces this |
| "10 compliance frameworks" | ✓ 10 tagging modules |
| "52 SAST CWE mappings" | ✓ SAST_CWE_MAP count |
| "12 cloud providers" | ✓ cloud/__init__.py _PROVIDERS |
| OSV as primary vuln source | ✓ |
| scrypt KDF for API keys | ✓ auth.py (n=16384, r=8, p=1) |
| HMAC-SHA256 audit log | ✓ audit_log.py |

---

## Audit Summary (all areas)

| Area | Status | Bugs Fixed | Open Issues |
Expand All @@ -461,6 +535,9 @@ ScanRequest → _run_scan_sync() [ThreadPoolExecutor]
| Runtime layer | PASS | 2 (F5-F6) | 0 |
| API layer | PASS | 0 | 0 |
| Data flow | PASS | 0 | 0 |
| CLI | PASS | 1 (F13) | 1 (O5: guard undocumented) |
| Discovery + peripheral modules | PASS | 0 | 0 |
| Accuracy (ARCHITECTURE + SVGs + README) | PASS | 7 (F7-F12 + F13) | 0 |

---

Expand All @@ -479,3 +556,6 @@ When re-running this audit, check:
- [ ] @mcp.tool count still matches _SERVER_CARD_TOOLS count (both should be 22)?
- [ ] Any new compliance frameworks added to scanner but not to ARCHITECTURE.md?
- [ ] Databricks security checks: if CIS ever publishes a Databricks benchmark, rename accordingly
- [ ] `guard` command documented in README? (O5)
- [ ] SVG detector count updated if more detectors added?
- [ ] `serve` and `api` commands still aligned on flags as features are added?
2 changes: 1 addition & 1 deletion docs/images/engine-internals-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/images/engine-internals-light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/images/modes-flow-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/images/modes-flow-light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/images/offerings-map-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/images/offerings-map-light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/images/scanner-architecture-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/images/scanner-architecture-light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 16 additions & 1 deletion src/agent_bom/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3662,6 +3662,15 @@ def serve_cmd(host: str, port: int, persist: Optional[str], cors_allow_all: bool
metavar="DB_PATH",
help="Enable persistent job storage via SQLite (e.g. --persist jobs.db). Jobs survive restarts.",
)
@click.option(
"--log-level",
"log_level",
type=click.Choice(["DEBUG", "INFO", "WARNING", "ERROR"], case_sensitive=False),
default="INFO",
show_default=True,
help="Log verbosity level.",
)
@click.option("--log-json", "log_json", is_flag=True, help="Emit structured JSON logs (for log aggregation pipelines).")
def api_cmd(
host: str,
port: int,
Expand All @@ -3672,6 +3681,8 @@ def api_cmd(
api_key: str | None,
rate_limit_rpm: int,
persist: str | None,
log_level: str,
log_json: bool,
):
"""Start the agent-bom REST API server.

Expand All @@ -3696,6 +3707,10 @@ def api_cmd(
agent-bom api --port 9000 # custom port
agent-bom api --reload # dev mode
"""
from agent_bom.logging_config import setup_logging

setup_logging(level=log_level, json_output=log_json)

try:
import uvicorn
except ImportError:
Expand Down Expand Up @@ -3746,7 +3761,7 @@ def api_cmd(
port=port,
reload=reload,
workers=1 if reload else workers,
log_level="info",
log_level=log_level.lower(),
)


Expand Down