@solongate/proxy 0.81.4 → 0.81.6

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/hooks/audit.mjs CHANGED
@@ -497,6 +497,10 @@ try { input += readFileSync(0, 'utf-8'); } catch {}
497
497
  if (tr && typeof tr === 'object' && typeof tr.stdout === 'string') updated = { ...tr, stdout: out };
498
498
  else if (tr && typeof tr === 'object' && typeof tr.content === 'string') updated = { ...tr, content: out };
499
499
  else if (tr && typeof tr === 'object' && Array.isArray(tr.content)) updated = { ...tr, content: [{ type: 'text', text: out }] };
500
+ // Read / NotebookRead nest the file body at file.content. It's a STRUCTURED
501
+ // result, so a bare-string replacement is rejected ("PostToolUse:Read hook
502
+ // warning") and the raw secret leaks through — preserve the shape instead.
503
+ else if (tr && typeof tr === 'object' && tr.file && typeof tr.file.content === 'string') updated = { ...tr, file: { ...tr.file, content: out } };
500
504
  else updated = out;
501
505
  EMITTED_PAYLOAD = JSON.stringify({
502
506
  hookSpecificOutput: { hookEventName: 'PostToolUse', updatedToolOutput: updated },
@@ -6787,8 +6787,14 @@ function blockTool(reason) {
6787
6787
  }));
6788
6788
  sgFinish(0);
6789
6789
  } else {
6790
- process.stderr.write(reason);
6791
- sgFinish(2);
6790
+ process.stdout.write(JSON.stringify({
6791
+ hookSpecificOutput: {
6792
+ hookEventName: "PreToolUse",
6793
+ permissionDecision: "deny",
6794
+ permissionDecisionReason: reason
6795
+ }
6796
+ }));
6797
+ sgFinish(0);
6792
6798
  }
6793
6799
  }
6794
6800
  function allowTool() {
@@ -7845,8 +7851,8 @@ if (!REFRESH_MODE) {
7845
7851
  process.stdout.write(JSON.stringify({ decision: "deny", reason: ghostHit }));
7846
7852
  sgFinish(0);
7847
7853
  }
7848
- process.stderr.write(ghostHit);
7849
- sgFinish(2);
7854
+ process.stdout.write(JSON.stringify({ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: ghostHit } }));
7855
+ sgFinish(0);
7850
7856
  }
7851
7857
  if (AGENT_TYPE !== "gemini-cli" && toolName === "Bash") {
7852
7858
  const rw = ghostListingRewrite(args, securityCfg.ghost);
package/hooks/guard.mjs CHANGED
@@ -326,9 +326,20 @@ function blockTool(reason) {
326
326
  }));
327
327
  sgFinish(0);
328
328
  } else {
329
- // Claude Code — exit code 2
330
- process.stderr.write(reason);
331
- sgFinish(2);
329
+ // Claude Code — signal the block via the JSON PreToolUse protocol on stdout,
330
+ // NOT exit code 2. On Windows the hook runs through PowerShell, which does not
331
+ // reliably propagate a native process's exit code back to Claude Code, so an
332
+ // `exit 2` block was being seen as a "non-blocking status code" and the tool ran
333
+ // anyway. Claude only processes this JSON on exit 0 (JSON is ignored if you exit
334
+ // 2), so we MUST exit 0 here. This is shell-independent and works everywhere.
335
+ process.stdout.write(JSON.stringify({
336
+ hookSpecificOutput: {
337
+ hookEventName: 'PreToolUse',
338
+ permissionDecision: 'deny',
339
+ permissionDecisionReason: reason,
340
+ },
341
+ }));
342
+ sgFinish(0);
332
343
  }
333
344
  }
334
345
 
@@ -1640,8 +1651,10 @@ if (!REFRESH_MODE) { try { input += readFileSync(0, 'utf-8'); } catch {} }
1640
1651
  } catch {}
1641
1652
  await maybeSelfUpdate();
1642
1653
  if (AGENT_TYPE === 'gemini-cli') { process.stdout.write(JSON.stringify({ decision: 'deny', reason: ghostHit })); sgFinish(0); }
1643
- process.stderr.write(ghostHit);
1644
- sgFinish(2);
1654
+ // Claude Code: JSON deny on stdout + exit 0 (see blockTool — exit codes are
1655
+ // not reliable through PowerShell on Windows; JSON is ignored on exit 2).
1656
+ process.stdout.write(JSON.stringify({ hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: ghostHit } }));
1657
+ sgFinish(0);
1645
1658
  }
1646
1659
  // No direct hit: if this is a listing command, rewrite it so hidden
1647
1660
  // entries are filtered out of its output (Claude Code only).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.81.4",
3
+ "version": "0.81.6",
4
4
  "description": "AI tool security proxy: protect any AI tool server with customizable policies, path/command constraints, rate limiting, and audit logging. No code changes required.",
5
5
  "type": "module",
6
6
  "bin": {