@clear-capabilities/agentic-security-scanner 0.172.0 → 0.172.1

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/CHANGELOG.md CHANGED
@@ -9,6 +9,20 @@
9
9
  > make the history less accurate, not more.
10
10
 
11
11
 
12
+ ## 0.172.1 - Deterministic Context Intelligence: adversarial review fixes
13
+
14
+ An adversarial review of the first release found nine problems, each demonstrated against the shipped code and each now pinned by a regression test (`scanner/test/context-intelligence/premortem.test.js`).
15
+
16
+ - **False ownership protection.** A route that built SQL from `req.params.id` and merely mentioned `req.user.id` and `owner_name` in the same call reported `ownership: present` with a `complete` bundle. An owner field now counts only when it is tied to the principal in the same clause, and the principal's own name is never read as an owner field. `present` carries a `strength` (`observed` or `inferred`).
17
+ - **Discarded guards.** `isOwner(...)` called as a bare statement no longer counts as a guard; it is `evaluated_and_ineffective`.
18
+ - **Prompt injection through the route label.** The label sat in the instruction text outside the untrusted fence. It is now sanitised and placed inside the fence.
19
+ - **Remote egress.** A non-loopback endpoint now needs `--allow-remote` or `review.allowRemote`; the egress policy and redaction still apply after approval.
20
+ - **Terminal escapes.** Every line the command prints has control characters and bidirectional marks replaced.
21
+ - **Citations.** A citation must now be about its cited lines (not only inside the bundle), spans at most 60 lines, and `no_issue_observed` must cite a control the deterministic pass also found.
22
+ - **Unmeasured numbers.** Precision, recall, F1 and calls per true positive are withheld while the interval is unmeasured; the CSV carries a `synthetic` column.
23
+ - **Compiler export.** A reference must name a callee that its cited line contains, or the whole import degrades.
24
+ - **Locale.** The one locale-sensitive ordering now uses code-unit order, so manifests do not differ by host locale.
25
+
12
26
  ## 0.172.0 - Deterministic Context Intelligence (`context` command)
13
27
 
14
28
  An opt-in, additive subsystem. The default scan, `hunt` and every existing command are unchanged.
