@holmes-lab/holmes-kit 0.7.0 → 0.7.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
@@ -4,6 +4,34 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+ <!-- @implements A-SPEC-209 -->
8
+ ## [0.7.1] - 2026-09-02
9
+
10
+ An adversarial self-review of the seven-language work — 42 probes the test suite never pinned
11
+ (hostile syntax through every new lowering, taint false-positive/negative controls, import
12
+ boundary and escape attempts). The strong properties held: zero crashes, zero invariant
13
+ violations, zero out-of-scan escapes, and the sanitizer's exact-match fail-open was confirmed
14
+ correct. Four low-severity harvest items were fixed; every keep-as-is verdict carries its
15
+ measurement.
16
+
17
+ ### Fixed
18
+
19
+ - **Labeled statements are breakable** (REQ-527): Java (and TS) allow `break L` out of ANY
20
+ labeled statement; the lowering registered only loops and switches, so the legal
21
+ `L: { … break L; }` refused the whole function. The labeled statement now owns its label's
22
+ breaks (loops/switches keep consuming labels as before; `continue` stays loop-only, as in the
23
+ languages themselves).
24
+ - **Two taint sources stopped bleeding through substrings** (REQ-527): C++'s bare `cin` matched
25
+ inside ordinary identifiers (`racing`, `medicine`…), C#'s bare `Form` matched every `*Form`.
26
+ Now `std::cin`/`cin >>` and `Request.Form`/`.Form[` — the adversarial probes are regression
27
+ tests, and the positive controls prove the real reads still fire. **Corpus re-verification:
28
+ identical numbers everywhere** (violations 0, C++'s two genuine findings kept, all five
29
+ import-arrival counts unchanged — no legitimate signal was lost).
30
+ - **Two wrong-frame import resolutions refuse** (REQ-527): an absolute `#include "/…"` no longer
31
+ normalizes into the repository frame, and `super::` beyond a Rust crate root resolves to
32
+ nothing (rustc calls it an error too). Both could only ever hit scanned files — wrong
33
+ coordinates, not escapes — and now hit none.
34
+
7
35
  <!-- @implements A-SPEC-209 -->
8
36
  ## [0.7.0] - 2026-09-02
9
37
 
