Security tool for detecting exposed LLM/AI API keys in public repositories
Project description
LLMLeaks
A security auditing tool for detecting exposed API keys in public GitHub repositories. Built with async Python for high performance and designed with SOLID principles for extensibility.
Overview
LLMLeaks scans GitHub repositories for accidentally committed API keys and validates their status against provider APIs. This tool is designed for:
- Security researchers conducting authorized vulnerability assessments
- Organizations auditing their own repositories for leaked credentials
- DevSecOps teams implementing proactive secret detection
Features
- Asynchronous scanning with fail-fast quota management
- Resilient HTTP validation (429 rate-limit = key exists, not invalid)
- Provider-scoped deduplication (no cross-provider cache poisoning)
- Greedy regex with negative lookaheads (no key truncation)
- Support for 10 AI/LLM providers:
- OpenAI (including
sk-proj-andsk-svcacct-variants) - Anthropic (Claude)
- Google Gemini
- DeepSeek
- Groq
- Perplexity
- Hugging Face
- OpenRouter
- Replicate
- RunwayML
- OpenAI (including
- Extensible validator architecture (Strategy + Factory patterns)
- Real-time validation of discovered keys
- Configurable concurrency and output
Installation
From PyPI
pip install llmleaks
From Source
git clone https://github.com/fariiixm/llmleaks.git
cd llmleaks
pip install -e .
Development Installation
pip install -e ".[dev]"
Usage
Command Line
# Basic usage
llmleaks --token YOUR_GITHUB_TOKEN --query "API_KEY"
# Scan with custom parameters
llmleaks --token YOUR_GITHUB_TOKEN \
--query "sk-proj-" \
--pages 10 \
--out results.txt \
--concurrency 3
# Using environment variable for token
export GITHUB_TOKEN=ghp_xxxxxxxxxxxx
llmleaks --query "OPENAI_API_KEY"
As a Python Module
python -m auditor --token YOUR_GITHUB_TOKEN --query "API_KEY"
Programmatic Usage
import asyncio
from auditor import AsyncKeyAuditor
async def main():
auditor = AsyncKeyAuditor(
github_token="YOUR_GITHUB_TOKEN",
max_concurrent=5
)
stats = await auditor.run(
query="API_KEY",
pages=5,
output_file="results.txt"
)
print(f"Found {stats['valid']} valid keys")
asyncio.run(main())
Adding Custom Validators
from auditor.core.base import Validator
from auditor.core.factory import ValidatorFactory
class CustomValidator(Validator):
name = "custom"
async def validate(self, client, key):
return await self._safe_fetch(
client,
"https://api.example.com/verify",
headers={"Authorization": f"Bearer {key}"},
)
# Register the validator
ValidatorFactory.register(
"custom",
r"custom-[a-z0-9]{32}(?=[^a-z0-9]|$)",
CustomValidator()
)
CLI Options
| Option | Short | Description | Default |
|---|---|---|---|
--token |
GitHub Personal Access Token | $GITHUB_TOKEN |
|
--query |
-q |
GitHub code search query | API_KEY |
--pages |
-p |
Number of result pages to scan | 5 |
--out |
-o |
Output file for valid keys | valid_keys.txt |
--concurrency |
-c |
Maximum concurrent requests | 5 |
--quiet |
Suppress progress output | false |
|
--verbose |
-v |
Enable debug logging | false |
--list-providers |
List supported API providers |
Architecture
llmleaks/
├── src/auditor/
│ ├── __init__.py # Package exports (v2.0.0)
│ ├── __main__.py # Module entry point
│ ├── cli.py # Command-line interface
│ ├── core/
│ │ ├── base.py # Abstract Validator + _safe_fetch
│ │ ├── factory.py # ValidatorFactory registry (10 providers)
│ │ └── engine.py # AsyncKeyAuditor (fail-fast, provider cache)
│ └── validators/
│ └── llm.py # LLM provider validators (10 classes)
├── tests/ # Test suite
├── docs/ # Documentation
└── pyproject.toml # Project configuration
Design Principles
- Strategy Pattern: Validators implement a common interface for different providers
- Factory Pattern: Centralized registry for validator management
- Dependency Injection: Validators are decoupled from the engine
- Async I/O: Non-blocking network operations for performance
- Fail-Fast: Startup handshake verifies quota before scanning
- Resilient Validation: 429 = key exists (rate-limited, not invalid)
Requirements
- Python 3.10+
- GitHub Personal Access Token with
reposcope
Creating a GitHub Token
- Go to GitHub Settings > Developer settings > Personal access tokens
- Click "Generate new token (classic)"
- Select the
reposcope - Copy the generated token
Development
Running Tests
pytest
Running Tests with Coverage
pytest --cov=auditor --cov-report=html
Code Quality
# Linting
ruff check src tests
# Type checking
mypy src
Ethical Use
This tool is intended for authorized security research only. Users must:
- Only scan repositories they own or have explicit authorization to audit
- Follow responsible disclosure practices for any vulnerabilities found
- Comply with GitHub's Terms of Service and API usage policies
- Never use discovered keys for unauthorized access
Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/new-validator) - Commit your changes (
git commit -am 'Add new validator') - Push to the branch (
git push origin feature/new-validator) - Open a Pull Request
See CONTRIBUTING.md for detailed guidelines.
Quick Start Guides
- Google Colab / Windows / Linux - Step-by-step setup
- Colab Notebook - One-click notebook
- Simple Example - Python script example
License
MIT License - see LICENSE for details.
Changelog
See CHANGELOG.md for version history.
Project details
Release history Release notifications | RSS feed
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 llmleaks-2.0.0.tar.gz.
File metadata
- Download URL: llmleaks-2.0.0.tar.gz
- Upload date:
- Size: 14.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eedb3953d2cdeb6bb84fc22205d84a6f8cbdb06e5b2cef0ccf823ffcf5d094cc
|
|
| MD5 |
65b73daead842b468d826ec15e823475
|
|
| BLAKE2b-256 |
3b62c6c63e686f74fe7d7c184cf5ccdbd6df861a30dbc0d77a73ce10b394f886
|
File details
Details for the file llmleaks-2.0.0-py3-none-any.whl.
File metadata
- Download URL: llmleaks-2.0.0-py3-none-any.whl
- Upload date:
- Size: 17.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2f16f57c882d69bb2f20ae639f9e42c515545d3f0602f92f9440e6ea5c25b5d2
|
|
| MD5 |
1876d80b247320ab5b832fe48f15f95a
|
|
| BLAKE2b-256 |
1dcff5395e83111e5af8d328a9627071f5df9e4ba91ea80acc0f5f93d1b474b4
|