Building a Custom Scanner
Create your own vulnerability scanner that integrates with PhantomYerra's AI orchestrator, evidence pipeline, and reporting system. Your scanner runs alongside the built-in engines and produces findings in the same format.
The BaseToolAdapter Interface
Every custom scanner must subclass BaseToolAdapter. This base class defines the contract that PhantomYerra's orchestrator uses to discover, configure, and run your scanner.
Required Attributes
| Attribute | Type | Description |
|---|---|---|
TOOL_NAME | str | A unique identifier for your scanner (e.g., "my_custom_scanner"). Must be lowercase with underscores. Used internally for routing and logging. |
DISPLAY_NAME | str | Human-readable name shown in the UI (e.g., "My Custom Scanner"). |
DESCRIPTION | str | One-line description of what the scanner tests for. |
SURFACES | list[str] | Attack surfaces this scanner handles. Must match registered surface names (e.g., ["web_app", "api", "graphql"]). |
SEVERITY_RANGE | tuple | Min and max severity this scanner can report: ("info", "critical"). |
VERSION | str | Scanner version string (e.g., "1.0.0"). |
Required Methods
| Method | Signature | Description |
|---|---|---|
scan() |
async def scan(self, target: str, config: dict) -> list[Finding] |
The main entry point. Receives the target URL/host and configuration. Returns a list of Finding objects. This is where all scanning logic lives. |
is_applicable() |
async def is_applicable(self, target: str, fingerprint: dict) -> bool |
Called by the orchestrator before scan(). Return True if your scanner is relevant for this target based on its technology fingerprint. Return False to skip. |
health_check() |
async def health_check(self) -> bool |
Verify that your scanner's dependencies are available. Called on startup. Return True if ready, False if not (with an error logged). |
Optional Methods
| Method | Description |
|---|---|
configure(config) | Receive user-defined configuration options (e.g., aggressiveness level, custom wordlists). |
cleanup() | Called after scanning completes. Clean up temporary files, close connections. |
get_config_schema() | Return a JSON Schema describing configuration options. Used to render settings UI. |
Minimal Scanner Example
Below is a complete, minimal custom scanner that checks for exposed configuration files:
Registering Your Scanner
In your extension's __init__.py, register the scanner with the surface registry:
Registration Parameters
| Parameter | Type | Description |
|---|---|---|
tool | BaseToolAdapter | An instance of your scanner class. |
surfaces | list[str] | Attack surfaces to register for. Valid values: web_app, api, graphql, network, cloud, mobile, firmware, source_code, container, active_directory. |
priority | int | Execution priority (0-100). Higher priority scanners run first. Built-in scanners use 70-90. Use 50 for default, higher if your scanner should run early (e.g., reconnaissance). |
The Finding Object
Every vulnerability your scanner detects must be wrapped in a Finding object. This ensures uniform presentation in the UI, reports, and API.
Finding Fields
| Field | Type | Required | Description |
|---|---|---|---|
title | str | Yes | Short, descriptive title for the vulnerability. |
severity | str | Yes | One of: critical, high, medium, low, info. |
description | str | Yes | Detailed description of the vulnerability: what it is, where it was found, why it matters. |
evidence | Evidence | Yes | Proof of the vulnerability. At minimum: the HTTP request and response that demonstrates the issue. |
remediation | str | Yes | Specific, actionable remediation steps. Not generic advice. |
poc_steps | list[str] | Yes | Numbered reproduction steps. Must be copy-paste ready. |
cvss_vector | str | Recommended | CVSS v3.1 vector string. PhantomYerra calculates the numeric score automatically. |
cwe_id | str | Recommended | CWE identifier (e.g., "CWE-200"). |
affected_url | str | Recommended | The specific URL or endpoint where the vulnerability was found. |
tags | list[str] | Optional | Tags for categorization (e.g., ["config", "exposure", "owasp-a05"]). |
references | list[str] | Optional | Links to relevant documentation, CVE entries, or OWASP pages. |
compliance_map | dict | Optional | Compliance framework mappings (e.g., {"PCI-DSS": "6.5.8", "NIST": "AC-3"}). |
Real-Time Progress: emit_activity()
Call emit_activity() throughout your scan to keep the user informed. These events appear in the PhantomYerra UI as live progress updates.
emit_activity Parameters
| Parameter | Type | Description |
|---|---|---|
tool | str | Your scanner's TOOL_NAME. |
phase | str | Current phase: "init", "scanning", "analyzing", "complete", "error". |
message | str | Human-readable status message. |
percent | int | Progress percentage (0-100). |
Testing Your Scanner
-
1
Unit Test
Write unit tests for your scanner logic. Mock HTTP responses using
httpx.MockTransportorrespx. Verify that findings are created correctly with all required fields. -
2
Integration Test
Run your scanner against a known-vulnerable test target (e.g., DVWA, Juice Shop, WebGoat). Verify that it correctly identifies and reports the expected vulnerabilities.
-
3
UI Verification
Launch PhantomYerra with your extension installed. Run a scan against the test target. Verify your scanner appears in the tool list, progress events show in the UI, and findings appear in the results with correct severity, evidence, and PoC steps.
-
4
Report Verification
Generate a report from the scan. Verify your findings are included with proper formatting, evidence, and remediation guidance.