Skip to content

Commit 12937f9

Browse files
committed
fix: Resolve agent_id from case entities, count auto_verdict for FP rate (refs #25)
Three fixes from @nikopuf's testing: 1. block_ip agent_id: Instead of hardcoding "001" (which may not exist), resolve agent_id from the case's host entity (e.g., agent_id: "002"). Falls back to "all" if no entity has an agent_id. 2. false_positive_rate: KPI endpoint now counts cases where auto_verdict is "false_positive" in addition to status === "false_positive". The investigation agent sets auto_verdict but the pipeline status continues to executed/closed, so the previous check missed them. 3. Better error context: Wazuh 400 errors now include a descriptive note explaining the likely cause per action type.
1 parent 4d03f8d commit 12937f9

File tree

2 files changed

+33
-10
lines changed

2 files changed

+33
-10
lines changed

runtime/autopilot-service/execution.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,8 +403,8 @@ describe("Plan Execution (Responder Enabled)", () => {
403403
assert.equal(mcpCall.method, "POST");
404404
assert.ok(mcpCall.url.includes("block_ip"));
405405
// buildMcpParams injects action.target as the target param name (fallback: "target")
406-
// block_ip now auto-injects agent_id: "001" (Wazuh manager) when not specified
407-
assert.deepStrictEqual(mcpCall.body, { ip: "10.0.0.42", direction: "both", target: "10.0.0.42", agent_id: "001" });
406+
// block_ip resolves agent_id from case entities, falls back to "all"
407+
assert.deepStrictEqual(mcpCall.body, { ip: "10.0.0.42", direction: "both", target: "10.0.0.42", agent_id: "all" });
408408
assert.ok(mcpCall.headers["authorization"]);
409409
assert.ok(mcpCall.headers["x-correlation-id"]);
410410
});

runtime/autopilot-service/index.js

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2055,8 +2055,28 @@ async function executePlan(planId, executorId) {
20552055
const results = [];
20562056
let allSuccess = true;
20572057

2058+
// Resolve agent_id from case entities for actions that need it (e.g., block_ip)
2059+
// This avoids hardcoding agent IDs and uses the actual reporting agent from the case
2060+
let caseAgentId = null;
2061+
try {
2062+
const caseData = await getCase(plan.case_id);
2063+
if (caseData) {
2064+
const hostEntity = (caseData.entities || []).find(e => e.type === "host" && e.agent_id);
2065+
if (hostEntity) {
2066+
caseAgentId = hostEntity.agent_id;
2067+
log("info", "plans", "Resolved agent_id from case entities", {
2068+
plan_id: planId, agent_id: caseAgentId, host: hostEntity.value,
2069+
});
2070+
}
2071+
}
2072+
} catch { /* best effort */ }
2073+
20582074
try {
20592075
for (const action of plan.actions) {
2076+
// Inject resolved agent_id for actions that need it
2077+
if (caseAgentId && !action.agent_id && !(action.params && action.params.agent_id)) {
2078+
action._case_agent_id = caseAgentId;
2079+
}
20602080
try {
20612081
// Validate action has required fields
20622082
if (!action.type || !action.target) {
@@ -2956,17 +2976,18 @@ function buildMcpParams(action) {
29562976
}
29572977

29582978
// For block_ip: ensure agent_id is set. Wazuh active-response API requires a
2959-
// specific agent ID, not "all". If the plan doesn't include agent_id, try to
2960-
// resolve it from the case entities or default to the reporting agent.
2979+
// specific agent ID, not "all". Resolve from plan params, action, or case entities.
29612980
if (action.type === "block_ip" && !params.agent_id) {
2962-
// Check if the plan action has an agent_id in params (LLM should include it)
2963-
// If not, try action.agent_id, then fall back to "001" (Wazuh manager)
29642981
if (action.agent_id) {
29652982
params.agent_id = action.agent_id;
2983+
} else if (action._case_agent_id) {
2984+
// Resolved from case entities during executePlan() — see below
2985+
params.agent_id = action._case_agent_id;
29662986
} else {
2967-
// Default to Wazuh manager agent (001) which can execute AR on behalf of all agents
2968-
params.agent_id = "001";
2969-
log("warn", "plans", "block_ip action missing agent_id, defaulting to 001 (manager)", {
2987+
// Last resort: use "all" and let the MCP Server handle it
2988+
// Note: Wazuh 4.14+ may reject "all" — prefer specific agent IDs
2989+
params.agent_id = "all";
2990+
log("warn", "plans", "block_ip action missing agent_id, using 'all' (may fail on some Wazuh versions)", {
29702991
target: action.target, action_type: action.type,
29712992
});
29722993
}
@@ -5529,7 +5550,9 @@ function createServer() {
55295550
if (delta >= 0) mttcValues.push(delta);
55305551
}
55315552

5532-
if (caseData.status === "false_positive") falsePositives++;
5553+
// Count false positives by status OR auto_verdict (investigation may classify
5554+
// as false_positive while the pipeline status continues to executed/closed)
5555+
if (caseData.status === "false_positive" || caseData.auto_verdict === "false_positive") falsePositives++;
55335556
} catch {
55345557
// Skip cases that can't be read
55355558
}

0 commit comments

Comments
 (0)