Asynchronous Python client for the Nuclei vulnerability scanner
Project description
nuclei-sdk (Python)
Async Python client for the Nuclei scanning engine, built by RevoltSecurities.
Same architecture as the Go SDK: one engine setup, many lightweight concurrent scans. Built on asyncio for maximum scalability — coroutines use ~1KB each vs ~8MB per thread.
Installation
pip install nuclei-sdk
The Go bridge binary (nuclei-sdk-bridge) is auto-installed from GitHub Releases on first use — no Go toolchain required. Supports Linux, macOS, and Windows on amd64/arm64.
Quick Start
import asyncio
from nucleisdk import ScanEngine
async def main():
# Binary auto-downloads if not found — zero setup needed
engine = ScanEngine(rate_limit=100, timeout=10, no_interactsh=True)
await engine.setup()
async for result in engine.scan(targets=["https://example.com"], tags=["cve"], severities=["high"]):
print(f"[{result.severity}] {result.template_id} - {result.matched_url}")
await engine.close()
asyncio.run(main())
Async Context Manager
async with ScanEngine(rate_limit=100, no_interactsh=True) as engine:
results = [r async for r in engine.scan(targets=["https://example.com"])]
Multiple Scans (Same Engine)
async with ScanEngine(rate_limit=100) as engine:
# Scan 1 — HTTP CVEs
async for r in engine.scan(targets=["https://a.com"], tags=["cve"], protocol_types="http"):
print(r.template_id)
# Scan 2 — SSL checks (same engine, no re-initialization)
async for r in engine.scan(targets=["b.com:443"], protocol_types="ssl"):
print(r.template_id)
ScanPool: Continuous Dynamic Scanning
Iterator Mode
from nucleisdk import ScanEngine, TemplateBytesEntry
async def main():
async with ScanEngine(rate_limit=100, no_interactsh=True) as engine:
pool = await engine.scan_pool(workers=5)
# Submit jobs dynamically
await pool.submit("CVE-2024-1234",
targets=["https://target.com"],
template_bytes=[TemplateBytesEntry("CVE-2024-1234", yaml_bytes)])
await pool.submit("wordpress",
targets=["https://wp.example.com"],
tags=["wordpress"])
# Consume results
async for lr in pool.results():
print(f"[{lr.label}] [{lr.result.severity}] {lr.result.template_id}")
await pool.close()
stats = await pool.stats()
print(f"Submitted: {stats.submitted}, Completed: {stats.completed}")
Callback Mode (No Manual Iteration)
async def handle_result(lr):
print(f"[{lr.label}] [{lr.result.severity}] {lr.result.template_id}")
async def main():
async with ScanEngine(rate_limit=100, no_interactsh=True) as engine:
pool = await engine.scan_pool(workers=5, on_result=handle_result)
await pool.submit("CVE-2024-1234", targets=["https://target.com"], ...)
await pool.submit("wordpress", targets=["https://wp.example.com"], tags=["wordpress"])
await pool.close() # waits for all jobs; callback fires automatically
Concurrent Submit + Consume with asyncio.gather
async def submit_jobs(pool):
for cve in cve_feed:
await pool.submit(cve["id"], targets=[cve["target"]], template_bytes=[...])
await pool.close()
async def consume_results(pool):
async for lr in pool.results():
print(f"[{lr.label}] {lr.result.severity}")
async def main():
async with ScanEngine(rate_limit=100, no_interactsh=True) as engine:
pool = await engine.scan_pool(workers=10)
await asyncio.gather(submit_jobs(pool), consume_results(pool))
Targeted Scan with Template Bytes
from nucleisdk import ScanEngine, TemplateBytesEntry
yaml_template = b"""
id: custom-check
info:
name: Custom Check
severity: high
author: me
http:
- method: GET
path:
- "{{BaseURL}}/admin"
matchers:
- type: status
status:
- 200
"""
async def main():
async with ScanEngine(no_interactsh=True) as engine:
async for r in engine.scan(
targets=["https://example.com"],
template_bytes=[TemplateBytesEntry("custom-check", yaml_template)]
):
print(f"[{r.severity}] {r.template_id}")
Bridge Binary Installation
The bridge binary is auto-installed on first use. You can also manage it manually:
Auto-Install (Default)
# Just use ScanEngine — binary is downloaded automatically if not found
async with ScanEngine(rate_limit=100) as engine:
...
Output on first run:
nucleisdk: nuclei-sdk-bridge not found locally, attempting auto-install from GitHub Releases...
nucleisdk: fetching latest release from RevoltSecurities/nuclei-sdk...
nucleisdk: latest release: v0.1.0
nucleisdk: downloading nuclei-sdk-bridge_0.1.0_darwin_arm64.tar.gz...
nucleisdk: checksum verified.
nucleisdk: installed nuclei-sdk-bridge v0.1.0 to /home/user/.local/bin/nuclei-sdk-bridge
Manual Install
from nucleisdk import install_bridge
# Install to default location (~/.local/bin/ on Linux/macOS)
path = install_bridge()
print(f"Installed to: {path}")
# Install to a custom directory
path = install_bridge(install_dir="/opt/nuclei/bin")
Quiet Mode (No Logging)
from nucleisdk import install_bridge
# Suppress all installation progress messages
path = install_bridge(quiet=True)
Custom Repository (Forks)
from nucleisdk import install_bridge
# Install from a fork — only 'owner/repo' format allowed
path = install_bridge(repo="MyOrg/my-nuclei-fork")
# Combined with other options
path = install_bridge(
repo="MyOrg/my-nuclei-fork",
install_dir="/opt/nuclei/bin",
quiet=True,
)
Only GitHub URLs are accepted — full URLs, path traversal, and query injection are all rejected with InstallError (SSRF protection).
Use a Specific Binary
# Skip auto-install entirely — use your own binary
engine = ScanEngine(
binary_path="/opt/homebrew/bin/nuclei-sdk-bridge",
rate_limit=100,
)
Build From Source (Alternative)
# Requires Go toolchain
cd /path/to/nuclei-sdk
go build -o bin/nuclei-sdk-bridge ./cmd/nuclei-sdk-bridge/
Error Handling
from nucleisdk import (
ScanEngine,
InstallError,
DownloadError,
ChecksumError,
UnsupportedPlatformError,
InstallPermissionError,
)
try:
async with ScanEngine() as engine:
...
except UnsupportedPlatformError:
# OS/arch not in pre-built releases (e.g., FreeBSD, 32-bit)
print("Build from source: go build -o bin/nuclei-sdk-bridge ./cmd/nuclei-sdk-bridge/")
except DownloadError:
# No internet, GitHub down, rate limited
print("Check internet connection or use binary_path=")
except ChecksumError:
# Downloaded file corrupted — retry
print("Download corrupted, try again")
except InstallPermissionError:
# Can't write to ~/.local/bin/
print("Use install_bridge(install_dir='./bin') for a writable path")
except InstallError:
# Catch-all for any install failure
print("Installation failed")
Install Locations
| Platform | Default Path |
|---|---|
| Linux | ~/.local/bin/nuclei-sdk-bridge |
| macOS | ~/.local/bin/nuclei-sdk-bridge |
| Windows | %LOCALAPPDATA%\nuclei-sdk\bin\nuclei-sdk-bridge.exe |
Exception Hierarchy
BridgeError
└── InstallError
├── UnsupportedPlatformError
├── DownloadError
├── ChecksumError
├── InstallPermissionError
└── VersionMismatchError
Version Management
The Python SDK automatically checks bridge binary version compatibility on startup.
Automatic Version Check
# Version is checked automatically when ScanEngine starts.
# If the installed binary is incompatible, a compatible one is auto-downloaded.
async with ScanEngine(rate_limit=100) as engine:
...
Check Version Manually
from nucleisdk import ensure_bridge, get_bridge_version, check_version_compatible, MIN_BRIDGE_VERSION
path = ensure_bridge(quiet=True)
ver = get_bridge_version(path)
print(f"Bridge version: {ver}")
print(f"Compatible: {check_version_compatible(ver)}")
print(f"Min required: {MIN_BRIDGE_VERSION}")
Force Update
from nucleisdk import install_bridge
# Always downloads the latest version, even if current is compatible
path = install_bridge(update=True)
Handle Version Mismatch
from nucleisdk import ensure_bridge, install_bridge, VersionMismatchError
try:
path = ensure_bridge(auto_install=False)
except VersionMismatchError as e:
print(f"Incompatible bridge: {e}")
path = install_bridge(update=True)
API Reference
ScanEngine
| Method | Description |
|---|---|
ScanEngine(**config) |
Create engine with config (rate_limit, timeout, tags, etc.) |
await setup() |
One-time heavy initialization |
async for r in scan(**opts) |
Lightweight scan, yields ScanResult |
await scan_collect(**opts) |
Same as scan(), returns List[ScanResult] |
await scan_pool(workers, on_result=None) |
Create a ScanPool |
await close() |
Shut down engine |
async with ScanEngine() as engine |
Context manager (auto setup/close) |
ScanPool
| Method | Description |
|---|---|
await submit(label, **opts) |
Queue a scan job |
async for lr in results() |
Iterate over LabeledResult |
await stats() |
Get PoolStats |
await close() |
Wait for jobs and shut down |
ScanResult
| Field | Type | Description |
|---|---|---|
template_id |
str | Template identifier |
severity |
str | info, low, medium, high, critical |
matched_url |
str | URL that matched |
host |
str | Target host |
tags |
List[str] | Template tags |
cve_id |
List[str] | CVE identifiers |
is_critical() |
bool | Severity == critical |
is_high_or_above() |
bool | Severity >= high |
severity_level() |
int | 0-5 numeric |
Installer & Version
| Function | Description |
|---|---|
install_bridge(install_dir=None, quiet=False, repo=..., update=False) |
Download and install the bridge binary |
ensure_bridge(binary_path=None, quiet=False, auto_install=True, update=False, repo=...) |
Find or auto-install a compatible bridge binary |
get_bridge_version(binary_path) |
Get version string from a bridge binary |
check_version_compatible(version) |
Check if a version meets SDK requirements |
| Parameter | Type | Default | Description |
|---|---|---|---|
install_dir |
str | None | None |
Custom install directory (default: ~/.local/bin/) |
quiet |
bool | False |
Suppress installation progress messages |
repo |
str | "RevoltSecurities/nuclei-sdk" |
GitHub owner/repo to fetch from (SSRF-safe) |
update |
bool | False |
Always download latest version |
auto_install |
bool | True |
Auto-download if not found or incompatible |
License
MIT
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 nuclei_sdk-1.0.0.tar.gz.
File metadata
- Download URL: nuclei_sdk-1.0.0.tar.gz
- Upload date:
- Size: 23.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0d06b58a4b2aed19f5bd8687999875126a041f1399ce69611cdef87a681373d8
|
|
| MD5 |
1bfdca2a2808c6ae1fbfdc2c98d749f0
|
|
| BLAKE2b-256 |
95576cc5ec2a0a3004de2964c35676f1761a5c679691179461d1e2d6139dd961
|
File details
Details for the file nuclei_sdk-1.0.0-py3-none-any.whl.
File metadata
- Download URL: nuclei_sdk-1.0.0-py3-none-any.whl
- Upload date:
- Size: 26.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cb2568e7016c6bd29d39ab85fa8a6d296b9e2b4aa0eb5f2f779336737200b8ce
|
|
| MD5 |
716e19d780b2faa5c83c2865b5e15173
|
|
| BLAKE2b-256 |
51c0a583fd51c2db2428439b5ac426c2db4659ba91c85e5498a48a76d377b2dc
|