@pensar/apex 2.6.1 → 2.7.0-canary.21cb83dc
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/build/{agent-yby11zaq.js → agent-68t2wzmc.js} +6 -6
- package/build/agent-jhekpern.js +13 -0
- package/build/{agent-ayreaj6z.js → agent-qvreg8a4.js} +5 -5
- package/build/{agent-runs-g37vkrjr.js → agent-runs-30dvbvmm.js} +18 -18
- package/build/agent-vybw255f.js +13 -0
- package/build/{apps-xn6km9yn.js → apps-na0y0992.js} +13 -13
- package/build/{auth-d105wc64.js → auth-dz2c5thx.js} +13 -13
- package/build/{blackboxAgent-s56sgx9b.js → blackboxAgent-2mwvaajb.js} +6 -6
- package/build/{blackboxPentest-ps78v3w7.js → blackboxPentest-s2mfze9p.js} +17 -12
- package/build/{cli-kb21qhvm.js → cli-1pmfnz1a.js} +4 -3
- package/build/{cli-ae8jqxqr.js → cli-563yntrb.js} +2 -2
- package/build/{cli-7eea4e2y.js → cli-5ab346kd.js} +6 -6
- package/build/{cli-1ve18npr.js → cli-72y76bpg.js} +3 -2
- package/build/{cli-fy6vh2a7.js → cli-98wmmphf.js} +1 -1
- package/build/{cli-c05y640n.js → cli-9jscnhtg.js} +1 -1
- package/build/{cli-3sp6nd8d.js → cli-9kk9tm58.js} +2 -2
- package/build/{cli-d37a42dc.js → cli-9pz9wjt2.js} +101 -18
- package/build/{cli-dpsw2fe6.js → cli-a6x4jm5a.js} +476 -193
- package/build/{cli-az3xq70v.js → cli-cfrdr25z.js} +1 -1
- package/build/{cli-ngq9dr8n.js → cli-fdrrbb9p.js} +3 -3
- package/build/{cli-k2nwxta0.js → cli-t09zj814.js} +2 -2
- package/build/{cli-qgz1q9g5.js → cli-t4q4t4dx.js} +223 -76
- package/build/{cli-xtc2t6me.js → cli-vdd1s3x9.js} +103 -39
- package/build/{cli-gx71jcke.js → cli-vn4cwac3.js} +1 -1
- package/build/{cli-65g6rcat.js → cli-w9nw2a4z.js} +1 -1
- package/build/{cli-n2en7j5b.js → cli-z7ysyrr4.js} +1 -1
- package/build/{cli-709rvekb.js → cli-zbbtr4sb.js} +4 -2
- package/build/cli.js +70 -40
- package/build/{config-zpqkd5cz.js → config-nkzm9arw.js} +1 -1
- package/build/{fastStrike-mcjdvwez.js → fastStrike-16jw1s61.js} +67 -15
- package/build/{fixes-kytx5dmj.js → fixes-6wxk22v5.js} +13 -13
- package/build/{index-4vqdgb42.js → index-36p56pcf.js} +1473 -1217
- package/build/{index-je6hcsqr.js → index-6nejymsc.js} +1 -1
- package/build/{index-fv64eb32.js → index-asm4y7e4.js} +2 -2
- package/build/{index-n65z6v7b.js → index-bfmn52za.js} +6 -6
- package/build/{index-qv4cc65p.js → index-bt4r13q7.js} +13 -13
- package/build/{index-dr3tg1w3.js → index-gfq1rp1q.js} +1 -1
- package/build/{index-wqetp6gq.js → index-j9pxgrzd.js} +12 -7
- package/build/{index-r9yjkfrv.js → index-n9kapn1x.js} +4 -4
- package/build/{index-ns4m4s01.js → index-tr6evhyg.js} +4 -4
- package/build/{issues-eb3q9d3a.js → issues-7rmf4ker.js} +13 -13
- package/build/{knowledge-fqsvtx0f.js → knowledge-s2sxh8nb.js} +13 -13
- package/build/lib/{chunk-1azdancq.js → chunk-0dnx79b1.js} +1 -1
- package/build/lib/{chunk-4hwzqthr.js → chunk-161ak2am.js} +3 -2
- package/build/lib/{chunk-z137x08b.js → chunk-1rqbhx4b.js} +7 -5
- package/build/lib/{chunk-1yct6cra.js → chunk-3hjvdf67.js} +1 -1
- package/build/lib/{chunk-r2dkp4tb.js → chunk-5j8pp0xf.js} +1 -1
- package/build/lib/{chunk-3c00n0cy.js → chunk-77se1d35.js} +8 -3
- package/build/lib/{chunk-yrnrbjes.js → chunk-7tft4b8b.js} +4 -2
- package/build/lib/{chunk-w66ce82a.js → chunk-8njjwwcw.js} +4 -2
- package/build/lib/{chunk-v3dk9j7k.js → chunk-aq9eyxas.js} +80 -21
- package/build/lib/{chunk-9tt2021m.js → chunk-d05cvnv8.js} +9 -9
- package/build/lib/{chunk-e8yy2k73.js → chunk-f3sq4896.js} +28 -2
- package/build/lib/{chunk-1fy8azee.js → chunk-fasqtjq1.js} +1 -1
- package/build/lib/{chunk-1jvwqnem.js → chunk-gm3b9j4y.js} +104 -40
- package/build/lib/{chunk-r4j8ybn9.js → chunk-hys7nemr.js} +87 -36
- package/build/lib/{chunk-7pwa92rn.js → chunk-j9668h16.js} +1 -1
- package/build/lib/{chunk-ba41jh41.js → chunk-kcw4cpr3.js} +418 -133
- package/build/lib/{chunk-z96pbjhd.js → chunk-m4swxy5r.js} +13 -6
- package/build/lib/{chunk-xhdpgpkm.js → chunk-mbsde9d0.js} +2 -2
- package/build/lib/{chunk-9yksqgzr.js → chunk-mpvjfvz0.js} +20 -13
- package/build/lib/{chunk-2qsq76xq.js → chunk-qkz5sm0d.js} +128 -34
- package/build/lib/{chunk-nhfax4gb.js → chunk-rfyjqybp.js} +1 -1
- package/build/lib/{chunk-mg03jdmt.js → chunk-vsnwzne5.js} +1 -1
- package/build/lib/{chunk-acgbyf5h.js → chunk-ydxhkgjw.js} +3 -2
- package/build/lib/{chunk-bb0ekhkw.js → chunk-z8kej0sp.js} +69 -18
- package/build/lib/core/agents/agentRuntime.d.ts +2 -1
- package/build/lib/core/agents/defineAgent.d.ts +3 -2
- package/build/lib/core/agents/offSecAgent/index.d.ts +3 -1
- package/build/lib/core/agents/offSecAgent/index.js +10 -8
- package/build/lib/core/agents/offSecAgent/prompt.d.ts +3 -1
- package/build/lib/core/agents/offSecAgent/subagentSpawner.d.ts +2 -1
- package/build/lib/core/agents/offSecAgent/subagentSpawner.js +5 -5
- package/build/lib/core/agents/offSecAgent/tools/index.js +4 -4
- package/build/lib/core/agents/offSecAgent/tools/listMemories.d.ts +11 -1
- package/build/lib/core/agents/offSecAgent/tools/reportError.d.ts +2 -1
- package/build/lib/core/agents/offSecAgent/tools/runPentestWorkflow.d.ts +20 -1
- package/build/lib/core/agents/offSecAgent/tools/spawnPentestAgent.d.ts +4 -7
- package/build/lib/core/agents/offSecAgent/tools/spawnPentestSwarm.d.ts +7 -1
- package/build/lib/core/agents/offSecAgent/tools/types.d.ts +2 -1
- package/build/lib/core/agents/offSecAgent/types.d.ts +7 -1
- package/build/lib/core/agents/specialized/attackSurface/blackboxAgent.js +9 -9
- package/build/lib/core/agents/specialized/attackSurface/schemas.js +9 -9
- package/build/lib/core/agents/specialized/authenticationAgent/index.js +10 -10
- package/build/lib/core/agents/specialized/codeAgent/agent.js +9 -9
- package/build/lib/core/agents/specialized/codeAgent/prompts.d.ts +1 -0
- package/build/lib/core/agents/specialized/findingJudge/agent.js +8 -8
- package/build/lib/core/agents/specialized/findingJudge/index.js +3 -3
- package/build/lib/core/agents/specialized/patching/index.js +10 -10
- package/build/lib/core/agents/specialized/pentest/agent.d.ts +18 -4
- package/build/lib/core/agents/specialized/pentest/agent.js +11 -9
- package/build/lib/core/agents/specialized/whiteboxAttackSurface/index.js +9 -9
- package/build/lib/core/ai/ai.d.ts +23 -6
- package/build/lib/core/ai/ai.js +2 -2
- package/build/lib/core/ai/index.js +2 -2
- package/build/lib/core/ai/models/claude-capabilities.d.ts +3 -2
- package/build/lib/core/ai/models/concentrate.d.ts +1 -0
- package/build/lib/core/ai/models/index.d.ts +1 -1
- package/build/lib/core/ai/providers/concentrate.d.ts +1 -0
- package/build/lib/core/ai/utils.d.ts +2 -0
- package/build/lib/core/ai/utils.js +4 -2
- package/build/lib/core/api/index.d.ts +3 -0
- package/build/lib/core/api/index.js +22 -19
- package/build/lib/core/api/targetedPentest.d.ts +1 -0
- package/build/lib/core/api/targetedPentest.js +10 -10
- package/build/lib/core/config/config.d.ts +3 -0
- package/build/lib/core/config/index.js +1 -1
- package/build/lib/core/findings/registry.js +3 -3
- package/build/lib/core/session/index.d.ts +12 -0
- package/build/lib/core/session/index.js +2 -2
- package/build/lib/core/session/persistence.d.ts +7 -0
- package/build/lib/core/session/persistence.js +7 -3
- package/build/lib/core/workflows/fastStrike.d.ts +3 -1
- package/build/lib/core/workflows/pentest.d.ts +8 -1
- package/build/lib/core/workflows/pentest.js +15 -15
- package/build/lib/core/workflows/seams.js +4 -4
- package/build/lib/core/workflows/whiteboxAttackSurface.js +11 -11
- package/build/lib/index.js +22 -22
- package/build/{logs-x62b7hk1.js → logs-70fyr848.js} +13 -13
- package/build/{native-rollout-evidence-c9yhfqcs.js → native-rollout-evidence-ntensvzb.js} +3 -3
- package/build/{offesecAgent-5whnpeta.js → offesecAgent-kk62tf60.js} +4 -4
- package/build/pentest-4jgznn7h.js +22 -0
- package/build/{pentests-e0f32aqx.js → pentests-3e41hj08.js} +13 -13
- package/build/recordedRunClient-w34kcch1.js +11 -0
- package/build/{targetedPentest-rcbs4nvp.js → targetedPentest-t9snxcj2.js} +18 -11
- package/build/{targets-xj9g3q0z.js → targets-1pyf21c7.js} +13 -13
- package/build/threatModel-48y5qjze.js +21 -0
- package/build/{uninstall-7s784q4a.js → uninstall-fbydd9t6.js} +1 -1
- package/build/{upload-4gvhwmvr.js → upload-sbhwe2fq.js} +4 -4
- package/build/{utils-cjrz64pc.js → utils-23903gcj.js} +3 -3
- package/package.json +1 -1
- package/src/cli.ts +48 -14
- package/src/core/agents/agentRuntime.test.ts +38 -0
- package/src/core/agents/agentRuntime.ts +5 -2
- package/src/core/agents/defineAgent.ts +3 -1
- package/src/core/agents/offSecAgent/index.ts +3 -0
- package/src/core/agents/offSecAgent/offensiveSecurityAgent.test.ts +92 -5
- package/src/core/agents/offSecAgent/offensiveSecurityAgent.ts +70 -31
- package/src/core/agents/offSecAgent/prompt.test.ts +46 -0
- package/src/core/agents/offSecAgent/prompt.ts +52 -8
- package/src/core/agents/offSecAgent/selectiveConstruction.publicPath.test.ts +38 -0
- package/src/core/agents/offSecAgent/selectiveConstruction.test.ts +64 -0
- package/src/core/agents/offSecAgent/subagentSpawner.ts +12 -1
- package/src/core/agents/offSecAgent/tools/delegateAuth.ts +1 -0
- package/src/core/agents/offSecAgent/tools/listMemories.test.ts +230 -0
- package/src/core/agents/offSecAgent/tools/listMemories.ts +73 -6
- package/src/core/agents/offSecAgent/tools/reportError.test.ts +6 -0
- package/src/core/agents/offSecAgent/tools/reportError.ts +10 -2
- package/src/core/agents/offSecAgent/tools/runAttackSurface.ts +2 -0
- package/src/core/agents/offSecAgent/tools/runPentestWorkflow.test.ts +57 -0
- package/src/core/agents/offSecAgent/tools/runPentestWorkflow.ts +19 -1
- package/src/core/agents/offSecAgent/tools/runWhiteboxScan.ts +5 -2
- package/src/core/agents/offSecAgent/tools/spawnCodingAgent.ts +1 -0
- package/src/core/agents/offSecAgent/tools/spawnPentestAgent.test.ts +72 -2
- package/src/core/agents/offSecAgent/tools/spawnPentestAgent.ts +32 -11
- package/src/core/agents/offSecAgent/tools/spawnPentestSwarm.ts +25 -5
- package/src/core/agents/offSecAgent/tools/threatModelGenerator.ts +1 -0
- package/src/core/agents/offSecAgent/tools/types.ts +6 -1
- package/src/core/agents/offSecAgent/tools/updateTask.ts +5 -1
- package/src/core/agents/offSecAgent/tools/whiteboxCandidates.ts +11 -4
- package/src/core/agents/offSecAgent/types.ts +9 -0
- package/src/core/agents/specialized/codeAgent/agent.test.ts +7 -0
- package/src/core/agents/specialized/codeAgent/agent.ts +9 -2
- package/src/core/agents/specialized/codeAgent/prompts.ts +6 -0
- package/src/core/agents/specialized/pentest/agent.test.ts +116 -0
- package/src/core/agents/specialized/pentest/agent.ts +178 -49
- package/src/core/agents/specialized/pentest/results.test.ts +59 -4
- package/src/core/agents/specialized/whiteboxAttackSurface/endpointDocumentationAgent.ts +1 -0
- package/src/core/ai/ai.operator-context.bench.test.ts +1408 -0
- package/src/core/ai/ai.recorded-context.test.ts +74 -0
- package/src/core/ai/ai.test.ts +54 -0
- package/src/core/ai/ai.ts +312 -82
- package/src/core/ai/compactionTelemetry.test.ts +2 -2
- package/src/core/ai/models/anthropic.ts +6 -0
- package/src/core/ai/models/bedrock.ts +18 -0
- package/src/core/ai/models/claude-capabilities.ts +29 -8
- package/src/core/ai/models/concentrate.ts +52 -0
- package/src/core/ai/models/index.ts +25 -4
- package/src/core/ai/models/models.test.ts +154 -0
- package/src/core/ai/models/openrouter.ts +43 -1
- package/src/core/ai/providers/anthropic-models.test.ts +60 -1
- package/src/core/ai/providers/bedrock-models.test.ts +81 -0
- package/src/core/ai/providers/concentrate.test.ts +31 -0
- package/src/core/ai/providers/concentrate.ts +11 -0
- package/src/core/ai/providers/gateway-models.test.ts +16 -6
- package/src/core/ai/utils.native-rollout-evidence.test.ts +1 -0
- package/src/core/ai/utils.ts +189 -143
- package/src/core/api/blackboxPentest.ts +9 -1
- package/src/core/api/index.ts +9 -0
- package/src/core/api/targetedPentest.ts +19 -5
- package/src/core/config/config.test.ts +35 -0
- package/src/core/config/config.ts +5 -0
- package/src/core/observability/telemetry.test.ts +2 -3
- package/src/core/session/index.ts +5 -0
- package/src/core/session/persistence.test.ts +62 -0
- package/src/core/session/persistence.ts +56 -0
- package/src/core/workflows/fastStrike.seams.test.ts +149 -0
- package/src/core/workflows/fastStrike.test.ts +18 -1
- package/src/core/workflows/fastStrike.ts +103 -11
- package/src/core/workflows/pentest.test.ts +104 -0
- package/src/core/workflows/pentest.ts +110 -7
- package/src/core/workflows/seams.test.ts +13 -0
- package/src/core/workflows/seams.ts +5 -1
- package/src/core/workflows/whiteboxAttackSurface.ts +1 -0
- package/src/tui/components/chat/loading-indicator.tsx +20 -4
- package/src/tui/components/chat/message-list.tsx +1 -0
- package/src/tui/components/commands/__tests__/advanced-settings-lifecycle.tsx +176 -0
- package/src/tui/components/commands/advanced-settings.test.ts +28 -0
- package/src/tui/components/commands/advanced-settings.tsx +237 -19
- package/src/tui/components/operator-dashboard/index.tsx +46 -9
- package/src/tui/components/operator-dashboard/logic.test.ts +106 -0
- package/src/tui/components/operator-dashboard/logic.ts +42 -2
- package/src/tui/components/operator-dashboard/run-events.test.ts +3 -1
- package/src/tui/components/shared/__tests__/tool-renderer-lifecycle.tsx +18 -0
- package/src/tui/components/shared/action-label.test.ts +9 -0
- package/src/tui/components/shared/compacting-icon.tsx +18 -0
- package/src/tui/components/shared/index.ts +1 -0
- package/src/tui/components/shared/tool-registry.ts +1 -0
- package/src/tui/components/shared/tool-renderer.tsx +21 -7
- package/src/tui/context/route.tsx +1 -0
- package/src/tui/keybindings/registry.ts +2 -0
- package/build/agent-6w7egcf1.js +0 -13
- package/build/agent-9zx75feh.js +0 -13
- package/build/pentest-e61hc38s.js +0 -22
- package/build/recordedRunClient-dp032c32.js +0 -11
- package/build/threatModel-jkxfa40s.js +0 -21
|
@@ -35,6 +35,22 @@ const ORIGIN_AUTH_FAILURE_GUIDANCE = `A 401/403 from one resource does not by it
|
|
|
35
35
|
* directly on the parent bus
|
|
36
36
|
*/
|
|
37
37
|
export function spawnPentestAgent(ctx: ToolContext) {
|
|
38
|
+
const externalFindings = ctx.findingPolicy === "external";
|
|
39
|
+
const findingSteps = externalFindings
|
|
40
|
+
? "2. Reports concise evidence-backed findings in its response\n3. Leaves acceptance to the caller's external evaluator"
|
|
41
|
+
: "2. Confirms vulnerabilities with POC scripts\n3. Documents findings to the shared findings registry only when the exploit path and material security impact are proven";
|
|
42
|
+
const findingGuidance = externalFindings
|
|
43
|
+
? "False-positive discipline: report a security weakness only when observations support a material abuse path. Include the affected target, evidence, and impact; do not add a separate in-run finding review."
|
|
44
|
+
: "False-positive discipline: workers should not call document_vulnerability for public/unauthenticated behavior, missing best-practice controls, generic errors, local HTTP deployment context, or other low-signal observations unless they prove a material abuse path such as non-public data access, credentialed cross-origin abuse, state change, denial of service, account takeover, or another concrete exploit chain.";
|
|
45
|
+
const returnGuidance = externalFindings
|
|
46
|
+
? "Returns the worker's summary, objective results, and reported findings so the parent can investigate chains."
|
|
47
|
+
: "Returns the worker's summary, objective results, and finding count — but NOT the findings themselves (those land in the shared registry).";
|
|
48
|
+
const exploratoryGuidance = externalFindings
|
|
49
|
+
? "Spawn a follow-up exploratory worker only when the assigned objective requires broader coverage or a credible untested lead remains. For detection-only work, finish once the requested evidence is sufficient."
|
|
50
|
+
: 'After fanning out one worker per objective, call this tool ONE MORE TIME for a final "exploratory" worker. Pass it the URLs of any suspicious behaviors discovered by earlier workers and ask it to chain findings together and probe for additional, unspecified vulnerabilities.';
|
|
51
|
+
const objectiveGuidance = externalFindings
|
|
52
|
+
? "Pass one focused objective per worker. A follow-up objective may cover a concrete chain or untested lead when the assignment calls for it."
|
|
53
|
+
: "For per-objective fan-out, pass exactly one objective. For the final exploratory pass, pass a single composite objective describing chaining + exploration.";
|
|
38
54
|
const targetScopeGuidance =
|
|
39
55
|
ctx.systemScope && ctx.systemScope.memberHosts.length > 0
|
|
40
56
|
? `For a cross-service follow-up, you may instead use a full URL on one of these authorized System Scope members: ${ctx.systemScope.memberHosts.join(", ")}.`
|
|
@@ -51,18 +67,17 @@ Authentication state is origin-specific. Before spawning a cross-service worker
|
|
|
51
67
|
|
|
52
68
|
Each spawn produces an independent TargetedPentestAgent that:
|
|
53
69
|
1. Tests the assigned objective(s) deeply (execute_command, http_request, browser tools)
|
|
54
|
-
|
|
55
|
-
3. Documents findings to the shared findings registry only when the exploit path and material security impact are proven
|
|
70
|
+
${findingSteps}
|
|
56
71
|
|
|
57
72
|
Cloned browser session — the worker gets its OWN isolated Chromium instance, but it is seeded at spawn time with a snapshot of your current cookies and localStorage. If you've already authenticated in your browser (or recon set up useful state), the worker starts already authenticated for the same origins. Critically, the worker's browser is a COPY: its navigations, form fills, \`localStorage.setItem\`, alerts, and any other browser-side mutations stay inside the worker's own browser and do NOT leak back to you or to sibling workers. If you want state to be visible to the next worker, set it up in YOUR browser before spawning.
|
|
58
73
|
|
|
59
74
|
The worker has no further spawn capability — it cannot recursively spawn other pentest agents.
|
|
60
75
|
|
|
61
|
-
|
|
76
|
+
${exploratoryGuidance}
|
|
62
77
|
|
|
63
|
-
|
|
78
|
+
${findingGuidance}
|
|
64
79
|
|
|
65
|
-
|
|
80
|
+
${returnGuidance}`,
|
|
66
81
|
inputSchema: z.object({
|
|
67
82
|
name: z
|
|
68
83
|
.string()
|
|
@@ -77,9 +92,7 @@ Returns the worker's summary, objective results, and finding count — but NOT t
|
|
|
77
92
|
objectives: z
|
|
78
93
|
.array(z.string())
|
|
79
94
|
.min(1)
|
|
80
|
-
.describe(
|
|
81
|
-
"Testing objectives for this worker. For per-objective fan-out, pass exactly one objective. For the final exploratory pass, pass a single composite objective describing chaining + exploration.",
|
|
82
|
-
),
|
|
95
|
+
.describe(`Testing objectives for this worker. ${objectiveGuidance}`),
|
|
83
96
|
context: z
|
|
84
97
|
.string()
|
|
85
98
|
.optional()
|
|
@@ -151,7 +164,7 @@ Returns the worker's summary, objective results, and finding count — but NOT t
|
|
|
151
164
|
const spawner = ctx.subagentSpawner;
|
|
152
165
|
let subagentId = "";
|
|
153
166
|
try {
|
|
154
|
-
const { findings, objectiveResults } =
|
|
167
|
+
const { findings, reportedFindings, objectiveResults } =
|
|
155
168
|
await spawner.spawn<PentestResult>({
|
|
156
169
|
spec: {
|
|
157
170
|
type: "pentest",
|
|
@@ -170,6 +183,7 @@ Returns the worker's summary, objective results, and finding count — but NOT t
|
|
|
170
183
|
browserSession: sharedBrowserSession,
|
|
171
184
|
},
|
|
172
185
|
runtime: {
|
|
186
|
+
findingPolicy: ctx.findingPolicy,
|
|
173
187
|
session: ctx.session,
|
|
174
188
|
model: ctx.model,
|
|
175
189
|
authConfig: ctx.authConfig,
|
|
@@ -203,9 +217,16 @@ Returns the worker's summary, objective results, and finding count — but NOT t
|
|
|
203
217
|
return {
|
|
204
218
|
success: true,
|
|
205
219
|
subagentId,
|
|
206
|
-
findingsCount:
|
|
220
|
+
findingsCount: externalFindings
|
|
221
|
+
? (reportedFindings?.length ?? 0)
|
|
222
|
+
: findings.length,
|
|
223
|
+
...(externalFindings
|
|
224
|
+
? { reportedFindings: reportedFindings ?? [] }
|
|
225
|
+
: {}),
|
|
207
226
|
objectiveResults,
|
|
208
|
-
message:
|
|
227
|
+
message: externalFindings
|
|
228
|
+
? `Worker '${name}' complete. Reported ${reportedFindings?.length ?? 0} finding(s) for external evaluation.`
|
|
229
|
+
: `Worker '${name}' complete. Documented ${findings.length} finding(s).`,
|
|
209
230
|
};
|
|
210
231
|
} catch (error) {
|
|
211
232
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -21,15 +21,21 @@ const log = scopedLogger(() => createLogger("spawn-pentest-swarm"));
|
|
|
21
21
|
* after recon, it calls this tool to fan-out testing to sub-agents.
|
|
22
22
|
*/
|
|
23
23
|
export function spawnPentestSwarm(ctx: ToolContext) {
|
|
24
|
+
const externalFindings = ctx.findingPolicy === "external";
|
|
25
|
+
const findingSteps = externalFindings
|
|
26
|
+
? "2. Surface concise evidence-backed findings in its response\n3. Leave finding acceptance to the caller's evaluator"
|
|
27
|
+
: "2. Create POC scripts for confirmed vulnerabilities\n3. Document findings with evidence and remediation";
|
|
28
|
+
const findingDestination = externalFindings
|
|
29
|
+
? "Reported findings are returned to this caller; no native finding review runs."
|
|
30
|
+
: "All findings are written to the shared session directory.";
|
|
24
31
|
return tool({
|
|
25
32
|
description: `Launch a swarm of targeted pentest sub-agents to test multiple targets in parallel.
|
|
26
33
|
|
|
27
34
|
Call this AFTER completing attack surface discovery. Each entry in 'targets' spawns an independent TargetedPentestAgent that will:
|
|
28
35
|
1. Test the specified target for the given objectives
|
|
29
|
-
|
|
30
|
-
3. Document findings with evidence and remediation
|
|
36
|
+
${findingSteps}
|
|
31
37
|
|
|
32
|
-
|
|
38
|
+
${findingDestination}
|
|
33
39
|
|
|
34
40
|
Pass every target you want tested — the swarm handles concurrency automatically.`,
|
|
35
41
|
inputSchema: z.object({
|
|
@@ -92,6 +98,7 @@ Pass every target you want tested — the swarm handles concurrency automaticall
|
|
|
92
98
|
);
|
|
93
99
|
|
|
94
100
|
const swarmResults = await runPentestSwarm({
|
|
101
|
+
findingPolicy: ctx.findingPolicy,
|
|
95
102
|
targets,
|
|
96
103
|
model: ctx.model!,
|
|
97
104
|
session: ctx.session,
|
|
@@ -107,6 +114,7 @@ Pass every target you want tested — the swarm handles concurrency automaticall
|
|
|
107
114
|
openAIReasoningEffort: ctx.openAIReasoningEffort,
|
|
108
115
|
display: ctx.display,
|
|
109
116
|
hooks: {
|
|
117
|
+
findingPolicy: ctx.findingPolicy,
|
|
110
118
|
backends: ctx.backends,
|
|
111
119
|
subagentSpawner: ctx.subagentSpawner,
|
|
112
120
|
languageModelMiddleware: ctx.languageModelMiddleware,
|
|
@@ -120,7 +128,11 @@ Pass every target you want tested — the swarm handles concurrency automaticall
|
|
|
120
128
|
|
|
121
129
|
// Aggregate
|
|
122
130
|
const totalFindings = swarmResults.reduce(
|
|
123
|
-
(sum, r) =>
|
|
131
|
+
(sum, r) =>
|
|
132
|
+
sum +
|
|
133
|
+
(externalFindings
|
|
134
|
+
? (r?.reportedFindings?.length ?? 0)
|
|
135
|
+
: (r?.findings.length ?? 0)),
|
|
124
136
|
0,
|
|
125
137
|
);
|
|
126
138
|
const failedCount = swarmResults.filter((r) => r == null).length;
|
|
@@ -132,7 +144,15 @@ Pass every target you want tested — the swarm handles concurrency automaticall
|
|
|
132
144
|
failedTargets: failedCount,
|
|
133
145
|
results: targets.map((t, i) => ({
|
|
134
146
|
target: t.target,
|
|
135
|
-
findingsCount:
|
|
147
|
+
findingsCount: externalFindings
|
|
148
|
+
? (swarmResults[i]?.reportedFindings?.length ?? 0)
|
|
149
|
+
: (swarmResults[i]?.findings.length ?? 0),
|
|
150
|
+
...(externalFindings
|
|
151
|
+
? {
|
|
152
|
+
reportedFindings: swarmResults[i]?.reportedFindings ?? [],
|
|
153
|
+
objectiveResults: swarmResults[i]?.objectiveResults ?? [],
|
|
154
|
+
}
|
|
155
|
+
: {}),
|
|
136
156
|
error: swarmResults[i] == null ? "Agent failed" : undefined,
|
|
137
157
|
})),
|
|
138
158
|
message: `Swarm complete. ${totalFindings} findings across ${total} targets. ${
|
|
@@ -22,7 +22,11 @@ import type { ToolBackends } from "../../../tools/backends/types";
|
|
|
22
22
|
import type { GrpcPentestContext } from "../../specialized/attackSurface/grpcSchema";
|
|
23
23
|
import type { SubagentSpawner } from "../subagentSpawner";
|
|
24
24
|
import type { StepTraceWriter } from "../trace";
|
|
25
|
-
import type {
|
|
25
|
+
import type {
|
|
26
|
+
FindingPolicy,
|
|
27
|
+
StreamIdFactory,
|
|
28
|
+
SystemPentestScope,
|
|
29
|
+
} from "../types";
|
|
26
30
|
import type { CallbackListenerRegistry } from "./callbackListener";
|
|
27
31
|
import type { EmailAdapterResolver } from "./email/adapters";
|
|
28
32
|
import type { PerCommandShell } from "./perCommandShell";
|
|
@@ -38,6 +42,7 @@ import type { SmsInbox } from "./smsInbox";
|
|
|
38
42
|
* session or agent internals directly.
|
|
39
43
|
*/
|
|
40
44
|
export type ToolContext = {
|
|
45
|
+
findingPolicy?: FindingPolicy;
|
|
41
46
|
attackSurfaceArtifactsPath?: string;
|
|
42
47
|
|
|
43
48
|
/** Session providing paths for findings, POCs, logs, scratchpad, etc. */
|
|
@@ -34,12 +34,16 @@ type UpdateTaskResult = {
|
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
export function updateTask(ctx: ToolContext) {
|
|
37
|
+
const completedGuidance =
|
|
38
|
+
ctx.findingPolicy === "external"
|
|
39
|
+
? "vulnerability identified with supporting evidence, OR conclusively determined the endpoint is not vulnerable / materiality is not met for this technique."
|
|
40
|
+
: "vulnerability found and documented via document_vulnerability, OR conclusively determined the endpoint is not vulnerable / materiality is not met for this technique. Documentation is NOT required to complete a non-vulnerable task.";
|
|
37
41
|
return tool({
|
|
38
42
|
description: `Update a task's status and record results.
|
|
39
43
|
|
|
40
44
|
Status transitions:
|
|
41
45
|
- "in_progress" — Set when you START testing a task. Pick one pending task at a time.
|
|
42
|
-
- "completed" — Set when testing is done:
|
|
46
|
+
- "completed" — Set when testing is done: ${completedGuidance}
|
|
43
47
|
- "failed" — Set when the technique didn't work: rate limited, blocked by WAF, dead end, or approach not applicable. Then create a NEW task with an alternative technique.
|
|
44
48
|
|
|
45
49
|
Always include a result or observation explaining the outcome.`,
|
|
@@ -47,12 +47,15 @@ const CandidateStateSchema = z.enum([
|
|
|
47
47
|
]);
|
|
48
48
|
|
|
49
49
|
export function createWhiteboxCandidate(ctx: ToolContext) {
|
|
50
|
+
const candidateGuidance =
|
|
51
|
+
ctx.findingPolicy === "external"
|
|
52
|
+
? "for the caller's final evaluation. Candidates keep hypotheses, source-to-sink traces, scanner artifacts, fuzzing logs, and verification status separate from reported findings."
|
|
53
|
+
: "for document_vulnerability. Candidates keep hypotheses, source-to-sink traces,\nscanner artifacts, fuzzing logs, and verification status out of official findings.";
|
|
50
54
|
return tool({
|
|
51
55
|
description: `Create a whitebox vulnerability candidate.
|
|
52
56
|
|
|
53
57
|
Use this for plausible source-code findings that are not yet confirmed enough
|
|
54
|
-
|
|
55
|
-
scanner artifacts, fuzzing logs, and verification status out of official findings.`,
|
|
58
|
+
${candidateGuidance}`,
|
|
56
59
|
inputSchema: z.object({
|
|
57
60
|
title: z.string(),
|
|
58
61
|
vulnerabilityClass: z.string(),
|
|
@@ -81,7 +84,9 @@ scanner artifacts, fuzzing logs, and verification status out of official finding
|
|
|
81
84
|
artifactPaths: candidate.artifacts.map((artifact) => artifact.path),
|
|
82
85
|
nextActions: [
|
|
83
86
|
"Move candidate to investigating after reading source context.",
|
|
84
|
-
|
|
87
|
+
ctx.findingPolicy === "external"
|
|
88
|
+
? "Gather the evidence required by the caller before reporting this candidate."
|
|
89
|
+
: "Attempt a reproducer or dynamic verification before document_vulnerability.",
|
|
85
90
|
],
|
|
86
91
|
truncated: false,
|
|
87
92
|
};
|
|
@@ -132,7 +137,9 @@ State machine: hypothesis -> investigating -> repro_attempted -> confirmed (requ
|
|
|
132
137
|
nextActions:
|
|
133
138
|
candidate.state === "confirmed"
|
|
134
139
|
? [
|
|
135
|
-
|
|
140
|
+
ctx.findingPolicy === "external"
|
|
141
|
+
? "Report only the evidence-backed conclusion to the caller."
|
|
142
|
+
: "Use document_vulnerability only if the proof demonstrates exploitability.",
|
|
136
143
|
]
|
|
137
144
|
: [
|
|
138
145
|
"Continue tracing or verification based on the candidate state.",
|
|
@@ -107,6 +107,9 @@ export type Finding = Omit<
|
|
|
107
107
|
/** Agent operating mode that controls which tools are available. */
|
|
108
108
|
export type AgentMode = "default" | "plan" | "fast-strike";
|
|
109
109
|
|
|
110
|
+
/** Selects Apex's native finding judge or a caller-owned evaluation boundary. */
|
|
111
|
+
export type FindingPolicy = "native" | "external";
|
|
112
|
+
|
|
110
113
|
/** Structured authorization envelope for a multi-application System pentest. */
|
|
111
114
|
export type SystemPentestScope = {
|
|
112
115
|
systemId: string;
|
|
@@ -135,6 +138,9 @@ export type StreamIdFactory = (context: StreamIdFactoryContext) => string;
|
|
|
135
138
|
* by forgetting to list it in a hand-written constructor.
|
|
136
139
|
*/
|
|
137
140
|
export interface AgentHooks {
|
|
141
|
+
/** External grading omits Apex's finding tool and leaves acceptance to the caller. */
|
|
142
|
+
findingPolicy?: FindingPolicy;
|
|
143
|
+
|
|
138
144
|
/** Opt-in shared backend directory for incremental recon endpoint artifacts. */
|
|
139
145
|
attackSurfaceArtifactsPath?: string;
|
|
140
146
|
|
|
@@ -351,11 +357,14 @@ export type OffensiveSecurityAgentInput<TResult = void> = AgentHooks & {
|
|
|
351
357
|
*
|
|
352
358
|
* Receives the completed `StreamTextResult` — at this point all lazy
|
|
353
359
|
* properties (`.steps`, `.text`, `.response`, etc.) are resolved.
|
|
360
|
+
* When the response tool executes, the optional second argument is its
|
|
361
|
+
* schema-validated result. It is absent if no response was submitted.
|
|
354
362
|
*
|
|
355
363
|
* If omitted, `consume()` returns `void`.
|
|
356
364
|
*/
|
|
357
365
|
resolveResult?: (
|
|
358
366
|
streamResult: StreamTextResult<ToolSet, never>,
|
|
367
|
+
validatedResponse?: unknown,
|
|
359
368
|
) => TResult | Promise<TResult>;
|
|
360
369
|
|
|
361
370
|
/** The subagent ID if this agent is a subagent */
|
|
@@ -27,6 +27,7 @@ function makeAgent(overrides: Record<string, unknown> = {}) {
|
|
|
27
27
|
return superInputs[superInputs.length - 1] as {
|
|
28
28
|
activeTools: string[];
|
|
29
29
|
fileWorkspaceRoot?: string;
|
|
30
|
+
system?: string;
|
|
30
31
|
};
|
|
31
32
|
}
|
|
32
33
|
|
|
@@ -86,4 +87,10 @@ describe("CodeAgent stays a read-focused analysis agent", () => {
|
|
|
86
87
|
expect(input.activeTools).toContain("response");
|
|
87
88
|
expect(input.activeTools).toContain("read_file");
|
|
88
89
|
});
|
|
90
|
+
|
|
91
|
+
it("does not tell an externally graded code child to use the native finding tool", () => {
|
|
92
|
+
const input = makeAgent({ findingPolicy: "external" });
|
|
93
|
+
expect(input.system).toContain("sink-first work");
|
|
94
|
+
expect(input.system).not.toContain("document_vulnerability");
|
|
95
|
+
});
|
|
89
96
|
});
|
|
@@ -3,7 +3,10 @@ import type { z } from "zod";
|
|
|
3
3
|
import { AgentRuntime } from "../../agentRuntime";
|
|
4
4
|
import { type AgentDefinition, defineAgent } from "../../defineAgent";
|
|
5
5
|
import type { SpecializedAgentInput } from "../../offSecAgent";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
CODE_AGENT_EXTERNAL_SYSTEM_PROMPT,
|
|
8
|
+
CODE_AGENT_SYSTEM_PROMPT,
|
|
9
|
+
} from "./prompts";
|
|
7
10
|
|
|
8
11
|
const CODE_AGENT_TOOLS: string[] = [
|
|
9
12
|
"read_file",
|
|
@@ -91,7 +94,11 @@ export function codeAgentDefinition<TResult = void>(): AgentDefinition<
|
|
|
91
94
|
return defineAgent<CodeAgentInput<TResult>, TResult>({
|
|
92
95
|
name: "code-agent",
|
|
93
96
|
role: "worker",
|
|
94
|
-
system: (opts) =>
|
|
97
|
+
system: (opts) =>
|
|
98
|
+
opts.system ??
|
|
99
|
+
(opts.findingPolicy === "external"
|
|
100
|
+
? CODE_AGENT_EXTERNAL_SYSTEM_PROMPT
|
|
101
|
+
: CODE_AGENT_SYSTEM_PROMPT),
|
|
95
102
|
activeTools: (opts) => {
|
|
96
103
|
let activeTools = [...CODE_AGENT_TOOLS];
|
|
97
104
|
if (opts.excludeTools?.length) {
|
|
@@ -46,3 +46,9 @@ Prefer catalog + code_query for sink-first work; use candidates to separate unve
|
|
|
46
46
|
4. **Stay focused** — stick to your objective. Don't wander into unrelated areas of the codebase.
|
|
47
47
|
5. **Be thorough** — don't stop at the first match. Make sure you've covered everything relevant to the objective.
|
|
48
48
|
`;
|
|
49
|
+
|
|
50
|
+
export const CODE_AGENT_EXTERNAL_SYSTEM_PROMPT =
|
|
51
|
+
CODE_AGENT_SYSTEM_PROMPT.replace(
|
|
52
|
+
"use candidates to separate unverified ideas from `document_vulnerability`",
|
|
53
|
+
"use candidates to separate unverified ideas from evidence-backed findings returned to the caller",
|
|
54
|
+
);
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
buildPentestActiveTools,
|
|
26
26
|
buildPentestPrompt,
|
|
27
27
|
buildPentestSystemPrompt,
|
|
28
|
+
PentestExternalResponseSchema,
|
|
28
29
|
PentestResponseSchema,
|
|
29
30
|
resolvePentestAgentRole,
|
|
30
31
|
resolvePentestBrowserEngine,
|
|
@@ -38,6 +39,121 @@ const session = {
|
|
|
38
39
|
};
|
|
39
40
|
|
|
40
41
|
describe("TargetedPentestAgent prompt contracts", () => {
|
|
42
|
+
it.each([
|
|
43
|
+
["worker", "default"],
|
|
44
|
+
["orchestrator", "default"],
|
|
45
|
+
["worker", "fast-strike"],
|
|
46
|
+
] as const)("keeps %s %s discovery tools and removes native finding guidance under external grading", (role, mode) => {
|
|
47
|
+
const tools = buildPentestActiveTools(role, session, "external");
|
|
48
|
+
const system = buildPentestSystemPrompt(
|
|
49
|
+
session,
|
|
50
|
+
role,
|
|
51
|
+
mode,
|
|
52
|
+
undefined,
|
|
53
|
+
undefined,
|
|
54
|
+
"external",
|
|
55
|
+
);
|
|
56
|
+
const assignment = buildPentestPrompt(
|
|
57
|
+
"https://example.com/api",
|
|
58
|
+
["Find security weaknesses"],
|
|
59
|
+
session,
|
|
60
|
+
undefined,
|
|
61
|
+
undefined,
|
|
62
|
+
undefined,
|
|
63
|
+
undefined,
|
|
64
|
+
role,
|
|
65
|
+
undefined,
|
|
66
|
+
undefined,
|
|
67
|
+
mode,
|
|
68
|
+
"external",
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
expect(tools).toContain("http_request");
|
|
72
|
+
expect(tools).toContain("browser_navigate");
|
|
73
|
+
expect(tools).not.toContain("document_vulnerability");
|
|
74
|
+
expect(system + assignment).toContain("reportedFindings");
|
|
75
|
+
expect(system + assignment).not.toContain("document_vulnerability");
|
|
76
|
+
expect(system + assignment).not.toContain("validation judge");
|
|
77
|
+
expect(system + assignment).not.toContain("ONE FINAL TIME");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("requires structured external findings without a native documentation count", () => {
|
|
81
|
+
const response = {
|
|
82
|
+
summary: "No issue found",
|
|
83
|
+
objectivesCovered: ["Find security weaknesses"],
|
|
84
|
+
objectiveResults: [
|
|
85
|
+
{ objective: "Find security weaknesses", completed: true },
|
|
86
|
+
],
|
|
87
|
+
reportedFindings: [],
|
|
88
|
+
};
|
|
89
|
+
expect(PentestExternalResponseSchema.safeParse(response).success).toBe(
|
|
90
|
+
true,
|
|
91
|
+
);
|
|
92
|
+
expect(
|
|
93
|
+
PentestExternalResponseSchema.safeParse({
|
|
94
|
+
...response,
|
|
95
|
+
reportedFindings: undefined,
|
|
96
|
+
}).success,
|
|
97
|
+
).toBe(false);
|
|
98
|
+
expect(PentestResponseSchema.safeParse(response).success).toBe(false);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it.each([
|
|
102
|
+
["worker", "default"],
|
|
103
|
+
["worker", "fast-strike"],
|
|
104
|
+
["orchestrator", "default"],
|
|
105
|
+
] as const)("preserves the exfiltration objective for external %s %s runs", (role, mode) => {
|
|
106
|
+
const exfilSession = {
|
|
107
|
+
...session,
|
|
108
|
+
config: { exfilMode: true },
|
|
109
|
+
};
|
|
110
|
+
const system = buildPentestSystemPrompt(
|
|
111
|
+
exfilSession,
|
|
112
|
+
role,
|
|
113
|
+
mode,
|
|
114
|
+
undefined,
|
|
115
|
+
undefined,
|
|
116
|
+
"external",
|
|
117
|
+
);
|
|
118
|
+
const assignment = buildPentestPrompt(
|
|
119
|
+
"https://example.com/api",
|
|
120
|
+
["Test the assigned target"],
|
|
121
|
+
exfilSession,
|
|
122
|
+
undefined,
|
|
123
|
+
undefined,
|
|
124
|
+
undefined,
|
|
125
|
+
undefined,
|
|
126
|
+
role,
|
|
127
|
+
undefined,
|
|
128
|
+
undefined,
|
|
129
|
+
mode,
|
|
130
|
+
"external",
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
expect(system).toContain("FLAG{...}");
|
|
134
|
+
expect(system).toContain("demonstrate extraction before calling response");
|
|
135
|
+
expect(assignment).toContain("locate and extract FLAG{...}");
|
|
136
|
+
expect(system + assignment).not.toContain("document_vulnerability");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("keeps configured source inspection available to an external Fast Strike worker", () => {
|
|
140
|
+
const scopedSession = {
|
|
141
|
+
...session,
|
|
142
|
+
config: { codebasePath: "/workspace/repository" },
|
|
143
|
+
};
|
|
144
|
+
const prompt = buildPentestSystemPrompt(
|
|
145
|
+
scopedSession,
|
|
146
|
+
"worker",
|
|
147
|
+
"fast-strike",
|
|
148
|
+
undefined,
|
|
149
|
+
undefined,
|
|
150
|
+
"external",
|
|
151
|
+
);
|
|
152
|
+
expect(prompt).toContain("/workspace/repository");
|
|
153
|
+
expect(prompt).toContain("Read-only Source Access");
|
|
154
|
+
expect(prompt).not.toContain("Source Code Prohibition");
|
|
155
|
+
});
|
|
156
|
+
|
|
41
157
|
it("forces Fast Strike into the single-worker role", () => {
|
|
42
158
|
expect(resolvePentestAgentRole("fast-strike", "orchestrator")).toBe(
|
|
43
159
|
"worker",
|