opencode-api-security-testing 5.4.1 → 5.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.ts +61 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-api-security-testing",
3
- "version": "5.4.1",
3
+ "version": "5.4.3",
4
4
  "description": "API Security Testing Plugin for OpenCode - Automated vulnerability scanning and penetration testing",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
package/src/index.ts CHANGED
@@ -334,6 +334,40 @@ const CYBER_SUPERVISOR = DEFAULT_CONFIG.cyber_supervisor;
334
334
  const modelFailureCounts = new Map<string, Map<string, number>>();
335
335
  const sessionFailures = new Map<string, number>();
336
336
 
337
+ // 首条消息追踪 (参考 oh-my-opencode 的 FirstMessageVariantGate 模式)
338
+ const pendingFirstMessages = new Set<string>();
339
+ const injectedSessions = new Set<string>();
340
+
341
+ // 标记新会话创建(由 event hook 调用)
342
+ function markSessionCreated(sessionID: string | undefined): void {
343
+ if (sessionID) {
344
+ pendingFirstMessages.add(sessionID);
345
+ console.log(`[api-security-testing] Session marked for first message injection: ${sessionID}`);
346
+ }
347
+ }
348
+
349
+ // 检查是否是首条消息(由 chat.message hook 调用)
350
+ function isFirstMessage(sessionID: string | undefined): boolean {
351
+ if (!sessionID) return false;
352
+ return pendingFirstMessages.has(sessionID);
353
+ }
354
+
355
+ // 标记首条消息已处理(由 chat.message hook 调用,注入后立即调用)
356
+ function markFirstMessageApplied(sessionID: string | undefined): void {
357
+ if (sessionID) {
358
+ pendingFirstMessages.delete(sessionID);
359
+ injectedSessions.add(sessionID); // 记录已注入
360
+ }
361
+ }
362
+
363
+ // 清理会话状态(由 event hook 在 session.deleted 时调用)
364
+ function clearSessionState(sessionID: string | undefined): void {
365
+ if (sessionID) {
366
+ pendingFirstMessages.delete(sessionID);
367
+ injectedSessions.delete(sessionID);
368
+ }
369
+ }
370
+
337
371
  function getConfigPath(ctx: { directory: string }): string {
338
372
  return join(ctx.directory, SKILL_DIR, "assets", CONFIG_FILE);
339
373
  }
@@ -1266,20 +1300,25 @@ print(json.dumps(result, ensure_ascii=False))
1266
1300
  },
1267
1301
 
1268
1302
  // 赛博监工 Hook - chat.message
1303
+ // 参考 oh-my-opencode 的 FirstMessageVariantGate 模式:只在首条消息注入一次
1269
1304
  "chat.message": async (input, output) => {
1270
1305
  const sessionID = input.sessionID;
1271
1306
 
1272
- // 注入 agents prompt(首次)
1273
- const agentsPrompt = getInjectedAgentsPrompt();
1274
- if (agentsPrompt) {
1275
- const parts = output.parts as Array<{ type: string; text?: string }>;
1276
- const textPart = parts.find(p => p.type === "text");
1277
- if (textPart && textPart.text) {
1278
- textPart.text += agentsPrompt;
1307
+ // 只在首条消息时注入 agents prompt (参考 oh-my-opencode 的 firstMessageVariantGate)
1308
+ if (isFirstMessage(sessionID)) {
1309
+ const agentsPrompt = getInjectedAgentsPrompt();
1310
+ if (agentsPrompt) {
1311
+ const parts = output.parts as Array<{ type: string; text?: string }>;
1312
+ const textPart = parts.find(p => p.type === "text");
1313
+ if (textPart && textPart.text) {
1314
+ textPart.text += agentsPrompt;
1315
+ }
1279
1316
  }
1317
+ // 标记该 session 已注入 (参考 oh-my-opencode 的 markApplied)
1318
+ markFirstMessageApplied(sessionID);
1280
1319
  }
1281
1320
 
1282
- // 赛博监工压力注入
1321
+ // 赛博监工压力注入(仅在失败时)
1283
1322
  if (config.cyber_supervisor.enabled && config.cyber_supervisor.auto_trigger) {
1284
1323
  const failures = getFailureCount(sessionID);
1285
1324
  if (failures > 0 && failures <= config.cyber_supervisor.max_retries) {
@@ -1353,10 +1392,22 @@ ${LEVEL_PROMPTS[level]}
1353
1392
  return output;
1354
1393
  },
1355
1394
 
1356
- // 会话清理
1395
+ // 会话事件处理
1357
1396
  event: async (input) => {
1358
1397
  const { event } = input;
1359
1398
 
1399
+ // 新会话创建 - 标记为首条消息待注入 (参考 oh-my-opencode)
1400
+ if (event.type === "session.created") {
1401
+ const props = event.properties as Record<string, unknown> | undefined;
1402
+ const sessionInfo = props?.info as { id?: string; parentID?: string } | undefined;
1403
+ // 只有主会话(非 fork)才注入
1404
+ if (sessionInfo?.id && !sessionInfo.parentID) {
1405
+ markSessionCreated(sessionInfo.id);
1406
+ console.log(`[api-security-testing] New session created: ${sessionInfo.id}`);
1407
+ }
1408
+ }
1409
+
1410
+ // 会话删除或压缩 - 清理状态
1360
1411
  if (event.type === "session.deleted" || event.type === "session.compacted") {
1361
1412
  const props = event.properties as Record<string, unknown> | undefined;
1362
1413
  let sessionID: string | undefined;
@@ -1368,6 +1419,7 @@ ${LEVEL_PROMPTS[level]}
1368
1419
  }
1369
1420
 
1370
1421
  if (sessionID) {
1422
+ clearSessionState(sessionID);
1371
1423
  resetFailureCount(sessionID);
1372
1424
  resetModelFailures(sessionID);
1373
1425
  }