Skip to main content
Glama
README.md
# deep-code-security

Multi-language SAST tool with agentic verification and AI-powered fuzzing. Two analysis modes:
1. **Static Analysis (SAST)** - Uses Semgrep with tree-sitter fallback for deterministic AST parsing, sandbox-verified exploit PoCs, and structured remediation guidance
2. **Dynamic Analysis (Fuzzing)** - AI-powered fuzzer with coverage-guided feedback, crash deduplication, and corpus management

Exposes all functionality via an MCP server for Claude Code integration.

## Quick Start

```bash
# Install (basic)
pip install -e ".[dev]"

# Install with fuzzing support
pip install -e ".[dev,fuzz]"

# Static analysis via CLI
dcs hunt /path/to/project
dcs hunt /path/to/project --ignore-suppressions
dcs verify --finding-ids <id1> <id2>

# Dynamic analysis (fuzzing) via CLI
dcs fuzz /path/to/target.py
dcs replay /path/to/corpus
dcs corpus /path/to/corpus
dcs fuzz-plugins
dcs report /path/to/output

# Integrated SAST-to-Fuzz pipeline
dcs hunt-fuzz /path/to/project

# Enable C fuzzing (in addition to Python)
export DCS_FUZZ_ALLOWED_PLUGINS=python,c

# Build the C fuzzer sandbox (Podman required)
make build-fuzz-c-sandbox

# Fuzz a C target with SAST-to-Fuzz bridge
dcs hunt-fuzz /path/to/c_project --consent

# Run via MCP server
python -m deep_code_security.mcp
```

## Architecture

### Static Analysis (SAST)

```
Target Codebase
    |
    v
HUNTER     scanner backend (Semgrep/tree-sitter) → source/sink match → taint track
    |      Output: RawFinding[] (JSON, paginated)
    v
AUDITOR    PoC generation → sandbox execution → confidence scoring
    |      Output: VerifiedFinding[] (JSON)  [Exploit = 10% bonus only]
    v
ARCHITECT  context gather → guidance generation → dependency analysis
           Output: RemediationGuidance[] (JSON)
```

### Dynamic Analysis (Fuzzing)

```
Target Function
    |
    v
FUZZER     LLM-guided input generation → sandboxed execution →
           crash detection → corpus management → coverage tracking
           Output: CrashReport[] (JSON, with reproducer inputs)
```

## Supported Languages (v1)

- Python (Flask, Django patterns; SAST + fuzzing)
- Go (net/http, database/sql patterns; SAST only)
- C (command injection, buffer overflow, format string, memory corruption
  patterns; SAST + fuzzing). C fuzzing requires
  `DCS_FUZZ_ALLOWED_PLUGINS=python,c` and AI-powered harness generation with
  AddressSanitizer and gcov instrumentation.

### Suppression Files

You can suppress specific findings by creating a `.dcs-suppress.yaml` file
in your project root. This is useful for marking false positives, accepted
risks, or findings that will be addressed later.

**Basic usage:**

```yaml
# .dcs-suppress.yaml
version: 1
suppressions:
  - rule: CWE-22
    file: "src/config/*.py"
    reason: "Config paths are admin-controlled"

  - rule: CWE-89
    file: "src/legacy/db.py"
    line: 145
    reason: "Legacy code scheduled for refactor in Q2"
```

**CLI flags:**
- By default, suppressions are applied during `dcs hunt`, `dcs full-scan`,
  and `dcs hunt-fuzz` commands
- Use `--ignore-suppressions` to bypass the suppression file and show all
  findings

**Notes:**
- Suppression files only affect SAST findings, not fuzzer crashes
- The file must be named `.dcs-suppress.yaml` and located in the project
  root
- All formatters (text, JSON, SARIF, HTML) show suppression statistics in
  their output

## Installation

