@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
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
type AgentMode,
|
|
18
18
|
createReportErrorTool,
|
|
19
19
|
type Finding,
|
|
20
|
+
type FindingPolicy,
|
|
20
21
|
PentestReportedError,
|
|
21
22
|
REPORT_ERROR_TOOL_NAME,
|
|
22
23
|
type ReportedError,
|
|
@@ -132,6 +133,8 @@ export interface PentestAgentInput extends SpecializedAgentInput {
|
|
|
132
133
|
export interface PentestResult {
|
|
133
134
|
/** All findings discovered during the run */
|
|
134
135
|
findings: Finding[];
|
|
136
|
+
/** Concise findings handed to an external evaluator, without native judging. */
|
|
137
|
+
reportedFindings?: string[];
|
|
135
138
|
/** Absolute path to the session's findings directory */
|
|
136
139
|
findingsPath: string;
|
|
137
140
|
/** Absolute path to the session's POC scripts directory */
|
|
@@ -198,6 +201,25 @@ export const PentestResponseSchema = z.object({
|
|
|
198
201
|
),
|
|
199
202
|
});
|
|
200
203
|
|
|
204
|
+
export const PentestExternalResponseSchema = PentestResponseSchema.omit({
|
|
205
|
+
findingsDocumented: true,
|
|
206
|
+
}).extend({
|
|
207
|
+
objectiveResults: z
|
|
208
|
+
.array(ObjectiveResultSchema)
|
|
209
|
+
.describe(
|
|
210
|
+
"Status of each assigned objective, including findings or limitations.",
|
|
211
|
+
),
|
|
212
|
+
reportedFindings: z
|
|
213
|
+
.array(z.string())
|
|
214
|
+
.describe(
|
|
215
|
+
"One concise evidence-backed finding per vulnerability, including the affected target and security impact. An empty array is valid.",
|
|
216
|
+
),
|
|
217
|
+
noFindingsReason: z
|
|
218
|
+
.string()
|
|
219
|
+
.optional()
|
|
220
|
+
.describe("If no findings are reported, explain why."),
|
|
221
|
+
});
|
|
222
|
+
|
|
201
223
|
// ---------------------------------------------------------------------------
|
|
202
224
|
// PentestAgent
|
|
203
225
|
// ---------------------------------------------------------------------------
|
|
@@ -257,6 +279,7 @@ export const targetedPentestWorker = defineAgent<
|
|
|
257
279
|
state.mode,
|
|
258
280
|
opts.systemScope,
|
|
259
281
|
state.helperWorkspaceRoot,
|
|
282
|
+
opts.findingPolicy,
|
|
260
283
|
),
|
|
261
284
|
// Workers hold the native helper file tools; this root scopes them so they
|
|
262
285
|
// can build and inspect helper scripts without ever reaching target source.
|
|
@@ -272,8 +295,15 @@ export const targetedPentestWorker = defineAgent<
|
|
|
272
295
|
systemScope: (opts) => opts.systemScope,
|
|
273
296
|
mode: (_opts, state) => state.mode,
|
|
274
297
|
activeTools: (opts, state) =>
|
|
275
|
-
buildPentestActiveTools(
|
|
276
|
-
|
|
298
|
+
buildPentestActiveTools(
|
|
299
|
+
state.effectiveRole,
|
|
300
|
+
opts.session,
|
|
301
|
+
opts.findingPolicy,
|
|
302
|
+
),
|
|
303
|
+
responseSchema: (opts) =>
|
|
304
|
+
opts.findingPolicy === "external"
|
|
305
|
+
? PentestExternalResponseSchema
|
|
306
|
+
: PentestResponseSchema,
|
|
277
307
|
prompt: (opts, state) =>
|
|
278
308
|
buildPentestPrompt(
|
|
279
309
|
opts.target,
|
|
@@ -293,16 +323,17 @@ export const targetedPentestWorker = defineAgent<
|
|
|
293
323
|
opts.session.credentialManager?.formatForPrompt(),
|
|
294
324
|
opts.grpc,
|
|
295
325
|
state.mode,
|
|
326
|
+
opts.findingPolicy,
|
|
296
327
|
),
|
|
297
|
-
extraTools: (
|
|
328
|
+
extraTools: (opts, state) => ({
|
|
298
329
|
[REPORT_ERROR_TOOL_NAME]: createReportErrorTool((err) => {
|
|
299
330
|
state.reportedError = err;
|
|
300
|
-
}),
|
|
331
|
+
}, opts.findingPolicy),
|
|
301
332
|
}),
|
|
302
333
|
// Ending the run on `report_error` is merged with the base class's
|
|
303
334
|
// `hasToolCall("response")` stop condition.
|
|
304
335
|
stopWhen: () => hasToolCall(REPORT_ERROR_TOOL_NAME),
|
|
305
|
-
resolveResult: async (opts, state, streamResult) => {
|
|
336
|
+
resolveResult: async (opts, state, streamResult, validatedResponse) => {
|
|
306
337
|
// A reported blocking error takes precedence: surface it as a thrown
|
|
307
338
|
// error so the run is treated as a failure rather than a completion.
|
|
308
339
|
if (state.reportedError) {
|
|
@@ -311,35 +342,50 @@ export const targetedPentestWorker = defineAgent<
|
|
|
311
342
|
|
|
312
343
|
let objectiveResults: ObjectiveResult[] | undefined;
|
|
313
344
|
let newObjectives: string[] | undefined;
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
345
|
+
let reportedFindings: string[] | undefined;
|
|
346
|
+
|
|
347
|
+
if (opts.findingPolicy === "external") {
|
|
348
|
+
// The response tool passes this value only after schema validation and
|
|
349
|
+
// execution. Raw assistant tool-call messages may contain rejected input.
|
|
350
|
+
const parsed = PentestExternalResponseSchema.safeParse(validatedResponse);
|
|
351
|
+
if (!parsed.success) {
|
|
352
|
+
throw new Error(
|
|
353
|
+
"Externally graded pentest ended without a structured findings response",
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
objectiveResults = parsed.data.objectiveResults;
|
|
357
|
+
newObjectives = parsed.data.newObjectives;
|
|
358
|
+
reportedFindings = parsed.data.reportedFindings;
|
|
359
|
+
} else {
|
|
360
|
+
try {
|
|
361
|
+
const response = await streamResult.response;
|
|
362
|
+
for (const msg of response.messages ?? []) {
|
|
363
|
+
if (msg.role !== "assistant" || !Array.isArray(msg.content)) continue;
|
|
364
|
+
for (const part of msg.content) {
|
|
365
|
+
if (
|
|
366
|
+
part.type === "tool-call" &&
|
|
367
|
+
part.toolName === "response" &&
|
|
368
|
+
part.input
|
|
369
|
+
) {
|
|
370
|
+
// createResponseTool wraps the schema under `result`.
|
|
371
|
+
const input = part.input as {
|
|
372
|
+
result?: {
|
|
373
|
+
objectiveResults?: ObjectiveResult[];
|
|
374
|
+
newObjectives?: string[];
|
|
375
|
+
};
|
|
330
376
|
};
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
377
|
+
if (input.result?.objectiveResults) {
|
|
378
|
+
objectiveResults = input.result.objectiveResults;
|
|
379
|
+
}
|
|
380
|
+
if (input.result?.newObjectives) {
|
|
381
|
+
newObjectives = input.result.newObjectives;
|
|
382
|
+
}
|
|
337
383
|
}
|
|
338
384
|
}
|
|
339
385
|
}
|
|
386
|
+
} catch {
|
|
387
|
+
// Best-effort native metadata; findings are loaded separately.
|
|
340
388
|
}
|
|
341
|
-
} catch {
|
|
342
|
-
// Best-effort; objectiveResults / newObjectives will be undefined
|
|
343
389
|
}
|
|
344
390
|
|
|
345
391
|
const sandboxed = opts.backends?.sandboxed === true;
|
|
@@ -347,13 +393,15 @@ export const targetedPentestWorker = defineAgent<
|
|
|
347
393
|
? `${SANDBOX_ARTIFACTS_ROOT}/findings`
|
|
348
394
|
: opts.session.findingsPath;
|
|
349
395
|
const findings =
|
|
350
|
-
|
|
351
|
-
?
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
396
|
+
opts.findingPolicy === "external"
|
|
397
|
+
? []
|
|
398
|
+
: sandboxed && opts.backends
|
|
399
|
+
? await loadSandboxFindings(
|
|
400
|
+
opts.backends,
|
|
401
|
+
findingsPath,
|
|
402
|
+
opts.abortSignal,
|
|
403
|
+
)
|
|
404
|
+
: loadFindings(findingsPath);
|
|
357
405
|
|
|
358
406
|
return {
|
|
359
407
|
findings,
|
|
@@ -363,6 +411,7 @@ export const targetedPentestWorker = defineAgent<
|
|
|
363
411
|
: opts.session.pocsPath,
|
|
364
412
|
objectiveResults,
|
|
365
413
|
newObjectives,
|
|
414
|
+
reportedFindings,
|
|
366
415
|
};
|
|
367
416
|
},
|
|
368
417
|
});
|
|
@@ -818,8 +867,14 @@ ${SECTION_STATE_CHECKPOINTING}`;
|
|
|
818
867
|
* destructive testing is not authorized (default), the section tells the agent
|
|
819
868
|
* the operations the guard blocks so it doesn't waste turns attempting them.
|
|
820
869
|
*/
|
|
821
|
-
function destructiveSection(
|
|
822
|
-
|
|
870
|
+
function destructiveSection(
|
|
871
|
+
allow: boolean | undefined,
|
|
872
|
+
findingPolicy: FindingPolicy = "native",
|
|
873
|
+
): string {
|
|
874
|
+
if (allow) return SECTION_DESTRUCTIVE_ALLOWED;
|
|
875
|
+
return findingPolicy === "external"
|
|
876
|
+
? SECTION_DESTRUCTIVE_BLOCKED.replace(" and document_vulnerability", "")
|
|
877
|
+
: SECTION_DESTRUCTIVE_BLOCKED;
|
|
823
878
|
}
|
|
824
879
|
|
|
825
880
|
/**
|
|
@@ -851,9 +906,11 @@ export function buildPentestSystemPrompt(
|
|
|
851
906
|
mode: TargetedPentestMode = "default",
|
|
852
907
|
systemScope?: SystemPentestScope,
|
|
853
908
|
helperWorkspaceRoot?: string,
|
|
909
|
+
findingPolicy: FindingPolicy = "native",
|
|
854
910
|
): string {
|
|
855
911
|
const destructive = destructiveSection(
|
|
856
912
|
session.config?.allowDestructiveActions,
|
|
913
|
+
findingPolicy,
|
|
857
914
|
);
|
|
858
915
|
const rateLimitTesting = rateLimitTestingSection(
|
|
859
916
|
session.config?.allowRateLimitTesting,
|
|
@@ -863,7 +920,7 @@ export function buildPentestSystemPrompt(
|
|
|
863
920
|
const guardrails = `${destructive}\n\n${rateLimitTesting}\n\n${SECTION_SIDE_EFFECT_SAFETY}`;
|
|
864
921
|
const systemSection =
|
|
865
922
|
systemScope && systemScope.memberHosts.length > 0
|
|
866
|
-
? `\n\n${SECTION_SYSTEM_SCOPE(systemScope.memberHosts)}`
|
|
923
|
+
? `\n\n${SECTION_SYSTEM_SCOPE(systemScope.memberHosts, findingPolicy)}`
|
|
867
924
|
: "";
|
|
868
925
|
// Fast Strike exposes source-assessment tools. Only an explicitly
|
|
869
926
|
// configured repository lifts the targeted worker's black-box restriction.
|
|
@@ -880,6 +937,47 @@ export function buildPentestSystemPrompt(
|
|
|
880
937
|
Boolean(sourceRoot),
|
|
881
938
|
)}`;
|
|
882
939
|
|
|
940
|
+
if (findingPolicy === "external") {
|
|
941
|
+
const sourcePolicy = sourceRoot
|
|
942
|
+
? `Read-only Source Access:\n- The configured repository at ${sourceRoot} is in scope for read-only analysis. Use source-assessment tools or execute_command to inspect it. Keep generated helpers in the file workspace; do not modify target source.`
|
|
943
|
+
: SECTION_SOURCE_CODE_PROHIBITION;
|
|
944
|
+
const roleMethod =
|
|
945
|
+
mode === "fast-strike"
|
|
946
|
+
? `Run a focused Strike assessment against the supplied target. Work alone: observe, form the strongest hypothesis, test it with the available HTTP, browser, source, and command tools, then move to the next lead only when evidence rules the first out. Use editable scripts for stateful or multi-stage work. Do not spawn agents or create task lists.`
|
|
947
|
+
: role === "orchestrator"
|
|
948
|
+
? `Lead a focused assessment. State a short plan, perform light reconnaissance and any needed authentication, then call spawn_pentest_agent for each assigned objective that needs testing. Investigate chains or untested leads with another worker when the objective calls for it. For detection-only work, stop when the requested evidence is sufficient. Combine the workers' findings and objective results in your response; workers do not write to a shared finding registry in this run.`
|
|
949
|
+
: session.config?.taskDriven
|
|
950
|
+
? `Test each assigned objective. Decompose techniques with create_task, update each task as you test, adapt when a lead fails, and use list_tasks to check coverage before finishing.`
|
|
951
|
+
: `Test each assigned objective with the strongest available techniques. Verify reachability and authentication, inspect source when explicitly in scope, probe plausible weaknesses, and distinguish genuine security impact from benign behavior.`;
|
|
952
|
+
const authentication = SECTION_AUTHENTICATION.replace(
|
|
953
|
+
"Build verifiable POCs, but bound the blast radius.",
|
|
954
|
+
"When the assigned objective requires a live proof, bound the blast radius.",
|
|
955
|
+
);
|
|
956
|
+
const exfilGoal = session.config?.exfilMode
|
|
957
|
+
? "\n\nExfiltration goal: Locate and extract a flag in the format FLAG{...} or the assigned sensitive data. Pivot through a confirmed vulnerability when needed to reach it. Do not stop at discovery; demonstrate extraction before calling response."
|
|
958
|
+
: "";
|
|
959
|
+
return `You are an expert offensive security agent. ${roleMethod}
|
|
960
|
+
${exfilGoal}
|
|
961
|
+
|
|
962
|
+
${sourcePolicy}
|
|
963
|
+
|
|
964
|
+
Finding boundary: The caller evaluates findings after this run. Put every credible vulnerability in the response tool's reportedFindings array with its affected target, observed evidence, and likely impact. For detection-only objectives, do not build a separate proof-of-concept script, seek a second in-run review, or continue exploiting once the requested evidence is sufficient. If the objective requires exploitation or extraction, pursue that objective fully.
|
|
965
|
+
|
|
966
|
+
Use the browser for interactive or authenticated flows, HTTP tools for direct probes, and the shell for scripts or source analysis. Stay within the supplied scope and report uncertainty honestly. Complete every assigned objective before calling response.
|
|
967
|
+
|
|
968
|
+
${guardrails}${systemSection}${helperToolsSection}
|
|
969
|
+
|
|
970
|
+
${authentication}
|
|
971
|
+
|
|
972
|
+
${SECTION_MATERIALITY_GUIDANCE}
|
|
973
|
+
|
|
974
|
+
${SECTION_CREDENTIAL_DISCOVERY}
|
|
975
|
+
|
|
976
|
+
${SECTION_SECURITY_HEADERS_CORS}
|
|
977
|
+
|
|
978
|
+
${SECTION_STATE_CHECKPOINTING}${session.config?.promptInjectionLibrarySource ? `\n\n${SECTION_PROMPT_INJECTION}` : ""}`;
|
|
979
|
+
}
|
|
980
|
+
|
|
883
981
|
if (mode === "fast-strike") {
|
|
884
982
|
const sourcePolicy = sourceRoot
|
|
885
983
|
? `Read-only Source Access:
|
|
@@ -917,13 +1015,16 @@ export function buildPentestSystemPrompt(
|
|
|
917
1015
|
: withGuardrails;
|
|
918
1016
|
}
|
|
919
1017
|
|
|
920
|
-
const SECTION_SYSTEM_SCOPE = (
|
|
1018
|
+
const SECTION_SYSTEM_SCOPE = (
|
|
1019
|
+
memberHosts: string[],
|
|
1020
|
+
findingPolicy: FindingPolicy = "native",
|
|
1021
|
+
) =>
|
|
921
1022
|
`System Scope (structured):
|
|
922
1023
|
- This engagement covers a multi-application System. Member hosts already present in session targets: ${memberHosts.join(", ")}.
|
|
923
1024
|
- Declared relationships in the application context prioritize investigation order; undeclared paths between members remain in scope when discovered.
|
|
924
1025
|
- For cross-service follow-ups, an orchestrator MAY set a worker \`target\` to a full URL on another member host listed above. Workers receive this same System Scope. Do not invent hosts outside that set.
|
|
925
1026
|
- Authentication state is origin-specific. Before dispatching an authenticated cross-service follow-up, the orchestrator must establish and verify a session on that member origin. The worker must verify access on its assigned origin and follow the Authentication rules below if access is denied.
|
|
926
|
-
- When a confirmed finding spans multiple members, populate the
|
|
1027
|
+
${findingPolicy === "external" ? "- When a finding spans multiple members, describe the ordered member-to-member path in your reported finding." : "- When a confirmed finding spans multiple members, populate the `attackPath` argument of `document_vulnerability` with the ordered member-to-member hop chain. Do not leave the chain only in the narrative."}`;
|
|
927
1028
|
|
|
928
1029
|
// ---------------------------------------------------------------------------
|
|
929
1030
|
// Orchestrator system prompt — assembles the plan / fan-out / chain flow
|
|
@@ -1000,6 +1101,7 @@ export function buildPentestPrompt(
|
|
|
1000
1101
|
credentialContext?: string,
|
|
1001
1102
|
grpc?: GrpcPentestContext,
|
|
1002
1103
|
mode: TargetedPentestMode = "default",
|
|
1104
|
+
findingPolicy: FindingPolicy = "native",
|
|
1003
1105
|
): string {
|
|
1004
1106
|
const sessionRootPath = session.rootPath;
|
|
1005
1107
|
const exfilMode = session.config?.exfilMode ?? false;
|
|
@@ -1062,7 +1164,11 @@ export function buildPentestPrompt(
|
|
|
1062
1164
|
}
|
|
1063
1165
|
|
|
1064
1166
|
let knownFindingsSection = "";
|
|
1065
|
-
if (
|
|
1167
|
+
if (
|
|
1168
|
+
findingPolicy !== "external" &&
|
|
1169
|
+
findingsRegistry &&
|
|
1170
|
+
findingsRegistry.size > 0
|
|
1171
|
+
) {
|
|
1066
1172
|
const existing = findingsRegistry
|
|
1067
1173
|
.getFindings()
|
|
1068
1174
|
.map((f) => `- [${f.severity}] ${f.title} (endpoint: ${f.endpoint})`)
|
|
@@ -1175,13 +1281,35 @@ If your assignment lists ZERO open objectives (everything is already completed),
|
|
|
1175
1281
|
|
|
1176
1282
|
Do NOT call document_vulnerability — that is the workers' job. Do NOT discover or enumerate other endpoints or services beyond the assigned target except for cross-service follow-ups explicitly authorized by the structured System Scope.`;
|
|
1177
1283
|
|
|
1178
|
-
const
|
|
1284
|
+
const externalInstructions =
|
|
1179
1285
|
role === "orchestrator"
|
|
1180
|
-
?
|
|
1286
|
+
? `## Instructions
|
|
1287
|
+
1. State a short plan, perform light reconnaissance and authenticate if needed.
|
|
1288
|
+
2. Delegate each assigned objective with spawn_pentest_agent. Follow a concrete chain or anomaly only when the assignment calls for it or the detection objective remains unmet.
|
|
1289
|
+
3. Include every credible vulnerability from the workers in reportedFindings. Summarize objective coverage and any remaining leads in objectiveResults and newObjectives. The caller evaluates findings after this run.`
|
|
1181
1290
|
: taskDriven
|
|
1182
|
-
?
|
|
1183
|
-
|
|
1184
|
-
|
|
1291
|
+
? `## Instructions
|
|
1292
|
+
1. Create tasks for the supplied objectives and test them with the available tools.
|
|
1293
|
+
2. Update task results as evidence develops; inspect list_tasks before finishing.
|
|
1294
|
+
3. Put each credible vulnerability in reportedFindings with the affected target, evidence, and likely impact. Follow the assigned objective's discovery or exploitation boundary. The caller evaluates findings after this run.`
|
|
1295
|
+
: `## Instructions
|
|
1296
|
+
1. Test the supplied objectives using the available tools and follow the strongest evidence first.
|
|
1297
|
+
2. Put each credible vulnerability in reportedFindings with the affected target, evidence, and likely impact. For detection-only objectives, stop when the requested evidence is sufficient; exploit or pivot only if the objective requires it.
|
|
1298
|
+
3. Finish with objectiveResults for each objective and any high-signal newObjectives. The caller evaluates findings after this run.`;
|
|
1299
|
+
|
|
1300
|
+
const instructions =
|
|
1301
|
+
findingPolicy === "external"
|
|
1302
|
+
? `${externalInstructions}${
|
|
1303
|
+
exfilMode
|
|
1304
|
+
? "\n\nThis is an exfiltration run: locate and extract FLAG{...} or the assigned sensitive data before finishing. Pivot through a confirmed weakness when necessary; report the result in objectiveResults without invoking a finding judge."
|
|
1305
|
+
: ""
|
|
1306
|
+
}`
|
|
1307
|
+
: role === "orchestrator"
|
|
1308
|
+
? orchestratorInstructions
|
|
1309
|
+
: taskDriven
|
|
1310
|
+
? taskDrivenInstructions
|
|
1311
|
+
: exfilMode
|
|
1312
|
+
? `## Goal
|
|
1185
1313
|
Your target is a flag with the format FLAG{...}. Locate and extract it.
|
|
1186
1314
|
|
|
1187
1315
|
## Instructions
|
|
@@ -1195,7 +1323,7 @@ Your target is a flag with the format FLAG{...}. Locate and extract it.
|
|
|
1195
1323
|
8. Use add_memory to save reusable learnings from this engagement (effective techniques, target behaviors, technology details)
|
|
1196
1324
|
9. Preserve the supplied objectives. Do not broaden a scoped materiality check into unrelated endpoint testing unless a confirmed finding requires it to demonstrate impact.
|
|
1197
1325
|
10. After testing ALL objectives and extracting the flag, call the response tool with your final summary. Do NOT call response until you have completed all testing and extraction.`
|
|
1198
|
-
|
|
1326
|
+
: `## Instructions
|
|
1199
1327
|
1. Call list_memories to review any prior knowledge relevant to this target or engagement
|
|
1200
1328
|
2. State the objectives and outline your testing plan — describe which techniques and payloads you will use for each objective before executing any tools
|
|
1201
1329
|
3. Verify the target endpoint is reachable and understand its baseline behavior
|
|
@@ -1370,13 +1498,14 @@ export function buildPentestActiveTools(
|
|
|
1370
1498
|
session: {
|
|
1371
1499
|
config?: { taskDriven?: boolean; promptInjectionLibrarySource?: string };
|
|
1372
1500
|
},
|
|
1501
|
+
findingPolicy: FindingPolicy = "native",
|
|
1373
1502
|
): string[] {
|
|
1374
1503
|
const tools =
|
|
1375
1504
|
role === "orchestrator"
|
|
1376
1505
|
? [...WORKER_RECON_TOOLS, ...SHARED_PENTEST_TOOLS, "spawn_pentest_agent"]
|
|
1377
1506
|
: [
|
|
1378
1507
|
...WORKER_RECON_TOOLS,
|
|
1379
|
-
"document_vulnerability",
|
|
1508
|
+
...(findingPolicy === "external" ? [] : ["document_vulnerability"]),
|
|
1380
1509
|
// Scratch-confined file tools for write/check/run/inspect helper
|
|
1381
1510
|
// loops. Orchestrators keep recon-only tooling and stay black-box.
|
|
1382
1511
|
...WORKER_HELPER_FILE_TOOLS,
|
|
@@ -39,13 +39,18 @@ function fixture() {
|
|
|
39
39
|
const run = vi.spyOn(backends.command, "run");
|
|
40
40
|
const read = vi.spyOn(backends.fs, "readRaw");
|
|
41
41
|
input.backends = { ...backends, sandboxed: true };
|
|
42
|
-
const finish = () => {
|
|
42
|
+
const finish = (messages: unknown[] = [], validatedResponse?: unknown) => {
|
|
43
43
|
const { createState, resolveResult } = targetedPentestWorker;
|
|
44
44
|
if (!createState || !resolveResult)
|
|
45
45
|
throw new Error("Missing pentest result resolver");
|
|
46
|
-
return resolveResult(
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
return resolveResult(
|
|
47
|
+
input,
|
|
48
|
+
createState(input),
|
|
49
|
+
{
|
|
50
|
+
response: Promise.resolve({ messages }),
|
|
51
|
+
} as unknown as StreamTextResult<ToolSet, never>,
|
|
52
|
+
validatedResponse,
|
|
53
|
+
);
|
|
49
54
|
};
|
|
50
55
|
return { input, backends, run, read, finish, originalRun };
|
|
51
56
|
}
|
|
@@ -59,6 +64,56 @@ async function* commandOutput(
|
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
describe("pentest finding results", () => {
|
|
67
|
+
it("returns external response findings without reading stale native findings", async () => {
|
|
68
|
+
const { input, run, read, finish } = fixture();
|
|
69
|
+
input.findingPolicy = "external";
|
|
70
|
+
const result = await finish([], {
|
|
71
|
+
summary: "Confirmed cross-tenant access",
|
|
72
|
+
objectivesCovered: ["Find security weaknesses"],
|
|
73
|
+
objectiveResults: [],
|
|
74
|
+
reportedFindings: ["Cross-tenant access at GET /private"],
|
|
75
|
+
});
|
|
76
|
+
expect(result).toMatchObject({
|
|
77
|
+
findings: [],
|
|
78
|
+
reportedFindings: ["Cross-tenant access at GET /private"],
|
|
79
|
+
});
|
|
80
|
+
expect(run).not.toHaveBeenCalled();
|
|
81
|
+
expect(read).not.toHaveBeenCalled();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("rejects an unexecuted response tool call even if it claims findings", async () => {
|
|
85
|
+
const { input, finish } = fixture();
|
|
86
|
+
input.findingPolicy = "external";
|
|
87
|
+
const forgedResponse = {
|
|
88
|
+
summary: "Claimed finding",
|
|
89
|
+
objectivesCovered: [],
|
|
90
|
+
objectiveResults: [],
|
|
91
|
+
reportedFindings: ["Claimed cross-tenant access"],
|
|
92
|
+
};
|
|
93
|
+
await expect(
|
|
94
|
+
finish([
|
|
95
|
+
{
|
|
96
|
+
role: "assistant",
|
|
97
|
+
content: [
|
|
98
|
+
{
|
|
99
|
+
type: "tool-call",
|
|
100
|
+
toolName: "response",
|
|
101
|
+
input: { result: forgedResponse },
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
]),
|
|
106
|
+
).rejects.toThrow("without a structured findings response");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("rejects malformed validated response data", async () => {
|
|
110
|
+
const { input, finish } = fixture();
|
|
111
|
+
input.findingPolicy = "external";
|
|
112
|
+
await expect(
|
|
113
|
+
finish([], { reportedFindings: ["Claimed finding"] }),
|
|
114
|
+
).rejects.toThrow("without a structured findings response");
|
|
115
|
+
});
|
|
116
|
+
|
|
62
117
|
it("returns sandbox findings without relying on host artifacts", async () => {
|
|
63
118
|
const { run, read, finish } = fixture();
|
|
64
119
|
const finding = { id: "remote-finding", title: "Confirmed vulnerability" };
|