# Permissions & Trust Contract

agent-bom is a **read-only security scanner**. This document is an explicit,
auditable contract of what the tool accesses — and what it never touches.

---

## What We Read

agent-bom reads only what you explicitly ask it to scan:

| Target | How specified | What is read |
|--------|--------------|--------------|
| Agent configs | auto-discovery or `--project` | Config JSON/YAML files (e.g. `claude_desktop_config.json`) |
| Inventory | `--inventory` | Your inventory JSON file |
| Lock files | inferred from project | `package-lock.json`, `requirements.txt`, `Cargo.lock`, etc. |
| Docker images | `--image` | Image filesystem layers (via container inspection subprocess) |
| Kubernetes | `--k8s` | Pod specs via `kubectl get pods -o json` (read-only) |
| Terraform | `--tf-dir` | `.tf` source files (no state files, no `.tfvars`) |
| GitHub Actions | `--gha` | `.github/workflows/*.yml` files |
| SBOM files | `--sbom` | CycloneDX/SPDX JSON you provide |

---

## Auto-Discovery Config Paths

**Auto-discovery** reads known client and project paths for supported agent
surfaces. Most entries are fixed file paths; some supported clients use bounded
project or profile globs so teams can find the same configs their tools load.
If a file does not exist, it is silently skipped.

### Global config files

