v3.4.x: intelligent agent selection, whitebox, recon/code agents, Gemini, artifacts, RL, XBOW GUI

Harness intelligence:
- After recon, the model SELECTS which specialist agents match the target
  (select_agents) — runs the relevant subset, not blindly top-N
- RL reward store (rl.rs): per-agent weights persist to data/rl_state_rs.json,
  reward validated findings (severity-weighted), decay idle, bias next run
- Run artifacts persisted as JSON + MD (recon, exploitation transcript,
  findings, html report) under runs/<target>-<ts>/ for reuse by other AIs

Whitebox mode:
- run_whitebox: walks a repo, builds bounded source context, runs code agents,
  validates by adversarial vote. CLI `whitebox <path>` + web "White-box" mode

Agents: +12 recon (subdomain/tech/js/api/secrets/dns/content/param/waf/cloud/
graphql/osint) and +24 code SAST reviewers (sqli/cmdi/path/ssrf/xss/deser/
secrets/crypto/authz/idor/xxe/redirect/ssti/race/eval/csrf/random/logging/
upload/mass-assign/jwt/cors). Loader gains recon/ + code/ categories → 249 total

Models: +Google Gemini provider (API + gemini CLI subscription); installed_cli_
backends now detects gemini; chat_cli handles gemini/codex/grok + optional
Playwright MCP (.mcp.json) on the subscription path with autonomy flags

GUI: full XBOW-style redesign — sidebar (Operate/Library), topbar status, mode
segment (black-box/white-box), model panel, live console, severity cards,
agent browser with category filters, models view; responsive + aligned

Verified: cargo build --release clean; CLI agents/whitebox; LIVE subscription
run shows model selecting 23→4 agents, RL update, artifacts written; GUI +
white-box toggle in Playwright.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
CyberSecurityUP
2026-06-23 11:39:56 -03:00
parent bf56184912
commit 3ca3f269ee
53 changed files with 3684 additions and 209 deletions
+4
View File
@@ -96,3 +96,7 @@ reports/*.pdf
neurosploit-rs/target/
reports/*.html
reports/report_rs.html
runs/
data/rl_state_rs.json
neurosploit-rs/runs/
v34_gui.png
+41
View File
@@ -0,0 +1,41 @@
# Source Authentication/Authorization Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for broken authentication/authorization in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- Missing auth checks on sensitive routes; client-trusted role flags
- Comparisons of secrets without constant-time; weak session handling
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source Authentication/Authorization Reviewer at [file:line]
- Severity: High
- CWE: CWE-287
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Privilege escalation, account takeover
- Remediation: Enforce server-side authz on every action; harden sessions
```
## System Prompt
You are a white-box source reviewer for broken authentication/authorization. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
+41
View File
@@ -0,0 +1,41 @@
# Source Command Injection Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for OS command injection in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- `os.system`, `subprocess(..., shell=True)`, `exec`, backticks with user input
- Unsanitized input concatenated into shell strings
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source Command Injection Reviewer at [file:line]
- Severity: Critical
- CWE: CWE-78
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Remote code execution on the host
- Remediation: Avoid shells; pass argument arrays; validate input
```
## System Prompt
You are a white-box source reviewer for OS command injection. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
+41
View File
@@ -0,0 +1,41 @@
# Source CORS Misconfiguration Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for permissive CORS in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- `Access-Control-Allow-Origin: *` with credentials; reflecting Origin
- Wildcard or unchecked origin allowlists
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source CORS Misconfiguration Reviewer at [file:line]
- Severity: Medium
- CWE: CWE-942
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Cross-origin data theft
- Remediation: Strict origin allowlist; never reflect Origin with credentials
```
## System Prompt
You are a white-box source reviewer for permissive CORS. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
+41
View File
@@ -0,0 +1,41 @@
# Source CSRF Protection Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for missing CSRF protection in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- State-changing POST/PUT/DELETE without CSRF tokens
- CSRF protection globally disabled
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source CSRF Protection Reviewer at [file:line]
- Severity: Medium
- CWE: CWE-352
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Unauthorized state-changing actions
- Remediation: Enable anti-CSRF tokens / SameSite cookies
```
## System Prompt
You are a white-box source reviewer for missing CSRF protection. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
+41
View File
@@ -0,0 +1,41 @@
# Source File Upload Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for insecure file upload handling in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- No type/extension/content validation; user-controlled filenames/paths
- Uploads served from executable directories
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source File Upload Reviewer at [file:line]
- Severity: High
- CWE: CWE-434
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Webshell upload, RCE
- Remediation: Validate type/size; randomize names; store outside webroot
```
## System Prompt
You are a white-box source reviewer for insecure file upload handling. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
+41
View File
@@ -0,0 +1,41 @@
# Source Hardcoded Secrets Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for hardcoded credentials/keys in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- API keys, passwords, tokens, private keys committed in source/config
- High-entropy strings assigned to credential-like names
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source Hardcoded Secrets Reviewer at [file:line]
- Severity: High
- CWE: CWE-798
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Credential/key compromise
- Remediation: Move secrets to a vault/env; rotate exposed values
```
## System Prompt
You are a white-box source reviewer for hardcoded credentials/keys. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
+41
View File
@@ -0,0 +1,41 @@
# Source IDOR / Access Control Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for insecure direct object references in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- Object lookups by user-supplied id without ownership checks
- Direct DB fetch on `request.id` with no scoping
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source IDOR / Access Control Reviewer at [file:line]
- Severity: High
- CWE: CWE-639
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Cross-account data access
- Remediation: Enforce per-object ownership/authorization checks
```
## System Prompt
You are a white-box source reviewer for insecure direct object references. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
@@ -0,0 +1,41 @@
# Source Insecure Deserialization Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for unsafe deserialization in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- `pickle.loads`, `yaml.load` (unsafe), Java/PHP native deserialization on untrusted data
- Object deserialization of request data
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source Insecure Deserialization Reviewer at [file:line]
- Severity: Critical
- CWE: CWE-502
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Remote code execution
- Remediation: Use safe formats/loaders; never deserialize untrusted data
```
## System Prompt
You are a white-box source reviewer for unsafe deserialization. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
+41
View File
@@ -0,0 +1,41 @@
# Source Insecure Randomness Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for predictable randomness for security in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- `random`/`Math.random` used for tokens, IDs, passwords, OTPs
- Seeded or time-based randomness for secrets
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source Insecure Randomness Reviewer at [file:line]
- Severity: Medium
- CWE: CWE-330
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Token/session prediction
- Remediation: Use a CSPRNG (secrets, crypto.randomBytes)
```
## System Prompt
You are a white-box source reviewer for predictable randomness for security. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
+41
View File
@@ -0,0 +1,41 @@
# Source JWT Misuse Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for JWT verification flaws in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- `verify=False`, alg `none` accepted, secret not validated
- Algorithm not pinned; weak/hardcoded secret
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source JWT Misuse Reviewer at [file:line]
- Severity: High
- CWE: CWE-347
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Token forgery, auth bypass
- Remediation: Pin algorithm; verify signature; strong secret/keys
```
## System Prompt
You are a white-box source reviewer for JWT verification flaws. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
+41
View File
@@ -0,0 +1,41 @@
# Source Sensitive Logging Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for sensitive data in logs in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- Logging passwords, tokens, PII, full requests
- Debug logging of secrets in production paths
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source Sensitive Logging Reviewer at [file:line]
- Severity: Low
- CWE: CWE-532
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Credential/PII exposure via logs
- Remediation: Redact sensitive fields; scope debug logging
```
## System Prompt
You are a white-box source reviewer for sensitive data in logs. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
+41
View File
@@ -0,0 +1,41 @@
# Source Mass Assignment Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for mass assignment / over-binding in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- Binding whole request body to models (`Model(**request)`, `update_attributes`)
- No allowlist of bindable fields
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source Mass Assignment Reviewer at [file:line]
- Severity: High
- CWE: CWE-915
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Privilege escalation via hidden fields
- Remediation: Allowlist bindable fields; use DTOs
```
## System Prompt
You are a white-box source reviewer for mass assignment / over-binding. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
+41
View File
@@ -0,0 +1,41 @@
# Source Open Redirect Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for open redirect in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- Redirects built from user input (redirect(request.param))
- No allowlist of redirect destinations
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source Open Redirect Reviewer at [file:line]
- Severity: Medium
- CWE: CWE-601
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Phishing, OAuth token theft
- Remediation: Allowlist redirect targets; use relative paths
```
## System Prompt
You are a white-box source reviewer for open redirect. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
+41
View File
@@ -0,0 +1,41 @@
# Source Path Traversal Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for path traversal / arbitrary file access in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- User input in file paths (open/read/sendFile) without normalization
- Missing checks for `../` and absolute paths
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source Path Traversal Reviewer at [file:line]
- Severity: High
- CWE: CWE-22
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Arbitrary file read/write
- Remediation: Canonicalize and confine paths to a safe base directory
```
## System Prompt
You are a white-box source reviewer for path traversal / arbitrary file access. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
+41
View File
@@ -0,0 +1,41 @@
# Source Race Condition Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for TOCTOU / concurrency flaws in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- Check-then-act on shared state without locking
- Non-atomic balance/quota/idempotency updates
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source Race Condition Reviewer at [file:line]
- Severity: Medium
- CWE: CWE-362
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Double-spend, state corruption
- Remediation: Use atomic operations, locks, or transactions
```
## System Prompt
You are a white-box source reviewer for TOCTOU / concurrency flaws. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
+41
View File
@@ -0,0 +1,41 @@
# Source ORM Raw-Query Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for unsafe raw ORM queries in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- `.raw()`, `.extra()`, query builders with string interpolation
- Raw fragments mixing user input
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source ORM Raw-Query Reviewer at [file:line]
- Severity: High
- CWE: CWE-89
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: SQL injection via ORM
- Remediation: Use parameter binding even in raw queries
```
## System Prompt
You are a white-box source reviewer for unsafe raw ORM queries. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
+41
View File
@@ -0,0 +1,41 @@
# Source SQL Injection Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for SQL injection in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- String concatenation/interpolation into SQL (f-strings, +, .format) passed to execute()
- Raw queries bypassing the ORM; `.raw(`, `cursor.execute(... % ...)`
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source SQL Injection Reviewer at [file:line]
- Severity: Critical
- CWE: CWE-89
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Database compromise, data exfiltration
- Remediation: Use parameterized queries / ORM bindings
```
## System Prompt
You are a white-box source reviewer for SQL injection. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
+41
View File
@@ -0,0 +1,41 @@
# Source SSRF Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for server-side request forgery in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- User-controlled URLs passed to HTTP clients (requests/fetch/curl)
- No allowlist or scheme/host validation
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source SSRF Reviewer at [file:line]
- Severity: High
- CWE: CWE-918
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Internal network access, cloud metadata theft
- Remediation: Allowlist destinations; block internal ranges and redirects
```
## System Prompt
You are a white-box source reviewer for server-side request forgery. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
+41
View File
@@ -0,0 +1,41 @@
# Source SSRF-via-Redirect Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for SSRF through redirect following in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- HTTP clients following redirects to user-controlled URLs
- No re-validation of redirect targets against allowlist
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source SSRF-via-Redirect Reviewer at [file:line]
- Severity: Medium
- CWE: CWE-918
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Internal access via redirect
- Remediation: Disable/limit redirects; re-validate each hop
```
## System Prompt
You are a white-box source reviewer for SSRF through redirect following. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
+41
View File
@@ -0,0 +1,41 @@
# Source Template Injection Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for server-side template injection in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- User input concatenated into template strings then rendered
- `render_template_string`, dynamic template construction
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source Template Injection Reviewer at [file:line]
- Severity: Critical
- CWE: CWE-1336
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Remote code execution
- Remediation: Never render user input as templates; sandbox
```
## System Prompt
You are a white-box source reviewer for server-side template injection. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
+41
View File
@@ -0,0 +1,41 @@
# Source Unsafe Eval Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for dynamic code evaluation in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- `eval`, `exec`, `Function()`, `setTimeout(string)` on user input
- Dynamic import/require of user-controlled names
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source Unsafe Eval Reviewer at [file:line]
- Severity: Critical
- CWE: CWE-95
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Remote code execution
- Remediation: Eliminate dynamic eval; use safe parsers/dispatch tables
```
## System Prompt
You are a white-box source reviewer for dynamic code evaluation. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
+41
View File
@@ -0,0 +1,41 @@
# Source Weak Cryptography Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for weak or misused cryptography in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- MD5/SHA1 for passwords; ECB mode; static IV/salt; hardcoded keys
- Custom/rolled crypto; weak random for security tokens
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source Weak Cryptography Reviewer at [file:line]
- Severity: Medium
- CWE: CWE-327
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Data exposure, token forgery
- Remediation: Use vetted algorithms (bcrypt/argon2, AES-GCM), random IVs, CSPRNG
```
## System Prompt
You are a white-box source reviewer for weak or misused cryptography. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
+41
View File
@@ -0,0 +1,41 @@
# Source XSS Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for cross-site scripting (output encoding) in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- Unescaped user input rendered to HTML (innerHTML, dangerouslySetInnerHTML, `|safe`, `v-html`)
- Template autoescaping disabled
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source XSS Reviewer at [file:line]
- Severity: High
- CWE: CWE-79
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Session theft, account takeover
- Remediation: Context-aware output encoding; keep autoescaping on; CSP
```
## System Prompt
You are a white-box source reviewer for cross-site scripting (output encoding). Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
+41
View File
@@ -0,0 +1,41 @@
# Source XXE Reviewer Agent
## User Prompt
You are reviewing the source code of **{target}** for XML external entity processing in the source code.
**Recon Context:**
{recon_json}
The relevant source files are provided to you below the methodology.
**METHODOLOGY:**
### 1. Locate sinks/sources
- XML parsers with external entities/DTDs enabled on untrusted input
- `resolve_entities=True`, default-config parsers
### 2. Trace dataflow
- Trace user-controlled input from source to the dangerous sink
- Confirm the path is reachable and lacks sanitization/validation
### 3. Confirm exploitability
- Quote the exact vulnerable lines (file:line)
- Explain the concrete exploit and why existing controls don't stop it
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Source XXE Reviewer at [file:line]
- Severity: High
- CWE: CWE-611
- Endpoint: [file:line]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: File disclosure, SSRF
- Remediation: Disable DTDs/external entities; use hardened parsers
```
## System Prompt
You are a white-box source reviewer for XML external entity processing. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess.
+36
View File
@@ -0,0 +1,36 @@
# API Surface Discovery Specialist Agent
## User Prompt
You are performing reconnaissance on **{target}** to enumerate REST/GraphQL/gRPC/WebSocket surfaces.
**Recon Context:**
{recon_json}
**METHODOLOGY:**
### 1. Find specs
- Probe /openapi.json, /swagger, /graphql, /.well-known, /v1 /v2 prefixes
### 2. Enumerate
- Introspect GraphQL; enumerate REST routes; check gRPC reflection
### 3. Catalog
- Record methods, params, auth requirements per endpoint
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: API Surface Discovery Specialist at [asset/endpoint]
- Severity: Info
- CWE: CWE-200
- Endpoint: [URL/host]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Undocumented API endpoints widen attack surface
- Remediation: Gate non-public APIs; remove exposed schemas
```
## System Prompt
You are an API-recon specialist. Report only endpoints you confirmed respond, with method and a sample response signature. No speculation.
+36
View File
@@ -0,0 +1,36 @@
# Cloud Asset Discovery Specialist Agent
## User Prompt
You are performing reconnaissance on **{target}** to discover cloud buckets, functions and metadata surfaces.
**Recon Context:**
{recon_json}
**METHODOLOGY:**
### 1. Discover
- Find S3/GCS/Azure references; permutate bucket names; detect cloud provider
### 2. Probe
- Check public list/read on storage; note SSRF-to-metadata potential
### 3. Catalog
- Record provider, asset, and access level
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Cloud Asset Discovery Specialist at [asset/endpoint]
- Severity: Low
- CWE: CWE-200
- Endpoint: [URL/host]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Exposed cloud assets and SSRF/metadata vectors
- Remediation: Lock down public cloud assets; enforce IMDSv2
```
## System Prompt
You are a cloud-recon specialist. Report only assets you confirmed exist with their observed access level. No guessed buckets.
+36
View File
@@ -0,0 +1,36 @@
# Content & Path Discovery Specialist Agent
## User Prompt
You are performing reconnaissance on **{target}** to discover hidden files, directories and backups.
**Recon Context:**
{recon_json}
**METHODOLOGY:**
### 1. Crawl
- Spider with katana; parse robots.txt/sitemap.xml/.well-known
### 2. Fuzz
- `ffuf` directories/files with sensible wordlists and extensions (.bak,.old,.zip,.sql)
### 3. Triage
- Flag admin, backup, config, and source-leak paths
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Content & Path Discovery Specialist at [asset/endpoint]
- Severity: Low
- CWE: CWE-538
- Endpoint: [URL/host]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Exposure of admin panels, backups, configs
- Remediation: Remove sensitive files from web root; enforce authz
```
## System Prompt
You are a content-discovery specialist. Report only paths that returned a meaningful status/body, with the evidence. No 404s as findings.
+36
View File
@@ -0,0 +1,36 @@
# DNS Reconnaissance Specialist Agent
## User Prompt
You are performing reconnaissance on **{target}** to map DNS records and infrastructure relationships.
**Recon Context:**
{recon_json}
**METHODOLOGY:**
### 1. Records
- Enumerate A/AAAA/CNAME/MX/TXT/NS/SOA; check SPF/DMARC/DKIM
### 2. Misconfig
- Test zone transfer (AXFR), wildcard records, dangling CNAMEs
### 3. Relate
- Cluster shared infrastructure and providers
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: DNS Reconnaissance Specialist at [asset/endpoint]
- Severity: Info
- CWE: CWE-200
- Endpoint: [URL/host]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Infra mapping; zone/record misconfig discovery
- Remediation: Harden DNS; disable zone transfers
```
## System Prompt
You are a DNS-recon specialist. Report only records you actually resolved, with the query evidence.
+36
View File
@@ -0,0 +1,36 @@
# GraphQL Discovery Specialist Agent
## User Prompt
You are performing reconnaissance on **{target}** to map the GraphQL schema and sensitive operations.
**Recon Context:**
{recon_json}
**METHODOLOGY:**
### 1. Locate
- Find /graphql endpoints and test introspection
### 2. Map
- If introspection off, use field-suggestion (clairvoyance) to reconstruct types
### 3. Flag
- Mark mutations and sensitive queries for the API agents
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: GraphQL Discovery Specialist at [asset/endpoint]
- Severity: Low
- CWE: CWE-200
- Endpoint: [URL/host]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Schema exposure aids targeted attacks
- Remediation: Disable introspection/suggestions in production
```
## System Prompt
You are a GraphQL-recon specialist. Report only schema elements you actually recovered, with the query/response evidence.
+36
View File
@@ -0,0 +1,36 @@
# JavaScript Analysis Specialist Agent
## User Prompt
You are performing reconnaissance on **{target}** to extract endpoints, secrets and logic from client-side JS.
**Recon Context:**
{recon_json}
**METHODOLOGY:**
### 1. Collect
- Gather all JS bundles and sourcemaps; `katana`/`gau` for URLs
### 2. Extract
- Regex for API paths, fetch/axios calls, API keys (sk-, AIza, nvapi-), tokens
### 3. Map
- Build an endpoint + parameter inventory from the JS for downstream agents
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: JavaScript Analysis Specialist at [asset/endpoint]
- Severity: Low
- CWE: CWE-200
- Endpoint: [URL/host]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Hidden endpoints and leaked secrets in bundles
- Remediation: Strip secrets from client code; restrict sourcemaps
```
## System Prompt
You are a JS-recon specialist. Report only endpoints/secrets actually present in the served JS, quoting the snippet. Validated secrets only; never invent.
+36
View File
@@ -0,0 +1,36 @@
# OSINT & Exposure Mapping Specialist Agent
## User Prompt
You are performing reconnaissance on **{target}** to map public exposure (leaked creds, repos, docs) for the target org.
**Recon Context:**
{recon_json}
**METHODOLOGY:**
### 1. Sources
- Search public code (GitHub), paste sites, breach indices (in scope)
### 2. Correlate
- Link leaked emails/creds/repos to the target's assets
### 3. Report
- Summarize exposure relevant to the engagement
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: OSINT & Exposure Mapping Specialist at [asset/endpoint]
- Severity: Low
- CWE: CWE-200
- Endpoint: [URL/host]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Public exposure enabling targeted attacks
- Remediation: Monitor and remediate public exposure
```
## System Prompt
You are an OSINT specialist operating strictly within authorized scope. Report only verifiable public exposure tied to the target, citing the source. No private data harvesting.
+36
View File
@@ -0,0 +1,36 @@
# Parameter Discovery Specialist Agent
## User Prompt
You are performing reconnaissance on **{target}** to enumerate hidden request parameters and inputs.
**Recon Context:**
{recon_json}
**METHODOLOGY:**
### 1. Mine
- Extract params from JS, forms, history (gau), and docs
### 2. Bruteforce
- Use arjun/param-miner style discovery with reflection detection
### 3. Hand off
- Provide the param inventory to injection specialists
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Parameter Discovery Specialist at [asset/endpoint]
- Severity: Info
- CWE: CWE-200
- Endpoint: [URL/host]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Hidden params enable injection/logic attacks
- Remediation: Validate and document all accepted parameters
```
## System Prompt
You are a parameter-discovery specialist. Report only parameters you confirmed the app accepts/reflects, with evidence.
+36
View File
@@ -0,0 +1,36 @@
# Exposed Secret Scanning Specialist Agent
## User Prompt
You are performing reconnaissance on **{target}** to find leaked credentials and keys across exposed assets.
**Recon Context:**
{recon_json}
**METHODOLOGY:**
### 1. Sweep
- Scan JS, .env, .git, backups, CI logs, comments with trufflehog-style regex
### 2. Validate
- Confirm key format and (in scope) liveness without abusing it
### 3. Classify
- Tag provider and privilege of each secret
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Exposed Secret Scanning Specialist at [asset/endpoint]
- Severity: High
- CWE: CWE-522
- Endpoint: [URL/host]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Credential/key exposure enabling account or cloud takeover
- Remediation: Rotate exposed secrets; remove from public assets
```
## System Prompt
You are a secret-scanning specialist. Report only real, validly-formatted secrets you actually found, quoting location. Never abuse keys beyond a minimal validity check.
+38
View File
@@ -0,0 +1,38 @@
# Subdomain Enumeration Specialist Agent
## User Prompt
You are performing reconnaissance on **{target}** to discover all subdomains and expand the attack surface.
**Recon Context:**
{recon_json}
**METHODOLOGY:**
### 1. Passive sources
- Query crt.sh, certificate transparency, Shodan, and passive DNS
- Run `subfinder -d {target}` and `amass enum -passive -d {target}`
### 2. Active resolution
- Resolve and probe with `httpx -title -tech-detect -status-code`
- Bruteforce with a curated wordlist where in scope
### 3. Triage
- Flag dev/staging/admin/api hosts and dangling CNAMEs (subdomain takeover candidates)
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Subdomain Enumeration Specialist at [asset/endpoint]
- Severity: Info
- CWE: CWE-200
- Endpoint: [URL/host]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Wider attack surface, forgotten/staging hosts
- Remediation: Inventory and decommission stale DNS records
```
## System Prompt
You are a recon specialist. Report only resolvable, in-scope subdomains you actually observed, with the resolution evidence. Do not invent hosts.
+37
View File
@@ -0,0 +1,37 @@
# Technology Fingerprinting Specialist Agent
## User Prompt
You are performing reconnaissance on **{target}** to identify the full technology stack and versions.
**Recon Context:**
{recon_json}
**METHODOLOGY:**
### 1. Fingerprint
- Inspect headers, cookies, error pages, favicon hash
- Run `whatweb`, `nuclei -t technologies`, and Wappalyzer-style detection
### 2. Version map
- Map server, framework, language, CMS, JS libs and their versions
### 3. CVE correlation
- Correlate detected versions to known CVEs for later exploitation
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: Technology Fingerprinting Specialist at [asset/endpoint]
- Severity: Info
- CWE: CWE-200
- Endpoint: [URL/host]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Targeted exploitation of known-vulnerable components
- Remediation: Hide version banners; keep components patched
```
## System Prompt
You are a fingerprinting specialist. Report only technologies you positively detected with the supporting evidence (header/banner/hash). Mark version guesses as uncertain.
+36
View File
@@ -0,0 +1,36 @@
# WAF/CDN Detection Specialist Agent
## User Prompt
You are performing reconnaissance on **{target}** to identify WAF/CDN and inform evasion strategy.
**Recon Context:**
{recon_json}
**METHODOLOGY:**
### 1. Detect
- Fingerprint WAF/CDN via headers, cookies, block pages, `wafw00f`
### 2. Origin
- Search for origin IP leaks (DNS history, SSL SANs, headers)
### 3. Strategy
- Note effective encodings/paths for later, in-scope testing
### 4. Report Format
For each CONFIRMED finding:
```
FINDING:
- Title: WAF/CDN Detection Specialist at [asset/endpoint]
- Severity: Info
- CWE: CWE-200
- Endpoint: [URL/host]
- Vector: [what/where]
- Payload: [PoC / vulnerable code snippet]
- Evidence: [proof / exact code quoted]
- Impact: Informs bypass strategy; reveals origin exposure
- Remediation: Ensure origin is not directly reachable
```
## System Prompt
You are a WAF/CDN specialist. Report only positively-identified protections and any verified origin exposure, with evidence.
+100 -23
View File
@@ -3,8 +3,8 @@
mod web;
use clap::{Parser, Subcommand};
use harness::{agents, models::ModelRef, pool::ModelPool, report, types::RunConfig};
use std::path::PathBuf;
use harness::{agents, models::ModelRef, pool::ModelPool, types::RunConfig, RunOutput};
use std::path::{Path, PathBuf};
#[derive(Parser)]
#[command(name = "neurosploit", version, about = "NeuroSploit v3.4.0 — multi-model autonomous pentest harness")]
@@ -37,6 +37,24 @@ enum Cmd {
/// instead of HTTP API keys.
#[arg(long)]
subscription: bool,
/// Enable Playwright MCP (browser proof) on the subscription/CLI path.
#[arg(long)]
mcp: bool,
},
/// White-box: analyse a local repository's source code for vulnerabilities.
Whitebox {
/// Path to the repository to analyse.
path: String,
#[arg(long = "model")]
models: Vec<String>,
#[arg(long, default_value_t = 0)]
max_agents: usize,
#[arg(long, default_value_t = 2)]
vote_n: usize,
#[arg(long)]
offline: bool,
#[arg(long)]
subscription: bool,
},
/// Show agent library counts.
Agents,
@@ -88,7 +106,7 @@ async fn main() -> anyhow::Result<()> {
}
}
}
Cmd::Run { url, models, max_agents, vote_n, offline, subscription } => {
Cmd::Run { url, models, max_agents, vote_n, offline, subscription, mcp } => {
let url = if url.starts_with("http") { url } else { format!("https://{url}") };
let mut cfg = RunConfig::new(&url);
cfg.max_agents = max_agents;
@@ -98,26 +116,20 @@ async fn main() -> anyhow::Result<()> {
if !models.is_empty() {
cfg.models = models;
}
let lib = agents::load(&base);
let refs: Vec<ModelRef> = cfg.models.iter().map(|s| ModelRef::parse(s)).collect();
let pool = ModelPool::with_auth(refs, cfg.concurrency, cfg.subscription);
let (tx, mut rx) = tokio::sync::mpsc::channel::<String>(256);
let printer = tokio::spawn(async move {
while let Some(line) = rx.recv().await {
println!(" [*] {line}");
}
});
let out = harness::run(cfg.clone(), &lib, &pool, tx).await;
let _ = printer.await;
println!("\n=== {} validated finding(s) ===", out.findings.len());
println!("{}", serde_json::to_string_pretty(&out.findings)?);
let html = report::html(&url, &out.findings);
std::fs::create_dir_all(base.join("reports")).ok();
let rp = base.join("reports").join("report_rs.html");
std::fs::write(&rp, html).ok();
println!("report → {}", rp.display());
let out = run_engagement(&base, cfg, mcp, false).await?;
print_findings(&out);
}
Cmd::Whitebox { path, models, max_agents, vote_n, offline, subscription } => {
let mut cfg = RunConfig::new(&path);
cfg.max_agents = max_agents;
cfg.vote_n = vote_n;
cfg.offline = offline;
cfg.subscription = subscription;
if !models.is_empty() {
cfg.models = models;
}
let out = run_engagement(&base, cfg, false, true).await?;
print_findings(&out);
}
Cmd::Serve { port } => {
web::serve(base, port).await?;
@@ -125,3 +137,68 @@ async fn main() -> anyhow::Result<()> {
}
Ok(())
}
/// Shared engagement runner for CLI `run` / `whitebox`.
async fn run_engagement(base: &Path, mut cfg: RunConfig, mcp: bool, whitebox: bool) -> anyhow::Result<RunOutput> {
let lib = agents::load(base);
let workdir = base.join("runs").join(format!("{}-{}", sanitize(&cfg.target), now_ts()));
cfg.workdir = Some(workdir.display().to_string());
cfg.rl_path = Some(base.join("data").join("rl_state_rs.json").display().to_string());
let mcp_config = if mcp && cfg.subscription {
match harness::write_mcp_config(&workdir) {
Ok(p) => {
println!(" [*] Playwright MCP enabled → {}", p.display());
Some(p.display().to_string())
}
Err(e) => {
eprintln!(" [!] MCP config failed: {e}");
None
}
}
} else {
None
};
let refs: Vec<ModelRef> = cfg.models.iter().map(|s| ModelRef::parse(s)).collect();
let pool = ModelPool::with_auth(refs, cfg.concurrency, cfg.subscription, mcp_config);
let (tx, mut rx) = tokio::sync::mpsc::channel::<String>(256);
let printer = tokio::spawn(async move {
while let Some(line) = rx.recv().await {
println!(" [*] {line}");
}
});
let out = if whitebox {
harness::run_whitebox(cfg, &lib, &pool, tx).await
} else {
harness::run(cfg, &lib, &pool, tx).await
};
let _ = printer.await;
Ok(out)
}
fn print_findings(out: &RunOutput) {
println!("\n=== {} validated finding(s) ===", out.findings.len());
println!("{}", serde_json::to_string_pretty(&out.findings).unwrap_or_default());
if !out.artifacts.is_empty() {
println!("artifacts: {}", out.artifacts.join(", "));
}
}
fn sanitize(s: &str) -> String {
let s = s.replace("https://", "").replace("http://", "");
let mut o: String = s.chars().map(|c| if c.is_alphanumeric() { c } else { '_' }).collect();
o.truncate(50);
let o = o.trim_matches('_').to_string();
if o.is_empty() {
"target".into()
} else {
o
}
}
fn now_ts() -> u64 {
use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_secs()).unwrap_or(0)
}
+41 -4
View File
@@ -57,7 +57,7 @@ async fn info(State(st): State<Arc<AppState>>) -> Json<Value> {
.collect();
Json(json!({
"version": "3.4.0",
"agents": {"vulns": lib.vulns.len(), "meta": lib.meta.len(), "total": lib.total()},
"agents": {"vulns": lib.vulns.len(), "meta": lib.meta.len(), "recon": lib.recon.len(), "code": lib.code.len(), "total": lib.total()},
"providers": provs,
"cli_backends": harness::installed_cli_backends(),
}))
@@ -68,6 +68,8 @@ async fn agents_list(State(st): State<Arc<AppState>>) -> Json<Value> {
let v: Vec<Value> = lib
.vulns
.iter()
.chain(lib.recon.iter())
.chain(lib.code.iter())
.chain(lib.meta.iter())
.map(|a| json!({"name": a.name, "title": a.title, "cwe": a.cwe, "kind": a.kind}))
.collect();
@@ -128,6 +130,16 @@ async fn run(State(st): State<Arc<AppState>>, Json(body): Json<Value>) -> Json<V
let max_agents = body.get("max_agents").and_then(|v| v.as_u64()).unwrap_or(0) as usize;
let offline = body.get("offline").and_then(|v| v.as_bool()).unwrap_or(false);
let subscription = body.get("subscription").and_then(|v| v.as_bool()).unwrap_or(false);
let mcp = body.get("mcp").and_then(|v| v.as_bool()).unwrap_or(false);
let mode = body.get("mode").and_then(|v| v.as_str()).unwrap_or("web").to_string();
// Whitebox uses a repo path instead of URLs.
if mode == "whitebox" {
if let Some(p) = body.get("repo").and_then(|v| v.as_str()) {
if !p.trim().is_empty() {
targets = vec![p.trim().to_string()];
}
}
}
let lib = agents::load(&base);
let refs: Vec<ModelRef> = if models.is_empty() {
@@ -135,7 +147,13 @@ async fn run(State(st): State<Arc<AppState>>, Json(body): Json<Value>) -> Json<V
} else {
models.iter().map(|s| ModelRef::parse(s)).collect()
};
let pool = ModelPool::with_auth(refs, 8, subscription);
let mcp_config = if mcp && subscription {
harness::write_mcp_config(&base.join("runs").join("_mcp")).ok().map(|p| p.display().to_string())
} else {
None
};
let pool = ModelPool::with_auth(refs, 8, subscription, mcp_config);
let rl_path = base.join("data").join("rl_state_rs.json").display().to_string();
let (tx, mut rx) = tokio::sync::mpsc::channel::<String>(256);
let stf = st2.clone();
@@ -163,8 +181,14 @@ async fn run(State(st): State<Arc<AppState>>, Json(body): Json<Value>) -> Json<V
cfg.max_agents = max_agents;
cfg.offline = offline;
cfg.subscription = subscription;
let _ = tx.send(format!("=== target: {url} ===")).await;
let out = harness::run(cfg, &lib, &pool, tx.clone()).await;
cfg.rl_path = Some(rl_path.clone());
cfg.workdir = Some(base.join("runs").join(format!("{}-{}", slug(url), now_ts())).display().to_string());
let _ = tx.send(format!("=== {}: {url} ===", if mode == "whitebox" { "whitebox repo" } else { "target" })).await;
let out = if mode == "whitebox" {
harness::run_whitebox(cfg, &lib, &pool, tx.clone()).await
} else {
harness::run(cfg, &lib, &pool, tx.clone()).await
};
all_findings.extend(out.findings);
all_ran.extend(out.agents_ran);
}
@@ -197,3 +221,16 @@ async fn report_html(Path(id): Path<String>, State(st): State<Arc<AppState>>) ->
let g = st.runs.lock().unwrap();
Html(g.get(&id).and_then(|r| r.report.clone()).unwrap_or_else(|| "<h1>no report</h1>".into()))
}
fn slug(s: &str) -> String {
let s = s.replace("https://", "").replace("http://", "");
let mut o: String = s.chars().map(|c| if c.is_alphanumeric() { c } else { '_' }).collect();
o.truncate(50);
let o = o.trim_matches('_').to_string();
if o.is_empty() { "target".into() } else { o }
}
fn now_ts() -> u64 {
use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_secs()).unwrap_or(0)
}
+281 -128
View File
@@ -3,171 +3,324 @@
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>NeuroSploit v3.4.0</title>
<title>NeuroSploit</title>
<style>
:root{--bg:#080910;--bg2:#0d0f17;--panel:#13151f;--panel2:#1a1d29;--line:#252938;--text:#e7e9ee;
--muted:#878da1;--accent:#8b5cf6;--accent2:#a855f7;--cy:#22d3ee;--ok:#34d399;--warn:#fbbf24;--crit:#f87171;--high:#fb923c}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--text);font:14px/1.55 ui-sans-serif,system-ui,Segoe UI,Roboto,sans-serif;
display:grid;grid-template-columns:228px 1fr;min-height:100vh}
.side{background:linear-gradient(180deg,var(--bg2),#0a0b12);border-right:1px solid var(--line);padding:20px 14px;
display:flex;flex-direction:column;gap:4px;position:sticky;top:0;height:100vh}
.brand{display:flex;align-items:center;gap:10px;margin:2px 6px 20px}
.logo{width:34px;height:34px;border-radius:9px;background:linear-gradient(135deg,var(--accent),var(--accent2));
display:grid;place-items:center;font-weight:800;color:#fff;box-shadow:0 6px 22px rgba(139,92,246,.4)}
.brand b{font-size:15px}.brand span{color:var(--muted);font-size:11px;display:block;margin-top:-2px}
.nav{display:flex;align-items:center;gap:10px;padding:9px 12px;border-radius:9px;color:var(--muted);cursor:pointer;font-size:13.5px}
:root{
--bg:#0a0b0f;--bg2:#0e1016;--panel:#13151d;--panel2:#191c26;--panel3:#1e2230;
--line:#242838;--line2:#2e3346;--text:#eef1f6;--muted:#9aa1b4;--dim:#6b7186;
--accent:#7c5cff;--accent2:#a855f7;--cy:#2dd4bf;--ok:#34d399;--warn:#fbbf24;
--crit:#f5556d;--high:#fb923c;--med:#fbbf24;--low:#38bdf8;--info:#94a3b8;
--radius:14px;--shadow:0 16px 50px rgba(0,0,0,.5);
}
*{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}
body{background:var(--bg);color:var(--text);font:14px/1.55 ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
-webkit-font-smoothing:antialiased}
::-webkit-scrollbar{width:9px;height:9px}::-webkit-scrollbar-thumb{background:var(--line2);border-radius:9px}
.app{display:grid;grid-template-columns:240px 1fr;min-height:100vh}
/* ===== sidebar ===== */
.side{background:linear-gradient(180deg,var(--bg2),#08090d);border-right:1px solid var(--line);
padding:22px 16px;display:flex;flex-direction:column;gap:3px;position:sticky;top:0;height:100vh}
.brand{display:flex;align-items:center;gap:11px;margin:2px 6px 24px}
.logo{width:38px;height:38px;border-radius:11px;background:linear-gradient(135deg,var(--accent),var(--accent2));
display:grid;place-items:center;font-weight:800;font-size:19px;color:#fff;box-shadow:0 8px 26px rgba(124,92,255,.45)}
.brand .nm{font-size:16px;font-weight:700;letter-spacing:.2px}
.brand .vr{font-size:10.5px;color:var(--muted);margin-top:-1px}
.badge-rs{font-size:8.5px;font-weight:800;color:#1a1209;background:#e6b673;border-radius:4px;padding:1px 5px;margin-left:6px;vertical-align:middle}
.navlabel{font-size:10px;text-transform:uppercase;letter-spacing:1px;color:var(--dim);margin:16px 10px 6px}
.nav{display:flex;align-items:center;gap:11px;padding:10px 12px;border-radius:10px;color:var(--muted);
cursor:pointer;font-size:13.5px;font-weight:500;transition:.13s}
.nav .ic{width:18px;height:18px;opacity:.8;flex:none}
.nav:hover{background:var(--panel);color:var(--text)}
.nav.on{background:linear-gradient(135deg,rgba(139,92,246,.22),rgba(168,85,247,.12));color:#fff;box-shadow:inset 0 0 0 1px rgba(139,92,246,.35)}
.sf{margin-top:auto;font-size:11px;color:var(--muted);padding:10px 8px;border-top:1px solid var(--line)}
.badge-rs{display:inline-block;font-size:9px;font-weight:700;color:#fff;background:#dea584;border-radius:4px;padding:1px 5px;margin-left:6px;vertical-align:middle}
main{padding:26px 32px;max-width:1060px}
h1{font-size:20px;margin:0}.sub{color:var(--muted);font-size:12.5px}
.head{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:18px}
.view{display:none}.view.on{display:block}
.card{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:20px;margin-bottom:18px}
label{display:block;font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.5px;margin:0 0 6px}
.field{margin-bottom:15px}
input,select,textarea{width:100%;background:var(--panel2);border:1px solid var(--line);color:var(--text);border-radius:9px;
padding:10px 11px;font-size:13.5px;outline:none;font-family:inherit}
textarea{resize:vertical;min-height:70px;font-family:ui-monospace,Menlo,monospace;font-size:12.5px}
input:focus,select:focus,textarea:focus{border-color:var(--accent)}
.row{display:flex;gap:12px}.row>*{flex:1}
.mpanel{max-height:200px;overflow:auto;border:1px solid var(--line);border-radius:9px;padding:6px}
.mopt{display:flex;align-items:center;gap:8px;padding:5px 8px;border-radius:7px;font-size:12.5px;cursor:pointer}
.mopt:hover{background:var(--panel2)}.mopt input{accent-color:var(--accent);width:15px;height:15px}
.kind{font-size:9px;padding:1px 6px;border-radius:4px;background:var(--line);color:var(--muted);text-transform:uppercase}
.kind.cli{background:rgba(139,92,246,.2);color:#c4b5fd}.kind.meta{background:rgba(34,211,238,.14);color:var(--cy)}
.toggles{display:flex;gap:10px;flex-wrap:wrap;margin:6px 0 14px}
.toggle{display:flex;align-items:center;gap:8px;background:var(--panel2);border:1px solid var(--line);border-radius:9px;padding:9px 12px;cursor:pointer;font-size:12.5px}
.toggle.on{border-color:var(--accent)}.toggle input{accent-color:var(--accent)}
.btns{display:flex;gap:10px}
button{border:0;border-radius:10px;padding:11px 16px;font-size:13.5px;font-weight:600;cursor:pointer}
.run{background:linear-gradient(135deg,var(--accent),var(--accent2));color:#fff;flex:1}.run:hover{filter:brightness(1.08)}
.ghost{background:var(--panel2);color:var(--text);border:1px solid var(--line)}.ghost:hover{border-color:var(--accent)}
.nav.on{background:linear-gradient(135deg,rgba(124,92,255,.2),rgba(168,85,247,.08));color:#fff;
box-shadow:inset 0 0 0 1px rgba(124,92,255,.32)}
.nav.on .ic{opacity:1}
.side .foot{margin-top:auto;border-top:1px solid var(--line);padding-top:12px;font-size:11px;color:var(--dim)}
.stat{display:flex;justify-content:space-between;padding:3px 8px}.stat b{color:var(--text)}
/* ===== main ===== */
main{padding:0;overflow:hidden}
.topbar{display:flex;align-items:center;justify-content:space-between;padding:18px 32px;border-bottom:1px solid var(--line);
background:rgba(14,16,22,.6);backdrop-filter:blur(8px);position:sticky;top:0;z-index:5}
.topbar h1{font-size:18px;font-weight:650}.topbar .crumb{color:var(--dim);font-size:12.5px;margin-top:2px}
.chipline{display:flex;gap:8px}
.mono{font-family:ui-monospace,"SF Mono",Menlo,monospace}
.wrap{padding:28px 32px;max-width:1180px}
.view{display:none;animation:fade .25s ease}.view.on{display:block}
@keyframes fade{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.grid2{display:grid;grid-template-columns:1.55fr 1fr;gap:20px;align-items:start}
@media(max-width:980px){.app{grid-template-columns:1fr}.side{position:static;height:auto;flex-direction:row;flex-wrap:wrap}
.grid2{grid-template-columns:1fr}.navlabel{display:none}.side .foot{display:none}}
.card{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:22px;margin-bottom:20px}
.card h2{font-size:14px;font-weight:650;margin-bottom:4px;display:flex;align-items:center;gap:8px}
.card .desc{color:var(--muted);font-size:12.5px;margin-bottom:16px}
label{display:block;font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.5px;margin:0 0 7px;font-weight:600}
.field{margin-bottom:16px}
input,select,textarea{width:100%;background:var(--panel2);border:1px solid var(--line2);color:var(--text);
border-radius:10px;padding:11px 13px;font-size:13.5px;outline:none;font-family:inherit;transition:.14s}
textarea{resize:vertical;min-height:76px;font-family:ui-monospace,Menlo,monospace;font-size:12.5px}
input:focus,select:focus,textarea:focus{border-color:var(--accent);box-shadow:0 0 0 3px rgba(124,92,255,.13)}
.row{display:flex;gap:13px;flex-wrap:wrap}.row>*{flex:1;min-width:120px}
/* segmented mode switch */
.seg{display:inline-flex;background:var(--panel2);border:1px solid var(--line2);border-radius:10px;padding:3px;gap:3px;margin-bottom:18px}
.seg button{background:transparent;border:0;color:var(--muted);padding:8px 16px;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;transition:.13s}
.seg button.on{background:linear-gradient(135deg,var(--accent),var(--accent2));color:#fff}
.toggles{display:flex;gap:10px;flex-wrap:wrap;margin:4px 0 18px}
.tg{display:flex;align-items:center;gap:9px;background:var(--panel2);border:1px solid var(--line2);border-radius:10px;
padding:10px 13px;cursor:pointer;font-size:12.5px;user-select:none;transition:.13s}
.tg.on{border-color:var(--accent);background:rgba(124,92,255,.1);color:#fff}
.tg input{accent-color:var(--accent);width:15px;height:15px}
.btns{display:flex;gap:11px}
button.act{border:0;border-radius:11px;padding:12px 18px;font-size:14px;font-weight:650;cursor:pointer;transition:.13s}
.primary{background:linear-gradient(135deg,var(--accent),var(--accent2));color:#fff;flex:1;box-shadow:0 8px 22px rgba(124,92,255,.3)}
.primary:hover{filter:brightness(1.09)}
.ghost{background:var(--panel2);color:var(--text);border:1px solid var(--line2)}.ghost:hover{border-color:var(--accent)}
button:disabled{opacity:.5;cursor:not-allowed}
.pill{display:inline-flex;gap:5px;background:var(--panel2);border:1px solid var(--line);border-radius:999px;padding:4px 11px;font-size:11.5px;color:var(--muted);margin-right:7px}
.pill b{color:var(--text)}
.term{background:#06070c;border:1px solid var(--line);border-radius:11px;padding:13px 15px;margin-top:14px;
font:12px/1.6 ui-monospace,Menlo,monospace;max-height:260px;overflow:auto;white-space:pre-wrap;color:#cbd3e6;display:none}
.term .h{color:var(--accent2)}.term .ok{color:var(--ok)}.term .e{color:var(--crit)}.term .v{color:var(--cy)}
.sevrow{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0;display:none}
.sev{border-radius:8px;padding:5px 11px;font-size:12px;font-weight:700}
.sev.Critical{background:rgba(248,113,113,.16);color:var(--crit)}.sev.High{background:rgba(251,146,60,.16);color:var(--high)}
.sev.Medium{background:rgba(251,191,36,.15);color:var(--warn)}.sev.Low{background:rgba(34,211,238,.14);color:var(--cy)}
.sev.none{background:rgba(52,211,153,.13);color:var(--ok)}
.find{border:1px solid var(--line);border-radius:11px;padding:14px;margin:10px 0;background:var(--panel2)}
.find h4{margin:0 0 5px;font-size:14px}.find .m{color:var(--muted);font-size:12px}
.find pre{background:#06070c;border-radius:7px;padding:9px;font-size:11.5px;overflow:auto;margin:7px 0}
.alist{max-height:430px;overflow:auto;border:1px solid var(--line);border-radius:10px}
.arow{display:flex;gap:10px;padding:9px 13px;border-bottom:1px solid var(--line);font-size:13px;align-items:center}
.arow:last-child{border:0}.arow code{color:var(--accent2)}.arow .t{color:var(--muted);margin-left:auto;font-size:11.5px}
.muted{color:var(--muted);font-size:12.5px}a{color:var(--accent2)}
.dl{display:inline-flex;gap:7px;background:var(--panel2);border:1px solid var(--line);border-radius:8px;padding:8px 13px;text-decoration:none;color:var(--text);font-size:12.5px}
.dl:hover{border-color:var(--accent)}iframe{width:100%;height:520px;border:1px solid var(--line);border-radius:10px;background:#fff;margin-top:12px}
.chip{display:inline-flex;align-items:center;gap:6px;background:var(--panel2);border:1px solid var(--line2);
border-radius:999px;padding:4px 11px;font-size:11.5px;color:var(--muted)}
.chip b{color:var(--text)}.dot{width:7px;height:7px;border-radius:50%;background:var(--ok);box-shadow:0 0 8px var(--ok)}
/* model panel */
.mpanel{max-height:230px;overflow:auto;border:1px solid var(--line2);border-radius:10px;padding:5px;background:var(--bg2)}
.mopt{display:flex;align-items:center;gap:9px;padding:7px 9px;border-radius:8px;font-size:12.5px;cursor:pointer}
.mopt:hover{background:var(--panel2)}.mopt input{accent-color:var(--accent)}
.tag{font-size:9px;font-weight:700;padding:2px 6px;border-radius:5px;text-transform:uppercase;letter-spacing:.4px}
.tag.cli{background:rgba(124,92,255,.2);color:#c4b5fd}.tag.api{background:rgba(45,212,191,.15);color:var(--cy)}
.tag.meta{background:rgba(45,212,191,.15);color:var(--cy)}.tag.recon{background:rgba(56,189,248,.16);color:var(--low)}
.tag.code{background:rgba(251,146,60,.16);color:var(--high)}.tag.vuln{background:rgba(245,85,109,.15);color:var(--crit)}
/* console */
.term{background:#070810;border:1px solid var(--line2);border-radius:12px;padding:14px 16px;
font:12px/1.65 ui-monospace,Menlo,monospace;max-height:340px;overflow:auto;white-space:pre-wrap;color:#c3cad8;min-height:120px}
.term .h{color:var(--accent2);font-weight:600}.term .ok{color:var(--ok)}.term .e{color:var(--crit)}
.term .v{color:var(--cy)}.term .s{color:var(--warn)}
.term .empty{color:var(--dim)}
/* findings */
.sevbar{display:flex;gap:9px;flex-wrap:wrap;margin-bottom:14px}
.scount{display:flex;flex-direction:column;align-items:center;background:var(--panel2);border:1px solid var(--line2);
border-radius:10px;padding:9px 16px;min-width:74px}
.scount .n{font-size:20px;font-weight:750}.scount .l{font-size:10px;text-transform:uppercase;letter-spacing:.5px;color:var(--muted)}
.scount.Critical .n{color:var(--crit)}.scount.High .n{color:var(--high)}.scount.Medium .n{color:var(--med)}
.scount.Low .n{color:var(--low)}.scount.Info .n{color:var(--info)}
.find{border:1px solid var(--line2);border-left-width:3px;border-radius:11px;padding:15px 17px;margin:11px 0;background:var(--panel2)}
.find.Critical{border-left-color:var(--crit)}.find.High{border-left-color:var(--high)}.find.Medium{border-left-color:var(--med)}
.find.Low{border-left-color:var(--low)}.find.Info{border-left-color:var(--info)}
.find h4{font-size:14px;margin-bottom:5px;display:flex;align-items:center;gap:9px}
.sev{font-size:10px;font-weight:700;padding:3px 8px;border-radius:6px;text-transform:uppercase}
.sev.Critical{background:rgba(245,85,109,.18);color:var(--crit)}.sev.High{background:rgba(251,146,60,.18);color:var(--high)}
.sev.Medium{background:rgba(251,191,36,.16);color:var(--med)}.sev.Low{background:rgba(56,189,248,.16);color:var(--low)}
.sev.Info{background:rgba(148,163,184,.16);color:var(--info)}
.find .m{color:var(--muted);font-size:11.5px;margin-bottom:6px}
.find pre{background:#070810;border:1px solid var(--line);border-radius:8px;padding:10px;font-size:11.5px;overflow:auto;margin:7px 0}
.empty-state{text-align:center;color:var(--dim);padding:36px 10px;font-size:13px}
/* agents list */
.toolbar{display:flex;gap:10px;margin-bottom:14px;flex-wrap:wrap}.toolbar input{flex:1;min-width:160px}
.fbtn{background:var(--panel2);border:1px solid var(--line2);color:var(--muted);border-radius:8px;padding:8px 13px;font-size:12px;cursor:pointer;font-weight:600}
.fbtn.on{border-color:var(--accent);color:#fff;background:rgba(124,92,255,.12)}
.alist{max-height:560px;overflow:auto;border:1px solid var(--line2);border-radius:11px}
.arow{display:flex;gap:11px;padding:10px 14px;border-bottom:1px solid var(--line);font-size:13px;align-items:center}
.arow:last-child{border:0}.arow:hover{background:var(--panel2)}.arow code{color:var(--accent2);font-size:12.5px}
.arow .t{color:var(--muted);margin-left:auto;font-size:11.5px;text-align:right}
.muted{color:var(--muted)}.dim{color:var(--dim)}a{color:var(--accent2);text-decoration:none}a:hover{text-decoration:underline}
.dl{display:inline-flex;gap:8px;background:var(--panel2);border:1px solid var(--line2);border-radius:9px;padding:9px 14px;margin:0 9px 9px 0;color:var(--text);font-size:12.5px}
.dl:hover{border-color:var(--accent);text-decoration:none}
iframe{width:100%;height:560px;border:1px solid var(--line2);border-radius:11px;background:#fff;margin-top:12px}
.mcard{padding:14px 16px;border:1px solid var(--line2);border-radius:11px;margin-bottom:11px;background:var(--panel2)}
.mcard h3{font-size:13.5px;margin-bottom:7px;display:flex;align-items:center;gap:8px}
.keyrow{display:flex;align-items:center;gap:8px;margin:5px 0;font-size:12px;color:var(--muted)}
.progress{height:3px;background:var(--line);border-radius:3px;overflow:hidden;margin-top:14px;display:none}
.progress.on{display:block}.progress .bar{height:100%;width:30%;background:linear-gradient(90deg,var(--accent),var(--accent2));
border-radius:3px;animation:slide 1.1s infinite ease-in-out}
@keyframes slide{0%{margin-left:-30%}100%{margin-left:100%}}
</style>
</head>
<body>
<div class="app">
<aside class="side">
<div class="brand"><div class="logo">N</div><div><b>NeuroSploit<span class="badge-rs">RUST</span></b><span>v3.4.0 · Multi-Model Harness</span></div></div>
<div class="nav on" data-v="run">▶ Run</div>
<div class="nav" data-v="agents">⛓ Agents</div>
<div class="nav" data-v="models">🧠 Models</div>
<div class="nav" data-v="reports">📄 Report</div>
<div class="sf" id="sf">loading…</div>
<div class="brand"><div class="logo">N</div><div><div class="nm">NeuroSploit<span class="badge-rs">RUST</span></div><div class="vr">v3.4.0 · Multi-Model Harness</div></div></div>
<div class="navlabel">Operate</div>
<div class="nav on" data-v="run"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="5 3 19 12 5 21 5 3"/></svg> Engagement</div>
<div class="nav" data-v="findings"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg> Findings</div>
<div class="nav" data-v="report"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg> Report</div>
<div class="navlabel">Library</div>
<div class="nav" data-v="agents"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M12 1v6m0 6v6m11-7h-6m-6 0H1"/></svg> Agents</div>
<div class="nav" data-v="models"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M9 9h6v6H9z"/></svg> Models</div>
<div class="foot">
<div class="stat"><span>Agents</span><b id="sf-agents"></b></div>
<div class="stat"><span>Providers</span><b id="sf-prov"></b></div>
<div class="stat"><span>Backends</span><b id="sf-cli"></b></div>
</div>
</aside>
<main>
<section class="view on" id="v-run">
<div class="head"><div><h1>Run engagement</h1><div class="sub">Parallel agents · provider failover · N-model validator voting.</div></div></div>
<div class="card">
<div class="field"><label>Targets (one URL per line)</label><textarea id="targets" placeholder="https://target-one.example&#10;https://target-two.example"></textarea></div>
<div class="field"><label>Model panel (1st = primary · others fail over &amp; vote)</label><div class="mpanel" id="mpanel"></div></div>
<div class="row">
<div class="field"><label>Validator votes (N)</label><input id="voten" type="number" value="3" min="1" max="9"/></div>
<div class="field"><label>Max agents (0 = all)</label><input id="maxa" type="number" value="8" min="0"/></div>
</div>
<div class="toggles">
<label class="toggle on" id="t-off"><input type="checkbox" id="offline" checked/> Offline (pipeline self-test)</label>
<label class="toggle" id="t-sub"><input type="checkbox" id="subscription"/> Use subscription (Claude/Codex login)</label>
</div>
<div class="btns"><button class="run" id="go">▶ Run harness</button></div>
<div class="term" id="term"></div>
<div class="sevrow" id="sevrow"></div>
<div id="findings"></div>
<div class="topbar">
<div><h1 id="bar-title">Engagement</h1><div class="crumb" id="bar-crumb">Configure and launch an autonomous run</div></div>
<div class="chipline"><span class="chip"><span class="dot"></span> <b>online</b></span><span class="chip mono" id="chip-models"></span></div>
</div>
<!-- ENGAGEMENT -->
<section class="view on" id="v-run"><div class="wrap">
<div class="seg" id="modeSeg">
<button class="on" data-m="web">🌐 Black-box (URL)</button>
<button data-m="whitebox">📦 White-box (repo)</button>
</div>
</section>
<section class="view" id="v-agents">
<div class="head"><div><h1>Agent library</h1><div class="sub" id="agentsub"></div></div></div>
<div class="card"><div class="field"><input id="asearch" placeholder="🔎 filter agents"/></div><div class="alist" id="alist"></div></div>
</section>
<section class="view" id="v-models">
<div class="head"><div><h1>Models</h1><div class="sub">OpenAI-compatible providers — CLI &amp; API.</div></div></div>
<div class="card" id="modelcard"></div>
</section>
<section class="view" id="v-reports">
<div class="head"><div><h1>Report</h1><div class="sub">Last engagement.</div></div></div>
<div class="card" id="reportcard"><span class="muted">Run an engagement to generate a report.</span></div>
</section>
<div class="grid2">
<div>
<div class="card">
<h2>Target</h2>
<div class="desc" id="targetDesc">One or more URLs — the harness recons each, then intelligently selects matching agents.</div>
<div class="field" id="urlField"><label>Targets (one per line)</label><textarea id="targets" placeholder="https://target-one.example&#10;https://target-two.example"></textarea></div>
<div class="field" id="repoField" style="display:none"><label>Repository path (local)</label><input id="repo" placeholder="/path/to/repo"/></div>
<div class="row">
<div class="field"><label>Validator votes (N)</label><input id="voten" type="number" value="3" min="1" max="9"/></div>
<div class="field"><label>Max agents (0 = all)</label><input id="maxa" type="number" value="0" min="0"/></div>
</div>
<div class="toggles">
<label class="tg on" id="tg-off"><input type="checkbox" id="offline" checked/> Offline self-test</label>
<label class="tg" id="tg-sub"><input type="checkbox" id="subscription"/> Subscription (Claude/Codex/Gemini login)</label>
<label class="tg" id="tg-mcp"><input type="checkbox" id="mcp"/> Playwright MCP</label>
</div>
<div class="btns"><button class="act primary" id="go">▶ Launch engagement</button></div>
<div class="progress" id="prog"><div class="bar"></div></div>
</div>
</div>
<div>
<div class="card">
<h2>Model panel</h2>
<div class="desc">1st = primary · others fail over &amp; form the validator jury.</div>
<div class="mpanel" id="mpanel"></div>
</div>
</div>
</div>
<div class="card">
<h2>Live execution</h2>
<div class="desc">Recon → intelligent agent selection → parallel exploitation → N-model voting → report. Artifacts saved to <span class="mono">runs/</span>.</div>
<div class="term" id="term"><span class="empty">— idle. Launch an engagement to stream activity. —</span></div>
</div>
</div></section>
<!-- FINDINGS -->
<section class="view" id="v-findings"><div class="wrap">
<div class="card">
<h2>Validated findings</h2>
<div class="desc">Only findings confirmed by multi-model adversarial voting appear here.</div>
<div class="sevbar" id="sevbar"></div>
<div id="findings"><div class="empty-state">No findings yet — run an engagement.</div></div>
</div>
</div></section>
<!-- REPORT -->
<section class="view" id="v-report"><div class="wrap">
<div class="card">
<h2>Report</h2>
<div class="desc">HTML report + JSON/MD artifacts for reuse by other tools/AIs.</div>
<div id="reportcard"><div class="empty-state">Run an engagement to generate a report.</div></div>
</div>
</div></section>
<!-- AGENTS -->
<section class="view" id="v-agents"><div class="wrap">
<div class="card">
<h2>Agent library</h2>
<div class="desc" id="agentsub"></div>
<div class="toolbar">
<input id="asearch" placeholder="🔎 filter by name / title / CWE"/>
<button class="fbtn on" data-k="all">All</button>
<button class="fbtn" data-k="vuln">Vuln</button>
<button class="fbtn" data-k="recon">Recon</button>
<button class="fbtn" data-k="code">Code</button>
<button class="fbtn" data-k="meta">Meta</button>
</div>
<div class="alist" id="alist"></div>
</div>
</div></section>
<!-- MODELS -->
<section class="view" id="v-models"><div class="wrap">
<div class="card">
<h2>Providers &amp; models</h2>
<div class="desc">Use via <b>API</b> key or <b>subscription</b> (local CLI login). CLI-capable providers are tagged.</div>
<div id="modelcard"></div>
</div>
</div></section>
</main>
</div>
<script>
const $=s=>document.querySelector(s),$$=s=>[...document.querySelectorAll(s)];
let INFO=null,AGENTS=[],lastRun=null;
let INFO=null,AGENTS=[],lastRun=null,mode='web',filter='all';
const TITLES={run:['Engagement','Configure and launch an autonomous run'],findings:['Findings','Validated, multi-model-confirmed results'],
report:['Report','Generated deliverables & artifacts'],agents:['Agents','The markdown agent library'],models:['Models','Providers, models & auth']};
$$('.nav').forEach(n=>n.onclick=()=>{$$('.nav').forEach(x=>x.classList.remove('on'));n.classList.add('on');
$$('.view').forEach(v=>v.classList.remove('on'));$('#v-'+n.dataset.v).classList.add('on');});
$('#t-off').querySelector('input').onchange=e=>$('#t-off').classList.toggle('on',e.target.checked);
$('#t-sub').querySelector('input').onchange=e=>$('#t-sub').classList.toggle('on',e.target.checked);
$$('.view').forEach(v=>v.classList.remove('on'));$('#v-'+n.dataset.v).classList.add('on');
const t=TITLES[n.dataset.v];$('#bar-title').textContent=t[0];$('#bar-crumb').textContent=t[1];});
// mode switch
$$('#modeSeg button').forEach(b=>b.onclick=()=>{$$('#modeSeg button').forEach(x=>x.classList.remove('on'));b.classList.add('on');
mode=b.dataset.m;const wb=mode==='whitebox';
$('#urlField').style.display=wb?'none':'';$('#repoField').style.display=wb?'':'none';
$('#targetDesc').textContent=wb?'A local repository path — code agents review the source for vulnerabilities.':'One or more URLs — the harness recons each, then intelligently selects matching agents.';});
// toggles
['off','sub','mcp'].forEach(k=>{const map={off:'offline',sub:'subscription',mcp:'mcp'};
$('#'+map[k]).onchange=e=>$('#tg-'+k).classList.toggle('on',e.target.checked);});
async function init(){
INFO=await (await fetch('/api/info')).json();
$('#sf').textContent=`${INFO.agents.total} agents · ${INFO.providers.length} providers`;
$('#agentsub').textContent=`${INFO.agents.vulns} vuln specialists · ${INFO.agents.meta} meta-agents`;
// model panel
const a=INFO.agents;
$('#sf-agents').textContent=a.total;$('#sf-prov').textContent=INFO.providers.length;
$('#sf-cli').textContent=(INFO.cli_backends||[]).length;
$('#chip-models').textContent=(INFO.cli_backends||[]).join(' · ')||'api-only';
$('#agentsub').textContent=`${a.vulns} vuln · ${a.recon||0} recon · ${a.code||0} code · ${a.meta} meta — ${a.total} total`;
let mh='',first=true;
INFO.providers.forEach(p=>p.models.forEach(m=>{const id=p.key+':'+m;
mh+=`<label class="mopt"><input type="checkbox" value="${id}" ${first?'checked':''}/> <span class="kind ${p.kind}">${p.kind}</span> <code>${id}</code></label>`;first=false;}));
mh+=`<label class="mopt"><input type="checkbox" value="${id}" ${first?'checked':''}/> <span class="tag ${p.kind}">${p.kind}</span> <code>${id}</code></label>`;first=false;}));
$('#mpanel').innerHTML=mh;
// models tab
$('#modelcard').innerHTML=INFO.providers.map(p=>`<div style="margin-bottom:12px"><b>${p.label}</b> <span class="kind ${p.kind}">${p.kind}</span>
<div class="muted" style="margin-top:4px">${p.models.map(m=>'<code>'+m+'</code>').join(' · ')}</div></div>`).join('');
$('#modelcard').innerHTML=INFO.providers.map(p=>`<div class="mcard"><h3><span class="tag ${p.kind}">${p.kind}</span> ${p.label}
${(INFO.cli_backends||[]).some(b=>['claude','codex','grok','gemini'].includes(b))&&p.kind==='cli'?'<span class="dim" style="font-size:11px">· subscription-capable</span>':''}</h3>
<div class="muted" style="font-size:12px">${p.models.map(m=>'<code>'+m+'</code>').join(' · ')}</div></div>`).join('');
AGENTS=(await (await fetch('/api/agents')).json()).agents;renderAgents();
}
function selectedModels(){return $$('#mpanel input:checked').map(i=>i.value);}
function logLine(t){const T=$('#term');T.style.display='block';const d=document.createElement('div');
d.className=t.startsWith('===')?'h':t.startsWith('vote')&&t.includes('CONFIRMED')?'ok':t.includes('failed')||t.startsWith('ERROR')?'e':t.startsWith('recon')||t.startsWith('exploit')?'v':'';
d.textContent=t;T.appendChild(d);T.scrollTop=T.scrollHeight;}
function logLine(t){const T=$('#term');if(T.querySelector('.empty'))T.innerHTML='';const d=document.createElement('div');
d.className=t.startsWith('===')?'h':t.includes('CONFIRMED')||t.includes('validated finding')||t.includes('updated')||t.startsWith('artifacts')?'ok':
t.includes('failed')||t.startsWith('ERROR')?'e':t.startsWith('recon')||t.startsWith('exploit')||t.startsWith('analyze')||t.startsWith('intelligently')||t.startsWith('agent selection')?'v':
t.startsWith('selected')||t.includes('candidate')?'s':'';
d.textContent=' '+t;T.appendChild(d);T.scrollTop=T.scrollHeight;}
let seen=0;
async function run(){
const targets=$('#targets').value.split('\n').map(s=>s.trim()).filter(Boolean);
if(!targets.length){$('#targets').focus();$('#targets').style.borderColor='var(--crit)';return;}
$('#go').disabled=true;$('#term').innerHTML='';$('#sevrow').style.display='none';$('#findings').innerHTML='';
const body={targets,models:selectedModels(),vote_n:+$('#voten').value,max_agents:+$('#maxa').value,offline:$('#offline').checked,subscription:$('#subscription').checked};
logLine('Queued '+targets.length+' target(s) · panel: '+(body.models.join(', ')||'default'));
let body={models:selectedModels(),vote_n:+$('#voten').value,max_agents:+$('#maxa').value,
offline:$('#offline').checked,subscription:$('#subscription').checked,mcp:$('#mcp').checked,mode};
if(mode==='whitebox'){const r=$('#repo').value.trim();if(!r){$('#repo').focus();$('#repo').style.borderColor='var(--crit)';return;}body.repo=r;}
else{const t=$('#targets').value.split('\n').map(s=>s.trim()).filter(Boolean);if(!t.length){$('#targets').focus();$('#targets').style.borderColor='var(--crit)';return;}body.targets=t;}
$('#go').disabled=true;$('#prog').classList.add('on');$('#term').innerHTML='';seen=0;
logLine((mode==='whitebox'?'White-box repo: '+body.repo:'Black-box targets: '+body.targets.length)+' · panel: '+(body.models.join(', ')||'default'));
const r=await (await fetch('/api/run',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)})).json();
if(r.error){logLine('ERROR: '+r.error);$('#go').disabled=false;return;}
if(r.error){logLine('ERROR: '+r.error);$('#go').disabled=false;$('#prog').classList.remove('on');return;}
poll(r.run_id);
}
async function poll(id){
const st=await (await fetch('/api/status/'+id)).json();
(st.log||[]).slice(seen).forEach(logLine);seen=(st.log||[]).length;
if(!st.done){setTimeout(()=>poll(id),600);return;}
seen=0;$('#go').disabled=false;lastRun=id;render(st.result||{});
$('#go').disabled=false;$('#prog').classList.remove('on');lastRun=id;render(st.result||{});
logLine('done.');
}
function render(res){
const sr=$('#sevrow');sr.style.display='flex';
const f=res.findings||[],by={};f.forEach(x=>by[x.severity]=(by[x.severity]||0)+1);
sr.innerHTML=f.length?Object.entries(by).map(([k,v])=>`<span class="sev ${k}">${k}: ${v}</span>`).join('')
:`<span class="sev none">✓ complete — ${(res.agents_ran||[]).length} agents ran, 0 validated findings</span>`;
$('#findings').innerHTML=f.map(x=>`<div class="find"><h4><span class="sev ${x.severity}" style="font-size:11px">${x.severity}</span> ${x.title||''}</h4>
<div class="m">${x.agent||''} · ${x.cwe||''} · votes ${x.votes||'-'} · conf ${(x.confidence||0).toFixed(2)} · ${x.endpoint||''}</div>
${x.payload?`<pre>${(x.payload+'').replace(/</g,'&lt;')}</pre>`:''}${x.evidence?`<div class="m">Evidence: ${x.evidence}</div>`:''}</div>`).join('');
const rc=$('#reportcard');
rc.innerHTML=`<a class="dl" href="/report/${lastRun}" target="_blank">⬇ open HTML report</a><iframe src="/report/${lastRun}"></iframe>`;
const f=res.findings||[],by={Critical:0,High:0,Medium:0,Low:0,Info:0};f.forEach(x=>by[x.severity]=(by[x.severity]||0)+1);
$('#sevbar').innerHTML=Object.entries(by).map(([k,v])=>`<div class="scount ${k}"><span class="n">${v}</span><span class="l">${k}</span></div>`).join('');
$('#findings').innerHTML=f.length?f.map(x=>`<div class="find ${x.severity}"><h4><span class="sev ${x.severity}">${x.severity}</span> ${esc(x.title||'')}</h4>
<div class="m mono">${esc(x.agent||'')} · ${esc(x.cwe||'')} · votes ${esc(x.votes||'-')} · conf ${(x.confidence||0).toFixed(2)} · ${esc(x.endpoint||'')}</div>
${x.payload?`<pre>${esc(x.payload)}</pre>`:''}${x.evidence?`<div class="m">Evidence: ${esc(x.evidence)}</div>`:''}
${x.remediation?`<div class="m">Fix: ${esc(x.remediation)}</div>`:''}</div>`).join('')
:`<div class="empty-state">✓ Run complete — ${(res.agents_ran||[]).length} agents ran, 0 validated findings.<br><span class="dim">${$('#offline').checked?'Offline mode performs no exploitation — enable a model (API key or subscription) to find issues.':''}</span></div>`;
$('#reportcard').innerHTML=`<a class="dl" href="/report/${lastRun}" target="_blank">⬇ Open HTML report</a><iframe src="/report/${lastRun}"></iframe>`;
}
function esc(s){return (s+'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');}
$$('.fbtn').forEach(b=>b.onclick=()=>{$$('.fbtn').forEach(x=>x.classList.remove('on'));b.classList.add('on');filter=b.dataset.k;renderAgents();});
function renderAgents(){const q=$('#asearch').value.toLowerCase();
$('#alist').innerHTML=AGENTS.filter(a=>!q||(a.name+a.title+a.cwe).toLowerCase().includes(q)).slice(0,400)
.map(a=>`<div class="arow"><span class="kind ${a.kind}">${a.kind}</span> <code>${a.name}</code> <span class="t">${(a.title||'').replace(' Agent','')} ${a.cwe?'· '+a.cwe:''}</span></div>`).join('')||'<div class="arow muted">no match</div>';}
const rows=AGENTS.filter(a=>(filter==='all'||a.kind===filter)&&(!q||(a.name+a.title+a.cwe).toLowerCase().includes(q)));
$('#alist').innerHTML=rows.slice(0,500).map(a=>`<div class="arow"><span class="tag ${a.kind}">${a.kind}</span> <code>${a.name}</code>
<span class="t">${esc((a.title||'').replace(' Agent',''))} ${a.cwe?'· '+a.cwe:''}</span></div>`).join('')||'<div class="arow muted">no match</div>';}
$('#asearch').oninput=renderAgents;
$('#go').onclick=run;$('#targets').oninput=()=>$('#targets').style.borderColor='';
$('#go').onclick=run;
$('#targets').oninput=()=>$('#targets').style.borderColor='';$('#repo').oninput=()=>$('#repo').style.borderColor='';
init();
</script>
</body>
+6 -2
View File
@@ -21,20 +21,24 @@ pub struct Agent {
pub struct Library {
pub vulns: Vec<Agent>,
pub meta: Vec<Agent>,
pub recon: Vec<Agent>,
pub code: Vec<Agent>,
}
impl Library {
pub fn total(&self) -> usize {
self.vulns.len() + self.meta.len()
self.vulns.len() + self.meta.len() + self.recon.len() + self.code.len()
}
}
/// Load `<base>/agents_md/{vulns,meta}/*.md`.
/// Load `<base>/agents_md/{vulns,meta,recon,code}/*.md`.
pub fn load(base: &Path) -> Library {
let root = base.join("agents_md");
Library {
vulns: load_dir(&root.join("vulns"), "vuln"),
meta: load_dir(&root.join("meta"), "meta"),
recon: load_dir(&root.join("recon"), "recon"),
code: load_dir(&root.join("code"), "code"),
}
}
+4 -1
View File
@@ -11,12 +11,15 @@ pub mod models;
pub mod pipeline;
pub mod pool;
pub mod report;
pub mod rl;
pub mod types;
pub use agents::{Agent, Library};
pub use models::{
cli_binary_for, installed_cli_backends, provider_for, providers, ChatClient, ModelRef, Provider,
cli_binary_for, installed_cli_backends, provider_for, providers, write_mcp_config, ChatClient,
ModelRef, Provider,
};
pub use pipeline::{run_whitebox, RunOutput};
pub use pipeline::run;
pub use pool::ModelPool;
pub use types::{Finding, RunConfig};
+47 -5
View File
@@ -28,6 +28,8 @@ pub fn providers() -> Vec<Provider> {
models: vec!["gpt-5.1", "o4"] },
Provider { key: "xai", label: "xAI Grok", base_url: "https://api.x.ai/v1", env_key: "XAI_API_KEY", kind: "cli",
models: vec!["grok-4", "grok-4-fast"] },
Provider { key: "gemini", label: "Google Gemini", base_url: "https://generativelanguage.googleapis.com/v1beta/openai", env_key: "GEMINI_API_KEY", kind: "cli",
models: vec!["gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.0-flash"] },
Provider { key: "nvidia_nim", label: "NVIDIA NIM", base_url: "https://integrate.api.nvidia.com/v1", env_key: "NVIDIA_NIM_API_KEY", kind: "api",
models: vec!["nvidia/llama-3.3-nemotron-super-49b-v1", "deepseek-ai/deepseek-r1", "qwen/qwen2.5-coder-32b-instruct"] },
Provider { key: "deepseek", label: "DeepSeek", base_url: "https://api.deepseek.com/v1", env_key: "DEEPSEEK_API_KEY", kind: "api",
@@ -124,9 +126,20 @@ impl ChatClient {
impl ChatClient {
/// Complete via a locally-installed **agentic CLI subscription** (Claude
/// Code / Codex / Grok) instead of an API key. This uses the user's logged-in
/// subscription, so no provider key is required.
pub async fn chat_cli(&self, provider: &str, model: &str, system: &str, user: &str) -> Result<String> {
/// Code / Codex / Grok / Gemini) instead of an API key. This uses the user's
/// logged-in subscription, so no provider key is required.
///
/// When `mcp_config` is set (a path to an `.mcp.json`), Claude/Codex run with
/// the MCP servers enabled and tool autonomy, so agents can actually drive
/// **Playwright** (browse, execute JS, screenshot) during execution.
pub async fn chat_cli(
&self,
provider: &str,
model: &str,
system: &str,
user: &str,
mcp_config: Option<&str>,
) -> Result<String> {
let bin = cli_binary_for(provider)
.ok_or_else(|| anyhow!("no CLI/subscription backend for provider '{}'", provider))?;
let prompt = format!("{system}\n\n{user}");
@@ -135,10 +148,24 @@ impl ChatClient {
// Claude Code headless print mode (uses the Claude subscription login).
"claude" => {
cmd.arg("-p").arg("--model").arg(model);
if let Some(mcp) = mcp_config {
cmd.arg("--mcp-config").arg(mcp).arg("--dangerously-skip-permissions");
// Required to allow tool autonomy when running as root.
cmd.env("IS_SANDBOX", "1");
}
}
// Codex non-interactive exec (uses the ChatGPT/Codex login), prompt on stdin.
"codex" => {
cmd.arg("exec").arg("--model").arg(model).arg("-");
cmd.arg("exec").arg("--model").arg(model);
if let Some(mcp) = mcp_config {
cmd.arg("--config").arg(format!("mcp_config_file={mcp}"))
.arg("--dangerously-bypass-approvals-and-sandbox");
}
cmd.arg("-");
}
// Google Gemini CLI (uses the Gemini subscription login).
"gemini" => {
cmd.arg("-m").arg(model);
}
// Grok CLI, prompt on stdin (best-effort flags).
"grok" => {
@@ -166,6 +193,7 @@ pub fn cli_binary_for(provider: &str) -> Option<&'static str> {
"anthropic" => Some("claude"),
"openai" => Some("codex"),
"xai" => Some("grok"),
"gemini" => Some("gemini"),
_ => None,
}
}
@@ -179,7 +207,21 @@ pub fn binary_in_path(name: &str) -> bool {
/// Which subscription CLI backends are installed locally.
pub fn installed_cli_backends() -> Vec<&'static str> {
["claude", "codex", "grok"].into_iter().filter(|b| binary_in_path(b)).collect()
["claude", "codex", "grok", "gemini"].into_iter().filter(|b| binary_in_path(b)).collect()
}
/// Write a Playwright `.mcp.json` into `dir` and return its path, so the agentic
/// CLI can drive a real browser (DOM/JS/network/screenshots) during execution.
pub fn write_mcp_config(dir: &std::path::Path) -> std::io::Result<std::path::PathBuf> {
std::fs::create_dir_all(dir)?;
let path = dir.join(".mcp.json");
let cfg = r#"{
"mcpServers": {
"playwright": { "command": "npx", "args": ["-y", "@playwright/mcp@latest", "--headless", "--isolated"] }
}
}"#;
std::fs::write(&path, cfg)?;
Ok(path)
}
impl Default for ChatClient {
+258 -42
View File
@@ -1,8 +1,11 @@
use crate::agents::{Agent, Library};
use crate::pool::ModelPool;
use crate::rl::{severity_reward, RlState};
use crate::types::{Finding, RunConfig};
use crate::report;
use futures::stream::{self, StreamExt};
use serde::Serialize;
use std::path::{Path, PathBuf};
use tokio::sync::mpsc::Sender;
/// Result of an engagement run.
@@ -11,26 +14,28 @@ pub struct RunOutput {
pub findings: Vec<Finding>,
pub agents_ran: Vec<String>,
pub candidates: usize,
pub recon: String,
/// Paths to persisted artifacts (recon/exploit/findings/report), if any.
pub artifacts: Vec<String>,
}
const RECON_SYS: &str = "You are a web recon specialist. Map the target's attack surface and reply with a compact JSON object (tech, endpoints, auth, apis, ai_features). No prose.";
const VOTE_SYS: &str = "You are an adversarial security validator. Decide if the candidate finding is a REAL, reproducible, exploitable vulnerability with proof. Reply with JSON {\"verdict\":\"confirmed\"|\"rejected\",\"reason\":\"...\"}. Default to rejected when uncertain.";
const CODE_VOTE_SYS: &str = "You are an adversarial source-code reviewer. Decide if the reported issue is a REAL vulnerability in the provided code (reachable, exploitable, not a false positive). Reply JSON {\"verdict\":\"confirmed\"|\"rejected\",\"reason\":\"...\"}.";
/// Run the full harness pipeline, streaming human-readable progress over `tx`.
/// Black-box web engagement: recon → parallel exploit → N-model vote → report.
pub async fn run(cfg: RunConfig, lib: &Library, pool: &ModelPool, tx: Sender<String>) -> RunOutput {
let _ = tx
.send(format!(
"Loaded {} agents ({} vuln / {} meta) · models: {} · vote_n={} · concurrency={}",
lib.total(),
lib.vulns.len(),
lib.meta.len(),
"Loaded {} agents ({} vuln / {} recon / {} code / {} meta) · models: {} · vote_n={} · concurrency={}{}",
lib.total(), lib.vulns.len(), lib.recon.len(), lib.code.len(), lib.meta.len(),
pool.candidates.iter().map(|m| m.label()).collect::<Vec<_>>().join(", "),
cfg.vote_n,
cfg.concurrency,
cfg.vote_n, cfg.concurrency,
if pool.mcp_config.is_some() { " · Playwright MCP ON" } else { "" },
))
.await;
// ---- 1. Recon -------------------------------------------------------
// ---- 1. Recon ------------------------------------------------------
let recon = if cfg.offline {
let _ = tx.send("recon: offline mode — skipping model calls".into()).await;
"{}".to_string()
@@ -47,23 +52,42 @@ pub async fn run(cfg: RunConfig, lib: &Library, pool: &ModelPool, tx: Sender<Str
}
};
// ---- 2. Select agents ----------------------------------------------
let cap = if cfg.max_agents > 0 { cfg.max_agents } else { lib.vulns.len() };
let selected: Vec<Agent> = lib.vulns.iter().take(cap).cloned().collect();
let _ = tx.send(format!("selected {} specialist agents", selected.len())).await;
// ---- 2. Intelligent, RL-ranked agent selection ---------------------
let mut rl = cfg.rl_path.as_ref().map(|p| RlState::load(Path::new(p))).unwrap_or_default();
let mut ranked: Vec<Agent> = lib.vulns.clone();
ranked.sort_by(|a, b| rl.weight(&b.name).partial_cmp(&rl.weight(&a.name)).unwrap_or(std::cmp::Ordering::Equal));
let cap = if cfg.max_agents > 0 { cfg.max_agents.min(ranked.len()) } else { ranked.len() };
if cfg.offline {
let _ = tx.send("offline: no exploitation performed (provide API keys to run live)".into()).await;
return RunOutput {
findings: vec![],
agents_ran: selected.iter().map(|a| a.name.clone()).collect(),
candidates: 0,
};
let selected: Vec<Agent> = ranked.into_iter().take(cap).collect();
let _ = tx.send(format!("selected {} specialist agents (RL-ranked)", selected.len())).await;
let _ = tx.send("offline: no exploitation performed (provide API keys or --subscription to run live)".into()).await;
let artifacts = persist(&cfg, &recon, "", &[]);
return RunOutput { findings: vec![], agents_ran: selected.iter().map(|a| a.name.clone()).collect(), candidates: 0, recon, artifacts };
}
// ---- 3. Exploit (parallel, bounded by the pool semaphore) ----------
// Use the model to pick the agents whose preconditions match the recon —
// the harness reasons about *which* specialists to run, not all of them.
let chosen = select_agents(pool, &recon, &ranked, &tx).await;
let selected: Vec<Agent> = {
let mut sel: Vec<Agent> = if chosen.is_empty() {
ranked.clone()
} else {
ranked.iter().filter(|a| chosen.iter().any(|c| c == &a.name)).cloned().collect()
};
if sel.is_empty() {
sel = ranked.clone();
}
sel.into_iter().take(cap).collect()
};
let _ = tx
.send(format!("intelligently selected {} agent(s) matching recon: {}", selected.len(),
selected.iter().map(|a| a.name.clone()).collect::<Vec<_>>().join(", ")))
.await;
// ---- 3. Exploit (parallel) -----------------------------------------
let target = cfg.target.clone();
let candidates: Vec<Finding> = stream::iter(selected.iter().cloned())
let raw: Vec<(String, String, Vec<Finding>)> = stream::iter(selected.iter().cloned())
.map(|ag| {
let target = target.clone();
let recon = recon.clone();
@@ -77,64 +101,221 @@ pub async fn run(cfg: RunConfig, lib: &Library, pool: &ModelPool, tx: Sender<Str
match pool.complete(&ag.system, &user).await {
Ok((m, text)) => {
let f = extract_findings(&text, &ag.name);
let _ = txc
.send(format!("exploit {} via {}{} candidate(s)", ag.name, m.label(), f.len()))
.await;
f
let _ = txc.send(format!("exploit {} via {}{} candidate(s)", ag.name, m.label(), f.len())).await;
(ag.name.clone(), text, f)
}
Err(e) => {
let _ = txc.send(format!("exploit {} failed: {e}", ag.name)).await;
vec![]
(ag.name.clone(), format!("ERROR: {e}"), vec![])
}
}
}
})
.buffer_unordered(cfg.concurrency)
.collect::<Vec<Vec<Finding>>>()
.await
.into_iter()
.flatten()
.collect();
.collect()
.await;
let transcript = transcript_of(&raw);
let candidates: Vec<Finding> = raw.iter().flat_map(|(_, _, f)| f.clone()).collect();
let _ = tx.send(format!("{} candidate finding(s) — validating by {}-model vote", candidates.len(), cfg.vote_n)).await;
// ---- 4. Validate by N-model voting ---------------------------------
let vote_n = cfg.vote_n;
let findings = validate(candidates, pool, VOTE_SYS, cfg.vote_n, &tx).await;
finish(cfg, lib, recon, transcript, findings, selected, &mut rl, tx).await
}
/// White-box engagement: analyse a repository's source for vulnerabilities.
pub async fn run_whitebox(cfg: RunConfig, lib: &Library, pool: &ModelPool, tx: Sender<String>) -> RunOutput {
let _ = tx.send(format!("WHITEBOX · repo: {} · {} code agents · models: {}", cfg.target, lib.code.len(),
pool.candidates.iter().map(|m| m.label()).collect::<Vec<_>>().join(", "))).await;
let context = collect_repo_context(Path::new(&cfg.target), 200, 120_000);
let bytes = context.len();
let _ = tx.send(format!("collected {} bytes of source context", bytes)).await;
if bytes == 0 {
let _ = tx.send("no readable source found at the given path".into()).await;
}
let mut rl = cfg.rl_path.as_ref().map(|p| RlState::load(Path::new(p))).unwrap_or_default();
let mut ranked: Vec<Agent> = if lib.code.is_empty() { lib.vulns.clone() } else { lib.code.clone() };
ranked.sort_by(|a, b| rl.weight(&b.name).partial_cmp(&rl.weight(&a.name)).unwrap_or(std::cmp::Ordering::Equal));
let cap = if cfg.max_agents > 0 { cfg.max_agents.min(ranked.len()) } else { ranked.len() };
let selected: Vec<Agent> = ranked.into_iter().take(cap).collect();
let _ = tx.send(format!("selected {} code-analysis agents", selected.len())).await;
if cfg.offline || bytes == 0 {
let artifacts = persist(&cfg, "{}", &context, &[]);
return RunOutput { findings: vec![], agents_ran: selected.iter().map(|a| a.name.clone()).collect(), candidates: 0, recon: String::new(), artifacts };
}
let raw: Vec<(String, String, Vec<Finding>)> = stream::iter(selected.iter().cloned())
.map(|ag| {
let ctx = context.clone();
let txc = tx.clone();
async move {
let user = format!(
"{}\n\nSOURCE CODE TO REVIEW:\n```\n{}\n```\n\nReply ONLY with a JSON array of findings (may be empty []). \
Each item: {{id,title,severity,cwe,endpoint,payload,evidence,impact,remediation,confidence}} \
where `endpoint` is the file:line and `evidence` quotes the vulnerable code.",
ag.user.replace("{target}", "the provided repository").replace("{recon_json}", "{}"),
ctx
);
match pool.complete(&ag.system, &user).await {
Ok((m, text)) => {
let f = extract_findings(&text, &ag.name);
let _ = txc.send(format!("analyze {} via {}{} candidate(s)", ag.name, m.label(), f.len())).await;
(ag.name.clone(), text, f)
}
Err(e) => {
let _ = txc.send(format!("analyze {} failed: {e}", ag.name)).await;
(ag.name.clone(), format!("ERROR: {e}"), vec![])
}
}
}
})
.buffer_unordered(cfg.concurrency)
.collect()
.await;
let transcript = transcript_of(&raw);
let candidates: Vec<Finding> = raw.iter().flat_map(|(_, _, f)| f.clone()).collect();
let _ = tx.send(format!("{} candidate finding(s) — validating", candidates.len())).await;
let findings = validate(candidates, pool, CODE_VOTE_SYS, cfg.vote_n, &tx).await;
finish(cfg, lib, "{}".into(), transcript, findings, selected, &mut rl, tx).await
}
// --------------------------------------------------------------------------- shared
const SELECT_SYS: &str = "You are a penetration-test orchestrator. Given recon of a target and a catalog of specialist agents, choose ONLY the agents whose preconditions clearly match the target's attack surface. Be selective. Reply with a JSON array of agent names (strings) drawn exactly from the catalog. No prose.";
/// Ask the model which agents to run for this recon. Returns chosen agent names
/// (empty on failure → caller falls back to RL-ranked agents).
async fn select_agents(pool: &ModelPool, recon: &str, catalog: &[Agent], tx: &Sender<String>) -> Vec<String> {
let list = catalog
.iter()
.map(|a| format!("{}{} [{}]", a.name, a.title.replace(" Agent", ""), a.cwe))
.collect::<Vec<_>>()
.join("\n");
let user = format!("RECON:\n{recon}\n\nAGENT CATALOG (name — title [cwe]):\n{list}\n\nReturn a JSON array of agent names to run.");
match pool.complete(SELECT_SYS, &user).await {
Ok((m, text)) => {
let names = parse_string_array(&text);
let _ = tx.send(format!("agent selection via {}{} agent(s) chosen", m.label(), names.len())).await;
names
}
Err(e) => {
let _ = tx.send(format!("agent selection failed ({e}) — falling back to RL ranking")).await;
vec![]
}
}
}
fn parse_string_array(text: &str) -> Vec<String> {
match (text.find('['), text.rfind(']')) {
(Some(a), Some(b)) if b > a => serde_json::from_str::<Vec<String>>(&text[a..=b]).unwrap_or_default(),
_ => vec![],
}
}
async fn validate(candidates: Vec<Finding>, pool: &ModelPool, sys: &str, vote_n: usize, tx: &Sender<String>) -> Vec<Finding> {
let validated: Vec<Finding> = stream::iter(candidates.into_iter())
.map(|mut f| {
let txc = tx.clone();
async move {
let q = format!(
"Finding: {} | severity {} | {} | endpoint {} | payload {} | evidence {}",
"Finding: {} | severity {} | {} | at {} | payload {} | evidence {}",
f.title, f.severity, f.cwe, f.endpoint, f.payload, f.evidence
);
let (yes, total) = pool.vote(VOTE_SYS, &q, vote_n).await;
let (yes, total) = pool.vote(sys, &q, vote_n).await;
f.validated = total > 0 && yes * 2 >= total;
f.votes = format!("{yes}/{total}");
if f.confidence == 0.0 && total > 0 {
f.confidence = yes as f64 / total as f64;
}
let _ = txc
.send(format!("vote {}{} ({})", f.title, if f.validated { "CONFIRMED" } else { "rejected" }, f.votes))
.await;
let _ = txc.send(format!("vote {}{} ({})", f.title, if f.validated { "CONFIRMED" } else { "rejected" }, f.votes)).await;
f
}
})
.buffer_unordered(cfg.concurrency)
.collect::<Vec<Finding>>()
.buffer_unordered(pool.candidates.len().max(2))
.collect()
.await;
validated.into_iter().filter(|f| f.validated).collect()
}
let candidates = validated.len();
let findings: Vec<Finding> = validated.into_iter().filter(|f| f.validated).collect();
async fn finish(cfg: RunConfig, _lib: &Library, recon: String, transcript: String, findings: Vec<Finding>,
selected: Vec<Agent>, rl: &mut RlState, tx: Sender<String>) -> RunOutput {
let _ = tx.send(format!("{} validated finding(s)", findings.len())).await;
// RL update: reward agents that produced validated findings; gently decay idle.
let hit: std::collections::HashMap<&str, f64> = findings.iter().fold(Default::default(), |mut m, f| {
let e = m.entry(f.agent.as_str()).or_insert(0.0);
*e = (*e + severity_reward(&f.severity)).min(1.0);
m
});
for a in &selected {
let r = hit.get(a.name.as_str()).copied().unwrap_or(-0.05);
rl.update(&a.name, r);
}
rl.runs += 1;
if let Some(p) = &cfg.rl_path {
rl.save(Path::new(p));
let _ = tx.send("RL rewards updated".into()).await;
}
let artifacts = persist(&cfg, &recon, &transcript, &findings);
if !artifacts.is_empty() {
let _ = tx.send(format!("artifacts saved: {}", artifacts.join(", "))).await;
}
RunOutput {
candidates: findings.len(),
findings,
agents_ran: selected.iter().map(|a| a.name.clone()).collect(),
candidates,
recon,
artifacts,
}
}
/// Write recon/exploit/findings/report as json+md for downstream reuse.
fn persist(cfg: &RunConfig, recon: &str, transcript: &str, findings: &[Finding]) -> Vec<String> {
let Some(dir) = &cfg.workdir else { return vec![] };
let dir = PathBuf::from(dir);
if std::fs::create_dir_all(&dir).is_err() {
return vec![];
}
let mut written = Vec::new();
let mut put = |name: &str, content: String| {
let p = dir.join(name);
if std::fs::write(&p, content).is_ok() {
written.push(p.display().to_string());
}
};
put("recon.json", recon.to_string());
put("recon.md", format!("# Recon — {}\n\n```json\n{}\n```\n", cfg.target, recon));
if !transcript.is_empty() {
put("exploitation.md", format!("# Agent transcript — {}\n\n{}", cfg.target, transcript));
}
put("findings.json", serde_json::to_string_pretty(findings).unwrap_or_else(|_| "[]".into()));
put("findings.md", findings_md(&cfg.target, findings));
put("report.html", report::html(&cfg.target, findings));
written
}
fn findings_md(target: &str, findings: &[Finding]) -> String {
let mut s = format!("# NeuroSploit findings — {}\n\n{} validated finding(s).\n", target, findings.len());
for (i, f) in findings.iter().enumerate() {
s.push_str(&format!(
"\n## {}. [{}] {}\n- agent: `{}` CWE: {} CVSS: {} votes: {} confidence: {:.2}\n- endpoint: {}\n\n**Payload**\n```\n{}\n```\n\n**Evidence**\n{}\n\n**Impact:** {}\n\n**Remediation:** {}\n",
i + 1, f.severity, f.title, f.agent, f.cwe, f.cvss, f.votes, f.confidence, f.endpoint, f.payload, f.evidence, f.impact, f.remediation
));
}
s
}
fn transcript_of(raw: &[(String, String, Vec<Finding>)]) -> String {
raw.iter().map(|(n, t, f)| format!("## {} ({} candidate)\n\n{}\n", n, f.len(), t)).collect::<Vec<_>>().join("\n")
}
/// Pull a JSON array (or object) of findings out of a model's reply.
fn extract_findings(text: &str, agent: &str) -> Vec<Finding> {
let slice = match (text.find('['), text.rfind(']')) {
@@ -154,7 +335,42 @@ fn extract_findings(text: &str, agent: &str) -> Vec<Finding> {
for f in out.iter_mut() {
f.agent = agent.to_string();
if f.id.is_empty() {
f.id = format!("{}-{}", agent, &f.title.chars().take(12).collect::<String>());
f.id = format!("{}-{}", agent, f.title.chars().take(12).collect::<String>());
}
}
out
}
/// Concatenate source files under `root` into a bounded review context.
fn collect_repo_context(root: &Path, max_files: usize, max_bytes: usize) -> String {
const EXTS: &[&str] = &[
"rs", "py", "js", "ts", "tsx", "jsx", "go", "java", "php", "rb", "c", "cc", "cpp", "h", "hpp",
"cs", "kt", "swift", "scala", "sh", "sql", "html", "vue", "yml", "yaml", "tf",
];
let mut out = String::new();
let mut files = 0usize;
if !root.exists() {
return out;
}
for entry in walkdir::WalkDir::new(root).max_depth(8).into_iter().flatten() {
if files >= max_files || out.len() >= max_bytes {
break;
}
let path = entry.path();
let s = path.to_string_lossy();
if s.contains("/.git/") || s.contains("/node_modules/") || s.contains("/target/") || s.contains("/vendor/") {
continue;
}
let ext = path.extension().and_then(|e| e.to_str()).unwrap_or("");
if !EXTS.contains(&ext) {
continue;
}
if let Ok(content) = std::fs::read_to_string(path) {
let rel = path.strip_prefix(root).unwrap_or(path).to_string_lossy();
let budget = max_bytes.saturating_sub(out.len());
let take = content.len().min(budget).min(8_000);
out.push_str(&format!("\n// ===== file: {} =====\n{}\n", rel, &content[..take]));
files += 1;
}
}
out
+15 -4
View File
@@ -13,14 +13,21 @@ pub struct ModelPool {
sem: Arc<Semaphore>,
pub candidates: Vec<ModelRef>,
pub subscription: bool,
/// Path to an `.mcp.json` (Playwright) used on the subscription/CLI path.
pub mcp_config: Option<String>,
}
impl ModelPool {
pub fn new(models: Vec<ModelRef>, concurrency: usize) -> Self {
Self::with_auth(models, concurrency, false)
Self::with_auth(models, concurrency, false, None)
}
pub fn with_auth(models: Vec<ModelRef>, concurrency: usize, subscription: bool) -> Self {
pub fn with_auth(
models: Vec<ModelRef>,
concurrency: usize,
subscription: bool,
mcp_config: Option<String>,
) -> Self {
let concurrency = concurrency.max(1);
ModelPool {
client: ChatClient::new(),
@@ -31,13 +38,17 @@ impl ModelPool {
models
},
subscription,
mcp_config,
}
}
/// One completion for a model, via subscription CLI or HTTP API.
/// One completion for a model, via subscription CLI (optionally with MCP) or HTTP API.
async fn one(&self, m: &ModelRef, system: &str, user: &str) -> Result<String> {
if self.subscription && cli_binary_for(&m.provider).is_some() {
return self.client.chat_cli(&m.provider, &m.model, system, user).await;
return self
.client
.chat_cli(&m.provider, &m.model, system, user, self.mcp_config.as_deref())
.await;
}
self.client.chat(m, system, user).await
}
+60
View File
@@ -0,0 +1,60 @@
//! Lightweight reinforcement-learning reward store for the harness.
//!
//! Each agent carries a weight in [0.05, 1.0]; validated findings reward it,
//! idle runs decay it slightly. Weights bias agent ordering on future runs and
//! persist to a JSON file so the harness gets sharper over time.
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::Path;
#[derive(Default, Serialize, Deserialize)]
pub struct RlState {
#[serde(default)]
pub weights: HashMap<String, f64>,
#[serde(default)]
pub runs: u64,
}
const ALPHA: f64 = 0.3;
const WMIN: f64 = 0.05;
const WMAX: f64 = 1.0;
impl RlState {
pub fn load(path: &Path) -> RlState {
std::fs::read_to_string(path)
.ok()
.and_then(|s| serde_json::from_str(&s).ok())
.unwrap_or_default()
}
pub fn weight(&self, agent: &str) -> f64 {
*self.weights.get(agent).unwrap_or(&0.5)
}
/// Reward in [-1, 1]; e.g. severity-weighted hits positive, idle negative.
pub fn update(&mut self, agent: &str, reward: f64) {
let w = self.weights.entry(agent.to_string()).or_insert(0.5);
*w = (*w + ALPHA * (reward - *w)).clamp(WMIN, WMAX);
}
pub fn save(&self, path: &Path) {
if let Some(parent) = path.parent() {
let _ = std::fs::create_dir_all(parent);
}
if let Ok(s) = serde_json::to_string_pretty(self) {
let _ = std::fs::write(path, s);
}
}
}
/// Severity → reward weight.
pub fn severity_reward(sev: &str) -> f64 {
match sev {
"Critical" => 1.0,
"High" => 0.7,
"Medium" => 0.4,
"Low" => 0.2,
_ => 0.05,
}
}
@@ -74,6 +74,12 @@ pub struct RunConfig {
/// of HTTP API keys.
#[serde(default)]
pub subscription: bool,
/// Directory to persist run artifacts (recon/exploit/findings json+md).
#[serde(default)]
pub workdir: Option<String>,
/// Path to the RL reward state file.
#[serde(default)]
pub rl_path: Option<String>,
}
fn default_vote() -> usize {
@@ -93,6 +99,8 @@ impl RunConfig {
max_agents: 0,
offline: false,
subscription: false,
workdir: None,
rl_path: None,
}
}
}
+24
View File
@@ -0,0 +1,24 @@
HTTP/2 200
date: Mon, 22 Jun 2026 20:03:23 GMT
content-type: text/html; charset=UTF-8
cf-ray: a0fddb2608004cf7-GRU
cf-cache-status: DYNAMIC
cache-control: no-store, no-cache, must-revalidate
expires: Thu, 19 Nov 1981 08:52:00 GMT
server: cloudflare
set-cookie: PHPSESSID=8e1ac50a4f9a4bec19d456ecc11ba781; path=/; secure; HttpOnly; SameSite=Lax
set-cookie: user_language=deleted; expires=Thu, 01 Jan 1970 00:00:01 GMT; Max-Age=0; path=/
set-cookie: user_language=pt; expires=Tue, 22 Jun 2027 20:03:23 GMT; Max-Age=31536000; path=/; domain=.hackersec.com; secure; HttpOnly; SameSite=Lax
strict-transport-security: max-age=15552000; includeSubDomains; preload
vary: Accept-Encoding
pragma: no-cache
content-security-policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com https://googleads.g.doubleclick.net https://www.google.com https://static.hotjar.com https://script.hotjar.com https://snap.licdn.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https://www.google.com https://*.google.com https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com https://*.g.doubleclick.net https://*.hotjar.com https://*.hotjar.io wss://ws.hotjar.com https://snap.licdn.com https://px.ads.linkedin.com; frame-src 'self' https://www.googletagmanager.com https://td.doubleclick.net https://vars.hotjar.com https://www.google.com https://maps.google.com https://www.google.com/maps/embed; frame-ancestors 'self'; form-action 'self'; base-uri 'self'
expect-ct: max-age=86400, enforce
referrer-policy: same-origin
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
report-to: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=VHXHHEFxf1FeB7p%2FSZOs6ttzdPjv0%2BZKnuq964Yjrm44aGqGaLbxIKorQ46QqRSB1aJH40Lg5h5k0Opf%2FbY8%2Fqt1bz2b4uVVdD0G4CU%2B4Yf1ACKi%2F1z8YD90k2RTk7U%3D"}]}
nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}
alt-svc: h3=":443"; ma=86400
File diff suppressed because it is too large Load Diff
+8
View File
@@ -0,0 +1,8 @@
# HackerSec - Robots.txt
# https://hackersec.com
User-agent: *
Allow: /
# Sitemap
Sitemap: https://hackersec.com/sitemap.php
File diff suppressed because one or more lines are too long
+100
View File
@@ -0,0 +1,100 @@
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
+214
View File
@@ -0,0 +1,214 @@
#!/usr/bin/env python3
"""
NeuroSploit v3.4.x — recon + whitebox(code) agent builder.
Emits two new categories the Rust harness loads:
agents_md/recon/ — information-gathering specialists
agents_md/code/ — white-box source-code (SAST) review specialists
Usage: python3 scripts/build_agents_v34.py
"""
import os
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
def render(a, code=False):
L = [f"# {a['title']} Agent\n", "## User Prompt"]
if code:
L.append(f"You are reviewing the source code of **{{target}}** for {a['for']}.\n")
L.append("**Recon Context:**\n{recon_json}\n")
L.append("The relevant source files are provided to you below the methodology.\n")
else:
L.append(f"You are performing reconnaissance on **{{target}}** to {a['for']}.\n")
L.append("**Recon Context:**\n{recon_json}\n")
L.append("**METHODOLOGY:**\n")
for i, (s, bs) in enumerate(a["steps"], 1):
L.append(f"### {i}. {s}")
L += [f"- {b}" for b in bs]
L.append("")
n = len(a["steps"]) + 1
L.append(f"### {n}. Report Format")
L.append("For each CONFIRMED finding:")
L.append("```")
L.append("FINDING:")
L.append(f"- Title: {a['title']} at [{'file:line' if code else 'asset/endpoint'}]")
L.append(f"- Severity: {a['sev']}")
L.append(f"- CWE: {a['cwe']}")
L.append(f"- Endpoint: [{'file:line' if code else 'URL/host'}]")
L.append("- Vector: [what/where]")
L.append("- Payload: [PoC / vulnerable code snippet]")
L.append("- Evidence: [proof / exact code quoted]")
L.append(f"- Impact: {a['impact']}")
L.append(f"- Remediation: {a['fix']}")
L.append("```\n")
L.append("## System Prompt")
L.append(a["system"])
return "\n".join(L) + "\n"
RECON = [
{"name":"subdomain_enum","title":"Subdomain Enumeration Specialist","for":"discover all subdomains and expand the attack surface","sev":"Info","cwe":"CWE-200","impact":"Wider attack surface, forgotten/staging hosts","fix":"Inventory and decommission stale DNS records",
"steps":[("Passive sources",["Query crt.sh, certificate transparency, Shodan, and passive DNS","Run `subfinder -d {target}` and `amass enum -passive -d {target}`"]),
("Active resolution",["Resolve and probe with `httpx -title -tech-detect -status-code`","Bruteforce with a curated wordlist where in scope"]),
("Triage",["Flag dev/staging/admin/api hosts and dangling CNAMEs (subdomain takeover candidates)"])],
"system":"You are a recon specialist. Report only resolvable, in-scope subdomains you actually observed, with the resolution evidence. Do not invent hosts."},
{"name":"tech_fingerprint","title":"Technology Fingerprinting Specialist","for":"identify the full technology stack and versions","sev":"Info","cwe":"CWE-200","impact":"Targeted exploitation of known-vulnerable components","fix":"Hide version banners; keep components patched",
"steps":[("Fingerprint",["Inspect headers, cookies, error pages, favicon hash","Run `whatweb`, `nuclei -t technologies`, and Wappalyzer-style detection"]),
("Version map",["Map server, framework, language, CMS, JS libs and their versions"]),
("CVE correlation",["Correlate detected versions to known CVEs for later exploitation"])],
"system":"You are a fingerprinting specialist. Report only technologies you positively detected with the supporting evidence (header/banner/hash). Mark version guesses as uncertain."},
{"name":"js_analysis","title":"JavaScript Analysis Specialist","for":"extract endpoints, secrets and logic from client-side JS","sev":"Low","cwe":"CWE-200","impact":"Hidden endpoints and leaked secrets in bundles","fix":"Strip secrets from client code; restrict sourcemaps",
"steps":[("Collect",["Gather all JS bundles and sourcemaps; `katana`/`gau` for URLs"]),
("Extract",["Regex for API paths, fetch/axios calls, API keys (sk-, AIza, nvapi-), tokens"]),
("Map",["Build an endpoint + parameter inventory from the JS for downstream agents"])],
"system":"You are a JS-recon specialist. Report only endpoints/secrets actually present in the served JS, quoting the snippet. Validated secrets only; never invent."},
{"name":"api_discovery","title":"API Surface Discovery Specialist","for":"enumerate REST/GraphQL/gRPC/WebSocket surfaces","sev":"Info","cwe":"CWE-200","impact":"Undocumented API endpoints widen attack surface","fix":"Gate non-public APIs; remove exposed schemas",
"steps":[("Find specs",["Probe /openapi.json, /swagger, /graphql, /.well-known, /v1 /v2 prefixes"]),
("Enumerate",["Introspect GraphQL; enumerate REST routes; check gRPC reflection"]),
("Catalog",["Record methods, params, auth requirements per endpoint"])],
"system":"You are an API-recon specialist. Report only endpoints you confirmed respond, with method and a sample response signature. No speculation."},
{"name":"secret_scanning","title":"Exposed Secret Scanning Specialist","for":"find leaked credentials and keys across exposed assets","sev":"High","cwe":"CWE-522","impact":"Credential/key exposure enabling account or cloud takeover","fix":"Rotate exposed secrets; remove from public assets",
"steps":[("Sweep",["Scan JS, .env, .git, backups, CI logs, comments with trufflehog-style regex"]),
("Validate",["Confirm key format and (in scope) liveness without abusing it"]),
("Classify",["Tag provider and privilege of each secret"])],
"system":"You are a secret-scanning specialist. Report only real, validly-formatted secrets you actually found, quoting location. Never abuse keys beyond a minimal validity check."},
{"name":"dns_recon","title":"DNS Reconnaissance Specialist","for":"map DNS records and infrastructure relationships","sev":"Info","cwe":"CWE-200","impact":"Infra mapping; zone/record misconfig discovery","fix":"Harden DNS; disable zone transfers",
"steps":[("Records",["Enumerate A/AAAA/CNAME/MX/TXT/NS/SOA; check SPF/DMARC/DKIM"]),
("Misconfig",["Test zone transfer (AXFR), wildcard records, dangling CNAMEs"]),
("Relate",["Cluster shared infrastructure and providers"])],
"system":"You are a DNS-recon specialist. Report only records you actually resolved, with the query evidence."},
{"name":"content_discovery","title":"Content & Path Discovery Specialist","for":"discover hidden files, directories and backups","sev":"Low","cwe":"CWE-538","impact":"Exposure of admin panels, backups, configs","fix":"Remove sensitive files from web root; enforce authz",
"steps":[("Crawl",["Spider with katana; parse robots.txt/sitemap.xml/.well-known"]),
("Fuzz",["`ffuf` directories/files with sensible wordlists and extensions (.bak,.old,.zip,.sql)"]),
("Triage",["Flag admin, backup, config, and source-leak paths"])],
"system":"You are a content-discovery specialist. Report only paths that returned a meaningful status/body, with the evidence. No 404s as findings."},
{"name":"parameter_discovery","title":"Parameter Discovery Specialist","for":"enumerate hidden request parameters and inputs","sev":"Info","cwe":"CWE-200","impact":"Hidden params enable injection/logic attacks","fix":"Validate and document all accepted parameters",
"steps":[("Mine",["Extract params from JS, forms, history (gau), and docs"]),
("Bruteforce",["Use arjun/param-miner style discovery with reflection detection"]),
("Hand off",["Provide the param inventory to injection specialists"])],
"system":"You are a parameter-discovery specialist. Report only parameters you confirmed the app accepts/reflects, with evidence."},
{"name":"waf_cdn_detection","title":"WAF/CDN Detection Specialist","for":"identify WAF/CDN and inform evasion strategy","sev":"Info","cwe":"CWE-200","impact":"Informs bypass strategy; reveals origin exposure","fix":"Ensure origin is not directly reachable",
"steps":[("Detect",["Fingerprint WAF/CDN via headers, cookies, block pages, `wafw00f`"]),
("Origin",["Search for origin IP leaks (DNS history, SSL SANs, headers)"]),
("Strategy",["Note effective encodings/paths for later, in-scope testing"])],
"system":"You are a WAF/CDN specialist. Report only positively-identified protections and any verified origin exposure, with evidence."},
{"name":"cloud_asset_discovery","title":"Cloud Asset Discovery Specialist","for":"discover cloud buckets, functions and metadata surfaces","sev":"Low","cwe":"CWE-200","impact":"Exposed cloud assets and SSRF/metadata vectors","fix":"Lock down public cloud assets; enforce IMDSv2",
"steps":[("Discover",["Find S3/GCS/Azure references; permutate bucket names; detect cloud provider"]),
("Probe",["Check public list/read on storage; note SSRF-to-metadata potential"]),
("Catalog",["Record provider, asset, and access level"])],
"system":"You are a cloud-recon specialist. Report only assets you confirmed exist with their observed access level. No guessed buckets."},
{"name":"graphql_discovery","title":"GraphQL Discovery Specialist","for":"map the GraphQL schema and sensitive operations","sev":"Low","cwe":"CWE-200","impact":"Schema exposure aids targeted attacks","fix":"Disable introspection/suggestions in production",
"steps":[("Locate",["Find /graphql endpoints and test introspection"]),
("Map",["If introspection off, use field-suggestion (clairvoyance) to reconstruct types"]),
("Flag",["Mark mutations and sensitive queries for the API agents"])],
"system":"You are a GraphQL-recon specialist. Report only schema elements you actually recovered, with the query/response evidence."},
{"name":"osint_employee","title":"OSINT & Exposure Mapping Specialist","for":"map public exposure (leaked creds, repos, docs) for the target org","sev":"Low","cwe":"CWE-200","impact":"Public exposure enabling targeted attacks","fix":"Monitor and remediate public exposure",
"steps":[("Sources",["Search public code (GitHub), paste sites, breach indices (in scope)"]),
("Correlate",["Link leaked emails/creds/repos to the target's assets"]),
("Report",["Summarize exposure relevant to the engagement"])],
"system":"You are an OSINT specialist operating strictly within authorized scope. Report only verifiable public exposure tied to the target, citing the source. No private data harvesting."},
]
def _code(name, title, vc, cwe, sev, patterns, fix, impact):
return {"name": name, "title": title, "for": f"{vc} in the source code", "sev": sev, "cwe": cwe,
"impact": impact, "fix": fix,
"steps": [("Locate sinks/sources", patterns),
("Trace dataflow", ["Trace user-controlled input from source to the dangerous sink",
"Confirm the path is reachable and lacks sanitization/validation"]),
("Confirm exploitability", ["Quote the exact vulnerable lines (file:line)",
"Explain the concrete exploit and why existing controls don't stop it"])],
"system": f"You are a white-box source reviewer for {vc}. Report ONLY issues you can prove in the PROVIDED code by quoting the exact vulnerable lines (file:line) and a reachable dataflow from untrusted input. Never report sanitized, unreachable, or hypothetical code. If the snippet is insufficient, say so rather than guess."}
CODE = [
_code("code_sqli","Source SQL Injection Reviewer","SQL injection","CWE-89","Critical",
["String concatenation/interpolation into SQL (f-strings, +, .format) passed to execute()","Raw queries bypassing the ORM; `.raw(`, `cursor.execute(... % ...)`"],
"Use parameterized queries / ORM bindings","Database compromise, data exfiltration"),
_code("code_command_injection","Source Command Injection Reviewer","OS command injection","CWE-78","Critical",
["`os.system`, `subprocess(..., shell=True)`, `exec`, backticks with user input","Unsanitized input concatenated into shell strings"],
"Avoid shells; pass argument arrays; validate input","Remote code execution on the host"),
_code("code_path_traversal","Source Path Traversal Reviewer","path traversal / arbitrary file access","CWE-22","High",
["User input in file paths (open/read/sendFile) without normalization","Missing checks for `../` and absolute paths"],
"Canonicalize and confine paths to a safe base directory","Arbitrary file read/write"),
_code("code_ssrf","Source SSRF Reviewer","server-side request forgery","CWE-918","High",
["User-controlled URLs passed to HTTP clients (requests/fetch/curl)","No allowlist or scheme/host validation"],
"Allowlist destinations; block internal ranges and redirects","Internal network access, cloud metadata theft"),
_code("code_xss","Source XSS Reviewer","cross-site scripting (output encoding)","CWE-79","High",
["Unescaped user input rendered to HTML (innerHTML, dangerouslySetInnerHTML, `|safe`, `v-html`)","Template autoescaping disabled"],
"Context-aware output encoding; keep autoescaping on; CSP","Session theft, account takeover"),
_code("code_insecure_deserialization","Source Insecure Deserialization Reviewer","unsafe deserialization","CWE-502","Critical",
["`pickle.loads`, `yaml.load` (unsafe), Java/PHP native deserialization on untrusted data","Object deserialization of request data"],
"Use safe formats/loaders; never deserialize untrusted data","Remote code execution"),
_code("code_hardcoded_secrets","Source Hardcoded Secrets Reviewer","hardcoded credentials/keys","CWE-798","High",
["API keys, passwords, tokens, private keys committed in source/config","High-entropy strings assigned to credential-like names"],
"Move secrets to a vault/env; rotate exposed values","Credential/key compromise"),
_code("code_weak_crypto","Source Weak Cryptography Reviewer","weak or misused cryptography","CWE-327","Medium",
["MD5/SHA1 for passwords; ECB mode; static IV/salt; hardcoded keys","Custom/rolled crypto; weak random for security tokens"],
"Use vetted algorithms (bcrypt/argon2, AES-GCM), random IVs, CSPRNG","Data exposure, token forgery"),
_code("code_auth_flaws","Source Authentication/Authorization Reviewer","broken authentication/authorization","CWE-287","High",
["Missing auth checks on sensitive routes; client-trusted role flags","Comparisons of secrets without constant-time; weak session handling"],
"Enforce server-side authz on every action; harden sessions","Privilege escalation, account takeover"),
_code("code_idor_access","Source IDOR / Access Control Reviewer","insecure direct object references","CWE-639","High",
["Object lookups by user-supplied id without ownership checks","Direct DB fetch on `request.id` with no scoping"],
"Enforce per-object ownership/authorization checks","Cross-account data access"),
_code("code_xxe","Source XXE Reviewer","XML external entity processing","CWE-611","High",
["XML parsers with external entities/DTDs enabled on untrusted input","`resolve_entities=True`, default-config parsers"],
"Disable DTDs/external entities; use hardened parsers","File disclosure, SSRF"),
_code("code_open_redirect","Source Open Redirect Reviewer","open redirect","CWE-601","Medium",
["Redirects built from user input (redirect(request.param))","No allowlist of redirect destinations"],
"Allowlist redirect targets; use relative paths","Phishing, OAuth token theft"),
_code("code_template_injection","Source Template Injection Reviewer","server-side template injection","CWE-1336","Critical",
["User input concatenated into template strings then rendered","`render_template_string`, dynamic template construction"],
"Never render user input as templates; sandbox","Remote code execution"),
_code("code_race_condition","Source Race Condition Reviewer","TOCTOU / concurrency flaws","CWE-362","Medium",
["Check-then-act on shared state without locking","Non-atomic balance/quota/idempotency updates"],
"Use atomic operations, locks, or transactions","Double-spend, state corruption"),
_code("code_unsafe_eval","Source Unsafe Eval Reviewer","dynamic code evaluation","CWE-95","Critical",
["`eval`, `exec`, `Function()`, `setTimeout(string)` on user input","Dynamic import/require of user-controlled names"],
"Eliminate dynamic eval; use safe parsers/dispatch tables","Remote code execution"),
_code("code_csrf_missing","Source CSRF Protection Reviewer","missing CSRF protection","CWE-352","Medium",
["State-changing POST/PUT/DELETE without CSRF tokens","CSRF protection globally disabled"],
"Enable anti-CSRF tokens / SameSite cookies","Unauthorized state-changing actions"),
_code("code_insecure_random","Source Insecure Randomness Reviewer","predictable randomness for security","CWE-330","Medium",
["`random`/`Math.random` used for tokens, IDs, passwords, OTPs","Seeded or time-based randomness for secrets"],
"Use a CSPRNG (secrets, crypto.randomBytes)","Token/session prediction"),
_code("code_logging_sensitive","Source Sensitive Logging Reviewer","sensitive data in logs","CWE-532","Low",
["Logging passwords, tokens, PII, full requests","Debug logging of secrets in production paths"],
"Redact sensitive fields; scope debug logging","Credential/PII exposure via logs"),
_code("code_sql_orm_raw","Source ORM Raw-Query Reviewer","unsafe raw ORM queries","CWE-89","High",
["`.raw()`, `.extra()`, query builders with string interpolation","Raw fragments mixing user input"],
"Use parameter binding even in raw queries","SQL injection via ORM"),
_code("code_file_upload","Source File Upload Reviewer","insecure file upload handling","CWE-434","High",
["No type/extension/content validation; user-controlled filenames/paths","Uploads served from executable directories"],
"Validate type/size; randomize names; store outside webroot","Webshell upload, RCE"),
_code("code_mass_assignment","Source Mass Assignment Reviewer","mass assignment / over-binding","CWE-915","High",
["Binding whole request body to models (`Model(**request)`, `update_attributes`)","No allowlist of bindable fields"],
"Allowlist bindable fields; use DTOs","Privilege escalation via hidden fields"),
_code("code_jwt_misuse","Source JWT Misuse Reviewer","JWT verification flaws","CWE-347","High",
["`verify=False`, alg `none` accepted, secret not validated","Algorithm not pinned; weak/hardcoded secret"],
"Pin algorithm; verify signature; strong secret/keys","Token forgery, auth bypass"),
_code("code_cors_misconfig","Source CORS Misconfiguration Reviewer","permissive CORS","CWE-942","Medium",
["`Access-Control-Allow-Origin: *` with credentials; reflecting Origin","Wildcard or unchecked origin allowlists"],
"Strict origin allowlist; never reflect Origin with credentials","Cross-origin data theft"),
_code("code_ssrf_redirect","Source SSRF-via-Redirect Reviewer","SSRF through redirect following","CWE-918","Medium",
["HTTP clients following redirects to user-controlled URLs","No re-validation of redirect targets against allowlist"],
"Disable/limit redirects; re-validate each hop","Internal access via redirect"),
]
def main():
rdir = os.path.join(ROOT, "agents_md", "recon")
cdir = os.path.join(ROOT, "agents_md", "code")
os.makedirs(rdir, exist_ok=True)
os.makedirs(cdir, exist_ok=True)
for a in RECON:
open(os.path.join(rdir, a["name"] + ".md"), "w").write(render(a, code=False))
for a in CODE:
open(os.path.join(cdir, a["name"] + ".md"), "w").write(render(a, code=True))
print(f"recon agents: {len(RECON)} | code agents: {len(CODE)}")
if __name__ == "__main__":
main()