Appearance
SonarCloud SAST
SonarCloud provides Static Application Security Testing (SAST) and code quality analysis for every repository in the Microtec ERP platform. It is the primary tool for identifying security hotspots, code smells, and quality gate enforcement.
What Is SAST?
Static Application Security Testing analyzes source code without executing it. SonarCloud's SAST engine:
- Detects security vulnerabilities: SQL injection, XSS, SSRF, path traversal, insecure deserialization
- Identifies security hotspots requiring human review
- Measures code quality: complexity, duplication, maintainability
- Enforces coverage thresholds on new code
SAST complements DAST (Stage 13 — OWASP ZAP) which tests the running application.
Quality Gate Conditions
The Microtec SonarCloud organization has a single shared quality gate called "Microtec ERP Gate" applied to all projects.
New Code Conditions (blocks pipeline)
| Metric | Threshold | Blocks |
|---|---|---|
| Coverage on new code | ≥ 80% | Yes |
| Duplicated lines on new code | < 3% | Yes |
| Security hotspots reviewed | 100% | Yes |
| New blocker / critical issues | 0 | Yes |
| New security vulnerabilities | 0 | Yes |
| Reliability rating | A | Yes |
| Security rating | A | Yes |
Overall Code Conditions (informational)
| Metric | Target | Blocks |
|---|---|---|
| Maintainability rating | A | No |
| Technical debt ratio | < 5% | No |
| Code smells | Tracked | No |
Important: Only new code conditions block the pipeline. Legacy code is not penalized retroactively — this follows SonarCloud's "clean as you code" philosophy.
Multi-Language Support
SonarCloud auto-detects language from file extensions. All languages used in the Microtec platform are supported natively.
| Language | Extension | Analysis Engine |
|---|---|---|
| C# (.NET 8) | .cs | Roslyn-based SonarAnalyzer |
| TypeScript | .ts, .tsx | ESLint-based rules |
| JavaScript | .js, .mjs | ESLint-based rules |
| Dart (Flutter) | .dart | Community plugin |
| Python | .py | Pylint-based rules |
| Bicep | .bicep | Experimental (IaC) |
| XML / JSON | .xml, .json | Schema validation |
.sonarcloud.properties Configuration
Each repository root contains a .sonarcloud.properties file. This file configures the SonarCloud scanner without requiring changes to the pipeline YAML.
Backend (.NET) — Platforms/.sonarcloud.properties
properties
# Project identification
sonar.projectKey=microtec_platforms
sonar.organization=microtec
# Source paths
sonar.sources=Src
sonar.tests=Src
# Exclusions — generated files, build artifacts, migrations
sonar.exclusions=\
**/obj/**,\
**/bin/**,\
**/Migrations/**,\
**/Migrations.*.cs,\
**/*.Designer.cs,\
**/wwwroot/**,\
**/node_modules/**
# Test file patterns
sonar.test.inclusions=\
**/*Tests/**,\
**/*Test.cs,\
**/*.Specs/**
# Coverage report path (generated by dotnet test --collect:"XPlat Code Coverage")
sonar.cs.opencover.reportsPaths=$(Agent.TempDirectory)/TestResults/**/coverage.opencover.xml
# Duplication exclusions
sonar.cpd.exclusions=\
**/Migrations/**,\
**/*Dto.cs,\
**/*Request.cs,\
**/*Response.csFrontend (Angular) — FrontApps/.sonarcloud.properties
properties
sonar.projectKey=microtec_frontapps
sonar.organization=microtec
sonar.sources=projects,libs
sonar.tests=projects,libs
sonar.test.inclusions=**/*.spec.ts
sonar.exclusions=\
**/node_modules/**,\
**/dist/**,\
**/*.spec.ts,\
**/*.e2e-spec.ts,\
**/environments/**
# Coverage from Jest / Karma
sonar.javascript.lcov.reportPaths=coverage/**/lcov.infoPipeline Integration
SonarCloud analysis runs as part of Stage 2 and uses the SonarCloud Azure DevOps service connection configured in the DevSecOps project.
yaml
# In security-pipeline-template.yml
- stage: SAST
displayName: 'Stage 2 - SAST (SonarCloud)'
jobs:
- job: SonarAnalysis
pool:
vmImage: 'ubuntu-latest'
steps:
- task: SonarCloudPrepare@1
inputs:
SonarCloud: 'SonarCloud-ServiceConnection'
organization: 'microtec'
scannerMode: '$(sonarScannerMode)' # MSBuild | CLI
projectKey: '$(sonarProjectKey)'
# For .NET: insert dotnet build here
# For TypeScript: npm ci && npm test
- task: SonarCloudAnalyze@1
- task: SonarCloudPublish@1
inputs:
pollingTimeoutSec: '300'
- task: Bash@3
displayName: 'Check Quality Gate'
inputs:
script: |
STATUS=$(curl -s -u $(SONAR_TOKEN): \
"https://sonarcloud.io/api/qualitygates/project_status?projectKey=$(sonarProjectKey)" \
| jq -r '.projectStatus.status')
echo "Quality Gate: $STATUS"
if [ "$STATUS" != "OK" ]; then exit 1; fiExclusions and Suppression Patterns
File-Level Exclusions
Add to .sonarcloud.properties:
properties
# Exclude auto-generated API clients
sonar.exclusions=**/MobileAPIClients/**,**/GeneratedCode/**Issue-Level Suppression (C#)
Use [SuppressMessage] with a mandatory justification comment:
csharp
// Justified: This is a test helper, not production code
[SuppressMessage("Security", "S2068:HardcodedPassword",
Justification = "Test fixture uses a well-known test password")]
private const string TestPassword = "TestPassword123!";Issue-Level Suppression (TypeScript)
typescript
// NOSONAR - Justified: intentional use of any for legacy compatibility
const legacyData: any = response.data; // NOSONARWarning: NOSONAR suppressions are tracked by the security team and reviewed quarterly. Unjustified suppressions will be rejected in code review.
Security Hotspots vs Vulnerabilities
SonarCloud distinguishes two security finding types:
| Type | Meaning | Action Required |
|---|---|---|
| Vulnerability | Confirmed security issue | Fix before merge |
| Security Hotspot | Potential issue requiring review | Developer must review and mark Safe/Fixed |
All security hotspots on new code must be reviewed (marked Safe or Fixed) before the quality gate passes.
Viewing Results
- SonarCloud UI:
https://sonarcloud.io/project/overview?id={sonarProjectKey} - PR decoration: SonarCloud automatically posts inline comments on Azure DevOps PRs
- Pipeline artifact: Quality gate JSON published under
sonar-results/
Service Connection Setup
The SonarCloud-ServiceConnection is a DevOps service connection of type SonarCloud. Token is stored in Azure Key Vault mic-erp-be-dev-skv under secret SonarCloud--Token and injected as the SONAR_TOKEN pipeline variable.