| Client | macOS path | Linux path |
|--------|-----------|------------|
| Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` | `~/.config/Claude/claude_desktop_config.json` |
| Claude Code | `~/.claude/settings.json`, `~/.claude.json` | same |
| Cursor | `~/Library/Application Support/Cursor/User/globalStorage/cursor.mcp/mcp.json`, `~/.cursor/mcp.json` | `~/.config/Cursor/User/globalStorage/cursor.mcp/mcp.json`, `~/.cursor/mcp.json` |
| Windsurf | `~/.windsurf/mcp.json`, `~/Library/Application Support/Windsurf/User/globalStorage/windsurf.mcp/mcp.json` | `~/.windsurf/mcp.json` |
| Cline (VS Code) | `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json` | `~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json` |
| VS Code Copilot | `~/Library/Application Support/Code/User/mcp.json` | `~/.config/Code/User/mcp.json` |
| Continue.dev | `~/.continue/config.json` | same |
| Zed | `~/.config/zed/settings.json` | same |
| OpenClaw | `~/.openclaw/openclaw.json` | same |
| Cortex Code | `~/.snowflake/cortex/mcp.json` | same |
| Docker MCP Toolkit | `~/.docker/mcp/registry.yaml`, `~/.docker/mcp/catalogs/docker-mcp.yaml` | same |

### Project-level config files (current working directory only)

- `.mcp.json`
- `mcp.json`
- `.cursor/mcp.json`
- `.vscode/mcp.json`
- `.openclaw/openclaw.json`

### Docker Compose files (current working directory only)

- `docker-compose.yml`
- `docker-compose.yaml`
- `compose.yml`
- `compose.yaml`

The exact path set changes as supported clients are added. Use
`agent-bom where --json` or `agent-bom where --dry-run` to preview the paths
that would be read on a given machine before any scan runs.

Default discovery stays scoped to known agent, MCP, Docker Compose, JetBrains,
and project config surfaces; arbitrary filesystem crawling is not part of
auto-discovery.

---

## What We Extract From Config Files

From each MCP client config, agent-bom extracts:

| Data element | Example | Used for |
|-------------|---------|----------|
| Server name | `"filesystem"` | Inventory labeling |
| Command | `"npx"` | Package identification |
| Arguments | `["@modelcontextprotocol/server-filesystem", "/path"]` | Package name + version extraction |
| Env var **names** | `"OPENAI_API_KEY"` | Credential exposure mapping |

### What is NOT extracted

- **Environment variable values** — only names are read, never values
- **File path arguments** — shown in inventory but never traversed or opened
- **Config file contents** — only the structured server definitions are parsed

### Credential name detection

Env var names matching these patterns are flagged as credential-like:

**Flagged**: `*KEY*`, `*TOKEN*`, `*SECRET*`, `*PASSWORD*`, `*CREDENTIAL*`, `*API_KEY*`, `*APIKEY*`, `*AUTH*`, `*PRIVATE*`, `*CONNECTION*`, `*CONN_STR*`, `*DATABASE_URL*`, `*DB_URL*`

**Excluded** (standard system vars): `PATH`, `HOME`, `LANG`, `SHELL`, `USER`, `TERM`, `EDITOR`, `DISPLAY`, `PWD`, `TMPDIR`

Credential values are **never** read, stored, logged, or transmitted. Only names appear in reports as blast-radius indicators.

---

## What We Never Do

- **Never write** to any config file, lock file, or project file
- **Never execute** MCP servers or agent processes
- **Never store** credential values — only env var _names_ appear in reports
- **Never transmit** your file contents, project structure, or inventory to external services unless you explicitly enable a push, export, or integration destination; pushed payloads are sanitized before transmission
- **Never cache** any personal data to disk (scan history is opt-in via `--save`)
- **Never require** authentication tokens or API keys (NVD key is optional for rate limits only)
- **Never access** arbitrary files outside the supported discovery surfaces you choose to scan
- **Never perform** broad recursive filesystem crawls during auto-discovery
- **Never run** background processes, daemons, cron jobs, or system services

---

## External API Calls (exhaustive list)

All network calls are read-only GET/POST to public vulnerability databases.
Only package names and versions are sent. No user data is included in requests.

| Service | URL | What we send | What we receive | Auth |
|---------|-----|-------------|----------------|------|
| OSV.dev | `https://api.osv.dev/v1/querybatch` | Package names + versions | CVE IDs, advisories | None |
| NVD/NIST | `https://services.nvd.nist.gov/rest/json/cves/2.0` | CVE IDs | CVSS scores, CWE IDs | Optional (NVD_API_KEY for higher rate limits) |
| FIRST EPSS | `https://api.first.org/data/v1/epss` | CVE IDs | Exploit probability scores | None |
| CISA KEV | `https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json` | _(none — full download)_ | KEV catalog | None |
| npm registry | `https://registry.npmjs.org/{pkg}/{version}` | Package name + version | Package metadata | None |
| PyPI | `https://pypi.org/pypi/{pkg}/{version}/json` | Package name + version | Package metadata | None |
| OpenSSF Scorecard | `https://api.securityscorecards.dev/projects/github.com/{owner}/{repo}` | GitHub owner/repo | Scorecard scores | None |
| Jira (optional) | `https://{instance}.atlassian.net/rest/api/3/issue` | Finding summaries | Ticket confirmation | API token (`--jira-token`) |
| Slack (optional) | User-provided webhook URL | Finding summaries | Delivery status | Webhook URL (`--slack-webhook`) |
| Vanta (optional) | `https://api.vanta.com/v1/` | Compliance evidence | Upload confirmation | API token (`--vanta-token`) |
| Drata (optional) | `https://public-api.drata.com/` | Compliance evidence | Upload confirmation | API token (`--drata-token`) |

### Data flow

```
[Local config files]  →  extract server name, command, args, env var NAMES
                          ↓
[Package names+versions]  →  sent to OSV.dev, NVD, EPSS, KEV, npm, PyPI, OpenSSF Scorecard
                          ↓
[Findings (optional)]     →  sent to Jira, Slack, Vanta, Drata (only if flags provided)
                          ↓
[CVE results]  →  returned to local process, written to stdout or --output file
```

- **Sent to APIs**: package name + version only (e.g., `express@4.17.1`)
- **Returned from APIs**: CVE IDs, severity scores, advisory URLs
- **Never sent**: file paths, config contents, env var values, scan results, hostnames, IP addresses

