fix: attack chain node linking and disable input while pentest runs
Some checks failed
CI / Security Audit (push) Has been skipped
CI / Tests (push) Has been skipped
CI / Detect Changes (push) Has been skipped
CI / Deploy Agent (push) Has been skipped
CI / Deploy Dashboard (push) Has been skipped
CI / Deploy Docs (push) Has been skipped
CI / Deploy MCP (push) Has been skipped
CI / Format (push) Failing after 4s
CI / Clippy (push) Failing after 1m42s
Some checks failed
CI / Security Audit (push) Has been skipped
CI / Tests (push) Has been skipped
CI / Detect Changes (push) Has been skipped
CI / Deploy Agent (push) Has been skipped
CI / Deploy Dashboard (push) Has been skipped
CI / Deploy Docs (push) Has been skipped
CI / Deploy MCP (push) Has been skipped
CI / Format (push) Failing after 4s
CI / Clippy (push) Failing after 1m42s
Link attack chain nodes to previous iteration's nodes via parent_node_ids so the DAG graph shows proper hierarchy instead of flat dots. Disable the chat input while a pentest session is running since messages have no effect. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -172,6 +172,7 @@ impl PentestOrchestrator {
|
|||||||
let mut total_findings = 0u32;
|
let mut total_findings = 0u32;
|
||||||
let mut total_tool_calls = 0u32;
|
let mut total_tool_calls = 0u32;
|
||||||
let mut total_successes = 0u32;
|
let mut total_successes = 0u32;
|
||||||
|
let mut prev_node_ids: Vec<String> = Vec::new();
|
||||||
|
|
||||||
for _iteration in 0..max_iterations {
|
for _iteration in 0..max_iterations {
|
||||||
let response = self
|
let response = self
|
||||||
@@ -233,6 +234,8 @@ impl PentestOrchestrator {
|
|||||||
tool_call_id: None,
|
tool_call_id: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let mut current_batch_node_ids: Vec<String> = Vec::new();
|
||||||
|
|
||||||
for tc in &tool_calls {
|
for tc in &tool_calls {
|
||||||
total_tool_calls += 1;
|
total_tool_calls += 1;
|
||||||
let node_id = uuid::Uuid::new_v4().to_string();
|
let node_id = uuid::Uuid::new_v4().to_string();
|
||||||
@@ -244,9 +247,12 @@ impl PentestOrchestrator {
|
|||||||
tc.arguments.clone(),
|
tc.arguments.clone(),
|
||||||
String::new(),
|
String::new(),
|
||||||
);
|
);
|
||||||
|
// Link to previous iteration's nodes
|
||||||
|
node.parent_node_ids = prev_node_ids.clone();
|
||||||
node.status = AttackNodeStatus::Running;
|
node.status = AttackNodeStatus::Running;
|
||||||
node.started_at = Some(chrono::Utc::now());
|
node.started_at = Some(chrono::Utc::now());
|
||||||
let _ = self.db.attack_chain_nodes().insert_one(&node).await;
|
let _ = self.db.attack_chain_nodes().insert_one(&node).await;
|
||||||
|
current_batch_node_ids.push(node_id.clone());
|
||||||
|
|
||||||
let _ = self.event_tx.send(PentestEvent::ToolStart {
|
let _ = self.event_tx.send(PentestEvent::ToolStart {
|
||||||
node_id: node_id.clone(),
|
node_id: node_id.clone(),
|
||||||
@@ -337,6 +343,9 @@ impl PentestOrchestrator {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Advance parent links so next iteration's nodes connect to this batch
|
||||||
|
prev_node_ids = current_batch_node_ids;
|
||||||
|
|
||||||
if let Some(sid) = session.id {
|
if let Some(sid) = session.id {
|
||||||
let _ = self
|
let _ = self
|
||||||
.db
|
.db
|
||||||
|
|||||||
@@ -560,27 +560,34 @@ pub fn PentestSessionPage(session_id: String) -> Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Input area
|
// Input area — disabled while pentest is running (messages have no effect)
|
||||||
div { style: "flex-shrink: 0; padding: 12px; border-top: 1px solid var(--border-color); display: flex; gap: 8px;",
|
if is_running {
|
||||||
textarea {
|
div { style: "flex-shrink: 0; padding: 12px; border-top: 1px solid var(--border-color); text-align: center; color: var(--text-secondary); font-size: 0.85rem;",
|
||||||
class: "chat-input",
|
Icon { icon: BsPlayCircle, width: 14, height: 14 }
|
||||||
style: "flex: 1;",
|
" Pentest is running — input disabled until complete"
|
||||||
placeholder: "Guide the pentest agent...",
|
|
||||||
value: "{input_text}",
|
|
||||||
oninput: move |e| input_text.set(e.value()),
|
|
||||||
onkeydown: move |e: Event<KeyboardData>| {
|
|
||||||
if e.key() == Key::Enter && !e.modifiers().shift() {
|
|
||||||
e.prevent_default();
|
|
||||||
do_send();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
button {
|
} else {
|
||||||
class: "btn btn-primary",
|
div { style: "flex-shrink: 0; padding: 12px; border-top: 1px solid var(--border-color); display: flex; gap: 8px;",
|
||||||
style: "align-self: flex-end;",
|
textarea {
|
||||||
disabled: *sending.read(),
|
class: "chat-input",
|
||||||
onclick: move |_| do_send_click(),
|
style: "flex: 1;",
|
||||||
"Send"
|
placeholder: "Guide the pentest agent...",
|
||||||
|
value: "{input_text}",
|
||||||
|
oninput: move |e| input_text.set(e.value()),
|
||||||
|
onkeydown: move |e: Event<KeyboardData>| {
|
||||||
|
if e.key() == Key::Enter && !e.modifiers().shift() {
|
||||||
|
e.prevent_default();
|
||||||
|
do_send();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
class: "btn btn-primary",
|
||||||
|
style: "align-self: flex-end;",
|
||||||
|
disabled: *sending.read(),
|
||||||
|
onclick: move |_| do_send_click(),
|
||||||
|
"Send"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user