Skip to content

CLI-244 CLI-245 callback infrastructure#157

Open
kirill-knize-sonarsource wants to merge 2 commits intomasterfrom
task/kk/CLI-244-245-callback-infrastructure
Open

CLI-244 CLI-245 callback infrastructure#157
kirill-knize-sonarsource wants to merge 2 commits intomasterfrom
task/kk/CLI-244-245-callback-infrastructure

Conversation

@kirill-knize-sonarsource
Copy link
Copy Markdown
Member

No description provided.

@hashicorp-vault-sonar-prod
Copy link
Copy Markdown

hashicorp-vault-sonar-prod bot commented Apr 8, 2026

CLI-244
CLI-245

@sonar-review-alpha
Copy link
Copy Markdown
Contributor

sonar-review-alpha bot commented Apr 8, 2026

Summary

This PR implements callback infrastructure for the Claude Code integration, specifically adding a PreToolUse secrets scanner that blocks file reads if hardcoded secrets are detected.

The main implementation (CLI-245) adds a sonar hook claude-pre-tool-use command that:

  • Receives tool invocations from Claude via stdin (JSON)
  • Scans files before they're read by the agent using the existing secrets binary
  • Returns a deny decision if secrets are found, allowing the agent to block access
  • Gracefully allows access if not authenticated, secrets binary missing, or input unparseable

Infrastructure additions (CLI-244) include:

  • New sonar hook command group (hidden, for internal use)
  • Stdin JSON payload handling utilities
  • Stubs for future handlers (claude-prompt-submit, claude-post-tool-use)
  • Documentation and test coverage

No changes to public-facing commands or existing behavior—purely additive infrastructure for AI agent integration.

What reviewers should know

Where to start:

  1. src/cli/command-tree.ts — hook command registration (lines ~260-280)
  2. src/cli/commands/hook/claude-pre-tool-use.ts — main PreToolUse handler logic
  3. src/cli/commands/hook/stdin.ts — JSON payload parsing
  4. tests/unit/hook-pre-tool-use.test.ts — unit test coverage of the scanner behavior

Key design decisions:

  • Naming: Uses claude- prefix (not agent-) following Claude Code convention, applied consistently across all hook names
  • Communication: Stdin/stdout JSON protocol—allows shell hooks to delegate to TypeScript for business logic
  • Graceful degradation: Returns silently (no output) if auth unavailable, binary missing, file doesn't exist, or stdin unparseable; only denies access when secrets actually found
  • Extensibility: Hook infrastructure is ready for additional handlers (prompt-submit stubs included)

Non-obvious details:

  • The hook command group is { hidden: true } — not user-facing, only called by shell hook scripts
  • Exit code protocol: secrets scanner uses exit code 99 to indicate findings (not a process failure)
  • Test coverage includes unit tests for stdin parsing edge cases and integration tests for end-to-end behavior
  • Documentation auto-generated from command-tree.ts (README.md note at end: "do not edit manually")

Watch for:

  • Graceful fallback logic in claude-pre-tool-use.ts relies on try-catch; verify no secrets are exposed in error logs
  • stdin.ts uses process.stdin directly; ensure it doesn't hang on unexpected input
  • Integration with Claude agent's hook system is not shown here—verify the agent actually calls these hooks as documented

  • Generate Walkthrough
  • Generate Diagram

🗣️ Give feedback

@kirill-knize-sonarsource kirill-knize-sonarsource force-pushed the task/kk/CLI-244-245-callback-infrastructure branch 3 times, most recently from da371c5 to 91be621 Compare April 8, 2026 17:54
sonar-review-alpha[bot]

This comment was marked as resolved.

@kirill-knize-sonarsource kirill-knize-sonarsource force-pushed the task/kk/CLI-244-245-callback-infrastructure branch from 91be621 to e5bc9f8 Compare April 8, 2026 18:37
sonar-review-alpha[bot]

This comment was marked as outdated.

@kirill-knize-sonarsource kirill-knize-sonarsource force-pushed the task/kk/CLI-244-245-callback-infrastructure branch from e5bc9f8 to 6f2f053 Compare April 8, 2026 19:17
@kirill-knize-sonarsource kirill-knize-sonarsource changed the base branch from master to fix/kk/coverage-phantom-lines April 8, 2026 19:18
sonar-review-alpha[bot]

This comment was marked as outdated.

@kirill-knize-sonarsource kirill-knize-sonarsource force-pushed the task/kk/CLI-244-245-callback-infrastructure branch from 6f2f053 to aa6bc64 Compare April 8, 2026 19:58
sonar-review-alpha[bot]

