Appearance
SonarQube Code Quality and Security Platform Cheat Sheet
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)
bash
# 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_USERNAME=sonar \
-e SONAR_JDBC_PASSWORD=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_USERNAME: sonar
SONAR_JDBC_PASSWORD: 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_PASSWORD: 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
bash
# 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.username=sonar
sonar.jdbc.password=sonar
sonar.jdbc.url=jdbc:postgresql://localhost/sonar
EOF
# Start SonarQube
bin/linux-x86-64/sonar.sh start
# Check status
bin/linux-x86-64/sonar.sh status
SonarScanner Installation
bash
# 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
bash
# 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
properties
# conf/sonar.properties
# Database configuration
sonar.jdbc.username=sonar
sonar.jdbc.password=sonar
sonar.jdbc.url=jdbc:postgresql://localhost/sonar
# Web server configuration
sonar.web.host=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)
properties
# sonar-project.properties
# Project identification
sonar.projectKey=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.target=11
sonar.java.binaries=target/classes
sonar.java.libraries=target/dependency/*.jar
# Exclusions
sonar.exclusions=**/*test*/**,**/*.min.js,**/vendor/**
sonar.test.exclusions=**/*test*/**
# Coverage reports
sonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml
sonar.javascript.lcov.reportPaths=coverage/lcov.info
Basic Usage
Running SonarScanner
bash
# Basic scan
sonar-scanner
# Scan with custom properties
sonar-scanner \
-Dsonar.projectKey=my-project \
-Dsonar.sources=. \
-Dsonar.host.url=http://localhost:9000 \
-Dsonar.login=your-token
# 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
Authentication
bash
# Generate authentication token
curl -u admin:admin -X POST "http://localhost:9000/api/user_tokens/generate?name=my-token"
# Use token in scan
sonar-scanner -Dsonar.login=your-generated-token
# Use username/password (not recommended)
sonar-scanner -Dsonar.login=admin -Dsonar.password=admin
Quality Gates
bash
# Check quality gate status
curl -u token: "http://localhost:9000/api/qualitygates/project_status?projectKey=my-project"
# Set quality gate for project
curl -u token: -X POST "http://localhost:9000/api/qualitygates/select?projectKey=my-project&gateId=1"
# Create custom quality gate
curl -u token: -X POST "http://localhost:9000/api/qualitygates/create?name=Custom+Gate"
Language-Specific Configuration
Java Projects
properties
# Maven integration
sonar.java.source=11
sonar.java.target=11
sonar.java.binaries=target/classes
sonar.java.libraries=target/dependency/*.jar
sonar.java.test.binaries=target/test-classes
sonar.java.test.libraries=target/dependency/*.jar
sonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml
xml
<!-- pom.xml Maven plugin -->
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>3.9.1.2184</version>
</plugin>
bash
# Maven scan
mvn clean verify sonar:sonar \
-Dsonar.projectKey=my-project \
-Dsonar.host.url=http://localhost:9000 \
-Dsonar.login=your-token
JavaScript/TypeScript Projects
properties
# 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/**
json
// package.json script
{
"scripts": {
"sonar": "sonar-scanner"
}
}
Python Projects
properties
# Python configuration
sonar.sources=src
sonar.tests=tests
sonar.python.coverage.reportPaths=coverage.xml
sonar.python.xunit.reportPath=test-reports/xunit.xml
C# Projects
properties
# 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
yaml
# .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
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Cache Maven packages
uses: actions/cache@v3
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2
- name: Build and analyze
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: mvn clean verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
GitLab CI
yaml
# .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:
key: "${CI_JOB_NAME}"
paths:
- .sonar/cache
script:
- mvn verify sonar:sonar
allow_failure: true
only:
- merge_requests
- master
- develop
Jenkins Pipeline
groovy
// Jenkinsfile
pipeline {
agent any
tools {
maven 'Maven-3.6.3'
jdk 'JDK-11'
}
environment {
SONAR_TOKEN = credentials('sonar-token')
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build') {
steps {
sh 'mvn clean compile'
}
}
stage('Test') {
steps {
sh 'mvn test'
}
post {
always {
junit 'target/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
yaml
# 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:
key: 'maven | "$(Agent.OS)" | **/pom.xml'
restoreKeys: |
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
bash
# Export quality profile
curl -u token: "http://localhost:9000/api/qualityprofiles/export?qualityProfile=MyProfile&language=java" > my-profile.xml
# Import quality profile
curl -u token: -X POST -F "backup=@my-profile.xml" "http://localhost:9000/api/qualityprofiles/restore"
# Create custom rule
curl -u token: -X POST \
"http://localhost:9000/api/rules/create" \
-d "custom_key=my-custom-rule" \
-d "name=My Custom Rule" \
-d "markdown_description=This is my custom rule" \
-d "severity=MAJOR" \
-d "status=READY" \
-d "template_key=java:S124"
Webhooks Configuration
bash
# Create webhook
curl -u token: -X POST \
"http://localhost:9000/api/webhooks/create" \
-d "name=MyWebhook" \
-d "url=https://my-server.com/webhook" \
-d "project=my-project"
# Test webhook
curl -u token: -X POST \
"http://localhost:9000/api/webhooks/deliveries" \
-d "webhook=webhook-id"
Branch Analysis
bash
# Analyze main branch
sonar-scanner \
-Dsonar.projectKey=my-project \
-Dsonar.branch.name=main
# Analyze feature branch
sonar-scanner \
-Dsonar.projectKey=my-project \
-Dsonar.branch.name=feature/new-feature \
-Dsonar.branch.target=main
# Analyze pull request
sonar-scanner \
-Dsonar.projectKey=my-project \
-Dsonar.pullrequest.key=123 \
-Dsonar.pullrequest.branch=feature/new-feature \
-Dsonar.pullrequest.base=main
Security Analysis
Security Hotspots
bash
# Get security hotspots
curl -u token: "http://localhost:9000/api/hotspots/search?projectKey=my-project"
# Change hotspot status
curl -u token: -X POST \
"http://localhost:9000/api/hotspots/change_status" \
-d "hotspot=hotspot-key" \
-d "status=REVIEWED" \
-d "resolution=SAFE"
# Add comment to hotspot
curl -u token: -X POST \
"http://localhost:9000/api/hotspots/add_comment" \
-d "hotspot=hotspot-key" \
-d "comment=This has been reviewed and is safe"
Security Rules Configuration
properties
# 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
python
#!/usr/bin/env python3
# sonar_quality_gate.py
import requests
import sys
import time
import argparse
class SonarQubeQualityGate:
def __init__(self, server_url, token, project_key):
self.server_url = server_url.rstrip('/')
self.token = token
self.project_key = project_key
self.session = requests.Session()
self.session.auth = (token, '')
def get_project_status(self):
"""Get project quality gate status"""
url = f"{self.server_url}/api/qualitygates/project_status"
params = {'projectKey': self.project_key}
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('metricKey', '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_key,
'metricKeys': '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_key}")
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('--token', required=True, help='Authentication token')
parser.add_argument('--project', required=True, help='Project key')
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.token, 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
bash
#!/bin/bash
# bulk_sonar_scan.sh
# Configuration
SONAR_SERVER="http://localhost:9000"
SONAR_TOKEN="your-token"
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.projectKey=$project_name
sonar.projectName=$project_name
sonar.projectVersion=1.0
sonar.sources=.
sonar.exclusions=**/node_modules/**,**/target/**,**/build/**,**/.git/**
EOF
fi
# Run scan
sonar-scanner \
-Dsonar.host.url="$SONAR_SERVER" \
-Dsonar.login="$SONAR_TOKEN" \
-Dsonar.projectKey="$project_name"
# Check quality gate
python3 sonar_quality_gate.py \
--server "$SONAR_SERVER" \
--token "$SONAR_TOKEN" \
--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
json
{
"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
properties
# Multi-module project configuration
sonar.projectKey=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
properties
# 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 processing
sonar.scanner.dumpToFile=.scannerwork/scanner-report.json
Troubleshooting
Common Issues
bash
# 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/**,**/target/**,**/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.target=main
Debug Mode
bash
# Enable debug logging
sonar-scanner -X
# Check scanner logs
tail -f .scannerwork/scanner-report.log
# Verify server connectivity
curl -u token: "http://localhost:9000/api/system/status"
Resources
- SonarQube Official Documentation
- SonarQube GitHub Repository
- SonarQube Community
- SonarQube Rules
- SonarQube Plugins
This cheat sheet provides comprehensive guidance for using SonarQube to maintain code quality and security. Regular monitoring and proper configuration are essential for effective DevSecOps integration.