@sdsrs/code-graph 0.126.2 → 0.127.0

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.
@@ -4,7 +4,7 @@
4
4
  "author": {
5
5
  "name": "sdsrs"
6
6
  },
7
- "version": "0.126.2",
7
+ "version": "0.127.0",
8
8
  "keywords": [
9
9
  "code-graph",
10
10
  "ast",
@@ -401,8 +401,21 @@ function findBinaryUncached() {
401
401
  if (process.env._FIND_BINARY_ROOT) {
402
402
  possibleRoots.add(path.resolve(process.env._FIND_BINARY_ROOT));
403
403
  }
404
- // From CLAUDE_PROJECT_DIR
405
- if (process.env.CLAUDE_PROJECT_DIR) {
404
+ // From CLAUDE_PROJECT_DIR — behind an explicit opt-in, and ONLY that.
405
+ //
406
+ // This root is the arbitrary directory the developer opened; the two above are
407
+ // derived from the plugin's own install. Accepting it made "clone an untrusted
408
+ // repo and open it" arbitrary code execution: this tier sits above the version
409
+ // gate, `isDevRepo` asks only whether a `Cargo.toml` exists, and resolution
410
+ // itself runs the file (`writeCacheEntry` → `readBinaryVersion`) — all before
411
+ // any file is read or any tool approved. A tracked, mode-755
412
+ // `target/release/code-graph-mcp` survives `.gitignore` into a fresh clone,
413
+ // so supplying one costs the attacker nothing.
414
+ //
415
+ // No property of the opened directory can re-establish trust here (an attacker
416
+ // supplies all of them), so the gate is the developer's own opt-in, spelled
417
+ // the same way `version-utils.js:isDevMode` spells it.
418
+ if (process.env.CLAUDE_PROJECT_DIR && process.env.CODE_GRAPH_DEV === '1') {
406
419
  possibleRoots.add(path.resolve(process.env.CLAUDE_PROJECT_DIR));
407
420
  }
408
421
 
@@ -1089,7 +1089,16 @@ function hookFirePayload(matcher) {
1089
1089
  case 'Write|Edit':
1090
1090
  return { tool_name: 'Edit', tool_input: { file_path: 'src/example.rs', old_string: 'a', new_string: 'b' } };
1091
1091
  case '': // UserPromptSubmit
1092
- return { prompt: 'where is the parse function defined' };
1092
+ // A SYMPTOM-flavoured prompt, for the same reason the Bash probe uses a
1093
+ // quoted identifier: it must reach a path that actually emits. The old
1094
+ // payload ('where is the parse function defined') produced
1095
+ // `determineQueryType(...) === null` — no query, no output — and the two
1096
+ // result-injecting paths that would emit need a real indexed binary, which
1097
+ // this throwaway fixture does not have. `symptom-hint` is prose-only, so it
1098
+ // engages on the fixture alone. Field name is `prompt`: this constructor
1099
+ // has always had it right, while the hook itself read `message`
1100
+ // (audit 2026-08-29 JS-01).
1101
+ return { prompt: 'why does the parser crash on empty input' };
1093
1102
  default:
1094
1103
  return { tool_name: 'Unknown', tool_input: {} };
1095
1104
  }
@@ -437,7 +437,24 @@ function runMain() {
437
437
  // sibling hooks already did this; this one was the holdout, so it read
438
438
  // nothing under exactly the conditions verifyHooksFire spawns it with.
439
439
  const input = JSON.parse(fs.readFileSync(0, 'utf8'));
440
- message = (input && input.message) || '';
440
+ // `prompt` FIRST: that is the field Claude Code puts the user's text in on
441
+ // UserPromptSubmit, and this hook read only `message` — so in production it
442
+ // parsed a payload, found nothing, and exited 0 in silence. The whole
443
+ // intent-driven injection surface (impact / callgraph / overview / search +
444
+ // the symptom hint) was dead from the day it shipped, and its adoption
445
+ // metrics read as a structural zero.
446
+ //
447
+ // Two things hid it, both worth naming because either alone would have been
448
+ // enough: the entire test suite fed `{message:…}` — a self-consistent copy
449
+ // of the defect — and `verifyHooksFire` only asserts exit 0, which is
450
+ // exactly what a hook that reads nothing does. The production tell was
451
+ // outside both: zero `.code-graph-ctx-*` cooldown flags in a heavily
452
+ // dogfooded tmp dir that held 93 flags from the sibling hooks
453
+ // (audit 2026-08-29 JS-01).
454
+ //
455
+ // `message` is kept as a fallback rather than replaced — it costs one `||`
456
+ // and covers any host that spells it the other way.
457
+ message = (input && (input.prompt || input.message)) || '';
441
458
  } catch {
442
459
  return;
443
460
  }
@@ -35,7 +35,7 @@ jobs:
35
35
  node-version: '20'
36
36
  - name: Build snapshot
37
37
  run: |
38
- npx -y -p @sdsrs/code-graph@0.126.2 code-graph-mcp snapshot create --out snapshot.db
38
+ npx -y -p @sdsrs/code-graph@0.127.0 code-graph-mcp snapshot create --out snapshot.db
39
39
  zstd -9 snapshot.db -o snapshot.db.zst
40
40
  mv snapshot.db.zst "code-graph-snapshot-${GITHUB_SHA:0:7}.db.zst"
41
41
  - name: Upload to release
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdsrs/code-graph",
3
- "version": "0.126.2",
3
+ "version": "0.127.0",
4
4
  "description": "MCP server that indexes codebases into an AST knowledge graph with semantic search, call graph traversal, and HTTP route tracing",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -35,10 +35,10 @@
35
35
  "node": ">=16"
36
36
  },
37
37
  "optionalDependencies": {
38
- "@sdsrs/code-graph-linux-x64": "0.126.2",
39
- "@sdsrs/code-graph-linux-arm64": "0.126.2",
40
- "@sdsrs/code-graph-darwin-x64": "0.126.2",
41
- "@sdsrs/code-graph-darwin-arm64": "0.126.2",
42
- "@sdsrs/code-graph-win32-x64": "0.126.2"
38
+ "@sdsrs/code-graph-linux-x64": "0.127.0",
39
+ "@sdsrs/code-graph-linux-arm64": "0.127.0",
40
+ "@sdsrs/code-graph-darwin-x64": "0.127.0",
41
+ "@sdsrs/code-graph-darwin-arm64": "0.127.0",
42
+ "@sdsrs/code-graph-win32-x64": "0.127.0"
43
43
  }
44
44
  }