Skip to main content

Production-readiness and security audit CLI for LangGraph / LangChain projects — brew doctor for your agent stack.

Project description

langdoctor 🩺

Scan your LangGraph/LangChain project for known CVEs, insecure configs, and production footguns — in seconds, offline, no API key.

brew doctor for your agent stack.

PyPI Python CI License: MIT

langdoctor scanning a vulnerable project

Quickstart

pipx run langdoctor        # or: uvx langdoctor

Run it in your project directory. langdoctor reads your dependency files and source, reports issues grouped by severity, and exits non-zero when it finds something at or above your threshold.

langdoctor                 # scan the current directory
langdoctor path/to/project # scan a specific directory
langdoctor list-checks     # every check, with IDs and severities
langdoctor --version       # tool version + advisory DB date

Why

2026 has been a rough year for the agent stack:

  • Langflow is under active attack. CVE-2025-3248 (unauthenticated RCE) was added to the CISA KEV catalog and exploited to deploy the Flodrix botnet; CVE-2026-5027 (path-traversal → RCE) is being exploited across ~7,000 exposed instances.
  • LangGraph checkpointer CVEs — SQL injection and unsafe deserialization in the SQLite/base checkpointers can chain to RCE via forged checkpoint rows.
  • Supply-chain attacks on the CI actions and packages the ecosystem depends on (the tj-actions and LiteLLM incidents) mean a pinned-to-a-tag action or an unpinned dependency is a real risk surface.

These are exactly the things a framework-specific linter can catch before you ship. langdoctor ships the CVE data with the package and runs fully offline.

What it checks

ID Category Severity What it catches
LD101 Known CVEs high SQL injection via metadata filter key in SQLite checkpointer
LD102 Known CVEs medium Unsafe msgpack deserialization in checkpoint loading
LD103 Known CVEs medium Unsafe JSON deserialization in checkpoint loading
LD104 Known CVEs high Path traversal in legacy load_prompt functions
LD105 Known CVEs critical Serialization-injection secret extraction in dumps/loads
LD106 Known CVEs critical 🔴KEV Unauthenticated RCE in /api/v1/validate/code (Langflow)
LD107 Known CVEs medium BaseCache deserialization of untrusted data may lead to RCE
LD108 Known CVEs high RCE in "json" mode of JsonPlusSerializer
LD109 Known CVEs high SQL injection in the SQLite store implementation
LD110 Known CVEs high SQL injection via filter key in SqliteStore
LD111 Known CVEs high 🔴KEV Path traversal → RCE via /api/v2/files upload (Langflow)
LD150 Known CVEs high Langflow older than the current secure baseline (1.10.1)
LD201 Checkpointer & state high MemorySaver used in a production-bound project
LD202 Checkpointer & state medium SqliteSaver may collapse under write concurrency
LD203 Checkpointer & state high (heuristic) Checkpoint history filtered by user-controlled input
LD204 Checkpointer & state medium Compiled graph with interrupts has no checkpointer
LD301 Graph & runtime config medium No recursion_limit configured for a LangGraph run
LD302 Graph & runtime config low (heuristic) LLM client created without a timeout
LD303 Graph & runtime config info Deprecated pre-1.0 LangChain import
LD304 Graph & runtime config high Legacy load_prompt() usage
LD401 Secrets & exposure critical Hardcoded API key in source
LD402 Secrets & exposure high .env file present but not gitignored
LD403 Secrets & exposure critical Langflow auto-login not explicitly disabled
LD501 Hygiene medium Dependencies are not pinned
LD502 Hygiene medium GitHub Actions uses an unpinned third-party action

Severities for CVE checks are derived from the CVSS score; 🔴KEV marks Known-Exploited vulnerabilities, which are always surfaced first.

CI integration

GitHub Action

# .github/workflows/langdoctor.yml
name: langdoctor
on: [push, pull_request]
permissions:
  contents: read
  security-events: write   # to upload SARIF
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: elaz48/langdoctor@v1
        with:
          fail-on: high
      - uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: langdoctor.sarif

Findings show up in your repo's Security → Code scanning tab.

pre-commit

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/elaz48/langdoctor
    rev: v0.1.0
    hooks:
      - id: langdoctor

GitLab CI

langdoctor:
  image: python:3.12-slim
  script:
    - pip install langdoctor
    - langdoctor --format sarif > gl-langdoctor.sarif
  artifacts:
    reports:
      sast: gl-langdoctor.sarif

Configuration

Configure defaults in pyproject.toml; command-line flags take precedence.

[tool.langdoctor]
fail-on = "high"            # critical | high | medium | low | never
ignore  = ["LD203", "LD302"]
exclude = ["examples", "vendor"]

Suppress a single finding inline — by LD id, CVE, or any alias:

API_KEY = get_key()  # langdoctor: ignore=LD401
langgraph==1.0.5  # langdoctor: ignore=LD102

Suppressed findings are always reported as a suppressed: N count so nothing disappears silently.

Output formats

--format console (default) · json · sarif · markdown

  • sarif — GitHub code scanning; security-severity carries the real CVSS.
  • markdown — paste straight into a PR or issue.
  • json — machine-readable, with a per-severity summary.

Exit codes: 0 clean (or below --fail-on), 1 findings at/above threshold, 2 scan error. Heuristic checks never affect the exit code unless --strict.

What langdoctor is NOT

  • Not a runtime guard. It scans code and dependencies; it does not sit in your request path.
  • Not an LLM firewall. It does no prompt-injection or content analysis.
  • Not a replacement for Semgrep/Snyk. It's the framework-specific layer those miss — LangGraph/LangChain/Langflow footguns, by ID, with fixes.

It is deterministic (no AI calls), offline (CVE data ships in the package), and zero-config.

Development

pip install -e ".[dev]"
pytest
ruff check .

Requires Python 3.10+. See CLAUDE.md for design principles, the check-ID conventions, and the "advisories are data" model.

License

MIT © 2026 elaz48

Topics: langgraph · langchain · langflow · security · sast · ai-agents · llm-security · devsecops · cli

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

langdoctor-0.1.0.tar.gz (542.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

langdoctor-0.1.0-py3-none-any.whl (35.5 kB view details)

Uploaded Python 3

File details

Details for the file langdoctor-0.1.0.tar.gz.

File metadata

  • Download URL: langdoctor-0.1.0.tar.gz
  • Upload date:
  • Size: 542.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for langdoctor-0.1.0.tar.gz
Algorithm Hash digest
SHA256 43f688cd8c51f793c64e27a6e0bddfd6331cf3f51da4098a26e04c6d2d77bf81
MD5 1d481bd0b3e815d4115b761ee8a32b3c
BLAKE2b-256 740dcf2f0ca9fa694791b745e5ba43fdcaeeea962d36c44d32d8b6df75563bc7

See more details on using hashes here.

Provenance

The following attestation bundles were made for langdoctor-0.1.0.tar.gz:

Publisher: release.yml on elaz48/langdoctor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file langdoctor-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: langdoctor-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 35.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for langdoctor-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 92c9517d9e1d8406b6323a73d0d0d2b5b10a4833a67106b3b994c327f76753d1
MD5 1a93af789d944f3b62f4cd44f1eb09d5
BLAKE2b-256 ef278d6602e5d71ce07bbe66232ed0f76f251ba95323b986094ecce6ab72680f

See more details on using hashes here.

Provenance

The following attestation bundles were made for langdoctor-0.1.0-py3-none-any.whl:

Publisher: release.yml on elaz48/langdoctor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page