pentesting 0.73.2 → 0.73.4
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/README.md +121 -0
- package/dist/agent-tool-MP274HWD.js +989 -0
- package/dist/{chunk-BGEXGHPB.js → chunk-3KWJPPYB.js} +917 -430
- package/dist/{chunk-KBJPZDIL.js → chunk-7E2VUIFU.js} +456 -211
- package/dist/chunk-I52SWXYV.js +1122 -0
- package/dist/main.js +2285 -474
- package/dist/{persistence-VFIOGTRC.js → persistence-BNVN3WW6.js} +2 -2
- package/dist/{process-registry-GSHEX2LT.js → process-registry-BI7BKPHN.js} +1 -1
- package/dist/prompts/main-agent.md +35 -1
- package/dist/prompts/strategist-system.md +34 -0
- package/package.json +3 -4
- package/dist/agent-tool-HYQGTZC4.js +0 -256
- package/dist/chunk-YFDJI3GO.js +0 -331
|
@@ -46,12 +46,16 @@ import {
|
|
|
46
46
|
debugLog,
|
|
47
47
|
ensureDirExists,
|
|
48
48
|
generateId,
|
|
49
|
+
generatePrefixedId,
|
|
49
50
|
getActiveSessionRuntime,
|
|
50
51
|
getErrorMessage,
|
|
51
52
|
getProcessOutput,
|
|
52
53
|
getTorBrowserArgs,
|
|
53
54
|
getUsedPorts,
|
|
54
55
|
listBackgroundProcesses,
|
|
56
|
+
llmNodeCooldownPolicy,
|
|
57
|
+
llmNodeOutputParsing,
|
|
58
|
+
llmNodeSystemPrompt,
|
|
55
59
|
promoteToShell,
|
|
56
60
|
readFileContent,
|
|
57
61
|
runCommand,
|
|
@@ -60,11 +64,12 @@ import {
|
|
|
60
64
|
startBackgroundProcess,
|
|
61
65
|
stopBackgroundProcess,
|
|
62
66
|
writeFileContent
|
|
63
|
-
} from "./chunk-
|
|
67
|
+
} from "./chunk-7E2VUIFU.js";
|
|
64
68
|
import {
|
|
65
69
|
DETECTION_PATTERNS,
|
|
66
70
|
HEALTH_CONFIG,
|
|
67
71
|
PROCESS_ACTIONS,
|
|
72
|
+
PROCESS_EVENTS,
|
|
68
73
|
PROCESS_ICONS,
|
|
69
74
|
PROCESS_LIMITS,
|
|
70
75
|
PROCESS_ROLES,
|
|
@@ -72,10 +77,8 @@ import {
|
|
|
72
77
|
SYSTEM_LIMITS,
|
|
73
78
|
__require,
|
|
74
79
|
getProcessEventLog,
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
llmNodeSystemPrompt
|
|
78
|
-
} from "./chunk-YFDJI3GO.js";
|
|
80
|
+
logEvent
|
|
81
|
+
} from "./chunk-I52SWXYV.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) {
|
|
@@ -2845,7 +2887,7 @@ var CoreAgent = class _CoreAgent {
|
|
|
2845
2887
|
);
|
|
2846
2888
|
return { output: "", toolsExecuted, isCompleted: false };
|
|
2847
2889
|
}
|
|
2848
|
-
// ─── AgentController Methods for Dynamic
|
|
2890
|
+
// ─── AgentController Methods for Dynamic Runtime Pipeline ─────────────────
|
|
2849
2891
|
async runLLMInference(ctx, systemPrompt) {
|
|
2850
2892
|
const iteration = ctx.memory.iteration || 0;
|
|
2851
2893
|
const progress = ctx.memory.progress;
|
|
@@ -3232,10 +3274,268 @@ 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 profile = params.profile;
|
|
3442
|
+
const result = await handleInteractAction(
|
|
3443
|
+
processId,
|
|
3444
|
+
getShellCheckCommand(profile),
|
|
3445
|
+
params.wait_ms
|
|
3446
|
+
);
|
|
3447
|
+
if (result.success && profile === "stability") {
|
|
3448
|
+
logEvent(processId, PROCESS_EVENTS.SHELL_STABILITY_CHECKED, "Shell stability probe executed by shell_check");
|
|
3449
|
+
}
|
|
3450
|
+
if (result.success && profile === "stability" && outputLooksStabilized(result.output)) {
|
|
3451
|
+
logEvent(processId, PROCESS_EVENTS.SHELL_STABILIZED, "Shell stability confirmed by shell_check");
|
|
3452
|
+
}
|
|
3453
|
+
if (result.success && profile === "stability" && !outputLooksStabilized(result.output)) {
|
|
3454
|
+
logEvent(
|
|
3455
|
+
processId,
|
|
3456
|
+
PROCESS_EVENTS.SHELL_STABILITY_INCOMPLETE,
|
|
3457
|
+
"Shell stability probe did not confirm a stable PTY"
|
|
3458
|
+
);
|
|
3459
|
+
}
|
|
3460
|
+
if (result.success && profile === "post") {
|
|
3461
|
+
logEvent(processId, PROCESS_EVENTS.POST_EXPLOITATION_ACTIVITY, "Post-exploitation probe executed by shell_check");
|
|
3462
|
+
}
|
|
3463
|
+
return result;
|
|
3464
|
+
}
|
|
3465
|
+
),
|
|
3466
|
+
createShellTool(
|
|
3467
|
+
TOOL_NAMES.SHELL_UPGRADE,
|
|
3468
|
+
"Send a bounded shell-upgrade attempt or upgrade probe through an active shell.",
|
|
3469
|
+
{
|
|
3470
|
+
process_id: { type: "string", description: "Shell process ID" },
|
|
3471
|
+
method: { type: "string", description: "python_pty | script | perl | ruby | socat_probe | stty_probe" },
|
|
3472
|
+
wait_ms: { type: "number", description: "Optional wait time for shell output" }
|
|
3473
|
+
},
|
|
3474
|
+
["process_id"],
|
|
3475
|
+
async (params) => executeShellUpgrade(
|
|
3476
|
+
params.process_id,
|
|
3477
|
+
params.method,
|
|
3478
|
+
params.wait_ms
|
|
3479
|
+
)
|
|
3480
|
+
)
|
|
3481
|
+
];
|
|
3482
|
+
|
|
3483
|
+
// src/engine/tools/system/offensive-bounded-tools.ts
|
|
3484
|
+
async function runBoundedForegroundCommand(command, timeout) {
|
|
3485
|
+
return runCommand(command, [], timeout ? { timeout } : {});
|
|
3486
|
+
}
|
|
3487
|
+
function createBoundedCommandTool(name, description) {
|
|
3488
|
+
return {
|
|
3489
|
+
name,
|
|
3490
|
+
description,
|
|
3491
|
+
parameters: {
|
|
3492
|
+
command: { type: "string", description: "Bounded foreground command for this phase-specific operation" },
|
|
3493
|
+
timeout: { type: "number", description: "Optional timeout in ms" }
|
|
3494
|
+
},
|
|
3495
|
+
required: ["command"],
|
|
3496
|
+
execute: async (params) => runBoundedForegroundCommand(
|
|
3497
|
+
params.command,
|
|
3498
|
+
params.timeout
|
|
3499
|
+
)
|
|
3500
|
+
};
|
|
3501
|
+
}
|
|
3502
|
+
var offensiveBoundedTools = [
|
|
3503
|
+
createBoundedCommandTool(
|
|
3504
|
+
TOOL_NAMES.EXPLOIT_CREDENTIAL_CHECK,
|
|
3505
|
+
"Run a bounded exploit credential/token validation probe. Use after a chain dumps credentials or tokens."
|
|
3506
|
+
),
|
|
3507
|
+
createBoundedCommandTool(
|
|
3508
|
+
TOOL_NAMES.EXPLOIT_ARTIFACT_CHECK,
|
|
3509
|
+
"Run a bounded exploit artifact validation probe. Use to verify the current artifact before replacing it."
|
|
3510
|
+
),
|
|
3511
|
+
createBoundedCommandTool(
|
|
3512
|
+
TOOL_NAMES.EXPLOIT_FOOTHOLD_CHECK,
|
|
3513
|
+
"Run a bounded foothold confirmation probe after an exploit chain appears to land access."
|
|
3514
|
+
),
|
|
3515
|
+
createBoundedCommandTool(
|
|
3516
|
+
TOOL_NAMES.EXPLOIT_VECTOR_CHECK,
|
|
3517
|
+
"Run a bounded exploit vector reachability or service confirmation probe before changing vectors."
|
|
3518
|
+
),
|
|
3519
|
+
createBoundedCommandTool(
|
|
3520
|
+
TOOL_NAMES.PWN_CRASH_REPRO,
|
|
3521
|
+
"Run a bounded pwn crash reproduction command from the preserved crash state."
|
|
3522
|
+
),
|
|
3523
|
+
createBoundedCommandTool(
|
|
3524
|
+
TOOL_NAMES.PWN_OFFSET_CHECK,
|
|
3525
|
+
"Run a bounded pwn offset verification command when control primitives are believed to be known."
|
|
3526
|
+
),
|
|
3527
|
+
createBoundedCommandTool(
|
|
3528
|
+
TOOL_NAMES.PWN_PAYLOAD_SMOKE,
|
|
3529
|
+
"Run a bounded pwn payload smoke-test command against the latest exploit revision."
|
|
3530
|
+
)
|
|
3531
|
+
];
|
|
3532
|
+
|
|
3235
3533
|
// src/engine/tools/system/index.ts
|
|
3236
3534
|
var systemTools = [
|
|
3237
3535
|
runCmdTool,
|
|
3238
3536
|
bgProcessTool,
|
|
3537
|
+
...shellTools,
|
|
3538
|
+
...offensiveBoundedTools,
|
|
3239
3539
|
readFileTool,
|
|
3240
3540
|
writeFileTool
|
|
3241
3541
|
];
|
|
@@ -4253,9 +4553,7 @@ function isTransientHttpError(status) {
|
|
|
4253
4553
|
async function parseNmap(xmlPath) {
|
|
4254
4554
|
try {
|
|
4255
4555
|
const fileResult = await readFileContent(xmlPath);
|
|
4256
|
-
if (!fileResult.success)
|
|
4257
|
-
return fileResult;
|
|
4258
|
-
}
|
|
4556
|
+
if (!fileResult.success) return fileResult;
|
|
4259
4557
|
const xmlContent = fileResult.output;
|
|
4260
4558
|
const results = {
|
|
4261
4559
|
targets: [],
|
|
@@ -4264,31 +4562,7 @@ async function parseNmap(xmlPath) {
|
|
|
4264
4562
|
const hostRegex = /<host[^>]*>[\s\S]*?<\/host>/g;
|
|
4265
4563
|
const hosts = xmlContent.match(hostRegex) || [];
|
|
4266
4564
|
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
|
-
}
|
|
4565
|
+
parseHost(hostBlock, results);
|
|
4292
4566
|
}
|
|
4293
4567
|
return {
|
|
4294
4568
|
success: true,
|
|
@@ -4302,6 +4576,37 @@ async function parseNmap(xmlPath) {
|
|
|
4302
4576
|
};
|
|
4303
4577
|
}
|
|
4304
4578
|
}
|
|
4579
|
+
function parseHost(hostBlock, results) {
|
|
4580
|
+
const ipMatch = hostBlock.match(/<address[^>]*addr="([^"]+)"/);
|
|
4581
|
+
const ip = ipMatch ? ipMatch[1] : "";
|
|
4582
|
+
const hostnameMatch = hostBlock.match(/<hostname[^>]*name="([^"]+)"/);
|
|
4583
|
+
const hostname = hostnameMatch ? hostnameMatch[1] : void 0;
|
|
4584
|
+
const ports = parsePorts(hostBlock, results);
|
|
4585
|
+
if (ip) {
|
|
4586
|
+
results.targets.push({ ip, hostname, ports });
|
|
4587
|
+
results.summary.totalTargets++;
|
|
4588
|
+
}
|
|
4589
|
+
}
|
|
4590
|
+
function parsePorts(hostBlock, results) {
|
|
4591
|
+
const ports = [];
|
|
4592
|
+
const portRegex = /<port[^>]*protocol="([^"]*)"[^>]*portid="(\d+)">[\s\S]*?<\/port>/g;
|
|
4593
|
+
let portMatch;
|
|
4594
|
+
while ((portMatch = portRegex.exec(hostBlock)) !== null) {
|
|
4595
|
+
const protocol = portMatch[1];
|
|
4596
|
+
const port = parseInt(portMatch[2]);
|
|
4597
|
+
const stateMatch = portMatch[0].match(/<state[^>]*state="([^"]+)"/);
|
|
4598
|
+
const state = stateMatch ? stateMatch[1] : "";
|
|
4599
|
+
const serviceMatch = portMatch[0].match(/<service[^>]*name="([^"]*)"(?:[^>]*version="([^"]*)")?/);
|
|
4600
|
+
const service = serviceMatch ? serviceMatch[1] : void 0;
|
|
4601
|
+
const version = serviceMatch && serviceMatch[2] ? serviceMatch[2] : void 0;
|
|
4602
|
+
if (state === PORT_STATE.OPEN) {
|
|
4603
|
+
ports.push({ port, protocol, state, service, version });
|
|
4604
|
+
results.summary.openPorts++;
|
|
4605
|
+
if (service) results.summary.servicesFound++;
|
|
4606
|
+
}
|
|
4607
|
+
}
|
|
4608
|
+
return ports;
|
|
4609
|
+
}
|
|
4305
4610
|
|
|
4306
4611
|
// src/engine/tools/intel-utils/cve-search.ts
|
|
4307
4612
|
import { execFileSync } from "child_process";
|
|
@@ -4861,54 +5166,57 @@ function getStorageAndAuthDumpSnippet(capturedVarName = "_capturedHeaders", safe
|
|
|
4861
5166
|
}
|
|
4862
5167
|
|
|
4863
5168
|
// 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});
|
|
5169
|
+
function buildCommonHeader(safePlaywrightPath) {
|
|
5170
|
+
return `const { chromium } = require(${safePlaywrightPath});
|
|
4874
5171
|
const fs = require('fs');
|
|
4875
5172
|
|
|
4876
5173
|
(async () => {
|
|
4877
5174
|
const browser = await chromium.launch({
|
|
4878
5175
|
headless: true,
|
|
4879
5176
|
args: ['${PLAYWRIGHT_ARG.NO_SANDBOX}', '${PLAYWRIGHT_ARG.DISABLE_SETUID_SANDBOX}', ${JSON.stringify(getTorBrowserArgs()).slice(1, -1)}].filter(Boolean)
|
|
4880
|
-
})
|
|
4881
|
-
|
|
4882
|
-
|
|
5177
|
+
});`;
|
|
5178
|
+
}
|
|
5179
|
+
function buildSessionContext(options, safeSessionPath) {
|
|
5180
|
+
const safeUserAgent = options.userAgent ? safeJsString(options.userAgent) : "undefined";
|
|
5181
|
+
const safeExtraHeaders = options.extraHeaders ? JSON.stringify(options.extraHeaders) : "{}";
|
|
5182
|
+
const viewportStr = options.viewport ? `viewport: { width: ${options.viewport.width}, height: ${options.viewport.height} },` : "";
|
|
5183
|
+
return `
|
|
4883
5184
|
const contextOptions = {
|
|
4884
|
-
userAgent: ${safeUserAgent}
|
|
4885
|
-
|
|
4886
|
-
extraHTTPHeaders: ${safeExtraHeaders}
|
|
5185
|
+
${options.userAgent ? `userAgent: ${safeUserAgent},` : ""}
|
|
5186
|
+
${viewportStr}
|
|
5187
|
+
${options.extraHeaders ? `extraHTTPHeaders: ${safeExtraHeaders},` : ""}
|
|
4887
5188
|
};
|
|
4888
5189
|
${safeSessionPath && options.useSession ? `
|
|
4889
5190
|
if (fs.existsSync(${safeSessionPath})) {
|
|
4890
5191
|
contextOptions.storageState = ${safeSessionPath};
|
|
4891
5192
|
}` : ""}
|
|
4892
|
-
|
|
4893
5193
|
const context = await browser.newContext(contextOptions);
|
|
5194
|
+
const page = await context.newPage();`;
|
|
5195
|
+
}
|
|
5196
|
+
function buildSessionSave(saveSession, safeSessionPath, targetObj, authHeadersProp) {
|
|
5197
|
+
if (!safeSessionPath || !saveSession) return "";
|
|
5198
|
+
return `
|
|
5199
|
+
await context.storageState({ path: ${safeSessionPath} });
|
|
5200
|
+
${targetObj}.sessionSaved = ${safeSessionPath};
|
|
4894
5201
|
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
5202
|
+
${getStorageAndAuthDumpSnippet(authHeadersProp, safeSessionPath)}
|
|
5203
|
+
${targetObj}.authHeadersSaved = _authHeadersPath;
|
|
5204
|
+
${targetObj}.authHeadersNote = 'auth-headers.json contains intercepted network headers + full storage dump. LLM should inspect _storage field for unlabeled tokens.';
|
|
5205
|
+
`;
|
|
5206
|
+
}
|
|
5207
|
+
function buildCommonFooter() {
|
|
5208
|
+
return `
|
|
5209
|
+
} catch (error) {
|
|
5210
|
+
console.log(JSON.stringify({ error: error.message }));
|
|
5211
|
+
} finally {
|
|
5212
|
+
await browser.close();
|
|
5213
|
+
}
|
|
5214
|
+
})();`;
|
|
5215
|
+
}
|
|
5216
|
+
function buildBrowseExtraction(options) {
|
|
5217
|
+
let extraction = "";
|
|
5218
|
+
if (options.extractContent) {
|
|
5219
|
+
extraction += `
|
|
4912
5220
|
result.text = await page.evaluate(() => {
|
|
4913
5221
|
const body = document.body;
|
|
4914
5222
|
const walker = document.createTreeWalker(body, NodeFilter.SHOW_TEXT, null, false);
|
|
@@ -4919,19 +5227,19 @@ const fs = require('fs');
|
|
|
4919
5227
|
if (content) text += content + '\\n';
|
|
4920
5228
|
}
|
|
4921
5229
|
return text.slice(0, ${BROWSER_LIMITS.MAX_TEXT_EXTRACTION});
|
|
4922
|
-
})
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
5230
|
+
});`;
|
|
5231
|
+
}
|
|
5232
|
+
if (options.extractLinks) {
|
|
5233
|
+
extraction += `
|
|
4926
5234
|
result.links = await page.evaluate(() => {
|
|
4927
5235
|
return Array.from(document.querySelectorAll('a[href]')).map(a => ({
|
|
4928
5236
|
href: a.href,
|
|
4929
5237
|
text: a.textContent.trim().slice(0, ${DISPLAY_LIMITS.LINK_TEXT_PREVIEW})
|
|
4930
5238
|
})).slice(0, ${BROWSER_LIMITS.MAX_LINKS_EXTRACTION});
|
|
4931
|
-
})
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
5239
|
+
});`;
|
|
5240
|
+
}
|
|
5241
|
+
if (options.extractForms) {
|
|
5242
|
+
extraction += `
|
|
4935
5243
|
result.forms = await page.evaluate(() => {
|
|
4936
5244
|
return Array.from(document.querySelectorAll('form')).map(form => ({
|
|
4937
5245
|
action: form.action,
|
|
@@ -4944,69 +5252,42 @@ const fs = require('fs');
|
|
|
4944
5252
|
isRequired: input.required
|
|
4945
5253
|
}))
|
|
4946
5254
|
}));
|
|
4947
|
-
})
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
5255
|
+
});`;
|
|
5256
|
+
}
|
|
5257
|
+
return extraction;
|
|
5258
|
+
}
|
|
5259
|
+
function buildBrowseScript(url, options, screenshotPath, sessionPath) {
|
|
5260
|
+
const safeUrl = safeJsString(url);
|
|
5261
|
+
const safeScreenshotPath = screenshotPath ? safeJsString(screenshotPath) : "null";
|
|
5262
|
+
const safePlaywrightPath = safeJsString(getPlaywrightPath());
|
|
5263
|
+
const safeSessionPath = sessionPath ? safeJsString(sessionPath) : null;
|
|
5264
|
+
options.userAgent = options.userAgent || BROWSER_CONFIG.DEFAULT_USER_AGENT;
|
|
5265
|
+
return `
|
|
5266
|
+
${buildCommonHeader(safePlaywrightPath)}
|
|
5267
|
+
${buildSessionContext(options, safeSessionPath)}
|
|
5268
|
+
${getNetworkAuthInterceptSnippet("_capturedHeaders")}
|
|
5269
|
+
try {
|
|
5270
|
+
await page.goto(${safeUrl}, { waitUntil: 'networkidle', timeout: ${options.timeout} });
|
|
5271
|
+
await page.waitForTimeout(${options.waitAfterLoad});
|
|
4951
5272
|
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
await context.storageState({ path: ${safeSessionPath} });
|
|
4955
|
-
result.sessionSaved = ${safeSessionPath};
|
|
5273
|
+
const result = { _interceptedAuth: _capturedHeaders };
|
|
5274
|
+
result.title = await page.title();
|
|
4956
5275
|
|
|
4957
|
-
${
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
` : ""}
|
|
5276
|
+
${buildBrowseExtraction(options)}
|
|
5277
|
+
${screenshotPath ? `await page.screenshot({ path: ${safeScreenshotPath}, fullPage: false });` : ""}
|
|
5278
|
+
${buildSessionSave(!!options.saveSession, safeSessionPath, "result", "result._interceptedAuth")}
|
|
4961
5279
|
|
|
4962
5280
|
console.log(JSON.stringify(result));
|
|
4963
|
-
|
|
4964
|
-
console.log(JSON.stringify({ error: error.message }));
|
|
4965
|
-
} finally {
|
|
4966
|
-
await browser.close();
|
|
4967
|
-
}
|
|
4968
|
-
})();
|
|
5281
|
+
${buildCommonFooter()}
|
|
4969
5282
|
`;
|
|
4970
5283
|
}
|
|
4971
|
-
function
|
|
4972
|
-
const { safeUrl, safeFormData, safePlaywrightPath, timeout, useSession, saveSession, safeSessionPath } = params;
|
|
5284
|
+
function buildFormFillingLogic(safeFormData) {
|
|
4973
5285
|
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
5286
|
const formData = ${safeFormData};
|
|
5003
5287
|
for (const [fieldKey, fieldValue] of Object.entries(formData)) {
|
|
5004
5288
|
const lk = fieldKey.toLowerCase();
|
|
5005
|
-
|
|
5006
|
-
// Strategy 1: exact name/id/aria-label attribute
|
|
5007
5289
|
let el = await page.$(\`[name="\${fieldKey}"], #\${fieldKey}\`);
|
|
5008
5290
|
|
|
5009
|
-
// Strategy 2: semantic aria-label or placeholder match
|
|
5010
5291
|
if (!el) {
|
|
5011
5292
|
el = await page.evaluateHandle((key) => {
|
|
5012
5293
|
const inputs = [...document.querySelectorAll('input, textarea, select')];
|
|
@@ -5019,22 +5300,16 @@ const fs = require('fs');
|
|
|
5019
5300
|
}, lk).then(h => h.asElement());
|
|
5020
5301
|
}
|
|
5021
5302
|
|
|
5022
|
-
// Strategy 3: type-based inference (username/email/user \u2192 type=text/email, password \u2192 type=password)
|
|
5023
5303
|
if (!el) {
|
|
5024
5304
|
let inferredType = null;
|
|
5025
5305
|
if (['username', 'user', 'email', 'login'].includes(lk)) inferredType = ['text', 'email'];
|
|
5026
5306
|
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
|
-
}
|
|
5307
|
+
if (inferredType) el = await page.$(\`input[type="\${inferredType[0]}"\${inferredType[1] ? \`, input[type="\${inferredType[1]}"\` : ''}]\`);
|
|
5030
5308
|
}
|
|
5031
5309
|
|
|
5032
|
-
if (el)
|
|
5033
|
-
await el.fill(String(fieldValue));
|
|
5034
|
-
}
|
|
5310
|
+
if (el) await el.fill(String(fieldValue));
|
|
5035
5311
|
}
|
|
5036
5312
|
|
|
5037
|
-
// \xA7LLM-AUTONOMY: Submit via semantic search \u2014 finds any element that looks like a submit action.
|
|
5038
5313
|
const submitEl = await page.$([
|
|
5039
5314
|
'button[type="submit"]',
|
|
5040
5315
|
'input[type="submit"]',
|
|
@@ -5047,27 +5322,29 @@ const fs = require('fs');
|
|
|
5047
5322
|
await submitEl.click();
|
|
5048
5323
|
await page.waitForLoadState('networkidle').catch(() => {});
|
|
5049
5324
|
}
|
|
5325
|
+
`;
|
|
5326
|
+
}
|
|
5327
|
+
function buildFormScript(params) {
|
|
5328
|
+
const { safeUrl, safeFormData, safePlaywrightPath, timeout, useSession, saveSession, safeSessionPath } = params;
|
|
5329
|
+
return `
|
|
5330
|
+
${buildCommonHeader(safePlaywrightPath)}
|
|
5331
|
+
${buildSessionContext({ useSession }, safeSessionPath)}
|
|
5332
|
+
${getNetworkAuthInterceptSnippet("_capturedHeaders")}
|
|
5333
|
+
try {
|
|
5334
|
+
await page.goto(${safeUrl}, { waitUntil: 'networkidle', timeout: ${timeout} });
|
|
5050
5335
|
|
|
5051
|
-
|
|
5052
|
-
${safeSessionPath && saveSession ? `
|
|
5053
|
-
await context.storageState({ path: ${safeSessionPath} });
|
|
5054
|
-
|
|
5055
|
-
${getStorageAndAuthDumpSnippet("_capturedHeaders", safeSessionPath)}
|
|
5056
|
-
` : ""}
|
|
5336
|
+
${buildFormFillingLogic(safeFormData)}
|
|
5057
5337
|
|
|
5058
5338
|
const result = {
|
|
5059
5339
|
url: page.url(),
|
|
5060
5340
|
title: await page.title(),
|
|
5061
|
-
|
|
5341
|
+
_interceptedAuth: _capturedHeaders
|
|
5062
5342
|
};
|
|
5063
5343
|
|
|
5344
|
+
${buildSessionSave(saveSession, safeSessionPath, "result", "result._interceptedAuth")}
|
|
5345
|
+
|
|
5064
5346
|
console.log(JSON.stringify(result));
|
|
5065
|
-
|
|
5066
|
-
console.log(JSON.stringify({ error: error.message }));
|
|
5067
|
-
} finally {
|
|
5068
|
-
await browser.close();
|
|
5069
|
-
}
|
|
5070
|
-
})();
|
|
5347
|
+
${buildCommonFooter()}
|
|
5071
5348
|
`;
|
|
5072
5349
|
}
|
|
5073
5350
|
|
|
@@ -7950,6 +8227,7 @@ var SessionState = class {
|
|
|
7950
8227
|
};
|
|
7951
8228
|
|
|
7952
8229
|
// src/engine/state/core/shared-state.ts
|
|
8230
|
+
var MAX_DELEGATED_TASKS = 50;
|
|
7953
8231
|
var SharedState = class {
|
|
7954
8232
|
attackGraph = new AttackGraph();
|
|
7955
8233
|
targetState;
|
|
@@ -7985,6 +8263,7 @@ var SharedState = class {
|
|
|
7985
8263
|
* Current objective for flexible task management.
|
|
7986
8264
|
*/
|
|
7987
8265
|
currentObjective = null;
|
|
8266
|
+
delegatedTasks = [];
|
|
7988
8267
|
constructor() {
|
|
7989
8268
|
this.targetState = new TargetState(this.attackGraph);
|
|
7990
8269
|
this.findingState = new FindingState();
|
|
@@ -8010,6 +8289,7 @@ var SharedState = class {
|
|
|
8010
8289
|
this.dynamicTechniques.clear();
|
|
8011
8290
|
this.artifacts = [];
|
|
8012
8291
|
this.currentObjective = null;
|
|
8292
|
+
this.delegatedTasks = [];
|
|
8013
8293
|
this.lastReflection = "";
|
|
8014
8294
|
this.lastTriageMemo = "";
|
|
8015
8295
|
}
|
|
@@ -8127,6 +8407,38 @@ var SharedState = class {
|
|
|
8127
8407
|
getTimeStatus() {
|
|
8128
8408
|
return this.sessionState.getTimeStatus();
|
|
8129
8409
|
}
|
|
8410
|
+
recordDelegatedTask(task) {
|
|
8411
|
+
const now = Date.now();
|
|
8412
|
+
const parentTask = task.resumeTaskId ? this.delegatedTasks.find((existing) => existing.id === task.resumeTaskId) : void 0;
|
|
8413
|
+
const record = {
|
|
8414
|
+
...task,
|
|
8415
|
+
parentTaskId: task.resumeTaskId || void 0,
|
|
8416
|
+
rootTaskId: parentTask ? parentTask.rootTaskId || parentTask.id : task.resumeTaskId || void 0,
|
|
8417
|
+
id: generatePrefixedId("delegated_task"),
|
|
8418
|
+
createdAt: now,
|
|
8419
|
+
updatedAt: now
|
|
8420
|
+
};
|
|
8421
|
+
this.delegatedTasks.push(record);
|
|
8422
|
+
if (this.delegatedTasks.length > MAX_DELEGATED_TASKS) {
|
|
8423
|
+
this.delegatedTasks = this.delegatedTasks.slice(this.delegatedTasks.length - MAX_DELEGATED_TASKS);
|
|
8424
|
+
}
|
|
8425
|
+
return record;
|
|
8426
|
+
}
|
|
8427
|
+
restoreDelegatedTask(task) {
|
|
8428
|
+
this.delegatedTasks.push(task);
|
|
8429
|
+
if (this.delegatedTasks.length > MAX_DELEGATED_TASKS) {
|
|
8430
|
+
this.delegatedTasks = this.delegatedTasks.slice(this.delegatedTasks.length - MAX_DELEGATED_TASKS);
|
|
8431
|
+
}
|
|
8432
|
+
}
|
|
8433
|
+
getDelegatedTasks() {
|
|
8434
|
+
return [...this.delegatedTasks];
|
|
8435
|
+
}
|
|
8436
|
+
getActiveDelegatedTasks() {
|
|
8437
|
+
return this.delegatedTasks.filter((task) => task.status === "waiting" || task.status === "running");
|
|
8438
|
+
}
|
|
8439
|
+
getDelegatedTask(taskId) {
|
|
8440
|
+
return this.delegatedTasks.find((task) => task.id === taskId);
|
|
8441
|
+
}
|
|
8130
8442
|
};
|
|
8131
8443
|
|
|
8132
8444
|
// src/shared/utils/policy/document.ts
|
|
@@ -8168,6 +8480,37 @@ function writePolicyDocument(content) {
|
|
|
8168
8480
|
);
|
|
8169
8481
|
}
|
|
8170
8482
|
|
|
8483
|
+
// src/engine/auxiliary-llm/strategist-prompt.ts
|
|
8484
|
+
function buildStrategistPrompt(input) {
|
|
8485
|
+
const state = input.state;
|
|
8486
|
+
const sections = ["## Engagement State", StateSerializer.toPrompt(state)];
|
|
8487
|
+
const failures = state.workingMemory.toPrompt();
|
|
8488
|
+
if (failures) sections.push("", "## Failed Attempts (DO NOT REPEAT THESE)", failures);
|
|
8489
|
+
try {
|
|
8490
|
+
const journalSummary = readJournalSummary();
|
|
8491
|
+
if (journalSummary) sections.push("", "## Session Journal (past turns summary)", journalSummary);
|
|
8492
|
+
} catch {
|
|
8493
|
+
}
|
|
8494
|
+
const policyDocument = readPolicyDocument();
|
|
8495
|
+
if (policyDocument) sections.push("", "## Policy Memory", policyDocument);
|
|
8496
|
+
const graph = state.attackGraph.toPrompt();
|
|
8497
|
+
if (graph) sections.push("", "## Attack Graph", graph);
|
|
8498
|
+
const timeline = state.episodicMemory.toPrompt();
|
|
8499
|
+
if (timeline) sections.push("", "## Recent Actions", timeline);
|
|
8500
|
+
const techniques = state.dynamicTechniques.toPrompt();
|
|
8501
|
+
if (techniques) sections.push("", "## Learned Techniques", techniques);
|
|
8502
|
+
sections.push("", "## Time", state.getTimeStatus());
|
|
8503
|
+
const analysis = state.getChallengeAnalysis?.();
|
|
8504
|
+
if (analysis && analysis.primaryType !== "unknown") {
|
|
8505
|
+
sections.push(
|
|
8506
|
+
"",
|
|
8507
|
+
"## Challenge Type",
|
|
8508
|
+
`${analysis.primaryType.toUpperCase()} (confidence: ${(analysis.confidence * 100).toFixed(0)}%)`
|
|
8509
|
+
);
|
|
8510
|
+
}
|
|
8511
|
+
return sections.join("\n");
|
|
8512
|
+
}
|
|
8513
|
+
|
|
8171
8514
|
// src/engine/auxiliary-llm/strategist.ts
|
|
8172
8515
|
var Strategist = class extends AuxiliaryLLMBase {
|
|
8173
8516
|
lastDirective = null;
|
|
@@ -8184,33 +8527,7 @@ var Strategist = class extends AuxiliaryLLMBase {
|
|
|
8184
8527
|
}
|
|
8185
8528
|
// ─── Abstract impl ───────────────────────────────────────────────────────
|
|
8186
8529
|
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");
|
|
8530
|
+
return buildStrategistPrompt(input);
|
|
8214
8531
|
}
|
|
8215
8532
|
parseOutput(_response) {
|
|
8216
8533
|
throw new Error("Unused \u2014 execute() is overridden in Strategist");
|
|
@@ -8631,7 +8948,7 @@ function isPortArray(value) {
|
|
|
8631
8948
|
(item) => typeof item === "object" && item !== null && typeof item.port === "number"
|
|
8632
8949
|
);
|
|
8633
8950
|
}
|
|
8634
|
-
function
|
|
8951
|
+
function parsePorts2(value) {
|
|
8635
8952
|
return isPortArray(value) ? value : [];
|
|
8636
8953
|
}
|
|
8637
8954
|
function isValidSeverity(value) {
|
|
@@ -8663,7 +8980,7 @@ var SPRAY_LOOT_TYPES = /* @__PURE__ */ new Set([
|
|
|
8663
8980
|
// src/domains/engagement/handlers/target.ts
|
|
8664
8981
|
async function executeAddTarget(p, state) {
|
|
8665
8982
|
const ip = p.ip;
|
|
8666
|
-
const ports =
|
|
8983
|
+
const ports = parsePorts2(p.ports);
|
|
8667
8984
|
const os = p.os;
|
|
8668
8985
|
const hostname = p.hostname;
|
|
8669
8986
|
const existing = state.getTarget(ip);
|
|
@@ -9172,99 +9489,70 @@ function getContextRecommendations(context, variantCount) {
|
|
|
9172
9489
|
}
|
|
9173
9490
|
|
|
9174
9491
|
// src/shared/utils/payload-mutator/core.ts
|
|
9492
|
+
var TRANSFORM_STRATEGIES = {
|
|
9493
|
+
[TRANSFORM_TYPE.URL]: (p, v) => {
|
|
9494
|
+
v.push({ payload: urlEncode(p), transform: "url", description: "URL encoded (special chars only)" });
|
|
9495
|
+
v.push({ payload: urlEncodeAll(p), transform: "url_full", description: "URL encoded (all chars)" });
|
|
9496
|
+
},
|
|
9497
|
+
[TRANSFORM_TYPE.DOUBLE_URL]: (p, v) => v.push({ payload: doubleUrlEncode(p), transform: "double_url", description: "Double URL encoded" }),
|
|
9498
|
+
[TRANSFORM_TYPE.TRIPLE_URL]: (p, v) => v.push({ payload: tripleUrlEncode(p), transform: "triple_url", description: "Triple URL encoded" }),
|
|
9499
|
+
[TRANSFORM_TYPE.UNICODE]: (p, v) => v.push({ payload: unicodeEncode(p), transform: "unicode", description: "Unicode escape sequences" }),
|
|
9500
|
+
[TRANSFORM_TYPE.HTML_ENTITY_DEC]: (p, v) => v.push({ payload: htmlEntityDec(p), transform: "html_entity_dec", description: "HTML decimal entities" }),
|
|
9501
|
+
[TRANSFORM_TYPE.HTML_ENTITY_HEX]: (p, v) => v.push({ payload: htmlEntityHex(p), transform: "html_entity_hex", description: "HTML hex entities" }),
|
|
9502
|
+
[TRANSFORM_TYPE.HEX]: (p, v) => v.push({ payload: hexEncode(p), transform: "hex", description: "Hex encoded" }),
|
|
9503
|
+
[TRANSFORM_TYPE.OCTAL]: (p, v) => v.push({ payload: octalEncode(p), transform: "octal", description: "Octal encoded" }),
|
|
9504
|
+
[TRANSFORM_TYPE.BASE64]: (p, v) => v.push({ payload: base64Encode(p), transform: "base64", description: "Base64 encoded" }),
|
|
9505
|
+
[TRANSFORM_TYPE.CASE_SWAP]: (p, v) => {
|
|
9506
|
+
for (let i = 0; i < 3; i++) v.push({ payload: caseSwap(p), transform: "case_swap", description: `Case variation ${i + 1}` });
|
|
9507
|
+
},
|
|
9508
|
+
[TRANSFORM_TYPE.COMMENT_INSERT]: (p, v) => v.push({ payload: commentInsert(p), transform: "comment_insert", description: "SQL comments in keywords" }),
|
|
9509
|
+
[TRANSFORM_TYPE.WHITESPACE_ALT]: (p, v) => {
|
|
9510
|
+
v.push({ payload: whitespaceAlt(p), transform: "whitespace_alt", description: "Alternative whitespace chars" });
|
|
9511
|
+
v.push({ payload: p.replace(/ /g, "/**/"), transform: "whitespace_comment", description: "Comment as whitespace" });
|
|
9512
|
+
v.push({ payload: p.replace(/ /g, "+"), transform: "whitespace_plus", description: "Plus as whitespace" });
|
|
9513
|
+
},
|
|
9514
|
+
[TRANSFORM_TYPE.CHAR_FUNCTION]: (p, v) => {
|
|
9515
|
+
v.push({ payload: charFunction(p, "mysql"), transform: "char_mysql", description: "MySQL CHAR() encoding" });
|
|
9516
|
+
v.push({ payload: charFunction(p, "mssql"), transform: "char_mssql", description: "MSSQL CHAR() encoding" });
|
|
9517
|
+
v.push({ payload: charFunction(p, "pg"), transform: "char_pg", description: "PostgreSQL CHR() encoding" });
|
|
9518
|
+
},
|
|
9519
|
+
[TRANSFORM_TYPE.CONCAT_SPLIT]: (p, v) => v.push({ payload: concatSplit(p), transform: "concat_split", description: "String split via CONCAT" }),
|
|
9520
|
+
[TRANSFORM_TYPE.NULL_BYTE]: (p, v) => {
|
|
9521
|
+
v.push({ payload: nullByteAppend(p), transform: "null_byte", description: "Null byte appended" });
|
|
9522
|
+
v.push({ payload: p + "%00.jpg", transform: "null_byte_ext", description: "Null byte + fake extension" });
|
|
9523
|
+
},
|
|
9524
|
+
[TRANSFORM_TYPE.REVERSE]: (p, v) => v.push({ payload: reversePayload(p), transform: "reverse", description: "Reversed (use with rev | sh)" }),
|
|
9525
|
+
[TRANSFORM_TYPE.UTF8_OVERLONG]: (p, v) => v.push({ payload: utf8Overlong(p), transform: "utf8_overlong", description: "UTF-8 overlong sequences" }),
|
|
9526
|
+
[TRANSFORM_TYPE.MIXED_ENCODING]: (p, v) => v.push({ payload: mixedEncoding(p), transform: "mixed_encoding", description: "Mixed encoding (partial)" }),
|
|
9527
|
+
[TRANSFORM_TYPE.TAG_ALTERNATIVE]: (p, v) => v.push(...generateXssAlternatives(p)),
|
|
9528
|
+
[TRANSFORM_TYPE.EVENT_HANDLER]: (p, v) => v.push(...generateXssAlternatives(p)),
|
|
9529
|
+
[TRANSFORM_TYPE.JS_ALTERNATIVE]: (p, v) => v.push(...generateXssAlternatives(p)),
|
|
9530
|
+
[TRANSFORM_TYPE.KEYWORD_BYPASS]: (p, v) => {
|
|
9531
|
+
v.push({ payload: keywordBypass(p), transform: "keyword_bypass", description: "Quote-inserted keyword bypass" });
|
|
9532
|
+
v.push({ payload: spaceBypass(p), transform: "space_bypass", description: "$IFS space bypass" });
|
|
9533
|
+
},
|
|
9534
|
+
[TRANSFORM_TYPE.SPACE_BYPASS]: (p, v) => {
|
|
9535
|
+
v.push({ payload: p.replace(/ /g, "${IFS}"), transform: "ifs", description: "$IFS space bypass" });
|
|
9536
|
+
v.push({ payload: p.replace(/ /g, "%09"), transform: "tab", description: "Tab space bypass" });
|
|
9537
|
+
v.push({ payload: p.replace(/ /g, "<"), transform: "redirect", description: "Redirect as separator" });
|
|
9538
|
+
const parts = p.split(" ");
|
|
9539
|
+
if (parts.length >= 2) v.push({ payload: `{${parts.join(",")}}`, transform: "brace", description: "Brace expansion" });
|
|
9540
|
+
}
|
|
9541
|
+
};
|
|
9175
9542
|
function mutatePayload(request) {
|
|
9176
9543
|
const { payload, transforms, context = "generic", maxVariants = 20 } = request;
|
|
9177
9544
|
const variants = [];
|
|
9178
|
-
const recommendations = [];
|
|
9179
9545
|
const activeTransforms = transforms || getDefaultTransforms(context);
|
|
9180
9546
|
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;
|
|
9547
|
+
const handler = TRANSFORM_STRATEGIES[transform];
|
|
9548
|
+
if (handler) {
|
|
9549
|
+
handler(payload, variants);
|
|
9262
9550
|
}
|
|
9263
9551
|
}
|
|
9264
9552
|
if (context.startsWith("sql")) {
|
|
9265
9553
|
variants.push(...generateSqlAlternatives(payload));
|
|
9266
9554
|
}
|
|
9267
|
-
recommendations
|
|
9555
|
+
const recommendations = getContextRecommendations(context, variants.length);
|
|
9268
9556
|
return {
|
|
9269
9557
|
variants: variants.slice(0, maxVariants),
|
|
9270
9558
|
recommendations
|
|
@@ -9324,8 +9612,8 @@ async function executeGetWordlists(p) {
|
|
|
9324
9612
|
};
|
|
9325
9613
|
const matchesSearch = (filePath, fileName) => {
|
|
9326
9614
|
if (!search) return true;
|
|
9327
|
-
const
|
|
9328
|
-
return fileName.toLowerCase().includes(
|
|
9615
|
+
const q2 = search.toLowerCase();
|
|
9616
|
+
return fileName.toLowerCase().includes(q2) || filePath.toLowerCase().includes(q2);
|
|
9329
9617
|
};
|
|
9330
9618
|
const results = ["# Available Wordlists on System\n"];
|
|
9331
9619
|
let totalCount = 0;
|
|
@@ -10653,7 +10941,7 @@ async function executeGoogleDork(args) {
|
|
|
10653
10941
|
const output = [
|
|
10654
10942
|
`=== Google Dork Queries for ${d} ===`,
|
|
10655
10943
|
"",
|
|
10656
|
-
...selected.map((
|
|
10944
|
+
...selected.map((q2, i) => `${i + 1}. https://www.google.com/search?q=${encodeURIComponent(q2)}`),
|
|
10657
10945
|
"",
|
|
10658
10946
|
"Paste these URLs into a browser to search manually.",
|
|
10659
10947
|
"Tip: Also try: https://pentest-tools.com/information-gathering/google-hacking"
|
|
@@ -10908,6 +11196,51 @@ function logSuccessfulAction(state, toolCall, approval, result) {
|
|
|
10908
11196
|
});
|
|
10909
11197
|
}
|
|
10910
11198
|
|
|
11199
|
+
// src/engine/parsers/security.ts
|
|
11200
|
+
function extractCredentials(output) {
|
|
11201
|
+
const creds = [];
|
|
11202
|
+
const patterns = [
|
|
11203
|
+
// user:password from hydra, medusa, etc.
|
|
11204
|
+
{ regex: /login:\s*(\S+)\s+password:\s*(\S+)/gi, type: "password" },
|
|
11205
|
+
// user:hash from hashdump, secretsdump, etc.
|
|
11206
|
+
{ regex: /(\S+):(\d+):([a-f0-9]{32}):([a-f0-9]{32})/g, type: "hash" },
|
|
11207
|
+
// Generic user:pass pattern
|
|
11208
|
+
{ regex: /(?:username|user|login)\s*[:=]\s*['"]?(\S+?)['"]?\s+(?:password|pass|pwd)\s*[:=]\s*['"]?(\S+?)['"]?/gi, type: "password" },
|
|
11209
|
+
// Database connection strings
|
|
11210
|
+
{ regex: /(?:postgres|mysql|mongodb):\/\/([^:]+):([^@]+)@/g, type: "password" },
|
|
11211
|
+
// API tokens/keys
|
|
11212
|
+
{ regex: /(?:api[_-]?key|token|secret|bearer)\s*[:=]\s*['"]?([a-zA-Z0-9_\-]{20,})['"]?/gi, type: "token" }
|
|
11213
|
+
];
|
|
11214
|
+
for (const { regex, type } of patterns) {
|
|
11215
|
+
let match;
|
|
11216
|
+
while ((match = regex.exec(output)) !== null) {
|
|
11217
|
+
const username = match[1] || "unknown";
|
|
11218
|
+
const credential = match[2] || match[1];
|
|
11219
|
+
if (credential && !creds.some((c) => c.username === username && c.credential === credential)) {
|
|
11220
|
+
creds.push({ username, credential, type, source: "auto-extracted" });
|
|
11221
|
+
}
|
|
11222
|
+
}
|
|
11223
|
+
}
|
|
11224
|
+
return creds;
|
|
11225
|
+
}
|
|
11226
|
+
function extractVulnerabilities(output) {
|
|
11227
|
+
const vulns = [];
|
|
11228
|
+
const cveRegex = /CVE-\d{4}-\d{4,}/g;
|
|
11229
|
+
let match;
|
|
11230
|
+
while ((match = cveRegex.exec(output)) !== null) {
|
|
11231
|
+
const cveId = match[0];
|
|
11232
|
+
if (!vulns.some((v) => v.id === cveId)) {
|
|
11233
|
+
vulns.push({
|
|
11234
|
+
id: cveId,
|
|
11235
|
+
severity: "unknown",
|
|
11236
|
+
description: `${cveId} referenced in output`,
|
|
11237
|
+
hasExploit: /exploit|poc|proof.of.concept/i.test(output)
|
|
11238
|
+
});
|
|
11239
|
+
}
|
|
11240
|
+
}
|
|
11241
|
+
return vulns;
|
|
11242
|
+
}
|
|
11243
|
+
|
|
10911
11244
|
// src/engine/parsers/types.ts
|
|
10912
11245
|
var PORT_STATE2 = {
|
|
10913
11246
|
OPEN: "open",
|
|
@@ -10977,51 +11310,6 @@ function extractFuzzStructured(output) {
|
|
|
10977
11310
|
};
|
|
10978
11311
|
}
|
|
10979
11312
|
|
|
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
11313
|
// src/shared/utils/binary-analysis/types.ts
|
|
11026
11314
|
var RELRO_STATUS = {
|
|
11027
11315
|
NO: "no",
|
|
@@ -11124,45 +11412,42 @@ function formatBinaryAnalysis(info) {
|
|
|
11124
11412
|
return lines.join("\n");
|
|
11125
11413
|
}
|
|
11126
11414
|
|
|
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
|
-
}
|
|
11415
|
+
// src/engine/parsers/auto-extractors.ts
|
|
11416
|
+
function extractNmapData(toolName, output, data) {
|
|
11142
11417
|
if (toolName === TOOL_NAMES.PARSE_NMAP || /nmap scan report/i.test(output)) {
|
|
11143
11418
|
const nmap = extractNmapStructured(output);
|
|
11144
11419
|
if (nmap.structured.openPorts && nmap.structured.openPorts.length > 0) {
|
|
11145
11420
|
data.openPorts = nmap.structured.openPorts;
|
|
11146
11421
|
data.hosts = nmap.structured.hosts;
|
|
11147
|
-
|
|
11422
|
+
return true;
|
|
11148
11423
|
}
|
|
11149
11424
|
}
|
|
11425
|
+
return false;
|
|
11426
|
+
}
|
|
11427
|
+
function extractFuzzData(output, data) {
|
|
11150
11428
|
if (/gobuster|ffuf|feroxbuster|dirbuster/i.test(output)) {
|
|
11151
11429
|
const fuzz = extractFuzzStructured(output);
|
|
11152
11430
|
if (fuzz.structured.paths && fuzz.structured.paths.length > 0) {
|
|
11153
11431
|
data.paths = fuzz.structured.paths;
|
|
11154
|
-
|
|
11432
|
+
return true;
|
|
11155
11433
|
}
|
|
11156
11434
|
}
|
|
11435
|
+
return false;
|
|
11436
|
+
}
|
|
11437
|
+
function extractBinaryData(output, data) {
|
|
11157
11438
|
if (/canary|RELRO|NX|PIE|Stack|FORTIFY/i.test(output) && /enabled|disabled|found|no /i.test(output)) {
|
|
11158
11439
|
const binaryInfo = parseChecksec(output);
|
|
11159
11440
|
if (binaryInfo.arch || binaryInfo.canary !== void 0 || binaryInfo.nx !== void 0) {
|
|
11160
11441
|
data.binaryInfo = binaryInfo;
|
|
11161
11442
|
data.binaryAnalysisSummary = formatBinaryAnalysis(binaryInfo);
|
|
11162
|
-
|
|
11443
|
+
return true;
|
|
11163
11444
|
}
|
|
11164
11445
|
}
|
|
11446
|
+
return false;
|
|
11447
|
+
}
|
|
11448
|
+
function extractOsintData(toolName, output, data) {
|
|
11165
11449
|
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) {
|
|
11450
|
+
let hasData = false;
|
|
11166
11451
|
const ipMatches = output.match(/\b(?:\d{1,3}\.){3}\d{1,3}\b/g);
|
|
11167
11452
|
if (ipMatches && ipMatches.length > 0) {
|
|
11168
11453
|
data.discoveredIPs = [...new Set(ipMatches)].slice(0, 50);
|
|
@@ -11173,21 +11458,52 @@ function autoExtractStructured(toolName, output) {
|
|
|
11173
11458
|
data.discoveredSubdomains = [...new Set(subMatches)].slice(0, 100);
|
|
11174
11459
|
hasData = true;
|
|
11175
11460
|
}
|
|
11461
|
+
return hasData;
|
|
11176
11462
|
}
|
|
11463
|
+
return false;
|
|
11464
|
+
}
|
|
11465
|
+
function extractCryptoData(toolName, output, data) {
|
|
11177
11466
|
if (toolName === TOOL_NAMES.RSA_ANALYZE || toolName === TOOL_NAMES.HASH_IDENTIFY || toolName === TOOL_NAMES.PADDING_ORACLE_CHECK) {
|
|
11178
11467
|
const weaknessMatches = output.match(/(?:FERMAT_FACTORED|SMALL_E|WEAK_N|Padding Oracle|hash type|cracked)[^\n]*/gi);
|
|
11179
11468
|
if (weaknessMatches && weaknessMatches.length > 0) {
|
|
11180
11469
|
data.cryptoWeaknesses = weaknessMatches.slice(0, 10);
|
|
11181
|
-
|
|
11470
|
+
return true;
|
|
11182
11471
|
}
|
|
11183
11472
|
}
|
|
11473
|
+
return false;
|
|
11474
|
+
}
|
|
11475
|
+
function extractForensicsData(toolName, output, data) {
|
|
11184
11476
|
if (toolName === TOOL_NAMES.BINWALK_ANALYZE || toolName === TOOL_NAMES.STEGHIDE_EXTRACT || toolName === TOOL_NAMES.ZSTEG_ANALYZE || toolName === TOOL_NAMES.STEGSEEK) {
|
|
11185
11477
|
const stegoMatches = output.match(/(?:JPEG|PNG|ZIP|embedded|hidden|extracted|passphrase|found)[^\n]*/gi);
|
|
11186
11478
|
if (stegoMatches && stegoMatches.length > 0) {
|
|
11187
11479
|
data.stegoHints = stegoMatches.slice(0, 10);
|
|
11188
|
-
|
|
11480
|
+
return true;
|
|
11189
11481
|
}
|
|
11190
11482
|
}
|
|
11483
|
+
return false;
|
|
11484
|
+
}
|
|
11485
|
+
|
|
11486
|
+
// src/engine/parsers/index.ts
|
|
11487
|
+
function autoExtractStructured(toolName, output) {
|
|
11488
|
+
if (!output || output.length < 10) return null;
|
|
11489
|
+
const data = {};
|
|
11490
|
+
let hasData = false;
|
|
11491
|
+
const creds = extractCredentials(output);
|
|
11492
|
+
if (creds && creds.length > 0) {
|
|
11493
|
+
data.credentials = creds;
|
|
11494
|
+
hasData = true;
|
|
11495
|
+
}
|
|
11496
|
+
const vulns = extractVulnerabilities(output);
|
|
11497
|
+
if (vulns && vulns.length > 0) {
|
|
11498
|
+
data.vulnerabilities = vulns;
|
|
11499
|
+
hasData = true;
|
|
11500
|
+
}
|
|
11501
|
+
if (extractNmapData(toolName, output, data)) hasData = true;
|
|
11502
|
+
if (extractFuzzData(output, data)) hasData = true;
|
|
11503
|
+
if (extractBinaryData(output, data)) hasData = true;
|
|
11504
|
+
if (extractOsintData(toolName, output, data)) hasData = true;
|
|
11505
|
+
if (extractCryptoData(toolName, output, data)) hasData = true;
|
|
11506
|
+
if (extractForensicsData(toolName, output, data)) hasData = true;
|
|
11191
11507
|
return hasData ? data : null;
|
|
11192
11508
|
}
|
|
11193
11509
|
|
|
@@ -11345,7 +11661,7 @@ function runPostExecutionPipeline(state, toolCall, result) {
|
|
|
11345
11661
|
// src/engine/tool-registry/core.ts
|
|
11346
11662
|
function validateRequiredParams(tool, input) {
|
|
11347
11663
|
if (!tool.required || tool.required.length === 0) return [];
|
|
11348
|
-
return tool.required.filter((param2) => !(param2 in input) || input[param2]
|
|
11664
|
+
return tool.required.filter((param2) => !(param2 in input) || input[param2] == null);
|
|
11349
11665
|
}
|
|
11350
11666
|
var ToolRegistry = class {
|
|
11351
11667
|
constructor(state, scopeGuard, approvalGate, events) {
|
|
@@ -11553,7 +11869,16 @@ After completion: record key loot/findings from the sub-agent output to canonica
|
|
|
11553
11869
|
task: { type: "string", description: "Delegated task goal" },
|
|
11554
11870
|
target: { type: "string", description: "Optional target IP/URL/path" },
|
|
11555
11871
|
context: { type: "string", description: "Optional extra context" },
|
|
11556
|
-
time_limit_min: { type: "number", description: "Optional hint only" }
|
|
11872
|
+
time_limit_min: { type: "number", description: "Optional hint only" },
|
|
11873
|
+
worker_type: {
|
|
11874
|
+
type: "string",
|
|
11875
|
+
enum: ["general", "shell-supervisor", "exploit", "pwn"],
|
|
11876
|
+
description: "Optional worker specialization for the delegated task"
|
|
11877
|
+
},
|
|
11878
|
+
resume_task_id: {
|
|
11879
|
+
type: "string",
|
|
11880
|
+
description: "Optional delegated task ID to resume/continue instead of creating a fresh chain"
|
|
11881
|
+
}
|
|
11557
11882
|
},
|
|
11558
11883
|
required: ["task"],
|
|
11559
11884
|
execute: async (params) => {
|
|
@@ -11568,18 +11893,43 @@ After completion: record key loot/findings from the sub-agent output to canonica
|
|
|
11568
11893
|
task: params["task"],
|
|
11569
11894
|
target: params["target"],
|
|
11570
11895
|
context: params["context"],
|
|
11571
|
-
timeLimitMin: params["time_limit_min"]
|
|
11896
|
+
timeLimitMin: params["time_limit_min"],
|
|
11897
|
+
workerType: params["worker_type"],
|
|
11898
|
+
resumeTaskId: params["resume_task_id"]
|
|
11572
11899
|
};
|
|
11573
|
-
const { AgentTool } = await import("./agent-tool-
|
|
11900
|
+
const { AgentTool } = await import("./agent-tool-MP274HWD.js");
|
|
11574
11901
|
const executor = new AgentTool(state, events, scopeGuard, approvalGate);
|
|
11575
11902
|
const result = await executor.execute(input);
|
|
11903
|
+
state.recordDelegatedTask({
|
|
11904
|
+
task: input.task,
|
|
11905
|
+
target: input.target,
|
|
11906
|
+
context: input.context,
|
|
11907
|
+
resumeTaskId: input.resumeTaskId,
|
|
11908
|
+
workerType: input.workerType,
|
|
11909
|
+
status: result.status,
|
|
11910
|
+
summary: result.summary,
|
|
11911
|
+
tried: result.tried,
|
|
11912
|
+
findings: result.findings,
|
|
11913
|
+
loot: result.loot,
|
|
11914
|
+
sessions: result.sessions,
|
|
11915
|
+
assets: result.assets,
|
|
11916
|
+
suggestedNext: result.suggestedNext,
|
|
11917
|
+
waitingOn: result.waitingOn,
|
|
11918
|
+
resumeHint: result.resumeHint,
|
|
11919
|
+
nextWorkerType: result.nextWorkerType
|
|
11920
|
+
});
|
|
11576
11921
|
const lines = [
|
|
11577
11922
|
`[Status] ${result.status}`,
|
|
11578
11923
|
`[Summary] ${result.summary}`,
|
|
11579
11924
|
result.findings.length ? `[Findings] ${result.findings.join(" | ")}` : "",
|
|
11580
11925
|
result.loot.length ? `[Loot] ${result.loot.join(" | ")}` : "",
|
|
11581
11926
|
result.sessions.length ? `[Sessions] ${result.sessions.join(", ")}` : "",
|
|
11927
|
+
result.assets.length ? `[Assets] ${result.assets.join(" | ")}` : "",
|
|
11582
11928
|
result.tried.length ? `[Tried] ${result.tried.join(" | ")}` : "",
|
|
11929
|
+
input.resumeTaskId ? `[ResumedFrom] ${input.resumeTaskId}` : "",
|
|
11930
|
+
result.waitingOn ? `[Waiting] ${result.waitingOn}` : "",
|
|
11931
|
+
result.resumeHint ? `[Resume] ${result.resumeHint}` : "",
|
|
11932
|
+
result.nextWorkerType ? `[NextWorker] ${result.nextWorkerType}` : "",
|
|
11583
11933
|
result.suggestedNext ? `[Next] ${result.suggestedNext}` : ""
|
|
11584
11934
|
].filter(Boolean);
|
|
11585
11935
|
return { success: true, output: lines.join("\n") };
|
|
@@ -11618,6 +11968,18 @@ var CategorizedToolRegistry = class extends ToolRegistry {
|
|
|
11618
11968
|
TOOL_NAMES.READ_FILE,
|
|
11619
11969
|
TOOL_NAMES.WRITE_FILE,
|
|
11620
11970
|
TOOL_NAMES.BG_PROCESS,
|
|
11971
|
+
TOOL_NAMES.LISTENER_START,
|
|
11972
|
+
TOOL_NAMES.LISTENER_STATUS,
|
|
11973
|
+
TOOL_NAMES.SHELL_PROMOTE,
|
|
11974
|
+
TOOL_NAMES.SHELL_EXEC,
|
|
11975
|
+
TOOL_NAMES.SHELL_CHECK,
|
|
11976
|
+
TOOL_NAMES.SHELL_UPGRADE,
|
|
11977
|
+
TOOL_NAMES.EXPLOIT_CREDENTIAL_CHECK,
|
|
11978
|
+
TOOL_NAMES.EXPLOIT_ARTIFACT_CHECK,
|
|
11979
|
+
TOOL_NAMES.EXPLOIT_FOOTHOLD_CHECK,
|
|
11980
|
+
TOOL_NAMES.PWN_CRASH_REPRO,
|
|
11981
|
+
TOOL_NAMES.PWN_OFFSET_CHECK,
|
|
11982
|
+
TOOL_NAMES.PWN_PAYLOAD_SMOKE,
|
|
11621
11983
|
TOOL_NAMES.PARSE_NMAP,
|
|
11622
11984
|
TOOL_NAMES.SEARCH_CVE,
|
|
11623
11985
|
TOOL_NAMES.WEB_SEARCH,
|
|
@@ -11666,6 +12028,127 @@ var CategorizedToolRegistry = class extends ToolRegistry {
|
|
|
11666
12028
|
}
|
|
11667
12029
|
};
|
|
11668
12030
|
|
|
12031
|
+
// src/engine/agent-tool/shell-supervisor-lifecycle.ts
|
|
12032
|
+
function hasConnectionSignal(processId, stdout) {
|
|
12033
|
+
if (DETECTION_PATTERNS.CONNECTION.some((pattern) => pattern.test(stdout))) {
|
|
12034
|
+
return true;
|
|
12035
|
+
}
|
|
12036
|
+
return getProcessEventLog().some(
|
|
12037
|
+
(event) => event.processId === processId && event.event === PROCESS_EVENTS.CONNECTION_DETECTED
|
|
12038
|
+
);
|
|
12039
|
+
}
|
|
12040
|
+
function getRecentCommandDetails(processId) {
|
|
12041
|
+
return getProcessEventLog().filter((event) => event.processId === processId && event.event === PROCESS_EVENTS.COMMAND_SENT).slice(-10).map((event) => event.detail.toLowerCase());
|
|
12042
|
+
}
|
|
12043
|
+
function hasEvent(processId, eventName) {
|
|
12044
|
+
return getProcessEventLog().some(
|
|
12045
|
+
(event) => event.processId === processId && event.event === eventName
|
|
12046
|
+
);
|
|
12047
|
+
}
|
|
12048
|
+
function getLatestEventTimestamp(processId, eventName) {
|
|
12049
|
+
return getProcessEventLog().filter((event) => event.processId === processId && event.event === eventName).reduce((latest, event) => Math.max(latest, event.timestamp), 0);
|
|
12050
|
+
}
|
|
12051
|
+
function isPtyUpgradeCommand(detail) {
|
|
12052
|
+
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"');
|
|
12053
|
+
}
|
|
12054
|
+
function isShellStabilized(stdout, commandDetails) {
|
|
12055
|
+
const normalized = stdout.toLowerCase();
|
|
12056
|
+
return normalized.includes("xterm") || normalized.includes("rows") || normalized.includes("columns") || commandDetails.some(
|
|
12057
|
+
(detail) => detail.includes("export term=") || detail.includes("export shell=") || detail.includes("stty rows") || detail.includes("stty columns")
|
|
12058
|
+
);
|
|
12059
|
+
}
|
|
12060
|
+
function isPostExploitationCommand(detail) {
|
|
12061
|
+
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");
|
|
12062
|
+
}
|
|
12063
|
+
function hasRecentEvent(processId, eventName) {
|
|
12064
|
+
return getProcessEventLog().some(
|
|
12065
|
+
(event) => event.processId === processId && event.event === eventName
|
|
12066
|
+
);
|
|
12067
|
+
}
|
|
12068
|
+
function getShellSupervisorLifecycleSnapshot() {
|
|
12069
|
+
const processes = listBackgroundProcesses().filter(
|
|
12070
|
+
(process2) => process2.isRunning && (process2.role === PROCESS_ROLES.ACTIVE_SHELL || process2.role === PROCESS_ROLES.LISTENER)
|
|
12071
|
+
);
|
|
12072
|
+
const activeShell = processes.find((process2) => process2.role === PROCESS_ROLES.ACTIVE_SHELL);
|
|
12073
|
+
if (activeShell) {
|
|
12074
|
+
const output = getProcessOutput(activeShell.id);
|
|
12075
|
+
const commandDetails = getRecentCommandDetails(activeShell.id);
|
|
12076
|
+
const lastStabilizedAt = getLatestEventTimestamp(activeShell.id, PROCESS_EVENTS.SHELL_STABILIZED);
|
|
12077
|
+
const lastIncompleteAt = getLatestEventTimestamp(activeShell.id, PROCESS_EVENTS.SHELL_STABILITY_INCOMPLETE);
|
|
12078
|
+
if (hasRecentEvent(activeShell.id, PROCESS_EVENTS.POST_EXPLOITATION_ACTIVITY) || commandDetails.some(isPostExploitationCommand)) {
|
|
12079
|
+
return {
|
|
12080
|
+
phase: "post_exploitation_active",
|
|
12081
|
+
activeShellId: activeShell.id,
|
|
12082
|
+
recommendation: `Active shell ${activeShell.id} is already in post-exploitation flow. Keep reusing it for controlled enumeration, privilege escalation checks, and pivot preparation.`
|
|
12083
|
+
};
|
|
12084
|
+
}
|
|
12085
|
+
if (hasEvent(activeShell.id, PROCESS_EVENTS.SHELL_STABILIZED) || output && isShellStabilized(output.stdout, commandDetails)) {
|
|
12086
|
+
if (lastIncompleteAt > lastStabilizedAt) {
|
|
12087
|
+
return {
|
|
12088
|
+
phase: "active_shell_stabilizing",
|
|
12089
|
+
activeShellId: activeShell.id,
|
|
12090
|
+
recommendation: `Active shell ${activeShell.id} lost stable PTY confirmation after the last probe. Re-run shell upgrade and verify TERM/TTY quality again before broad enumeration.`
|
|
12091
|
+
};
|
|
12092
|
+
}
|
|
12093
|
+
return {
|
|
12094
|
+
phase: "active_shell_stabilized",
|
|
12095
|
+
activeShellId: activeShell.id,
|
|
12096
|
+
recommendation: `Active shell ${activeShell.id} appears stabilized. Reuse it for controlled enumeration and follow-up operations.`
|
|
12097
|
+
};
|
|
12098
|
+
}
|
|
12099
|
+
if (hasEvent(activeShell.id, PROCESS_EVENTS.SHELL_UPGRADE_ATTEMPTED) || hasRecentEvent(activeShell.id, PROCESS_EVENTS.SHELL_STABILITY_INCOMPLETE) || hasRecentEvent(activeShell.id, PROCESS_EVENTS.SHELL_STABILITY_CHECKED) || commandDetails.some(isPtyUpgradeCommand)) {
|
|
12100
|
+
return {
|
|
12101
|
+
phase: "active_shell_stabilizing",
|
|
12102
|
+
activeShellId: activeShell.id,
|
|
12103
|
+
recommendation: `Active shell ${activeShell.id} is in PTY stabilization flow. Verify TERM/TTY quality, then continue controlled enumeration.`
|
|
12104
|
+
};
|
|
12105
|
+
}
|
|
12106
|
+
return {
|
|
12107
|
+
phase: "active_shell_ready",
|
|
12108
|
+
activeShellId: activeShell.id,
|
|
12109
|
+
recommendation: `Reuse active shell ${activeShell.id}, validate stability, and upgrade PTY before broad enumeration.`
|
|
12110
|
+
};
|
|
12111
|
+
}
|
|
12112
|
+
const listeners = processes.filter((process2) => process2.role === PROCESS_ROLES.LISTENER);
|
|
12113
|
+
for (const listener of listeners) {
|
|
12114
|
+
const output = getProcessOutput(listener.id);
|
|
12115
|
+
if (!output) continue;
|
|
12116
|
+
if (hasConnectionSignal(listener.id, output.stdout)) {
|
|
12117
|
+
return {
|
|
12118
|
+
phase: "listener_callback_detected",
|
|
12119
|
+
listenerId: listener.id,
|
|
12120
|
+
recommendation: `Listener ${listener.id} has a callback signal. Confirm status, promote immediately, then validate and stabilize the shell.`
|
|
12121
|
+
};
|
|
12122
|
+
}
|
|
12123
|
+
}
|
|
12124
|
+
if (listeners.length > 0) {
|
|
12125
|
+
return {
|
|
12126
|
+
phase: "listener_waiting",
|
|
12127
|
+
listenerId: listeners[0]?.id,
|
|
12128
|
+
recommendation: `Reuse listener ${listeners[0]?.id} and keep polling for a callback instead of opening a duplicate listener.`
|
|
12129
|
+
};
|
|
12130
|
+
}
|
|
12131
|
+
return {
|
|
12132
|
+
phase: "no_asset",
|
|
12133
|
+
recommendation: "No shell or listener asset is active. Create exactly one listener only if the callback path still requires it."
|
|
12134
|
+
};
|
|
12135
|
+
}
|
|
12136
|
+
function buildShellSupervisorLifecycleSection() {
|
|
12137
|
+
const snapshot = getShellSupervisorLifecycleSnapshot();
|
|
12138
|
+
const lines = [
|
|
12139
|
+
"## Shell Supervisor Lifecycle",
|
|
12140
|
+
`Current phase: ${snapshot.phase}`,
|
|
12141
|
+
`Recommendation: ${snapshot.recommendation}`
|
|
12142
|
+
];
|
|
12143
|
+
if (snapshot.activeShellId) {
|
|
12144
|
+
lines.push(`Active shell: ${snapshot.activeShellId}`);
|
|
12145
|
+
}
|
|
12146
|
+
if (snapshot.listenerId) {
|
|
12147
|
+
lines.push(`Tracked listener: ${snapshot.listenerId}`);
|
|
12148
|
+
}
|
|
12149
|
+
return lines.join("\n");
|
|
12150
|
+
}
|
|
12151
|
+
|
|
11669
12152
|
export {
|
|
11670
12153
|
ENV_KEYS,
|
|
11671
12154
|
DEFAULT_MODEL,
|
|
@@ -11680,6 +12163,8 @@ export {
|
|
|
11680
12163
|
HealthMonitor,
|
|
11681
12164
|
ZombieHunter,
|
|
11682
12165
|
getLLMClient,
|
|
12166
|
+
getGlobalRequestCount,
|
|
12167
|
+
getGlobalTokenUsage,
|
|
11683
12168
|
createContextExtractor,
|
|
11684
12169
|
parseTurnNumbers,
|
|
11685
12170
|
rotateTurnRecords,
|
|
@@ -11706,5 +12191,7 @@ export {
|
|
|
11706
12191
|
formatChallengeAnalysis,
|
|
11707
12192
|
setCurrentTurn,
|
|
11708
12193
|
CoreAgent,
|
|
12194
|
+
getShellSupervisorLifecycleSnapshot,
|
|
12195
|
+
buildShellSupervisorLifecycleSection,
|
|
11709
12196
|
CategorizedToolRegistry
|
|
11710
12197
|
};
|