---
title: "Code Security Scanning"
description: "Automatic SAST, secret detection, and dependency scanning on agent code changes"
---

# Code Security Scanning

Caged automatically scans agent code changes for security issues before they reach your repository. Every `git commit` and `git push` can trigger:

- **SAST scanning** — Static analysis for SQL injection, XSS, insecure crypto
- **Secret detection** — Find leaked API keys, passwords, tokens, private keys
- **Dependency scanning** — Identify vulnerable packages in manifests

## How It Works

When an agent runs `git commit` or `git push` inside a sandbox:

1. Caged intercepts the git command via MCP tool hooks
2. Scans the changed files using configured scanners
3. Compares findings against your policy's severity threshold
4. Blocks the operation if critical/high issues are found
5. Records the scan report for observability

```mermaid
flowchart LR
    A[Agent: git commit] --> B[Scan Hook]
    B --> C[SAST + Secrets + Deps]
    C --> D{Threshold<br/>exceeded?}
    D -->|No| E[✓ Commit allowed]
    D -->|Yes| F[✗ Commit blocked]
```

## Enabling Scans

Output scanning is controlled by [policies](/guides/policies). Add an `output_scan` scope to your sandbox policy:

```yaml
# In your policy.yaml or via API
rules:
  - scope: output_scan
    action: deny
    config:
      severity_threshold: high  # Block if findings >= high
      scan_types:
        - sast
        - secret
        - dependency
    message: "Security scan failed. Fix issues before committing."
```

### Severity Levels

| Level | Examples |
|-------|----------|
| `critical` | RCE, SQL injection, leaked private keys |
| `high` | XSS, hardcoded secrets, critical CVEs |
| `medium` | Insecure defaults, medium CVEs |
| `low` | Deprecated functions, low CVEs |
| `info` | Style issues, informational findings |

### Scan Types

| Type | Scanner | What It Finds |
|------|---------|---------------|
| `sast` | Semgrep | Code patterns: injection, XSS, crypto issues |
| `secret` | Gitleaks | API keys, passwords, tokens, private keys |
| `dependency` | Trivy | Vulnerable packages in package.json, go.mod, requirements.txt |

## CLI Commands

### Run a scan manually

```bash
caged scan run sbx_abc123

# Scan specific types
caged scan run sbx_abc123 --types=sast,secret

# Scan only changed files (diff against HEAD~1)
caged scan run sbx_abc123 --diff=HEAD~1

# JSON output
caged scan run sbx_abc123 --json
```

### List scan reports

```bash
caged scan list --sandbox=sbx_abc123
caged scan list --session=sess_xyz
```

### Get scan details

```bash
caged scan get <report-id>
caged scan get <report-id> --json
```

## API Reference

### Trigger a Scan

```bash
POST /v1/scans
```

```json
{
  "sandbox_id": "sbx_abc123",
  "session_id": "sess_xyz",       // optional
  "work_dir": "/workspace",
  "diff_ref": "HEAD~1",           // optional, scan only changed files
  "scan_types": ["sast", "secret"] // optional, defaults to all
}
```

### Response

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "sandbox_id": "sbx_abc123",
  "session_id": "sess_xyz",
  "blocked": false,
  "total_count": 3,
  "by_severity": {
    "medium": 2,
    "low": 1
  },
  "by_type": {
    "sast": 2,
    "secret": 1
  },
  "results": [
    {
      "scanner_name": "semgrep",
      "scan_type": "sast",
      "findings": [
        {
          "title": "Possible SQL injection",
          "severity": "medium",
          "file_path": "api/users.go",
          "start_line": 45,
          "end_line": 45,
          "code_snippet": "query := fmt.Sprintf(\"SELECT * FROM users WHERE id = %s\", id)",
          "suggestion": "Use parameterized queries instead of string interpolation"
        }
      ]
    }
  ]
}
```

### List Scan Reports

```bash
GET /v1/scans?sandbox_id=sbx_abc123&limit=20
GET /v1/scans?session_id=sess_xyz&limit=20
```

### Get Scan Report

```bash
GET /v1/scans/:id
```

## Skipping Scans

Agents can skip scans for specific commits using the `skip_scan` argument:

```json
{
  "name": "git_commit",
  "arguments": {
    "message": "WIP: checkpoint",
    "skip_scan": true
  }
}
```

<Warning>
Skipping scans should be rare. All skipped commits are logged and visible in session replay.
</Warning>

## Events

Each scan emits an event to NATS JetStream for observability:

```json
{
  "type": "output_scan",
  "sandbox_id": "sbx_abc123",
  "session_id": "sess_xyz",
  "scan_id": "uuid",
  "total_findings": 5,
  "by_severity": {"high": 2, "medium": 3},
  "blocked": true,
  "block_reason": "2 high-severity findings exceed threshold"
}
```

## Dashboard

Scan findings are visible in:

1. **Session timeline** — Scan events appear inline with file changes
2. **File diff view** — Findings annotated on affected lines
3. **Sandbox overview** — Summary of recent scans with block status

## Best Practices

<CardGroup cols={2}>
  <Card title="Start with warnings" icon="triangle-exclamation">
    Set `action: warn` initially to see what gets flagged without blocking agents
  </Card>
  <Card title="Tune thresholds" icon="sliders">
    Lower threshold to `medium` for production codebases, keep `high` for prototypes
  </Card>
  <Card title="Custom rules" icon="code">
    Add Semgrep rules for your specific patterns (internal API usage, deprecated methods)
  </Card>
  <Card title="Track trends" icon="chart-line">
    Monitor scan events over time to catch agents that consistently produce risky code
  </Card>
</CardGroup>