@@ -9069,107 +9069,107 @@ function dbg(...args) {
9069
9069
  // === Body ===
9070
9070
 
9071
9071
  var ASM_CONSTS = {
9072
- 21603448: () => { return current_async_call_id; },
9073
- 21603482: ($0) => { reject_async($0, new Error("Memory allocation failed")); },
9074
- 21603543: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9075
- 21603598: () => { return current_async_call_id; },
9076
- 21603632: ($0) => { reject_async($0, new Error("Memory allocation failed")); },
9077
- 21603693: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9078
- 21603748: () => { return current_async_call_id; },
9079
- 21603782: ($0) => { reject_async($0, new Error("Memory allocation failed")); },
9080
- 21603843: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9081
- 21603898: () => { return current_async_call_id; },
9082
- 21603932: ($0) => { reject_async($0, new Error("Memory allocation failed")); },
9083
- 21603993: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9084
- 21604048: () => { return current_async_call_id; },
9085
- 21604082: ($0) => { reject_async($0, new Error("Memory allocation failed")); },
9086
- 21604143: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9087
- 21604198: ($0, $1) => { resolve_async($1, $0); },
9088
- 21604225: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9089
- 21604291: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9090
- 21604342: () => { clearTimeout(threadTimeouts.shift()); },
9091
- 21604384: ($0, $1) => { resolve_async($1, $0); },
9092
- 21604411: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9093
- 21604477: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9094
- 21604528: () => { clearTimeout(threadTimeouts.shift()); },
9095
- 21604570: ($0, $1) => { resolve_async($1, $0); },
9096
- 21604597: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9097
- 21604663: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9098
- 21604714: () => { clearTimeout(threadTimeouts.shift()); },
9099
- 21604756: ($0, $1) => { resolve_async($1, $0); },
9100
- 21604783: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9101
- 21604849: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9102
- 21604900: () => { clearTimeout(threadTimeouts.shift()); },
9103
- 21604942: ($0, $1) => { resolve_async($1, $0); },
9104
- 21604969: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9105
- 21605035: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9106
- 21605086: () => { clearTimeout(threadTimeouts.shift()); },
9107
- 21605128: () => { return current_async_call_id; },
9108
- 21605162: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9109
- 21605217: ($0, $1) => { resolve_async($1, UTF8ToString($0)); },
9110
- 21605258: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9111
- 21605324: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9112
- 21605375: () => { clearTimeout(threadTimeouts.shift()); },
9113
- 21605417: () => { return current_async_call_id; },
9114
- 21605451: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9115
- 21605506: ($0, $1) => { resolve_async($1, $0); },
9116
- 21605533: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9117
- 21605599: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9118
- 21605650: () => { clearTimeout(threadTimeouts.shift()); },
9119
- 21605692: () => { return current_async_call_id; },
9120
- 21605726: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9121
- 21605781: ($0, $1) => { resolve_async($1, $0); },
9122
- 21605808: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9123
- 21605874: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9124
- 21605925: () => { clearTimeout(threadTimeouts.shift()); },
9125
- 21605967: () => { return current_async_call_id; },
9126
- 21606001: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9127
- 21606056: ($0, $1) => { resolve_async($1, $0); },
9128
- 21606083: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9129
- 21606149: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9130
- 21606200: () => { clearTimeout(threadTimeouts.shift()); },
9131
- 21606242: () => { return current_async_call_id; },
9132
- 21606276: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9133
- 21606331: ($0, $1) => { resolve_async($1, $0); },
9134
- 21606358: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9135
- 21606424: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9136
- 21606475: () => { clearTimeout(threadTimeouts.shift()); },
9137
- 21606517: () => { return current_async_call_id; },
9138
- 21606551: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9139
- 21606606: ($0, $1) => { resolve_async($1, $0); },
9140
- 21606633: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9141
- 21606699: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9142
- 21606750: () => { clearTimeout(threadTimeouts.shift()); },
9143
- 21606792: () => { return current_async_call_id; },
9144
- 21606826: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9145
- 21606881: ($0, $1) => { resolve_async($1, $0); },
9146
- 21606908: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9147
- 21606974: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9148
- 21607025: () => { clearTimeout(threadTimeouts.shift()); },
9149
- 21607067: () => { return current_async_call_id; },
9150
- 21607101: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9151
- 21607156: ($0, $1) => { resolve_async($1, $0); },
9152
- 21607183: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9153
- 21607249: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9154
- 21607300: () => { clearTimeout(threadTimeouts.shift()); },
9155
- 21607342: () => { return current_async_call_id; },
9156
- 21607376: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9157
- 21607431: ($0, $1) => { resolve_async($1, $0); },
9158
- 21607458: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9159
- 21607524: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9160
- 21607575: () => { clearTimeout(threadTimeouts.shift()); },
9161
- 21607617: () => { return current_async_call_id; },
9162
- 21607651: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9163
- 21607706: ($0, $1) => { resolve_async($1, $0); },
9164
- 21607733: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9165
- 21607799: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9166
- 21607850: () => { clearTimeout(threadTimeouts.shift()); },
9167
- 21607892: () => { return current_async_call_id; },
9168
- 21607926: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9169
- 21607981: ($0, $1) => { resolve_async($1, $0); },
9170
- 21608008: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9171
- 21608074: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9172
- 21608125: () => { clearTimeout(threadTimeouts.shift()); }
9072
+ 21603560: () => { return current_async_call_id; },
9073
+ 21603594: ($0) => { reject_async($0, new Error("Memory allocation failed")); },
9074
+ 21603655: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9075
+ 21603710: () => { return current_async_call_id; },
9076
+ 21603744: ($0) => { reject_async($0, new Error("Memory allocation failed")); },
9077
+ 21603805: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9078
+ 21603860: () => { return current_async_call_id; },
9079
+ 21603894: ($0) => { reject_async($0, new Error("Memory allocation failed")); },
9080
+ 21603955: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9081
+ 21604010: () => { return current_async_call_id; },
9082
+ 21604044: ($0) => { reject_async($0, new Error("Memory allocation failed")); },
9083
+ 21604105: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9084
+ 21604160: () => { return current_async_call_id; },
9085
+ 21604194: ($0) => { reject_async($0, new Error("Memory allocation failed")); },
9086
+ 21604255: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9087
+ 21604310: ($0, $1) => { resolve_async($1, $0); },
9088
+ 21604337: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9089
+ 21604403: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9090
+ 21604454: () => { clearTimeout(threadTimeouts.shift()); },
9091
+ 21604496: ($0, $1) => { resolve_async($1, $0); },
9092
+ 21604523: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9093
+ 21604589: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9094
+ 21604640: () => { clearTimeout(threadTimeouts.shift()); },
9095
+ 21604682: ($0, $1) => { resolve_async($1, $0); },
9096
+ 21604709: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9097
+ 21604775: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9098
+ 21604826: () => { clearTimeout(threadTimeouts.shift()); },
9099
+ 21604868: ($0, $1) => { resolve_async($1, $0); },
9100
+ 21604895: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9101
+ 21604961: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9102
+ 21605012: () => { clearTimeout(threadTimeouts.shift()); },
9103
+ 21605054: ($0, $1) => { resolve_async($1, $0); },
9104
+ 21605081: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9105
+ 21605147: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9106
+ 21605198: () => { clearTimeout(threadTimeouts.shift()); },
9107
+ 21605240: () => { return current_async_call_id; },
9108
+ 21605274: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9109
+ 21605329: ($0, $1) => { resolve_async($1, UTF8ToString($0)); },
9110
+ 21605370: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9111
+ 21605436: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9112
+ 21605487: () => { clearTimeout(threadTimeouts.shift()); },
9113
+ 21605529: () => { return current_async_call_id; },
9114
+ 21605563: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9115
+ 21605618: ($0, $1) => { resolve_async($1, $0); },
9116
+ 21605645: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9117
+ 21605711: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9118
+ 21605762: () => { clearTimeout(threadTimeouts.shift()); },
9119
+ 21605804: () => { return current_async_call_id; },
9120
+ 21605838: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9121
+ 21605893: ($0, $1) => { resolve_async($1, $0); },
9122
+ 21605920: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9123
+ 21605986: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9124
+ 21606037: () => { clearTimeout(threadTimeouts.shift()); },
9125
+ 21606079: () => { return current_async_call_id; },
9126
+ 21606113: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9127
+ 21606168: ($0, $1) => { resolve_async($1, $0); },
9128
+ 21606195: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9129
+ 21606261: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9130
+ 21606312: () => { clearTimeout(threadTimeouts.shift()); },
9131
+ 21606354: () => { return current_async_call_id; },
9132
+ 21606388: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9133
+ 21606443: ($0, $1) => { resolve_async($1, $0); },
9134
+ 21606470: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9135
+ 21606536: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9136
+ 21606587: () => { clearTimeout(threadTimeouts.shift()); },
9137
+ 21606629: () => { return current_async_call_id; },
9138
+ 21606663: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9139
+ 21606718: ($0, $1) => { resolve_async($1, $0); },
9140
+ 21606745: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9141
+ 21606811: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9142
+ 21606862: () => { clearTimeout(threadTimeouts.shift()); },
9143
+ 21606904: () => { return current_async_call_id; },
9144
+ 21606938: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9145
+ 21606993: ($0, $1) => { resolve_async($1, $0); },
9146
+ 21607020: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9147
+ 21607086: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9148
+ 21607137: () => { clearTimeout(threadTimeouts.shift()); },
9149
+ 21607179: () => { return current_async_call_id; },
9150
+ 21607213: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9151
+ 21607268: ($0, $1) => { resolve_async($1, $0); },
9152
+ 21607295: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9153
+ 21607361: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9154
+ 21607412: () => { clearTimeout(threadTimeouts.shift()); },
9155
+ 21607454: () => { return current_async_call_id; },
9156
+ 21607488: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9157
+ 21607543: ($0, $1) => { resolve_async($1, $0); },
9158
+ 21607570: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9159
+ 21607636: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9160
+ 21607687: () => { clearTimeout(threadTimeouts.shift()); },
9161
+ 21607729: () => { return current_async_call_id; },
9162
+ 21607763: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9163
+ 21607818: ($0, $1) => { resolve_async($1, $0); },
9164
+ 21607845: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9165
+ 21607911: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9166
+ 21607962: () => { clearTimeout(threadTimeouts.shift()); },
9167
+ 21608004: () => { return current_async_call_id; },
9168
+ 21608038: () => { threadTimeouts.push(setTimeout(() => {}, 600000)); },
9169
+ 21608093: ($0, $1) => { resolve_async($1, $0); },
9170
+ 21608120: ($0) => { reject_async($0, new Error('failed with unknown exception')); },
9171
+ 21608186: ($0, $1) => { reject_async($1, new Error(UTF8ToString($0))); },
9172
+ 21608237: () => { clearTimeout(threadTimeouts.shift()); }
9173
9173
  };
9174
9174
 
9175
9175
  // end include: preamble.js
@@ -114,6 +114,8 @@ function metricsFor(units, protocolHash, name) {
114
114
  precision: (0,_posture_evaluation_interval_js__WEBPACK_IMPORTED_MODULE_5__/* .precisionOf */ .ij)(c), recall: (0,_posture_evaluation_interval_js__WEBPACK_IMPORTED_MODULE_5__/* .recallOf */ .HR)(c), f1: (0,_posture_evaluation_interval_js__WEBPACK_IMPORTED_MODULE_5__/* .f1Of */ .li)(c),
115
115
  precisionInterval: pn > 0 ? (0,_posture_evaluation_interval_js__WEBPACK_IMPORTED_MODULE_5__/* .wilsonInterval */ .YV)(c.tp, pn) : { status: 'unmeasured', reason: 'nothing was reported' },
116
116
  f1Interval: f1,
117
+ // A point estimate with no interval around it reads as a finding. It is reportable only when the interval is measured.
118
+ reportable: f1 && f1.status === 'measured',
117
119
  };
118
120
  }
