@xaccefy/pi-casefile 0.9.2 → 0.9.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xaccefy/pi-casefile",
3
- "version": "0.9.2",
3
+ "version": "0.9.3",
4
4
  "description": "Offensive security case tracker for Pi Agent — bug bounties, CTFs, security audits",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -49,7 +49,7 @@
49
49
  ],
50
50
  "main": "src/index.ts",
51
51
  "dependencies": {
52
- "@xaccefy/pi-shared": "0.9.2",
52
+ "@xaccefy/pi-shared": "0.9.3",
53
53
  "undici": "^8.9.0"
54
54
  },
55
55
  "pi": {
package/src/evidence.ts CHANGED
@@ -45,6 +45,14 @@ export type PoCEvidence = {
45
45
  expect: VerifyExpect;
46
46
  /** Optional stronger causality dimension, independent of the authored predicate. */
47
47
  canary?: VerifyCanary;
48
+ /**
49
+ * Differential shape. "inter_host" (default) = same request to target vs a
50
+ * distinct patched control host (body-carried proof). "intra_target" = attack
51
+ * request vs a legitimate same-host `baseline` request (access-control /
52
+ * business-logic classes, where the discriminating variable is identity or a
53
+ * parameter, not the host) — requires `baseline`.
54
+ */
55
+ mode?: "inter_host" | "intra_target";
48
56
  };
49
57
  /** What the script itself saw — corroboration only, never proof. */
50
58
  observations: string[];
@@ -253,6 +261,19 @@ export function parsePoCEvidence(
253
261
  };
254
262
  }
255
263
  }
264
+ if (verify.mode !== undefined && verify.mode !== "inter_host" && verify.mode !== "intra_target") {
265
+ return {
266
+ ok: false,
267
+ error: 'evidence.json verify.mode must be "inter_host" or "intra_target"',
268
+ };
269
+ }
270
+ if (verify.mode === "intra_target" && !isRecord(raw.baseline)) {
271
+ return {
272
+ ok: false,
273
+ error:
274
+ "evidence.json verify.mode intra_target requires baseline — a legitimate same-host request whose response must NOT satisfy the attack predicate",
275
+ };
276
+ }
256
277
  const expect = verify.expect;
257
278
  if (!isRecord(expect))
258
279
  return { ok: false, error: "evidence.json verify.expect must be an object" };
@@ -543,6 +543,29 @@ async function replayRequest(
543
543
  return { attempted: true, url: observedUrl(), note: "unreachable redirect state" };
544
544
  }
545
545
 
546
+ /**
547
+ * Two requests are "the same" when method, URL, header set, and body all match.
548
+ * An intra-target differential whose attack and baseline are identical proves
549
+ * nothing — the discriminating variable must actually differ.
550
+ */
551
+ export function sameRequest(
552
+ a: { method: string; url: string; headers?: Record<string, string>; body?: string },
553
+ b: { method: string; url: string; headers?: Record<string, string>; body?: string },
554
+ ): boolean {
555
+ const norm = (h?: Record<string, string>) =>
556
+ JSON.stringify(
557
+ Object.entries(h ?? {})
558
+ .map(([k, v]) => [k.toLowerCase(), v] as const)
559
+ .sort(([x], [y]) => (x < y ? -1 : x > y ? 1 : 0)),
560
+ );
561
+ return (
562
+ a.method.toUpperCase() === b.method.toUpperCase() &&
563
+ a.url === b.url &&
564
+ (a.body ?? "") === (b.body ?? "") &&
565
+ norm(a.headers) === norm(b.headers)
566
+ );
567
+ }
568
+
546
569
  function injectCanary(
547
570
  verify: PoCEvidence["verify"],
548
571
  token: string | undefined,
@@ -612,6 +635,49 @@ export async function replayVerify(
612
635
  };
613
636
  }
614
637
 
