Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Please use the exception types defined in `src/cli/commands/_common/error.ts` fo

## Tests

Please try to create integration tests in priority. If the test is too complicated to set up, write unit tests.
Always prefer end-to-end integration tests. Unit tests are a last resort — only when e2e is genuinely impractical (e.g. the dependency cannot be controlled or isolated at all).
Try to get inspiration from other tests to follow the same structure.

- Unit tests: `tests/unit/` — run with `bun test:unit`
Expand Down
39 changes: 37 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ Revoke a user token
sonar api post "/api/user_tokens/revoke" --data '{"name":"my-token"}'
```


---

### `sonar integrate`
Expand All @@ -180,7 +181,7 @@ Setup SonarQube integration for Claude Code. This will install secrets scanning

| Option | Type | Required | Description | Default |
| ------------------- | ------- | -------- | --------------------------------------------------------------------------- | ------- |
| `--project`, `-p` | string | No | Project key | - |
| `--project`, `-p` | string | No | SonarCloud project key (overrides auto-detected project) | - |
| `--non-interactive` | boolean | No | Non-interactive mode (no prompts) | - |
| `--global`, `-g` | boolean | No | Install hooks and config globally to ~/.claude instead of project directory | - |

Expand Down Expand Up @@ -392,6 +393,40 @@ Update sonar CLI to the latest version

---

### `sonar hook`

Internal callback handlers for agent and git hooks

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

PreToolUse handler: scan files for secrets before agent reads them

---

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

PreToolUse handler for Codex: scan files for secrets before agent reads them
Comment thread
kirill-knize-sonarsource marked this conversation as resolved.

---

#### `sonar hook agent-prompt-submit`

UserPromptSubmit handler: scan prompts for secrets before sending

---

#### `sonar hook agent-post-tool-use`

PostToolUse handler: run SQAA analysis on modified files

**Options:**

| Option | Type | Required | Description | Default |
| ----------------- | ------ | -------- | ---------------------- | ------- |
| `--project`, `-p` | string | No | SonarCloud project key | - |

---

## Option Types

- `string` — text value (e.g. `--server https://sonarcloud.io`)
Expand Down Expand Up @@ -438,7 +473,7 @@ Both are enabled by default and share the same opt-out toggle. To disable all da
sonar config telemetry --disabled
```

No personally identifiable information is transmitted. File paths in error reports are anonymized by replacing your home directory with `~`.
No personally identifiable information is transmitted.

## Contributing

Expand Down
43 changes: 43 additions & 0 deletions src/cli/command-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ import { parseInteger } from './commands/_common/parsing';
import { MAX_PAGE_SIZE } from '../sonarqube/projects';
import { apiCommand, apiExtraHelpText, type ApiCommandOptions } from './commands/api/api';
import { GENERIC_HTTP_METHODS } from '../sonarqube/client';
import { claudePreToolUse } from './commands/hook/claude-pre-tool-use';
import { agentPromptSubmit } from './commands/hook/agent-prompt-submit';
import {
agentPostToolUse,
type AgentPostToolUseOptions,
} from './commands/hook/agent-post-tool-use';
import { gitPreCommit } from './commands/hook/git-pre-commit';
import { gitPrePush } from './commands/hook/git-pre-push';

const DEFAULT_PAGE_SIZE = MAX_PAGE_SIZE;

Expand Down Expand Up @@ -244,6 +252,41 @@ COMMAND_TREE.command('self-update')
.option('--force', 'Install the latest version even if already up to date')
.anonymousAction((options: SelfUpdateOptions) => selfUpdate(options));

// 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 hook handlers for agent and git hooks')
.enablePositionalOptions()
.anonymousAction(function (this: Command) {
this.outputHelp();
});

hookCommand
.command('claude-pre-tool-use')
.description('PreToolUse handler: scan files for secrets before agent reads them')
.anonymousAction(() => claudePreToolUse());

hookCommand
.command('claude-prompt-submit')
.description('UserPromptSubmit handler: scan prompts for secrets before sending')
.anonymousAction(() => agentPromptSubmit());

hookCommand
.command('claude-post-tool-use')
.description('PostToolUse handler: run SQAA analysis after agent edits or writes a file')
.requiredOption('--project <key>', 'SonarQube Cloud project key')
.anonymousAction((options: AgentPostToolUseOptions) => agentPostToolUse(options));

hookCommand
.command('git-pre-commit')
.description('git pre-commit handler: scan staged files for secrets')
.anonymousAction(() => gitPreCommit());

hookCommand
.command('git-pre-push')
.description('git pre-push handler: scan files in new commits for secrets')
.anonymousAction(() => gitPrePush());

// Hidden flush command — only registered when running as a telemetry worker.
if (process.env[TELEMETRY_FLUSH_MODE_ENV]) {
COMMAND_TREE.command('flush-telemetry', { hidden: true }).anonymousAction(flushTelemetry);
Expand Down
10 changes: 10 additions & 0 deletions src/cli/commands/_common/install/secrets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,13 @@ function cleanupOldVersionBinaries(binDir: string, currentBinaryName: string): v
export function buildLocalBinaryName(platformInfo: PlatformInfo): string {
return `sonar-secrets-${SONAR_SECRETS_VERSION}${buildPlatformSuffix(platformInfo)}`;
}

/**
* Returns the path to the installed sonar-secrets binary, or null if not present.
* Never downloads — use this where silent operation is required (e.g. hook handlers).
*/
export function resolveSecretsBinaryPath(): string | null {
const platform = detectPlatform();
const binaryPath = join(BIN_DIR, buildLocalBinaryName(platform));
return existsSync(binaryPath) ? binaryPath : null;
}
Loading
Loading