All external calls can be completely disabled with `--no-scan` (inventory-only mode).

**No telemetry, analytics, or tracking.** Zero network calls unless scanning for vulnerabilities.

---

## Credential Handling

Environment variables in MCP server configs are **never read for their values**.
Only the _key names_ (e.g. `OPENAI_API_KEY`, `DATABASE_URL`) are inspected to
determine whether credentials are present. Values are always shown as `***REDACTED***`.

When you explicitly scan project files for hardcoded secrets or PII, agent-bom
must read the files inside the requested scan scope to classify the risk. The
report still does not store the matched value or a prefix of it. Secret-scan
findings retain only:

- relative file path
- line number
- finding type and severity
- redacted evidence label such as `[CREDENTIAL_REDACTED]`
- relevant actor, tenant, device, agent, resource, and attack-path metadata

agent-bom does not validate leaked credentials, call provider APIs with them,
or transmit the matched value. Validation of whether a secret is live belongs
in a separate, explicit, operator-approved workflow.

agent-bom itself optionally uses one env var:
- `NVD_API_KEY` — increases NVD rate limit from 5 to 50 requests per 30 seconds. This key is sent only to `services.nvd.nist.gov` and is never logged, cached, or transmitted elsewhere.

---

## User Control Over Scope

Users can restrict or bypass auto-discovery entirely:

| Flag | Effect |
|------|--------|
| `--dry-run` | Shows exactly which files, APIs, and data would be accessed, then exits without reading anything |
| `--inventory <file>` | Reads agents/packages from a JSON inventory file |
| `--inventory-only` / `--no-discover` | Uses only explicit input artifacts and suppresses ambient project, cwd, skill, model, dataset, and secret discovery |
| `--project <dir>` | Restricts discovery to MCP configs in a specific project directory (does not include machine-wide configs) |
| `--config-dir <dir>` | Reads MCP configs from a single custom directory only |
| `--no-skill` | Disables skill/instruction file scanning |
| `--skill-only` | Runs only skill scanning, skips all agent/package/CVE analysis |
| `--no-scan` | Inventory-only mode — discovers configs but makes no network calls |

**Recommended first run**: `agent-bom agents --dry-run` to preview the complete access plan before any actual scanning.

---

## Verifying Our Claims

This is an open-source tool — you can verify every claim above:

| Verification | How |
|---|---|
| Read source code | `src/agent_bom/` — all scanning logic is in plain Python |
| Audit network calls | `grep -rn "osv.dev\|nvd.nist\|first.org\|cisa.gov\|npmjs.org\|pypi.org" src/agent_bom/` — exhaustive list of all outbound URLs |
| Audit file access | `grep -rn "open(\|Path(" src/agent_bom/discovery/` — all file reads in the discovery module |
| Audit credential handling | `src/agent_bom/models.py` — `MCPServer.credential_names` property + `SENSITIVE_PATTERNS` in `security.py` |
| Inspect boundary contract | `agent-bom trust --format json` — code-generated data, network, storage, auth, and SCIM boundaries surfaced through CLI/API/UI |
| Verify SCIM tenant boundary | `docs/SCIM_SECURITY_MODEL.md` and `/v1/auth/policy` — tenant comes from `AGENT_BOM_SCIM_TENANT_ID`, not IdP payload fields |
| Run in isolation | `--no-scan` skips all network calls; `--dry-run` reads nothing |
| Verify signed releases | `cosign verify-blob dist/agent_bom-*.whl --bundle dist/agent_bom-*.whl.sigstore.json --certificate-oidc-issuer https://token.actions.githubusercontent.com` |
| OpenSSF Scorecard | [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/msaad00/agent-bom/badge)](https://securityscorecards.dev/viewer/?uri=github.com/msaad00/agent-bom) |

---

## Least Privilege — API Server

When running `agent-bom api`, the HTTP server adds these headers to every response:

```
X-Agent-Bom-Read-Only: true
X-Agent-Bom-No-Credential-Storage: true
```

The API server itself runs entirely in-process. It does not make third-party outbound
connections unless a scan job explicitly requests enrichment (`"enrich": true` in the
request body). Browser clients and local dashboards may still connect to the local
agent-bom API over HTTP/WebSocket for normal UI operation.

---

## Authentication Model — Zero-Credential, Zero-Trust

agent-bom **never stores, logs, or transmits credentials**. All authentication
follows the principle of least privilege with read-only scopes.

### How auth works per provider

| Provider | Preferred auth | Fallback | What we read |
|----------|---------------|----------|--------------|
| **Snowflake** | SSO via `externalbrowser` (Okta/Azure AD/Google) — default | Key-pair (`SNOWFLAKE_PRIVATE_KEY_PATH`) for CI/CD | Read-only SQL on ACCOUNT_USAGE |
| **Databricks** | SDK credential chain: `~/.databrickscfg` OAuth profile or workload identity | `DATABRICKS_TOKEN` PAT env | Read-only cluster/library listing |
| **AWS** | IAM role / OIDC workload identity (GitHub Actions) / `~/.aws/credentials` profile | `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY` env | Read-only: `Describe*`, `List*`, `Get*` |
| **Azure** | Managed identity / `az login` / service principal | `AZURE_CLIENT_SECRET` env | Read-only resource listing |
| **GCP** | Application Default Credentials / workload identity | `GOOGLE_APPLICATION_CREDENTIALS` env | Read-only resource listing |
| **HuggingFace** | `HF_TOKEN` env (read-only scope) — optional, public models need nothing | None | Model metadata via Hub API |
| **NVD** | `NVD_API_KEY` env — optional, only increases rate limits | None (public API) | CVE data only |

### Snowflake — explicit auth options

```bash
# Preferred: SSO (default — opens browser for Okta/Azure AD/Google)
agent-bom agents --snowflake

# Key-pair auth (recommended for CI/CD — no browser needed)
SNOWFLAKE_PRIVATE_KEY_PATH=~/.ssh/snowflake_key.p8 \
agent-bom agents --snowflake

# Explicit SSO
agent-bom agents --snowflake --snowflake-authenticator externalbrowser

# OAuth access token
SNOWFLAKE_AUTHENTICATOR=oauth SNOWFLAKE_TOKEN=<token> \
agent-bom agents --snowflake

# OAuth token
SNOWFLAKE_TOKEN=<token> \
agent-bom agents --snowflake --snowflake-authenticator oauth
```

### CIA Triad compliance

| Principle | How agent-bom upholds it |
|-----------|--------------------------|
| **Confidentiality** | Credentials pass env → SDK, never logged. `sanitize_error()` strips secrets from all error messages. Audit logs stored at `0600` permissions. |
| **Integrity** | SHA-256 payload hashing on every proxied MCP call. Audit logs HMAC-signed (SHA-256) for tamper detection. Set `AGENT_BOM_AUDIT_HMAC_KEY` in production to persist verifiability across restarts. Model weight hash verification (`agent-bom verify --model-dir ...` or `--verify-model-hashes` in scan workflows). SBOM + VEX support for supply chain integrity. |
| **Availability** | Graceful degradation on auth failure (warning, not crash). Offline mode (`--no-scan`). Rate limiting respected — we never exhaust API quotas. |

### What we never do

- Never write to any cloud resource (pure read-only)
- Never cache credentials to disk
- Never log credential values (sanitize_error removes them)
- Never require admin/write permissions
- Never make third-party network calls beyond the explicitly listed data sources.
  Local UI-to-API traffic is expected when using the dashboard or browser client.

---

## Reporting a Security Issue

See [SECURITY.md](../SECURITY.md) for responsible disclosure via GitHub Security Advisories.