638
+ /**
639
+ * Combine the two observations into the differential verdict. Shared by the
640
+ * inter-host (target vs control host) and intra-target (attack vs same-host
641
+ * baseline) replays — only the note labels differ.
642
+ */
643
+ function judgeDifferential(
644
+ target: HarnessResponseObservation,
645
+ control: HarnessResponseObservation,
646
+ token: string | undefined,
647
+ label: { kind: string; a: string; b: string },
648
+ ): HarnessVerifyResult {
649
+ const attempted = target.attempted && control.attempted;
650
+ const conclusive = target.matched !== undefined && control.matched !== undefined;
651
+ const differential = conclusive
652
+ ? target.matched === true
653
+ ? control.matched === true
654
+ ? "both"
655
+ : "target_only"
656
+ : control.matched === true
657
+ ? "control_only"
658
+ : "neither"
659
+ : undefined;
660
+ const canary = canaryResult(token, target, control);
661
+ const pass =
662
+ attempted &&
663
+ conclusive &&
664
+ differential === "target_only" &&
665
+ (canary === undefined || canary.pass === true);
666
+ return {
667
+ attempted,
668
+ pass,
669
+ status: target.status,
670
+ target,
671
+ control,
672
+ differential,
673
+ canary,
674
+ proofStrength: canary?.pass ? "canary_differential" : "predicate_differential",
675
+ note:
676
+ `harness ${label.kind} ${differential ?? "inconclusive"}: ${label.a} (${target.note}); ` +
677
+ `${label.b} (${control.note})${canary ? `; ${canary.note}` : ""}`,
678
+ };
679
+ }
680
+
615
681
  /**
616
682
  * Execute one harness-owned request template against both the case target and
617
683
  * a distinct control origin. The PoC cannot weaken the control request: the
@@ -658,36 +724,75 @@ export async function replayDifferential(
658
724
  token,
659
725
  opts,
660
726
  );
661
- const attempted = target.attempted && control.attempted;
662
- const conclusive = target.matched !== undefined && control.matched !== undefined;
663
- const targetMatched = target.matched === true;
664
- const controlMatched = control.matched === true;
665
- const differential = conclusive
666
- ? targetMatched
667
- ? controlMatched
668
- ? "both"
669
- : "target_only"
670
- : controlMatched
671
- ? "control_only"
672
- : "neither"
727
+ return judgeDifferential(target, control, token, {
728
+ kind: "differential",
729
+ a: "target",
730
+ b: "control",
731
+ });
732
+ }
733
+
734
+ /**
735
+ * Same-host differential (Tier 2, intra-target). For access-control and
736
+ * business-logic classes the discriminating variable is the attacker's
737
+ * identity or a request parameter, NOT the host — so the sound baseline is a
738
+ * legitimate request to the SAME target, not the same request to another host.
739
+ * The harness sends the attack request and the model-declared `evidence.baseline`
740
+ * request to the case target, applies the attack's `verify.expect` predicates to
741
+ * BOTH responses, and passes only when the proof appears on the attack response
742
+ * and is absent from the baseline (`target_only`, where "target" = attack and
743
+ * "control" = baseline). The baseline is bound to the case target so it cannot
744
+ * be redirected to a weaker origin, and it must differ from the attack request.
745
+ */
746
+ export async function replayIntraTarget(
747
+ evidence: PoCEvidence,
748
+ caseTarget: string,
749
+ opts?: ReplayOptions,
750
+ ): Promise<HarnessVerifyResult> {
751
+ const baseline = evidence.baseline;
752
+ if (!baseline) {
753
+ return {
754
+ attempted: false,
755
+ pass: false,
756
+ note: "intra-target differential requires evidence.baseline (a legitimate same-host request)",
757
+ };
758
+ }
759
+ const attackBinding = verifyUrlBindingError(evidence.verify.url, caseTarget);
760
+ if (attackBinding) {
761
+ return { attempted: false, pass: false, note: `attack binding failed: ${attackBinding}` };
762
+ }
763
+ const baselineBinding = verifyUrlBindingError(baseline.url, caseTarget);
764
+ if (baselineBinding) {
765
+ return { attempted: false, pass: false, note: `baseline binding failed: ${baselineBinding}` };
766
+ }
767
+ if (sameRequest(evidence.verify, baseline)) {
768
+ return {
769
+ attempted: false,
770
+ pass: false,
771
+ note: "attack and baseline requests are identical — an intra-target differential must vary identity or a parameter",
772
+ };
773
+ }
774
+
775
+ const token = evidence.verify.canary
776
+ ? `poc_canary_${randomBytes(24).toString("hex")}`
673
777
  : undefined;
674
- const canary = canaryResult(token, target, control);
675
- const pass =
676
- attempted &&
677
- conclusive &&
678
- differential === "target_only" &&
679
- (canary === undefined || canary.pass === true);
680
- return {
681
- attempted,
682
- pass,
683
- status: target.status,
684
- target,
685
- control,
686
- differential,
687
- canary,
688
- proofStrength: canary?.pass ? "canary_differential" : "predicate_differential",
689
- note:
690
- `harness differential ${differential ?? "inconclusive"}: target (${target.note}); ` +
691
- `control (${control.note})${canary ? `; ${canary.note}` : ""}`,
692
- };
778
+ const attackVerify = injectCanary(evidence.verify, token);
779
+ const attack = await replayRequest(attackVerify, attackVerify.expect, token, opts);
780
+ // The baseline carries the attack's predicates: the proof must be ABSENT here.
781
+ const baselineVerify = injectCanary(
782
+ {
783
+ ...evidence.verify,
784
+ method: baseline.method,
785
+ url: baseline.url,
786
+ headers: baseline.headers,
787
+ body: baseline.body,
788
+ },
789
+ token,
790
+ );
791
+ const base = await replayRequest(baselineVerify, attackVerify.expect, token, opts);
792
+
793
+ return judgeDifferential(attack, base, token, {
794
+ kind: "intra-target",
795
+ a: "attack",
796
+ b: "baseline",
797
+ });
693
798
  }
package/src/index.ts CHANGED
@@ -19,7 +19,12 @@ import {
19
19
  SEVERITY_MATCH_VALUES,
20
20
  validateMainAgentVerdict,
21
21
  } from "./evidence.ts";
22
- import { controlTargetAuthorizationError, replayDifferential } from "./harness-verify.ts";
22
+ import {
23
+ controlTargetAuthorizationError,
24
+ type HarnessVerifyResult,
25
+ replayDifferential,
26
+ replayIntraTarget,
27
+ } from "./harness-verify.ts";
23
28
  import {
24
29
  addCaseResult,
25
30
  addEvidenceItemResult,
@@ -198,11 +203,20 @@ const PromoteSchema = Type.Object(
198
203
  "Optional absolute path to the SAME script as poc_path (sha256-equality is ENFORCED). Defaults to poc_path. The harness runs it with PI_POC_MODE=control and PI_POC_TARGET=control_target.",
199
204
  }),
200
205
  ),
