appsec-agent 2.8.0 → 3.1.0
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 +105 -31
- package/conf/appsec_agent.yaml +7 -0
- package/dist/bin/agent-run.js +14 -17
- package/dist/bin/agent-run.js.map +1 -1
- package/dist/conf/appsec_agent.yaml +7 -0
- package/dist/src/__tests__/mocks/codex_sdk.d.ts +53 -0
- package/dist/src/__tests__/mocks/codex_sdk.d.ts.map +1 -0
- package/dist/src/__tests__/mocks/codex_sdk.js +8 -0
- package/dist/src/__tests__/mocks/codex_sdk.js.map +1 -0
- package/dist/src/agent_actions.d.ts +5 -1
- package/dist/src/agent_actions.d.ts.map +1 -1
- package/dist/src/agent_actions.js +103 -27
- package/dist/src/agent_actions.js.map +1 -1
- package/dist/src/agent_options.d.ts +20 -94
- package/dist/src/agent_options.d.ts.map +1 -1
- package/dist/src/agent_options.js +219 -311
- package/dist/src/agent_options.js.map +1 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +6 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/llm_query.d.ts +4 -43
- package/dist/src/llm_query.d.ts.map +1 -1
- package/dist/src/llm_query.js +4 -145
- package/dist/src/llm_query.js.map +1 -1
- package/dist/src/main.d.ts.map +1 -1
- package/dist/src/main.js +70 -10
- package/dist/src/main.js.map +1 -1
- package/dist/src/mcp_internal.d.ts +13 -0
- package/dist/src/mcp_internal.d.ts.map +1 -0
- package/dist/src/mcp_internal.js +34 -0
- package/dist/src/mcp_internal.js.map +1 -0
- package/dist/src/providers/claude_provider.d.ts +18 -0
- package/dist/src/providers/claude_provider.d.ts.map +1 -0
- package/dist/src/providers/claude_provider.js +27 -0
- package/dist/src/providers/claude_provider.js.map +1 -0
- package/dist/src/providers/claude_role_spec.d.ts +10 -0
- package/dist/src/providers/claude_role_spec.d.ts.map +1 -0
- package/dist/src/providers/claude_role_spec.js +85 -0
- package/dist/src/providers/claude_role_spec.js.map +1 -0
- package/dist/src/providers/codex_model.d.ts +12 -0
- package/dist/src/providers/codex_model.d.ts.map +1 -0
- package/dist/src/providers/codex_model.js +45 -0
- package/dist/src/providers/codex_model.js.map +1 -0
- package/dist/src/providers/codex_provider.d.ts +30 -0
- package/dist/src/providers/codex_provider.d.ts.map +1 -0
- package/dist/src/providers/codex_provider.js +170 -0
- package/dist/src/providers/codex_provider.js.map +1 -0
- package/dist/src/providers/codex_role_spec.d.ts +16 -0
- package/dist/src/providers/codex_role_spec.d.ts.map +1 -0
- package/dist/src/providers/codex_role_spec.js +63 -0
- package/dist/src/providers/codex_role_spec.js.map +1 -0
- package/dist/src/providers/query_message.d.ts +45 -0
- package/dist/src/providers/query_message.d.ts.map +1 -0
- package/dist/src/providers/query_message.js +8 -0
- package/dist/src/providers/query_message.js.map +1 -0
- package/dist/src/providers/resolve_provider.d.ts +10 -0
- package/dist/src/providers/resolve_provider.d.ts.map +1 -0
- package/dist/src/providers/resolve_provider.js +29 -0
- package/dist/src/providers/resolve_provider.js.map +1 -0
- package/dist/src/providers/role_spec.d.ts +39 -0
- package/dist/src/providers/role_spec.d.ts.map +1 -0
- package/dist/src/providers/role_spec.js +8 -0
- package/dist/src/providers/role_spec.js.map +1 -0
- package/dist/src/providers/structured_output.d.ts +21 -0
- package/dist/src/providers/structured_output.d.ts.map +1 -0
- package/dist/src/providers/structured_output.js +61 -0
- package/dist/src/providers/structured_output.js.map +1 -0
- package/dist/src/providers/types.d.ts +18 -0
- package/dist/src/providers/types.d.ts.map +1 -0
- package/dist/src/providers/types.js +15 -0
- package/dist/src/providers/types.js.map +1 -0
- package/dist/src/schemas/threat_adversary_pass.d.ts +18 -0
- package/dist/src/schemas/threat_adversary_pass.d.ts.map +1 -0
- package/dist/src/schemas/threat_adversary_pass.js +59 -0
- package/dist/src/schemas/threat_adversary_pass.js.map +1 -0
- package/dist/src/schemas/threat_model_report.d.ts +11 -0
- package/dist/src/schemas/threat_model_report.d.ts.map +1 -1
- package/dist/src/schemas/threat_model_report.js +21 -4
- package/dist/src/schemas/threat_model_report.js.map +1 -1
- package/dist/src/utils.js +1 -1
- package/dist/src/utils.js.map +1 -1
- package/package.json +3 -3
- package/dist/src/openai_tools.d.ts +0 -26
- package/dist/src/openai_tools.d.ts.map +0 -1
- package/dist/src/openai_tools.js +0 -194
- package/dist/src/openai_tools.js.map +0 -1
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
* Author: Sam Li
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.AgentOptions = exports.MCP_INTERNAL_TOOL_NAMES = exports.MCP_INTERNAL_SERVER_NAME = exports.DEFAULT_MCP_SERVER_NAME = void 0;
|
|
9
|
-
exports.buildMcpInternalToolNames = buildMcpInternalToolNames;
|
|
8
|
+
exports.AgentOptions = exports.buildMcpInternalToolNames = exports.MCP_INTERNAL_TOOL_NAMES = exports.MCP_INTERNAL_SERVER_NAME = exports.DEFAULT_MCP_SERVER_NAME = void 0;
|
|
10
9
|
exports.buildPrReviewerMcpNudgeSystemPromptSuffix = buildPrReviewerMcpNudgeSystemPromptSuffix;
|
|
11
10
|
const security_report_1 = require("./schemas/security_report");
|
|
12
11
|
const threat_model_report_1 = require("./schemas/threat_model_report");
|
|
@@ -16,6 +15,12 @@ const finding_validator_1 = require("./schemas/finding_validator");
|
|
|
16
15
|
const context_extraction_1 = require("./schemas/context_extraction");
|
|
17
16
|
const learned_guidance_1 = require("./schemas/learned_guidance");
|
|
18
17
|
const fp_adversary_pass_1 = require("./schemas/fp_adversary_pass");
|
|
18
|
+
const claude_role_spec_1 = require("./providers/claude_role_spec");
|
|
19
|
+
const mcp_internal_1 = require("./mcp_internal");
|
|
20
|
+
Object.defineProperty(exports, "DEFAULT_MCP_SERVER_NAME", { enumerable: true, get: function () { return mcp_internal_1.DEFAULT_MCP_SERVER_NAME; } });
|
|
21
|
+
Object.defineProperty(exports, "MCP_INTERNAL_SERVER_NAME", { enumerable: true, get: function () { return mcp_internal_1.MCP_INTERNAL_SERVER_NAME; } });
|
|
22
|
+
Object.defineProperty(exports, "MCP_INTERNAL_TOOL_NAMES", { enumerable: true, get: function () { return mcp_internal_1.MCP_INTERNAL_TOOL_NAMES; } });
|
|
23
|
+
Object.defineProperty(exports, "buildMcpInternalToolNames", { enumerable: true, get: function () { return mcp_internal_1.buildMcpInternalToolNames; } });
|
|
19
24
|
const FIX_CODE_VS_OPTIONS_GUIDANCE = `
|
|
20
25
|
|
|
21
26
|
FIXED CODE vs FIX OPTIONS:
|
|
@@ -24,54 +29,6 @@ FIXED CODE vs FIX OPTIONS:
|
|
|
24
29
|
- Use "fix_options" when the fix requires architectural decisions, domain-specific knowledge,
|
|
25
30
|
or when multiple valid remediation approaches exist. Each option needs an id, title, and description.
|
|
26
31
|
- Provide either fixed_code OR fix_options per finding, not both.`;
|
|
27
|
-
/**
|
|
28
|
-
* Default identifier the agent uses to register a parent-app-managed MCP
|
|
29
|
-
* server with the Claude Agent SDK. The resulting tool names exposed to
|
|
30
|
-
* the LLM follow the SDK convention `mcp__<server>__<tool>`, so this
|
|
31
|
-
* value becomes the literal prefix the model sees on its tool list.
|
|
32
|
-
*
|
|
33
|
-
* The default is intentionally generic (`appsec-internal`) so the
|
|
34
|
-
* `appsec-agent` package is reusable across parent apps. Callers that
|
|
35
|
-
* need a different identifier — e.g. to keep an existing prompt-nudge or
|
|
36
|
-
* counter contract stable — pass `--mcp-server-name <name>` on the CLI
|
|
37
|
-
* (or `mcpServerName` to the role builders directly), and the override
|
|
38
|
-
* threads through to both `Options.mcpServers[<name>]` and the namespaced
|
|
39
|
-
* tool names in the subagent whitelist.
|
|
40
|
-
*/
|
|
41
|
-
exports.DEFAULT_MCP_SERVER_NAME = 'appsec-internal';
|
|
42
|
-
/**
|
|
43
|
-
* @deprecated since v2.4.2 — historical alias for the default server name.
|
|
44
|
-
* Kept exported so existing imports keep type-checking; new code should
|
|
45
|
-
* read `DEFAULT_MCP_SERVER_NAME` (and pass an override via
|
|
46
|
-
* `mcpServerName` when a specific identifier is required).
|
|
47
|
-
*/
|
|
48
|
-
exports.MCP_INTERNAL_SERVER_NAME = exports.DEFAULT_MCP_SERVER_NAME;
|
|
49
|
-
/**
|
|
50
|
-
* Tools exposed by the per-scan in-process MCP server the parent app
|
|
51
|
-
* stands up. Pinned at this set (`queryFindingsHistory`,
|
|
52
|
-
* `queryImportGraph`, `queryRuntimeEnrichment`, `queryCodebaseGraph`) so
|
|
53
|
-
* the agent's tool whitelist is deterministic at the current version;
|
|
54
|
-
* parent apps that expose a different surface should fork or extend this
|
|
55
|
-
* list rather than rely on dynamic discovery (the SDK would otherwise have
|
|
56
|
-
* to round-trip the server before constructing the whitelist).
|
|
57
|
-
*/
|
|
58
|
-
exports.MCP_INTERNAL_TOOL_NAMES = [
|
|
59
|
-
'queryFindingsHistory',
|
|
60
|
-
'queryImportGraph',
|
|
61
|
-
'queryRuntimeEnrichment',
|
|
62
|
-
'queryCodebaseGraph',
|
|
63
|
-
];
|
|
64
|
-
/**
|
|
65
|
-
* SDK-namespaced tool names the LLM sees on its tool list when the server
|
|
66
|
-
* is wired up. Exposed as a helper rather than a constant so callers in
|
|
67
|
-
* `agent_options.ts` and the test suite share one source of truth.
|
|
68
|
-
*
|
|
69
|
-
* @param serverName - Override for the MCP server identifier. Defaults
|
|
70
|
-
* to `DEFAULT_MCP_SERVER_NAME` (`appsec-internal`).
|
|
71
|
-
*/
|
|
72
|
-
function buildMcpInternalToolNames(serverName = exports.DEFAULT_MCP_SERVER_NAME) {
|
|
73
|
-
return exports.MCP_INTERNAL_TOOL_NAMES.map((tool) => `mcp__${serverName}__${tool}`);
|
|
74
|
-
}
|
|
75
32
|
/**
|
|
76
33
|
* System-prompt suffix for `pr_reviewer` / `code_reviewer` when
|
|
77
34
|
* `--mcp-server-url` is set. Steers the model toward all live parent-app
|
|
@@ -88,8 +45,8 @@ function buildMcpInternalToolNames(serverName = exports.DEFAULT_MCP_SERVER_NAME)
|
|
|
88
45
|
* @param mcpServerName - Same override as `attachMcpServerToOptions`
|
|
89
46
|
* (`DEFAULT_MCP_SERVER_NAME` when omitted).
|
|
90
47
|
*/
|
|
91
|
-
function buildPrReviewerMcpNudgeSystemPromptSuffix(mcpServerName =
|
|
92
|
-
const name = mcpServerName ||
|
|
48
|
+
function buildPrReviewerMcpNudgeSystemPromptSuffix(mcpServerName = mcp_internal_1.DEFAULT_MCP_SERVER_NAME) {
|
|
49
|
+
const name = mcpServerName || mcp_internal_1.DEFAULT_MCP_SERVER_NAME;
|
|
93
50
|
const findingsTool = `mcp__${name}__queryFindingsHistory`;
|
|
94
51
|
const importGraphTool = `mcp__${name}__queryImportGraph`;
|
|
95
52
|
const runtimeTool = `mcp__${name}__queryRuntimeEnrichment`;
|
|
@@ -101,24 +58,13 @@ function buildPrReviewerMcpNudgeSystemPromptSuffix(mcpServerName = exports.DEFAU
|
|
|
101
58
|
}
|
|
102
59
|
/**
|
|
103
60
|
* Mutate an already-built `Options` object to attach the MCP server config
|
|
104
|
-
*
|
|
105
|
-
* whitelist with the backend-backed tool surface. No-op when
|
|
106
|
-
* `mcpServerUrl` is falsy — preserves the pre-v2.4.0 behaviour for
|
|
107
|
-
* callers that don't pass the new parameter.
|
|
108
|
-
*
|
|
109
|
-
* Mutating in place (rather than returning a fresh object) keeps the
|
|
110
|
-
* existing role builders' return statements untouched and avoids a deep
|
|
111
|
-
* clone of the (large) `Options` shape.
|
|
112
|
-
*
|
|
113
|
-
* @param mcpServerName - Override for the server identifier. Defaults
|
|
114
|
-
* to `DEFAULT_MCP_SERVER_NAME` (`appsec-internal`); pass the parent
|
|
115
|
-
* app's chosen name to keep tool-name contracts stable.
|
|
61
|
+
* @deprecated Use attachMcpToRoleSpec + roleSpecToClaudeOptions instead.
|
|
116
62
|
*/
|
|
117
|
-
function attachMcpServerToOptions(options, mcpServerUrl, agentKey, mcpServerName =
|
|
63
|
+
function attachMcpServerToOptions(options, mcpServerUrl, agentKey, mcpServerName = mcp_internal_1.DEFAULT_MCP_SERVER_NAME, mcpServerBearer) {
|
|
118
64
|
if (!mcpServerUrl) {
|
|
119
65
|
return;
|
|
120
66
|
}
|
|
121
|
-
const serverName = mcpServerName ||
|
|
67
|
+
const serverName = mcpServerName || mcp_internal_1.DEFAULT_MCP_SERVER_NAME;
|
|
122
68
|
const httpEntry = {
|
|
123
69
|
type: 'http',
|
|
124
70
|
url: mcpServerUrl,
|
|
@@ -133,7 +79,7 @@ function attachMcpServerToOptions(options, mcpServerUrl, agentKey, mcpServerName
|
|
|
133
79
|
const agent = options.agents?.[agentKey];
|
|
134
80
|
if (agent) {
|
|
135
81
|
const existingTools = agent.tools ?? [];
|
|
136
|
-
agent.tools = [...existingTools, ...buildMcpInternalToolNames(serverName)];
|
|
82
|
+
agent.tools = [...existingTools, ...(0, mcp_internal_1.buildMcpInternalToolNames)(serverName)];
|
|
137
83
|
}
|
|
138
84
|
}
|
|
139
85
|
class AgentOptions {
|
|
@@ -181,19 +127,26 @@ class AgentOptions {
|
|
|
181
127
|
/**
|
|
182
128
|
* Get options for simple query agent
|
|
183
129
|
*/
|
|
184
|
-
|
|
130
|
+
getSimpleQueryAgentRoleSpec(role = 'simple_query_agent', srcDir) {
|
|
185
131
|
const roleConfig = this.confDict[this.environment]?.[role];
|
|
186
132
|
let systemPrompt = roleConfig?.options?.system_prompt ||
|
|
187
133
|
'You are an Application Security (AppSec) expert assistant. You are responsible for providing security advice and guidance to the user.';
|
|
188
|
-
// Add source directory context to system prompt if provided
|
|
189
134
|
if (srcDir) {
|
|
190
135
|
systemPrompt += ` You have access to a source code directory at ${srcDir} that you can search and read files from to answer questions.`;
|
|
191
136
|
}
|
|
192
137
|
return {
|
|
193
|
-
|
|
194
|
-
|
|
138
|
+
roleId: 'simple_query_agent',
|
|
139
|
+
systemPrompt,
|
|
140
|
+
maxTurns: roleConfig?.options?.max_turns || 1,
|
|
141
|
+
capabilities: {},
|
|
142
|
+
noTools: true,
|
|
143
|
+
model: this.model,
|
|
144
|
+
workingDirectory: srcDir ?? undefined,
|
|
195
145
|
};
|
|
196
146
|
}
|
|
147
|
+
getSimpleQueryAgentOptions(role = 'simple_query_agent', srcDir) {
|
|
148
|
+
return (0, claude_role_spec_1.roleSpecToClaudeOptions)(this.getSimpleQueryAgentRoleSpec(role, srcDir));
|
|
149
|
+
}
|
|
197
150
|
/**
|
|
198
151
|
* Get options for security code reviewer
|
|
199
152
|
*
|
|
@@ -213,7 +166,7 @@ class AgentOptions {
|
|
|
213
166
|
* @param mcpServerName - Override for the MCP server identifier
|
|
214
167
|
* @param mcpServerBearer - Bearer token for MCP HTTP requests
|
|
215
168
|
*/
|
|
216
|
-
|
|
169
|
+
getCodeReviewerRoleSpec(role = 'code_reviewer', outputFormat, mcpServerUrl, mcpServerName, mcpServerBearer, workingDirectory) {
|
|
217
170
|
const roleConfig = this.confDict[this.environment]?.[role];
|
|
218
171
|
let systemPrompt = roleConfig?.options?.system_prompt ||
|
|
219
172
|
'You are an Application Security (AppSec) expert assistant. You are responsible for performing a thorough code review. List out all the potential security and privacy issues found in the code.';
|
|
@@ -223,59 +176,87 @@ class AgentOptions {
|
|
|
223
176
|
if (mcpServerUrl) {
|
|
224
177
|
systemPrompt += buildPrReviewerMcpNudgeSystemPromptSuffix(mcpServerName);
|
|
225
178
|
}
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
},
|
|
237
|
-
permissionMode: 'bypassPermissions'
|
|
179
|
+
const spec = {
|
|
180
|
+
roleId: 'code_reviewer',
|
|
181
|
+
systemPrompt,
|
|
182
|
+
maxTurns: roleConfig?.options?.max_turns ?? 30,
|
|
183
|
+
agentName: 'code-reviewer',
|
|
184
|
+
agentDescription: 'Reviews code for best practices and potential security issues only',
|
|
185
|
+
capabilities: { read: true, grep: true, write: true },
|
|
186
|
+
permissionMode: 'bypassPermissions',
|
|
187
|
+
model: this.model,
|
|
188
|
+
workingDirectory,
|
|
238
189
|
};
|
|
239
|
-
// Add JSON schema enforcement when output format is JSON
|
|
240
190
|
if (outputFormat?.toLowerCase() === 'json') {
|
|
241
|
-
|
|
242
|
-
type: 'json_schema',
|
|
243
|
-
schema: security_report_1.SECURITY_REPORT_SCHEMA
|
|
244
|
-
};
|
|
191
|
+
spec.outputSchema = security_report_1.SECURITY_REPORT_SCHEMA;
|
|
245
192
|
}
|
|
246
|
-
|
|
247
|
-
return
|
|
193
|
+
(0, mcp_internal_1.attachMcpToRoleSpec)(spec, mcpServerUrl, mcpServerName, mcpServerBearer);
|
|
194
|
+
return spec;
|
|
195
|
+
}
|
|
196
|
+
getCodeReviewerOptions(role = 'code_reviewer', outputFormat, mcpServerUrl, mcpServerName, mcpServerBearer) {
|
|
197
|
+
return (0, claude_role_spec_1.roleSpecToClaudeOptions)(this.getCodeReviewerRoleSpec(role, outputFormat, mcpServerUrl, mcpServerName, mcpServerBearer));
|
|
248
198
|
}
|
|
249
199
|
/**
|
|
250
|
-
*
|
|
251
|
-
* @param role - The role configuration key
|
|
252
|
-
* @param outputFormat - Output format (json, markdown, etc.)
|
|
200
|
+
* Provider-neutral spec for threat modeler (Phase 3 RoleSpec spike).
|
|
253
201
|
*/
|
|
254
|
-
|
|
202
|
+
getThreatModelerRoleSpec(role = 'threat_modeler', outputFormat, workingDirectory, maxTurnsOverride) {
|
|
255
203
|
const roleConfig = this.confDict[this.environment]?.[role];
|
|
256
204
|
const systemPrompt = roleConfig?.options?.system_prompt ||
|
|
257
205
|
'You are an Application Security (AppSec) expert assistant. You are responsible for performing risk assessment on the source code repository for SOC2 type 2 compliance audit using the STRIDE methodology.';
|
|
258
206
|
const isJson = outputFormat?.toLowerCase() === 'json';
|
|
259
|
-
const resolvedMaxTurns = roleConfig?.options?.max_turns ??
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
|
|
207
|
+
const resolvedMaxTurns = maxTurnsOverride ?? roleConfig?.options?.max_turns ?? 100;
|
|
208
|
+
const spec = {
|
|
209
|
+
roleId: 'threat_modeler',
|
|
210
|
+
systemPrompt,
|
|
211
|
+
maxTurns: resolvedMaxTurns,
|
|
212
|
+
agentName: 'threat-modeler',
|
|
213
|
+
agentDescription: 'Performs threat modeling and risk assessment using STRIDE methodology',
|
|
214
|
+
capabilities: isJson
|
|
215
|
+
? { read: true, grep: true }
|
|
216
|
+
: { read: true, grep: true, write: true, graphviz: true },
|
|
217
|
+
permissionMode: 'bypassPermissions',
|
|
218
|
+
model: this.model,
|
|
219
|
+
workingDirectory,
|
|
271
220
|
};
|
|
272
221
|
if (isJson) {
|
|
273
|
-
|
|
274
|
-
type: 'json_schema',
|
|
275
|
-
schema: threat_model_report_1.THREAT_MODEL_REPORT_SCHEMA
|
|
276
|
-
};
|
|
222
|
+
spec.outputSchema = threat_model_report_1.THREAT_MODEL_REPORT_SCHEMA;
|
|
277
223
|
}
|
|
278
|
-
return
|
|
224
|
+
return spec;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Get options for threat modeler
|
|
228
|
+
* @param role - The role configuration key
|
|
229
|
+
* @param outputFormat - Output format (json, markdown, etc.)
|
|
230
|
+
*/
|
|
231
|
+
getThreatModelerOptions(role = 'threat_modeler', outputFormat, maxTurnsOverride) {
|
|
232
|
+
return (0, claude_role_spec_1.roleSpecToClaudeOptions)(this.getThreatModelerRoleSpec(role, outputFormat, undefined, maxTurnsOverride));
|
|
233
|
+
}
|
|
234
|
+
getThreatAdversaryRoleSpec(role = 'threat_adversary', srcDir, maxTurns) {
|
|
235
|
+
const roleConfig = this.confDict[this.environment]?.[role];
|
|
236
|
+
let systemPrompt = roleConfig?.options?.system_prompt ||
|
|
237
|
+
'You are a senior application security engineer performing an adversarial second pass on a STRIDE threat model. ' +
|
|
238
|
+
'Skeptically verify each threat against the real codebase using Read and Grep. ' +
|
|
239
|
+
'Keep only threats with a concrete, demonstrable attack path and confirmed source_locations. ' +
|
|
240
|
+
'Drop generic, mitigated, or ungrounded threats. Reconcile risks and metadata counts.';
|
|
241
|
+
if (srcDir) {
|
|
242
|
+
systemPrompt += `\n\nSource code is available at: ${srcDir}. Use Read and Grep to verify code paths before keeping a threat.`;
|
|
243
|
+
}
|
|
244
|
+
const spec = {
|
|
245
|
+
roleId: 'threat_adversary',
|
|
246
|
+
systemPrompt,
|
|
247
|
+
maxTurns: maxTurns ?? roleConfig?.options?.max_turns ?? 100,
|
|
248
|
+
agentName: 'threat-adversary',
|
|
249
|
+
agentDescription: 'Adversarial second pass: filters STRIDE threats by concrete code-grounded attack paths',
|
|
250
|
+
capabilities: { read: true, grep: true },
|
|
251
|
+
permissionMode: 'bypassPermissions',
|
|
252
|
+
model: this.model,
|
|
253
|
+
outputSchema: threat_model_report_1.THREAT_MODEL_REPORT_SCHEMA,
|
|
254
|
+
workingDirectory: srcDir ?? undefined,
|
|
255
|
+
};
|
|
256
|
+
return spec;
|
|
257
|
+
}
|
|
258
|
+
getThreatAdversaryOptions(role = 'threat_adversary', srcDir, maxTurns) {
|
|
259
|
+
return (0, claude_role_spec_1.roleSpecToClaudeOptions)(this.getThreatAdversaryRoleSpec(role, srcDir, maxTurns));
|
|
279
260
|
}
|
|
280
261
|
/**
|
|
281
262
|
* Get options for PR diff-focused code reviewer
|
|
@@ -285,7 +266,7 @@ class AgentOptions {
|
|
|
285
266
|
* @param srcDir - Optional source directory path
|
|
286
267
|
* @param outputFormat - Output format (json, markdown, etc.)
|
|
287
268
|
*/
|
|
288
|
-
|
|
269
|
+
getDiffReviewerRoleSpec(role = 'code_reviewer', srcDir, outputFormat, maxTurns, noTools, experimentEnabled, mcpServerUrl, mcpServerName, mcpServerBearer) {
|
|
289
270
|
const roleConfig = this.confDict[this.environment]?.[role];
|
|
290
271
|
let systemPrompt;
|
|
291
272
|
if (noTools) {
|
|
@@ -341,7 +322,6 @@ You have access to Read, Grep, and Write tools:
|
|
|
341
322
|
if (srcDir) {
|
|
342
323
|
systemPrompt += `\n\nSource directory available at: ${srcDir}`;
|
|
343
324
|
}
|
|
344
|
-
// Allow role config to override the system prompt
|
|
345
325
|
if (roleConfig?.options?.diff_reviewer_system_prompt) {
|
|
346
326
|
systemPrompt = roleConfig.options.diff_reviewer_system_prompt;
|
|
347
327
|
}
|
|
@@ -356,76 +336,59 @@ You have access to Read, Grep, and Write tools:
|
|
|
356
336
|
if (mcpServerUrl && role === 'pr_reviewer') {
|
|
357
337
|
systemPrompt += buildPrReviewerMcpNudgeSystemPromptSuffix(mcpServerName);
|
|
358
338
|
}
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
}
|
|
371
|
-
},
|
|
372
|
-
permissionMode: 'bypassPermissions'
|
|
339
|
+
const spec = {
|
|
340
|
+
roleId: role === 'pr_reviewer' ? 'pr_reviewer' : 'code_reviewer',
|
|
341
|
+
systemPrompt,
|
|
342
|
+
maxTurns: maxTurns ?? roleConfig?.options?.max_turns ?? 10,
|
|
343
|
+
agentName: 'diff-reviewer',
|
|
344
|
+
agentDescription: 'Reviews PR diff changes for security vulnerabilities',
|
|
345
|
+
capabilities: noTools ? {} : { read: true, grep: true, write: true },
|
|
346
|
+
allowedTools: noTools ? ['Write'] : undefined,
|
|
347
|
+
permissionMode: 'bypassPermissions',
|
|
348
|
+
model: this.model,
|
|
349
|
+
workingDirectory: srcDir ?? undefined,
|
|
373
350
|
};
|
|
374
|
-
// Add JSON schema enforcement when output format is JSON
|
|
375
351
|
if (outputFormat?.toLowerCase() === 'json') {
|
|
376
|
-
|
|
377
|
-
type: 'json_schema',
|
|
378
|
-
schema: security_report_1.SECURITY_REPORT_SCHEMA
|
|
379
|
-
};
|
|
352
|
+
spec.outputSchema = security_report_1.SECURITY_REPORT_SCHEMA;
|
|
380
353
|
}
|
|
381
|
-
|
|
382
|
-
return
|
|
354
|
+
(0, mcp_internal_1.attachMcpToRoleSpec)(spec, mcpServerUrl, mcpServerName, mcpServerBearer);
|
|
355
|
+
return spec;
|
|
383
356
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
* @param role - The role configuration key
|
|
389
|
-
* @param srcDir - Optional source directory path for additional context
|
|
390
|
-
*/
|
|
391
|
-
getCodeFixerOptions(role = 'code_fixer', srcDir, mcpServerUrl, mcpServerName, mcpServerBearer) {
|
|
357
|
+
getDiffReviewerOptions(role = 'code_reviewer', srcDir, outputFormat, maxTurns, noTools, experimentEnabled, mcpServerUrl, mcpServerName, mcpServerBearer) {
|
|
358
|
+
return (0, claude_role_spec_1.roleSpecToClaudeOptions)(this.getDiffReviewerRoleSpec(role, srcDir, outputFormat, maxTurns, noTools, experimentEnabled, mcpServerUrl, mcpServerName, mcpServerBearer));
|
|
359
|
+
}
|
|
360
|
+
getCodeFixerRoleSpec(role = 'code_fixer', srcDir, mcpServerUrl, mcpServerName, mcpServerBearer) {
|
|
392
361
|
const roleConfig = this.confDict[this.environment]?.[role];
|
|
393
362
|
let systemPrompt = roleConfig?.options?.system_prompt ||
|
|
394
363
|
'You are an expert security engineer specializing in fixing vulnerabilities in code. ' +
|
|
395
364
|
'You receive a finding with code context and must produce a precise, minimal fix that resolves ' +
|
|
396
|
-
|
|
365
|
+
"the security issue while preserving the original code's functionality and indentation. " +
|
|
397
366
|
'Only modify the affected lines. Always use the recommended secure alternatives when applicable.';
|
|
398
367
|
if (srcDir) {
|
|
399
368
|
systemPrompt += `\n\nSource directory available at: ${srcDir}. You may read files for additional context if needed.`;
|
|
400
369
|
}
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
model: this.model,
|
|
409
|
-
maxTurns: resolvedMaxTurns
|
|
410
|
-
}
|
|
411
|
-
},
|
|
370
|
+
const spec = {
|
|
371
|
+
roleId: 'code_fixer',
|
|
372
|
+
systemPrompt,
|
|
373
|
+
maxTurns: roleConfig?.options?.max_turns ?? 10,
|
|
374
|
+
agentName: 'code-fixer',
|
|
375
|
+
agentDescription: 'Generates precise security fixes for code vulnerabilities',
|
|
376
|
+
capabilities: { read: true, grep: true },
|
|
412
377
|
permissionMode: 'bypassPermissions',
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
}
|
|
378
|
+
model: this.model,
|
|
379
|
+
outputSchema: security_fix_1.FIX_OUTPUT_SCHEMA,
|
|
380
|
+
workingDirectory: srcDir ?? undefined,
|
|
417
381
|
};
|
|
418
|
-
|
|
419
|
-
return
|
|
382
|
+
(0, mcp_internal_1.attachMcpToRoleSpec)(spec, mcpServerUrl, mcpServerName, mcpServerBearer);
|
|
383
|
+
return spec;
|
|
420
384
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
getQaVerifierOptions(role = 'qa_verifier', srcDir) {
|
|
385
|
+
getCodeFixerOptions(role = 'code_fixer', srcDir, mcpServerUrl, mcpServerName, mcpServerBearer) {
|
|
386
|
+
return (0, claude_role_spec_1.roleSpecToClaudeOptions)(this.getCodeFixerRoleSpec(role, srcDir, mcpServerUrl, mcpServerName, mcpServerBearer));
|
|
387
|
+
}
|
|
388
|
+
getQaVerifierRoleSpec(role = 'qa_verifier', srcDir) {
|
|
426
389
|
const roleConfig = this.confDict[this.environment]?.[role];
|
|
427
390
|
let systemPrompt = roleConfig?.options?.system_prompt ||
|
|
428
|
-
|
|
391
|
+
"You are a QA verification engineer. Your task is to verify security fixes by running the project's test suite " +
|
|
429
392
|
'and analyzing the results. You have access to the project source code and can execute shell commands to run tests. ' +
|
|
430
393
|
'First, set up the environment (install dependencies if needed), then run the test suite. ' +
|
|
431
394
|
'If tests fail, analyze the failures to determine if they are caused by the security fix or are pre-existing issues. ' +
|
|
@@ -433,30 +396,23 @@ You have access to Read, Grep, and Write tools:
|
|
|
433
396
|
if (srcDir) {
|
|
434
397
|
systemPrompt += `\n\nProject source code is available at: ${srcDir}. Use Read and Grep to inspect files, and Bash to execute commands.`;
|
|
435
398
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
model: this.model,
|
|
444
|
-
maxTurns: resolvedMaxTurns
|
|
445
|
-
}
|
|
446
|
-
},
|
|
399
|
+
return {
|
|
400
|
+
roleId: 'qa_verifier',
|
|
401
|
+
systemPrompt,
|
|
402
|
+
maxTurns: roleConfig?.options?.max_turns ?? 15,
|
|
403
|
+
agentName: 'qa-verifier',
|
|
404
|
+
agentDescription: 'Verifies security fixes by running project tests and analyzing results',
|
|
405
|
+
capabilities: { read: true, grep: true, shell: true },
|
|
447
406
|
permissionMode: 'bypassPermissions',
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
}
|
|
407
|
+
model: this.model,
|
|
408
|
+
outputSchema: qa_context_1.QA_VERDICT_SCHEMA,
|
|
409
|
+
workingDirectory: srcDir ?? undefined,
|
|
452
410
|
};
|
|
453
|
-
return options;
|
|
454
411
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
getContextExtractorOptions(role = 'context_extractor') {
|
|
412
|
+
getQaVerifierOptions(role = 'qa_verifier', srcDir) {
|
|
413
|
+
return (0, claude_role_spec_1.roleSpecToClaudeOptions)(this.getQaVerifierRoleSpec(role, srcDir));
|
|
414
|
+
}
|
|
415
|
+
getContextExtractorRoleSpec(role = 'context_extractor') {
|
|
460
416
|
const roleConfig = this.confDict[this.environment]?.[role];
|
|
461
417
|
const systemPrompt = roleConfig?.options?.system_prompt ||
|
|
462
418
|
'You are a security-aware software analyst. Your task is to analyze repository files and metadata ' +
|
|
@@ -470,25 +426,23 @@ You have access to Read, Grep, and Write tools:
|
|
|
470
426
|
'(logs, uploads, work-dir, data), IDE config (.cursor, .vscode), utility scripts, and documentation. Use ' +
|
|
471
427
|
'specific paths from the tree (e.g., "backend/scripts/**" not just "scripts/**"). Only suggest patterns NOT ' +
|
|
472
428
|
'already in the standard preset. If a field has no relevant information, return an empty string.';
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
},
|
|
482
|
-
},
|
|
429
|
+
return {
|
|
430
|
+
roleId: 'context_extractor',
|
|
431
|
+
systemPrompt,
|
|
432
|
+
maxTurns: 1,
|
|
433
|
+
agentName: 'context-extractor',
|
|
434
|
+
agentDescription: 'Extracts structured project intelligence from repository files',
|
|
435
|
+
capabilities: {},
|
|
436
|
+
allowedTools: [],
|
|
483
437
|
permissionMode: 'bypassPermissions',
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
schema: context_extraction_1.CONTEXT_EXTRACTION_SCHEMA,
|
|
487
|
-
},
|
|
438
|
+
model: this.model,
|
|
439
|
+
outputSchema: context_extraction_1.CONTEXT_EXTRACTION_SCHEMA,
|
|
488
440
|
};
|
|
489
|
-
return options;
|
|
490
441
|
}
|
|
491
|
-
|
|
442
|
+
getContextExtractorOptions(role = 'context_extractor') {
|
|
443
|
+
return (0, claude_role_spec_1.roleSpecToClaudeOptions)(this.getContextExtractorRoleSpec(role));
|
|
444
|
+
}
|
|
445
|
+
getFindingValidatorRoleSpec(role = 'finding_validator', srcDir, mcpServerUrl, mcpServerName, mcpServerBearer) {
|
|
492
446
|
const roleConfig = this.confDict[this.environment]?.[role];
|
|
493
447
|
let systemPrompt = roleConfig?.options?.system_prompt ||
|
|
494
448
|
'You are a security expert specializing in vulnerability validation. ' +
|
|
@@ -498,45 +452,25 @@ You have access to Read, Grep, and Write tools:
|
|
|
498
452
|
if (srcDir) {
|
|
499
453
|
systemPrompt += `\n\nSource code is available at: ${srcDir}. Use Read and Grep to inspect files for additional context if needed.`;
|
|
500
454
|
}
|
|
501
|
-
const
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
model: this.model,
|
|
509
|
-
maxTurns: resolvedMaxTurns
|
|
510
|
-
}
|
|
511
|
-
},
|
|
455
|
+
const spec = {
|
|
456
|
+
roleId: 'finding_validator',
|
|
457
|
+
systemPrompt,
|
|
458
|
+
maxTurns: roleConfig?.options?.max_turns ?? 5,
|
|
459
|
+
agentName: 'finding-validator',
|
|
460
|
+
agentDescription: 'Validates whether a previously detected security vulnerability is still present in code',
|
|
461
|
+
capabilities: { read: true, grep: true },
|
|
512
462
|
permissionMode: 'bypassPermissions',
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
}
|
|
463
|
+
model: this.model,
|
|
464
|
+
outputSchema: finding_validator_1.RETEST_VERDICT_SCHEMA,
|
|
465
|
+
workingDirectory: srcDir ?? undefined,
|
|
517
466
|
};
|
|
518
|
-
|
|
519
|
-
return
|
|
467
|
+
(0, mcp_internal_1.attachMcpToRoleSpec)(spec, mcpServerUrl, mcpServerName, mcpServerBearer);
|
|
468
|
+
return spec;
|
|
520
469
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
*
|
|
526
|
-
* Tools: NONE — the agent must work from the provided buckets only. No
|
|
527
|
-
* source-tree access, no MCP server. The parent app's `runSynthesizerAgent`
|
|
528
|
-
* spawns this from a temp working directory anyway, so even if a tool were
|
|
529
|
-
* attached it would have nothing to read.
|
|
530
|
-
*
|
|
531
|
-
* Output schema (LEARNED_GUIDANCE_OUTPUT_SCHEMA):
|
|
532
|
-
* { bullets: [{ cwe, bullet (≤300 chars), confidence (0..1) }] }
|
|
533
|
-
*
|
|
534
|
-
* Confidence floor and active-bullet cap are enforced by the parent app
|
|
535
|
-
* (`MIN_CONFIDENCE = 0.6`, `MAX_ACTIVE_BULLETS_PER_PROJECT = 12`); this
|
|
536
|
-
* role is allowed to return up to 50 bullets and the parent ranks +
|
|
537
|
-
* truncates.
|
|
538
|
-
*/
|
|
539
|
-
getLearnedGuidanceSynthesizerOptions(role = 'learned_guidance_synthesizer') {
|
|
470
|
+
getFindingValidatorOptions(role = 'finding_validator', srcDir, mcpServerUrl, mcpServerName, mcpServerBearer) {
|
|
471
|
+
return (0, claude_role_spec_1.roleSpecToClaudeOptions)(this.getFindingValidatorRoleSpec(role, srcDir, mcpServerUrl, mcpServerName, mcpServerBearer));
|
|
472
|
+
}
|
|
473
|
+
getLearnedGuidanceSynthesizerRoleSpec(role = 'learned_guidance_synthesizer') {
|
|
540
474
|
const roleConfig = this.confDict[this.environment]?.[role];
|
|
541
475
|
const systemPrompt = roleConfig?.options?.system_prompt ||
|
|
542
476
|
'You are a senior application security engineer summarizing patterns from past PR-scan ' +
|
|
@@ -548,30 +482,23 @@ You have access to Read, Grep, and Write tools:
|
|
|
548
482
|
'vague to ground a specific rule, OMIT that bucket entirely — it is better to return zero ' +
|
|
549
483
|
'bullets than a bullet the reviewer cannot act on. Output is constrained to the required ' +
|
|
550
484
|
'JSON schema; emit nothing else.';
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
maxTurns: resolvedMaxTurns,
|
|
560
|
-
},
|
|
561
|
-
},
|
|
485
|
+
return {
|
|
486
|
+
roleId: 'learned_guidance_synthesizer',
|
|
487
|
+
systemPrompt,
|
|
488
|
+
maxTurns: roleConfig?.options?.max_turns ?? 1,
|
|
489
|
+
agentName: 'learned-guidance-synthesizer',
|
|
490
|
+
agentDescription: 'Synthesizes class-level learned-guidance bullets from per-CWE dismissal-signal buckets',
|
|
491
|
+
capabilities: {},
|
|
492
|
+
allowedTools: [],
|
|
562
493
|
permissionMode: 'bypassPermissions',
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
schema: learned_guidance_1.LEARNED_GUIDANCE_OUTPUT_SCHEMA,
|
|
566
|
-
},
|
|
494
|
+
model: this.model,
|
|
495
|
+
outputSchema: learned_guidance_1.LEARNED_GUIDANCE_OUTPUT_SCHEMA,
|
|
567
496
|
};
|
|
568
|
-
return options;
|
|
569
497
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
getPrAdversaryOptions(role = 'pr_adversary', srcDir, maxTurns, experimentEnabled, mcpServerUrl, mcpServerName, mcpServerBearer) {
|
|
498
|
+
getLearnedGuidanceSynthesizerOptions(role = 'learned_guidance_synthesizer') {
|
|
499
|
+
return (0, claude_role_spec_1.roleSpecToClaudeOptions)(this.getLearnedGuidanceSynthesizerRoleSpec(role));
|
|
500
|
+
}
|
|
501
|
+
getPrAdversaryRoleSpec(role = 'pr_adversary', srcDir, maxTurns, experimentEnabled, mcpServerUrl, mcpServerName, mcpServerBearer) {
|
|
575
502
|
const roleConfig = this.confDict[this.environment]?.[role];
|
|
576
503
|
let systemPrompt = roleConfig?.options?.system_prompt ||
|
|
577
504
|
'You are a senior application security engineer performing an adversarial second pass on security findings. ' +
|
|
@@ -585,42 +512,25 @@ You have access to Read, Grep, and Write tools:
|
|
|
585
512
|
systemPrompt +=
|
|
586
513
|
'\n\n**Experiment (treatment):** Bias toward dropping borderline issues unless the diff plus quick repo checks show a real attack surface.';
|
|
587
514
|
}
|
|
588
|
-
const
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
model: this.model,
|
|
596
|
-
maxTurns: resolvedMaxTurns,
|
|
597
|
-
},
|
|
598
|
-
},
|
|
515
|
+
const spec = {
|
|
516
|
+
roleId: 'pr_adversary',
|
|
517
|
+
systemPrompt,
|
|
518
|
+
maxTurns: maxTurns ?? roleConfig?.options?.max_turns ?? 15,
|
|
519
|
+
agentName: 'pr-adversary',
|
|
520
|
+
agentDescription: 'Adversarial second pass: filters PR scan findings by concrete failure paths',
|
|
521
|
+
capabilities: { read: true, grep: true },
|
|
599
522
|
permissionMode: 'bypassPermissions',
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
},
|
|
523
|
+
model: this.model,
|
|
524
|
+
outputSchema: security_report_1.SECURITY_REPORT_SCHEMA,
|
|
525
|
+
workingDirectory: srcDir ?? undefined,
|
|
604
526
|
};
|
|
605
|
-
|
|
606
|
-
return
|
|
527
|
+
(0, mcp_internal_1.attachMcpToRoleSpec)(spec, mcpServerUrl, mcpServerName, mcpServerBearer);
|
|
528
|
+
return spec;
|
|
607
529
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
* is constrained to FP_ADVERSARY_REPORT_SCHEMA so the verdict contract stays
|
|
613
|
-
* model-independent across Claude upgrades.
|
|
614
|
-
*
|
|
615
|
-
* Differences from `getPrAdversaryOptions`:
|
|
616
|
-
* - Output schema is the dedicated `fp_adversary_report` (not
|
|
617
|
-
* SECURITY_REPORT_SCHEMA) — verdicts round-trip on `fingerprint`, not `id`.
|
|
618
|
-
* - No `experiment_enabled` plumbing (Lane-2 deliberately omits A/B per M4).
|
|
619
|
-
* - System prompt references the four structured posture fields and
|
|
620
|
-
* `similar_dismissed` precedent that the parent app threads in via
|
|
621
|
-
* `--adversarial-context`.
|
|
622
|
-
*/
|
|
623
|
-
getFpAdversaryOptions(role = 'fp_adversary', srcDir, maxTurns, mcpServerUrl, mcpServerName, mcpServerBearer) {
|
|
530
|
+
getPrAdversaryOptions(role = 'pr_adversary', srcDir, maxTurns, experimentEnabled, mcpServerUrl, mcpServerName, mcpServerBearer) {
|
|
531
|
+
return (0, claude_role_spec_1.roleSpecToClaudeOptions)(this.getPrAdversaryRoleSpec(role, srcDir, maxTurns, experimentEnabled, mcpServerUrl, mcpServerName, mcpServerBearer));
|
|
532
|
+
}
|
|
533
|
+
getFpAdversaryRoleSpec(role = 'fp_adversary', srcDir, maxTurns, mcpServerUrl, mcpServerName, mcpServerBearer) {
|
|
624
534
|
const roleConfig = this.confDict[this.environment]?.[role];
|
|
625
535
|
let systemPrompt = roleConfig?.options?.system_prompt ||
|
|
626
536
|
'You are a senior application security engineer performing an adversarial false-positive review on a full-repository security scan. ' +
|
|
@@ -636,25 +546,23 @@ You have access to Read, Grep, and Write tools:
|
|
|
636
546
|
if (mcpServerUrl) {
|
|
637
547
|
systemPrompt += buildPrReviewerMcpNudgeSystemPromptSuffix(mcpServerName);
|
|
638
548
|
}
|
|
639
|
-
const
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
model: this.model,
|
|
647
|
-
maxTurns: resolvedMaxTurns,
|
|
648
|
-
},
|
|
649
|
-
},
|
|
549
|
+
const spec = {
|
|
550
|
+
roleId: 'fp_adversary',
|
|
551
|
+
systemPrompt,
|
|
552
|
+
maxTurns: maxTurns ?? roleConfig?.options?.max_turns ?? 15,
|
|
553
|
+
agentName: 'fp-adversary',
|
|
554
|
+
agentDescription: 'Adversarial false-positive filter for full-repo scans: emits per-finding (verdict, confidence, rationale) verdicts',
|
|
555
|
+
capabilities: { read: true, grep: true },
|
|
650
556
|
permissionMode: 'bypassPermissions',
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
},
|
|
557
|
+
model: this.model,
|
|
558
|
+
outputSchema: fp_adversary_pass_1.FP_ADVERSARY_REPORT_SCHEMA,
|
|
559
|
+
workingDirectory: srcDir ?? undefined,
|
|
655
560
|
};
|
|
656
|
-
|
|
657
|
-
return
|
|
561
|
+
(0, mcp_internal_1.attachMcpToRoleSpec)(spec, mcpServerUrl, mcpServerName, mcpServerBearer);
|
|
562
|
+
return spec;
|
|
563
|
+
}
|
|
564
|
+
getFpAdversaryOptions(role = 'fp_adversary', srcDir, maxTurns, mcpServerUrl, mcpServerName, mcpServerBearer) {
|
|
565
|
+
return (0, claude_role_spec_1.roleSpecToClaudeOptions)(this.getFpAdversaryRoleSpec(role, srcDir, maxTurns, mcpServerUrl, mcpServerName, mcpServerBearer));
|
|
658
566
|
}
|
|
659
567
|
}
|
|
660
568
|
exports.AgentOptions = AgentOptions;
|