configwarden
Security scanner for AI agent configurations. Finds hardcoded secrets, dangerous MCP server setups and supply-chain risks before they reach production.
AI agents (Claude, Cursor, VS Code Copilot…) are increasingly wired to tools through the Model Context Protocol (MCP). One bad config line can hand an agent, or anyone who hijacks it through prompt injection, a shell on your machine or your whole home directory. configwarden catches these mistakes in seconds.
- Zero runtime dependencies: nothing extra to trust.
- Never prints a secret in full: reports are safe to share in CI logs.
- SARIF output: results show up natively in GitHub Code Scanning and other security dashboards.
Formerly named agentguard: renamed in October 2026 because that name was too close to other projects. Releases up to v0.2.2 use the old name.
Quick start
Requires Python 3.10+.
pip install configwarden==0.4.0
configwarden scan .
Try it on the bundled example
git clone https://github.com/Matadi-afk/configwarden
cd configwarden
pip install .
configwarden scan examples/vulnerable-mcp # 15 findings
configwarden scan examples/safe-mcp # the fixed version: no issues
[HIGH] CW101 MCP server runs a shell script or inline code
mcp.json:16 Server 'helper' executes commands through 'bash -c'.
Fix: Call the server program directly, with its arguments as separate items ...
[HIGH] CW106 MCP server container escapes isolation
mcp.json:26 Server 'sandbox' runs a container with a mount of '/var/run/docker.sock'.
Fix: Remove --privileged, host namespaces and mounts of '/', the home directory or the Docker socket. ...
Scanned 3 file(s), skipped 0. 15 finding(s): 0 critical, 12 high, 3 medium, 0 low.
Every value in examples/ is a fake placeholder, and the example files are named so that no AI client loads them.
Rules
| ID | Severity | What it detects |
|---|---|---|
| CW001 | critical | API keys and tokens written in clear (Anthropic, OpenAI, GitHub, AWS, Google, Hugging Face, Slack, Stripe, private keys); documentation examples (…EXAMPLE, sk-xxxx, truncated keys) are ignored |
| CW100 | medium | MCP config that cannot be parsed (some clients still run the servers they can read from a broken file), or a script too long to be checked entirely |
| CW101 | high | Shell running a script (bash -c, pwsh -Command, su -c), cmd /c or WSL with a command line or special characters, npx -c and npm exec -c (read exactly as npm reads them), pnpm -c dlx, npm_config_call, a command line with shell operators in command, inline code (node -e, python -c) |
| CW102 | medium | Registry package without an exact version (^1.0, @beta, >=1 are not pinned), whatever the launcher: npx, uvx, pnpm dlx, yarn dlx, bun x, npm exec, uv tool run, pipx run, deno run npm:; extra packages too (uvx --with, uv run --with) |
| CW103 | high | Secret written literally in an MCP server's env, headers, command line (--api-key …, -e TOKEN=…) or URL (password, ?token=…); placeholders, references and plain settings are ignored |
| CW104 | high | Filesystem server exposed to /, a whole drive or a whole home directory |
| CW105 | high | Remote MCP server reached over plain http:// |
| CW106 | high | Docker/Podman server started with --privileged, host namespaces, or mounts of /, the home directory, .ssh/.aws… or the Docker socket (options placed before the image, glued forms such as -v/:/host or --privileged=1 included) |
| CW107 | high | Package or script installed from git, a URL or a GitHub shorthand instead of the npm / PyPI registry (uv run --with git+…, deno run https://……) |
| CW108 | medium | Tools auto-approved (alwaysAllow, autoApprove, trust: true): no human confirmation |
| CW109 | high | AI client set to approve everything: Claude Code enableAllProjectMcpServers or bypassPermissions, VS Code chat.tools.global.autoApprove, Zed, Cursor *:*, Kiro Autopilot |
| CW110 | high | TLS certificate checks turned off: NODE_TLS_REJECT_UNAUTHORIZED=0, *_SSL_VERIFY=false, --insecure, --strict-ssl=false, --allow-insecure-host |
| CW111 | medium | Container image run by docker run, podman run or nerdctl run without a digest or an exact version: no tag, :latest, :main, :stable, a major version only (:20); Docker Hub official images (node) and local registries are ignored |
Commands hidden later in a shell script (bash -c "cd /srv && npx -y server") are checked too.
Run configwarden rules to list them from the CLI.
Supported configuration files
configwarden reads every .json / .jsonc file (comments and trailing commas allowed) and looks for MCP servers wherever each AI client keeps them:
| Client | Where the servers live |
|---|---|
| Claude Desktop, Cursor, Windsurf, Cline, Roo Code, Kiro, Amazon Q, GitHub Copilot CLI | mcpServers |
| Claude Code | .mcp.json, and ~/.claude.json (user and per-project servers) |
| Gemini CLI | mcpServers in .gemini/settings.json and extensions (url, httpUrl) |
| VS Code | servers in .vscode/mcp.json, mcp.servers in settings, devcontainer.json customizations |
| Zed | context_servers in settings.json |
Other JSON files are only reported when they contain MCP servers. YAML (Continue) and TOML (Codex CLI) configurations are not supported yet.
Wrapped commands are unwrapped before being checked: cmd /c npx …, wsl …, env VAR=1 … (and env -S), sudo …, uv run …, pnpm exec … and bash -c "…" are all analysed for the program they really start.
Usage
configwarden scan PATH [--format text|json|sarif] [--output FILE]
[--fail-on low|medium|high|critical] [--exclude GLOB]...
[--no-ignore | --ignore-file FILE]
Exit codes: 0 nothing at or above --fail-on, 1 findings, 2 usage error.
Accepting a finding: .configwarden-ignore
Put a .configwarden-ignore file at the root of the scanned folder, one exception per line, always with a reason:
# rule file, or folder ending with / [optional end date] -- reason (required)
CW106 examples/demo/mcp.json -- demo file, intentionally vulnerable
CW111 .vscode/mcp.json until=2026-12-31 -- vendor has no versioned image yet
- One rule per line and one file or folder: no wildcard, never the whole project.
- After
until=, the exception stops applying and the finding comes back. - A malformed line is reported (by its number, without copying its content) and ignored. So is a line containing a control or invisible character, even in a comment.
- A secret pasted in a reason by mistake is masked in every report.
- Suppressed findings no longer fail the scan, but they are always listed at the end of the text report and in the JSON report, with their reason: a repository cannot hide its own dangerous configuration without it showing. They are left out of SARIF reports, because GitHub Code Scanning ignores SARIF suppressions and would show them as open alerts.
--no-ignorereports everything.--ignore-file FILEreads exceptions from a file you trust instead (for example outside a repository you are reviewing). The project's own file is never read through a symbolic link.
GitHub Action
jobs:
configwarden:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write # only needed to upload SARIF results
steps:
- uses: actions/checkout@<commit-sha> # v7
with:
persist-credentials: false
- uses: Matadi-afk/configwarden@<commit-sha> # v0.4.0
with:
fail-on: high # low (default), medium, high or critical
sarif-file: configwarden.sarif # optional
# exclude: | # optional, one glob per line
# examples/*
- uses: github/codeql-action/upload-sarif@<commit-sha> # v4
if: always()
with:
sarif_file: configwarden.sarif
Pin every action to a full commit SHA (git ls-remote https://github.com/Matadi-afk/configwarden refs/tags/v0.4.0 prints the one for v0.4.0). The action needs no token and no permission, installs nothing, and runs configwarden in isolated mode with the Python already installed on the runner (3.10 or later; add actions/setup-python before it otherwise), so the scanned repository cannot replace its code. Results then appear in the repository's Security → Code scanning tab.
File and server names from the scanned repository cannot drive the runner either: under GitHub Actions the text report is printed with workflow commands stopped (::stop-commands:: and a random token), and ## sequences (##[…], Azure's ##vso[…]) are neutralised in every report format.
pre-commit
repos:
- repo: https://github.com/Matadi-afk/configwarden
rev: <commit-sha> # frozen: v0.4.0
hooks:
- id: configwarden
The hook runs on every commit and scans the whole repository: a dangerous MCP configuration or a secret, in any file, blocks the commit.
Development
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
pre-commit install
pytest
See CHANGELOG.md for release notes and SECURITY.md to report a vulnerability.
License
Apache-2.0
Metadata
Release files for configwarden 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| configwarden-0.4.0.tar.gz | 139.3 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| configwarden-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 224.9 kB
Release files / configwarden-0.4.0.tar.gz
| Download URL | configwarden-0.4.0.tar.gz |
|---|---|
| Size | 139.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e434a385f0b528081dfeb8966fc01bab885ffcb8fb659a2f3155879cab5b1e3f
|
|
BLAKE2b-256 checksum How to use checksums |
e11986591478de3fb15a48e314ea3053f354dc5800231fcd9f63f2be114de5fa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logRelease files / configwarden-0.4.0-py3-none-any.whl
| Download URL | configwarden-0.4.0-py3-none-any.whl |
|---|---|
| Size | 85.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5a0c3422a7e75e52467e247dddf446ec3752d12c1b6feaab6bbff321fe670059
|
|
BLAKE2b-256 checksum How to use checksums |
1e68bd0d7e6ec6a407b5e1fe5448833ae1dfbdcb1dd46f0220b3a353e954479d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logSecurity for configwarden 0.4.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for configwarden-0.4.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.