119
121
 
@@ -149,6 +151,9 @@ function readLedger(root) {
149
151
  const pct = (x) => (x === null || x === undefined ? 'n/a' : (x * 100).toFixed(0) + '%');
150
152
  const interval = (i) => (!i || i.status !== 'measured' ? `unmeasured (${i && i.reason ? i.reason.split(',')[0] : 'no data'})` : `${pct(i.low)} to ${pct(i.high)}`);
151
153
 
154
+ // Precision, recall and F1 are printed only when their interval is measured; otherwise the counts stand alone.
155
+ const shown = (m, x) => (m.reportable ? pct(x) : 'withheld');
156
+
152
157
  function renderMarkdown(card) {
153
158
  const L = [];
154
159
  L.push('# Context Intelligence evaluation scorecard', '');
@@ -163,18 +168,20 @@ function renderMarkdown(card) {
163
168
  for (const s of card.systems) {
164
169
  if (s.status !== 'measured') { L.push(`| ${s.system} | ${s.status}: ${s.reason} | | | | | | | | | | |`); continue; }
165
170
  const m = s.metrics;
166
- L.push(`| ${s.system} | measured | ${m.counts.tp} | ${m.counts.fp} | ${m.counts.fn} | ${m.counts.tn} | ${pct(m.precision)} | ${pct(m.recall)} | ${pct(m.f1)} | ${interval(m.f1Interval)} | ${s.calls} | ${s.promptChars} |`);
171
+ L.push(`| ${s.system} | measured | ${m.counts.tp} | ${m.counts.fp} | ${m.counts.fn} | ${m.counts.tn} | ${shown(m, m.precision)} | ${shown(m, m.recall)} | ${shown(m, m.f1)} | ${interval(m.f1Interval)} | ${s.calls} | ${s.promptChars} |`);
167
172
  }
168
173
  L.push('', '## Economics (estimated; no price supplied)', '', '| system | median ctx tokens | P95 | P99 | model calls | calls per true positive | wall ms |', '|---|---|---|---|---|---|---|');
169
- for (const s of card.systems) if (s.economics) { const e = s.economics; L.push(`| ${s.system} | ${e.contextTokens.median ?? 'n/a'} | ${e.contextTokens.p95 ?? 'n/a'} | ${e.contextTokens.p99 ?? 'n/a'} | ${e.modelCalls} | ${e.callsPerTruePositive === null ? 'n/a' : e.callsPerTruePositive.toFixed(1)} | ${e.wallMs} |`); }
174
+ for (const s of card.systems) if (s.economics) { const e = s.economics; L.push(`| ${s.system} | ${e.contextTokens.median ?? 'n/a'} | ${e.contextTokens.p95 ?? 'n/a'} | ${e.contextTokens.p99 ?? 'n/a'} | ${e.modelCalls} | ${e.callsPerTruePositive === null || !s.metrics || !s.metrics.reportable ? 'withheld' : e.callsPerTruePositive.toFixed(1)} | ${e.wallMs} |`); }
175
+ L.push('', 'Precision, recall, F1 and calls per true positive are withheld while their interval is unmeasured (fewer than 10 independent groups). The counts above are what was observed; they are not an estimate of anything.');
170
176
  L.push('', `Replay: \`${card.replay}\``, '');
171
177
  if (card.leakage.length) { L.push('## Leakage hits', ...card.leakage.map(h => `- ${h.where} ${h.file || ''} ${h.term}`), ''); }
172
178
  return L.join('\n');
173
179
  }
174
180
 
175
181
  function renderCsv(card) {
176
- const rows = [['system', 'case', 'variant', 'vulnerable', 'flagged', 'outcome', 'calls', 'promptChars']];
177
- for (const s of card.systems) for (const u of (s.units || [])) rows.push([s.system, u.caseId, u.variant, u.vulnerable, u.flagged, u.outcome, u.calls, u.promptChars]);
182
+ // The marker is a column, not a banner: a CSV is the format that gets pasted into a spreadsheet without its header text.
183
+ const rows = [['system', 'case', 'variant', 'vulnerable', 'flagged', 'outcome', 'calls', 'promptChars', 'synthetic']];
184
+ for (const s of card.systems) for (const u of (s.units || [])) rows.push([s.system, u.caseId, u.variant, u.vulnerable, u.flagged, u.outcome, u.calls, u.promptChars, card.synthetic === false ? false : true]);
178
185
  return rows.map(r => r.join(',')).join('\n') + '\n';
179
186
  }
180
187
 
@@ -625,6 +632,16 @@ function stripUntrustedKeepUser(t) { return stripUntrusted(t).replace(/""/g, '""
625
632
 
626
633
  function cite(l, role, claim) { return { role, file: l.file, startLine: l.n, endLine: l.n, claim }; }
627
634
 
635
+ /** A reader that cites a line says what is on it: the claim quotes the cited text, as a careful model would. */
636
+ function quoteCited(obj, blocks) {
637
+ const text = (c) => {
638
+ for (const b of blocks) if (b.file === c.file) { const l = b.lines.find(x => x.n === c.startLine); if (l) return l.text.trim().slice(0, 100); }
639
+ return '';
640
+ };
641
+ const fix = (list) => (Array.isArray(list) ? list.map(c => ({ ...c, claim: `${c.claim}: ${text(c)}` })) : list);
642
+ return { ...obj, evidence: fix(obj.evidence), observedMitigations: fix(obj.observedMitigations) };
643
+ }
644
+
628
645
  /** The decision for a pack, from the observations. Returns the review JSON object. */
629
646
  function decide(packId, obs, ctx) {
630
647
  const base = { observedMitigations: [], attackerControl: [], uncertainties: [], likelihood: 'low' };
@@ -673,7 +690,7 @@ function scriptedReader() {
673
690
  const invoke = async (prompt) => {
674
691
  if (/^QUESTION PACK: /m.test(prompt)) {
675
692
  const packId = /^QUESTION PACK: ([\w-]+)\//m.exec(prompt)[1];
676
- const label = /^ENTRY POINT 1: (.*?) \(id /m.exec(prompt)[1];
693
+ const label = (/^ENTRY POINT LABEL \(data\): (.*)$/m.exec(prompt) || [])[1] || '';
677
694
  const { blocks, unresolved, notIncluded, limit } = blocksFromReviewPrompt(prompt);
678
695
  const routes = findRoutes(blocks);
679
696
  const [method, ...rest] = label.split(' ');
@@ -682,7 +699,7 @@ function scriptedReader() {
682
699
  if (!route) return JSON.stringify({ evidence: [], observedMitigations: [], conclusion: 'insufficient_evidence', invariant: 'unspecified', uncertainties: ['the registration is not in the provided context'] });
683
700
  const obs = analyseRoute(route, blocks);
684
701
  const complete = !unresolved && !notIncluded && !limit;
685
- return JSON.stringify(decide(packId, obs, { complete, unresolved }));
702
+ return JSON.stringify(quoteCited(decide(packId, obs, { complete, unresolved }), blocks));
686
703
  }
687
704
  // hunt prompt: report the candidates for the lens, with a location
688
705
  const lens = /Your lens is ([\w ]+)\./.exec(prompt);