14 - Sonar Static Analysis
Run SonarSource static analysis on the backend solution, generate a reviewable HTML report, and fix issues by severity. Everything runs locally — no SonarQube server required.
A SARIF report per project produced during every dotnet build, plus a single merged HTML report you can open in any browser. The same Sonar C# rule engine used by SonarQube Community Edition runs as a Roslyn analyzer in-process.
How it is wired
Three things are set up in the backend/ folder:
Directory.Build.props— addsSonarAnalyzer.CSharpas a project-wide analyzer and tells MSBuild to write a SARIF file per project at<project>/bin/sonar.sarifon every build.scripts/aggregate-sonar.js— Node script that walks every SARIF file and writes a summary, rule breakdown, and machine-readable JSON intobackend/sonar-report/.scripts/fix-*.js— targeted auto-fixers for specific rule categories (S2139 log-and-rethrow, S6667 log-in-catch, unused loggers, IDisposable pattern, etc.).
backend/Directory.Build.propsXML
<Project>
<PropertyGroup>
<!-- SARIF v2.1.0 error log so we can collect a machine-readable report -->
<ErrorLog>$(MSBuildProjectDirectory)\bin\sonar.sarif,version=2.1</ErrorLog>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<RunAnalyzersDuringBuild>true</RunAnalyzersDuringBuild>
<RunAnalyzersDuringLiveAnalysis>true</RunAnalyzersDuringLiveAnalysis>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.32.0.97167">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
Prerequisites (one-time)
Python 3.8+ and Node.js. The rest comes from dotnet build.
BashInstall sarif-tools (once per machine)
# sarif-tools is the community-standard SARIF → HTML converter.
# It is what we use instead of spinning up a SonarQube server.
pip install sarif-tools
# Verify
sarif --version
If sarif isn't found after installing, the Python Scripts folder may not be on PATH. On Windows with Store-installed Python it lives at something like:
C:\Users\<you>\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\LocalCache\local-packages\Python312\Scripts
Either add that to PATH permanently, or prefix the command as shown in the run steps below.
Step 1 — Build (produces SARIF)
Nothing special to run — a normal build already emits sonar.sarif per project thanks to Directory.Build.props. Make sure no instance of MO.Ekyc.Api.exe is running (Visual Studio F5 / dotnet run) before building, otherwise file locks will fail the copy step.
BashBuild the solution
# From repo root
cd backend
# Clean build to be sure all analyzers run
dotnet build MO.Ekyc.sln -c Debug --no-incremental
# Verify SARIF files were written
find src tests -name sonar.sarif -not -path "*/obj/*"
Step 2 — Aggregate into a summary
The Node aggregator merges all per-project SARIF files, skips #pragma warning disable suppressed items, and writes three files into backend/sonar-report/.
BashRun the aggregator
cd backend
node scripts/aggregate-sonar.js
Outputs:
sonar-report/summary.txt— total count, severity split, top rules, per-project countssonar-report/rules.txt— rule-by-rule breakdown with example file:line locationssonar-report/issues.json— full machine-readable dataset (used by the HTML step)
Step 3 — Generate the HTML report
Collect the SARIF files into one folder and run sarif html.
BashGenerate sonar.html
cd backend
# 1. Gather the per-project SARIF files into one folder
mkdir -p sonar-report/sarif
rm -rf sonar-report/sarif/*
find src tests -name sonar.sarif -not -path "*/obj/*" \
-exec cp --parents {} sonar-report/sarif/ \;
# 2. Convert to a single browsable HTML file
sarif html sonar-report/sarif -o sonar-report/sonar.html
# 3. Open it
start sonar-report/sonar.html # Windows
All reports are under backend/sonar-report/:
sonar.html— single-file HTML dashboard (open in browser)sarif/— raw per-project SARIF files (input to sarif-tools)summary.txt,rules.txt,issues.json— text + JSON reports
Full run in one copy-paste
BashEnd-to-end (from backend/)
cd backend
# Build (emits per-project sonar.sarif)
dotnet build MO.Ekyc.sln -c Debug --no-incremental
# Aggregate summary/rules/issues JSON
node scripts/aggregate-sonar.js
# Produce the HTML report
mkdir -p sonar-report/sarif
rm -rf sonar-report/sarif/*
find src tests -name sonar.sarif -not -path "*/obj/*" \
-exec cp --parents {} sonar-report/sarif/ \;
sarif html sonar-report/sarif -o sonar-report/sonar.html
# Open it
start sonar-report/sonar.html
Reading the report
Severity mapping used during the April 2026 cleanup:
- Blocker — compile errors (
CS0001..CS9999), security issues:S4830(SSL cert bypass),S6781(JWT secret disclosure),EF1002(SQL injection). - Critical —
S2139(log-and-rethrow),S2696(instance method mutating static state),S907(goto),S6667(log in catch without exception),S3923/S2583/S2589(dead branches / unreachable null checks). - High —
S3881+CA1816(IDisposable pattern),CS860xnullable warnings,S4487/S1144(unused private members),S1172(unused params),S6964(value-type under-posting in controllers),CS1998/CA2016(async / ct forwarding). - Medium / Low — style and perf hints (
S1075hardcoded URIs,CA1822static,S3358nested ternary,S6934,CA1861, etc.). Skipped by default; fix opportunistically.
Ready-made fixers
Each script is self-documenting at the top. They are idempotent — safe to re-run.
backend/scripts/Available fixers
aggregate-sonar.js # Build summary/rules/issues from SARIF
fix-s2139.js # Remove _logger.LogError(ex,...) before rethrow
fix-s6667.js # Add `ex` to catch + logger call
fix-cancel-catches.js # Simplify OperationCanceledException catches
fix-unused-logger.js # Strip unused ILogger from controllers
fix-unused-field.js # Strip unused injected service fields
fix-idisposable.js # Add sealed + GC.SuppressFinalize
run-sonar.ps1 # End-to-end PowerShell driver
The fixers use line/column offsets from the SARIF report combined with bracket-matching. Running them with a stale report produces silent corruption (we've hit this once with a regex edit inside a comment). Always re-run dotnet build to refresh SARIF before running a fixer a second time.
Editor integration (optional)
The analyzer runs live in Rider and Visual Studio because RunAnalyzersDuringLiveAnalysis is on. You'll see the same S2139 / S6667 / CA1822 squiggles as the batch run reports — minus the SARIF file output, which only lands on a full build.
CI tip
In a CI pipeline, fail the build when new error-level findings appear:
BashGate a build on Sonar errors
dotnet build MO.Ekyc.sln -c Release --no-incremental
node backend/scripts/aggregate-sonar.js
if grep -q '"level": "error"' backend/sonar-report/issues.json; then
echo "Sonar reported error-level findings"
exit 1
fi
See also
- Frontend Sonar Analysis — same workflow for the Flutter app
- Sonar C# rule catalog — rule IDs, descriptions, code samples
- sarif-tools on GitHub — HTML / CSV / diff commands