```bash
# Clone
git clone https://github.com/your-org/deep-code-security.git
cd deep-code-security

# Install with dev dependencies
pip install -e ".[dev]"

# Install with fuzzing support (requires anthropic SDK)
pip install -e ".[dev,fuzz]"

# Build sandbox images (requires Docker or Podman)
make build-sandboxes        # Build auditor/architect sandbox images
make build-fuzz-sandbox     # Build Python fuzzer sandbox image (Podman only)
make build-fuzz-c-sandbox   # Build C fuzzer sandbox image (Podman only)
```

## MCP Configuration

Add to `~/.claude/settings.json`:

```json
{
  "mcpServers": {
    "deep-code-security": {
      "command": "python",
      "args": ["-m", "deep_code_security.mcp"],
      "cwd": "/path/to/deep-code-security",
      "env": {
        "DCS_REGISTRY_PATH": "/path/to/deep-code-security/registries",
        "DCS_ALLOWED_PATHS": "/path/to/projects",
        "DCS_CONTAINER_RUNTIME": "auto",
        "ANTHROPIC_API_KEY": "your-api-key-here"
      }
    }
  }
}
```

### Optional Environment Variables

For advanced tuning, additional variables are available:

**Static Analysis**:

| Variable | Default | Description |
|----------|---------|-------------|
| `DCS_SCANNER_BACKEND` | `auto` | Scanner backend: `semgrep`, `treesitter`, or `auto` (prefer semgrep if available) |
| `DCS_SEMGREP_TIMEOUT` | `120` | Maximum seconds for Semgrep subprocess |
| `DCS_SEMGREP_RULES_PATH` | `<registry>/semgrep` | Path to DCS Semgrep rule files |
| `DCS_MAX_RESULTS` | `100` | Max findings returned per hunt operation |
| `DCS_MAX_VERIFICATIONS` | `50` | Max findings to verify in auditor phase |
| `DCS_SANDBOX_TIMEOUT` | `30` | Per-exploit timeout in seconds |
| `DCS_MAX_FILES` | `10000` | Max files per scan |
| `DCS_MAX_CONCURRENT_SANDBOXES` | `2` | Concurrency limit for sandbox execution |
| `DCS_QUERY_TIMEOUT` | `5.0` | Tree-sitter query timeout in seconds |
| `DCS_QUERY_MAX_RESULTS` | `1000` | Max results per tree-sitter query |
| `DCS_BRIDGE_MAX_TARGETS` | `10` | Max fuzz targets produced by SAST-to-Fuzz bridge |

**Dynamic Analysis (Fuzzing)**:

| Variable | Default | Description |
|----------|---------|-------------|
| `ANTHROPIC_API_KEY` | (none) | API key for Claude (required for fuzzing) |
| `GOOGLE_CLOUD_PROJECT` | (none) | GCP project ID for Vertex AI (optional) |
| `CLOUD_ML_PROJECT_NUMBER` | (none) | GCP project number for Vertex AI (optional) |
| `ANTHROPIC_VERTEX_PROJECT_ID` | (none) | Vertex AI project override (optional) |
| `DCS_FUZZ_MODEL` | `claude-sonnet-4-6` | Claude model for input generation |
| `DCS_FUZZ_MAX_ITERATIONS` | `10` | Max fuzzing iterations |
| `DCS_FUZZ_INPUTS_PER_ITER` | `10` | Inputs generated per iteration |
| `DCS_FUZZ_TIMEOUT_MS` | `5000` | Per-input execution timeout |
| `DCS_FUZZ_MAX_COST_USD` | `5.0` | API cost budget |
| `DCS_FUZZ_OUTPUT_DIR` | `./fuzzy-output` | Corpus and report output directory |
| `DCS_FUZZ_CONSENT` | `false` | Pre-configured consent for CI |
| `DCS_FUZZ_GCP_REGION` | `us-east5` | GCP region for Vertex AI |
| `DCS_FUZZ_ALLOWED_PLUGINS` | `python` | Comma-separated allowlist of fuzzer plugins (default: python; set to python,c to enable both) |
| `DCS_FUZZ_MCP_TIMEOUT` | `120` | Hard wall-clock timeout for MCP fuzz invocations |
| `DCS_FUZZ_CONTAINER_IMAGE` | `dcs-fuzz-python:latest` | Podman image used by ContainerBackend for MCP fuzz runs |
| `DCS_FUZZ_C_CONTAINER_IMAGE` | `dcs-fuzz-c:latest` | Podman image used by CContainerBackend |
| `DCS_FUZZ_C_COMPILE_FLAGS` | `""` | Comma-separated gcc flags (e.g., `-O2,-march=native`) |
| `DCS_FUZZ_C_INCLUDE_PATHS` | `""` | Comma-separated include paths for C harness compilation |

## MCP Tools

| Tool | Description |
|------|-------------|
| `deep_scan_hunt` | Run Hunter phase (AST parse + taint track) |
| `deep_scan_verify` | Run Auditor phase (sandbox exploit verification) |
| `deep_scan_remediate` | Run Architect phase (remediation guidance) |
| `deep_scan_full` | Run all three phases sequentially |
| `deep_scan_status` | Check sandbox health and registry info |
| `deep_scan_fuzz` | Run AI-powered fuzzing (requires Podman container backend) |
| `deep_scan_hunt_fuzz` | Run SAST analysis followed by AI-powered fuzzing of identified vulnerable functions (requires Podman + consent) |
| `deep_scan_fuzz_status` | Check fuzzer availability and configuration |

## Confidence Scoring

The confidence score (0-100) uses a weighted composite:

| Factor | Weight | Notes |
|--------|--------|-------|
| Taint path completeness | 45% | Full path = 100, partial = 50, heuristic = 20 |
| Sanitizer absence | 25% | No sanitizer = 100, full sanitizer = 0 |
| CWE severity baseline | 20% | Critical = 100, High = 75, Medium = 50, Low = 25 |
| Exploit verification | 10% | **Bonus only** — failed PoC does not penalize |

Thresholds: `>=75` confirmed, `>=45` likely, `>=20` unconfirmed, `<20` false positive

## Sandbox Security Policy

Sandbox containers enforce:
- `--network=none` — no network access
- `--read-only` — read-only root filesystem
- `--tmpfs /tmp:rw,noexec,nosuid,size=64m` — writable temp with noexec
- `--cap-drop=ALL` — no Linux capabilities
- `--security-opt=no-new-privileges` — no privilege escalation
- `--security-opt seccomp=seccomp-default.json` — custom seccomp profile
- `--pids-limit=64` — no fork bombs
- `--memory=512m` — memory ceiling
- `--user=65534:65534` — run as nobody

## Development

```bash
make lint           # Lint with ruff
make test           # All tests (90%+ coverage required)
make test-hunter    # Hunter tests only
make test-auditor   # Auditor tests only
make test-architect # Architect tests only
make test-mcp       # MCP server tests only
make test-fuzzer    # Fuzzer tests only
make test-c-fuzzer  # C fuzzer plugin tests only
make sast           # Security scan with bandit
make security       # sast + pip-audit
```

## Registry Format

See `registries/README.md` for the YAML registry format documentation.

## Known Limitations (v1)

1. **Intraprocedural taint only** — source and sink must be in the same function body. Expected detection rate: **10-25%** of real-world injection vulnerabilities. Most web app vulnerabilities span multiple function call boundaries and will NOT be detected by v1.

2. **Query brittleness (tree-sitter backend only)** — tree-sitter queries match specific AST shapes. When using Semgrep backend, aliased imports, fully-qualified names, and class attributes are handled correctly. The following patterns are NOT matched when using tree-sitter:
   - Aliased imports: `req = request; req.form`
   - Fully-qualified names: `flask.request.form`
   - Class attributes: `self.request.form`
   - Chained calls: `request.form.get("key")` (partial match only)

3. **PoC verification is bonus-only** — most template-based PoCs fail due to missing execution context (framework setup, dependency injection, state initialization). A failed PoC does NOT mean the vulnerability is false. The exploit bonus is capped at 10 points.

4. **No cross-language taint** — Python calling C via FFI is not analyzed. Each language is analyzed independently.

5. **No interprocedural analysis** — call graphs across functions/files are not traced in v1. Deferred to v1.1.

6. **Fuzzer requires optional dependencies** — dynamic analysis requires `pip install -e ".[fuzz]"` to install the `anthropic` SDK and related packages. The fuzzer will not be available without these dependencies.

7. **`deep_scan_fuzz` MCP tool requires Podman** — The MCP fuzzing tool uses
   ContainerBackend for full isolation and is only available when Podman is
   installed and the `dcs-fuzz-python:latest` image is built via
   `make build-fuzz-sandbox`. CLI fuzzing supports both SubprocessBackend
   (rlimits-only) and ContainerBackend.

8. **C language: no preprocessor resolution** — `#ifdef` guards and macro
   expansions are invisible to the scanner. Code hidden behind conditional
   compilation will not be analyzed.

9. **C language: no struct member taint tracking** — taint does not propagate
   through struct field assignments. Only scalar variables and direct pointer
   dereferences are tracked.

10. **C language: pointer aliasing tracked within same function only** —
    intraprocedural constraint applies (same as limitation #1). Pointer
    aliases passed to other functions are not tracked.

11. **C language: output-parameter sources deferred** — C source functions
    that deliver tainted data via output parameters (`recv`, `fread`, `read`,
    `scanf`, `getline`, `getdelim`) are not effective taint sources in v1.
    Only functions whose return value is the tainted data (`argv`, `getenv`,
    `gets`, `fgets`) work correctly with the LHS-seeding taint engine.

12. **C language: CWE-416 (use-after-free) detection deferred** — requires
    temporal ordering analysis (tracking that `free(ptr)` precedes a
    subsequent use of `ptr`), which is fundamentally different from the
    source-to-sink taint model.

13. **C language: `mktemp()`/`tmpnam()` detection gap** — registered as
    CWE-676 sinks, but the taint-flow pipeline requires a source-to-sink
    path. Most real-world uses call these with hardcoded template strings, so
    they will NOT be flagged.

14. **C fuzzer harness validation is allowlist-based, not
    sandbox-escape-proof** — The C fuzzer compiles and executes LLM-generated
    C code inside a Podman container with seccomp enforcement. Dual-layer AST
    validation prohibits dangerous syscalls and non-standard includes, but
    this is a defense-in-depth measure, not a complete security boundary. The
    primary isolation mechanism is the Podman container with `--network=none`,
    `--read-only`, `--cap-drop=ALL`, and a custom seccomp profile
    (`sandbox/seccomp-fuzz-c.json`). Prompt injection attacks that evade the
    AST allowlist could still execute arbitrary code inside the container (but
    not escape to the host).

## Security Model

The MCP server runs as a **native stdio process** on the host. It does NOT run
inside a Docker container, avoiding the Docker socket mount attack vector.
Docker or Podman is used for sandbox containers that execute exploit PoCs
(auditor/architect phases). The fuzzer's ContainerBackend uses Podman
exclusively for rootless container execution.

All file access goes through `DCS_ALLOWED_PATHS` allowlist validation with symlink resolution. All RawFinding fields are validated before exploit template interpolation. Finding provenance is verified via server-side session store (external callers cannot inject arbitrary findings).

### Fuzzer Execution Model

The Python and C fuzzers use fundamentally different execution models inside the sandbox container:

- **Python fuzzer** (`_worker.py`): uses `eval()` with restricted globals (no imports, limited builtins) and dual-layer AST validation. No arbitrary machine code is generated.
- **C fuzzer** (`_c_worker.py`): does NOT use `eval()`. Instead, AI-generated C source is compiled with `gcc` and the resulting binary is executed. This means arbitrary machine code runs inside the container, making the Podman seccomp profile (`sandbox/seccomp-fuzz-c.json`) the primary security boundary.

Both backends run under `--network=none`, `--read-only`, `--cap-drop=ALL`, `--pids-limit=64`, and `--memory=512m`.

## v1.1 Roadmap

- Interprocedural taint tracking (call graph construction)
- Java and Rust language support
- Additional registry patterns (aliased imports, fully-qualified names)
- gVisor/Firecracker sandbox option for higher-risk deployments