201
- control_target: Type.String({
202
- minLength: 1,
203
- description:
204
- "REQUIRED: a distinct baseline target that lacks the vulnerability and is operator-approved through PI_POC_CONTROL_TARGETS (patched replica, second account, baseline service).",
205
- }),
206
+ mode: Type.Optional(
207
+ Type.String({
208
+ enum: ["inter_host", "intra_target"],
209
+ description:
210
+ "Differential shape. 'inter_host' (default) proves target-dependence with a distinct patched control host — for body-carried proof (file read, injection exfil, info leak, reflection). 'intra_target' proves it with a legitimate same-host baseline request declared in the evidence — for access-control / business-logic classes (IDOR, auth bypass, privilege escalation, logic flaws) where the discriminating variable is identity or a parameter, not the host. In intra_target the evidence must set verify.mode='intra_target' and include a baseline; control_target/control_path are not used.",
211
+ }),
212
+ ),
213
+ control_target: Type.Optional(
214
+ Type.String({
215
+ minLength: 1,
216
+ description:
217
+ "REQUIRED for mode='inter_host': a distinct baseline target that lacks the vulnerability and is operator-approved through PI_POC_CONTROL_TARGETS (patched replica, second account, baseline service). Not used for mode='intra_target'.",
218
+ }),
219
+ ),
206
220
  local: Type.Optional(
207
221
  Type.Boolean({
208
222
  description:
@@ -1181,22 +1195,30 @@ export default function casefileExtension(pi: ExtensionAPI) {
1181
1195
  const pocPath = (params.poc_path as string | undefined)?.trim() ?? "";
1182
1196
  const controlPath = (params.control_path as string | undefined)?.trim() || pocPath;
1183
1197
  const controlTarget = (params.control_target as string | undefined)?.trim() ?? "";
1198
+ const mode: "inter_host" | "intra_target" =
1199
+ (params.mode as string | undefined) === "intra_target" ? "intra_target" : "inter_host";
1200
+ const isIntra = mode === "intra_target";
1184
1201
  if (!pocPath) {
1185
1202
  return fail("poc_path is REQUIRED: absolute path to the PoC script run by the harness.", {
1186
1203
  missingPocPath: true,
1187
1204
  });
1188
1205
  }
1189
- if (!controlTarget) {
1190
- return fail(
1191
- "control_target is REQUIRED: a distinct baseline target that lacks the vulnerability.",
1192
- { missingControlTarget: true },
1193
- );
1194
- }
1195
- if (controlTarget === current.target) {
1196
- return fail(
1197
- "control_target must differ from the case target; a control run against the vulnerable target proves nothing.",
1198
- { controlTargetEqualsCaseTarget: true },
1199
- );
1206
+ // Control-target preconditions apply only to the inter-host differential.
1207
+ // Intra-target proves target-dependence with a same-host baseline request
1208
+ // carried in the evidence, so it needs no control target or control script.
1209
+ if (!isIntra) {
1210
+ if (!controlTarget) {
1211
+ return fail(
1212
+ "control_target is REQUIRED for inter-host mode: a distinct baseline target that lacks the vulnerability. For access-control/logic bugs use mode='intra_target' with an evidence baseline instead.",
1213
+ { missingControlTarget: true },
1214
+ );
1215
+ }
1216
+ if (controlTarget === current.target) {
1217
+ return fail(
1218
+ "control_target must differ from the case target; a control run against the vulnerable target proves nothing.",
1219
+ { controlTargetEqualsCaseTarget: true },
1220
+ );
1221
+ }
1200
1222
  }
1201
1223
  if (params.local === true && process.env.PI_POC_ALLOW_NETWORK !== "1") {
1202
1224
  return fail(
@@ -1204,33 +1226,43 @@ export default function casefileExtension(pi: ExtensionAPI) {
1204
1226
  { networkNotAuthorized: true },
1205
1227
  );
1206
1228
  }
1207
- const controlAuthorization = controlTargetAuthorizationError(controlTarget);
1208
- if (controlAuthorization) {
1209
- return fail(
1210
- `CONTROL AUTHORIZATION FAILED: ${controlAuthorization}. ` +
1211
- "The operator must set PI_POC_CONTROL_TARGETS to the exact approved control host/origin before this control can anchor confirmation.",
1212
- { controlNotAuthorized: true },
1213
- );
1229
+ if (!isIntra) {
1230
+ const controlAuthorization = controlTargetAuthorizationError(controlTarget);
1231
+ if (controlAuthorization) {
1232
+ return fail(
1233
+ `CONTROL AUTHORIZATION FAILED: ${controlAuthorization}. ` +
1234
+ "The operator must set PI_POC_CONTROL_TARGETS to the exact approved control host/origin before this control can anchor confirmation.",
1235
+ { controlNotAuthorized: true },
1236
+ );
1237
+ }
1214
1238
  }
1215
1239
 
1216
- // Same-file contract (anti-cheat): control must be the SAME bytes as the
1217
- // PoC, differing only via the harness-set env. Check BEFORE any run.
1240
+ // Anti-cheat: hash the PoC (always) and, for inter-host, require the
1241
+ // control script to be the SAME bytes (differing only via harness env).
1218
1242
  let pocHash: string | undefined;
1219
- let controlHash: string | undefined;
1220
1243
  try {
1221
1244
  pocHash = createHash("sha256").update(readFileSync(pocPath)).digest("hex");
1222
- controlHash = createHash("sha256").update(readFileSync(controlPath)).digest("hex");
1223
1245
  } catch (e) {
1224
- return fail(
1225
- `Cannot read PoC/control scripts for the same-file check: ${(e as Error).message}`,
1226
- { sameFileCheckFailed: true },
1227
- );
1246
+ return fail(`Cannot read PoC script: ${(e as Error).message}`, {
1247
+ sameFileCheckFailed: true,
1248
+ });
1228
1249
  }
1229
- if (pocHash !== controlHash) {
1230
- return fail(
1231
- "CONTROL CHECK FAILED: control_path must be the SAME script as poc_path (sha256 mismatch). Case remains investigating.",
1232
- { controlHashMismatch: true },
1233
- );
1250
+ if (!isIntra) {
1251
+ let controlHash: string | undefined;
1252
+ try {
1253
+ controlHash = createHash("sha256").update(readFileSync(controlPath)).digest("hex");
1254
+ } catch (e) {
1255
+ return fail(
1256
+ `Cannot read control script for the same-file check: ${(e as Error).message}`,
1257
+ { sameFileCheckFailed: true },
1258
+ );
1259
+ }
1260
+ if (pocHash !== controlHash) {
1261
+ return fail(
1262
+ "CONTROL CHECK FAILED: control_path must be the SAME script as poc_path (sha256 mismatch). Case remains investigating.",
1263
+ { controlHashMismatch: true },
1264
+ );
1265
+ }
1234
1266
  }
1235
1267
 
1236
1268
  // ── OOB callback (Tier 1, opt-in for blind classes) ──
@@ -1251,12 +1283,11 @@ export default function casefileExtension(pi: ExtensionAPI) {
1251
1283
  });
1252
1284
 
1253
1285
  const caseTarget = current.target ?? "";
1254
- // Determinism: TWO target runs + one control run. Exit 0 is run
1255
- // integrity only; nonce-bound body evidence and the harness-owned
1256
- // target/control replay form the machine gate.
1286
+ // Determinism: TWO target runs. Exit 0 is run integrity only; nonce-bound
1287
+ // body evidence plus the harness-owned differential replay (inter-host
1288
+ // control, or intra-target same-host baseline) form the machine gate.
1257
1289
  const run1 = runPoc(pocPath, runOptions("poc", caseTarget));
1258
1290
  const run2 = runPoc(pocPath, runOptions("poc", caseTarget));
1259
- const controlRun = runPoc(controlPath, runOptions("control", controlTarget));
1260
1291
 
1261
1292
  const evidenceRun = (
1262
1293
  r: PocRun,
@@ -1304,28 +1335,55 @@ export default function casefileExtension(pi: ExtensionAPI) {
1304
1335
  evidenceRun(run1, "poc", caseTarget),
1305
1336
  evidenceRun(run2, "poc", caseTarget),
1306
1337
  ];
1307
- const control = evidenceRun(controlRun, "control", controlTarget);
1308
-
1309
- // Tier 2 (docs/poc-trust-model.md): the harness executes the SAME
1310
- // request template against target and operator-approved control, applying
1311
- // the target's predicates to both. DNS is pinned at connect time.
1312
- const harnessVerified = await replayDifferential(
1313
- targetRuns[0].evidence,
1314
- caseTarget,
1315
- controlTarget,
1316
- { allowPrivate: process.env.PI_POC_ALLOW_PRIVATE_REPLAY === "1" },
1317
- );
1318
1338
 
1339
+ const allowPrivateReplay = process.env.PI_POC_ALLOW_PRIVATE_REPLAY === "1";
1340
+ let harnessVerified: HarnessVerifyResult;
1341
+ let controlRun: PocEvidenceRun | undefined;
1342
+ if (isIntra) {
1343
+ // Intra-target: prove target-dependence with the evidence's same-host
1344
+ // baseline request — no separate control run. The harness sends attack +
1345
+ // baseline to the case target and requires the proof on attack only.
1346
+ const ev0 = targetRuns[0].evidence;
1347
+ if (ev0.verify.mode !== "intra_target") {
1348
+ return fail(
1349
+ "INTRA-TARGET FAILED: the PoC's evidence.json must set verify.mode='intra_target' when promoting in intra-target mode.",
1350
+ { intraModeMismatch: true },
1351
+ );
1352
+ }
1353
+ if (!ev0.baseline) {
1354
+ return fail(
1355
+ "INTRA-TARGET FAILED: evidence.json must include a baseline — a legitimate same-host request whose response must NOT satisfy the attack predicate.",
1356
+ { intraBaselineMissing: true },
1357
+ );
1358
+ }
1359
+ harnessVerified = await replayIntraTarget(ev0, caseTarget, {
1360
+ allowPrivate: allowPrivateReplay,
1361
+ });
1362
+ } else {
1363
+ // Inter-host (Tier 2): the harness executes the SAME request template
1364
+ // against target and operator-approved control, applying the target's
1365
+ // predicates to both. DNS is pinned at connect time.
1366
+ controlRun = evidenceRun(
1367
+ runPoc(controlPath, runOptions("control", controlTarget)),
1368
+ "control",
1369
+ controlTarget,
1370
+ );
1371
+ harnessVerified = await replayDifferential(
1372
+ targetRuns[0].evidence,
1373
+ caseTarget,
1374
+ controlTarget,
1375
+ { allowPrivate: allowPrivateReplay },
1376
+ );
1377
+ }
1319
1378
  const bundle: PendingConfirmation = {
1320
1379
  caseId,
1321
1380
  ranAt: new Date().toISOString(),
1322
1381
  pocPath,
1323
1382
  pocSha256: pocHash,
1324
- controlPath,
1325
- controlTarget,
1383
+ mode,
1326
1384
  targetRuns,
1327
- controlRun: control,
1328
1385
  harnessVerified,
1386
+ ...(isIntra ? {} : { controlPath, controlTarget, controlRun }),
1329
1387
  };
1330
1388
 
1331
1389
  let record: CaseRecord;
@@ -1343,11 +1401,11 @@ export default function casefileExtension(pi: ExtensionAPI) {
1343
1401
  type: "text",
1344
1402
  text:
1345
1403
  `Phase 1 complete — evidence bundle recorded on ${caseId} (expires in 1h).\n` +
1346
- `Target runs: 2, Control run: 1 — all with validated nonce-bound evidence.json.\n` +
1404
+ `Mode: ${mode}. ${isIntra ? "Target runs: 2, same-host baseline differential" : "Target runs: 2, Control run: 1"} — all with validated nonce-bound evidence.json.\n` +
1347
1405
  `Evidence sha256: ${targetRuns[0].evidenceSha256}\n` +
1348
1406
  `PoC script sha256 (at run time): ${pocHash}\n` +
1349
1407
  `Harness verify replay: ${harnessVerified.attempted ? (harnessVerified.pass ? `PASS (status ${harnessVerified.status})` : `FAILED — ${harnessVerified.note}`) : harnessVerified.note}\n` +
1350
- `\nMAIN-AGENT REVIEW REQUIRED (do not delegate): inspect case ${caseId}, PoC ${pocPath}, control ${controlTarget}, evidence ${targetRuns[0].evidenceSha256}, and PoC hash ${pocHash}. Hunt for a trivial predicate or fabricated differential and perform a concrete disconfirmation attempt, then call ConfirmFinding yourself. A CONFIRMED call performs and stores a fresh harness-owned target/control replay; NOT_CONFIRMED keeps the case investigating.`,
1408
+ `\nMAIN-AGENT REVIEW REQUIRED (do not delegate): inspect case ${caseId}, PoC ${pocPath}, ${isIntra ? "same-host baseline" : `control ${controlTarget}`}, evidence ${targetRuns[0].evidenceSha256}, and PoC hash ${pocHash}. Hunt for a trivial predicate or fabricated differential and perform a concrete disconfirmation attempt, then call ConfirmFinding yourself. A CONFIRMED call performs and stores a fresh harness-owned ${isIntra ? "attack/baseline" : "target/control"} replay; NOT_CONFIRMED keeps the case investigating.`,
1351
1409
  },
1352
1410
  ],
1353
1411
  details: {
@@ -1355,9 +1413,10 @@ export default function casefileExtension(pi: ExtensionAPI) {
1355
1413
  bundle: {
1356
1414
  caseId,
1357
1415
  ranAt: bundle.ranAt,
1416
+ mode,
1358
1417
  pocPath,
1359
- controlPath,
1360
- controlTarget,
1418
+ controlPath: isIntra ? undefined : controlPath,
1419
+ controlTarget: isIntra ? undefined : controlTarget,
1361
1420
  pocSha256: pocHash,
1362
1421
  evidenceSha256: targetRuns[0].evidenceSha256,
1363
1422
  harnessVerified,
@@ -1426,16 +1485,29 @@ export default function casefileExtension(pi: ExtensionAPI) {
1426
1485
  if (!bundle) {
1427
1486
  throw new Error("No pending confirmation on this case — run PromoteFinding first");
1428
1487
  }
1429
- const controlAuthorizationError = controlTargetAuthorizationError(bundle.controlTarget);
1430
- if (controlAuthorizationError) {
1431
- throw new Error(`CONTROL AUTHORIZATION FAILED: ${controlAuthorizationError}`);
1488
+ const allowPrivate = process.env.PI_POC_ALLOW_PRIVATE_REPLAY === "1";
1489
+ const caseTargetForReplay = current.target ?? bundle.targetRuns[0].target;
1490
+ let replay: HarnessVerifyResult;
1491
+ if (bundle.mode === "intra_target") {
1492
+ // Same-host attack-vs-baseline replay; no control target to authorize.
1493
+ replay = await replayIntraTarget(bundle.targetRuns[0].evidence, caseTargetForReplay, {
1494
+ allowPrivate,
1495
+ });
1496
+ } else {
1497
+ if (!bundle.controlTarget) {
1498
+ throw new Error("inter-host confirmation requires a control target");
1499
+ }
1500
+ const controlAuthorizationError = controlTargetAuthorizationError(bundle.controlTarget);
1501
+ if (controlAuthorizationError) {
1502
+ throw new Error(`CONTROL AUTHORIZATION FAILED: ${controlAuthorizationError}`);
1503
+ }
1504
+ replay = await replayDifferential(
1505
+ bundle.targetRuns[0].evidence,
1506
+ caseTargetForReplay,
1507
+ bundle.controlTarget,
1508
+ { allowPrivate },
1509
+ );
1432
1510
  }
1433
- const replay = await replayDifferential(
1434
- bundle.targetRuns[0].evidence,
1435
- current.target ?? bundle.targetRuns[0].target,
1436
- bundle.controlTarget,
1437
- { allowPrivate: process.env.PI_POC_ALLOW_PRIVATE_REPLAY === "1" },
1438
- );
1439
1511
  phase2Verification = {
1440
1512
  at: new Date().toISOString(),
1441
1513
  result: replay,
package/src/ledger.ts CHANGED
@@ -31,7 +31,7 @@ import {
31
31
  parsePoCEvidence,
32
32
  validateMainAgentVerdict,
33
33
  } from "./evidence.ts";
34
- import { type HarnessVerifyResult, verifyUrlBindingError } from "./harness-verify.ts";
34
+ import { type HarnessVerifyResult, sameRequest, verifyUrlBindingError } from "./harness-verify.ts";
35
35
  import {
36
36
  assertSafeRegularFile,
37
37
  ensureSafeStateDirectory,
@@ -328,10 +328,21 @@ export type PendingConfirmation = {
328
328
  pocPath: string;
329
329
  /** SHA-256 of the PoC script AT RUN TIME — re-hashed at confirm to catch edits. */
330
330
  pocSha256: string;
331
- controlPath: string;
332
- controlTarget: string;
331
+ /**
332
+ * Differential shape. Absent/"inter_host" (default) = same request to target
333
+ * vs a distinct patched control host, proven by a separate control run +
334
+ * `replayDifferential`. "intra_target" = attack vs a legitimate same-host
335
+ * `baseline` request inside each run's evidence, proven by `replayIntraTarget`
336
+ * — no control run or control target (access-control / business-logic classes).
337
+ */
338
+ mode?: "inter_host" | "intra_target";
339
+ /** inter_host only. */
340
+ controlPath?: string;
341
+ /** inter_host only. */
342
+ controlTarget?: string;
333
343
  targetRuns: [PocEvidenceRun, PocEvidenceRun];
334
- controlRun: PocEvidenceRun;
344
+ /** inter_host only — the same PoC run against the control target. */
345
+ controlRun?: PocEvidenceRun;
335
346
  /** Harness's own replay of evidence.verify (public targets). Absent = legacy bundle. */
336
347
  harnessVerified?: HarnessVerifyResult;
337
348
  /** Harness-owned OOB listener log for the run (opt-in blind classes). */
@@ -2064,13 +2075,19 @@ function validateRunEvidence(run: PocEvidenceRun, label: string): void {
2064
2075
  }
2065
2076
 
2066
2077
  /** Determinism + differential on normalized evidence (nonce/observations stripped). */
2067
- function assertEvidenceDifferential(bundle: PendingConfirmation): void {
2078
+ function assertEvidenceDifferential(bundle: PendingConfirmation, isIntra = false): void {
2068
2079
  const [r1, r2] = bundle.targetRuns;
2069
2080
  if (normalizeEvidence(r1.evidence) !== normalizeEvidence(r2.evidence)) {
2070
2081
  throw new Error(
2071
2082
  "Target runs produced inconsistent evidence — the exploit did not reproduce deterministically",
2072
2083
  );
2073
2084
  }
2085
+ // Intra-target target-dependence is proven by the harness attack-vs-baseline
2086
+ // replay (same host), not by comparing a target run to a separate control run.
2087
+ if (isIntra) return;
2088
+ if (!bundle.controlRun) {
2089
+ throw new Error("inter-host confirmation requires a control run");
2090
+ }
2074
2091
  if (normalizeEvidence(r1.evidence) === normalizeEvidence(bundle.controlRun.evidence)) {
2075
2092
  throw new Error(
2076
2093
  "Control run produced identical evidence to the target — the claimed impact is not target-dependent",
@@ -2143,6 +2160,7 @@ function assertHarnessCanary(
2143
2160
  function assertMainAgentVerification(
2144
2161
  bundle: PendingConfirmation,
2145
2162
  verification: MainAgentVerification | undefined,
2163
+ isIntra = false,
2146
2164
  ): asserts verification is MainAgentVerification {
2147
2165
  if (!verification) {
2148
2166
  throw new Error(
@@ -2179,8 +2197,16 @@ function assertMainAgentVerification(
2179
2197
  if (!targetUrl || verifyUrlBindingError(targetUrl, targetIdentity)) {
2180
2198
  throw new Error("MAIN-AGENT REPLAY FAILED: target transcript is not bound to the case target");
2181
2199
  }
2182
- if (!controlUrl || verifyUrlBindingError(controlUrl, bundle.controlTarget)) {
2183
- throw new Error("MAIN-AGENT REPLAY FAILED: control transcript is not bound to control_target");
2200
+ // Intra-target: the "control" transcript is the legitimate baseline request,
2201
+ // which is bound to the SAME case target. Inter-host: it is bound to the
2202
+ // distinct control target.
2203
+ const controlBindTarget = isIntra ? targetIdentity : bundle.controlTarget;
2204
+ if (!controlUrl || !controlBindTarget || verifyUrlBindingError(controlUrl, controlBindTarget)) {
2205
+ throw new Error(
2206
+ isIntra
2207
+ ? "MAIN-AGENT REPLAY FAILED: baseline transcript is not bound to the case target"
2208
+ : "MAIN-AGENT REPLAY FAILED: control transcript is not bound to control_target",
2209
+ );
2184
2210
  }
2185
2211
  }
2186
2212
 
@@ -2230,6 +2256,75 @@ export function assertPromotable(id: string): CaseRecord {
2230
2256
  return current;
2231
2257
  }
2232
2258
 
2259
+ /**
2260
+ * Phase 1 (intra-target): validate a same-host attack-vs-baseline bundle. The
2261
+ * differential is proven by the harness replay (attack matched, baseline did
2262
+ * not, both against the case target), not by a separate control run — the
2263
+ * discriminating variable is the request's identity or a parameter, not the host.
2264
+ */
2265
+ function validateIntraTargetBundle(
2266
+ current: CaseRecord,
2267
+ id: string,
2268
+ bundle: PendingConfirmation,
2269
+ ): CaseRecord {
2270
+ if (bundle.targetRuns.length !== 2) {
2271
+ throw new Error("Intra-target confirmation requires two target runs");
2272
+ }
2273
+ if (bundle.controlRun || bundle.controlTarget) {
2274
+ throw new Error(
2275
+ "Intra-target confirmation must not carry a control run or control target — the baseline is a same-host request inside the evidence",
2276
+ );
2277
+ }
2278
+ const targetRunTarget = bundle.targetRuns[0]?.target;
2279
+ if (!targetRunTarget || bundle.targetRuns.some((r) => r.target !== targetRunTarget)) {
2280
+ throw new Error("Intra-target confirmation requires both runs against the same case target");
2281
+ }
2282
+ let pocHash: string | undefined;
2283
+ try {
2284
+ pocHash = createHash("sha256").update(readFileSync(bundle.pocPath)).digest("hex");
2285
+ } catch {
2286
+ pocHash = undefined;
2287
+ }
2288
+ if (!pocHash || (bundle.pocSha256 && bundle.pocSha256 !== pocHash)) {
2289
+ throw new Error("pocSha256 does not match the PoC file on disk");
2290
+ }
2291
+ for (const run of bundle.targetRuns) {
2292
+ validateRunEvidence(run, `${run.mode} run`);
2293
+ const ev = run.evidence;
2294
+ if (ev.verify.mode !== "intra_target") {
2295
+ throw new Error(
2296
+ "INTRA-TARGET FAILED: each run's evidence.verify.mode must be 'intra_target'",
2297
+ );
2298
+ }
2299
+ if (!ev.baseline) {
2300
+ throw new Error(
2301
+ "INTRA-TARGET FAILED: evidence.baseline (a legitimate same-host request) is required",
2302
+ );
2303
+ }
2304
+ const attackBinding = verifyUrlBindingError(ev.verify.url, targetRunTarget);
2305
+ if (attackBinding) throw new Error(`ATTACK BINDING FAILED: ${attackBinding}`);
2306
+ const baselineBinding = verifyUrlBindingError(ev.baseline.url, targetRunTarget);
2307
+ if (baselineBinding) throw new Error(`BASELINE BINDING FAILED: ${baselineBinding}`);
2308
+ if (ev.baseline && sameRequest(ev.verify, ev.baseline)) {
2309
+ throw new Error(
2310
+ "INTRA-TARGET FAILED: attack and baseline requests are identical — vary identity or a parameter",
2311
+ );
2312
+ }
2313
+ }
2314
+ if (bundle.caseId !== id) throw new Error("Pending confirmation caseId mismatch");
2315
+ assertEvidenceDifferential(bundle, true);
2316
+ // Machine floor: attack matched, baseline did not, both against the case target.
2317
+ assertMachineConfirmation(bundle);
2318
+ assertHarnessCanary(
2319
+ bundle.harnessVerified,
2320
+ bundle.targetRuns[0].evidence.verify.canary !== undefined,
2321
+ "PHASE-1 CANARY FAILED",
2322
+ );
2323
+ const next = buildRecord({ pendingConfirmation: bundle }, current);
2324
+ validateCase(next);
2325
+ return next;
2326
+ }
2327
+
2233
2328
  /**
2234
2329
  * Phase 1: record the harness-observed evidence bundle on the case. The whole
2235
2330
  * contract is validated here — same-file control, nonce binding, run
@@ -2248,6 +2343,11 @@ export function storePendingConfirmation(id: string, bundle: PendingConfirmation
2248
2343
  );
2249
2344
  }
2250
2345
  if (bundle.caseId !== id) throw new Error("Pending confirmation caseId mismatch");
2346
+ if (bundle.mode === "intra_target") {
2347
+ const next = validateIntraTargetBundle(current, id, bundle);
2348
+ upsertCase(db, next);
2349
+ return next;
2350
+ }
2251
2351
  if (bundle.targetRuns.length !== 2 || !bundle.controlRun) {
2252
2352
  throw new Error("Pending confirmation requires two target runs and one control run");
2253
2353
  }
@@ -2423,10 +2523,14 @@ export function applyConfirmationResult(
2423
2523
 
2424
2524
  // CONFIRMED — re-validate the whole bundle (defense in depth; the case may
2425
2525
  // have been touched between phase 1 and the verdict).
2426
- for (const run of [...bundle.targetRuns, bundle.controlRun]) {
2526
+ const isIntra = bundle.mode === "intra_target";
2527
+ const allRuns = isIntra
2528
+ ? [...bundle.targetRuns]
2529
+ : [...bundle.targetRuns, ...(bundle.controlRun ? [bundle.controlRun] : [])];
2530
+ for (const run of allRuns) {
2427
2531
  validateRunEvidence(run, `${run.mode} run`);
2428
2532
  }
2429
- assertEvidenceDifferential(bundle);
2533
+ assertEvidenceDifferential(bundle, isIntra);
2430
2534
  assertMachineConfirmation(bundle);
2431
2535
  assertHarnessCanary(bundle.harnessVerified, canaryRequested, "PHASE-1 CANARY FAILED");
2432
2536
  let pocHash: string | undefined;
@@ -2450,7 +2554,7 @@ export function applyConfirmationResult(
2450
2554
  `(bundle target: ${targetRun.target}, case target: ${current.target ?? "(none)"}).`,
2451
2555
  );
2452
2556
  }
2453
- if (current.target === bundle.controlTarget) {
2557
+ if (!isIntra && current.target === bundle.controlTarget) {
2454
2558
  throw new Error(
2455
2559
  "Case target now equals the control target — the claimed impact is not target-dependent; " +
2456
2560
  "re-run PromoteFinding with a distinct control_target.",
@@ -2469,7 +2573,7 @@ export function applyConfirmationResult(
2469
2573
  // Phase 1 proves the evidence floor. Phase 2 must freshly replay that same
2470
2574
  // request inside the main agent's ConfirmFinding call; a caller-provided
2471
2575
  // boolean is not accepted as proof of re-execution.
2472
- assertMainAgentVerification(bundle, phase2Verification);
2576
+ assertMainAgentVerification(bundle, phase2Verification, isIntra);
2473
2577
 
2474
2578
  const reproductionItem: EvidenceItem = {
2475
2579
  id: `ev_${stableShortId(`${id}\nreproduction\n${targetRun.ranAt}`)}`,
@@ -2480,7 +2584,7 @@ export function applyConfirmationResult(
2480
2584
  // exists, so the item stays artifact-backed and re-verifiable.
2481
2585
  artifactPath: targetRun.evidencePath ? basename(targetRun.evidencePath) : "evidence.json",
2482
2586
  sha256: targetRun.evidenceSha256,
2483
- summary: `PoC evidence accepted (2 target runs + control; ${recorded.proofStrength}) — main agent semantic confirmation${verdict.model ? ` (${verdict.model})` : ""}`,
2587
+ summary: `PoC evidence accepted (2 target runs + ${isIntra ? "same-host baseline" : "control"}; ${recorded.proofStrength}) — main agent semantic confirmation${verdict.model ? ` (${verdict.model})` : ""}`,
2484
2588
  createdAt: targetRun.ranAt,
2485
2589
  };
2486
2590
 
@@ -2506,17 +2610,30 @@ export function applyConfirmationResult(
2506
2610
  mode: "poc",
2507
2611
  target: targetRun.target,
2508
2612
  },
2509
- controlVerified: {
2510
- path: bundle.controlPath,
2511
- exitCode: bundle.controlRun.exitCode,
2512
- ranAt: bundle.controlRun.ranAt,
2513
- output: bundle.controlRun.output,
2514
- sandbox: bundle.controlRun.sandbox,
2515
- completed: true,
2516
- outputComplete: true,
2517
- mode: "control",
2518
- target: bundle.controlRun.target,
2519
- },
2613
+ controlVerified:
2614
+ isIntra || !bundle.controlRun
2615
+ ? {
2616
+ path: bundle.pocPath,
2617
+ exitCode: targetRun.exitCode,
2618
+ ranAt: targetRun.ranAt,
2619
+ output: `intra-target baseline (same host): ${bundle.harnessVerified?.control?.note ?? "baseline did not satisfy the attack predicate"}`,
2620
+ sandbox: targetRun.sandbox,
2621
+ completed: true,
2622
+ outputComplete: true,
2623
+ mode: "baseline",
2624
+ target: targetRun.target,
2625
+ }
2626
+ : {
2627
+ path: bundle.controlPath ?? bundle.pocPath,
2628
+ exitCode: bundle.controlRun.exitCode,
2629
+ ranAt: bundle.controlRun.ranAt,
2630
+ output: bundle.controlRun.output,
2631
+ sandbox: bundle.controlRun.sandbox,
2632
+ completed: true,
2633
+ outputComplete: true,
2634
+ mode: "control",
2635
+ target: bundle.controlRun.target,
2636
+ },
2520
2637
  disconfirmation: verdict.disconfirmation_attempt,
2521
2638
  confirmerVerdict: recorded,
2522
2639
  pendingConfirmation: undefined,
package/src/workflow.ts CHANGED
@@ -84,6 +84,15 @@ ${d.reference}
84
84
 
85
85
  **Swarm delegation boundary:** only auditor (HUNT rounds), tracer (TRACE), skeptic (high-confidence challenge), and chain (CHAIN) run as subagents. You, the main coordinator, own RECON, VALIDATE/PoC writing, ConfirmFinding, patching, final reports, state decisions, and all orchestration.
86
86
 
87
+ ## Dispatch Discipline (fewer calls, batched, verifier-in-the-loop)
88
+
89
+ This pipeline is SEQUENTIAL-dependent: each stage consumes the previous stage's output, so only HUNT genuinely fans out. Do NOT scatter one async subagent call per finding — that pays full coordination cost for no parallel payoff and turns you into a message router. Two rules keep it cheap:
90
+
91
+ 1. **Two dispatch points, each ONE batched call.** (a) **HUNT** — one call whose \`runs.all\`/\`tasks\` carries ≤3 batched auditors (related classes grouped by surface/family). (b) **TRACE+SKEPTIC** — one call carrying a trace task per prioritized finding, plus a skeptic task for each \`confidence: high\` finding; batch the whole round in a single dispatch, never one dispatch per finding. RECON, VALIDATE/PoC, ConfirmFinding, CHAIN, and REPORT stay INLINE with you.
92
+ 2. **Barrier, then submit the whole batch in one pass.** Let the batched call return ALL of its results, then \`PipelineSubmit\` each output back-to-back before choosing the next stage. Do not interleave fresh dispatches with the delivery of a prior batch. A crash / timeout / unparseable / schema-invalid result for one item is a RETRY for THAT item in the next batch — never a verdict, never a reason to drop the stage.
93
+
94
+ Every stage boundary is a \`PipelineSubmit\` gate (schema + pre-filter, in code): nothing advances on prose. This is the verifier-in-the-loop — the same principle the machine PoC gate applies at CONFIRMED, applied at every stage transition.
95
+
87
96
  ## Stage Machine (run in order — you are the coordinator)
88
97
 
89
98
  RECON (you, inline) → **HUNT** (2-3 batched auditor subagents) → TRACE (tracer for prioritized findings) → SKEPTIC (bounded high-risk review) → VALIDATE (you, inline) → CHAIN (chain subagent) → REPORT (you, inline)
@@ -192,7 +201,12 @@ Weak: "Tried to disprove. Could not." — insufficient.
192
201
 
193
202
  **Main-agent validation only:** do not dispatch validation. You write the smallest reliable PoC that demonstrates the **maximum reachable impact** of the vulnerability, set the case's poc/evidence/impact/severity/target fields, and run PromoteFinding yourself. "Smallest" means no fragile ceremony, mocks, or unrelated exploit steps — not a weaker impact demonstration. Do not stop at a benign marker if a stronger in-scope, non-destructive primitive is reachable (read/write, privilege change, account takeover path, data exposure, etc.). If the PoC fails, refine it yourself up to the local budget; if proof cannot meet the gate, kill or keep the case investigating with the exact blocker.
194
203
 
195
- **PromoteFinding (phase 1) — evidence bundle, not markers.** Call it with \`poc_path\`, an operator-approved \`control_target\` from \`PI_POC_CONTROL_TARGETS\`, optional same-byte \`control_path\` (defaults to \`poc_path\`), and \`local: true\` when the bug needs network. Every run must complete with fully captured output and write nonce-bound \`evidence.json\` whose \`expect\` includes \`body_contains\` or \`body_regex\`; status-only evidence is rejected. The harness pins DNS at connect time, keeps redirects on the bound host, sends the same request to target/control, and requires two conclusive responses with \`target_only\`. Private replay requires operator authorization. Blind/OOB classes fail closed until a source-separated oracle exists.
204
+ **PromoteFinding (phase 1) — evidence bundle, not markers.** Pick the differential \`mode\` that fits the class:
205
+
206
+ - **\`mode: "inter_host"\` (default)** — body-carried proof that is the same on any host (file read, injection exfil, info leak, reflection). Call with \`poc_path\`, an operator-approved \`control_target\` from \`PI_POC_CONTROL_TARGETS\`, optional same-byte \`control_path\` (defaults to \`poc_path\`), and \`local: true\` when the bug needs network. The harness sends the SAME request to target and control and requires \`target_only\`.
207
+ - **\`mode: "intra_target"\`** — access-control and business-logic classes (IDOR/BOLA, auth bypass, privilege escalation, mass assignment, logic/price tampering) where the discriminating variable is the attacker's IDENTITY or a PARAMETER, not the host. A different host lacks the victim's object/state, so inter-host proves nothing. Instead the evidence declares \`verify.mode: "intra_target"\` and a \`baseline\` (a legitimate SAME-host request — the attacker's own object, a properly-authorized request, the field omitted); the harness sends attack + baseline to the case target and requires the proof on the attack response only. No \`control_target\`/\`control_path\`.
208
+
209
+ Every run must complete with fully captured output and write nonce-bound \`evidence.json\` whose \`expect\` includes \`body_contains\` or \`body_regex\`; status-only evidence is rejected. The harness pins DNS at connect time and keeps redirects on the bound host. Private replay requires operator authorization. Blind/OOB classes fail closed until a source-separated oracle exists.
196
210
 
197
211
  **ConfirmFinding (phase 2) — main-agent-only commit.** After PromoteFinding succeeds, do not dispatch confirmation. The main/coordinator agent must inspect the exact PoC/evidence, hunt trivial predicates/fabrication, attempt disconfirmation, and call \`ConfirmFinding(case_id, verdict)\` itself. A CONFIRMED call performs and stores a fresh harness-owned target/control replay; a caller-supplied re-execution checkbox is not accepted. CONFIRMED requires \`re_execution_note\`, \`differential: "target_only"\`, and the main agent's \`disconfirmation_attempt\`. Worker processes are rejected. **Never \`CaseUpdate(status: "confirmed")\` directly.**
198
212
 
@@ -345,7 +359,7 @@ Write the final report as a self-contained markdown file at the report path Case
345
359
  - **No finding is confirmed until its target is verified in scope** per the program's scope instruction. Out-of-scope findings are killed, not confirmed.
346
360
  - **No finding is validated without a reachability trace** showing REACHABLE. UNREACHABLE requires a concrete blocker; unresolved auth/WAF/source ambiguity stays INVESTIGATING or BLOCKED, not killed.
347
361
  - **High-confidence findings: do your own adversarial disconfirmation.** No skeptic subagent in lite mode — actively try to disprove your own finding and document the attempt in \`disconfirmation\`. Failing to disprove is the expected outcome.
348
- - **Confirmed requires** evidence + poc + impact + severity + target + disconfirmation, via the two-phase gate: **PromoteFinding** with same-script target/control execution and an operator-approved \`control_target\`; then you, the main agent, inspect the bundle, attempt disconfirmation, and call **ConfirmFinding** yourself. That call captures a fresh second target/control replay before commit. Do not delegate validation or confirmation. The machine gate requires zero-exit complete runs, nonce binding, body evidence, determinism, DNS-pinned conclusive \`target_only\` replay, and script identity; zero exit is never proof and markers are diagnostic only. \`local:true\` and private replay remain operator-gated. No mocks and no direct \`CaseUpdate(status: "confirmed")\`.
362
+ - **Confirmed requires** evidence + poc + impact + severity + target + disconfirmation, via the two-phase gate: **PromoteFinding** in the differential \`mode\` that fits the class — \`inter_host\` (default) with an operator-approved \`control_target\` for body-carried proof (file read, injection exfil, info leak, reflection), or \`intra_target\` for access-control/logic classes (IDOR, auth bypass, privilege escalation, logic), where the evidence declares \`verify.mode:"intra_target"\` + a same-host \`baseline\` and the harness requires the proof on the attack response only (no control target). Then you, the main agent, inspect the bundle, attempt disconfirmation, and call **ConfirmFinding** yourself. That call captures a fresh second replay before commit. Do not delegate validation or confirmation. The machine gate requires zero-exit complete runs, nonce binding, body evidence, determinism, a DNS-pinned conclusive \`target_only\` differential, and script identity; zero exit is never proof and markers are diagnostic only. \`local:true\` and private replay remain operator-gated. No mocks and no direct \`CaseUpdate(status: "confirmed")\`.
349
363
  - **Severity is derived from proven PoC impact, not theory.** Demonstrate and claim the highest impact the attacker can actually reach; claiming less than a proven escalation is wrong, and over-claiming an unproven one gets the finding rejected at triage.
350
364
  - **Evidence-first:** every claim must be traceable to observed/reproduced behavior, source code, or documented platform behavior.
351
365
  - **Design & runtime check (mandatory before CONFIRMED):** actively search the target's docs, git history, changelog, and runtime/framework docs for evidence the behavior is BY DESIGN or already FIXED IN THE RUNTIME. Found it → KILL (\`intended_behavior\` / \`framework_protection\`), unless the documented intent is itself the flaw with real attacker impact. Not found → document the search in \`disconfirmation\` as non-intentionality proof.