SonarQube Code Quality and Security Platform aide-mémoire
Overview
SonarQube is a comprehensive platform for continuous inspection of code quality and security. It performs automatic reviews with static analysis of code to detect bugs, code smells, and security vulnerabilities across 25+ programming languages. SonarQube is essential in DevSecOps pipelines for maintaining code quality standards and identifying security issues early in the development lifecycle.
⚠️ Note: SonarQube requires proper configuration and rule customization for optimal results. It should be integrated into CI/CD pipelines for continuous monitoring.
Installation
Docker Installation (Recommended)
# Pull SonarQube image
docker pull sonarqube:community
# Run SonarQube with PostgreSQL
docker run -d --name sonarqube \
-p 9000:9000 \
-e SONAR_JDBC_URL=jdbc:postgresql://db:5432/sonar \
-e SONAR_JDBC_nom d'utilisateur=sonar \
-e SONAR_JDBC_mot de passe=sonar \
sonarqube:community
# Using Docker Compose
cat > docker-compose.yml << 'EOF'
version: "3"
services:
sonarqube:
image: sonarqube:community
depends_on:
- db
environment:
SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar
SONAR_JDBC_nom d'utilisateur: sonar
SONAR_JDBC_mot de passe: sonar
ports:
- "9000:9000"
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_extensions:/opt/sonarqube/extensions
- sonarqube_logs:/opt/sonarqube/logs
db:
image: postgres:13
environment:
POSTGRES_USER: sonar
POSTGRES_mot de passe: sonar
POSTGRES_DB: sonar
volumes:
- postgresql:/var/lib/postgresql
- postgresql_data:/var/lib/postgresql/data
volumes:
sonarqube_data:
sonarqube_extensions:
sonarqube_logs:
postgresql:
postgresql_data:
EOF
docker-compose up -d
Manual Installation
# Download SonarQube
wget https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-9.9.0.65466.zip
unzip sonarqube-9.9.0.65466.zip
cd sonarqube-9.9.0.65466
# Configure database (PostgreSQL recommended)
# Edit conf/sonar.properties
cat >> conf/sonar.properties << 'EOF'
sonar.jdbc.nom d'utilisateur=sonar
sonar.jdbc.mot de passe=sonar
sonar.jdbc.url=jdbc:postgresql://localhôte/sonar
EOF
# Start SonarQube
bin/linux-x86-64/sonar.sh start
# Check status
bin/linux-x86-64/sonar.sh status
SonarScanner Installation
# Download SonarScanner
wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.8.0.2856-linux.zip
unzip sonar-scanner-cli-4.8.0.2856-linux.zip
sudo mv sonar-scanner-4.8.0.2856-linux /opt/sonar-scanner
# Add to PATH
echo 'export PATH="/opt/sonar-scanner/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
# Verify Installation
sonar-scanner --version
Package Manager Installation
# Ubuntu/Debian
wget -qO - https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.8.0.2856-linux.zip
# macOS with Homebrew
brew install sonar-scanner
# Using npm
npm install -g sonarqube-scanner
Basic configuration
SonarQube Server configuration
# conf/sonar.properties
# Database configuration
sonar.jdbc.nom d'utilisateur=sonar
sonar.jdbc.mot de passe=sonar
sonar.jdbc.url=jdbc:postgresql://localhôte/sonar
# Web server configuration
sonar.web.hôte=0.0.0.0
sonar.web.port=9000
sonar.web.context=/sonar
# Elasticsearch configuration
sonar.search.javaOpts=-Xmx512m -Xms512m -XX:MaxDirectMemorySize=256m
# Security configuration
sonar.security.realm=LDAP
sonar.authenticator.downcase=true
# Quality gate configuration
sonar.qualitygate.wait=true
Project configuration (sonar-project.properties)
# sonar-project.properties
# Project identification
sonar.projectclé=my-project
sonar.projectName=My Project
sonar.projectVersion=1.0
# Source code configuration
sonar.sources=src
sonar.tests=tests
sonar.sourceEncoding=UTF-8
# Language-specific settings
sonar.java.source=11
sonar.java.cible=11
sonar.java.binaries=cible/classes
sonar.java.libraries=cible/dependency/*.jar
# Exclusions
sonar.exclusions=**/*test*/**,**/*.min.js,**/vendor/**
sonar.test.exclusions=**/*test*/**
# Coverage reports
sonar.coverage.jacoco.xmlReportPaths=cible/site/jacoco/jacoco.xml
sonar.javascript.lcov.reportPaths=coverage/lcov.info
Basic utilisation
Running SonarScanner
# Basic scan
sonar-scanner
# Scan with custom properties
sonar-scanner \
-Dsonar.projectclé=my-project \
-Dsonar.sources=. \
-Dsonar.hôte.url=http://localhôte:9000 \
-Dsonar.login=your-jeton
# Scan specific directory
sonar-scanner -Dsonar.sources=src/main/java
# Scan with exclusions
sonar-scanner \
-Dsonar.exclusions="**/*test*/**,**/*.min.js" \
-Dsonar.test.exclusions="**/*test*/**"
# Debug mode
sonar-scanner -X
authentification
# Generate authentification jeton
curl -u admin:admin -X POST "http://localhôte:9000/api/user_jetons/generate?name=my-jeton"
# Use jeton in scan
sonar-scanner -Dsonar.login=your-generated-jeton
# Use nom d'utilisateur/mot de passe (not recommended)
sonar-scanner -Dsonar.login=admin -Dsonar.mot de passe=admin
Quality Gates
# Check quality gate status
curl -u jeton: "http://localhôte:9000/api/qualitygates/project_status?projectclé=my-project"
# Set quality gate for project
curl -u jeton: -X POST "http://localhôte:9000/api/qualitygates/select?projectclé=my-project&gateId;=1"
# Create custom quality gate
curl -u jeton: -X POST "http://localhôte:9000/api/qualitygates/create?name=Custom+Gate"
Language-Specific configuration
Java Projects
# Maven integration
sonar.java.source=11
sonar.java.cible=11
sonar.java.binaries=cible/classes
sonar.java.libraries=cible/dependency/*.jar
sonar.java.test.binaries=cible/test-classes
sonar.java.test.libraries=cible/dependency/*.jar
sonar.coverage.jacoco.xmlReportPaths=cible/site/jacoco/jacoco.xml
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>3.9.1.2184</version>
</plugin>
# Maven scan
mvn clean verify sonar:sonar \
-Dsonar.projectclé=my-project \
-Dsonar.hôte.url=http://localhôte:9000 \
-Dsonar.login=your-jeton
JavaScript/TypeScript Projects
# JavaScript configuration
sonar.sources=src
sonar.tests=test
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.typescript.lcov.reportPaths=coverage/lcov.info
sonar.exclusions=node_modules/**,dist/**,build/**
// package.json script
\\\\{
"scripts": \\\\{
"sonar": "sonar-scanner"
\\\\}
\\\\}
Python Projects
# Python configuration
sonar.sources=src
sonar.tests=tests
sonar.python.coverage.reportPaths=coverage.xml
sonar.python.xunit.reportPath=test-reports/xunit.xml
C# Projects
# C# configuration
sonar.cs.dotcover.reportsPaths=dotCover.html
sonar.cs.nunit.reportsPaths=TestResult.xml
sonar.cs.opencover.reportsPaths=opencover.xml
CI/CD Integration
GitHub Actions
# .github/workflows/sonarqube.yml
name: SonarQube Analysis
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
sonarqube:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
- name: Cache SonarQube packages
uses: actions/cache@v3
with:
path: ~/.sonar/cache
clé: $\\\\{\\\\{ runner.os \\\\}\\\\}-sonar
restore-clés: $\\\\{\\\\{ runner.os \\\\}\\\\}-sonar
- name: Cache Maven packages
uses: actions/cache@v3
with:
path: ~/.m2
clé: $\\\\{\\\\{ runner.os \\\\}\\\\}-m2-$\\\\{\\\\{ hashFiles('**/pom.xml') \\\\}\\\\}
restore-clés: $\\\\{\\\\{ runner.os \\\\}\\\\}-m2
- name: Build and analyze
env:
GITHUB_jeton: $\\\\{\\\\{ secrets.GITHUB_jeton \\\\}\\\\}
SONAR_jeton: $\\\\{\\\\{ secrets.SONAR_jeton \\\\}\\\\}
run: mvn clean verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
GitLab CI
# .gitlab-ci.yml
stages:
- test
- sonarqube
sonarqube-check:
stage: sonarqube
image: maven:3.6.3-jdk-11
variables:
SONAR_USER_HOME: "$\\\\{CI_PROJECT_DIR\\\\}/.sonar"
GIT_DEPTH: "0"
cache:
clé: "$\\\\{CI_JOB_NAME\\\\}"
paths:
- .sonar/cache
script:
- mvn verify sonar:sonar
allow_failure: true
only:
- merge_requests
- master
- develop
Jenkins Pipeline
// Jenkinsfile
pipeline \\\\{
agent any
tools \\\\{
maven 'Maven-3.6.3'
jdk 'JDK-11'
\\\\}
environment \\\\{
SONAR_jeton = identifiants('sonar-jeton')
\\\\}
stages \\\\{
stage('Checkout') \\\\{
steps \\\\{
checkout scm
\\\\}
\\\\}
stage('Build') \\\\{
steps \\\\{
sh 'mvn clean compile'
\\\\}
\\\\}
stage('Test') \\\\{
steps \\\\{
sh 'mvn test'
\\\\}
post \\\\{
always \\\\{
junit 'cible/surefire-reports/*.xml'
\\\\}
\\\\}
\\\\}
stage('SonarQube Analysis') \\\\{
steps \\\\{
withSonarQubeEnv('SonarQube') \\\\{
sh 'mvn sonar:sonar'
\\\\}
\\\\}
\\\\}
stage('Quality Gate') \\\\{
steps \\\\{
timeout(time: 1, unit: 'HOURS') \\\\{
waitForQualityGate abortPipeline: true
\\\\}
\\\\}
\\\\}
\\\\}
\\\\}
Azure DevOps
# azure-pipelines.yml
trigger:
- main
pool:
vmImage: 'ubuntu-latest'
variables:
MAVEN_CACHE_FOLDER: $(Pipeline.Workspace)/.m2/repository
MAVEN_OPTS: '-Dmaven.repo.local=$(MAVEN_CACHE_FOLDER)'
steps:
- task: Cache@2
inputs:
| clé: 'maven | "$(Agent.OS)" | **/pom.xml' |
restoreclés: |
maven|"$(Agent.OS)"
maven
path: $(MAVEN_CACHE_FOLDER)
displayName: Cache Maven local repo
- task: SonarQubePrepare@4
inputs:
SonarQube: 'SonarQube'
scannerMode: 'Other'
- task: Maven@3
inputs:
mavenPomFile: 'pom.xml'
goals: 'clean verify'
options: '-Dmaven.repo.local=$(MAVEN_CACHE_FOLDER)'
javaHomeoption: 'JDKVersion'
jdkVersionoption: '1.11'
- task: SonarQubeAnalyze@4
- task: SonarQubePublish@4
inputs:
pollingTimeoutSec: '300'
Advanced configuration
Custom Rules and Quality Profiles
# Export quality profile
curl -u jeton: "http://localhôte:9000/api/qualityprofiles/export?qualityProfile=MyProfile&language;=java" > my-profile.xml
# Import quality profile
curl -u jeton: -X POST -F "backup=@my-profile.xml" "http://localhôte:9000/api/qualityprofiles/restore"
# Create custom rule
curl -u jeton: -X POST \
"http://localhôte:9000/api/rules/create" \
-d "custom_clé=my-custom-rule" \
-d "name=My Custom Rule" \
-d "markdown_Description=This is my custom rule" \
-d "severity=MAJOR" \
-d "status=READY" \
-d "template_clé=java:S124"
Webhooks configuration
# Create webhook
curl -u jeton: -X POST \
"http://localhôte:9000/api/webhooks/create" \
-d "name=MyWebhook" \
-d "url=https://my-server.com/webhook" \
-d "project=my-project"
# Test webhook
curl -u jeton: -X POST \
"http://localhôte:9000/api/webhooks/deliveries" \
-d "webhook=webhook-id"
Branch Analysis
# Analyze main branch
sonar-scanner \
-Dsonar.projectclé=my-project \
-Dsonar.branch.name=main
# Analyze feature branch
sonar-scanner \
-Dsonar.projectclé=my-project \
-Dsonar.branch.name=feature/new-feature \
-Dsonar.branch.cible=main
# Analyze pull request
sonar-scanner \
-Dsonar.projectclé=my-project \
-Dsonar.pullrequest.clé=123 \
-Dsonar.pullrequest.branch=feature/new-feature \
-Dsonar.pullrequest.base=main
Security Analysis
Security Hotspots
# Get security hotspots
curl -u jeton: "http://localhôte:9000/api/hotspots/search?projectclé=my-project"
# Change hotspot status
curl -u jeton: -X POST \
"http://localhôte:9000/api/hotspots/change_status" \
-d "hotspot=hotspot-clé" \
-d "status=REVIEWED" \
-d "resolution=SAFE"
# Add comment to hotspot
curl -u jeton: -X POST \
"http://localhôte:9000/api/hotspots/add_comment" \
-d "hotspot=hotspot-clé" \
-d "comment=This has been reviewed and is safe"
Security Rules configuration
# Enable security rules
sonar.security.hotspots.enabled=true
sonar.security.review.enabled=true
# OWASP Top 10 categories
sonar.security.owasp-a1.enabled=true
sonar.security.owasp-a2.enabled=true
sonar.security.owasp-a3.enabled=true
sonar.security.owasp-a4.enabled=true
sonar.security.owasp-a5.enabled=true
sonar.security.owasp-a6.enabled=true
sonar.security.owasp-a7.enabled=true
sonar.security.owasp-a8.enabled=true
sonar.security.owasp-a9.enabled=true
sonar.security.owasp-a10.enabled=true
# SANS Top 25 categories
sonar.security.sans-top25.enabled=true
Automation and Scripting
Automated Quality Gate Check
#!/usr/bin/env python3
# sonar_quality_gate.py
import requests
import sys
import time
import argparse
class SonarQubeQualityGate:
def __init__(self, server_url, jeton, project_clé):
self.server_url = server_url.rstrip('/')
self.jeton = jeton
self.project_clé = project_clé
self.session = requests.session()
self.session.auth = (jeton, '')
def get_project_status(self):
"""Get project quality gate status"""
url = f"\\\\{self.server_url\\\\}/api/qualitygates/project_status"
params = \\\\{'projectclé': self.project_clé\\\\}
try:
response = self.session.get(url, params=params)
response.raise_for_status()
return response.json()
except requests.RequestException as e:
print(f"Error getting project status: \\\\{e\\\\}")
return None
def wait_for_analysis(self, timeout=300, interval=10):
"""Wait for analysis to complete"""
start_time = time.time()
while time.time() - start_time < timeout:
status = self.get_project_status()
if status and 'projectStatus' in status:
project_status = status['projectStatus']
if project_status.get('status') != 'IN_PROGRESS':
return status
print(f"Analysis in progress... waiting \\\\{interval\\\\} seconds")
time.sleep(interval)
else:
print("Could not get project status")
time.sleep(interval)
print(f"Timeout waiting for analysis to complete (\\\\{timeout\\\\}s)")
return None
def check_quality_gate(self):
"""Check if quality gate passes"""
status = self.get_project_status()
if not status:
return False, "Could not get project status"
project_status = status.get('projectStatus', \\\\{\\\\})
gate_status = project_status.get('status')
if gate_status == 'OK':
return True, "Quality gate passed"
elif gate_status == 'ERROR':
conditions = project_status.get('conditions', [])
failed_conditions = [c for c in conditions if c.get('status') == 'ERROR']
error_msg = "Quality gate failed:\n"
for condition in failed_conditions:
metric = condition.get('metricclé', 'Unknown')
actual = condition.get('actualValue', 'N/A')
threshold = condition.get('errorThreshold', 'N/A')
error_msg += f" - \\\\{metric\\\\}: \\\\{actual\\\\} (threshold: \\\\{threshold\\\\})\n"
return False, error_msg
else:
return False, f"Unknown quality gate status: \\\\{gate_status\\\\}"
def get_metrics(self):
"""Get project metrics"""
url = f"\\\\{self.server_url\\\\}/api/measures/component"
params = \\\\{
'component': self.project_clé,
'metricclés': 'bugs,vulnerabilities,code_smells,coverage,duplicated_lines_density'
\\\\}
try:
response = self.session.get(url, params=params)
response.raise_for_status()
data = response.json()
metrics = \\\\{\\\\}
for measure in data.get('component', \\\\{\\\\}).get('measures', []):
metrics[measure['metric']] = measure.get('value', '0')
return metrics
except requests.RequestException as e:
print(f"Error getting metrics: \\\\{e\\\\}")
return \\\\{\\\\}
def print_summary(self):
"""Print analysis summary"""
metrics = self.get_metrics()
print("\n=== SonarQube Analysis Summary ===")
print(f"Project: \\\\{self.project_clé\\\\}")
print(f"Bugs: \\\\{metrics.get('bugs', 'N/A')\\\\}")
print(f"Vulnerabilities: \\\\{metrics.get('vulnerabilities', 'N/A')\\\\}")
print(f"Code Smells: \\\\{metrics.get('code_smells', 'N/A')\\\\}")
print(f"Coverage: \\\\{metrics.get('coverage', 'N/A')\\\\}%")
print(f"Duplicated Lines: \\\\{metrics.get('duplicated_lines_density', 'N/A')\\\\}%")
def main():
parser = argparse.ArgumentParser(Description='SonarQube Quality Gate Checker')
parser.add_argument('--server', required=True, help='SonarQube server URL')
parser.add_argument('--jeton', required=True, help='authentification jeton')
parser.add_argument('--project', required=True, help='Project clé')
parser.add_argument('--wait', action='store_true', help='Wait for analysis to complete')
parser.add_argument('--timeout', type=int, default=300, help='Timeout in seconds')
args = parser.parse_args()
qg = SonarQubeQualityGate(args.server, args.jeton, args.project)
if args.wait:
print("Waiting for analysis to complete...")
status = qg.wait_for_analysis(timeout=args.timeout)
if not status:
print("Analysis did not complete within timeout")
sys.exit(1)
qg.print_summary()
passed, message = qg.check_quality_gate()
print(f"\n\\\\{message\\\\}")
sys.exit(0 if passed else 1)
if __name__ == '__main__':
main()
Bulk Project Management
#!/bin/bash
# bulk_sonar_scan.sh
# configuration
SONAR_SERVER="http://localhôte:9000"
SONAR_jeton="your-jeton"
PROJECTS_DIR="/path/to/projects"
REportS_DIR="/path/to/reports"
# Function to scan project
scan_project() \\\\{
local project_path="$1"
local project_name=$(basename "$project_path")
echo "Scanning $project_name..."
cd "$project_path"
# Create sonar-project.properties if it doesn't exist
if [ ! -f "sonar-project.properties" ]; then
cat > sonar-project.properties << EOF
sonar.projectclé=$project_name
sonar.projectName=$project_name
sonar.projectVersion=1.0
sonar.sources=.
sonar.exclusions=**/node_modules/**,**/cible/**,**/build/**,**/.git/**
EOF
fi
# Run scan
sonar-scanner \
-Dsonar.hôte.url="$SONAR_SERVER" \
-Dsonar.login="$SONAR_jeton" \
-Dsonar.projectclé="$project_name"
# Check quality gate
python3 sonar_quality_gate.py \
--server "$SONAR_SERVER" \
--jeton "$SONAR_jeton" \
--project "$project_name" \
--wait
if [ $? -ne 0 ]; then
echo "$project_name" >> "$REportS_DIR/failed_quality_gates.txt"
fi
\\\\}
# Create reports directory
mkdir -p "$REportS_DIR"
# Scan all projects
find "$PROJECTS_DIR" -maxdepth 1 -type d|while read -r project_dir; do
if [ "$project_dir" != "$PROJECTS_DIR" ]; then
scan_project "$project_dir"
fi
done
echo "Bulk scanning completed. Check $REportS_DIR for results."
Best Practices
Quality Gate configuration
\\\\{
"name": "Strict Security Gate",
"conditions": [
\\\\{
"metric": "new_vulnerabilities",
"op": "GT",
"error": "0"
\\\\},
\\\\{
"metric": "new_bugs",
"op": "GT",
"error": "0"
\\\\},
\\\\{
"metric": "new_security_hotspots_reviewed",
"op": "LT",
"error": "100"
\\\\},
\\\\{
"metric": "new_coverage",
"op": "LT",
"error": "80"
\\\\},
\\\\{
"metric": "new_duplicated_lines_density",
"op": "GT",
"error": "3"
\\\\}
]
\\\\}
Project Organization
# Multi-module project configuration
sonar.projectclé=my-company:my-project
sonar.projectName=My Project
sonar.projectVersion=1.0
# Module configuration
sonar.modules=module1,module2,module3
module1.sonar.projectName=Module 1
module1.sonar.sources=module1/src
module1.sonar.tests=module1/test
module2.sonar.projectName=Module 2
module2.sonar.sources=module2/src
module2.sonar.tests=module2/test
module3.sonar.projectName=Module 3
module3.sonar.sources=module3/src
module3.sonar.tests=module3/test
Performance Optimization
# Performance tuning
sonar.scanner.metadataFilePath=.scannerwork/report-task.txt
sonar.working.directory=.scannerwork
sonar.scm.disabled=false
sonar.scm.provider=git
# Memory settings
sonar.scanner.javaOpts=-Xmx2048m -XX:MaxPermSize=256m
# Parallel processusing
sonar.scanner.dumpToFile=.scannerwork/scanner-report.json
dépannage
Common Issues
# Issue: Out of memory errors
# Solution: Increase memory allocation
export SONAR_SCANNER_OPTS="-Xmx2048m"
# Issue: Analysis taking too long
# Solution: Exclude unnecessary files
sonar-scanner -Dsonar.exclusions="**/node_modules/**,**/cible/**,**/build/**"
# Issue: Quality gate webhook not working
# Solution: Check webhook configuration and network connectivity
curl -X POST https://your-webhook-url.com/webhook -d '\\\\{"test": "data"\\\\}'
# Issue: Branch analysis not working
# Solution: Ensure proper branch configuration
sonar-scanner -Dsonar.branch.name=feature-branch -Dsonar.branch.cible=main
Debug Mode
# Enable debug logging
sonar-scanner -X
# Check scanner logs
tail -f .scannerwork/scanner-report.log
# Verify server connectivity
curl -u jeton: "http://localhôte:9000/api/system/status"
Resources
- SonarQube Documentation Officielle
- SonarQube GitHub Repository
- SonarQube Community
- SonarQube Rules
- SonarQube Plugins
This aide-mémoire provides comprehensive guidance for using SonarQube to maintain code quality and security. Regular monitoring and proper configuration are essential for effective DevSecOps integration.