Skip to content

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

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


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.