Add a complete AI pentest system where Claude autonomously drives security testing via tool-calling. The LLM selects from 16 tools, chains results, and builds an attack chain DAG. Core: - PentestTool trait (dyn-compatible) with PentestToolContext/Result - PentestSession, AttackChainNode, PentestMessage, PentestEvent models - 10 new DastVulnType variants (DNS, DMARC, TLS, cookies, CSP, CORS, etc.) - LLM client chat_with_tools() for OpenAI-compatible tool calling Tools (16 total): - 5 agent wrappers: SQL injection, XSS, auth bypass, SSRF, API fuzzer - 11 new infra tools: DNS checker, DMARC checker, TLS analyzer, security headers, cookie analyzer, CSP analyzer, rate limit tester, console log detector, CORS checker, OpenAPI parser, recon - ToolRegistry for tool lookup and LLM definition generation Orchestrator: - PentestOrchestrator with iterative tool-calling loop (max 50 rounds) - Attack chain node recording per tool invocation - SSE event broadcasting for real-time progress - Strategy-aware system prompts (quick/comprehensive/targeted/aggressive/stealth) API (9 endpoints): - POST/GET /pentest/sessions, GET /pentest/sessions/:id - POST /pentest/sessions/:id/chat, GET /pentest/sessions/:id/stream - GET /pentest/sessions/:id/attack-chain, messages, findings - GET /pentest/stats Dashboard: - Pentest dashboard with stat cards, severity distribution, session list - Chat-based session page with split layout (chat + findings/attack chain) - Inline tool execution indicators, auto-polling, new session modal - Sidebar navigation item Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
146 lines
5.1 KiB
Rust
146 lines
5.1 KiB
Rust
use axum::routing::{delete, get, patch, post};
|
|
use axum::Router;
|
|
|
|
use crate::api::handlers;
|
|
use crate::webhooks;
|
|
|
|
pub fn build_router() -> Router {
|
|
Router::new()
|
|
.route("/api/v1/health", get(handlers::health))
|
|
.route("/api/v1/stats/overview", get(handlers::stats_overview))
|
|
.route(
|
|
"/api/v1/settings/ssh-public-key",
|
|
get(handlers::get_ssh_public_key),
|
|
)
|
|
.route("/api/v1/repositories", get(handlers::list_repositories))
|
|
.route("/api/v1/repositories", post(handlers::add_repository))
|
|
.route(
|
|
"/api/v1/repositories/{id}/scan",
|
|
post(handlers::trigger_scan),
|
|
)
|
|
.route(
|
|
"/api/v1/repositories/{id}",
|
|
delete(handlers::delete_repository).patch(handlers::update_repository),
|
|
)
|
|
.route(
|
|
"/api/v1/repositories/{id}/webhook-config",
|
|
get(handlers::get_webhook_config),
|
|
)
|
|
.route("/api/v1/findings", get(handlers::list_findings))
|
|
.route("/api/v1/findings/{id}", get(handlers::get_finding))
|
|
.route(
|
|
"/api/v1/findings/{id}/status",
|
|
patch(handlers::update_finding_status),
|
|
)
|
|
.route(
|
|
"/api/v1/findings/bulk-status",
|
|
patch(handlers::bulk_update_finding_status),
|
|
)
|
|
.route(
|
|
"/api/v1/findings/{id}/feedback",
|
|
patch(handlers::update_finding_feedback),
|
|
)
|
|
.route("/api/v1/sbom", get(handlers::list_sbom))
|
|
.route("/api/v1/sbom/filters", get(handlers::sbom_filters))
|
|
.route("/api/v1/sbom/export", get(handlers::export_sbom))
|
|
.route("/api/v1/sbom/licenses", get(handlers::license_summary))
|
|
.route("/api/v1/sbom/diff", get(handlers::sbom_diff))
|
|
.route("/api/v1/issues", get(handlers::list_issues))
|
|
.route("/api/v1/scan-runs", get(handlers::list_scan_runs))
|
|
// Graph API endpoints
|
|
.route("/api/v1/graph/{repo_id}", get(handlers::graph::get_graph))
|
|
.route(
|
|
"/api/v1/graph/{repo_id}/nodes",
|
|
get(handlers::graph::get_nodes),
|
|
)
|
|
.route(
|
|
"/api/v1/graph/{repo_id}/communities",
|
|
get(handlers::graph::get_communities),
|
|
)
|
|
.route(
|
|
"/api/v1/graph/{repo_id}/impact/{finding_id}",
|
|
get(handlers::graph::get_impact),
|
|
)
|
|
.route(
|
|
"/api/v1/graph/{repo_id}/search",
|
|
get(handlers::graph::search_symbols),
|
|
)
|
|
.route(
|
|
"/api/v1/graph/{repo_id}/file-content",
|
|
get(handlers::graph::get_file_content),
|
|
)
|
|
.route(
|
|
"/api/v1/graph/{repo_id}/build",
|
|
post(handlers::graph::trigger_build),
|
|
)
|
|
// DAST API endpoints
|
|
.route("/api/v1/dast/targets", get(handlers::dast::list_targets))
|
|
.route("/api/v1/dast/targets", post(handlers::dast::add_target))
|
|
.route(
|
|
"/api/v1/dast/targets/{id}/scan",
|
|
post(handlers::dast::trigger_scan),
|
|
)
|
|
.route(
|
|
"/api/v1/dast/scan-runs",
|
|
get(handlers::dast::list_scan_runs),
|
|
)
|
|
.route("/api/v1/dast/findings", get(handlers::dast::list_findings))
|
|
.route(
|
|
"/api/v1/dast/findings/{id}",
|
|
get(handlers::dast::get_finding),
|
|
)
|
|
// Chat / RAG API endpoints
|
|
.route("/api/v1/chat/{repo_id}", post(handlers::chat::chat))
|
|
.route(
|
|
"/api/v1/chat/{repo_id}/build-embeddings",
|
|
post(handlers::chat::build_embeddings),
|
|
)
|
|
.route(
|
|
"/api/v1/chat/{repo_id}/status",
|
|
get(handlers::chat::embedding_status),
|
|
)
|
|
// Pentest API endpoints
|
|
.route(
|
|
"/api/v1/pentest/sessions",
|
|
get(handlers::pentest::list_sessions).post(handlers::pentest::create_session),
|
|
)
|
|
.route(
|
|
"/api/v1/pentest/sessions/{id}",
|
|
get(handlers::pentest::get_session),
|
|
)
|
|
.route(
|
|
"/api/v1/pentest/sessions/{id}/chat",
|
|
post(handlers::pentest::send_message),
|
|
)
|
|
.route(
|
|
"/api/v1/pentest/sessions/{id}/stream",
|
|
get(handlers::pentest::session_stream),
|
|
)
|
|
.route(
|
|
"/api/v1/pentest/sessions/{id}/attack-chain",
|
|
get(handlers::pentest::get_attack_chain),
|
|
)
|
|
.route(
|
|
"/api/v1/pentest/sessions/{id}/messages",
|
|
get(handlers::pentest::get_messages),
|
|
)
|
|
.route(
|
|
"/api/v1/pentest/sessions/{id}/findings",
|
|
get(handlers::pentest::get_session_findings),
|
|
)
|
|
.route("/api/v1/pentest/stats", get(handlers::pentest::pentest_stats))
|
|
// Webhook endpoints (proxied through dashboard)
|
|
.route(
|
|
"/webhook/github/{repo_id}",
|
|
post(webhooks::github::handle_github_webhook),
|
|
)
|
|
.route(
|
|
"/webhook/gitlab/{repo_id}",
|
|
post(webhooks::gitlab::handle_gitlab_webhook),
|
|
)
|
|
.route(
|
|
"/webhook/gitea/{repo_id}",
|
|
post(webhooks::gitea::handle_gitea_webhook),
|
|
)
|
|
}
|