Skip to main content

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


Download files

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

Source Distribution

nuclei_sdk-1.0.0.tar.gz (23.7 kB view details)

Uploaded Source

Built Distribution

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

nuclei_sdk-1.0.0-py3-none-any.whl (26.4 kB view details)

Uploaded Python 3

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

Hashes for nuclei_sdk-1.0.0.tar.gz
Algorithm Hash digest
SHA256 0d06b58a4b2aed19f5bd8687999875126a041f1399ce69611cdef87a681373d8
MD5 1bfdca2a2808c6ae1fbfdc2c98d749f0
BLAKE2b-256 95576cc5ec2a0a3004de2964c35676f1761a5c679691179461d1e2d6139dd961

See more details on using hashes here.

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

Hashes for nuclei_sdk-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cb2568e7016c6bd29d39ab85fa8a6d296b9e2b4aa0eb5f2f779336737200b8ce
MD5 716e19d780b2faa5c83c2865b5e15173
BLAKE2b-256 51c0a583fd51c2db2428439b5ac426c2db4659ba91c85e5498a48a76d377b2dc

See more details on using hashes here.

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