JsSecuritySensorV2 (Jasmin) never terminates on a single file — CI analysis runs until the 6h GitHub

Environment

  • SonarQube Cloud (sonarcloud.io), CI-based analysis (Automatic Analysis disabled)
  • SonarScanner CLI 8.0.1.6346, invoked via SonarSource/sonarqube-scan-action@v7
  • GitHub Actions, ubuntu-latest
  • Project: private, organization measinc, project key measinc_scoring-app
  • Codebase: TypeScript/React SPA (Vite) — 39 JS/TS files — plus a Rust service analyzed in the same scan

What happens

Every analysis of this project hangs inside the JS security sensor and never finishes. The job runs until it is cancelled or hits the 6-hour GitHub Actions limit. The log always stops at the same point:

INFO  Sensor JsSecuritySensorV2 [jasmin]
INFO  39 file(s) will be analysed by SonarJasmin.
INFO  Analysis progress:   7% (3/39 files)
...
INFO  Analysis progress:  61% (24/39 files)
<nothing further — for hours, until the job is killed>

This is fully deterministic: 8+ runs over 4 days, PR analyses and branch (main) analyses alike, all freeze at the same file count. On cancellation the runner reports orphaned java processes still alive.

The project analyzed cleanly until a routine feature PR grew the app’s import graph; from that merge onward, every scan hangs. No scanner, action, or configuration change happened on our side at that boundary.

Isolating the file

Re-running with sonar.verbose=true names the file: the debug log’s last line before the freeze is

DEBUG Analyzing /home/runner/work/<repo>/<repo>/src/main.tsx (25/39)

src/main.tsx is the application entry point, and it is trivial — a standard Vite/React bootstrap:

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { KioskBoundary } from "./ui/KioskBoundary";
import "./theme.css";
import "./styles.css";

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <KioskBoundary>
      <App />
    </KioskBoundary>
  </React.StrictMode>,
);

Evidence that this file is the trigger:

  • With sonar.exclusions=src/main.tsx and no other change, the entire analysis completes in ~1 minute, including Jasmin over the remaining 38 files.
  • Removing the exclusion reproduces the hang immediately.
  • Every other file in the project analyzes in milliseconds-to-seconds (visible in the verbose log).

Since the file itself contains no logic, our reading is that the taint exploration starting from the entry point walks the application’s whole import graph, and some recent growth in that graph pushed the engine into non-terminating (or effectively unbounded) exploration. The hang began exactly when the app’s component graph grew — the entry file itself was untouched.

Impact

  • Every PR and every push to main consumed a CI runner for up to 6 hours before we found the workaround.
  • The quality gate never reports, so PRs sit with a permanently pending check.

Expected behavior

The sensor should complete or fail within a bounded time. A per-file or per-sensor timeout — as other sensors have — would have turned a multi-day investigation into one log line.

Questions

  1. Is this a known issue in the current Jasmin engine?
  2. Is there a supported analysis parameter to bound or disable Jasmin’s per-file exploration, short of sonar.exclusions?
  3. We’re happy to share analysis IDs, full verbose logs, or a private reproducer with SonarSource engineers — the hang is 100% reproducible on our project.

Current workaround, for anyone else hitting this: a narrow sonar.exclusions entry for the entry-point file restores normal analysis; the file is bootstrap wiring, so nothing meaningful is lost.

Hi @Michael_mnt, thank you for reporting this and for providing these details!

We have a strong hypothesis regarding the root cause and a fix planned for our next release. However, having a minimal reproducer would help us verify that our fix completely resolves your exact scenario. I will reach out to you via DM concerning the reproducer.

Hello @Michael_mnt, Just a quick message to let you know that we have released the new plugin version with the fix and it is now live on SQC. We hope that this solves the issue you described above. Please let us know otherwise.