SonarQube Code Qualität und Sicherheit Plattform Cheat Blatt¶
Überblick¶
SonarQube ist eine umfassende Plattform zur kontinuierlichen Überprüfung von Codequalität und Sicherheit. Es führt automatische Bewertungen mit statischer Analyse von Code, um Fehler, Codegeruch und Sicherheitslücken über 25+ Programmiersprachen zu erkennen. Sonar Qube ist in DevSecOps-Pipelines für die Einhaltung von Code-Qualitätsstandards und die Identifizierung von Sicherheitsfragen früh im Entwicklungslebenszyklus unerlässlich.
ZEIT Note: SonarQube erfordert eine richtige Konfiguration und Regelanpassung für optimale Ergebnisse. Es sollte in CI/CD-Pipelines zur kontinuierlichen Überwachung integriert werden.
Installation¶
Docker Installation (empfohlen)¶
```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 ```_
Manuelle 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 ```_
Installation des Paketmanagers¶
```bash
Ubuntu/Debian¶
macOS with Homebrew¶
brew install sonar-scanner
Using npm¶
npm install -g sonarqube-scanner ```_
Grundkonfiguration¶
SonarQube Server Konfiguration¶
```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 ```_
Projektkonfiguration (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 ```_
Basisnutzung¶
Laufender 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 ```_
Qualitätstore¶
```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" ```_
Sprach-spezifische Konfiguration¶
Java Projekte¶
```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
_
bash
Maven scan¶
mvn clean verify sonar:sonar \ -Dsonar.projectKey=my-project \ -Dsonar.host.url=http://localhost:9000 \ -Dsonar.login=your-token ```_
JavaScript/TypScript Projekte¶
```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 Projekte¶
```properties
Python configuration¶
sonar.sources=src sonar.tests=tests sonar.python.coverage.reportPaths=coverage.xml sonar.python.xunit.reportPath=test-reports/xunit.xml ```_
C# Projekte¶
```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 Aktionen¶
```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 DevOs¶
```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' ```_
Erweiterte Konfiguration¶
Kundenspezifische Regeln und Qualitätsprofile¶
```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 Konfiguration¶
```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" ```_
Messtechnik¶
```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 ```_
Sicherheitsanalyse¶
Sicherheit 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" ```_
Sicherheitsregeln Konfiguration¶
```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 ```_
Automatisierung und Schrift¶
Automatisierte Qualität 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¶
Qualität Gate Konfiguration¶
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"
\\\\}
]
\\\\}
_
Projektorganisation¶
```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 ```_
Leistungsoptimierung¶
```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 ```_
Fehlerbehebung¶
Gemeinsame Themen¶
```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" ```_
Ressourcen¶
- SonarQube Offizielle Dokumentation
- SonarQube GitHub Repository
- [SonarQube Community](LINK_5__ -%20SonarQube%20Rules
- SonarQube Plugins
--
*Dieses Betrugsblatt bietet umfassende Anleitung für die Verwendung von SonarQube, um Codequalität und Sicherheit zu erhalten. Regelmäßige Überwachung und richtige Konfiguration sind für eine effektive DevSecOps-Integration unerlässlich. *