@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
|
-
|
|
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
|
-
|
|
329
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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",
|