package/dist/.build-id CHANGED
@@ -1 +1 @@
1
- 7cb32c4-mtjifuwa
1
+ 9196dd1-mtk1gito
@@ -325,8 +325,31 @@ function cfgOf(ast, fn, source) {
325
325
  return lowerSeq(stmtChildren(i), ctx);
326
326
  case 'labeled_statement': {
327
327
  const [labelNode, inner] = [kids.of(i)[0], stmtChildren(i).at(-1)];
328
- ctx.pendingLabel = labelName(labelNode);
329
- return lowerStmt(inner, ctx);
328
+ const lname = labelName(labelNode);
329
+ // @implements A-SPEC-527.1 — Java (and TS) allow `break L` out of ANY labeled
330
+ // statement, not only loops and switches. Loop/switch inners keep consuming the label
331
+ // themselves (their break machinery owns the exits); for every OTHER statement the
332
+ // labeled statement itself becomes the breakable, and its parked breaks exit past it.
333
+ // `continue` is deliberately NOT registered — the language keeps it loop-only too.
334
+ const LABEL_CONSUMERS = new Set([
335
+ 'while_statement', 'do_statement', 'for_statement', 'for_in_statement',
336
+ 'enhanced_for_statement', 'foreach_statement', 'for_range_loop',
337
+ 'switch_statement', 'switch_expression', 'expression_switch_statement',
338
+ 'type_switch_statement', 'select_statement',
339
+ 'for_expression', 'while_expression', 'loop_expression',
340
+ ]);
341
+ if (LABEL_CONSUMERS.has(ast.nodes[inner].type)) {
342
+ ctx.pendingLabel = lname;
343
+ return lowerStmt(inner, ctx);
344
+ }
345
+ const owner = { label: lname, target: -1 };
346
+ ctx.breakT.push({ label: lname, owner });
347
+ const f = lowerStmt(inner, ctx);
348
+ ctx.breakT.pop();
349
+ const exits = [...f.exits];
350
+ for (const b of claimBreaks(owner))
351
+ exits.push({ from: b.point, kind: 'break' });
352
+ return { entry: f.entry, exits };
330
353
  }
331
354
  case 'elif_clause':
332
355
  case 'if_statement': {
@@ -413,6 +413,10 @@ function addImportEdges(scanned, graph, opts) {
413
413
  else if (head === 'super') {
414
414
  baseDir = path.posix.dirname(path.posix.dirname(fromFile));
415
415
  while (segs[0] === 'super') {
416
+ // @implements A-SPEC-527.1 — super:: above the crate root is an rustc error; resolving
417
+ // it pinned '.' and produced edges to repo-root files (adversarial sweep). Refuse.
418
+ if (baseDir === '.' || baseDir === '/')
419
+ return null;
416
420
  segs.shift();
417
421
  baseDir = path.posix.dirname(baseDir);
418
422
  }
@@ -447,6 +451,11 @@ function addImportEdges(scanned, graph, opts) {
447
451
  if (/\.cs$/.test(fromFile))
448
452
  return uniqueSuffix(`${spec.split('.').join('/')}.cs`);
449
453
  if (/\.(cpp|cc|cxx|hpp|h)$/.test(fromFile)) {
454
+ // @implements A-SPEC-527.1 — an ABSOLUTE include is not a repository coordinate: joining
455
+ // it into the repo frame let `#include "/etc/passwd"` match a coincidentally-shaped
456
+ // scanned file (adversarial sweep). Absolute means absolute; it resolves to nothing here.
457
+ if (spec.startsWith('/'))
458
+ return null;
450
459
  // extension preserved: the dotted split below would butcher `util/env.h`
451
460
  const relative = path.posix.normalize(path.posix.join(path.posix.dirname(fromFile), spec));
452
461
  if (known.has(relative))
@@ -51,7 +51,9 @@ const JAVA_VOCABULARY = {
51
51
  sanitizers: ['escapeHtml', 'quoteReplacement', 'encode'],
52
52
  };
53
53
  const CSHARP_VOCABULARY = {
54
- sources: ['GetEnvironmentVariable', 'ReadLine', 'QueryString', 'Form'],
54
+ // @implements A-SPEC-527.1 — bare 'Form' matched every *Form identifier (WinForms world);
55
+ // the request-shaped spellings keep the actual web input reads.
56
+ sources: ['GetEnvironmentVariable', 'ReadLine', 'QueryString', 'Request.Form', '.Form['],
55
57
  sinks: ['Start', 'ExecuteReader', 'ExecuteNonQuery', 'ExecuteScalar', 'Deserialize'],
56
58
  sanitizers: ['HtmlEncode', 'UrlEncode', 'EscapeDataString'],
57
59
  };
@@ -61,7 +63,9 @@ const RUST_VOCABULARY = {
61
63
  sanitizers: ['escape', 'quote'],
62
64
  };
63
65
  const CPP_VOCABULARY = {
64
- sources: ['getenv', 'argv', 'cin', 'fgets'],
66
+ // @implements A-SPEC-527.1 'cin' alone bled into English identifiers (racing, medicine…),
67
+ // measured by the adversarial sweep; the qualified spellings keep the real reads.
68
+ sources: ['getenv', 'argv', 'std::cin', 'cin >>', 'fgets'],
65
69
  sinks: ['system', 'popen', 'exec', 'execl', 'execlp', 'execle', 'execv', 'execvp', 'ShellExecute'],
66
70
  sanitizers: ['escape', 'quote'],
67
71
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "//": "@implements A-SPEC-209",
3
3
  "name": "@holmes-lab/holmes-kit",
4
- "version": "0.7.0",
4
+ "version": "0.7.1",
5
5
  "description": "Holmes-Kit — deterministic Agentic Software Engineering (ASE) harness with causal traceability (spec chain + D-CPG + RTM + phase guardrail)",
6
6
  "main": "dist/holmes/mcp/server.js",
7
7
  "types": "dist/holmes/mcp/server.d.ts",