This comment was marked as outdated.

@kirill-knize-sonarsource kirill-knize-sonarsource force-pushed the fix/kk/coverage-phantom-lines branch from b2c895c to b8e4288 Compare April 8, 2026 20:26
@kirill-knize-sonarsource kirill-knize-sonarsource force-pushed the task/kk/CLI-244-245-callback-infrastructure branch from aa6bc64 to 3a31a77 Compare April 8, 2026 20:30
sonar-review-alpha[bot]

This comment was marked as resolved.

@kirill-knize-sonarsource kirill-knize-sonarsource force-pushed the task/kk/CLI-244-245-callback-infrastructure branch from 3a31a77 to c4b4665 Compare April 8, 2026 21:01
sonar-review-alpha[bot]

This comment was marked as outdated.

@kirill-knize-sonarsource kirill-knize-sonarsource force-pushed the fix/kk/coverage-phantom-lines branch from b8e4288 to 26b6e97 Compare April 8, 2026 23:00
@kirill-knize-sonarsource kirill-knize-sonarsource force-pushed the task/kk/CLI-244-245-callback-infrastructure branch 2 times, most recently from e44c9d3 to d117a3a Compare April 8, 2026 23:34
@kirill-knize-sonarsource kirill-knize-sonarsource force-pushed the fix/kk/coverage-phantom-lines branch from 26b6e97 to 74d9271 Compare April 8, 2026 23:39
sonar-review-alpha[bot]

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love the direction this is going 😍

I left a few comments & suggestions, mainly about duplication of secrets scanning logic. I will test it now.

Comment on lines +48 to +52
const auth = await resolveAuth().catch(() => null);
if (!auth) return; // not authenticated — allow gracefully

const binaryPath = resolveSecretsBinaryPath();
if (!binaryPath) return; // binary not installed — allow gracefully
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has this been agreed with PM & UX? I agree it's good not to fail the Claude process completely, but at the same time somehow we need to let the users know that their integration is broken, so that they don't have a false sense of security.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's possible to put something in Claude status bar at this point?

@kirill-knize-sonarsource kirill-knize-sonarsource force-pushed the fix/kk/coverage-phantom-lines branch from 74d9271 to 8e2ad74 Compare April 9, 2026 09:44
@kirill-knize-sonarsource kirill-knize-sonarsource force-pushed the task/kk/CLI-244-245-callback-infrastructure branch from d117a3a to 4b1d963 Compare April 9, 2026 09:45
Copy link
Copy Markdown
Contributor

@sonar-review-alpha sonar-review-alpha bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conclusion: Clean implementation of the callback infrastructure. One minor logic duplication to fix before the pattern spreads to future handlers.

🗣️ Give feedback

