pentesting 0.73.2 → 0.73.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-tool-JEFUBDZE.js +989 -0
- package/dist/{chunk-BGEXGHPB.js → chunk-BKWCGMSV.js} +879 -427
- package/dist/{chunk-YFDJI3GO.js → chunk-GLO6TOJN.js} +2 -0
- package/dist/{chunk-KBJPZDIL.js → chunk-UB7RW6LM.js} +267 -153
- package/dist/main.js +1377 -196
- package/dist/{persistence-VFIOGTRC.js → persistence-2WKQHGOL.js} +2 -2
- package/dist/{process-registry-GSHEX2LT.js → process-registry-QIW7ZIUT.js} +1 -1
- package/dist/prompts/main-agent.md +35 -1
- package/dist/prompts/strategist-system.md +34 -0
- package/package.json +1 -1
- package/dist/agent-tool-HYQGTZC4.js +0 -256
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
debugLog,
|
|
47
47
|
ensureDirExists,
|
|
48
48
|
generateId,
|
|
49
|
+
generatePrefixedId,
|
|
49
50
|
getActiveSessionRuntime,
|
|
50
51
|
getErrorMessage,
|
|
51
52
|
getProcessOutput,
|
|
@@ -60,11 +61,12 @@ import {
|
|
|
60
61
|
startBackgroundProcess,
|
|
61
62
|
stopBackgroundProcess,
|
|
62
63
|
writeFileContent
|
|
63
|
-
} from "./chunk-
|
|
64
|
+
} from "./chunk-UB7RW6LM.js";
|
|
64
65
|
import {
|
|
65
66
|
DETECTION_PATTERNS,
|
|
66
67
|
HEALTH_CONFIG,
|
|
67
68
|
PROCESS_ACTIONS,
|
|
69
|
+
PROCESS_EVENTS,
|
|
68
70
|
PROCESS_ICONS,
|
|
69
71
|
PROCESS_LIMITS,
|
|
70
72
|
PROCESS_ROLES,
|
|
@@ -74,8 +76,9 @@ import {
|
|
|
74
76
|
getProcessEventLog,
|
|
75
77
|
llmNodeCooldownPolicy,
|
|
76
78
|
llmNodeOutputParsing,
|
|
77
|
-
llmNodeSystemPrompt
|
|
78
|
-
|
|
79
|
+
llmNodeSystemPrompt,
|
|
80
|
+
logEvent
|
|
81
|
+
} from "./chunk-GLO6TOJN.js";
|
|
79
82
|
|
|
80
83
|
// src/shared/utils/config/env.ts
|
|
81
84
|
var ENV_KEYS = {
|
|
@@ -363,88 +366,99 @@ async function makeRequest(baseUrl, apiKey, body, signal) {
|
|
|
363
366
|
|
|
364
367
|
// src/engine/llm-client/stream-processor.ts
|
|
365
368
|
function processStreamEvent(event, requestId, context) {
|
|
366
|
-
const { toolCallsMap, callbacks, onContent, onReasoning, onUsage, getTotalChars, currentBlockRef } = context;
|
|
367
369
|
switch (event.type) {
|
|
368
370
|
case LLM_SSE_EVENT.CONTENT_BLOCK_START:
|
|
369
|
-
|
|
370
|
-
const blockType = event.content_block.type;
|
|
371
|
-
if (blockType === LLM_BLOCK_TYPE.TEXT) {
|
|
372
|
-
currentBlockRef.value = LLM_BLOCK_TYPE.TEXT;
|
|
373
|
-
context.onTextStart?.();
|
|
374
|
-
callbacks?.onOutputStart?.();
|
|
375
|
-
} else if (blockType === LLM_BLOCK_TYPE.THINKING || blockType === LLM_BLOCK_TYPE.REASONING) {
|
|
376
|
-
currentBlockRef.value = LLM_BLOCK_TYPE.REASONING;
|
|
377
|
-
context.onReasoningStart?.();
|
|
378
|
-
callbacks?.onReasoningStart?.();
|
|
379
|
-
} else if (blockType === LLM_BLOCK_TYPE.TOOL_USE) {
|
|
380
|
-
currentBlockRef.value = LLM_BLOCK_TYPE.TOOL_USE;
|
|
381
|
-
toolCallsMap.set(event.content_block.id || "", {
|
|
382
|
-
id: event.content_block.id || "",
|
|
383
|
-
name: event.content_block.name || "",
|
|
384
|
-
input: {},
|
|
385
|
-
_pendingJson: "",
|
|
386
|
-
_index: event.index
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
}
|
|
371
|
+
handleContentBlockStart(event, context);
|
|
390
372
|
break;
|
|
391
373
|
case LLM_SSE_EVENT.CONTENT_BLOCK_DELTA:
|
|
392
|
-
|
|
393
|
-
if (event.delta.type === LLM_DELTA_TYPE.TEXT_DELTA && event.delta.text) {
|
|
394
|
-
onContent(event.delta.text);
|
|
395
|
-
callbacks?.onOutputDelta?.(event.delta.text);
|
|
396
|
-
} else if (
|
|
397
|
-
// Anthropic: thinking_delta / GLM-DeepSeek: reasoning_delta
|
|
398
|
-
event.delta.type === LLM_DELTA_TYPE.THINKING_DELTA && event.delta.thinking || event.delta.type === LLM_DELTA_TYPE.REASONING_DELTA && event.delta.reasoning
|
|
399
|
-
) {
|
|
400
|
-
const chunk = event.delta.thinking || event.delta.reasoning || "";
|
|
401
|
-
onReasoning(chunk);
|
|
402
|
-
callbacks?.onReasoningDelta?.(chunk);
|
|
403
|
-
} else if (event.delta.type === LLM_DELTA_TYPE.INPUT_JSON_DELTA && event.delta.partial_json) {
|
|
404
|
-
const index = event.index;
|
|
405
|
-
if (index !== void 0) {
|
|
406
|
-
const toolCall = Array.from(toolCallsMap.values()).find((t) => t._index === index);
|
|
407
|
-
if (toolCall) {
|
|
408
|
-
toolCall._pendingJson = (toolCall._pendingJson || "") + event.delta.partial_json;
|
|
409
|
-
debugLog("llm", `[${requestId}] JSON DELTA applied`, { index, toolId: toolCall.id, json: event.delta.partial_json });
|
|
410
|
-
} else {
|
|
411
|
-
debugLog("llm", `[${requestId}] JSON DELTA no tool found for index`, { index });
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
const estimatedOutput = Math.ceil(getTotalChars() / LLM_LIMITS.charsPerTokenEstimate);
|
|
416
|
-
callbacks?.onUsageUpdate?.({ input_tokens: 0, output_tokens: estimatedOutput });
|
|
417
|
-
}
|
|
374
|
+
handleContentBlockDelta(event, requestId, context);
|
|
418
375
|
break;
|
|
419
|
-
case LLM_SSE_EVENT.CONTENT_BLOCK_STOP:
|
|
420
|
-
|
|
421
|
-
currentBlockRef.value = null;
|
|
422
|
-
if (stoppedType === LLM_BLOCK_TYPE.TEXT) {
|
|
423
|
-
context.onTextEnd?.();
|
|
424
|
-
callbacks?.onOutputEnd?.();
|
|
425
|
-
} else if (stoppedType === LLM_BLOCK_TYPE.REASONING) {
|
|
426
|
-
context.onReasoningEnd?.();
|
|
427
|
-
callbacks?.onReasoningEnd?.();
|
|
428
|
-
}
|
|
376
|
+
case LLM_SSE_EVENT.CONTENT_BLOCK_STOP:
|
|
377
|
+
handleContentBlockStop(context);
|
|
429
378
|
break;
|
|
430
|
-
}
|
|
431
379
|
case LLM_SSE_EVENT.MESSAGE_START:
|
|
432
|
-
|
|
433
|
-
onUsage({ input_tokens: event.message.usage.input_tokens || 0, output_tokens: event.message.usage.output_tokens || 0 });
|
|
434
|
-
callbacks?.onUsageUpdate?.({ input_tokens: event.message.usage.input_tokens || 0, output_tokens: event.message.usage.output_tokens || 0 });
|
|
435
|
-
}
|
|
380
|
+
handleMessageStart(event, context);
|
|
436
381
|
break;
|
|
437
382
|
case LLM_SSE_EVENT.MESSAGE_DELTA:
|
|
438
|
-
|
|
439
|
-
onUsage({ input_tokens: 0, output_tokens: event.usage.output_tokens || 0 });
|
|
440
|
-
callbacks?.onUsageUpdate?.({ input_tokens: 0, output_tokens: event.usage.output_tokens || 0 });
|
|
441
|
-
}
|
|
383
|
+
handleMessageDelta(event, context);
|
|
442
384
|
break;
|
|
443
385
|
case LLM_SSE_EVENT.ERROR:
|
|
444
386
|
if (event.error) throw new Error(`${event.error.type}: ${event.error.message}`);
|
|
445
387
|
break;
|
|
446
388
|
}
|
|
447
389
|
}
|
|
390
|
+
function handleContentBlockStart(event, context) {
|
|
391
|
+
if (!event.content_block) return;
|
|
392
|
+
const { toolCallsMap, callbacks, currentBlockRef } = context;
|
|
393
|
+
const blockType = event.content_block.type;
|
|
394
|
+
if (blockType === LLM_BLOCK_TYPE.TEXT) {
|
|
395
|
+
currentBlockRef.value = LLM_BLOCK_TYPE.TEXT;
|
|
396
|
+
context.onTextStart?.();
|
|
397
|
+
callbacks?.onOutputStart?.();
|
|
398
|
+
} else if (blockType === LLM_BLOCK_TYPE.THINKING || blockType === LLM_BLOCK_TYPE.REASONING) {
|
|
399
|
+
currentBlockRef.value = LLM_BLOCK_TYPE.REASONING;
|
|
400
|
+
context.onReasoningStart?.();
|
|
401
|
+
callbacks?.onReasoningStart?.();
|
|
402
|
+
} else if (blockType === LLM_BLOCK_TYPE.TOOL_USE) {
|
|
403
|
+
currentBlockRef.value = LLM_BLOCK_TYPE.TOOL_USE;
|
|
404
|
+
toolCallsMap.set(event.content_block.id || "", {
|
|
405
|
+
id: event.content_block.id || "",
|
|
406
|
+
name: event.content_block.name || "",
|
|
407
|
+
input: {},
|
|
408
|
+
_pendingJson: "",
|
|
409
|
+
_index: event.index
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
function handleContentBlockDelta(event, requestId, context) {
|
|
414
|
+
if (!event.delta) return;
|
|
415
|
+
const { toolCallsMap, callbacks, onContent, onReasoning, getTotalChars } = context;
|
|
416
|
+
if (event.delta.type === LLM_DELTA_TYPE.TEXT_DELTA && event.delta.text) {
|
|
417
|
+
onContent(event.delta.text);
|
|
418
|
+
callbacks?.onOutputDelta?.(event.delta.text);
|
|
419
|
+
} else if (event.delta.type === LLM_DELTA_TYPE.THINKING_DELTA && event.delta.thinking || event.delta.type === LLM_DELTA_TYPE.REASONING_DELTA && event.delta.reasoning) {
|
|
420
|
+
const chunk = event.delta.thinking || event.delta.reasoning || "";
|
|
421
|
+
onReasoning(chunk);
|
|
422
|
+
callbacks?.onReasoningDelta?.(chunk);
|
|
423
|
+
} else if (event.delta.type === LLM_DELTA_TYPE.INPUT_JSON_DELTA && event.delta.partial_json) {
|
|
424
|
+
const index = event.index;
|
|
425
|
+
if (index !== void 0) {
|
|
426
|
+
const toolCall = Array.from(toolCallsMap.values()).find((t) => t._index === index);
|
|
427
|
+
if (toolCall) {
|
|
428
|
+
toolCall._pendingJson = (toolCall._pendingJson || "") + event.delta.partial_json;
|
|
429
|
+
debugLog("llm", `[${requestId}] JSON DELTA applied`, { index, toolId: toolCall.id, json: event.delta.partial_json });
|
|
430
|
+
} else {
|
|
431
|
+
debugLog("llm", `[${requestId}] JSON DELTA no tool found for index`, { index });
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
const estimatedOutput = Math.ceil(getTotalChars() / LLM_LIMITS.charsPerTokenEstimate);
|
|
436
|
+
callbacks?.onUsageUpdate?.({ input_tokens: 0, output_tokens: estimatedOutput });
|
|
437
|
+
}
|
|
438
|
+
function handleContentBlockStop(context) {
|
|
439
|
+
const { callbacks, currentBlockRef } = context;
|
|
440
|
+
const stoppedType = currentBlockRef.value;
|
|
441
|
+
currentBlockRef.value = null;
|
|
442
|
+
if (stoppedType === LLM_BLOCK_TYPE.TEXT) {
|
|
443
|
+
context.onTextEnd?.();
|
|
444
|
+
callbacks?.onOutputEnd?.();
|
|
445
|
+
} else if (stoppedType === LLM_BLOCK_TYPE.REASONING) {
|
|
446
|
+
context.onReasoningEnd?.();
|
|
447
|
+
callbacks?.onReasoningEnd?.();
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
function handleMessageStart(event, context) {
|
|
451
|
+
if (!event.message?.usage) return;
|
|
452
|
+
const { onUsage, callbacks } = context;
|
|
453
|
+
onUsage({ input_tokens: event.message.usage.input_tokens || 0, output_tokens: event.message.usage.output_tokens || 0 });
|
|
454
|
+
callbacks?.onUsageUpdate?.({ input_tokens: event.message.usage.input_tokens || 0, output_tokens: event.message.usage.output_tokens || 0 });
|
|
455
|
+
}
|
|
456
|
+
function handleMessageDelta(event, context) {
|
|
457
|
+
if (!event.usage) return;
|
|
458
|
+
const { onUsage, callbacks } = context;
|
|
459
|
+
onUsage({ input_tokens: 0, output_tokens: event.usage.output_tokens || 0 });
|
|
460
|
+
callbacks?.onUsageUpdate?.({ input_tokens: 0, output_tokens: event.usage.output_tokens || 0 });
|
|
461
|
+
}
|
|
448
462
|
function createStreamContext(callbacks) {
|
|
449
463
|
let fullContent = "";
|
|
450
464
|
let fullReasoning = "";
|
|
@@ -476,7 +490,6 @@ var LLMClient = class {
|
|
|
476
490
|
apiKey;
|
|
477
491
|
baseUrl;
|
|
478
492
|
model;
|
|
479
|
-
requestCount = 0;
|
|
480
493
|
constructor() {
|
|
481
494
|
this.apiKey = getApiKey();
|
|
482
495
|
this.baseUrl = getBaseUrl() || LLM_API.DEFAULT_BASE_URL;
|
|
@@ -493,6 +506,7 @@ var LLMClient = class {
|
|
|
493
506
|
return this.model;
|
|
494
507
|
}
|
|
495
508
|
async executeNonStream(messages, tools, systemPrompt, callbacks) {
|
|
509
|
+
const requestId = incrementGlobalRequestCount();
|
|
496
510
|
const thinking = isThinkingEnabled() ? { type: "enabled", budget_tokens: getThinkingBudget() } : void 0;
|
|
497
511
|
const requestBody = {
|
|
498
512
|
model: this.model,
|
|
@@ -502,13 +516,14 @@ var LLMClient = class {
|
|
|
502
516
|
tools,
|
|
503
517
|
...thinking && { thinking }
|
|
504
518
|
};
|
|
505
|
-
debugLog("llm",
|
|
519
|
+
debugLog("llm", `[${requestId}] Non-stream request START`, { model: this.model, toolCount: tools?.length, thinking: !!thinking });
|
|
506
520
|
const response = await makeRequest(this.baseUrl, this.apiKey, requestBody, callbacks?.abortSignal);
|
|
507
521
|
const data = await response.json();
|
|
508
522
|
const textBlock = data.content.find((b) => b.type === LLM_BLOCK_TYPE.TEXT);
|
|
509
523
|
const toolBlocks = data.content.filter((b) => b.type === LLM_BLOCK_TYPE.TOOL_USE);
|
|
510
|
-
debugLog("llm",
|
|
524
|
+
debugLog("llm", `[${requestId}] Non-stream response`, { toolCount: toolBlocks.length, tools: toolBlocks.map((t) => ({ id: t.id, name: t.name, input: t.input })) });
|
|
511
525
|
const usage = { input_tokens: data.usage?.input_tokens || 0, output_tokens: data.usage?.output_tokens || 0 };
|
|
526
|
+
addGlobalTokenUsage(usage);
|
|
512
527
|
callbacks?.onUsageUpdate?.(usage);
|
|
513
528
|
return {
|
|
514
529
|
content: textBlock?.text || "",
|
|
@@ -518,8 +533,7 @@ var LLMClient = class {
|
|
|
518
533
|
};
|
|
519
534
|
}
|
|
520
535
|
async executeStream(messages, tools, systemPrompt, callbacks) {
|
|
521
|
-
|
|
522
|
-
const requestId = this.requestCount;
|
|
536
|
+
const requestId = incrementGlobalRequestCount();
|
|
523
537
|
const thinking = isThinkingEnabled() ? { type: LLM_THINKING_MODE.ENABLED, budget_tokens: getThinkingBudget() } : void 0;
|
|
524
538
|
const requestBody = {
|
|
525
539
|
model: this.model,
|
|
@@ -560,6 +574,9 @@ var LLMClient = class {
|
|
|
560
574
|
}
|
|
561
575
|
const toolCalls = resolveToolCalls(toolCallsMap);
|
|
562
576
|
const stripped = stripThinkTags(contentChunks.join(""), reasoningChunks.join(""));
|
|
577
|
+
if (usage.input_tokens > 0 || usage.output_tokens > 0) {
|
|
578
|
+
addGlobalTokenUsage(usage);
|
|
579
|
+
}
|
|
563
580
|
return {
|
|
564
581
|
content: stripped.cleanText,
|
|
565
582
|
toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
|
|
@@ -621,12 +638,27 @@ function resolveToolCalls(toolCallsMap) {
|
|
|
621
638
|
|
|
622
639
|
// src/engine/llm-client/factory.ts
|
|
623
640
|
var llmInstance = null;
|
|
641
|
+
var globalRequestCount = 0;
|
|
642
|
+
var globalTokenUsage = { input_tokens: 0, output_tokens: 0 };
|
|
624
643
|
function getLLMClient() {
|
|
625
644
|
if (!llmInstance) {
|
|
626
645
|
llmInstance = new LLMClient();
|
|
627
646
|
}
|
|
628
647
|
return llmInstance;
|
|
629
648
|
}
|
|
649
|
+
function incrementGlobalRequestCount() {
|
|
650
|
+
return ++globalRequestCount;
|
|
651
|
+
}
|
|
652
|
+
function getGlobalRequestCount() {
|
|
653
|
+
return globalRequestCount;
|
|
654
|
+
}
|
|
655
|
+
function addGlobalTokenUsage(usage) {
|
|
656
|
+
globalTokenUsage.input_tokens += usage.input_tokens || 0;
|
|
657
|
+
globalTokenUsage.output_tokens += usage.output_tokens || 0;
|
|
658
|
+
}
|
|
659
|
+
function getGlobalTokenUsage() {
|
|
660
|
+
return globalTokenUsage;
|
|
661
|
+
}
|
|
630
662
|
|
|
631
663
|
// src/engine/auxiliary-llm/auxiliary-llm-base.ts
|
|
632
664
|
var AuxiliaryLLMBase = class {
|
|
@@ -1706,19 +1738,8 @@ var CHALLENGE_TYPE_SIGNALS = {
|
|
|
1706
1738
|
"escape"
|
|
1707
1739
|
]
|
|
1708
1740
|
};
|
|
1709
|
-
function
|
|
1710
|
-
|
|
1711
|
-
return {
|
|
1712
|
-
primaryType: "unknown",
|
|
1713
|
-
secondaryTypes: [],
|
|
1714
|
-
confidence: 0,
|
|
1715
|
-
matchedSignals: [],
|
|
1716
|
-
recommendedTechniques: TYPE_TECHNIQUE_MAP.unknown,
|
|
1717
|
-
recommendedPhasePrompt: TYPE_PHASE_PROMPT_MAP.unknown
|
|
1718
|
-
};
|
|
1719
|
-
}
|
|
1720
|
-
const lowerData = reconData.toLowerCase();
|
|
1721
|
-
const scores = {
|
|
1741
|
+
function initializeScores() {
|
|
1742
|
+
return {
|
|
1722
1743
|
web: { score: 0, signals: [] },
|
|
1723
1744
|
pwn: { score: 0, signals: [] },
|
|
1724
1745
|
crypto: { score: 0, signals: [] },
|
|
@@ -1728,6 +1749,8 @@ function analyzeChallenge(reconData) {
|
|
|
1728
1749
|
network: { score: 0, signals: [] },
|
|
1729
1750
|
unknown: { score: 0, signals: [] }
|
|
1730
1751
|
};
|
|
1752
|
+
}
|
|
1753
|
+
function matchKeywordSignals(lowerData, scores) {
|
|
1731
1754
|
for (const [type, signals] of Object.entries(CHALLENGE_TYPE_SIGNALS)) {
|
|
1732
1755
|
const challengeType = type;
|
|
1733
1756
|
if (!(challengeType in scores)) continue;
|
|
@@ -1738,6 +1761,8 @@ function analyzeChallenge(reconData) {
|
|
|
1738
1761
|
}
|
|
1739
1762
|
}
|
|
1740
1763
|
}
|
|
1764
|
+
}
|
|
1765
|
+
function applyRegexBoosts(reconData, lowerData, scores) {
|
|
1741
1766
|
if (WEB_PORT_PATTERN.test(reconData)) {
|
|
1742
1767
|
scores.web.score += 2;
|
|
1743
1768
|
scores.web.signals.push("web-port");
|
|
@@ -1758,6 +1783,8 @@ function analyzeChallenge(reconData) {
|
|
|
1758
1783
|
scores.crypto.score += 3;
|
|
1759
1784
|
scores.crypto.signals.push("crypto-file");
|
|
1760
1785
|
}
|
|
1786
|
+
}
|
|
1787
|
+
function calculateAnalysisResult(scores) {
|
|
1761
1788
|
const sorted = Object.entries(scores).filter(([type]) => type !== "unknown").sort(([, a], [, b]) => b.score - a.score);
|
|
1762
1789
|
const [primaryType, primaryData] = sorted[0];
|
|
1763
1790
|
const totalSignals = sorted.reduce((sum, [, data]) => sum + data.score, 0);
|
|
@@ -1773,6 +1800,16 @@ function analyzeChallenge(reconData) {
|
|
|
1773
1800
|
recommendedPhasePrompt: TYPE_PHASE_PROMPT_MAP[primaryType] || TYPE_PHASE_PROMPT_MAP.unknown
|
|
1774
1801
|
};
|
|
1775
1802
|
}
|
|
1803
|
+
function analyzeChallenge(reconData) {
|
|
1804
|
+
if (!reconData || reconData.trim().length === 0) {
|
|
1805
|
+
return calculateAnalysisResult(initializeScores());
|
|
1806
|
+
}
|
|
1807
|
+
const lowerData = reconData.toLowerCase();
|
|
1808
|
+
const scores = initializeScores();
|
|
1809
|
+
matchKeywordSignals(lowerData, scores);
|
|
1810
|
+
applyRegexBoosts(reconData, lowerData, scores);
|
|
1811
|
+
return calculateAnalysisResult(scores);
|
|
1812
|
+
}
|
|
1776
1813
|
|
|
1777
1814
|
// src/shared/utils/knowledge/challenge/formatter.ts
|
|
1778
1815
|
function formatChallengeAnalysis(analysis) {
|
|
@@ -2746,13 +2783,13 @@ var CoreAgent = class _CoreAgent {
|
|
|
2746
2783
|
maxIterations;
|
|
2747
2784
|
abortController = null;
|
|
2748
2785
|
toolExecutor = null;
|
|
2749
|
-
constructor(
|
|
2750
|
-
this.agentType = agentType;
|
|
2751
|
-
this.state = state;
|
|
2752
|
-
this.events = events;
|
|
2753
|
-
this.toolRegistry = toolRegistry;
|
|
2786
|
+
constructor(options) {
|
|
2787
|
+
this.agentType = options.agentType;
|
|
2788
|
+
this.state = options.state;
|
|
2789
|
+
this.events = options.events;
|
|
2790
|
+
this.toolRegistry = options.toolRegistry;
|
|
2754
2791
|
this.llm = getLLMClient();
|
|
2755
|
-
this.maxIterations = maxIterations || AGENT_LIMITS.MAX_ITERATIONS;
|
|
2792
|
+
this.maxIterations = options.maxIterations || AGENT_LIMITS.MAX_ITERATIONS;
|
|
2756
2793
|
}
|
|
2757
2794
|
setToolRegistry(registry) {
|
|
2758
2795
|
this.toolRegistry = registry;
|
|
@@ -2772,14 +2809,19 @@ var CoreAgent = class _CoreAgent {
|
|
|
2772
2809
|
// Over 4+ hour sessions this can accumulate GB of tool results in memory.
|
|
2773
2810
|
// This cap ensures messages never exceed a safe size regardless of extractor health.
|
|
2774
2811
|
static MAX_MESSAGES_HARD_CAP = 50;
|
|
2812
|
+
static MAX_MESSAGES_CHAR_CAP = 1e6;
|
|
2775
2813
|
trimMessagesIfNeeded(messages) {
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
messages.length
|
|
2781
|
-
|
|
2782
|
-
|
|
2814
|
+
let totalChars = messages.reduce((sum, m) => {
|
|
2815
|
+
const len = typeof m.content === "string" ? m.content.length : JSON.stringify(m.content).length;
|
|
2816
|
+
return sum + len;
|
|
2817
|
+
}, 0);
|
|
2818
|
+
if (messages.length <= _CoreAgent.MAX_MESSAGES_HARD_CAP && totalChars <= _CoreAgent.MAX_MESSAGES_CHAR_CAP) return;
|
|
2819
|
+
while (messages.length > 2 && (messages.length > _CoreAgent.MAX_MESSAGES_HARD_CAP || totalChars > _CoreAgent.MAX_MESSAGES_CHAR_CAP)) {
|
|
2820
|
+
const removed = messages.splice(1, 1)[0];
|
|
2821
|
+
if (removed) {
|
|
2822
|
+
totalChars -= typeof removed.content === "string" ? removed.content.length : JSON.stringify(removed.content).length;
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2783
2825
|
}
|
|
2784
2826
|
/** The core loop: Think → Act → Observe */
|
|
2785
2827
|
async run(task, systemPrompt) {
|
|
@@ -3232,10 +3274,250 @@ auto-proxied by the system \u2014 no extra work needed for those.`,
|
|
|
3232
3274
|
execute: async (params) => writeFileContent(params.path, params.content)
|
|
3233
3275
|
};
|
|
3234
3276
|
|
|
3277
|
+
// src/engine/tools/system/shell-tools.ts
|
|
3278
|
+
function createShellTool(name, description, parameters, required, execute) {
|
|
3279
|
+
return {
|
|
3280
|
+
name,
|
|
3281
|
+
description,
|
|
3282
|
+
parameters,
|
|
3283
|
+
required,
|
|
3284
|
+
execute
|
|
3285
|
+
};
|
|
3286
|
+
}
|
|
3287
|
+
function getShellCheckCommand(profile) {
|
|
3288
|
+
switch (profile) {
|
|
3289
|
+
case "stability":
|
|
3290
|
+
return "echo $TERM && tty && stty -a";
|
|
3291
|
+
case "post":
|
|
3292
|
+
return "id && whoami && hostname && ip a | head -n 20";
|
|
3293
|
+
case "environment":
|
|
3294
|
+
return "pwd && uname -a && cat /etc/os-release 2>/dev/null | head -n 5";
|
|
3295
|
+
case "identity":
|
|
3296
|
+
default:
|
|
3297
|
+
return "id && whoami && hostname";
|
|
3298
|
+
}
|
|
3299
|
+
}
|
|
3300
|
+
function getShellUpgradeCommand(method) {
|
|
3301
|
+
switch (method || "python_pty") {
|
|
3302
|
+
case "python_pty":
|
|
3303
|
+
return `python3 -c 'import pty; pty.spawn("/bin/bash")' || python -c 'import pty; pty.spawn("/bin/bash")'`;
|
|
3304
|
+
case "script":
|
|
3305
|
+
return "script -q /dev/null -c /bin/bash";
|
|
3306
|
+
case "perl":
|
|
3307
|
+
return q(`perl -e 'exec "/bin/bash";'`);
|
|
3308
|
+
case "ruby":
|
|
3309
|
+
return q(`ruby -e 'exec "/bin/bash"'`);
|
|
3310
|
+
case "socat_probe":
|
|
3311
|
+
return "command -v socat && socat -h | head -n 5";
|
|
3312
|
+
case "stty_probe":
|
|
3313
|
+
return "echo $TERM && tty && stty -a";
|
|
3314
|
+
default:
|
|
3315
|
+
return null;
|
|
3316
|
+
}
|
|
3317
|
+
}
|
|
3318
|
+
function outputLooksStabilized(output) {
|
|
3319
|
+
const normalized = output.toLowerCase();
|
|
3320
|
+
return normalized.includes("xterm") || normalized.includes("/dev/pts/") || normalized.includes("rows") || normalized.includes("columns");
|
|
3321
|
+
}
|
|
3322
|
+
function q(value) {
|
|
3323
|
+
return value;
|
|
3324
|
+
}
|
|
3325
|
+
async function executeShellUpgrade(processId, method, waitMs) {
|
|
3326
|
+
const command = getShellUpgradeCommand(method);
|
|
3327
|
+
if (!command) {
|
|
3328
|
+
return {
|
|
3329
|
+
success: false,
|
|
3330
|
+
output: "",
|
|
3331
|
+
error: `Unknown shell upgrade method: ${method}. Use: python_pty, script, perl, ruby, socat_probe, stty_probe`
|
|
3332
|
+
};
|
|
3333
|
+
}
|
|
3334
|
+
logEvent(processId, PROCESS_EVENTS.SHELL_UPGRADE_ATTEMPTED, `Shell upgrade method: ${method || "python_pty"}`);
|
|
3335
|
+
return handleInteractAction(processId, command, waitMs);
|
|
3336
|
+
}
|
|
3337
|
+
function buildListenerCommand(port, bind) {
|
|
3338
|
+
return bind ? `nc -lvnp ${port} -s ${bind}` : `nc -lvnp ${port}`;
|
|
3339
|
+
}
|
|
3340
|
+
function executeListenerStart(port, bind, purpose) {
|
|
3341
|
+
if (!Number.isFinite(port) || port <= 0) {
|
|
3342
|
+
return {
|
|
3343
|
+
success: false,
|
|
3344
|
+
output: "",
|
|
3345
|
+
error: "Invalid port. Provide a positive numeric port."
|
|
3346
|
+
};
|
|
3347
|
+
}
|
|
3348
|
+
const usedPorts = getUsedPorts();
|
|
3349
|
+
if (usedPorts.includes(port)) {
|
|
3350
|
+
return {
|
|
3351
|
+
success: false,
|
|
3352
|
+
output: `[X] PORT CONFLICT: Port ${port} is already in use.
|
|
3353
|
+
Used ports: ${usedPorts.join(", ")}`,
|
|
3354
|
+
error: `Port ${port} already in use`
|
|
3355
|
+
};
|
|
3356
|
+
}
|
|
3357
|
+
const command = buildListenerCommand(port, bind);
|
|
3358
|
+
const portMatch = command.match(DETECTION_PATTERNS.LISTENER);
|
|
3359
|
+
if (!portMatch) {
|
|
3360
|
+
return {
|
|
3361
|
+
success: false,
|
|
3362
|
+
output: "",
|
|
3363
|
+
error: `Failed to build listener command for port ${port}`
|
|
3364
|
+
};
|
|
3365
|
+
}
|
|
3366
|
+
const proc = startBackgroundProcess(command, {
|
|
3367
|
+
description: `listener ${port}`,
|
|
3368
|
+
purpose: purpose || `Reverse shell listener on port ${port}`
|
|
3369
|
+
});
|
|
3370
|
+
return {
|
|
3371
|
+
success: true,
|
|
3372
|
+
output: `Listener started.
|
|
3373
|
+
Process ID: ${proc.id}
|
|
3374
|
+
PID: ${proc.pid}
|
|
3375
|
+
Port: ${proc.listeningPort || port}
|
|
3376
|
+
Role: ${proc.role}
|
|
3377
|
+
Purpose: ${proc.purpose}`
|
|
3378
|
+
};
|
|
3379
|
+
}
|
|
3380
|
+
var shellTools = [
|
|
3381
|
+
createShellTool(
|
|
3382
|
+
TOOL_NAMES.LISTENER_START,
|
|
3383
|
+
"Start exactly one reverse-shell listener as a tracked background process.",
|
|
3384
|
+
{
|
|
3385
|
+
port: { type: "number", description: "Listener port" },
|
|
3386
|
+
bind: { type: "string", description: "Optional local bind address" },
|
|
3387
|
+
purpose: { type: "string", description: "Optional purpose for resource tracking" }
|
|
3388
|
+
},
|
|
3389
|
+
["port"],
|
|
3390
|
+
async (params) => executeListenerStart(
|
|
3391
|
+
Number(params.port),
|
|
3392
|
+
params.bind,
|
|
3393
|
+
params.purpose
|
|
3394
|
+
)
|
|
3395
|
+
),
|
|
3396
|
+
createShellTool(
|
|
3397
|
+
TOOL_NAMES.LISTENER_STATUS,
|
|
3398
|
+
"Check a reverse-shell listener in detail. Use this instead of bg_process status when supervising callbacks.",
|
|
3399
|
+
{
|
|
3400
|
+
process_id: { type: "string", description: "Listener process ID" }
|
|
3401
|
+
},
|
|
3402
|
+
["process_id"],
|
|
3403
|
+
async (params) => handleStatusAction(params.process_id)
|
|
3404
|
+
),
|
|
3405
|
+
createShellTool(
|
|
3406
|
+
TOOL_NAMES.SHELL_PROMOTE,
|
|
3407
|
+
"Promote a listener with a confirmed callback into the active shell role.",
|
|
3408
|
+
{
|
|
3409
|
+
process_id: { type: "string", description: "Listener process ID" },
|
|
3410
|
+
description: { type: "string", description: "Optional shell description" }
|
|
3411
|
+
},
|
|
3412
|
+
["process_id"],
|
|
3413
|
+
async (params) => handlePromoteAction(params.process_id, params.description)
|
|
3414
|
+
),
|
|
3415
|
+
createShellTool(
|
|
3416
|
+
TOOL_NAMES.SHELL_EXEC,
|
|
3417
|
+
"Execute a command through an active reverse shell and return new output.",
|
|
3418
|
+
{
|
|
3419
|
+
process_id: { type: "string", description: "Active shell process ID" },
|
|
3420
|
+
command: { type: "string", description: "Command to execute through the shell" },
|
|
3421
|
+
wait_ms: { type: "number", description: "Optional wait time for shell output" }
|
|
3422
|
+
},
|
|
3423
|
+
["process_id", "command"],
|
|
3424
|
+
async (params) => handleInteractAction(
|
|
3425
|
+
params.process_id,
|
|
3426
|
+
params.command,
|
|
3427
|
+
params.wait_ms
|
|
3428
|
+
)
|
|
3429
|
+
),
|
|
3430
|
+
createShellTool(
|
|
3431
|
+
TOOL_NAMES.SHELL_CHECK,
|
|
3432
|
+
"Run a bounded shell quality or environment check. Profiles: identity, stability, environment, post.",
|
|
3433
|
+
{
|
|
3434
|
+
process_id: { type: "string", description: "Shell process ID" },
|
|
3435
|
+
profile: { type: "string", description: "identity | stability | environment | post" },
|
|
3436
|
+
wait_ms: { type: "number", description: "Optional wait time for shell output" }
|
|
3437
|
+
},
|
|
3438
|
+
["process_id"],
|
|
3439
|
+
async (params) => {
|
|
3440
|
+
const processId = params.process_id;
|
|
3441
|
+
const result = await handleInteractAction(
|
|
3442
|
+
processId,
|
|
3443
|
+
getShellCheckCommand(params.profile),
|
|
3444
|
+
params.wait_ms
|
|
3445
|
+
);
|
|
3446
|
+
if (result.success && params.profile === "stability" && outputLooksStabilized(result.output)) {
|
|
3447
|
+
logEvent(processId, PROCESS_EVENTS.SHELL_STABILIZED, "Shell stability confirmed by shell_check");
|
|
3448
|
+
}
|
|
3449
|
+
return result;
|
|
3450
|
+
}
|
|
3451
|
+
),
|
|
3452
|
+
createShellTool(
|
|
3453
|
+
TOOL_NAMES.SHELL_UPGRADE,
|
|
3454
|
+
"Send a bounded shell-upgrade attempt or upgrade probe through an active shell.",
|
|
3455
|
+
{
|
|
3456
|
+
process_id: { type: "string", description: "Shell process ID" },
|
|
3457
|
+
method: { type: "string", description: "python_pty | script | perl | ruby | socat_probe | stty_probe" },
|
|
3458
|
+
wait_ms: { type: "number", description: "Optional wait time for shell output" }
|
|
3459
|
+
},
|
|
3460
|
+
["process_id"],
|
|
3461
|
+
async (params) => executeShellUpgrade(
|
|
3462
|
+
params.process_id,
|
|
3463
|
+
params.method,
|
|
3464
|
+
params.wait_ms
|
|
3465
|
+
)
|
|
3466
|
+
)
|
|
3467
|
+
];
|
|
3468
|
+
|
|
3469
|
+
// src/engine/tools/system/offensive-bounded-tools.ts
|
|
3470
|
+
async function runBoundedForegroundCommand(command, timeout) {
|
|
3471
|
+
return runCommand(command, [], timeout ? { timeout } : {});
|
|
3472
|
+
}
|
|
3473
|
+
function createBoundedCommandTool(name, description) {
|
|
3474
|
+
return {
|
|
3475
|
+
name,
|
|
3476
|
+
description,
|
|
3477
|
+
parameters: {
|
|
3478
|
+
command: { type: "string", description: "Bounded foreground command for this phase-specific operation" },
|
|
3479
|
+
timeout: { type: "number", description: "Optional timeout in ms" }
|
|
3480
|
+
},
|
|
3481
|
+
required: ["command"],
|
|
3482
|
+
execute: async (params) => runBoundedForegroundCommand(
|
|
3483
|
+
params.command,
|
|
3484
|
+
params.timeout
|
|
3485
|
+
)
|
|
3486
|
+
};
|
|
3487
|
+
}
|
|
3488
|
+
var offensiveBoundedTools = [
|
|
3489
|
+
createBoundedCommandTool(
|
|
3490
|
+
TOOL_NAMES.EXPLOIT_CREDENTIAL_CHECK,
|
|
3491
|
+
"Run a bounded exploit credential/token validation probe. Use after a chain dumps credentials or tokens."
|
|
3492
|
+
),
|
|
3493
|
+
createBoundedCommandTool(
|
|
3494
|
+
TOOL_NAMES.EXPLOIT_ARTIFACT_CHECK,
|
|
3495
|
+
"Run a bounded exploit artifact validation probe. Use to verify the current artifact before replacing it."
|
|
3496
|
+
),
|
|
3497
|
+
createBoundedCommandTool(
|
|
3498
|
+
TOOL_NAMES.EXPLOIT_FOOTHOLD_CHECK,
|
|
3499
|
+
"Run a bounded foothold confirmation probe after an exploit chain appears to land access."
|
|
3500
|
+
),
|
|
3501
|
+
createBoundedCommandTool(
|
|
3502
|
+
TOOL_NAMES.PWN_CRASH_REPRO,
|
|
3503
|
+
"Run a bounded pwn crash reproduction command from the preserved crash state."
|
|
3504
|
+
),
|
|
3505
|
+
createBoundedCommandTool(
|
|
3506
|
+
TOOL_NAMES.PWN_OFFSET_CHECK,
|
|
3507
|
+
"Run a bounded pwn offset verification command when control primitives are believed to be known."
|
|
3508
|
+
),
|
|
3509
|
+
createBoundedCommandTool(
|
|
3510
|
+
TOOL_NAMES.PWN_PAYLOAD_SMOKE,
|
|
3511
|
+
"Run a bounded pwn payload smoke-test command against the latest exploit revision."
|
|
3512
|
+
)
|
|
3513
|
+
];
|
|
3514
|
+
|
|
3235
3515
|
// src/engine/tools/system/index.ts
|
|
3236
3516
|
var systemTools = [
|
|
3237
3517
|
runCmdTool,
|
|
3238
3518
|
bgProcessTool,
|
|
3519
|
+
...shellTools,
|
|
3520
|
+
...offensiveBoundedTools,
|
|
3239
3521
|
readFileTool,
|
|
3240
3522
|
writeFileTool
|
|
3241
3523
|
];
|
|
@@ -4253,9 +4535,7 @@ function isTransientHttpError(status) {
|
|
|
4253
4535
|
async function parseNmap(xmlPath) {
|
|
4254
4536
|
try {
|
|
4255
4537
|
const fileResult = await readFileContent(xmlPath);
|
|
4256
|
-
if (!fileResult.success)
|
|
4257
|
-
return fileResult;
|
|
4258
|
-
}
|
|
4538
|
+
if (!fileResult.success) return fileResult;
|
|
4259
4539
|
const xmlContent = fileResult.output;
|
|
4260
4540
|
const results = {
|
|
4261
4541
|
targets: [],
|
|
@@ -4264,31 +4544,7 @@ async function parseNmap(xmlPath) {
|
|
|
4264
4544
|
const hostRegex = /<host[^>]*>[\s\S]*?<\/host>/g;
|
|
4265
4545
|
const hosts = xmlContent.match(hostRegex) || [];
|
|
4266
4546
|
for (const hostBlock of hosts) {
|
|
4267
|
-
|
|
4268
|
-
const ip = ipMatch ? ipMatch[1] : "";
|
|
4269
|
-
const hostnameMatch = hostBlock.match(/<hostname[^>]*name="([^"]+)"/);
|
|
4270
|
-
const hostname = hostnameMatch ? hostnameMatch[1] : void 0;
|
|
4271
|
-
const ports = [];
|
|
4272
|
-
const portRegex = /<port[^>]*protocol="([^"]*)"[^>]*portid="(\d+)">[\s\S]*?<\/port>/g;
|
|
4273
|
-
let portMatch;
|
|
4274
|
-
while ((portMatch = portRegex.exec(hostBlock)) !== null) {
|
|
4275
|
-
const protocol = portMatch[1];
|
|
4276
|
-
const port = parseInt(portMatch[2]);
|
|
4277
|
-
const stateMatch = portMatch[0].match(/<state[^>]*state="([^"]+)"/);
|
|
4278
|
-
const state = stateMatch ? stateMatch[1] : "";
|
|
4279
|
-
const serviceMatch = portMatch[0].match(/<service[^>]*name="([^"]*)"(?:[^>]*version="([^"]*)")?/);
|
|
4280
|
-
const service = serviceMatch ? serviceMatch[1] : void 0;
|
|
4281
|
-
const version = serviceMatch && serviceMatch[2] ? serviceMatch[2] : void 0;
|
|
4282
|
-
if (state === PORT_STATE.OPEN) {
|
|
4283
|
-
ports.push({ port, protocol, state, service, version });
|
|
4284
|
-
results.summary.openPorts++;
|
|
4285
|
-
if (service) results.summary.servicesFound++;
|
|
4286
|
-
}
|
|
4287
|
-
}
|
|
4288
|
-
if (ip) {
|
|
4289
|
-
results.targets.push({ ip, hostname, ports });
|
|
4290
|
-
results.summary.totalTargets++;
|
|
4291
|
-
}
|
|
4547
|
+
parseHost(hostBlock, results);
|
|
4292
4548
|
}
|
|
4293
4549
|
return {
|
|
4294
4550
|
success: true,
|
|
@@ -4302,6 +4558,37 @@ async function parseNmap(xmlPath) {
|
|
|
4302
4558
|
};
|
|
4303
4559
|
}
|
|
4304
4560
|
}
|
|
4561
|
+
function parseHost(hostBlock, results) {
|
|
4562
|
+
const ipMatch = hostBlock.match(/<address[^>]*addr="([^"]+)"/);
|
|
4563
|
+
const ip = ipMatch ? ipMatch[1] : "";
|
|
4564
|
+
const hostnameMatch = hostBlock.match(/<hostname[^>]*name="([^"]+)"/);
|
|
4565
|
+
const hostname = hostnameMatch ? hostnameMatch[1] : void 0;
|
|
4566
|
+
const ports = parsePorts(hostBlock, results);
|
|
4567
|
+
if (ip) {
|
|
4568
|
+
results.targets.push({ ip, hostname, ports });
|
|
4569
|
+
results.summary.totalTargets++;
|
|
4570
|
+
}
|
|
4571
|
+
}
|
|
4572
|
+
function parsePorts(hostBlock, results) {
|
|
4573
|
+
const ports = [];
|
|
4574
|
+
const portRegex = /<port[^>]*protocol="([^"]*)"[^>]*portid="(\d+)">[\s\S]*?<\/port>/g;
|
|
4575
|
+
let portMatch;
|
|
4576
|
+
while ((portMatch = portRegex.exec(hostBlock)) !== null) {
|
|
4577
|
+
const protocol = portMatch[1];
|
|
4578
|
+
const port = parseInt(portMatch[2]);
|
|
4579
|
+
const stateMatch = portMatch[0].match(/<state[^>]*state="([^"]+)"/);
|
|
4580
|
+
const state = stateMatch ? stateMatch[1] : "";
|
|
4581
|
+
const serviceMatch = portMatch[0].match(/<service[^>]*name="([^"]*)"(?:[^>]*version="([^"]*)")?/);
|
|
4582
|
+
const service = serviceMatch ? serviceMatch[1] : void 0;
|
|
4583
|
+
const version = serviceMatch && serviceMatch[2] ? serviceMatch[2] : void 0;
|
|
4584
|
+
if (state === PORT_STATE.OPEN) {
|
|
4585
|
+
ports.push({ port, protocol, state, service, version });
|
|
4586
|
+
results.summary.openPorts++;
|
|
4587
|
+
if (service) results.summary.servicesFound++;
|
|
4588
|
+
}
|
|
4589
|
+
}
|
|
4590
|
+
return ports;
|
|
4591
|
+
}
|
|
4305
4592
|
|
|
4306
4593
|
// src/engine/tools/intel-utils/cve-search.ts
|
|
4307
4594
|
import { execFileSync } from "child_process";
|
|
@@ -4861,54 +5148,57 @@ function getStorageAndAuthDumpSnippet(capturedVarName = "_capturedHeaders", safe
|
|
|
4861
5148
|
}
|
|
4862
5149
|
|
|
4863
5150
|
// src/engine/tools/web-browser/scripts/builder.ts
|
|
4864
|
-
function
|
|
4865
|
-
const
|
|
4866
|
-
const safeUserAgent = safeJsString(options.userAgent || BROWSER_CONFIG.DEFAULT_USER_AGENT);
|
|
4867
|
-
const safeScreenshotPath = screenshotPath ? safeJsString(screenshotPath) : "null";
|
|
4868
|
-
const safeExtraHeaders = JSON.stringify(options.extraHeaders || {});
|
|
4869
|
-
const playwrightPath = getPlaywrightPath();
|
|
4870
|
-
const safePlaywrightPath = safeJsString(playwrightPath);
|
|
4871
|
-
const safeSessionPath = sessionPath ? safeJsString(sessionPath) : null;
|
|
4872
|
-
return `
|
|
4873
|
-
const { chromium } = require(${safePlaywrightPath});
|
|
5151
|
+
function buildCommonHeader(safePlaywrightPath) {
|
|
5152
|
+
return `const { chromium } = require(${safePlaywrightPath});
|
|
4874
5153
|
const fs = require('fs');
|
|
4875
5154
|
|
|
4876
5155
|
(async () => {
|
|
4877
5156
|
const browser = await chromium.launch({
|
|
4878
5157
|
headless: true,
|
|
4879
5158
|
args: ['${PLAYWRIGHT_ARG.NO_SANDBOX}', '${PLAYWRIGHT_ARG.DISABLE_SETUID_SANDBOX}', ${JSON.stringify(getTorBrowserArgs()).slice(1, -1)}].filter(Boolean)
|
|
4880
|
-
})
|
|
4881
|
-
|
|
4882
|
-
|
|
5159
|
+
});`;
|
|
5160
|
+
}
|
|
5161
|
+
function buildSessionContext(options, safeSessionPath) {
|
|
5162
|
+
const safeUserAgent = options.userAgent ? safeJsString(options.userAgent) : "undefined";
|
|
5163
|
+
const safeExtraHeaders = options.extraHeaders ? JSON.stringify(options.extraHeaders) : "{}";
|
|
5164
|
+
const viewportStr = options.viewport ? `viewport: { width: ${options.viewport.width}, height: ${options.viewport.height} },` : "";
|
|
5165
|
+
return `
|
|
4883
5166
|
const contextOptions = {
|
|
4884
|
-
userAgent: ${safeUserAgent}
|
|
4885
|
-
|
|
4886
|
-
extraHTTPHeaders: ${safeExtraHeaders}
|
|
5167
|
+
${options.userAgent ? `userAgent: ${safeUserAgent},` : ""}
|
|
5168
|
+
${viewportStr}
|
|
5169
|
+
${options.extraHeaders ? `extraHTTPHeaders: ${safeExtraHeaders},` : ""}
|
|
4887
5170
|
};
|
|
4888
5171
|
${safeSessionPath && options.useSession ? `
|
|
4889
5172
|
if (fs.existsSync(${safeSessionPath})) {
|
|
4890
5173
|
contextOptions.storageState = ${safeSessionPath};
|
|
4891
5174
|
}` : ""}
|
|
4892
|
-
|
|
4893
5175
|
const context = await browser.newContext(contextOptions);
|
|
5176
|
+
const page = await context.newPage();`;
|
|
5177
|
+
}
|
|
5178
|
+
function buildSessionSave(saveSession, safeSessionPath, targetObj, authHeadersProp) {
|
|
5179
|
+
if (!safeSessionPath || !saveSession) return "";
|
|
5180
|
+
return `
|
|
5181
|
+
await context.storageState({ path: ${safeSessionPath} });
|
|
5182
|
+
${targetObj}.sessionSaved = ${safeSessionPath};
|
|
4894
5183
|
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
5184
|
+
${getStorageAndAuthDumpSnippet(authHeadersProp, safeSessionPath)}
|
|
5185
|
+
${targetObj}.authHeadersSaved = _authHeadersPath;
|
|
5186
|
+
${targetObj}.authHeadersNote = 'auth-headers.json contains intercepted network headers + full storage dump. LLM should inspect _storage field for unlabeled tokens.';
|
|
5187
|
+
`;
|
|
5188
|
+
}
|
|
5189
|
+
function buildCommonFooter() {
|
|
5190
|
+
return `
|
|
5191
|
+
} catch (error) {
|
|
5192
|
+
console.log(JSON.stringify({ error: error.message }));
|
|
5193
|
+
} finally {
|
|
5194
|
+
await browser.close();
|
|
5195
|
+
}
|
|
5196
|
+
})();`;
|
|
5197
|
+
}
|
|
5198
|
+
function buildBrowseExtraction(options) {
|
|
5199
|
+
let extraction = "";
|
|
5200
|
+
if (options.extractContent) {
|
|
5201
|
+
extraction += `
|
|
4912
5202
|
result.text = await page.evaluate(() => {
|
|
4913
5203
|
const body = document.body;
|
|
4914
5204
|
const walker = document.createTreeWalker(body, NodeFilter.SHOW_TEXT, null, false);
|
|
@@ -4919,19 +5209,19 @@ const fs = require('fs');
|
|
|
4919
5209
|
if (content) text += content + '\\n';
|
|
4920
5210
|
}
|
|
4921
5211
|
return text.slice(0, ${BROWSER_LIMITS.MAX_TEXT_EXTRACTION});
|
|
4922
|
-
})
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
5212
|
+
});`;
|
|
5213
|
+
}
|
|
5214
|
+
if (options.extractLinks) {
|
|
5215
|
+
extraction += `
|
|
4926
5216
|
result.links = await page.evaluate(() => {
|
|
4927
5217
|
return Array.from(document.querySelectorAll('a[href]')).map(a => ({
|
|
4928
5218
|
href: a.href,
|
|
4929
5219
|
text: a.textContent.trim().slice(0, ${DISPLAY_LIMITS.LINK_TEXT_PREVIEW})
|
|
4930
5220
|
})).slice(0, ${BROWSER_LIMITS.MAX_LINKS_EXTRACTION});
|
|
4931
|
-
})
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
5221
|
+
});`;
|
|
5222
|
+
}
|
|
5223
|
+
if (options.extractForms) {
|
|
5224
|
+
extraction += `
|
|
4935
5225
|
result.forms = await page.evaluate(() => {
|
|
4936
5226
|
return Array.from(document.querySelectorAll('form')).map(form => ({
|
|
4937
5227
|
action: form.action,
|
|
@@ -4944,69 +5234,42 @@ const fs = require('fs');
|
|
|
4944
5234
|
isRequired: input.required
|
|
4945
5235
|
}))
|
|
4946
5236
|
}));
|
|
4947
|
-
})
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
5237
|
+
});`;
|
|
5238
|
+
}
|
|
5239
|
+
return extraction;
|
|
5240
|
+
}
|
|
5241
|
+
function buildBrowseScript(url, options, screenshotPath, sessionPath) {
|
|
5242
|
+
const safeUrl = safeJsString(url);
|
|
5243
|
+
const safeScreenshotPath = screenshotPath ? safeJsString(screenshotPath) : "null";
|
|
5244
|
+
const safePlaywrightPath = safeJsString(getPlaywrightPath());
|
|
5245
|
+
const safeSessionPath = sessionPath ? safeJsString(sessionPath) : null;
|
|
5246
|
+
options.userAgent = options.userAgent || BROWSER_CONFIG.DEFAULT_USER_AGENT;
|
|
5247
|
+
return `
|
|
5248
|
+
${buildCommonHeader(safePlaywrightPath)}
|
|
5249
|
+
${buildSessionContext(options, safeSessionPath)}
|
|
5250
|
+
${getNetworkAuthInterceptSnippet("_capturedHeaders")}
|
|
5251
|
+
try {
|
|
5252
|
+
await page.goto(${safeUrl}, { waitUntil: 'networkidle', timeout: ${options.timeout} });
|
|
5253
|
+
await page.waitForTimeout(${options.waitAfterLoad});
|
|
4951
5254
|
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
await context.storageState({ path: ${safeSessionPath} });
|
|
4955
|
-
result.sessionSaved = ${safeSessionPath};
|
|
5255
|
+
const result = { _interceptedAuth: _capturedHeaders };
|
|
5256
|
+
result.title = await page.title();
|
|
4956
5257
|
|
|
4957
|
-
${
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
` : ""}
|
|
5258
|
+
${buildBrowseExtraction(options)}
|
|
5259
|
+
${screenshotPath ? `await page.screenshot({ path: ${safeScreenshotPath}, fullPage: false });` : ""}
|
|
5260
|
+
${buildSessionSave(!!options.saveSession, safeSessionPath, "result", "result._interceptedAuth")}
|
|
4961
5261
|
|
|
4962
5262
|
console.log(JSON.stringify(result));
|
|
4963
|
-
|
|
4964
|
-
console.log(JSON.stringify({ error: error.message }));
|
|
4965
|
-
} finally {
|
|
4966
|
-
await browser.close();
|
|
4967
|
-
}
|
|
4968
|
-
})();
|
|
5263
|
+
${buildCommonFooter()}
|
|
4969
5264
|
`;
|
|
4970
5265
|
}
|
|
4971
|
-
function
|
|
4972
|
-
const { safeUrl, safeFormData, safePlaywrightPath, timeout, useSession, saveSession, safeSessionPath } = params;
|
|
5266
|
+
function buildFormFillingLogic(safeFormData) {
|
|
4973
5267
|
return `
|
|
4974
|
-
const { chromium } = require(${safePlaywrightPath});
|
|
4975
|
-
const fs = require('fs');
|
|
4976
|
-
|
|
4977
|
-
(async () => {
|
|
4978
|
-
const browser = await chromium.launch({
|
|
4979
|
-
headless: true,
|
|
4980
|
-
args: ['${PLAYWRIGHT_ARG.NO_SANDBOX}', '${PLAYWRIGHT_ARG.DISABLE_SETUID_SANDBOX}', ${JSON.stringify(getTorBrowserArgs()).slice(1, -1)}].filter(Boolean)
|
|
4981
|
-
});
|
|
4982
|
-
|
|
4983
|
-
// \xA74 Session: load existing session if requested
|
|
4984
|
-
const contextOptions = {};
|
|
4985
|
-
${safeSessionPath && useSession ? `
|
|
4986
|
-
if (fs.existsSync(${safeSessionPath})) {
|
|
4987
|
-
contextOptions.storageState = ${safeSessionPath};
|
|
4988
|
-
}` : ""}
|
|
4989
|
-
const context = await browser.newContext(contextOptions);
|
|
4990
|
-
const page = await context.newPage();
|
|
4991
|
-
|
|
4992
|
-
${getNetworkAuthInterceptSnippet("_capturedHeaders")}
|
|
4993
|
-
|
|
4994
|
-
try {
|
|
4995
|
-
await page.goto(${safeUrl}, { waitUntil: 'networkidle', timeout: ${timeout} });
|
|
4996
|
-
|
|
4997
|
-
// \xA7LLM-AUTONOMY: Semantic form field matching via Accessibility Tree + multiple strategies.
|
|
4998
|
-
// No hardcoded CSS selector rules. Tries in priority order:
|
|
4999
|
-
// 1. name/id attribute (exact)
|
|
5000
|
-
// 2. aria-label / placeholder semantic match (case-insensitive)
|
|
5001
|
-
// 3. input[type] semantic inference (username\u2192text, password\u2192password)
|
|
5002
5268
|
const formData = ${safeFormData};
|
|
5003
5269
|
for (const [fieldKey, fieldValue] of Object.entries(formData)) {
|
|
5004
5270
|
const lk = fieldKey.toLowerCase();
|
|
5005
|
-
|
|
5006
|
-
// Strategy 1: exact name/id/aria-label attribute
|
|
5007
5271
|
let el = await page.$(\`[name="\${fieldKey}"], #\${fieldKey}\`);
|
|
5008
5272
|
|
|
5009
|
-
// Strategy 2: semantic aria-label or placeholder match
|
|
5010
5273
|
if (!el) {
|
|
5011
5274
|
el = await page.evaluateHandle((key) => {
|
|
5012
5275
|
const inputs = [...document.querySelectorAll('input, textarea, select')];
|
|
@@ -5019,22 +5282,16 @@ const fs = require('fs');
|
|
|
5019
5282
|
}, lk).then(h => h.asElement());
|
|
5020
5283
|
}
|
|
5021
5284
|
|
|
5022
|
-
// Strategy 3: type-based inference (username/email/user \u2192 type=text/email, password \u2192 type=password)
|
|
5023
5285
|
if (!el) {
|
|
5024
5286
|
let inferredType = null;
|
|
5025
5287
|
if (['username', 'user', 'email', 'login'].includes(lk)) inferredType = ['text', 'email'];
|
|
5026
5288
|
if (['password', 'pass', 'pwd'].includes(lk)) inferredType = ['password'];
|
|
5027
|
-
if (inferredType) {
|
|
5028
|
-
el = await page.$(\`input[type="\${inferredType[0]}"\${inferredType[1] ? \`, input[type="\${inferredType[1]}"\` : ''}]\`);
|
|
5029
|
-
}
|
|
5289
|
+
if (inferredType) el = await page.$(\`input[type="\${inferredType[0]}"\${inferredType[1] ? \`, input[type="\${inferredType[1]}"\` : ''}]\`);
|
|
5030
5290
|
}
|
|
5031
5291
|
|
|
5032
|
-
if (el)
|
|
5033
|
-
await el.fill(String(fieldValue));
|
|
5034
|
-
}
|
|
5292
|
+
if (el) await el.fill(String(fieldValue));
|
|
5035
5293
|
}
|
|
5036
5294
|
|
|
5037
|
-
// \xA7LLM-AUTONOMY: Submit via semantic search \u2014 finds any element that looks like a submit action.
|
|
5038
5295
|
const submitEl = await page.$([
|
|
5039
5296
|
'button[type="submit"]',
|
|
5040
5297
|
'input[type="submit"]',
|
|
@@ -5047,27 +5304,29 @@ const fs = require('fs');
|
|
|
5047
5304
|
await submitEl.click();
|
|
5048
5305
|
await page.waitForLoadState('networkidle').catch(() => {});
|
|
5049
5306
|
}
|
|
5307
|
+
`;
|
|
5308
|
+
}
|
|
5309
|
+
function buildFormScript(params) {
|
|
5310
|
+
const { safeUrl, safeFormData, safePlaywrightPath, timeout, useSession, saveSession, safeSessionPath } = params;
|
|
5311
|
+
return `
|
|
5312
|
+
${buildCommonHeader(safePlaywrightPath)}
|
|
5313
|
+
${buildSessionContext({ useSession }, safeSessionPath)}
|
|
5314
|
+
${getNetworkAuthInterceptSnippet("_capturedHeaders")}
|
|
5315
|
+
try {
|
|
5316
|
+
await page.goto(${safeUrl}, { waitUntil: 'networkidle', timeout: ${timeout} });
|
|
5050
5317
|
|
|
5051
|
-
|
|
5052
|
-
${safeSessionPath && saveSession ? `
|
|
5053
|
-
await context.storageState({ path: ${safeSessionPath} });
|
|
5054
|
-
|
|
5055
|
-
${getStorageAndAuthDumpSnippet("_capturedHeaders", safeSessionPath)}
|
|
5056
|
-
` : ""}
|
|
5318
|
+
${buildFormFillingLogic(safeFormData)}
|
|
5057
5319
|
|
|
5058
5320
|
const result = {
|
|
5059
5321
|
url: page.url(),
|
|
5060
5322
|
title: await page.title(),
|
|
5061
|
-
|
|
5323
|
+
_interceptedAuth: _capturedHeaders
|
|
5062
5324
|
};
|
|
5063
5325
|
|
|
5326
|
+
${buildSessionSave(saveSession, safeSessionPath, "result", "result._interceptedAuth")}
|
|
5327
|
+
|
|
5064
5328
|
console.log(JSON.stringify(result));
|
|
5065
|
-
|
|
5066
|
-
console.log(JSON.stringify({ error: error.message }));
|
|
5067
|
-
} finally {
|
|
5068
|
-
await browser.close();
|
|
5069
|
-
}
|
|
5070
|
-
})();
|
|
5329
|
+
${buildCommonFooter()}
|
|
5071
5330
|
`;
|
|
5072
5331
|
}
|
|
5073
5332
|
|
|
@@ -7950,6 +8209,7 @@ var SessionState = class {
|
|
|
7950
8209
|
};
|
|
7951
8210
|
|
|
7952
8211
|
// src/engine/state/core/shared-state.ts
|
|
8212
|
+
var MAX_DELEGATED_TASKS = 50;
|
|
7953
8213
|
var SharedState = class {
|
|
7954
8214
|
attackGraph = new AttackGraph();
|
|
7955
8215
|
targetState;
|
|
@@ -7985,6 +8245,7 @@ var SharedState = class {
|
|
|
7985
8245
|
* Current objective for flexible task management.
|
|
7986
8246
|
*/
|
|
7987
8247
|
currentObjective = null;
|
|
8248
|
+
delegatedTasks = [];
|
|
7988
8249
|
constructor() {
|
|
7989
8250
|
this.targetState = new TargetState(this.attackGraph);
|
|
7990
8251
|
this.findingState = new FindingState();
|
|
@@ -8010,6 +8271,7 @@ var SharedState = class {
|
|
|
8010
8271
|
this.dynamicTechniques.clear();
|
|
8011
8272
|
this.artifacts = [];
|
|
8012
8273
|
this.currentObjective = null;
|
|
8274
|
+
this.delegatedTasks = [];
|
|
8013
8275
|
this.lastReflection = "";
|
|
8014
8276
|
this.lastTriageMemo = "";
|
|
8015
8277
|
}
|
|
@@ -8127,6 +8389,38 @@ var SharedState = class {
|
|
|
8127
8389
|
getTimeStatus() {
|
|
8128
8390
|
return this.sessionState.getTimeStatus();
|
|
8129
8391
|
}
|
|
8392
|
+
recordDelegatedTask(task) {
|
|
8393
|
+
const now = Date.now();
|
|
8394
|
+
const parentTask = task.resumeTaskId ? this.delegatedTasks.find((existing) => existing.id === task.resumeTaskId) : void 0;
|
|
8395
|
+
const record = {
|
|
8396
|
+
...task,
|
|
8397
|
+
parentTaskId: task.resumeTaskId || void 0,
|
|
8398
|
+
rootTaskId: parentTask ? parentTask.rootTaskId || parentTask.id : task.resumeTaskId || void 0,
|
|
8399
|
+
id: generatePrefixedId("delegated_task"),
|
|
8400
|
+
createdAt: now,
|
|
8401
|
+
updatedAt: now
|
|
8402
|
+
};
|
|
8403
|
+
this.delegatedTasks.push(record);
|
|
8404
|
+
if (this.delegatedTasks.length > MAX_DELEGATED_TASKS) {
|
|
8405
|
+
this.delegatedTasks = this.delegatedTasks.slice(this.delegatedTasks.length - MAX_DELEGATED_TASKS);
|
|
8406
|
+
}
|
|
8407
|
+
return record;
|
|
8408
|
+
}
|
|
8409
|
+
restoreDelegatedTask(task) {
|
|
8410
|
+
this.delegatedTasks.push(task);
|
|
8411
|
+
if (this.delegatedTasks.length > MAX_DELEGATED_TASKS) {
|
|
8412
|
+
this.delegatedTasks = this.delegatedTasks.slice(this.delegatedTasks.length - MAX_DELEGATED_TASKS);
|
|
8413
|
+
}
|
|
8414
|
+
}
|
|
8415
|
+
getDelegatedTasks() {
|
|
8416
|
+
return [...this.delegatedTasks];
|
|
8417
|
+
}
|
|
8418
|
+
getActiveDelegatedTasks() {
|
|
8419
|
+
return this.delegatedTasks.filter((task) => task.status === "waiting" || task.status === "running");
|
|
8420
|
+
}
|
|
8421
|
+
getDelegatedTask(taskId) {
|
|
8422
|
+
return this.delegatedTasks.find((task) => task.id === taskId);
|
|
8423
|
+
}
|
|
8130
8424
|
};
|
|
8131
8425
|
|
|
8132
8426
|
// src/shared/utils/policy/document.ts
|
|
@@ -8168,6 +8462,37 @@ function writePolicyDocument(content) {
|
|
|
8168
8462
|
);
|
|
8169
8463
|
}
|
|
8170
8464
|
|
|
8465
|
+
// src/engine/auxiliary-llm/strategist-prompt.ts
|
|
8466
|
+
function buildStrategistPrompt(input) {
|
|
8467
|
+
const state = input.state;
|
|
8468
|
+
const sections = ["## Engagement State", StateSerializer.toPrompt(state)];
|
|
8469
|
+
const failures = state.workingMemory.toPrompt();
|
|
8470
|
+
if (failures) sections.push("", "## Failed Attempts (DO NOT REPEAT THESE)", failures);
|
|
8471
|
+
try {
|
|
8472
|
+
const journalSummary = readJournalSummary();
|
|
8473
|
+
if (journalSummary) sections.push("", "## Session Journal (past turns summary)", journalSummary);
|
|
8474
|
+
} catch {
|
|
8475
|
+
}
|
|
8476
|
+
const policyDocument = readPolicyDocument();
|
|
8477
|
+
if (policyDocument) sections.push("", "## Policy Memory", policyDocument);
|
|
8478
|
+
const graph = state.attackGraph.toPrompt();
|
|
8479
|
+
if (graph) sections.push("", "## Attack Graph", graph);
|
|
8480
|
+
const timeline = state.episodicMemory.toPrompt();
|
|
8481
|
+
if (timeline) sections.push("", "## Recent Actions", timeline);
|
|
8482
|
+
const techniques = state.dynamicTechniques.toPrompt();
|
|
8483
|
+
if (techniques) sections.push("", "## Learned Techniques", techniques);
|
|
8484
|
+
sections.push("", "## Time", state.getTimeStatus());
|
|
8485
|
+
const analysis = state.getChallengeAnalysis?.();
|
|
8486
|
+
if (analysis && analysis.primaryType !== "unknown") {
|
|
8487
|
+
sections.push(
|
|
8488
|
+
"",
|
|
8489
|
+
"## Challenge Type",
|
|
8490
|
+
`${analysis.primaryType.toUpperCase()} (confidence: ${(analysis.confidence * 100).toFixed(0)}%)`
|
|
8491
|
+
);
|
|
8492
|
+
}
|
|
8493
|
+
return sections.join("\n");
|
|
8494
|
+
}
|
|
8495
|
+
|
|
8171
8496
|
// src/engine/auxiliary-llm/strategist.ts
|
|
8172
8497
|
var Strategist = class extends AuxiliaryLLMBase {
|
|
8173
8498
|
lastDirective = null;
|
|
@@ -8184,33 +8509,7 @@ var Strategist = class extends AuxiliaryLLMBase {
|
|
|
8184
8509
|
}
|
|
8185
8510
|
// ─── Abstract impl ───────────────────────────────────────────────────────
|
|
8186
8511
|
formatInput(input) {
|
|
8187
|
-
|
|
8188
|
-
const sections = ["## Engagement State", StateSerializer.toPrompt(state)];
|
|
8189
|
-
const failures = state.workingMemory.toPrompt();
|
|
8190
|
-
if (failures) sections.push("", "## Failed Attempts (DO NOT REPEAT THESE)", failures);
|
|
8191
|
-
try {
|
|
8192
|
-
const journalSummary = readJournalSummary();
|
|
8193
|
-
if (journalSummary) sections.push("", "## Session Journal (past turns summary)", journalSummary);
|
|
8194
|
-
} catch {
|
|
8195
|
-
}
|
|
8196
|
-
const policyDocument = readPolicyDocument();
|
|
8197
|
-
if (policyDocument) sections.push("", "## Policy Memory", policyDocument);
|
|
8198
|
-
const graph = state.attackGraph.toPrompt();
|
|
8199
|
-
if (graph) sections.push("", "## Attack Graph", graph);
|
|
8200
|
-
const timeline = state.episodicMemory.toPrompt();
|
|
8201
|
-
if (timeline) sections.push("", "## Recent Actions", timeline);
|
|
8202
|
-
const techniques = state.dynamicTechniques.toPrompt();
|
|
8203
|
-
if (techniques) sections.push("", "## Learned Techniques", techniques);
|
|
8204
|
-
sections.push("", "## Time", state.getTimeStatus());
|
|
8205
|
-
const analysis = state.getChallengeAnalysis?.();
|
|
8206
|
-
if (analysis && analysis.primaryType !== "unknown") {
|
|
8207
|
-
sections.push(
|
|
8208
|
-
"",
|
|
8209
|
-
"## Challenge Type",
|
|
8210
|
-
`${analysis.primaryType.toUpperCase()} (confidence: ${(analysis.confidence * 100).toFixed(0)}%)`
|
|
8211
|
-
);
|
|
8212
|
-
}
|
|
8213
|
-
return sections.join("\n");
|
|
8512
|
+
return buildStrategistPrompt(input);
|
|
8214
8513
|
}
|
|
8215
8514
|
parseOutput(_response) {
|
|
8216
8515
|
throw new Error("Unused \u2014 execute() is overridden in Strategist");
|
|
@@ -8631,7 +8930,7 @@ function isPortArray(value) {
|
|
|
8631
8930
|
(item) => typeof item === "object" && item !== null && typeof item.port === "number"
|
|
8632
8931
|
);
|
|
8633
8932
|
}
|
|
8634
|
-
function
|
|
8933
|
+
function parsePorts2(value) {
|
|
8635
8934
|
return isPortArray(value) ? value : [];
|
|
8636
8935
|
}
|
|
8637
8936
|
function isValidSeverity(value) {
|
|
@@ -8663,7 +8962,7 @@ var SPRAY_LOOT_TYPES = /* @__PURE__ */ new Set([
|
|
|
8663
8962
|
// src/domains/engagement/handlers/target.ts
|
|
8664
8963
|
async function executeAddTarget(p, state) {
|
|
8665
8964
|
const ip = p.ip;
|
|
8666
|
-
const ports =
|
|
8965
|
+
const ports = parsePorts2(p.ports);
|
|
8667
8966
|
const os = p.os;
|
|
8668
8967
|
const hostname = p.hostname;
|
|
8669
8968
|
const existing = state.getTarget(ip);
|
|
@@ -9172,99 +9471,70 @@ function getContextRecommendations(context, variantCount) {
|
|
|
9172
9471
|
}
|
|
9173
9472
|
|
|
9174
9473
|
// src/shared/utils/payload-mutator/core.ts
|
|
9474
|
+
var TRANSFORM_STRATEGIES = {
|
|
9475
|
+
[TRANSFORM_TYPE.URL]: (p, v) => {
|
|
9476
|
+
v.push({ payload: urlEncode(p), transform: "url", description: "URL encoded (special chars only)" });
|
|
9477
|
+
v.push({ payload: urlEncodeAll(p), transform: "url_full", description: "URL encoded (all chars)" });
|
|
9478
|
+
},
|
|
9479
|
+
[TRANSFORM_TYPE.DOUBLE_URL]: (p, v) => v.push({ payload: doubleUrlEncode(p), transform: "double_url", description: "Double URL encoded" }),
|
|
9480
|
+
[TRANSFORM_TYPE.TRIPLE_URL]: (p, v) => v.push({ payload: tripleUrlEncode(p), transform: "triple_url", description: "Triple URL encoded" }),
|
|
9481
|
+
[TRANSFORM_TYPE.UNICODE]: (p, v) => v.push({ payload: unicodeEncode(p), transform: "unicode", description: "Unicode escape sequences" }),
|
|
9482
|
+
[TRANSFORM_TYPE.HTML_ENTITY_DEC]: (p, v) => v.push({ payload: htmlEntityDec(p), transform: "html_entity_dec", description: "HTML decimal entities" }),
|
|
9483
|
+
[TRANSFORM_TYPE.HTML_ENTITY_HEX]: (p, v) => v.push({ payload: htmlEntityHex(p), transform: "html_entity_hex", description: "HTML hex entities" }),
|
|
9484
|
+
[TRANSFORM_TYPE.HEX]: (p, v) => v.push({ payload: hexEncode(p), transform: "hex", description: "Hex encoded" }),
|
|
9485
|
+
[TRANSFORM_TYPE.OCTAL]: (p, v) => v.push({ payload: octalEncode(p), transform: "octal", description: "Octal encoded" }),
|
|
9486
|
+
[TRANSFORM_TYPE.BASE64]: (p, v) => v.push({ payload: base64Encode(p), transform: "base64", description: "Base64 encoded" }),
|
|
9487
|
+
[TRANSFORM_TYPE.CASE_SWAP]: (p, v) => {
|
|
9488
|
+
for (let i = 0; i < 3; i++) v.push({ payload: caseSwap(p), transform: "case_swap", description: `Case variation ${i + 1}` });
|
|
9489
|
+
},
|
|
9490
|
+
[TRANSFORM_TYPE.COMMENT_INSERT]: (p, v) => v.push({ payload: commentInsert(p), transform: "comment_insert", description: "SQL comments in keywords" }),
|
|
9491
|
+
[TRANSFORM_TYPE.WHITESPACE_ALT]: (p, v) => {
|
|
9492
|
+
v.push({ payload: whitespaceAlt(p), transform: "whitespace_alt", description: "Alternative whitespace chars" });
|
|
9493
|
+
v.push({ payload: p.replace(/ /g, "/**/"), transform: "whitespace_comment", description: "Comment as whitespace" });
|
|
9494
|
+
v.push({ payload: p.replace(/ /g, "+"), transform: "whitespace_plus", description: "Plus as whitespace" });
|
|
9495
|
+
},
|
|
9496
|
+
[TRANSFORM_TYPE.CHAR_FUNCTION]: (p, v) => {
|
|
9497
|
+
v.push({ payload: charFunction(p, "mysql"), transform: "char_mysql", description: "MySQL CHAR() encoding" });
|
|
9498
|
+
v.push({ payload: charFunction(p, "mssql"), transform: "char_mssql", description: "MSSQL CHAR() encoding" });
|
|
9499
|
+
v.push({ payload: charFunction(p, "pg"), transform: "char_pg", description: "PostgreSQL CHR() encoding" });
|
|
9500
|
+
},
|
|
9501
|
+
[TRANSFORM_TYPE.CONCAT_SPLIT]: (p, v) => v.push({ payload: concatSplit(p), transform: "concat_split", description: "String split via CONCAT" }),
|
|
9502
|
+
[TRANSFORM_TYPE.NULL_BYTE]: (p, v) => {
|
|
9503
|
+
v.push({ payload: nullByteAppend(p), transform: "null_byte", description: "Null byte appended" });
|
|
9504
|
+
v.push({ payload: p + "%00.jpg", transform: "null_byte_ext", description: "Null byte + fake extension" });
|
|
9505
|
+
},
|
|
9506
|
+
[TRANSFORM_TYPE.REVERSE]: (p, v) => v.push({ payload: reversePayload(p), transform: "reverse", description: "Reversed (use with rev | sh)" }),
|
|
9507
|
+
[TRANSFORM_TYPE.UTF8_OVERLONG]: (p, v) => v.push({ payload: utf8Overlong(p), transform: "utf8_overlong", description: "UTF-8 overlong sequences" }),
|
|
9508
|
+
[TRANSFORM_TYPE.MIXED_ENCODING]: (p, v) => v.push({ payload: mixedEncoding(p), transform: "mixed_encoding", description: "Mixed encoding (partial)" }),
|
|
9509
|
+
[TRANSFORM_TYPE.TAG_ALTERNATIVE]: (p, v) => v.push(...generateXssAlternatives(p)),
|
|
9510
|
+
[TRANSFORM_TYPE.EVENT_HANDLER]: (p, v) => v.push(...generateXssAlternatives(p)),
|
|
9511
|
+
[TRANSFORM_TYPE.JS_ALTERNATIVE]: (p, v) => v.push(...generateXssAlternatives(p)),
|
|
9512
|
+
[TRANSFORM_TYPE.KEYWORD_BYPASS]: (p, v) => {
|
|
9513
|
+
v.push({ payload: keywordBypass(p), transform: "keyword_bypass", description: "Quote-inserted keyword bypass" });
|
|
9514
|
+
v.push({ payload: spaceBypass(p), transform: "space_bypass", description: "$IFS space bypass" });
|
|
9515
|
+
},
|
|
9516
|
+
[TRANSFORM_TYPE.SPACE_BYPASS]: (p, v) => {
|
|
9517
|
+
v.push({ payload: p.replace(/ /g, "${IFS}"), transform: "ifs", description: "$IFS space bypass" });
|
|
9518
|
+
v.push({ payload: p.replace(/ /g, "%09"), transform: "tab", description: "Tab space bypass" });
|
|
9519
|
+
v.push({ payload: p.replace(/ /g, "<"), transform: "redirect", description: "Redirect as separator" });
|
|
9520
|
+
const parts = p.split(" ");
|
|
9521
|
+
if (parts.length >= 2) v.push({ payload: `{${parts.join(",")}}`, transform: "brace", description: "Brace expansion" });
|
|
9522
|
+
}
|
|
9523
|
+
};
|
|
9175
9524
|
function mutatePayload(request) {
|
|
9176
9525
|
const { payload, transforms, context = "generic", maxVariants = 20 } = request;
|
|
9177
9526
|
const variants = [];
|
|
9178
|
-
const recommendations = [];
|
|
9179
9527
|
const activeTransforms = transforms || getDefaultTransforms(context);
|
|
9180
9528
|
for (const transform of activeTransforms) {
|
|
9181
|
-
|
|
9182
|
-
|
|
9183
|
-
|
|
9184
|
-
variants.push({ payload: urlEncodeAll(payload), transform: "url_full", description: "URL encoded (all chars)" });
|
|
9185
|
-
break;
|
|
9186
|
-
case TRANSFORM_TYPE.DOUBLE_URL:
|
|
9187
|
-
variants.push({ payload: doubleUrlEncode(payload), transform: "double_url", description: "Double URL encoded" });
|
|
9188
|
-
break;
|
|
9189
|
-
case TRANSFORM_TYPE.TRIPLE_URL:
|
|
9190
|
-
variants.push({ payload: tripleUrlEncode(payload), transform: "triple_url", description: "Triple URL encoded" });
|
|
9191
|
-
break;
|
|
9192
|
-
case TRANSFORM_TYPE.UNICODE:
|
|
9193
|
-
variants.push({ payload: unicodeEncode(payload), transform: "unicode", description: "Unicode escape sequences" });
|
|
9194
|
-
break;
|
|
9195
|
-
case TRANSFORM_TYPE.HTML_ENTITY_DEC:
|
|
9196
|
-
variants.push({ payload: htmlEntityDec(payload), transform: "html_entity_dec", description: "HTML decimal entities" });
|
|
9197
|
-
break;
|
|
9198
|
-
case TRANSFORM_TYPE.HTML_ENTITY_HEX:
|
|
9199
|
-
variants.push({ payload: htmlEntityHex(payload), transform: "html_entity_hex", description: "HTML hex entities" });
|
|
9200
|
-
break;
|
|
9201
|
-
case TRANSFORM_TYPE.HEX:
|
|
9202
|
-
variants.push({ payload: hexEncode(payload), transform: "hex", description: "Hex encoded" });
|
|
9203
|
-
break;
|
|
9204
|
-
case TRANSFORM_TYPE.OCTAL:
|
|
9205
|
-
variants.push({ payload: octalEncode(payload), transform: "octal", description: "Octal encoded" });
|
|
9206
|
-
break;
|
|
9207
|
-
case TRANSFORM_TYPE.BASE64:
|
|
9208
|
-
variants.push({ payload: base64Encode(payload), transform: "base64", description: "Base64 encoded" });
|
|
9209
|
-
break;
|
|
9210
|
-
case TRANSFORM_TYPE.CASE_SWAP:
|
|
9211
|
-
for (let i = 0; i < 3; i++) {
|
|
9212
|
-
variants.push({ payload: caseSwap(payload), transform: "case_swap", description: `Case variation ${i + 1}` });
|
|
9213
|
-
}
|
|
9214
|
-
break;
|
|
9215
|
-
case TRANSFORM_TYPE.COMMENT_INSERT:
|
|
9216
|
-
variants.push({ payload: commentInsert(payload), transform: "comment_insert", description: "SQL comments in keywords" });
|
|
9217
|
-
break;
|
|
9218
|
-
case TRANSFORM_TYPE.WHITESPACE_ALT:
|
|
9219
|
-
variants.push({ payload: whitespaceAlt(payload), transform: "whitespace_alt", description: "Alternative whitespace chars" });
|
|
9220
|
-
variants.push({ payload: payload.replace(/ /g, "/**/"), transform: "whitespace_comment", description: "Comment as whitespace" });
|
|
9221
|
-
variants.push({ payload: payload.replace(/ /g, "+"), transform: "whitespace_plus", description: "Plus as whitespace" });
|
|
9222
|
-
break;
|
|
9223
|
-
case TRANSFORM_TYPE.CHAR_FUNCTION:
|
|
9224
|
-
variants.push({ payload: charFunction(payload, "mysql"), transform: "char_mysql", description: "MySQL CHAR() encoding" });
|
|
9225
|
-
variants.push({ payload: charFunction(payload, "mssql"), transform: "char_mssql", description: "MSSQL CHAR() encoding" });
|
|
9226
|
-
variants.push({ payload: charFunction(payload, "pg"), transform: "char_pg", description: "PostgreSQL CHR() encoding" });
|
|
9227
|
-
break;
|
|
9228
|
-
case TRANSFORM_TYPE.CONCAT_SPLIT:
|
|
9229
|
-
variants.push({ payload: concatSplit(payload), transform: "concat_split", description: "String split via CONCAT" });
|
|
9230
|
-
break;
|
|
9231
|
-
case TRANSFORM_TYPE.NULL_BYTE:
|
|
9232
|
-
variants.push({ payload: nullByteAppend(payload), transform: "null_byte", description: "Null byte appended" });
|
|
9233
|
-
variants.push({ payload: payload + "%00.jpg", transform: "null_byte_ext", description: "Null byte + fake extension" });
|
|
9234
|
-
break;
|
|
9235
|
-
case TRANSFORM_TYPE.REVERSE:
|
|
9236
|
-
variants.push({ payload: reversePayload(payload), transform: "reverse", description: "Reversed (use with rev | sh)" });
|
|
9237
|
-
break;
|
|
9238
|
-
case TRANSFORM_TYPE.UTF8_OVERLONG:
|
|
9239
|
-
variants.push({ payload: utf8Overlong(payload), transform: "utf8_overlong", description: "UTF-8 overlong sequences" });
|
|
9240
|
-
break;
|
|
9241
|
-
case TRANSFORM_TYPE.MIXED_ENCODING:
|
|
9242
|
-
variants.push({ payload: mixedEncoding(payload), transform: "mixed_encoding", description: "Mixed encoding (partial)" });
|
|
9243
|
-
break;
|
|
9244
|
-
case TRANSFORM_TYPE.TAG_ALTERNATIVE:
|
|
9245
|
-
case TRANSFORM_TYPE.EVENT_HANDLER:
|
|
9246
|
-
case TRANSFORM_TYPE.JS_ALTERNATIVE:
|
|
9247
|
-
variants.push(...generateXssAlternatives(payload));
|
|
9248
|
-
break;
|
|
9249
|
-
case TRANSFORM_TYPE.KEYWORD_BYPASS:
|
|
9250
|
-
variants.push({ payload: keywordBypass(payload), transform: "keyword_bypass", description: "Quote-inserted keyword bypass" });
|
|
9251
|
-
variants.push({ payload: spaceBypass(payload), transform: "space_bypass", description: "$IFS space bypass" });
|
|
9252
|
-
break;
|
|
9253
|
-
case TRANSFORM_TYPE.SPACE_BYPASS:
|
|
9254
|
-
variants.push({ payload: payload.replace(/ /g, "${IFS}"), transform: "ifs", description: "$IFS space bypass" });
|
|
9255
|
-
variants.push({ payload: payload.replace(/ /g, "%09"), transform: "tab", description: "Tab space bypass" });
|
|
9256
|
-
variants.push({ payload: payload.replace(/ /g, "<"), transform: "redirect", description: "Redirect as separator" });
|
|
9257
|
-
const parts = payload.split(" ");
|
|
9258
|
-
if (parts.length >= 2) {
|
|
9259
|
-
variants.push({ payload: `{${parts.join(",")}}`, transform: "brace", description: "Brace expansion" });
|
|
9260
|
-
}
|
|
9261
|
-
break;
|
|
9529
|
+
const handler = TRANSFORM_STRATEGIES[transform];
|
|
9530
|
+
if (handler) {
|
|
9531
|
+
handler(payload, variants);
|
|
9262
9532
|
}
|
|
9263
9533
|
}
|
|
9264
9534
|
if (context.startsWith("sql")) {
|
|
9265
9535
|
variants.push(...generateSqlAlternatives(payload));
|
|
9266
9536
|
}
|
|
9267
|
-
recommendations
|
|
9537
|
+
const recommendations = getContextRecommendations(context, variants.length);
|
|
9268
9538
|
return {
|
|
9269
9539
|
variants: variants.slice(0, maxVariants),
|
|
9270
9540
|
recommendations
|
|
@@ -9324,8 +9594,8 @@ async function executeGetWordlists(p) {
|
|
|
9324
9594
|
};
|
|
9325
9595
|
const matchesSearch = (filePath, fileName) => {
|
|
9326
9596
|
if (!search) return true;
|
|
9327
|
-
const
|
|
9328
|
-
return fileName.toLowerCase().includes(
|
|
9597
|
+
const q2 = search.toLowerCase();
|
|
9598
|
+
return fileName.toLowerCase().includes(q2) || filePath.toLowerCase().includes(q2);
|
|
9329
9599
|
};
|
|
9330
9600
|
const results = ["# Available Wordlists on System\n"];
|
|
9331
9601
|
let totalCount = 0;
|
|
@@ -10653,7 +10923,7 @@ async function executeGoogleDork(args) {
|
|
|
10653
10923
|
const output = [
|
|
10654
10924
|
`=== Google Dork Queries for ${d} ===`,
|
|
10655
10925
|
"",
|
|
10656
|
-
...selected.map((
|
|
10926
|
+
...selected.map((q2, i) => `${i + 1}. https://www.google.com/search?q=${encodeURIComponent(q2)}`),
|
|
10657
10927
|
"",
|
|
10658
10928
|
"Paste these URLs into a browser to search manually.",
|
|
10659
10929
|
"Tip: Also try: https://pentest-tools.com/information-gathering/google-hacking"
|
|
@@ -10908,6 +11178,51 @@ function logSuccessfulAction(state, toolCall, approval, result) {
|
|
|
10908
11178
|
});
|
|
10909
11179
|
}
|
|
10910
11180
|
|
|
11181
|
+
// src/engine/parsers/security.ts
|
|
11182
|
+
function extractCredentials(output) {
|
|
11183
|
+
const creds = [];
|
|
11184
|
+
const patterns = [
|
|
11185
|
+
// user:password from hydra, medusa, etc.
|
|
11186
|
+
{ regex: /login:\s*(\S+)\s+password:\s*(\S+)/gi, type: "password" },
|
|
11187
|
+
// user:hash from hashdump, secretsdump, etc.
|
|
11188
|
+
{ regex: /(\S+):(\d+):([a-f0-9]{32}):([a-f0-9]{32})/g, type: "hash" },
|
|
11189
|
+
// Generic user:pass pattern
|
|
11190
|
+
{ regex: /(?:username|user|login)\s*[:=]\s*['"]?(\S+?)['"]?\s+(?:password|pass|pwd)\s*[:=]\s*['"]?(\S+?)['"]?/gi, type: "password" },
|
|
11191
|
+
// Database connection strings
|
|
11192
|
+
{ regex: /(?:postgres|mysql|mongodb):\/\/([^:]+):([^@]+)@/g, type: "password" },
|
|
11193
|
+
// API tokens/keys
|
|
11194
|
+
{ regex: /(?:api[_-]?key|token|secret|bearer)\s*[:=]\s*['"]?([a-zA-Z0-9_\-]{20,})['"]?/gi, type: "token" }
|
|
11195
|
+
];
|
|
11196
|
+
for (const { regex, type } of patterns) {
|
|
11197
|
+
let match;
|
|
11198
|
+
while ((match = regex.exec(output)) !== null) {
|
|
11199
|
+
const username = match[1] || "unknown";
|
|
11200
|
+
const credential = match[2] || match[1];
|
|
11201
|
+
if (credential && !creds.some((c) => c.username === username && c.credential === credential)) {
|
|
11202
|
+
creds.push({ username, credential, type, source: "auto-extracted" });
|
|
11203
|
+
}
|
|
11204
|
+
}
|
|
11205
|
+
}
|
|
11206
|
+
return creds;
|
|
11207
|
+
}
|
|
11208
|
+
function extractVulnerabilities(output) {
|
|
11209
|
+
const vulns = [];
|
|
11210
|
+
const cveRegex = /CVE-\d{4}-\d{4,}/g;
|
|
11211
|
+
let match;
|
|
11212
|
+
while ((match = cveRegex.exec(output)) !== null) {
|
|
11213
|
+
const cveId = match[0];
|
|
11214
|
+
if (!vulns.some((v) => v.id === cveId)) {
|
|
11215
|
+
vulns.push({
|
|
11216
|
+
id: cveId,
|
|
11217
|
+
severity: "unknown",
|
|
11218
|
+
description: `${cveId} referenced in output`,
|
|
11219
|
+
hasExploit: /exploit|poc|proof.of.concept/i.test(output)
|
|
11220
|
+
});
|
|
11221
|
+
}
|
|
11222
|
+
}
|
|
11223
|
+
return vulns;
|
|
11224
|
+
}
|
|
11225
|
+
|
|
10911
11226
|
// src/engine/parsers/types.ts
|
|
10912
11227
|
var PORT_STATE2 = {
|
|
10913
11228
|
OPEN: "open",
|
|
@@ -10977,51 +11292,6 @@ function extractFuzzStructured(output) {
|
|
|
10977
11292
|
};
|
|
10978
11293
|
}
|
|
10979
11294
|
|
|
10980
|
-
// src/engine/parsers/security.ts
|
|
10981
|
-
function extractCredentials(output) {
|
|
10982
|
-
const creds = [];
|
|
10983
|
-
const patterns = [
|
|
10984
|
-
// user:password from hydra, medusa, etc.
|
|
10985
|
-
{ regex: /login:\s*(\S+)\s+password:\s*(\S+)/gi, type: "password" },
|
|
10986
|
-
// user:hash from hashdump, secretsdump, etc.
|
|
10987
|
-
{ regex: /(\S+):(\d+):([a-f0-9]{32}):([a-f0-9]{32})/g, type: "hash" },
|
|
10988
|
-
// Generic user:pass pattern
|
|
10989
|
-
{ regex: /(?:username|user|login)\s*[:=]\s*['"]?(\S+?)['"]?\s+(?:password|pass|pwd)\s*[:=]\s*['"]?(\S+?)['"]?/gi, type: "password" },
|
|
10990
|
-
// Database connection strings
|
|
10991
|
-
{ regex: /(?:postgres|mysql|mongodb):\/\/([^:]+):([^@]+)@/g, type: "password" },
|
|
10992
|
-
// API tokens/keys
|
|
10993
|
-
{ regex: /(?:api[_-]?key|token|secret|bearer)\s*[:=]\s*['"]?([a-zA-Z0-9_\-]{20,})['"]?/gi, type: "token" }
|
|
10994
|
-
];
|
|
10995
|
-
for (const { regex, type } of patterns) {
|
|
10996
|
-
let match;
|
|
10997
|
-
while ((match = regex.exec(output)) !== null) {
|
|
10998
|
-
const username = match[1] || "unknown";
|
|
10999
|
-
const credential = match[2] || match[1];
|
|
11000
|
-
if (credential && !creds.some((c) => c.username === username && c.credential === credential)) {
|
|
11001
|
-
creds.push({ username, credential, type, source: "auto-extracted" });
|
|
11002
|
-
}
|
|
11003
|
-
}
|
|
11004
|
-
}
|
|
11005
|
-
return creds;
|
|
11006
|
-
}
|
|
11007
|
-
function extractVulnerabilities(output) {
|
|
11008
|
-
const vulns = [];
|
|
11009
|
-
const cveRegex = /CVE-\d{4}-\d{4,}/g;
|
|
11010
|
-
let match;
|
|
11011
|
-
while ((match = cveRegex.exec(output)) !== null) {
|
|
11012
|
-
const cveId = match[0];
|
|
11013
|
-
if (!vulns.some((v) => v.id === cveId)) {
|
|
11014
|
-
vulns.push({
|
|
11015
|
-
id: cveId,
|
|
11016
|
-
severity: "unknown",
|
|
11017
|
-
description: `${cveId} referenced in output`,
|
|
11018
|
-
hasExploit: /exploit|poc|proof.of.concept/i.test(output)
|
|
11019
|
-
});
|
|
11020
|
-
}
|
|
11021
|
-
}
|
|
11022
|
-
return vulns;
|
|
11023
|
-
}
|
|
11024
|
-
|
|
11025
11295
|
// src/shared/utils/binary-analysis/types.ts
|
|
11026
11296
|
var RELRO_STATUS = {
|
|
11027
11297
|
NO: "no",
|
|
@@ -11124,45 +11394,42 @@ function formatBinaryAnalysis(info) {
|
|
|
11124
11394
|
return lines.join("\n");
|
|
11125
11395
|
}
|
|
11126
11396
|
|
|
11127
|
-
// src/engine/parsers/
|
|
11128
|
-
function
|
|
11129
|
-
if (!output || output.length < 10) return null;
|
|
11130
|
-
const data = {};
|
|
11131
|
-
let hasData = false;
|
|
11132
|
-
const creds = extractCredentials(output);
|
|
11133
|
-
if (creds && creds.length > 0) {
|
|
11134
|
-
data.credentials = creds;
|
|
11135
|
-
hasData = true;
|
|
11136
|
-
}
|
|
11137
|
-
const vulns = extractVulnerabilities(output);
|
|
11138
|
-
if (vulns && vulns.length > 0) {
|
|
11139
|
-
data.vulnerabilities = vulns;
|
|
11140
|
-
hasData = true;
|
|
11141
|
-
}
|
|
11397
|
+
// src/engine/parsers/auto-extractors.ts
|
|
11398
|
+
function extractNmapData(toolName, output, data) {
|
|
11142
11399
|
if (toolName === TOOL_NAMES.PARSE_NMAP || /nmap scan report/i.test(output)) {
|
|
11143
11400
|
const nmap = extractNmapStructured(output);
|
|
11144
11401
|
if (nmap.structured.openPorts && nmap.structured.openPorts.length > 0) {
|
|
11145
11402
|
data.openPorts = nmap.structured.openPorts;
|
|
11146
11403
|
data.hosts = nmap.structured.hosts;
|
|
11147
|
-
|
|
11404
|
+
return true;
|
|
11148
11405
|
}
|
|
11149
11406
|
}
|
|
11407
|
+
return false;
|
|
11408
|
+
}
|
|
11409
|
+
function extractFuzzData(output, data) {
|
|
11150
11410
|
if (/gobuster|ffuf|feroxbuster|dirbuster/i.test(output)) {
|
|
11151
11411
|
const fuzz = extractFuzzStructured(output);
|
|
11152
11412
|
if (fuzz.structured.paths && fuzz.structured.paths.length > 0) {
|
|
11153
11413
|
data.paths = fuzz.structured.paths;
|
|
11154
|
-
|
|
11414
|
+
return true;
|
|
11155
11415
|
}
|
|
11156
11416
|
}
|
|
11417
|
+
return false;
|
|
11418
|
+
}
|
|
11419
|
+
function extractBinaryData(output, data) {
|
|
11157
11420
|
if (/canary|RELRO|NX|PIE|Stack|FORTIFY/i.test(output) && /enabled|disabled|found|no /i.test(output)) {
|
|
11158
11421
|
const binaryInfo = parseChecksec(output);
|
|
11159
11422
|
if (binaryInfo.arch || binaryInfo.canary !== void 0 || binaryInfo.nx !== void 0) {
|
|
11160
11423
|
data.binaryInfo = binaryInfo;
|
|
11161
11424
|
data.binaryAnalysisSummary = formatBinaryAnalysis(binaryInfo);
|
|
11162
|
-
|
|
11425
|
+
return true;
|
|
11163
11426
|
}
|
|
11164
11427
|
}
|
|
11428
|
+
return false;
|
|
11429
|
+
}
|
|
11430
|
+
function extractOsintData(toolName, output, data) {
|
|
11165
11431
|
if (toolName === TOOL_NAMES.WHOIS_LOOKUP || toolName === TOOL_NAMES.DNS_RECON || toolName === TOOL_NAMES.SUBDOMAIN_ENUM || toolName === TOOL_NAMES.HARVESTER || toolName === TOOL_NAMES.CERT_TRANSPARENCY) {
|
|
11432
|
+
let hasData = false;
|
|
11166
11433
|
const ipMatches = output.match(/\b(?:\d{1,3}\.){3}\d{1,3}\b/g);
|
|
11167
11434
|
if (ipMatches && ipMatches.length > 0) {
|
|
11168
11435
|
data.discoveredIPs = [...new Set(ipMatches)].slice(0, 50);
|
|
@@ -11173,21 +11440,52 @@ function autoExtractStructured(toolName, output) {
|
|
|
11173
11440
|
data.discoveredSubdomains = [...new Set(subMatches)].slice(0, 100);
|
|
11174
11441
|
hasData = true;
|
|
11175
11442
|
}
|
|
11443
|
+
return hasData;
|
|
11176
11444
|
}
|
|
11445
|
+
return false;
|
|
11446
|
+
}
|
|
11447
|
+
function extractCryptoData(toolName, output, data) {
|
|
11177
11448
|
if (toolName === TOOL_NAMES.RSA_ANALYZE || toolName === TOOL_NAMES.HASH_IDENTIFY || toolName === TOOL_NAMES.PADDING_ORACLE_CHECK) {
|
|
11178
11449
|
const weaknessMatches = output.match(/(?:FERMAT_FACTORED|SMALL_E|WEAK_N|Padding Oracle|hash type|cracked)[^\n]*/gi);
|
|
11179
11450
|
if (weaknessMatches && weaknessMatches.length > 0) {
|
|
11180
11451
|
data.cryptoWeaknesses = weaknessMatches.slice(0, 10);
|
|
11181
|
-
|
|
11452
|
+
return true;
|
|
11182
11453
|
}
|
|
11183
11454
|
}
|
|
11455
|
+
return false;
|
|
11456
|
+
}
|
|
11457
|
+
function extractForensicsData(toolName, output, data) {
|
|
11184
11458
|
if (toolName === TOOL_NAMES.BINWALK_ANALYZE || toolName === TOOL_NAMES.STEGHIDE_EXTRACT || toolName === TOOL_NAMES.ZSTEG_ANALYZE || toolName === TOOL_NAMES.STEGSEEK) {
|
|
11185
11459
|
const stegoMatches = output.match(/(?:JPEG|PNG|ZIP|embedded|hidden|extracted|passphrase|found)[^\n]*/gi);
|
|
11186
11460
|
if (stegoMatches && stegoMatches.length > 0) {
|
|
11187
11461
|
data.stegoHints = stegoMatches.slice(0, 10);
|
|
11188
|
-
|
|
11462
|
+
return true;
|
|
11189
11463
|
}
|
|
11190
11464
|
}
|
|
11465
|
+
return false;
|
|
11466
|
+
}
|
|
11467
|
+
|
|
11468
|
+
// src/engine/parsers/index.ts
|
|
11469
|
+
function autoExtractStructured(toolName, output) {
|
|
11470
|
+
if (!output || output.length < 10) return null;
|
|
11471
|
+
const data = {};
|
|
11472
|
+
let hasData = false;
|
|
11473
|
+
const creds = extractCredentials(output);
|
|
11474
|
+
if (creds && creds.length > 0) {
|
|
11475
|
+
data.credentials = creds;
|
|
11476
|
+
hasData = true;
|
|
11477
|
+
}
|
|
11478
|
+
const vulns = extractVulnerabilities(output);
|
|
11479
|
+
if (vulns && vulns.length > 0) {
|
|
11480
|
+
data.vulnerabilities = vulns;
|
|
11481
|
+
hasData = true;
|
|
11482
|
+
}
|
|
11483
|
+
if (extractNmapData(toolName, output, data)) hasData = true;
|
|
11484
|
+
if (extractFuzzData(output, data)) hasData = true;
|
|
11485
|
+
if (extractBinaryData(output, data)) hasData = true;
|
|
11486
|
+
if (extractOsintData(toolName, output, data)) hasData = true;
|
|
11487
|
+
if (extractCryptoData(toolName, output, data)) hasData = true;
|
|
11488
|
+
if (extractForensicsData(toolName, output, data)) hasData = true;
|
|
11191
11489
|
return hasData ? data : null;
|
|
11192
11490
|
}
|
|
11193
11491
|
|
|
@@ -11345,7 +11643,7 @@ function runPostExecutionPipeline(state, toolCall, result) {
|
|
|
11345
11643
|
// src/engine/tool-registry/core.ts
|
|
11346
11644
|
function validateRequiredParams(tool, input) {
|
|
11347
11645
|
if (!tool.required || tool.required.length === 0) return [];
|
|
11348
|
-
return tool.required.filter((param2) => !(param2 in input) || input[param2]
|
|
11646
|
+
return tool.required.filter((param2) => !(param2 in input) || input[param2] == null);
|
|
11349
11647
|
}
|
|
11350
11648
|
var ToolRegistry = class {
|
|
11351
11649
|
constructor(state, scopeGuard, approvalGate, events) {
|
|
@@ -11553,7 +11851,16 @@ After completion: record key loot/findings from the sub-agent output to canonica
|
|
|
11553
11851
|
task: { type: "string", description: "Delegated task goal" },
|
|
11554
11852
|
target: { type: "string", description: "Optional target IP/URL/path" },
|
|
11555
11853
|
context: { type: "string", description: "Optional extra context" },
|
|
11556
|
-
time_limit_min: { type: "number", description: "Optional hint only" }
|
|
11854
|
+
time_limit_min: { type: "number", description: "Optional hint only" },
|
|
11855
|
+
worker_type: {
|
|
11856
|
+
type: "string",
|
|
11857
|
+
enum: ["general", "shell-supervisor", "exploit", "pwn"],
|
|
11858
|
+
description: "Optional worker specialization for the delegated task"
|
|
11859
|
+
},
|
|
11860
|
+
resume_task_id: {
|
|
11861
|
+
type: "string",
|
|
11862
|
+
description: "Optional delegated task ID to resume/continue instead of creating a fresh chain"
|
|
11863
|
+
}
|
|
11557
11864
|
},
|
|
11558
11865
|
required: ["task"],
|
|
11559
11866
|
execute: async (params) => {
|
|
@@ -11568,18 +11875,43 @@ After completion: record key loot/findings from the sub-agent output to canonica
|
|
|
11568
11875
|
task: params["task"],
|
|
11569
11876
|
target: params["target"],
|
|
11570
11877
|
context: params["context"],
|
|
11571
|
-
timeLimitMin: params["time_limit_min"]
|
|
11878
|
+
timeLimitMin: params["time_limit_min"],
|
|
11879
|
+
workerType: params["worker_type"],
|
|
11880
|
+
resumeTaskId: params["resume_task_id"]
|
|
11572
11881
|
};
|
|
11573
|
-
const { AgentTool } = await import("./agent-tool-
|
|
11882
|
+
const { AgentTool } = await import("./agent-tool-JEFUBDZE.js");
|
|
11574
11883
|
const executor = new AgentTool(state, events, scopeGuard, approvalGate);
|
|
11575
11884
|
const result = await executor.execute(input);
|
|
11885
|
+
state.recordDelegatedTask({
|
|
11886
|
+
task: input.task,
|
|
11887
|
+
target: input.target,
|
|
11888
|
+
context: input.context,
|
|
11889
|
+
resumeTaskId: input.resumeTaskId,
|
|
11890
|
+
workerType: input.workerType,
|
|
11891
|
+
status: result.status,
|
|
11892
|
+
summary: result.summary,
|
|
11893
|
+
tried: result.tried,
|
|
11894
|
+
findings: result.findings,
|
|
11895
|
+
loot: result.loot,
|
|
11896
|
+
sessions: result.sessions,
|
|
11897
|
+
assets: result.assets,
|
|
11898
|
+
suggestedNext: result.suggestedNext,
|
|
11899
|
+
waitingOn: result.waitingOn,
|
|
11900
|
+
resumeHint: result.resumeHint,
|
|
11901
|
+
nextWorkerType: result.nextWorkerType
|
|
11902
|
+
});
|
|
11576
11903
|
const lines = [
|
|
11577
11904
|
`[Status] ${result.status}`,
|
|
11578
11905
|
`[Summary] ${result.summary}`,
|
|
11579
11906
|
result.findings.length ? `[Findings] ${result.findings.join(" | ")}` : "",
|
|
11580
11907
|
result.loot.length ? `[Loot] ${result.loot.join(" | ")}` : "",
|
|
11581
11908
|
result.sessions.length ? `[Sessions] ${result.sessions.join(", ")}` : "",
|
|
11909
|
+
result.assets.length ? `[Assets] ${result.assets.join(" | ")}` : "",
|
|
11582
11910
|
result.tried.length ? `[Tried] ${result.tried.join(" | ")}` : "",
|
|
11911
|
+
input.resumeTaskId ? `[ResumedFrom] ${input.resumeTaskId}` : "",
|
|
11912
|
+
result.waitingOn ? `[Waiting] ${result.waitingOn}` : "",
|
|
11913
|
+
result.resumeHint ? `[Resume] ${result.resumeHint}` : "",
|
|
11914
|
+
result.nextWorkerType ? `[NextWorker] ${result.nextWorkerType}` : "",
|
|
11583
11915
|
result.suggestedNext ? `[Next] ${result.suggestedNext}` : ""
|
|
11584
11916
|
].filter(Boolean);
|
|
11585
11917
|
return { success: true, output: lines.join("\n") };
|
|
@@ -11618,6 +11950,18 @@ var CategorizedToolRegistry = class extends ToolRegistry {
|
|
|
11618
11950
|
TOOL_NAMES.READ_FILE,
|
|
11619
11951
|
TOOL_NAMES.WRITE_FILE,
|
|
11620
11952
|
TOOL_NAMES.BG_PROCESS,
|
|
11953
|
+
TOOL_NAMES.LISTENER_START,
|
|
11954
|
+
TOOL_NAMES.LISTENER_STATUS,
|
|
11955
|
+
TOOL_NAMES.SHELL_PROMOTE,
|
|
11956
|
+
TOOL_NAMES.SHELL_EXEC,
|
|
11957
|
+
TOOL_NAMES.SHELL_CHECK,
|
|
11958
|
+
TOOL_NAMES.SHELL_UPGRADE,
|
|
11959
|
+
TOOL_NAMES.EXPLOIT_CREDENTIAL_CHECK,
|
|
11960
|
+
TOOL_NAMES.EXPLOIT_ARTIFACT_CHECK,
|
|
11961
|
+
TOOL_NAMES.EXPLOIT_FOOTHOLD_CHECK,
|
|
11962
|
+
TOOL_NAMES.PWN_CRASH_REPRO,
|
|
11963
|
+
TOOL_NAMES.PWN_OFFSET_CHECK,
|
|
11964
|
+
TOOL_NAMES.PWN_PAYLOAD_SMOKE,
|
|
11621
11965
|
TOOL_NAMES.PARSE_NMAP,
|
|
11622
11966
|
TOOL_NAMES.SEARCH_CVE,
|
|
11623
11967
|
TOOL_NAMES.WEB_SEARCH,
|
|
@@ -11666,6 +12010,110 @@ var CategorizedToolRegistry = class extends ToolRegistry {
|
|
|
11666
12010
|
}
|
|
11667
12011
|
};
|
|
11668
12012
|
|
|
12013
|
+
// src/engine/agent-tool/shell-supervisor-lifecycle.ts
|
|
12014
|
+
function hasConnectionSignal(processId, stdout) {
|
|
12015
|
+
if (DETECTION_PATTERNS.CONNECTION.some((pattern) => pattern.test(stdout))) {
|
|
12016
|
+
return true;
|
|
12017
|
+
}
|
|
12018
|
+
return getProcessEventLog().some(
|
|
12019
|
+
(event) => event.processId === processId && event.event === PROCESS_EVENTS.CONNECTION_DETECTED
|
|
12020
|
+
);
|
|
12021
|
+
}
|
|
12022
|
+
function getRecentCommandDetails(processId) {
|
|
12023
|
+
return getProcessEventLog().filter((event) => event.processId === processId && event.event === PROCESS_EVENTS.COMMAND_SENT).slice(-10).map((event) => event.detail.toLowerCase());
|
|
12024
|
+
}
|
|
12025
|
+
function hasEvent(processId, eventName) {
|
|
12026
|
+
return getProcessEventLog().some(
|
|
12027
|
+
(event) => event.processId === processId && event.event === eventName
|
|
12028
|
+
);
|
|
12029
|
+
}
|
|
12030
|
+
function isPtyUpgradeCommand(detail) {
|
|
12031
|
+
return detail.includes("pty.spawn(") || detail.includes("import pty; pty.spawn(") || detail.includes("script -qc") || detail.includes("script -q /dev/null -c /bin/bash") || detail.includes("script /dev/null -c bash") || detail.includes("stty raw -echo") || detail.includes("export term=") || detail.includes("export shell=") || detail.includes("stty rows") || detail.includes("stty columns") || detail.includes("tty") || detail.includes("/usr/bin/expect -c") || detail.includes('exec "/bin/bash"');
|
|
12032
|
+
}
|
|
12033
|
+
function isShellStabilized(stdout, commandDetails) {
|
|
12034
|
+
const normalized = stdout.toLowerCase();
|
|
12035
|
+
return normalized.includes("xterm") || normalized.includes("rows") || normalized.includes("columns") || commandDetails.some(
|
|
12036
|
+
(detail) => detail.includes("export term=") || detail.includes("export shell=") || detail.includes("stty rows") || detail.includes("stty columns")
|
|
12037
|
+
);
|
|
12038
|
+
}
|
|
12039
|
+
function isPostExploitationCommand(detail) {
|
|
12040
|
+
return detail.includes("sudo -l") || detail.includes("ip a") || detail.includes("ip route") || detail.includes("ps aux") || detail.includes("ss -tlnp") || detail.includes("netstat -tlnp") || detail.includes("env | grep") || detail.includes("find / -perm -4000") || detail.includes("getcap -r /") || detail.includes("cat /etc/os-release") || detail.includes("uname -a") || detail.includes("whoami && hostname");
|
|
12041
|
+
}
|
|
12042
|
+
function getShellSupervisorLifecycleSnapshot() {
|
|
12043
|
+
const processes = listBackgroundProcesses().filter(
|
|
12044
|
+
(process2) => process2.isRunning && (process2.role === PROCESS_ROLES.ACTIVE_SHELL || process2.role === PROCESS_ROLES.LISTENER)
|
|
12045
|
+
);
|
|
12046
|
+
const activeShell = processes.find((process2) => process2.role === PROCESS_ROLES.ACTIVE_SHELL);
|
|
12047
|
+
if (activeShell) {
|
|
12048
|
+
const output = getProcessOutput(activeShell.id);
|
|
12049
|
+
const commandDetails = getRecentCommandDetails(activeShell.id);
|
|
12050
|
+
if (commandDetails.some(isPostExploitationCommand)) {
|
|
12051
|
+
return {
|
|
12052
|
+
phase: "post_exploitation_active",
|
|
12053
|
+
activeShellId: activeShell.id,
|
|
12054
|
+
recommendation: `Active shell ${activeShell.id} is already in post-exploitation flow. Keep reusing it for controlled enumeration, privilege escalation checks, and pivot preparation.`
|
|
12055
|
+
};
|
|
12056
|
+
}
|
|
12057
|
+
if (hasEvent(activeShell.id, PROCESS_EVENTS.SHELL_STABILIZED) || output && isShellStabilized(output.stdout, commandDetails)) {
|
|
12058
|
+
return {
|
|
12059
|
+
phase: "active_shell_stabilized",
|
|
12060
|
+
activeShellId: activeShell.id,
|
|
12061
|
+
recommendation: `Active shell ${activeShell.id} appears stabilized. Reuse it for controlled enumeration and follow-up operations.`
|
|
12062
|
+
};
|
|
12063
|
+
}
|
|
12064
|
+
if (hasEvent(activeShell.id, PROCESS_EVENTS.SHELL_UPGRADE_ATTEMPTED) || commandDetails.some(isPtyUpgradeCommand)) {
|
|
12065
|
+
return {
|
|
12066
|
+
phase: "active_shell_stabilizing",
|
|
12067
|
+
activeShellId: activeShell.id,
|
|
12068
|
+
recommendation: `Active shell ${activeShell.id} is in PTY stabilization flow. Verify TERM/TTY quality, then continue controlled enumeration.`
|
|
12069
|
+
};
|
|
12070
|
+
}
|
|
12071
|
+
return {
|
|
12072
|
+
phase: "active_shell_ready",
|
|
12073
|
+
activeShellId: activeShell.id,
|
|
12074
|
+
recommendation: `Reuse active shell ${activeShell.id}, validate stability, and upgrade PTY before broad enumeration.`
|
|
12075
|
+
};
|
|
12076
|
+
}
|
|
12077
|
+
const listeners = processes.filter((process2) => process2.role === PROCESS_ROLES.LISTENER);
|
|
12078
|
+
for (const listener of listeners) {
|
|
12079
|
+
const output = getProcessOutput(listener.id);
|
|
12080
|
+
if (!output) continue;
|
|
12081
|
+
if (hasConnectionSignal(listener.id, output.stdout)) {
|
|
12082
|
+
return {
|
|
12083
|
+
phase: "listener_callback_detected",
|
|
12084
|
+
listenerId: listener.id,
|
|
12085
|
+
recommendation: `Listener ${listener.id} has a callback signal. Confirm status, promote immediately, then validate and stabilize the shell.`
|
|
12086
|
+
};
|
|
12087
|
+
}
|
|
12088
|
+
}
|
|
12089
|
+
if (listeners.length > 0) {
|
|
12090
|
+
return {
|
|
12091
|
+
phase: "listener_waiting",
|
|
12092
|
+
listenerId: listeners[0]?.id,
|
|
12093
|
+
recommendation: `Reuse listener ${listeners[0]?.id} and keep polling for a callback instead of opening a duplicate listener.`
|
|
12094
|
+
};
|
|
12095
|
+
}
|
|
12096
|
+
return {
|
|
12097
|
+
phase: "no_asset",
|
|
12098
|
+
recommendation: "No shell or listener asset is active. Create exactly one listener only if the callback path still requires it."
|
|
12099
|
+
};
|
|
12100
|
+
}
|
|
12101
|
+
function buildShellSupervisorLifecycleSection() {
|
|
12102
|
+
const snapshot = getShellSupervisorLifecycleSnapshot();
|
|
12103
|
+
const lines = [
|
|
12104
|
+
"## Shell Supervisor Lifecycle",
|
|
12105
|
+
`Current phase: ${snapshot.phase}`,
|
|
12106
|
+
`Recommendation: ${snapshot.recommendation}`
|
|
12107
|
+
];
|
|
12108
|
+
if (snapshot.activeShellId) {
|
|
12109
|
+
lines.push(`Active shell: ${snapshot.activeShellId}`);
|
|
12110
|
+
}
|
|
12111
|
+
if (snapshot.listenerId) {
|
|
12112
|
+
lines.push(`Tracked listener: ${snapshot.listenerId}`);
|
|
12113
|
+
}
|
|
12114
|
+
return lines.join("\n");
|
|
12115
|
+
}
|
|
12116
|
+
|
|
11669
12117
|
export {
|
|
11670
12118
|
ENV_KEYS,
|
|
11671
12119
|
DEFAULT_MODEL,
|
|
@@ -11680,6 +12128,8 @@ export {
|
|
|
11680
12128
|
HealthMonitor,
|
|
11681
12129
|
ZombieHunter,
|
|
11682
12130
|
getLLMClient,
|
|
12131
|
+
getGlobalRequestCount,
|
|
12132
|
+
getGlobalTokenUsage,
|
|
11683
12133
|
createContextExtractor,
|
|
11684
12134
|
parseTurnNumbers,
|
|
11685
12135
|
rotateTurnRecords,
|
|
@@ -11706,5 +12156,7 @@ export {
|
|
|
11706
12156
|
formatChallengeAnalysis,
|
|
11707
12157
|
setCurrentTurn,
|
|
11708
12158
|
CoreAgent,
|
|
12159
|
+
getShellSupervisorLifecycleSnapshot,
|
|
12160
|
+
buildShellSupervisorLifecycleSection,
|
|
11709
12161
|
CategorizedToolRegistry
|
|
11710
12162
|
};
|