# Security Policy

## Reporting a Vulnerability

If you discover a security vulnerability in lean-ctx, please report it privately:

- **Email**: yves@pounce.ch
- **GitHub**: [Create a private security advisory](https://github.com/yvgude/lean-ctx/security/advisories/new)
- **Response time**: We aim to acknowledge reports within 48 hours
- **Disclosure**: We follow responsible disclosure practices (90-day embargo)

**Please do NOT:**
- Open public GitHub issues for security vulnerabilities
- Disclose vulnerabilities on social media or forums before we've had a chance to address them

---

## What lean-ctx Does (and Doesn't Do)

lean-ctx is a **local-only CLI tool and MCP server**. Understanding its scope helps assess risk:

**Does:**
- Read files from your local filesystem (explicit reads and tool-driven scans within the project boundary)
- Execute shell commands (only commands you or your AI tool explicitly invoke)
- Cache file contents in memory during a session
- Store statistics in `~/.lean-ctx/stats.json` (command counts, token savings)
- Store session state in `~/.lean-ctx/sessions/` (task context, findings)

### I/O Boundary (PathJail + Roles)

lean-ctx enforces a **project boundary** for filesystem I/O:

- **PathJail**: all tool path inputs are resolved and jailed under the current `project_root`.
  - If a path would escape, the call fails with a clear hint to explicitly allow additional roots.
- **Explicit allow roots**:
  - Env: `LEAN_CTX_ALLOW_PATH` (or `LCTX_ALLOW_PATH`) — a path list (`:` on Unix, `;` on Windows)
  - Config: `allow_paths` in `~/.lean-ctx/config.toml`
- **Symlink escape protection**: canonicalization ensures that symlinks pointing outside the jail are rejected.

In addition, roles can restrict **unsafe I/O**:

- **Secret-like deny-by-default**:
  - Search skips secret-like files (e.g. `.env`, `*.pem`, `id_rsa`, `.ssh/`, `.aws/`) unless the active role explicitly allows them.
  - Artifact registry resolution rejects secret-like artifact paths unless allowed (artifacts are indexed/shareable by design).
  - Direct reads/edits can warn or error depending on boundary mode.
- **`.gitignore` bypass is policy-gated**:
  - `ctx_search ignore_gitignore=true` requires explicit role permission (typically the `admin` role).
- **Boundary mode**:
  - Roles can set `io.boundary_mode = "warn" | "enforce"`.
  - Env override: `LEAN_CTX_IO_BOUNDARY_MODE=warn|enforce`.
- **Auditability**:
  - Boundary denials/warnings emit local `PolicyViolation` events (no secret content is returned as part of the violation).

### Threat Model (v2)

**Primary risks (local-only, but high impact):**
- **Accidental secret exfiltration to LLMs** via `ctx_read`, `ctx_search`, compressed `ctx_shell`, archives, or exported artifacts.
- **Boundary escapes** via absolute paths, symlinks, linked projects, or artifact path tricks.
- **Amplification / token burn** by scanning large files or returning unbounded outputs.
- **ReDoS** via user-supplied regex patterns in `ctx_search`.
- **Cross-workspace data access** in team server deployments (IDOR).

**Core mitigations:**
- **PathJail** + explicit allow roots (`LEAN_CTX_ALLOW_PATH` / `allow_paths`).
- **Role-gated unsafe I/O** (`ignore_gitignore`, secret-like allow).
- **Secret path check on all MCP read paths** — `.env`, SSH keys, etc. blocked by default.
- **Shell CWD jail enforcement** — explicit `cwd` parameters are jail-checked, `cd` targets validated.
- **Deterministic redaction** on tool outputs (non-admin roles, and for persisted archives).
- **Hard caps** on reads and outputs to limit DoS/token burn.
- **Regex guards** — pattern length (1024 chars) and DFA size (1 MiB) limits on `ctx_search`.
- **MCP message size limit** — 32 MiB cap on JSON-RPC message size.
- **Constant-time token comparison** in all auth paths (dashboard, HTTP server, team server).
- **Team server tenant isolation** — workspace enforced from authenticated token, not query parameters.
- **JSON-RPC batch rejection** — batch requests rejected on team server to prevent scope bypass.
- **Event payload redaction** — REST API responses redacted to `Summary` level by default.
- **Pipeline archive redaction** — shell output archives redacted before storage.
- **UDS socket permissions** — `0o600` enforced on Unix domain sockets after bind.
- **Error response sanitization** — internal details logged server-side, generic codes returned to clients.

**Optional network activity (fully disableable):**
- **Update check**: a lightweight daily GET to `leanctx.com/version.txt` to notify you of new versions. Sends only the current version as User-Agent. Disable with `update_check_disabled = true` in `~/.lean-ctx/config.toml` or `LEAN_CTX_NO_UPDATE_CHECK=1`.
- **Anonymous stats sharing** (opt-in, off by default): if you enable `contribute_enabled` in setup, anonymized compression statistics (token counts, compression ratios — no file names, no code, no PII) are periodically sent to `api.leanctx.com`.

**Does NOT:**
- Collect tracking analytics, fingerprints, or PII
- Access files outside of requested paths
- Store or transmit credentials, API keys, or secrets
- Require elevated privileges (runs as your user)

---

## Automated Security Checks

Every push and pull request triggers our CI security pipeline:

1. **`cargo audit`** — Scans dependencies for known CVEs
2. **`cargo clippy`** — Enforces Rust safety lints (warnings = errors)
3. **Dangerous pattern scan** — Detects potentially unsafe code patterns:
   - Shell injection vectors (`Command::new("sh")` with user input)
   - Network operations (`reqwest::`, `std::net::`, `hyper::`)
   - Unsafe code blocks (`unsafe {`)
   - Environment manipulation (`.env("LD_PRELOAD")`)
   - Hardcoded secrets or obfuscated strings
4. **`cargo test`** — Full test suite must pass

---

## Critical Files Requiring Enhanced Review

Changes to these files receive extra scrutiny:

| File | Risk | Why |
|------|------|-----|
| `rust/src/shell/` | Shell execution | Wraps your shell, executes commands |
| `rust/src/server/` | MCP protocol | Handles all tool calls from AI editors/agents |
| `rust/src/hooks/` | Editor integration | Installs hooks/config into Claude Code, Cursor, etc. |
| `rust/src/core/cache.rs` | File caching | Reads and stores file contents |
| `rust/Cargo.toml` | Supply chain | Dependency manifest |
| `.github/workflows/*.yml` | CI/CD | Release pipeline integrity |

---

## Dependency Security

All dependencies in `Cargo.toml` meet these criteria:

- **Established crates**: All 29 dependencies are well-known, widely-used Rust crates
- **License**: MIT or Apache-2.0 compatible
- **Active maintenance**: Recent commits within 6 months
- **Minimal network**: `ureq` (lightweight HTTP client) used only for version check and opt-in cloud sync

Key dependencies and their purpose:

| Crate | Purpose | Downloads |
|-------|---------|-----------|
| `rmcp` | MCP protocol (stdio transport only) | Rust MCP reference impl |
| `tiktoken-rs` | Token counting (o200k_base) | OpenAI's tokenizer |
| `tree-sitter` + grammars | AST parsing for 18 languages | Mozilla's parser |
| `tokio` | Async runtime (for MCP server) | 200M+ downloads |
| `serde` / `serde_json` | JSON serialization | 400M+ downloads |
| `similar` | Myers diff algorithm | Well-established |
| `walkdir` | Directory traversal | 100M+ downloads |

---

## VirusTotal False Positives

Rust binaries are frequently flagged by ML-based antivirus engines (particularly Microsoft Defender's `Wacatac.B!ml` classifier). This is a **known issue** affecting many Rust projects:

- [Rust lang discussion on false positives](https://users.rust-lang.org/t/rust-programs-flagged-as-malware/49799)
- 1/72 engines flagging = definitively a false positive
- The `!ml` suffix in `Wacatac.B!ml` means "Machine Learning detection" (heuristic, not signature-based)

**Why it happens:**
- Statically linked binaries (~30 MB) are unusual for Windows
- `strip = true` + `lto = true` optimizations alter binary structure
- New/unsigned executables trigger ML classifiers trained on known-good signed software

**How to verify lean-ctx yourself:**
1. Build from source: `cargo install lean-ctx` (compiles on your machine)
2. Compare SHA256 checksums against our [GitHub Releases](https://github.com/yvgude/lean-ctx/releases)
3. Audit the source code: the entire codebase is open source

---

## Build Reproducibility

To verify that a release binary matches the source code:

```bash
# Clone and build
git clone https://github.com/yvgude/lean-ctx.git
cd lean-ctx/rust
cargo build --release

# Compare with installed version
lean-ctx --version
./target/release/lean-ctx --version
```

SHA256 checksums for all release binaries are published in each [GitHub Release](https://github.com/yvgude/lean-ctx/releases).

---

## Disclosure Timeline

When vulnerabilities are reported:

1. **Day 0**: Acknowledgment sent to reporter
2. **Day 7**: Severity assessment completed
3. **Day 14**: Patch development begins
4. **Day 30**: Patch released + CVE filed (if applicable)
5. **Day 90**: Public disclosure

Critical vulnerabilities (RCE, data exfiltration) are fast-tracked.

---

## Known Residual Risks

### TOCTOU (Time-of-Check to Time-of-Use)

**Status:** Accepted residual risk.

A race condition exists between `jail_path` validation and the subsequent file read. The filesystem could change between the check and the operation (e.g., a symlink replaced after validation). Complete mitigation would require `openat`/`O_NOFOLLOW` at the syscall level, which is impractical across all supported platforms.

**Recommendation for regulated environments:** Run lean-ctx inside a container or VM where the filesystem is controlled and no untrusted processes can modify symlinks concurrently.

### ctx_execute Sandbox Naming

**Status:** Documented limitation.

The `ctx_execute` tool provides **timeout enforcement** and **output capping** but does **not** provide OS-level sandboxing (no containers, namespaces, or seccomp filters). The term "sandbox" in tool descriptions refers to the execution boundary, not kernel-level isolation.

**Recommendation for regulated environments:** Disable `ctx_execute` via role configuration (`denied: ["ctx_execute"]`) or run lean-ctx in a pre-existing container sandbox.

### Shell Command Validation (REQ-57177)

**Status:** Accepted risk — by design.

`ctx_shell` executes commands requested by the user or their AI agent. lean-ctx does **not** validate or sanitize shell command content itself — that responsibility lies with the agent's permission model (e.g., Claude Code's allowlists, Cursor's approval dialogs). lean-ctx enforces CWD jail boundaries and output capping but does not attempt to parse or restrict shell syntax.

**Rationale:** Any shell validation can be bypassed (encoding, quoting, aliases). The correct security boundary is the AI agent's permission model, not a regex-based shell filter.

**Mitigation:** Use role-based `denied: ["ctx_shell"]` to disable shell access entirely in sensitive environments.

### PathJail TOCTOU Race (REQ-57178)

**Status:** Accepted residual risk.

A race condition exists between `jail_path` validation and the subsequent file operation. The filesystem could change between the check and the operation (e.g., a symlink replaced after validation). Complete mitigation would require `openat`/`O_NOFOLLOW` at the syscall level, which is impractical across all supported platforms.

**Mitigation:** Symlink-following canonicalization before access. For regulated environments: run lean-ctx inside a container where no untrusted processes can modify symlinks concurrently.

### Cloud Server Database TLS (REQ-57188)

**Status:** Accepted risk — localhost-only by default.

The cloud server's PostgreSQL connection does not enforce TLS by default. This is acceptable because the cloud server is designed for localhost/loopback deployment where DB traffic does not traverse a network.

**Mitigation for production:** Set `DATABASE_URL` with `?sslmode=require` or use a connection string that enforces TLS. When deployed behind a reverse proxy (nginx/Caddy), ensure TLS terminates before the DB.

### HuggingFace Model Downloads

**Status:** Documented risk.

Embedding models for semantic search are downloaded from HuggingFace Hub. Verification is size-based heuristic only, not cryptographic (no SHA256 pinning for model files).

**Recommendation for regulated environments:** Pre-provision models manually from an internal mirror with signature verification. Set `LEAN_CTX_EMBEDDING_MODEL_DIR` to point to the pre-provisioned directory to skip downloads entirely.

---

## Security Architecture for Enterprise Deployments

### Recommended Configuration (Bank / Regulated)

```toml
# ~/.lean-ctx/config.toml

# Disable all outbound network
update_check_disabled = true
contribute_enabled = false

# Enforce strict I/O boundary
[io]
boundary_mode = "enforce"
allow_secret_paths = false

# Use restrictive role
[roles.bank]
denied = ["ctx_execute"]
io.boundary_mode = "enforce"
io.allow_secret_paths = false
io.allow_ignore_gitignore = false
```

### Network Surface

| Endpoint | Purpose | Disable |
|----------|---------|---------|
| `leanctx.com/version.txt` | Update check (daily GET) | `update_check_disabled = true` |
| `api.leanctx.com` | Opt-in anonymous stats | `contribute_enabled = false` (default) |
| `huggingface.co` | Embedding model download | Pre-provision models, set `LEAN_CTX_EMBEDDING_MODEL_DIR` |
| `localhost:PORT` | Dashboard (local TCP) | Don't start dashboard, or bind to loopback only |
| UDS socket | Daemon IPC | Permissions `0o600`, owner-only access |

### Team Server Hardening

When running the team server (`lean-ctx team-server`):

1. **Token rotation**: Rotate workspace tokens periodically. Tokens are stored in the team config.
2. **Scope minimization**: Grant only necessary scopes per workspace token (e.g., `read` only, no `shell`).
3. **Network isolation**: Bind the team server to an internal network interface, not `0.0.0.0`.
4. **Audit log monitoring**: Team server writes audit logs for all tool calls. Monitor for denied requests.
5. **JSON-RPC batch requests**: Rejected by default to prevent scope bypass.

### Supply Chain

- **`cargo audit`** runs on every CI push (zero known CVEs tolerated).
- **`cargo deny`** checks licenses and advisories.
- **npm `postinstall.js`** verifies SHA256 of downloaded binaries against `SHA256SUMS` release asset.
- **GitHub Actions** uses pinned action versions with hash verification.

---

## Contact

- **Security issues**: yves@pounce.ch
- **General questions**: [GitHub Discussions](https://github.com/yvgude/lean-ctx/discussions)
- **Discord**: [Join our server](https://discord.gg/pTHkG9Hew9)

---

**Last updated**: 2026-05-11