Comment on lines +40 to +59
let timeoutId: ReturnType<typeof setTimeout> | undefined;
try {
return await Promise.race([
new Promise<string>((resolve, reject) => {
const chunks: Buffer[] = [];
process.stdin.on('data', (chunk: Buffer) => chunks.push(chunk));
process.stdin.on('end', () => {
resolve(Buffer.concat(chunks).toString('utf-8'));
});
process.stdin.on('error', reject);
}),
new Promise<never>((_, reject) => {
timeoutId = setTimeout(() => {
reject(new Error(`stdin read timed out after ${STDIN_TIMEOUT_MS}ms`));
}, STDIN_TIMEOUT_MS);
}),
]);
} finally {
clearTimeout(timeoutId);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic duplication: The Promise.race + setTimeout + finally clearTimeout pattern here is identical in structure to scanFiles in secrets-scan.ts (lines 57–80). Both do:

  1. Race an operation against a timeout
  2. Optionally trigger a cleanup (kill process / nothing for stdin)
  3. Clear the timeout in finally

With more handlers coming, this will be copy-pasted again. Extract a shared utility, e.g.:

export async function withTimeout<T>(
  operation: Promise<T>,
  ms: number,
  onTimeout?: () => void,
): Promise<T> {
  let timeoutId: ReturnType<typeof setTimeout> | undefined;
  try {
    return await Promise.race([
      operation,
      new Promise<never>((_, reject) => {
        timeoutId = setTimeout(() => {
          onTimeout?.();
          reject(new Error(`Operation timed out after ${ms}ms`));
        }, ms);
      }),
    ]);
  } finally {
    clearTimeout(timeoutId);
  }
}

Then readRawStdin and scanFiles each become a single withTimeout(...) call.

  • Mark as noise

@kirill-knize-sonarsource kirill-knize-sonarsource force-pushed the task/kk/CLI-244-245-callback-infrastructure branch from 4b1d963 to fc175e7 Compare April 9, 2026 09:53
@kirill-knize-sonarsource kirill-knize-sonarsource force-pushed the task/kk/CLI-244-245-callback-infrastructure branch 2 times, most recently from d3d8cdc to b83cb4f Compare April 9, 2026 10:06
Copy link
Copy Markdown
Contributor

@sonar-review-alpha sonar-review-alpha bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conclusion: Solid refactor that cleanly moves hook logic from brittle shell scripts into testable TypeScript. One bug needs fixing before merge — the README was regenerated before this commit's renames took effect, so it documents commands that don't exist or have wrong names.

🗣️ Give feedback


#### `sonar hook codex-pre-tool-use`

PreToolUse handler for Codex: scan files for secrets before agent reads them
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README was regenerated before this commit's renames were applied, so three entries here are stale:

  • sonar hook codex-pre-tool-use (this line) — removed from command-tree.ts in this commit, but still documented here
  • sonar hook agent-prompt-submit (~line 413) — renamed to claude-prompt-submit; old name is registered nowhere
  • sonar hook agent-post-tool-use (~line 419) — renamed to claude-post-tool-use; same issue

Run gen:docs again after the renames and re-commit before merge.

  • Mark as noise

@kirill-knize-sonarsource kirill-knize-sonarsource force-pushed the task/kk/CLI-244-245-callback-infrastructure branch from b83cb4f to 8617798 Compare April 9, 2026 11:05
sonar-review-alpha[bot]

This comment was marked as outdated.

// Hidden callback command — internal handlers for agent and git hooks.
// Shell hook scripts call `sonar hook <event>` to delegate all business logic to TypeScript.
export const hookCommand = COMMAND_TREE.command('hook', { hidden: true })
.description('Internal callback handlers for agent and git hooks')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.description('Internal callback handlers for agent and git hooks')
.description('Internal hook handlers for agent and git hooks')

@kirill-knize-sonarsource kirill-knize-sonarsource force-pushed the task/kk/CLI-244-245-callback-infrastructure branch 2 times, most recently from e04ce87 to a0b217b Compare April 9, 2026 13:44
sonar-review-alpha[bot]

This comment was marked as outdated.

@kirill-knize-sonarsource kirill-knize-sonarsource force-pushed the task/kk/CLI-244-245-callback-infrastructure branch from a0b217b to f4cbc21 Compare April 9, 2026 14:05
sonar-review-alpha[bot]

This comment was marked as outdated.

@kirill-knize-sonarsource kirill-knize-sonarsource force-pushed the task/kk/CLI-244-245-callback-infrastructure branch from f4cbc21 to 468e45c Compare April 9, 2026 14:33
sonar-review-alpha[bot]

This comment was marked as outdated.

@kirill-knize-sonarsource kirill-knize-sonarsource force-pushed the fix/kk/coverage-phantom-lines branch from 8e2ad74 to b9df45c Compare April 9, 2026 22:42
@kirill-knize-sonarsource kirill-knize-sonarsource force-pushed the task/kk/CLI-244-245-callback-infrastructure branch from 468e45c to 38f15b5 Compare April 9, 2026 23:09
sonar-review-alpha[bot]

This comment was marked as outdated.

Base automatically changed from fix/kk/coverage-phantom-lines to master April 10, 2026 09:09
@kirill-knize-sonarsource kirill-knize-sonarsource force-pushed the task/kk/CLI-244-245-callback-infrastructure branch from 00173a3 to 9191141 Compare April 10, 2026 12:53
sonar-review-alpha[bot]

This comment was marked as outdated.

PR feedback: rename symbols, remove codex stub, fix hook naming, update tests

- Rename scanFiles → scanFilesForSecrets in secrets-scan.ts
- Rename callbackCommand → hookCommand in command-tree.ts
- Remove codex-pre-tool-use command (out of scope)
- Remove PR-specific comments from command-tree.ts
- Rename agent-prompt-submit → claude-prompt-submit
- Rename agent-post-tool-use → claude-post-tool-use
- Consolidate duplicate hook.test.ts tests, assert description shown
- Update README.md via gen:docs

Improve coverage: unit tests for readStdinJson / readRawStdin
@kirill-knize-sonarsource kirill-knize-sonarsource force-pushed the task/kk/CLI-244-245-callback-infrastructure branch from 9191141 to 129cc50 Compare April 13, 2026 09:34
@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown
Contributor

@sonar-review-alpha sonar-review-alpha bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! ✅

Two previously flagged issues remain open and unresolved — see the existing review threads for details.

🗣️ Give feedback

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants