LLMSafe
LLMSafe is an open-source static security scanner for AI-powered and agentic Python applications. It traces user input and model-controlled data into dangerous capabilities such as code execution, shells, SQL, outbound requests, and dynamic tool dispatch.
It runs locally. Source code is not uploaded to a model or external analysis service.
Status:
v0.2.1is an early release. LLMSafe provides reviewable security signals, not a guarantee that an AI system is secure.
Why another security scanner?
Traditional Python scanners are good at finding dangerous APIs. Agentic applications add a different question: can untrusted user or model output reach that capability?
flowchart LR
A["User input"] --> C["Assignments and transforms"]
B["Model output"] --> C
C --> D["Shell / eval / SQL / HTTP / tool dispatch"]
D --> E["Finding with source-to-sink evidence"]
LLMSafe combines focused API checks with AST-based dataflow and agent-framework rules:
def run_agent(client, user_input):
response = client.responses.create(input=user_input)
generated_code = response.output_text
return eval(generated_code)
The scanner reports both the dangerous eval() and the path from the model response to that
sink:
agent.py:4:12: CRITICAL FLOW001 Untrusted data reaches code execution
Untrusted or model-controlled data flows into eval(). Source: model, user.
Trace 2:16: model source: client.responses.create
Trace 1:23: user source: user_input
Trace 4:12: reaches eval
Fix: Replace dynamic execution with a typed parser and an allow-listed operation.
Detection coverage
| Family | Rule IDs | Examples |
|---|---|---|
| Dataflow | FLOW001–FLOW005 |
Model/user data reaching code, shell, SQL, URL, or tool dispatch |
| Agent tools | AGENT001–AGENT003 |
Python/shell tools, dangerous capability flags, disabled approval |
| Secrets | SECRET001–SECRET005 |
Provider keys, tokens, private keys, hard-coded credentials |
| Python | PY001–PY004 |
eval, exec, unsafe pickle and YAML deserialization |
| Shell | SHELL001–SHELL002 |
os.system and subprocess(..., shell=True) |
| Prompt trust | LLM001 |
Dynamic data interpolated into system/developer instructions |
| MCP | MCP001–MCP003 |
Shell launch, remote HTTP, wildcard tool permissions |
See the complete rule catalog and threat model.
Integrations can query the same catalog without parsing documentation:
llmsafe --list-rules
llmsafe --list-rules --format json
The versioned JSON output includes every stable ID, severity, family, description, and remediation.
Install
LLMSafe supports Python 3.9 and newer.
python3 -m venv .venv
source .venv/bin/activate
python -m pip install llmsafe
For development:
git clone https://github.com/rezerpaul-crypto/llmsafe.git
cd llmsafe
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -e ".[dev]"
Use the CLI
Scan the current repository:
llmsafe .
Scan selected paths and fail on medium-or-higher findings:
llmsafe src agent.py --fail-on medium --exclude "generated/**"
Generate machine-readable reports:
llmsafe . --format json --output reports/llmsafe.json
llmsafe . --format sarif --output reports/llmsafe.sarif
Exit codes are stable for automation:
| Code | Meaning |
|---|---|
0 |
No finding at or above the selected threshold |
1 |
At least one finding reached the selected threshold |
2 |
Invalid configuration, missing target, or scan error |
Repository policy
Commit a .llmsafe.toml file:
[llmsafe]
exclude = ["generated/**", "vendor/**"]
fail_on = "high"
max_file_size = 1000000
disabled_rules = ["PY004"]
CLI options override or extend repository policy. Policy can also live under [tool.llmsafe] in
pyproject.toml. See configuration.
Adopt LLMSafe without ignoring new risk
Existing repositories can review and commit a baseline of current findings:
llmsafe . --write-baseline .llmsafe-baseline.json
llmsafe . --baseline .llmsafe-baseline.json
The second command reports and fails only on findings not represented in the baseline. Matching is line-independent, duplicate-aware, and deterministic so ordinary code movement does not create noise while an additional dangerous operation is still reported. Baselines are review artifacts, not permanent suppressions; see incremental adoption.
Suppress one reviewed finding
Place a narrow suppression on the finding line or immediately above it:
# llmsafe: ignore[PY001] -- expression is generated from a fixed internal grammar
result = eval(TRUSTED_EXPRESSION)
Prefer a rule-specific suppression over a bare llmsafe: ignore.
GitHub Code Scanning
The repository includes a reusable composite action. A consumer workflow can scan, upload SARIF, then enforce the configured threshold:
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: "3.12"
- id: llmsafe
continue-on-error: true
uses: rezerpaul-crypto/llmsafe@v0.2.1
with:
path: .
fail-on: high
- if: always()
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: ${{ steps.llmsafe.outputs.sarif-file }}
- if: steps.llmsafe.outcome == 'failure'
run: exit 1
The workflow uses only contents: read and security-events: write.
Pre-commit
repos:
- repo: https://github.com/rezerpaul-crypto/llmsafe
rev: v0.2.1
hooks:
- id: llmsafe
Benchmark
The checked-in benchmark exercises vulnerable and safe agent boundaries:
python -m benchmarks.run
Current expectations cover 18 rule-level signals across direct and local-helper code execution, shell execution, SQL, SSRF, tool dispatch, prompt boundaries, high-impact tools, approval bypasses, and MCP. This is a regression corpus—not an industry benchmark or a claim of real-world detection rate. See the benchmark methodology.
How LLMSafe fits
| Tool category | Primary strength | LLMSafe relationship |
|---|---|---|
| General Python SAST | Broad language and API security checks | Complementary; LLMSafe focuses on AI/agent trust boundaries |
| Pattern-rule engines | Highly customizable organizational rules | LLMSafe supplies opinionated agent rules without rule authoring |
| Dependency scanners | Known vulnerable packages and supply chain | Out of scope; run alongside LLMSafe |
| Runtime guardrails | Enforce live policy and monitor model/tool calls | Out of scope; LLMSafe reviews source and configuration before runtime |
Read the architecture for implementation boundaries and tradeoffs.
Contributing and security
Contributions are welcome. Start with CONTRIBUTING.md and the public roadmap. Report vulnerabilities privately according to SECURITY.md.
LLMSafe is released under the MIT License.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file llmsafe-0.2.1.tar.gz.
File metadata
- Download URL: llmsafe-0.2.1.tar.gz
- Upload date:
- Size: 39.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
60d616242cedb4af3c78f3067a33a06a72041d0c8bfe38c0cbe7dc27f21f66e2
|
|
| MD5 |
bd876317d096b651774fccdb41bac090
|
|
| BLAKE2b-256 |
c0422a2815369ecd57ce393a3e69761df6bd6eb41556ac75a48ea1859aece476
|
Provenance
The following attestation bundles were made for llmsafe-0.2.1.tar.gz:
Publisher:
release.yml on rezerpaul-crypto/llmsafe
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
llmsafe-0.2.1.tar.gz -
Subject digest:
60d616242cedb4af3c78f3067a33a06a72041d0c8bfe38c0cbe7dc27f21f66e2 - Sigstore transparency entry: 2576376213
- Sigstore integration time:
-
Permalink:
rezerpaul-crypto/llmsafe@3c0769f597478bd318ade56b933409e5e5a0f24a -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/rezerpaul-crypto
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3c0769f597478bd318ade56b933409e5e5a0f24a -
Trigger Event:
release
-
Statement type:
File details
Details for the file llmsafe-0.2.1-py3-none-any.whl.
File metadata
- Download URL: llmsafe-0.2.1-py3-none-any.whl
- Upload date:
- Size: 38.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
62909c7d8babc7eaa0c247f705f8bcf03d069d66acce9a023e1fc328a32878f6
|
|
| MD5 |
a8eb8bc5921e497c9f617c129210e320
|
|
| BLAKE2b-256 |
ae0676f83954fdc74fb0ce335e483ae8633d3b18a723015bb97df73fbd179d25
|
Provenance
The following attestation bundles were made for llmsafe-0.2.1-py3-none-any.whl:
Publisher:
release.yml on rezerpaul-crypto/llmsafe
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
llmsafe-0.2.1-py3-none-any.whl -
Subject digest:
62909c7d8babc7eaa0c247f705f8bcf03d069d66acce9a023e1fc328a32878f6 - Sigstore transparency entry: 2576376313
- Sigstore integration time:
-
Permalink:
rezerpaul-crypto/llmsafe@3c0769f597478bd318ade56b933409e5e5a0f24a -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/rezerpaul-crypto
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3c0769f597478bd318ade56b933409e5e5a0f24a -
Trigger Event:
release
-
Statement type: