mirror of
https://github.com/CyberSecurityUP/NeuroSploit.git
synced 2026-08-15 06:00:28 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e565270f43 | ||
|
|
c6fd5d6ac8 | ||
|
|
9dfcea87bc | ||
|
|
3ca3f269ee | ||
|
|
bf56184912 | ||
|
|
d59f28f36d | ||
|
|
9c4f912323 | ||
|
|
a05a99e0f6 | ||
|
|
56d3f0c723 |
@@ -91,3 +91,12 @@ logs/webgui.log
|
||||
# generated reports
|
||||
reports/report.*
|
||||
reports/*.pdf
|
||||
|
||||
# Rust build artifacts (v3.4.0)
|
||||
neurosploit-rs/target/
|
||||
reports/*.html
|
||||
reports/report_rs.html
|
||||
runs/
|
||||
data/rl_state_rs.json
|
||||
neurosploit-rs/runs/
|
||||
v34_gui.png
|
||||
|
||||
@@ -1,23 +1,76 @@
|
||||
# NeuroSploit v3.3.0
|
||||
# NeuroSploit v3.4.0
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
**Autonomous, markdown-driven AI penetration testing.**
|
||||
**Autonomous, markdown-driven AI penetration testing — now with a Rust multi-model harness.**
|
||||
|
||||
NeuroSploit v3.3.0 is a ground-up re-model of the pentest agent. Instead of a
|
||||
monolithic Python orchestrator, it is now a **lean engine that turns a URL into
|
||||
an autonomous engagement**: it composes a master prompt from a curated library
|
||||
of **213 markdown agents** and hands execution to whichever **agentic CLI
|
||||
backend** you have installed — **Claude Code, Codex, or Grok CLI** (or a Claude
|
||||
subscription) — augmented with **Playwright MCP** for real browser-based proof,
|
||||
and a **reinforcement-learning** loop that gets smarter every run.
|
||||
NeuroSploit turns a URL (or a code repository) into an autonomous security
|
||||
engagement. A high-performance **Rust harness** (`tokio` + `axum`) drives a
|
||||
**pool of LLM models** with concurrency, **provider failover**, and **N-model
|
||||
validator voting** — multiple models must independently agree a finding is real
|
||||
before it is reported. After recon, the harness **intelligently selects** which
|
||||
of the **249 markdown agents** match the target instead of running them blindly,
|
||||
learns across runs via a **reinforcement-learning** reward loop, and serves its
|
||||
own polished web dashboard.
|
||||
|
||||
> The previous Python orchestration now lives in [`legacy/`](legacy/README.md).
|
||||
> The Python engine (v3.3.0) and the original monolith live in
|
||||
> [`legacy/`](legacy/README.md); the v3.3.0 stdlib dashboard remains in `webgui/`.
|
||||
|
||||
## 🦀 The Rust harness (`neurosploit-rs/`)
|
||||
|
||||
```bash
|
||||
cd neurosploit-rs && cargo build --release
|
||||
|
||||
# Web dashboard (black-box + white-box modes)
|
||||
./target/release/neurosploit serve # → http://127.0.0.1:8788
|
||||
|
||||
# Black-box: recon → intelligent agent selection → parallel exploit → vote → report
|
||||
./target/release/neurosploit run https://target.example \
|
||||
--model anthropic:claude-opus-4-8 --model openai:gpt-5.1 --vote-n 3
|
||||
|
||||
# White-box: analyse a repository's source for vulnerabilities
|
||||
./target/release/neurosploit whitebox /path/to/repo --subscription --model anthropic:claude-opus-4-8
|
||||
|
||||
# Subscription (no API key) + real browser proof via Playwright MCP
|
||||
./target/release/neurosploit run https://t.example --subscription --mcp --model anthropic:claude-opus-4-8
|
||||
|
||||
# Pipeline self-test, no keys/login required
|
||||
./target/release/neurosploit run https://t.example --offline
|
||||
```
|
||||
|
||||
**What it does**
|
||||
|
||||
- **Two modes** — *black-box* (URL recon → exploit) and *white-box* (walk a repo,
|
||||
run code-review/SAST agents on the source).
|
||||
- **Intelligent selection** — the model picks the agents whose preconditions match
|
||||
the recon, then runs that subset (not top-N).
|
||||
- **Multi-model pool** — bounded concurrency, **provider failover**, and the same
|
||||
panel forms the **N-model validator jury** that cuts false positives.
|
||||
- **Two auth paths** — **model APIs** (provider key) *or* **subscription**: drive
|
||||
your local **Claude Code / Codex / Grok / Gemini** logins directly, no API key.
|
||||
- **12 providers / 40+ models** (Claude, GPT, Grok, **Gemini**, NVIDIA NIM,
|
||||
DeepSeek, Mistral, Qwen, Groq, Together, OpenRouter, Ollama).
|
||||
- **RL rewards** persisted to `data/rl_state_rs.json` — validated findings reward
|
||||
an agent, biasing the next run.
|
||||
- **Artifacts for reuse** — every run writes `runs/<target>-<ts>/`:
|
||||
`recon.json/md`, `exploitation.md`, `findings.json/md`, `report.html`.
|
||||
- **Playwright MCP** on the subscription path for real browser-based proof.
|
||||
|
||||
### Agent library — 249 agents
|
||||
|
||||
| Category | Dir | Count | Purpose |
|
||||
|----------|-----|-------|---------|
|
||||
| Vulnerability specialists | `agents_md/vulns/` | 196 | Exploit a specific vuln class |
|
||||
| Recon | `agents_md/recon/` | 12 | Information gathering / attack surface |
|
||||
| Code (white-box SAST) | `agents_md/code/` | 24 | Source-code vulnerability review |
|
||||
| Meta | `agents_md/meta/` | 17 | Orchestrator, validator, scorers, reporter, RL |
|
||||
|
||||
---
|
||||
|
||||
|
||||
+46
@@ -1,3 +1,49 @@
|
||||
# NeuroSploit v3.4.0 — Release Notes
|
||||
|
||||
**Release Date:** June 2026
|
||||
**Codename:** Rust Multi-Model Harness
|
||||
**License:** MIT
|
||||
|
||||
---
|
||||
|
||||
## TL;DR
|
||||
|
||||
A new **Rust harness** (`neurosploit-rs/`) re-implements the autonomous runtime
|
||||
as a single, fast binary built on `tokio` + `axum`. It drives a **pool of LLM
|
||||
models** with concurrency limits, **provider failover**, and **N-model validator
|
||||
voting** — multiple models must independently agree a finding is real before it
|
||||
is reported — then serves its own solid web dashboard. It reuses the existing
|
||||
`agents_md/` library (213 agents) unchanged.
|
||||
|
||||
## Highlights
|
||||
|
||||
- **`neurosploit-rs/` cargo workspace**: `harness` lib crate + `neurosploit`
|
||||
binary. `cargo build --release` → one static-ish binary.
|
||||
- **Multi-model pool** (`pool.rs`): bounded concurrency + automatic **failover**
|
||||
across providers; the same panel is reused as the **validator voting** jury.
|
||||
- **Pipeline** (`pipeline.rs`): recon → parallel agent exploitation (semaphore
|
||||
bounded) → **N-model adversarial vote** → score → report. Streams live
|
||||
progress over a channel.
|
||||
- **11 providers / 31 models** (`models.rs`), all OpenAI-compatible: Anthropic,
|
||||
OpenAI, xAI, NVIDIA NIM, DeepSeek, Mistral, Qwen, Groq, Together, OpenRouter,
|
||||
Ollama. Models like **Qwen / DeepSeek / Llama** usable directly.
|
||||
- **Axum web dashboard** (`app/`): multi-model selection panel, live execution
|
||||
console, findings, agent browser, embedded HTML report. Single binary serves
|
||||
the SPA — no npm/build.
|
||||
- **CLI**: `neurosploit serve | run <url> | agents | models`, plus `--offline`
|
||||
mode to exercise the full pipeline without any API keys.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
cd neurosploit-rs && cargo build --release
|
||||
./target/release/neurosploit serve # → http://127.0.0.1:8788
|
||||
./target/release/neurosploit run https://t.example \
|
||||
--model anthropic:claude-opus-4-8 --model openai:gpt-5.1 --vote-n 3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# NeuroSploit v3.3.0 — Release Notes
|
||||
|
||||
**Release Date:** June 2026
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
HTTP/1.1 404 Not Found
|
||||
Content-Type: text/html
|
||||
Server: Microsoft-IIS/8.5
|
||||
X-Powered-By: ASP.NET
|
||||
Date: Tue, 23 Jun 2026 21:13:25 GMT
|
||||
Content-Length: 1245
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
|
||||
<title>404 - File or directory not found.</title>
|
||||
<style type="text/css">
|
||||
<!--
|
||||
body{margin:0;font-size:.7em;font-family:Verdana, Arial, Helvetica, sans-serif;background:#EEEEEE;}
|
||||
fieldset{padding:0 15px 10px 15px;}
|
||||
h1{font-size:2.4em;margin:0;color:#FFF;}
|
||||
h2{font-size:1.7em;margin:0;color:#CC0000;}
|
||||
h3{font-size:1.2em;margin:10px 0 0 0;color:#000000;}
|
||||
#header{width:96%;margin:0 0 0 0;padding:6px 2% 6px 2%;font-family:"trebuchet MS", Verdana, sans-serif;color:#FFF;
|
||||
background-color:#555555;}
|
||||
#content{margin:0 0 0 2%;position:relative;}
|
||||
.content-container{background:#FFF;width:96%;margin-top:8px;padding:10px;position:relative;}
|
||||
-->
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="header"><h1>Server Error</h1></div>
|
||||
<div id="content">
|
||||
<div class="content-container"><fieldset>
|
||||
<h2>404 - File or directory not found.</h2>
|
||||
<h3>The resource you are looking for might have been removed, had its name changed, or is temporarily unavailable.</h3>
|
||||
</fieldset></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -0,0 +1,5 @@
|
||||
# Netscape HTTP Cookie File
|
||||
# https://curl.se/docs/http-cookies.html
|
||||
# This file was generated by libcurl! Edit at your own risk.
|
||||
|
||||
#HttpOnly_testaspnet.vulnweb.com FALSE / FALSE 0 ASP.NET_SessionId 1mkryz45pc3j44ua53yfe545
|
||||
@@ -0,0 +1,5 @@
|
||||
# Netscape HTTP Cookie File
|
||||
# https://curl.se/docs/http-cookies.html
|
||||
# This file was generated by libcurl! Edit at your own risk.
|
||||
|
||||
#HttpOnly_testaspnet.vulnweb.com FALSE / FALSE 0 ASP.NET_SessionId okc513jjz1kxsxbmkmidnmfs
|
||||
@@ -0,0 +1,5 @@
|
||||
# Netscape HTTP Cookie File
|
||||
# https://curl.se/docs/http-cookies.html
|
||||
# This file was generated by libcurl! Edit at your own risk.
|
||||
|
||||
#HttpOnly_testaspnet.vulnweb.com FALSE / FALSE 0 ASP.NET_SessionId r2w133jnjihmgf552tyes4uh
|
||||
@@ -0,0 +1,122 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Validation of viewstate MAC failed. If this application is hosted by a Web Farm or cluster, ensure that <machineKey> configuration specifies the same validationKey and validation algorithm. AutoGenerate cannot be used in a cluster.<br><br>http://go.microsoft.com/fwlink/?LinkID=314055</title>
|
||||
<style>
|
||||
body {font-family:"Verdana";font-weight:normal;font-size: .7em;color:black;}
|
||||
p {font-family:"Verdana";font-weight:normal;color:black;margin-top: -5px}
|
||||
b {font-family:"Verdana";font-weight:bold;color:black;margin-top: -5px}
|
||||
H1 { font-family:"Verdana";font-weight:normal;font-size:18pt;color:red }
|
||||
H2 { font-family:"Verdana";font-weight:normal;font-size:14pt;color:maroon }
|
||||
pre {font-family:"Lucida Console";font-size: .9em}
|
||||
.marker {font-weight: bold; color: black;text-decoration: none;}
|
||||
.version {color: gray;}
|
||||
.error {margin-bottom: 10px;}
|
||||
.expandable { text-decoration:underline; font-weight:bold; color:navy; cursor:hand; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body bgcolor="white">
|
||||
|
||||
<span><H1>Server Error in '/' Application.<hr width=100% size=1 color=silver></H1>
|
||||
|
||||
<h2> <i>Validation of viewstate MAC failed. If this application is hosted by a Web Farm or cluster, ensure that <machineKey> configuration specifies the same validationKey and validation algorithm. AutoGenerate cannot be used in a cluster.<br><br>http://go.microsoft.com/fwlink/?LinkID=314055</i> </h2></span>
|
||||
|
||||
<font face="Arial, Helvetica, Geneva, SunSans-Regular, sans-serif ">
|
||||
|
||||
<b> Description: </b>An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
|
||||
|
||||
<br><br>
|
||||
|
||||
<b> Exception Details: </b>System.Web.HttpException: Validation of viewstate MAC failed. If this application is hosted by a Web Farm or cluster, ensure that <machineKey> configuration specifies the same validationKey and validation algorithm. AutoGenerate cannot be used in a cluster.<br><br>http://go.microsoft.com/fwlink/?LinkID=314055<br><br>
|
||||
|
||||
<b>Source Error:</b> <br><br>
|
||||
|
||||
<table width=100% bgcolor="#ffffcc">
|
||||
<tr>
|
||||
<td>
|
||||
<code><pre>
|
||||
|
||||
[No relevant source lines]</pre></code>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br>
|
||||
|
||||
<b> Source File: </b> c:\Windows\Microsoft.NET\Framework64\v2.0.50727\Temporary ASP.NET Files\root\e6eb278b\4a52d72d\App_Web_pebpzm2g.0.cs<b> Line: </b> 0
|
||||
<br><br>
|
||||
|
||||
<b>Stack Trace:</b> <br><br>
|
||||
|
||||
<table width=100% bgcolor="#ffffcc">
|
||||
<tr>
|
||||
<td>
|
||||
<code><pre>
|
||||
|
||||
[ViewStateException: Invalid viewstate.
|
||||
Client IP: 177.62.32.16
|
||||
Port: 56298
|
||||
User-Agent: Mozilla/5.0
|
||||
ViewState: /wEPDwUKLTg2MjcwMzE2Mg9kFgICAQ9kFgICAQ9kFgQCAQ8WBB4EaHJlZgUKbG9naW4uYXNweB4JaW5uZXJodG1sBQVsb2dpbmQCAw8WBB8AZB4HVmlzaWJsZWhkAgMPFgIfAQVJcG9zdGVkIGJ5IDxzdHJvbmc+YWRtaW4gICAgICAgICAgICAgICAgICAgIDwvc3Ryb25nPjUvMTYvMjAxOSAxMjozMjozMCBQTWQCBQ8WBB8BBT5BY3VuZXRpeCBWdWxuZXJhYmlsaXR5IFNjYW5uZXIgTm93IFdpdGggTmV0d29yayBTZWN1cml0eSBTY2Fucx8ABRJSZWFkTmV3cy5hc3B4P2lkPTBkAgcPFgIfAQVEU2VhbWxlc3MgT3BlblZBUyBpbnRlZ3JhdGlvbiBub3cgYWxzbyBhdmFpbGFibGUgb24gV2luZG93cyBhbmQgTGludXhkAgkPZBYCAgEPZBYGZg9kFgJmDxYCHwEFJTxJTUcgc3JjPSJpbWFnZXMvY29tbWVudC1iZWZvcmUuZ2lmIj5kAgEPZBYCZg8WAh4FY2xhc3MFB0NvbW1lbnRkAgIPZBYCZg8WAh8BBSQ8SU1HIHNyYz0iaW1hZ2VzL2NvbW1lbnQtYWZ0ZXIuZ2lmIj5kZLtjZhxvUS4ci8HIFlqscBeWoXbu
|
||||
Referer:
|
||||
Path: /Comments.aspx]
|
||||
|
||||
[HttpException (0x80004005): Validation of viewstate MAC failed. If this application is hosted by a Web Farm or cluster, ensure that <machineKey> configuration specifies the same validationKey and validation algorithm. AutoGenerate cannot be used in a cluster.
|
||||
|
||||
http://go.microsoft.com/fwlink/?LinkID=314055]
|
||||
System.Web.UI.ViewStateException.ThrowError(Exception inner, String persistedState, String errorPageMessage, Boolean macValidationError) +190
|
||||
System.Web.UI.ObjectStateFormatter.Deserialize(String inputString) +11093249
|
||||
System.Web.UI.Util.DeserializeWithAssert(IStateFormatter formatter, String serializedState) +59
|
||||
System.Web.UI.HiddenFieldPageStatePersister.Load() +11093352
|
||||
System.Web.UI.Page.LoadPageStateFromPersistenceMedium() +11178689
|
||||
System.Web.UI.Page.LoadAllState() +46
|
||||
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +11174087
|
||||
System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +11173626
|
||||
System.Web.UI.Page.ProcessRequest() +91
|
||||
System.Web.UI.Page.ProcessRequest(HttpContext context) +240
|
||||
ASP.comments_aspx.ProcessRequest(HttpContext context) in c:\Windows\Microsoft.NET\Framework64\v2.0.50727\Temporary ASP.NET Files\root\e6eb278b\4a52d72d\App_Web_pebpzm2g.0.cs:0
|
||||
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +599
|
||||
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +171
|
||||
</pre></code>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br>
|
||||
|
||||
<hr width=100% size=1 color=silver>
|
||||
|
||||
<b>Version Information:</b> Microsoft .NET Framework Version:2.0.50727.8974; ASP.NET Version:2.0.50727.8974
|
||||
|
||||
</font>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
<!--
|
||||
[ViewStateException]: Invalid viewstate.
|
||||
Client IP: 177.62.32.16
|
||||
Port: 56298
|
||||
User-Agent: Mozilla/5.0
|
||||
ViewState: /wEPDwUKLTg2MjcwMzE2Mg9kFgICAQ9kFgICAQ9kFgQCAQ8WBB4EaHJlZgUKbG9naW4uYXNweB4JaW5uZXJodG1sBQVsb2dpbmQCAw8WBB8AZB4HVmlzaWJsZWhkAgMPFgIfAQVJcG9zdGVkIGJ5IDxzdHJvbmc+YWRtaW4gICAgICAgICAgICAgICAgICAgIDwvc3Ryb25nPjUvMTYvMjAxOSAxMjozMjozMCBQTWQCBQ8WBB8BBT5BY3VuZXRpeCBWdWxuZXJhYmlsaXR5IFNjYW5uZXIgTm93IFdpdGggTmV0d29yayBTZWN1cml0eSBTY2Fucx8ABRJSZWFkTmV3cy5hc3B4P2lkPTBkAgcPFgIfAQVEU2VhbWxlc3MgT3BlblZBUyBpbnRlZ3JhdGlvbiBub3cgYWxzbyBhdmFpbGFibGUgb24gV2luZG93cyBhbmQgTGludXhkAgkPZBYCAgEPZBYGZg9kFgJmDxYCHwEFJTxJTUcgc3JjPSJpbWFnZXMvY29tbWVudC1iZWZvcmUuZ2lmIj5kAgEPZBYCZg8WAh4FY2xhc3MFB0NvbW1lbnRkAgIPZBYCZg8WAh8BBSQ8SU1HIHNyYz0iaW1hZ2VzL2NvbW1lbnQtYWZ0ZXIuZ2lmIj5kZLtjZhxvUS4ci8HIFlqscBeWoXbu
|
||||
Referer:
|
||||
Path: /Comments.aspx
|
||||
[HttpException]: Validation of viewstate MAC failed. If this application is hosted by a Web Farm or cluster, ensure that <machineKey> configuration specifies the same validationKey and validation algorithm. AutoGenerate cannot be used in a cluster.
|
||||
|
||||
http://go.microsoft.com/fwlink/?LinkID=314055
|
||||
at System.Web.UI.ViewStateException.ThrowError(Exception inner, String persistedState, String errorPageMessage, Boolean macValidationError)
|
||||
at System.Web.UI.ObjectStateFormatter.Deserialize(String inputString)
|
||||
at System.Web.UI.Util.DeserializeWithAssert(IStateFormatter formatter, String serializedState)
|
||||
at System.Web.UI.HiddenFieldPageStatePersister.Load()
|
||||
at System.Web.UI.Page.LoadPageStateFromPersistenceMedium()
|
||||
at System.Web.UI.Page.LoadAllState()
|
||||
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
|
||||
at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
|
||||
at System.Web.UI.Page.ProcessRequest()
|
||||
at System.Web.UI.Page.ProcessRequest(HttpContext context)
|
||||
at ASP.comments_aspx.ProcessRequest(HttpContext context) in c:\Windows\Microsoft.NET\Framework64\v2.0.50727\Temporary ASP.NET Files\root\e6eb278b\4a52d72d\App_Web_pebpzm2g.0.cs:line 0
|
||||
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
|
||||
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
|
||||
--><!--
|
||||
This error page might contain sensitive information because ASP.NET is configured to show verbose error messages using <customErrors mode="Off"/>. Consider using <customErrors mode="On"/> or <customErrors mode="RemoteOnly"/> in production environments.-->
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,4 @@
|
||||
# Netscape HTTP Cookie File
|
||||
# https://curl.se/docs/http-cookies.html
|
||||
# This file was generated by libcurl! Edit at your own risk.
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# Netscape HTTP Cookie File
|
||||
# https://curl.se/docs/http-cookies.html
|
||||
# This file was generated by libcurl! Edit at your own risk.
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+10
@@ -0,0 +1,10 @@
|
||||
HTTP/1.1 200 OK
|
||||
Cache-Control: private
|
||||
Content-Type: text/html; charset=utf-8
|
||||
Server: Microsoft-IIS/8.5
|
||||
X-AspNet-Version: 2.0.50727
|
||||
Set-Cookie: ASP.NET_SessionId=fnvw5h45lqt4ay45z1d0bd2u; path=/; HttpOnly
|
||||
X-Powered-By: ASP.NET
|
||||
Date: Tue, 23 Jun 2026 21:13:51 GMT
|
||||
Content-Length: 13318
|
||||
|
||||
+84
File diff suppressed because one or more lines are too long
+84
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,102 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>A potentially dangerous Request.Form value was detected from the client (tbUsername=""><h1>HTML_INJECTED_HE...").</title>
|
||||
<style>
|
||||
body {font-family:"Verdana";font-weight:normal;font-size: .7em;color:black;}
|
||||
p {font-family:"Verdana";font-weight:normal;color:black;margin-top: -5px}
|
||||
b {font-family:"Verdana";font-weight:bold;color:black;margin-top: -5px}
|
||||
H1 { font-family:"Verdana";font-weight:normal;font-size:18pt;color:red }
|
||||
H2 { font-family:"Verdana";font-weight:normal;font-size:14pt;color:maroon }
|
||||
pre {font-family:"Lucida Console";font-size: .9em}
|
||||
.marker {font-weight: bold; color: black;text-decoration: none;}
|
||||
.version {color: gray;}
|
||||
.error {margin-bottom: 10px;}
|
||||
.expandable { text-decoration:underline; font-weight:bold; color:navy; cursor:hand; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body bgcolor="white">
|
||||
|
||||
<span><H1>Server Error in '/' Application.<hr width=100% size=1 color=silver></H1>
|
||||
|
||||
<h2> <i>A potentially dangerous Request.Form value was detected from the client (tbUsername=""><h1>HTML_INJECTED_HE...").</i> </h2></span>
|
||||
|
||||
<font face="Arial, Helvetica, Geneva, SunSans-Regular, sans-serif ">
|
||||
|
||||
<b> Description: </b>Request Validation has detected a potentially dangerous client input value, and processing of the request has been aborted. This value may indicate an attempt to compromise the security of your application, such as a cross-site scripting attack. You can disable request validation by setting validateRequest=false in the Page directive or in the <pages> configuration section. However, it is strongly recommended that your application explicitly check all inputs in this case.
|
||||
<br><br>
|
||||
|
||||
<b> Exception Details: </b>System.Web.HttpRequestValidationException: A potentially dangerous Request.Form value was detected from the client (tbUsername=""><h1>HTML_INJECTED_HE...").<br><br>
|
||||
|
||||
<b>Source Error:</b> <br><br>
|
||||
|
||||
<table width=100% bgcolor="#ffffcc">
|
||||
<tr>
|
||||
<td>
|
||||
<code><pre>
|
||||
|
||||
[No relevant source lines]</pre></code>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br>
|
||||
|
||||
<b> Source File: </b> c:\Windows\Microsoft.NET\Framework64\v2.0.50727\Temporary ASP.NET Files\root\e6eb278b\4a52d72d\App_Web_pebpzm2g.1.cs<b> Line: </b> 0
|
||||
<br><br>
|
||||
|
||||
<b>Stack Trace:</b> <br><br>
|
||||
|
||||
<table width=100% bgcolor="#ffffcc">
|
||||
<tr>
|
||||
<td>
|
||||
<code><pre>
|
||||
|
||||
[HttpRequestValidationException (0x80004005): A potentially dangerous Request.Form value was detected from the client (tbUsername=""><h1>HTML_INJECTED_HE...").]
|
||||
System.Web.HttpRequest.ValidateString(String s, String valueName, String collectionName) +11208427
|
||||
System.Web.HttpRequest.ValidateNameValueCollection(NameValueCollection nvc, String collectionName) +71
|
||||
System.Web.HttpRequest.get_Form() +178
|
||||
System.Web.HttpRequest.get_HasForm() +11208663
|
||||
System.Web.UI.Page.GetCollectionBasedOnMethod(Boolean dontReturnNull) +124
|
||||
System.Web.UI.Page.DeterminePostBackMode() +83
|
||||
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +11174087
|
||||
System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +11173626
|
||||
System.Web.UI.Page.ProcessRequest() +91
|
||||
System.Web.UI.Page.ProcessRequest(HttpContext context) +240
|
||||
ASP.login_aspx.ProcessRequest(HttpContext context) in c:\Windows\Microsoft.NET\Framework64\v2.0.50727\Temporary ASP.NET Files\root\e6eb278b\4a52d72d\App_Web_pebpzm2g.1.cs:0
|
||||
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +599
|
||||
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +171
|
||||
</pre></code>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br>
|
||||
|
||||
<hr width=100% size=1 color=silver>
|
||||
|
||||
<b>Version Information:</b> Microsoft .NET Framework Version:2.0.50727.8974; ASP.NET Version:2.0.50727.8974
|
||||
|
||||
</font>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
<!--
|
||||
[HttpRequestValidationException]: A potentially dangerous Request.Form value was detected from the client (tbUsername=""><h1>HTML_INJECTED_HE...").
|
||||
at System.Web.HttpRequest.ValidateString(String s, String valueName, String collectionName)
|
||||
at System.Web.HttpRequest.ValidateNameValueCollection(NameValueCollection nvc, String collectionName)
|
||||
at System.Web.HttpRequest.get_Form()
|
||||
at System.Web.HttpRequest.get_HasForm()
|
||||
at System.Web.UI.Page.GetCollectionBasedOnMethod(Boolean dontReturnNull)
|
||||
at System.Web.UI.Page.DeterminePostBackMode()
|
||||
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
|
||||
at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
|
||||
at System.Web.UI.Page.ProcessRequest()
|
||||
at System.Web.UI.Page.ProcessRequest(HttpContext context)
|
||||
at ASP.login_aspx.ProcessRequest(HttpContext context) in c:\Windows\Microsoft.NET\Framework64\v2.0.50727\Temporary ASP.NET Files\root\e6eb278b\4a52d72d\App_Web_pebpzm2g.1.cs:line 0
|
||||
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
|
||||
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
|
||||
--><!--
|
||||
This error page might contain sensitive information because ASP.NET is configured to show verbose error messages using <customErrors mode="Off"/>. Consider using <customErrors mode="On"/> or <customErrors mode="RemoteOnly"/> in production environments.-->
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Generated
+2019
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
[workspace]
|
||||
members = ["crates/harness", "app"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "3.4.0"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/JoasASantos/NeuroSploit"
|
||||
|
||||
[workspace.dependencies]
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
|
||||
anyhow = "1"
|
||||
futures = "0.3"
|
||||
|
||||
[profile.release]
|
||||
opt-level = 2
|
||||
lto = "thin"
|
||||
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "neurosploit"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[[bin]]
|
||||
name = "neurosploit"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
harness = { package = "neurosploit-harness", path = "../crates/harness" }
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
tokio.workspace = true
|
||||
anyhow.workspace = true
|
||||
futures.workspace = true
|
||||
axum = { version = "0.7", features = ["ws"] }
|
||||
tower-http = { version = "0.5", features = ["cors"] }
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
uuid = { version = "1", features = ["v4"] }
|
||||
@@ -0,0 +1,204 @@
|
||||
//! NeuroSploit v3.4.0 — single binary: `serve` (web dashboard) or `run` (CLI).
|
||||
|
||||
mod web;
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
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")]
|
||||
struct Cli {
|
||||
#[command(subcommand)]
|
||||
cmd: Cmd,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Cmd {
|
||||
/// Start the web dashboard.
|
||||
Serve {
|
||||
#[arg(long, default_value_t = 8788)]
|
||||
port: u16,
|
||||
},
|
||||
/// Run an engagement from the CLI.
|
||||
Run {
|
||||
url: String,
|
||||
/// Models as provider:model (repeatable). First is primary; rest fail over + vote.
|
||||
#[arg(long = "model")]
|
||||
models: Vec<String>,
|
||||
#[arg(long, default_value_t = 0)]
|
||||
max_agents: usize,
|
||||
#[arg(long, default_value_t = 3)]
|
||||
vote_n: usize,
|
||||
/// Exercise the pipeline without calling any model API.
|
||||
#[arg(long)]
|
||||
offline: bool,
|
||||
/// Use local agentic CLI subscriptions (Claude Code / Codex / Grok)
|
||||
/// 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,
|
||||
/// List providers and models.
|
||||
Models,
|
||||
}
|
||||
|
||||
/// Locate the repo root that holds `agents_md/` (walk up from CWD, then fall
|
||||
/// back to the crate's compile-time location).
|
||||
fn find_base() -> PathBuf {
|
||||
if let Ok(b) = std::env::var("NEUROSPLOIT_BASE") {
|
||||
return PathBuf::from(b);
|
||||
}
|
||||
if let Ok(cwd) = std::env::current_dir() {
|
||||
let mut dir = cwd.as_path();
|
||||
for _ in 0..6 {
|
||||
if dir.join("agents_md").is_dir() {
|
||||
return dir.to_path_buf();
|
||||
}
|
||||
match dir.parent() {
|
||||
Some(p) => dir = p,
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
// crate is at <root>/neurosploit-rs/app → root is two levels up
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent()
|
||||
.and_then(|p| p.parent())
|
||||
.map(|p| p.to_path_buf())
|
||||
.unwrap_or_else(|| PathBuf::from("."))
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let cli = Cli::parse();
|
||||
let base = find_base();
|
||||
|
||||
match cli.cmd {
|
||||
Cmd::Agents => {
|
||||
let lib = agents::load(&base);
|
||||
println!("{{\"vulns\":{},\"meta\":{},\"total\":{}}}", lib.vulns.len(), lib.meta.len(), lib.total());
|
||||
}
|
||||
Cmd::Models => {
|
||||
for p in harness::providers() {
|
||||
println!("{:<4} {:<14} {} models [{}]", p.kind, p.key, p.models.len(), p.label);
|
||||
for m in &p.models {
|
||||
println!(" {}:{}", p.key, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
cfg.vote_n = vote_n;
|
||||
cfg.offline = offline;
|
||||
cfg.subscription = subscription;
|
||||
if !models.is_empty() {
|
||||
cfg.models = models;
|
||||
}
|
||||
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?;
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
//! Axum web dashboard for the v3.4.0 harness.
|
||||
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
response::Html,
|
||||
routing::{get, post},
|
||||
Json, Router,
|
||||
};
|
||||
use harness::{agents, models::ModelRef, pool::ModelPool, report, types::RunConfig};
|
||||
use serde_json::{json, Value};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
struct RunState {
|
||||
log: Vec<String>,
|
||||
done: bool,
|
||||
result: Option<Value>,
|
||||
report: Option<String>,
|
||||
}
|
||||
|
||||
pub struct AppState {
|
||||
base: PathBuf,
|
||||
runs: Mutex<HashMap<String, RunState>>,
|
||||
}
|
||||
|
||||
pub async fn serve(base: PathBuf, port: u16) -> anyhow::Result<()> {
|
||||
let state = Arc::new(AppState { base, runs: Mutex::new(HashMap::new()) });
|
||||
let app = Router::new()
|
||||
.route("/", get(index))
|
||||
.route("/api/info", get(info))
|
||||
.route("/api/agents", get(agents_list))
|
||||
.route("/api/models", get(models_list))
|
||||
.route("/api/run", post(run))
|
||||
.route("/api/status/:id", get(status))
|
||||
.route("/report/:id", get(report_html))
|
||||
.with_state(state);
|
||||
|
||||
let addr = format!("127.0.0.1:{port}");
|
||||
println!("NeuroSploit v3.4.0 dashboard → http://{addr}");
|
||||
let listener = tokio::net::TcpListener::bind(&addr).await?;
|
||||
axum::serve(listener, app).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn index() -> Html<&'static str> {
|
||||
Html(include_str!("../web/index.html"))
|
||||
}
|
||||
|
||||
async fn info(State(st): State<Arc<AppState>>) -> Json<Value> {
|
||||
let lib = agents::load(&st.base);
|
||||
let provs: Vec<Value> = harness::providers()
|
||||
.iter()
|
||||
.map(|p| json!({"key": p.key, "label": p.label, "kind": p.kind, "models": p.models}))
|
||||
.collect();
|
||||
Json(json!({
|
||||
"version": "3.4.0",
|
||||
"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(),
|
||||
}))
|
||||
}
|
||||
|
||||
async fn agents_list(State(st): State<Arc<AppState>>) -> Json<Value> {
|
||||
let lib = agents::load(&st.base);
|
||||
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();
|
||||
Json(json!({ "agents": v }))
|
||||
}
|
||||
|
||||
async fn models_list() -> Json<Value> {
|
||||
let provs: Vec<Value> = harness::providers()
|
||||
.iter()
|
||||
.map(|p| json!({"key": p.key, "label": p.label, "kind": p.kind, "models": p.models}))
|
||||
.collect();
|
||||
Json(json!({ "providers": provs }))
|
||||
}
|
||||
|
||||
fn norm(u: &str) -> String {
|
||||
if u.starts_with("http") {
|
||||
u.to_string()
|
||||
} else {
|
||||
format!("https://{u}")
|
||||
}
|
||||
}
|
||||
|
||||
async fn run(State(st): State<Arc<AppState>>, Json(body): Json<Value>) -> Json<Value> {
|
||||
let id = uuid::Uuid::new_v4().to_string();
|
||||
st.runs
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(id.clone(), RunState { log: vec![], done: false, result: None, report: None });
|
||||
|
||||
let st2 = st.clone();
|
||||
let id2 = id.clone();
|
||||
tokio::spawn(async move {
|
||||
let base = st2.base.clone();
|
||||
|
||||
let mut targets: Vec<String> = Vec::new();
|
||||
if let Some(arr) = body.get("targets").and_then(|v| v.as_array()) {
|
||||
for t in arr {
|
||||
if let Some(s) = t.as_str() {
|
||||
if !s.trim().is_empty() {
|
||||
targets.push(norm(s.trim()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if targets.is_empty() {
|
||||
if let Some(u) = body.get("url").and_then(|v| v.as_str()) {
|
||||
if !u.trim().is_empty() {
|
||||
targets.push(norm(u.trim()));
|
||||
}
|
||||
}
|
||||
}
|
||||
let models: Vec<String> = body
|
||||
.get("models")
|
||||
.and_then(|v| v.as_array())
|
||||
.map(|a| a.iter().filter_map(|x| x.as_str().map(|s| s.to_string())).collect())
|
||||
.unwrap_or_default();
|
||||
let vote_n = body.get("vote_n").and_then(|v| v.as_u64()).unwrap_or(3) as usize;
|
||||
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() {
|
||||
vec![ModelRef::parse("anthropic:claude-opus-4-8")]
|
||||
} else {
|
||||
models.iter().map(|s| ModelRef::parse(s)).collect()
|
||||
};
|
||||
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();
|
||||
let idf = id2.clone();
|
||||
let fwd = tokio::spawn(async move {
|
||||
while let Some(line) = rx.recv().await {
|
||||
if let Ok(mut g) = stf.runs.lock() {
|
||||
if let Some(r) = g.get_mut(&idf) {
|
||||
r.log.push(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let mut all_findings = Vec::new();
|
||||
let mut all_ran = Vec::new();
|
||||
for url in &targets {
|
||||
let mut cfg = RunConfig::new(url);
|
||||
cfg.models = if models.is_empty() {
|
||||
vec!["anthropic:claude-opus-4-8".into()]
|
||||
} else {
|
||||
models.clone()
|
||||
};
|
||||
cfg.vote_n = vote_n;
|
||||
cfg.max_agents = max_agents;
|
||||
cfg.offline = offline;
|
||||
cfg.subscription = subscription;
|
||||
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);
|
||||
}
|
||||
drop(tx);
|
||||
let _ = fwd.await;
|
||||
|
||||
let report_html = report::html(targets.first().map(|s| s.as_str()).unwrap_or(""), &all_findings);
|
||||
let result = json!({"findings": all_findings, "agents_ran": all_ran, "targets": targets});
|
||||
if let Ok(mut g) = st2.runs.lock() {
|
||||
if let Some(r) = g.get_mut(&id2) {
|
||||
r.result = Some(result);
|
||||
r.report = Some(report_html);
|
||||
r.done = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Json(json!({ "run_id": id }))
|
||||
}
|
||||
|
||||
async fn status(Path(id): Path<String>, State(st): State<Arc<AppState>>) -> Json<Value> {
|
||||
let g = st.runs.lock().unwrap();
|
||||
match g.get(&id) {
|
||||
Some(r) => Json(json!({"log": r.log, "done": r.done, "result": r.result, "has_report": r.report.is_some()})),
|
||||
None => Json(json!({"error": "unknown run"})),
|
||||
}
|
||||
}
|
||||
|
||||
async fn report_html(Path(id): Path<String>, State(st): State<Arc<AppState>>) -> Html<String> {
|
||||
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)
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<title>NeuroSploit</title>
|
||||
<style>
|
||||
: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(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}
|
||||
.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><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>
|
||||
<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>
|
||||
<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 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 & 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 & 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,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');
|
||||
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();
|
||||
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="tag ${p.kind}">${p.kind}</span> <code>${id}</code></label>`;first=false;}));
|
||||
$('#mpanel').innerHTML=mh;
|
||||
$('#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');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(){
|
||||
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;$('#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;}
|
||||
$('#go').disabled=false;$('#prog').classList.remove('on');lastRun=id;render(st.result||{});
|
||||
logLine('done.');
|
||||
}
|
||||
function render(res){
|
||||
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,'&').replace(/</g,'<').replace(/>/g,'>');}
|
||||
$$('.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();
|
||||
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='';$('#repo').oninput=()=>$('#repo').style.borderColor='';
|
||||
init();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "neurosploit-harness"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "harness"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
tokio.workspace = true
|
||||
reqwest.workspace = true
|
||||
anyhow.workspace = true
|
||||
futures.workspace = true
|
||||
walkdir = "2"
|
||||
regex = "1"
|
||||
@@ -0,0 +1,81 @@
|
||||
use regex::Regex;
|
||||
use serde::Serialize;
|
||||
use std::path::Path;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
/// One markdown specialist/meta agent.
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct Agent {
|
||||
pub name: String,
|
||||
pub title: String,
|
||||
pub cwe: String,
|
||||
pub kind: String, // "vuln" | "meta"
|
||||
#[serde(skip)]
|
||||
pub system: String,
|
||||
#[serde(skip)]
|
||||
pub user: String,
|
||||
}
|
||||
|
||||
/// The loaded `agents_md/` library.
|
||||
#[derive(Default)]
|
||||
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.recon.len() + self.code.len()
|
||||
}
|
||||
}
|
||||
|
||||
/// 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"),
|
||||
}
|
||||
}
|
||||
|
||||
fn load_dir(dir: &Path, kind: &str) -> Vec<Agent> {
|
||||
let title_re = Regex::new(r"(?m)^#\s+(.+?)\s*$").unwrap();
|
||||
let cwe_re = Regex::new(r"CWE-\d+").unwrap();
|
||||
let user_re = Regex::new(r"(?s)##\s*User Prompt\s*\n(.*?)(?:\n##\s|\z)").unwrap();
|
||||
let sys_re = Regex::new(r"(?s)##\s*System Prompt\s*\n(.*?)(?:\n##\s|\z)").unwrap();
|
||||
let mut out = Vec::new();
|
||||
if !dir.is_dir() {
|
||||
return out;
|
||||
}
|
||||
for entry in WalkDir::new(dir).max_depth(1).into_iter().flatten() {
|
||||
let path = entry.path();
|
||||
if path.extension().and_then(|e| e.to_str()) != Some("md") {
|
||||
continue;
|
||||
}
|
||||
let text = std::fs::read_to_string(path).unwrap_or_default();
|
||||
let name = path.file_stem().and_then(|s| s.to_str()).unwrap_or("").to_string();
|
||||
let title = title_re
|
||||
.captures(&text)
|
||||
.and_then(|c| c.get(1))
|
||||
.map(|m| m.as_str().trim().to_string())
|
||||
.unwrap_or_else(|| name.clone());
|
||||
let cwe = cwe_re.find(&text).map(|m| m.as_str().to_string()).unwrap_or_default();
|
||||
let user = user_re
|
||||
.captures(&text)
|
||||
.and_then(|c| c.get(1))
|
||||
.map(|m| m.as_str().trim().to_string())
|
||||
.unwrap_or_default();
|
||||
let system = sys_re
|
||||
.captures(&text)
|
||||
.and_then(|c| c.get(1))
|
||||
.map(|m| m.as_str().trim().to_string())
|
||||
.unwrap_or_default();
|
||||
out.push(Agent { name, title, cwe, kind: kind.to_string(), system, user });
|
||||
}
|
||||
out.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
out
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
//! NeuroSploit v3.4.0 harness — a robust multi-model runtime for the
|
||||
//! markdown-driven autonomous pentest engine.
|
||||
//!
|
||||
//! The harness loads the `agents_md/` library, drives a *pool* of LLM models
|
||||
//! (any OpenAI-compatible provider) with concurrency + provider failover, runs
|
||||
//! the specialist agents in parallel, then validates every candidate finding by
|
||||
//! **N-model voting** before scoring and reporting.
|
||||
|
||||
pub mod agents;
|
||||
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, 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};
|
||||
@@ -0,0 +1,258 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use serde::Serialize;
|
||||
use std::process::Stdio;
|
||||
use std::time::Duration;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::process::Command;
|
||||
|
||||
/// A model provider exposing an OpenAI-compatible `/chat/completions` endpoint.
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct Provider {
|
||||
pub key: &'static str,
|
||||
pub label: &'static str,
|
||||
pub base_url: &'static str,
|
||||
pub env_key: &'static str,
|
||||
/// "cli" (also drivable by an agentic CLI) | "api"
|
||||
pub kind: &'static str,
|
||||
pub models: Vec<&'static str>,
|
||||
}
|
||||
|
||||
/// The full provider registry. Every entry speaks the OpenAI chat schema
|
||||
/// (Anthropic, xAI, NVIDIA NIM, DeepSeek, Mistral, Qwen, Groq, Together,
|
||||
/// OpenRouter, Gemini-compat, Ollama).
|
||||
pub fn providers() -> Vec<Provider> {
|
||||
vec![
|
||||
Provider { key: "anthropic", label: "Anthropic Claude", base_url: "https://api.anthropic.com/v1", env_key: "ANTHROPIC_API_KEY", kind: "cli",
|
||||
models: vec!["claude-opus-4-8", "claude-sonnet-4-6", "claude-haiku-4-5"] },
|
||||
Provider { key: "openai", label: "OpenAI", base_url: "https://api.openai.com/v1", env_key: "OPENAI_API_KEY", kind: "cli",
|
||||
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",
|
||||
models: vec!["deepseek-reasoner", "deepseek-chat"] },
|
||||
Provider { key: "mistral", label: "Mistral", base_url: "https://api.mistral.ai/v1", env_key: "MISTRAL_API_KEY", kind: "api",
|
||||
models: vec!["mistral-large-latest", "codestral-latest"] },
|
||||
Provider { key: "qwen", label: "Qwen (DashScope)", base_url: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1", env_key: "DASHSCOPE_API_KEY", kind: "api",
|
||||
models: vec!["qwen-max", "qwen2.5-coder-32b-instruct", "qwq-plus"] },
|
||||
Provider { key: "groq", label: "Groq", base_url: "https://api.groq.com/openai/v1", env_key: "GROQ_API_KEY", kind: "api",
|
||||
models: vec!["llama-3.3-70b-versatile", "qwen-2.5-coder-32b"] },
|
||||
Provider { key: "together", label: "Together AI", base_url: "https://api.together.xyz/v1", env_key: "TOGETHER_API_KEY", kind: "api",
|
||||
models: vec!["Qwen/Qwen2.5-Coder-32B-Instruct", "deepseek-ai/DeepSeek-R1", "meta-llama/Llama-3.3-70B-Instruct-Turbo"] },
|
||||
Provider { key: "openrouter", label: "OpenRouter", base_url: "https://openrouter.ai/api/v1", env_key: "OPENROUTER_API_KEY", kind: "api",
|
||||
models: vec!["anthropic/claude-opus-4-8", "qwen/qwen-2.5-coder-32b-instruct", "deepseek/deepseek-r1", "meta-llama/llama-3.3-70b-instruct"] },
|
||||
Provider { key: "ollama", label: "Ollama (local)", base_url: "http://localhost:11434/v1", env_key: "OLLAMA_API_KEY", kind: "api",
|
||||
models: vec!["qwen2.5-coder:32b", "qwq:32b", "deepseek-r1:32b", "llama3.3:70b"] },
|
||||
]
|
||||
}
|
||||
|
||||
pub fn provider_for(key: &str) -> Option<Provider> {
|
||||
providers().into_iter().find(|p| p.key == key)
|
||||
}
|
||||
|
||||
/// A `provider:model` selection.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ModelRef {
|
||||
pub provider: String,
|
||||
pub model: String,
|
||||
}
|
||||
|
||||
impl ModelRef {
|
||||
pub fn parse(s: &str) -> ModelRef {
|
||||
match s.split_once(':') {
|
||||
Some((p, m)) => ModelRef { provider: p.to_string(), model: m.to_string() },
|
||||
None => ModelRef { provider: "anthropic".into(), model: s.to_string() },
|
||||
}
|
||||
}
|
||||
pub fn label(&self) -> String {
|
||||
format!("{}:{}", self.provider, self.model)
|
||||
}
|
||||
}
|
||||
|
||||
/// OpenAI-compatible chat client shared across the model pool.
|
||||
#[derive(Clone)]
|
||||
pub struct ChatClient {
|
||||
http: reqwest::Client,
|
||||
}
|
||||
|
||||
impl ChatClient {
|
||||
pub fn new() -> Self {
|
||||
let http = reqwest::Client::builder()
|
||||
.timeout(Duration::from_secs(120))
|
||||
.build()
|
||||
.unwrap_or_else(|_| reqwest::Client::new());
|
||||
ChatClient { http }
|
||||
}
|
||||
|
||||
/// One chat completion. Errors (missing key, network, non-2xx) propagate so
|
||||
/// the pool can fail over to the next candidate model.
|
||||
pub async fn chat(&self, m: &ModelRef, system: &str, user: &str) -> Result<String> {
|
||||
let p = provider_for(&m.provider)
|
||||
.ok_or_else(|| anyhow!("unknown provider '{}'", m.provider))?;
|
||||
let key = std::env::var(p.env_key).unwrap_or_default();
|
||||
if key.is_empty() && p.key != "ollama" {
|
||||
return Err(anyhow!("no API key ({}) for provider '{}'", p.env_key, p.key));
|
||||
}
|
||||
let url = format!("{}/chat/completions", p.base_url.trim_end_matches('/'));
|
||||
let body = serde_json::json!({
|
||||
"model": m.model,
|
||||
"max_tokens": 4096,
|
||||
"temperature": 0.2,
|
||||
"messages": [
|
||||
{"role": "system", "content": system},
|
||||
{"role": "user", "content": user}
|
||||
]
|
||||
});
|
||||
let mut req = self.http.post(&url).json(&body);
|
||||
if !key.is_empty() {
|
||||
req = req.bearer_auth(&key);
|
||||
}
|
||||
let resp = req.send().await?;
|
||||
let status = resp.status();
|
||||
let text = resp.text().await.unwrap_or_default();
|
||||
if !status.is_success() {
|
||||
return Err(anyhow!("{} returned {}: {}", p.key, status, truncate(&text, 200)));
|
||||
}
|
||||
let v: serde_json::Value = serde_json::from_str(&text)?;
|
||||
let content = v["choices"][0]["message"]["content"]
|
||||
.as_str()
|
||||
.ok_or_else(|| anyhow!("no content in response"))?;
|
||||
Ok(content.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl ChatClient {
|
||||
/// Complete via a locally-installed **agentic CLI subscription** (Claude
|
||||
/// 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}");
|
||||
let mut cmd = Command::new(bin);
|
||||
match bin {
|
||||
// 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);
|
||||
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" => {
|
||||
cmd.arg("--model").arg(model);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
cmd.stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped());
|
||||
let mut child = cmd.spawn().map_err(|e| anyhow!("spawn {} failed: {}", bin, e))?;
|
||||
if let Some(mut stdin) = child.stdin.take() {
|
||||
stdin.write_all(prompt.as_bytes()).await?;
|
||||
// Drop closes stdin so the CLI processes the prompt and exits.
|
||||
}
|
||||
let out = child.wait_with_output().await?;
|
||||
let stdout = String::from_utf8_lossy(&out.stdout).trim().to_string();
|
||||
let stderr = String::from_utf8_lossy(&out.stderr);
|
||||
if !out.status.success() {
|
||||
// The CLI often writes the real reason (rate limit, auth) to stdout,
|
||||
// not stderr — surface both plus the exit code so the error isn't blank.
|
||||
let detail = if !stderr.trim().is_empty() {
|
||||
stderr.trim().to_string()
|
||||
} else if !stdout.is_empty() {
|
||||
stdout.clone()
|
||||
} else {
|
||||
"no output".to_string()
|
||||
};
|
||||
return Err(anyhow!(
|
||||
"{} subscription CLI exit {}: {}",
|
||||
bin,
|
||||
out.status.code().map(|c| c.to_string()).unwrap_or_else(|| "signal".into()),
|
||||
truncate(&detail, 240)
|
||||
));
|
||||
}
|
||||
if stdout.is_empty() {
|
||||
return Err(anyhow!("{} subscription CLI returned empty output", bin));
|
||||
}
|
||||
Ok(stdout)
|
||||
}
|
||||
}
|
||||
|
||||
/// Map a provider to its local agentic CLI binary (subscription backend).
|
||||
pub fn cli_binary_for(provider: &str) -> Option<&'static str> {
|
||||
match provider {
|
||||
"anthropic" => Some("claude"),
|
||||
"openai" => Some("codex"),
|
||||
"xai" => Some("grok"),
|
||||
"gemini" => Some("gemini"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Is `name` an executable found on PATH?
|
||||
pub fn binary_in_path(name: &str) -> bool {
|
||||
std::env::var_os("PATH")
|
||||
.map(|path| std::env::split_paths(&path).any(|dir| dir.join(name).is_file()))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Which subscription CLI backends are installed locally.
|
||||
pub fn installed_cli_backends() -> Vec<&'static str> {
|
||||
["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 {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn truncate(s: &str, n: usize) -> String {
|
||||
if s.len() <= n {
|
||||
s.to_string()
|
||||
} else {
|
||||
format!("{}…", &s[..n])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,453 @@
|
||||
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.
|
||||
#[derive(Default, Serialize)]
|
||||
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\":\"...\"}.";
|
||||
|
||||
/// 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 / {} 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,
|
||||
if pool.mcp_config.is_some() { " · Playwright MCP ON" } else { "" },
|
||||
))
|
||||
.await;
|
||||
|
||||
// ---- 1. Recon ------------------------------------------------------
|
||||
let recon = if cfg.offline {
|
||||
let _ = tx.send("recon: offline mode — skipping model calls".into()).await;
|
||||
"{}".to_string()
|
||||
} else {
|
||||
match pool.complete(RECON_SYS, &format!("Target: {}", cfg.target)).await {
|
||||
Ok((m, t)) => {
|
||||
let _ = tx.send(format!("recon complete via {}", m.label())).await;
|
||||
t
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = tx.send(format!("recon failed ({e}) — continuing with empty recon")).await;
|
||||
"{}".to_string()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ---- 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 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 };
|
||||
}
|
||||
|
||||
// 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 raw: Vec<(String, String, Vec<Finding>)> = stream::iter(selected.iter().cloned())
|
||||
.map(|ag| {
|
||||
let target = target.clone();
|
||||
let recon = recon.clone();
|
||||
let txc = tx.clone();
|
||||
async move {
|
||||
let user = format!(
|
||||
"{}\n\nReply ONLY with a JSON array of confirmed findings (may be empty []). \
|
||||
Each item: {{id,title,severity,cwe,endpoint,payload,evidence,impact,remediation,confidence}}.",
|
||||
ag.user.replace("{target}", &target).replace("{recon_json}", &recon)
|
||||
);
|
||||
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;
|
||||
(ag.name.clone(), text, f)
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = txc.send(format!("exploit {} 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 by {}-model vote", candidates.len(), cfg.vote_n)).await;
|
||||
|
||||
// ---- 4. Validate by N-model voting ---------------------------------
|
||||
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 {} | {} | at {} | payload {} | evidence {}",
|
||||
f.title, f.severity, f.cwe, f.endpoint, f.payload, f.evidence
|
||||
);
|
||||
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;
|
||||
f
|
||||
}
|
||||
})
|
||||
.buffer_unordered(pool.candidates.len().max(2))
|
||||
.collect()
|
||||
.await;
|
||||
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(),
|
||||
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.
|
||||
///
|
||||
/// Models are inconsistent about field types — e.g. `confidence` may be a number
|
||||
/// (0.9), a numeric string ("0.9"), or a word ("High"); `cvss` may be a number or
|
||||
/// a string. Strict typed deserialization fails the whole batch on any mismatch,
|
||||
/// so we parse leniently into `Value` and coerce every field.
|
||||
fn extract_findings(text: &str, agent: &str) -> Vec<Finding> {
|
||||
let slice = match (text.find('['), text.rfind(']')) {
|
||||
(Some(a), Some(b)) if b > a => &text[a..=b],
|
||||
_ => match (text.find('{'), text.rfind('}')) {
|
||||
(Some(a), Some(b)) if b > a => &text[a..=b],
|
||||
_ => return vec![],
|
||||
},
|
||||
};
|
||||
let val: serde_json::Value = match serde_json::from_str(slice) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return vec![],
|
||||
};
|
||||
let items: Vec<serde_json::Value> = match val {
|
||||
serde_json::Value::Array(a) => a,
|
||||
serde_json::Value::Object(_) => vec![val],
|
||||
_ => return vec![],
|
||||
};
|
||||
items
|
||||
.into_iter()
|
||||
.filter_map(|it| {
|
||||
let o = it.as_object()?;
|
||||
let title = s(o, "title");
|
||||
if title.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some(Finding {
|
||||
id: {
|
||||
let id = s(o, "id");
|
||||
if id.is_empty() {
|
||||
format!("{}-{}", agent, title.chars().take(12).collect::<String>())
|
||||
} else {
|
||||
id
|
||||
}
|
||||
},
|
||||
agent: agent.to_string(),
|
||||
title,
|
||||
severity: norm_sev(&s(o, "severity")),
|
||||
cwe: s(o, "cwe"),
|
||||
cvss: s(o, "cvss"),
|
||||
endpoint: s(o, "endpoint"),
|
||||
payload: s(o, "payload"),
|
||||
evidence: s(o, "evidence"),
|
||||
impact: s(o, "impact"),
|
||||
remediation: s(o, "remediation"),
|
||||
confidence: conf(o.get("confidence")),
|
||||
validated: false,
|
||||
votes: String::new(),
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Coerce any JSON scalar to a trimmed string.
|
||||
fn s(o: &serde_json::Map<String, serde_json::Value>, k: &str) -> String {
|
||||
match o.get(k) {
|
||||
Some(serde_json::Value::String(v)) => v.trim().to_string(),
|
||||
Some(serde_json::Value::Number(n)) => n.to_string(),
|
||||
Some(serde_json::Value::Bool(b)) => b.to_string(),
|
||||
_ => String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Accept confidence as number, numeric string, or qualitative word.
|
||||
fn conf(v: Option<&serde_json::Value>) -> f64 {
|
||||
match v {
|
||||
Some(serde_json::Value::Number(n)) => n.as_f64().unwrap_or(0.0),
|
||||
Some(serde_json::Value::String(t)) => {
|
||||
if let Ok(f) = t.trim().parse::<f64>() {
|
||||
f
|
||||
} else {
|
||||
match t.to_lowercase().as_str() {
|
||||
s if s.contains("critical") || s.contains("very high") => 0.97,
|
||||
s if s.contains("high") => 0.9,
|
||||
s if s.contains("med") => 0.6,
|
||||
s if s.contains("low") => 0.3,
|
||||
_ => 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
fn norm_sev(s: &str) -> String {
|
||||
match s.to_lowercase().as_str() {
|
||||
x if x.starts_with("crit") => "Critical",
|
||||
x if x.starts_with("high") => "High",
|
||||
x if x.starts_with("med") => "Medium",
|
||||
x if x.starts_with("low") => "Low",
|
||||
"" => "Info",
|
||||
_ => "Info",
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
use crate::models::{cli_binary_for, ChatClient, ModelRef};
|
||||
use anyhow::{anyhow, Result};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Semaphore;
|
||||
|
||||
/// A pool of candidate models with a global concurrency cap and provider
|
||||
/// failover. The same panel of models is reused for validator voting.
|
||||
///
|
||||
/// `subscription = true` routes each model through its local agentic CLI
|
||||
/// (Claude Code / Codex / Grok login) instead of an HTTP API key.
|
||||
pub struct ModelPool {
|
||||
client: ChatClient,
|
||||
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, None)
|
||||
}
|
||||
|
||||
pub fn with_auth(
|
||||
models: Vec<ModelRef>,
|
||||
concurrency: usize,
|
||||
subscription: bool,
|
||||
mcp_config: Option<String>,
|
||||
) -> Self {
|
||||
// Subscription spawns one CLI process per call; too many in parallel
|
||||
// trips provider rate limits, so cap concurrency on that path.
|
||||
let concurrency = if subscription { concurrency.clamp(1, 3) } else { concurrency.max(1) };
|
||||
ModelPool {
|
||||
client: ChatClient::new(),
|
||||
sem: Arc::new(Semaphore::new(concurrency)),
|
||||
candidates: if models.is_empty() {
|
||||
vec![ModelRef::parse("anthropic:claude-opus-4-8")]
|
||||
} else {
|
||||
models
|
||||
},
|
||||
subscription,
|
||||
mcp_config,
|
||||
}
|
||||
}
|
||||
|
||||
/// One completion for a model, via subscription CLI (optionally with MCP) or
|
||||
/// HTTP API, with a short retry/backoff to ride out transient failures
|
||||
/// (rate limits, MCP cold-starts, network blips).
|
||||
async fn one(&self, m: &ModelRef, system: &str, user: &str) -> Result<String> {
|
||||
let use_cli = self.subscription && cli_binary_for(&m.provider).is_some();
|
||||
let mut last = anyhow::anyhow!("no attempt");
|
||||
for attempt in 0..3u64 {
|
||||
if attempt > 0 {
|
||||
// 1.5s, 4.5s backoff.
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1500 * attempt * attempt.max(1))).await;
|
||||
}
|
||||
let r = if use_cli {
|
||||
self.client
|
||||
.chat_cli(&m.provider, &m.model, system, user, self.mcp_config.as_deref())
|
||||
.await
|
||||
} else {
|
||||
self.client.chat(m, system, user).await
|
||||
};
|
||||
match r {
|
||||
Ok(t) => return Ok(t),
|
||||
Err(e) => last = e,
|
||||
}
|
||||
}
|
||||
Err(last)
|
||||
}
|
||||
|
||||
/// Complete a prompt, trying each candidate model until one succeeds.
|
||||
/// Returns the model that answered and its text.
|
||||
pub async fn complete(&self, system: &str, user: &str) -> Result<(ModelRef, String)> {
|
||||
let _permit = self.sem.acquire().await.expect("semaphore closed");
|
||||
let mut last = anyhow!("no candidate models");
|
||||
for m in &self.candidates {
|
||||
match self.one(m, system, user).await {
|
||||
Ok(text) => return Ok((m.clone(), text)),
|
||||
Err(e) => last = e,
|
||||
}
|
||||
}
|
||||
Err(last)
|
||||
}
|
||||
|
||||
/// Ask up to `n` distinct models the same yes/no validation question and
|
||||
/// return (confirmations, total_votes). A model answering "yes"/"confirmed"
|
||||
/// counts as a confirmation. Used to cut false positives.
|
||||
pub async fn vote(&self, system: &str, user: &str, n: usize) -> (usize, usize) {
|
||||
let panel: Vec<ModelRef> = self.candidates.iter().take(n.max(1)).cloned().collect();
|
||||
let mut confirmed = 0usize;
|
||||
let mut total = 0usize;
|
||||
for m in &panel {
|
||||
let _permit = match self.sem.acquire().await {
|
||||
Ok(p) => p,
|
||||
Err(_) => break,
|
||||
};
|
||||
if let Ok(text) = self.one(m, system, user).await {
|
||||
total += 1;
|
||||
let t = text.to_lowercase();
|
||||
if t.contains("\"verdict\": \"confirmed\"")
|
||||
|| t.trim_start().starts_with("yes")
|
||||
|| t.contains("confirmed: true")
|
||||
|| t.contains("is_real\": true")
|
||||
{
|
||||
confirmed += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
(confirmed, total)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
use crate::types::Finding;
|
||||
|
||||
fn sev_rank(s: &str) -> u8 {
|
||||
match s {
|
||||
"Critical" => 0,
|
||||
"High" => 1,
|
||||
"Medium" => 2,
|
||||
"Low" => 3,
|
||||
_ => 4,
|
||||
}
|
||||
}
|
||||
|
||||
fn sev_color(s: &str) -> &'static str {
|
||||
match s {
|
||||
"Critical" => "#c0392b",
|
||||
"High" => "#e67e22",
|
||||
"Medium" => "#f1c40f",
|
||||
"Low" => "#3498db",
|
||||
_ => "#7f8c8d",
|
||||
}
|
||||
}
|
||||
|
||||
fn esc(s: &str) -> String {
|
||||
s.replace('&', "&").replace('<', "<").replace('>', ">")
|
||||
}
|
||||
|
||||
/// Render an HTML report for the validated findings.
|
||||
pub fn html(target: &str, findings: &[Finding]) -> String {
|
||||
let mut sorted = findings.to_vec();
|
||||
sorted.sort_by_key(|f| sev_rank(&f.severity));
|
||||
|
||||
let mut counts: std::collections::BTreeMap<&str, usize> = Default::default();
|
||||
for f in &sorted {
|
||||
*counts.entry(f.severity.as_str()).or_default() += 1;
|
||||
}
|
||||
let chips: String = if counts.is_empty() {
|
||||
"<span class=chip style=background:#27ae60>No validated findings</span>".into()
|
||||
} else {
|
||||
counts
|
||||
.iter()
|
||||
.map(|(s, n)| format!("<span class=chip style=background:{}>{}: {}</span>", sev_color(s), s, n))
|
||||
.collect()
|
||||
};
|
||||
|
||||
let rows: String = sorted
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, f)| {
|
||||
format!(
|
||||
"<section class=finding><h3><span class=sev style=background:{}>{}</span> {}. {}</h3>\
|
||||
<div class=m>{} · {} · CVSS {} · votes {} · conf {:.2}</div>\
|
||||
<div class=m>Endpoint: {}</div>\
|
||||
<h4>Payload</h4><pre>{}</pre><h4>Evidence</h4><pre>{}</pre>\
|
||||
<h4>Impact</h4><p>{}</p><h4>Remediation</h4><p>{}</p></section>",
|
||||
sev_color(&f.severity), esc(&f.severity), i + 1, esc(&f.title),
|
||||
esc(&f.agent), esc(&f.cwe), esc(&f.cvss), esc(&f.votes), f.confidence,
|
||||
esc(&f.endpoint), esc(&f.payload), esc(&f.evidence), esc(&f.impact), esc(&f.remediation),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let body = if rows.is_empty() {
|
||||
"<p><em>No validated findings were produced for this engagement.</em></p>".to_string()
|
||||
} else {
|
||||
rows
|
||||
};
|
||||
|
||||
format!(
|
||||
"<!DOCTYPE html><html><head><meta charset=utf-8><title>NeuroSploit Report — {t}</title><style>\
|
||||
body{{font:14px/1.6 -apple-system,Segoe UI,Roboto,sans-serif;color:#1a1a1a;max-width:860px;margin:40px auto;padding:0 24px}}\
|
||||
h1{{margin:0}}.meta{{color:#666;margin:4px 0 18px}}.chip{{color:#fff;border-radius:999px;padding:4px 12px;margin-right:8px;font-size:13px;font-weight:600}}\
|
||||
.finding{{border:1px solid #e3e3e3;border-radius:12px;padding:16px 20px;margin:16px 0}}.finding h3{{margin:0 0 8px;font-size:16px}}\
|
||||
.sev{{color:#fff;border-radius:6px;padding:2px 8px;font-size:12px;margin-right:8px}}.m{{color:#666;font-size:12px}}\
|
||||
pre{{background:#0f1117;color:#dfe6f3;padding:11px;border-radius:8px;overflow:auto;font-size:12.5px}}\
|
||||
h4{{margin:12px 0 3px;font-size:12px;text-transform:uppercase;letter-spacing:.5px;color:#8b5cf6}}\
|
||||
.b{{color:#8b5cf6;font-weight:800}}</style></head><body>\
|
||||
<h1><span class=b>NeuroSploit</span> Penetration Test Report</h1>\
|
||||
<div class=meta>Target: <b>{t}</b> · v3.4.0 Rust harness · multi-model validated</div>\
|
||||
<div>{chips}</div><h2>Findings ({n})</h2>{body}\
|
||||
<p class=meta>Authorized testing only. Findings confirmed by multi-model adversarial voting.</p></body></html>",
|
||||
t = esc(target), chips = chips, n = sorted.len(), body = body,
|
||||
)
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A validated (or candidate) security finding.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Finding {
|
||||
pub id: String,
|
||||
pub agent: String,
|
||||
pub title: String,
|
||||
pub severity: String,
|
||||
#[serde(default)]
|
||||
pub cwe: String,
|
||||
#[serde(default)]
|
||||
pub cvss: String,
|
||||
#[serde(default)]
|
||||
pub endpoint: String,
|
||||
#[serde(default)]
|
||||
pub payload: String,
|
||||
#[serde(default)]
|
||||
pub evidence: String,
|
||||
#[serde(default)]
|
||||
pub impact: String,
|
||||
#[serde(default)]
|
||||
pub remediation: String,
|
||||
#[serde(default)]
|
||||
pub confidence: f64,
|
||||
#[serde(default)]
|
||||
pub validated: bool,
|
||||
/// Per-model vote summary, e.g. "3/4 confirmed".
|
||||
#[serde(default)]
|
||||
pub votes: String,
|
||||
}
|
||||
|
||||
impl Default for Finding {
|
||||
fn default() -> Self {
|
||||
Finding {
|
||||
id: String::new(),
|
||||
agent: String::new(),
|
||||
title: String::new(),
|
||||
severity: "Info".into(),
|
||||
cwe: String::new(),
|
||||
cvss: String::new(),
|
||||
endpoint: String::new(),
|
||||
payload: String::new(),
|
||||
evidence: String::new(),
|
||||
impact: String::new(),
|
||||
remediation: String::new(),
|
||||
confidence: 0.0,
|
||||
validated: false,
|
||||
votes: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration for a single engagement run.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RunConfig {
|
||||
pub target: String,
|
||||
/// Model references in `provider:model` form. The first is primary; the
|
||||
/// rest are failover candidates and also the voting panel.
|
||||
pub models: Vec<String>,
|
||||
/// Number of models that cross-check each candidate finding.
|
||||
#[serde(default = "default_vote")]
|
||||
pub vote_n: usize,
|
||||
/// Max concurrent model calls.
|
||||
#[serde(default = "default_concurrency")]
|
||||
pub concurrency: usize,
|
||||
/// Cap on specialist agents to run (0 = all).
|
||||
#[serde(default)]
|
||||
pub max_agents: usize,
|
||||
/// Offline mode: exercise the full pipeline without calling any model API.
|
||||
#[serde(default)]
|
||||
pub offline: bool,
|
||||
/// Use local agentic CLI subscriptions (Claude Code / Codex / Grok) instead
|
||||
/// 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 {
|
||||
3
|
||||
}
|
||||
fn default_concurrency() -> usize {
|
||||
8
|
||||
}
|
||||
|
||||
impl RunConfig {
|
||||
pub fn new(target: impl Into<String>) -> Self {
|
||||
RunConfig {
|
||||
target: target.into(),
|
||||
models: vec!["anthropic:claude-opus-4-8".into()],
|
||||
vote_n: 3,
|
||||
concurrency: 8,
|
||||
max_agents: 0,
|
||||
offline: false,
|
||||
subscription: false,
|
||||
workdir: None,
|
||||
rl_path: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
|
||||
+104
File diff suppressed because one or more lines are too long
+104
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+104
File diff suppressed because one or more lines are too long
+104
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,98 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>A potentially dangerous Request.QueryString value was detected from the client (id="<h1>HTML_INJECTED</h...").</title>
|
||||
<style>
|
||||
body {font-family:"Verdana";font-weight:normal;font-size: .7em;color:black;}
|
||||
p {font-family:"Verdana";font-weight:normal;color:black;margin-top: -5px}
|
||||
b {font-family:"Verdana";font-weight:bold;color:black;margin-top: -5px}
|
||||
H1 { font-family:"Verdana";font-weight:normal;font-size:18pt;color:red }
|
||||
H2 { font-family:"Verdana";font-weight:normal;font-size:14pt;color:maroon }
|
||||
pre {font-family:"Lucida Console";font-size: .9em}
|
||||
.marker {font-weight: bold; color: black;text-decoration: none;}
|
||||
.version {color: gray;}
|
||||
.error {margin-bottom: 10px;}
|
||||
.expandable { text-decoration:underline; font-weight:bold; color:navy; cursor:hand; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body bgcolor="white">
|
||||
|
||||
<span><H1>Server Error in '/' Application.<hr width=100% size=1 color=silver></H1>
|
||||
|
||||
<h2> <i>A potentially dangerous Request.QueryString value was detected from the client (id="<h1>HTML_INJECTED</h...").</i> </h2></span>
|
||||
|
||||
<font face="Arial, Helvetica, Geneva, SunSans-Regular, sans-serif ">
|
||||
|
||||
<b> Description: </b>Request Validation has detected a potentially dangerous client input value, and processing of the request has been aborted. This value may indicate an attempt to compromise the security of your application, such as a cross-site scripting attack. You can disable request validation by setting validateRequest=false in the Page directive or in the <pages> configuration section. However, it is strongly recommended that your application explicitly check all inputs in this case.
|
||||
<br><br>
|
||||
|
||||
<b> Exception Details: </b>System.Web.HttpRequestValidationException: A potentially dangerous Request.QueryString value was detected from the client (id="<h1>HTML_INJECTED</h...").<br><br>
|
||||
|
||||
<b>Source Error:</b> <br><br>
|
||||
|
||||
<table width=100% bgcolor="#ffffcc">
|
||||
<tr>
|
||||
<td>
|
||||
<code><pre>
|
||||
|
||||
[No relevant source lines]</pre></code>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br>
|
||||
|
||||
<b> Source File: </b> c:\Windows\Microsoft.NET\Framework64\v2.0.50727\Temporary ASP.NET Files\root\e6eb278b\4a52d72d\App_Web_pebpzm2g.6.cs<b> Line: </b> 0
|
||||
<br><br>
|
||||
|
||||
<b>Stack Trace:</b> <br><br>
|
||||
|
||||
<table width=100% bgcolor="#ffffcc">
|
||||
<tr>
|
||||
<td>
|
||||
<code><pre>
|
||||
|
||||
[HttpRequestValidationException (0x80004005): A potentially dangerous Request.QueryString value was detected from the client (id="<h1>HTML_INJECTED</h...").]
|
||||
System.Web.HttpRequest.ValidateString(String s, String valueName, String collectionName) +11208427
|
||||
System.Web.HttpRequest.ValidateNameValueCollection(NameValueCollection nvc, String collectionName) +71
|
||||
System.Web.HttpRequest.get_QueryString() +178
|
||||
System.Web.UI.Page.DeterminePostBackMode() +83
|
||||
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +11174087
|
||||
System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +11173626
|
||||
System.Web.UI.Page.ProcessRequest() +91
|
||||
System.Web.UI.Page.ProcessRequest(HttpContext context) +240
|
||||
ASP.readnews_aspx.ProcessRequest(HttpContext context) in c:\Windows\Microsoft.NET\Framework64\v2.0.50727\Temporary ASP.NET Files\root\e6eb278b\4a52d72d\App_Web_pebpzm2g.6.cs:0
|
||||
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +599
|
||||
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +171
|
||||
</pre></code>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br>
|
||||
|
||||
<hr width=100% size=1 color=silver>
|
||||
|
||||
<b>Version Information:</b> Microsoft .NET Framework Version:2.0.50727.8974; ASP.NET Version:2.0.50727.8974
|
||||
|
||||
</font>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
<!--
|
||||
[HttpRequestValidationException]: A potentially dangerous Request.QueryString value was detected from the client (id="<h1>HTML_INJECTED</h...").
|
||||
at System.Web.HttpRequest.ValidateString(String s, String valueName, String collectionName)
|
||||
at System.Web.HttpRequest.ValidateNameValueCollection(NameValueCollection nvc, String collectionName)
|
||||
at System.Web.HttpRequest.get_QueryString()
|
||||
at System.Web.UI.Page.DeterminePostBackMode()
|
||||
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
|
||||
at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
|
||||
at System.Web.UI.Page.ProcessRequest()
|
||||
at System.Web.UI.Page.ProcessRequest(HttpContext context)
|
||||
at ASP.readnews_aspx.ProcessRequest(HttpContext context) in c:\Windows\Microsoft.NET\Framework64\v2.0.50727\Temporary ASP.NET Files\root\e6eb278b\4a52d72d\App_Web_pebpzm2g.6.cs:line 0
|
||||
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
|
||||
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
|
||||
--><!--
|
||||
This error page might contain sensitive information because ASP.NET is configured to show verbose error messages using <customErrors mode="Off"/>. Consider using <customErrors mode="On"/> or <customErrors mode="RemoteOnly"/> in production environments.-->
|
||||
@@ -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()
|
||||
@@ -0,0 +1,51 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>The resource cannot be found.</title>
|
||||
<style>
|
||||
body {font-family:"Verdana";font-weight:normal;font-size: .7em;color:black;}
|
||||
p {font-family:"Verdana";font-weight:normal;color:black;margin-top: -5px}
|
||||
b {font-family:"Verdana";font-weight:bold;color:black;margin-top: -5px}
|
||||
H1 { font-family:"Verdana";font-weight:normal;font-size:18pt;color:red }
|
||||
H2 { font-family:"Verdana";font-weight:normal;font-size:14pt;color:maroon }
|
||||
pre {font-family:"Lucida Console";font-size: .9em}
|
||||
.marker {font-weight: bold; color: black;text-decoration: none;}
|
||||
.version {color: gray;}
|
||||
.error {margin-bottom: 10px;}
|
||||
.expandable { text-decoration:underline; font-weight:bold; color:navy; cursor:hand; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body bgcolor="white">
|
||||
|
||||
<span><H1>Server Error in '/' Application.<hr width=100% size=1 color=silver></H1>
|
||||
|
||||
<h2> <i>The resource cannot be found.</i> </h2></span>
|
||||
|
||||
<font face="Arial, Helvetica, Geneva, SunSans-Regular, sans-serif ">
|
||||
|
||||
<b> Description: </b>HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
|
||||
<br><br>
|
||||
|
||||
<b> Requested URL: </b>/Search.aspx<br><br>
|
||||
|
||||
<hr width=100% size=1 color=silver>
|
||||
|
||||
<b>Version Information:</b> Microsoft .NET Framework Version:2.0.50727.8974; ASP.NET Version:2.0.50727.8974
|
||||
|
||||
</font>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
<!--
|
||||
[HttpException]: The file '/Search.aspx' does not exist.
|
||||
at System.Web.UI.Util.CheckVirtualFileExists(VirtualPath virtualPath)
|
||||
at System.Web.Compilation.BuildManager.GetVPathBuildResultInternal(VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile)
|
||||
at System.Web.Compilation.BuildManager.GetVPathBuildResultWithNoAssert(HttpContext context, VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile)
|
||||
at System.Web.Compilation.BuildManager.GetVirtualPathObjectFactory(VirtualPath virtualPath, HttpContext context, Boolean allowCrossApp, Boolean noAssert)
|
||||
at System.Web.Compilation.BuildManager.CreateInstanceFromVirtualPath(VirtualPath virtualPath, Type requiredBaseType, HttpContext context, Boolean allowCrossApp, Boolean noAssert)
|
||||
at System.Web.UI.PageHandlerFactory.GetHandlerHelper(HttpContext context, String requestType, VirtualPath virtualPath, String physicalPath)
|
||||
at System.Web.HttpApplication.MapHttpHandler(HttpContext context, String requestType, VirtualPath path, String pathTranslated, Boolean useAppConfig)
|
||||
at System.Web.HttpApplication.MapHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
|
||||
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
|
||||
--><!--
|
||||
This error page might contain sensitive information because ASP.NET is configured to show verbose error messages using <customErrors mode="Off"/>. Consider using <customErrors mode="On"/> or <customErrors mode="RemoteOnly"/> in production environments.-->
|
||||
@@ -0,0 +1,5 @@
|
||||
# Netscape HTTP Cookie File
|
||||
# https://curl.se/docs/http-cookies.html
|
||||
# This file was generated by libcurl! Edit at your own risk.
|
||||
|
||||
#HttpOnly_testaspnet.vulnweb.com FALSE / FALSE 0 ASP.NET_SessionId gd4l4l454mqpkf3wbxfftuiq
|
||||
@@ -0,0 +1,5 @@
|
||||
# Netscape HTTP Cookie File
|
||||
# https://curl.se/docs/http-cookies.html
|
||||
# This file was generated by libcurl! Edit at your own risk.
|
||||
|
||||
#HttpOnly_testaspnet.vulnweb.com FALSE / FALSE 0 ASP.NET_SessionId 3rcifo453uarvx55eyggfr45
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user