Aller au contenu

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

# 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


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.