TLDR overview
- Sonar Vortex surfaces vulnerability, malware, and license data for a dependency before a coding agent edits the manifest. A separately configured SonarQube CLI pre-commit hook then scans for project-level dependency risks before Git records the commit.
- Vortex check evaluates a specific versioned package URL before any files change. The agent receives vulnerability, malware, and license evidence it uses to decide whether to proceed with the edit.
- When the Vortex response triggers refusal criteria, the agent reports the findings in the conversation before editing files. When the commit-time scan finds new risks at MEDIUM severity or above, the hook blocks the commit with the specific findings and a remediation command.
- Both checkpoints surface dependency information earlier than pull request or CI stages, reducing the cost of discovering a problematic package after other work depends on it.
A developer asked Claude to add nanoid@5.1.6 for request IDs in a Node.js notification service. Claude could translate that request into a package.json edit and an npm install command without knowing whether the package carried known security vulnerabilities, had been flagged as malicious, or was licensed in a way the organization permits. Build steps and test suites would confirm that the application works, but they say nothing about the package itself.
Sonar Vortex and a SonarQube CLI pre-commit hook add two checkpoints to this workflow. Before the manifest changes, Sonar Vortex evaluates the exact package version and returns vulnerability, malware, and license data that the agent uses to decide whether to proceed. After the manifest changes, a separately configured pre-commit hook runs a project dependency-risk scan before Git records the commit.
What Sonar Vortex returned for nanoid@5.1.6
Before running npm install, Claude called the Sonar Vortex dependency check with the versioned package URL pkg:npm/nanoid@5.1.6. The response arrived before Claude edited any project files.
{
"purl": "pkg:npm/nanoid@5.1.6",
"license": { "expression": "MIT", "allowed": true },
"vulnerabilities": [],
"malicious": false,
"action_required": "MUST FOLLOW: before adding or upgrading this dependency, evaluate every field in this response. Refuse it if `malicious` is true, if `license.allowed` is false, or if any non-withdrawn vulnerability meets any block condition: `riskSeverity` is BLOCKER/HIGH, `cvssScore` is high, or `cweIds` contains a dangerous weakness. For blocked vulnerabilities, show the CVE details and propose a safe version from `fixedVersions`/`unaffectedVersions`; if `license.allowed` is null, present `license.expression` for manual policy assessment."
} The action_required field carried a directive telling Claude to evaluate every field and refuse the dependency under specific conditions: malicious set to true, license.allowed set to false, or any non-withdrawn vulnerability with a riskSeverity of BLOCKER or HIGH, a high cvssScore, or dangerous cweIds. For blocking vulnerabilities, the directive also required Claude to show CVE details and propose a version from the fixedVersions or unaffectedVersions fields.
Claude checked each condition against the response. The package was not flagged as malicious, the license (MIT) was marked as allowed, and the vulnerability list was empty. No refusal criteria applied, so Claude ran npm install --package-lock-only --save-exact nanoid@5.1.6, adding nanoid to both package.json and package-lock.json. The response and Claude's evaluation both appeared in the conversation, so the developer could inspect the evidence and the criteria before accepting the edit.
The check accepted one versioned package URL rather than a package name, because vulnerability records, malicious-package findings, and license declarations can differ between releases of the same package. The purl from the developer's request gave the agent context for the exact version it was about to write into the lockfile.
How the project change was checked at commit time
The install added nanoid@5.1.6 alongside the existing lodash dependency.
"dependencies": {
- "lodash": "4.18.1"
+ "lodash": "4.18.1",
+ "nanoid": "5.1.6"
} After Claude staged the two dependency files and ran git commit, a separately configured SonarQube CLI pre-commit hook recognized that dependency manifests had changed and started its analysis.
Synchronizing settings...
Discovering dependency manifests...
Scanning manifests for secrets...
Analyzing dependency risks...
✓ No dependency risks found.
[demo/vortex-agent-safe-dependencies-20260721-142554 ea94d42] Add nanoid for notification request IDs
2 files changed, 22 insertions(+), 2 deletions(-)Synchronizing settings...
Discovering dependency manifests...
Scanning manifests for secrets...
Analyzing dependency risks...
✓ No dependency risks found.
[demo/vortex-agent-safe-dependencies-20260721-142554 ea94d42] Add nanoid for notification request IDs
2 files changed, 22 insertions(+), 2 deletions(-)
The hook triggered because staged filenames matched dependency-file patterns that the scanner provides dynamically. Once triggered, the scanner synchronized the project's settings from SonarQube Cloud, discovered all dependency manifests in the working directory, ran a secrets pre-scan to check those manifests for accidentally committed credentials, and then completed the dependency-risk analysis. Each of those stages is visible in the hook output, and the secrets pre-scan and dependency-risk analysis run in sequence within the same hook invocation, so a single git commit can surface both credential leaks and vulnerable dependencies before the commit is recorded. The scan found no new risks at MEDIUM severity or above, so the hook returned successfully and Git recorded the commit.
Sonar Vortex had evaluated a single versioned package URL before Claude edited the manifest. The hook ran a project-level dependency-risk scan afterward, triggered by the staged dependency files. A clean package response from Sonar Vortex does not establish the project-level result, because the hook analyzes the project's dependency manifests and lockfiles (so it also catches transitive dependencies that the new package pulls in), as a separate project-level operation.
When a dependency gets flagged
Package check
The nanoid response had an empty vulnerability list, a permitted license, and no malware flag. A different package or version can produce a response where one or more of those fields triggers the refusal criteria in the action_required directive.
When malicious is true, the directive tells the agent to refuse the package outright. When license.allowed is false, the package violates the organization's license policy and the directive requires refusal. When license.allowed is null, the automated policy evaluation was not available, so the directive tells the agent to present the SPDX license.expression for the developer to assess manually.
Vulnerabilities with a riskSeverity of BLOCKER or HIGH, a high cvssScore, or cweIds matching dangerous weaknesses also trigger refusal. The directive tells the agent to show CVE details and propose a safe version when the returned fixedVersions or unaffectedVersions information identifies a candidate. A response can also return package_not_found or version_not_found when the purl does not match a known package or release.
Because the response arrives before any project files have changed, a flagged package does not require unwinding a lockfile edit or reverting staged changes. The developer sees the specific finding in the conversation and can stop the edit, choose a different version, or override the recommendation. When the directive requires the agent to propose a safe version, the fixedVersions and unaffectedVersions information in the response may identify a candidate that addresses the reported vulnerability, and the same dependency check is available for the replacement version. The dependency check returns data and guidance without editing files or blocking any process, so the agent's response to a flagged package depends on its configuration and surrounding instructions.
Commit checkpoint
When the hook's scan finds new dependency risks at MEDIUM, HIGH, or BLOCKER severity, the CLI produces an error that prevents Git from recording the commit. The SonarQube CLI source code shows the error message format: 1 dependency risk found (1 HIGH) for a single finding, or 3 dependency risks found (1 BLOCKER, 2 HIGH) when multiple severities are involved, listed highest-first. The error includes a remediation hint directing the developer to run sonar analyze dependency-risks -p <project-key> for a detailed view of findings and fix recommendations, with git commit --no-verify named as a bypass when the risks have already been reviewed.
The thrown error produces a non-zero exit status that the generated shell hook propagates, so Git does not record the commit in the normal hook path. The hook filters to risks with status new at MEDIUM severity or above, and pre-existing risks and lower-severity findings pass through without stopping the commit. A developer whose commit is blocked can run the suggested follow-up command to see full details, address the finding by choosing a different package version, and retry the commit.
After the mandatory secrets scan succeeds, the dependency-risk stage fails open for operational failures. A missing SCA scanner binary, network issues during dependency-risk analysis, and errors in that analysis produce a warning rather than blocking the commit for a reason unrelated to actual dependency risks.
What changes in the developer loop
Before both checkpoints are configured, dependency information typically first appears downstream in a pull request or CI report, at a point when other work may already depend on the change. A developer who discovers a vulnerability at that stage faces a more expensive decision, because reverting the dependency and finding an alternative can mean redoing work that depended on the original package. With Sonar Vortex and the pre-commit hook in place, the developer receives package-level evidence while the agent is still deciding whether to proceed with the edit, and a project-level dependency-risk result before the commit is recorded locally.
Sonar Vortex evaluates one versioned package URL and returns advisory results that the agent uses during the dependency decision. The pre-commit hook runs a scanner-backed project analysis triggered by staged dependency files and can block the commit for new risks at MEDIUM severity or above. The two checkpoints cover different scopes and run at different moments in the workflow, and they also use different thresholds. Sonar Vortex flags a package at BLOCKER or HIGH severity, while the commit hook blocks at MEDIUM and above. A package that clears the Vortex check can still be stopped at commit, so both are important and complementary.
The hook runs locally, applies only where the project-scoped integration is installed, and can be bypassed with git commit --no-verify. After the mandatory secrets scan succeeds, SCA scanner or network failures leave the commit unblocked by design.
To add the package decision point, configure Sonar Vortex context augmentation for the coding agent and confirm that the SonarQube Cloud project has the required access. Configure the commit checkpoint separately by installing the SonarQube CLI and following the Git hook instructions with --dependency-risks; the dependency-risk stage requires SonarQube Advanced Security with SCA enabled on the project.
