If you discover a security vulnerability in lean-ctx, please report it privately:
- Email: yves@pounce.ch
- GitHub: Create a private security advisory
- 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
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)
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(orLCTX_ALLOW_PATH) — a path list (:on Unix,;on Windows) - Config:
allow_pathsin~/.lean-ctx/config.toml
- Env:
- 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.
- Search skips secret-like files (e.g.
.gitignorebypass is policy-gated:ctx_search ignore_gitignore=truerequires explicit role permission (typically theadminrole).
- Boundary mode:
- Roles can set
io.boundary_mode = "warn" | "enforce". - Env override:
LEAN_CTX_IO_BOUNDARY_MODE=warn|enforce.
- Roles can set
- Auditability:
- Boundary denials/warnings emit local
PolicyViolationevents (no secret content is returned as part of the violation).
- Boundary denials/warnings emit local
Primary risks (local-only, but high impact):
- Accidental secret exfiltration to LLMs via
ctx_read,ctx_search, compressedctx_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
cwdparameters are jail-checked,cdtargets 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
Summarylevel by default. - Pipeline archive redaction — shell output archives redacted before storage.
- UDS socket permissions —
0o600enforced 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.txtto notify you of new versions. Sends only the current version as User-Agent. Disable withupdate_check_disabled = truein~/.lean-ctx/config.tomlorLEAN_CTX_NO_UPDATE_CHECK=1. - Anonymous stats sharing (opt-in, off by default): if you enable
contribute_enabledin setup, anonymized compression statistics (token counts, compression ratios — no file names, no code, no PII) are periodically sent toapi.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)
Every push and pull request triggers our CI security pipeline:
cargo audit— Scans dependencies for known CVEscargo clippy— Enforces Rust safety lints (warnings = errors)- 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
- Shell injection vectors (
cargo test— Full test suite must pass
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 |
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 |
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
- 1/72 engines flagging = definitively a false positive
- The
!mlsuffix inWacatac.B!mlmeans "Machine Learning detection" (heuristic, not signature-based)
Why it happens:
- Statically linked binaries (~30 MB) are unusual for Windows
strip = true+lto = trueoptimizations alter binary structure- New/unsigned executables trigger ML classifiers trained on known-good signed software
How to verify lean-ctx yourself:
- Build from source:
cargo install lean-ctx(compiles on your machine) - Compare SHA256 checksums against our GitHub Releases
- Audit the source code: the entire codebase is open source
To verify that a release binary matches the source code:
# 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 --versionSHA256 checksums for all release binaries are published in each GitHub Release.
When vulnerabilities are reported:
- Day 0: Acknowledgment sent to reporter
- Day 7: Severity assessment completed
- Day 14: Patch development begins
- Day 30: Patch released + CVE filed (if applicable)
- Day 90: Public disclosure
Critical vulnerabilities (RCE, data exfiltration) are fast-tracked.
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.
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.
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.
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.
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.
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.
# ~/.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| 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 |
When running the team server (lean-ctx team-server):
- Token rotation: Rotate workspace tokens periodically. Tokens are stored in the team config.
- Scope minimization: Grant only necessary scopes per workspace token (e.g.,
readonly, noshell). - Network isolation: Bind the team server to an internal network interface, not
0.0.0.0. - Audit log monitoring: Team server writes audit logs for all tool calls. Monitor for denied requests.
- JSON-RPC batch requests: Rejected by default to prevent scope bypass.
cargo auditruns on every CI push (zero known CVEs tolerated).cargo denychecks licenses and advisories.- npm
postinstall.jsverifies SHA256 of downloaded binaries againstSHA256SUMSrelease asset. - GitHub Actions uses pinned action versions with hash verification.
- Security issues: yves@pounce.ch
- General questions: GitHub Discussions
- Discord: Join our server
Last updated: 2026-05-11