SonarQube コード品質とセキュリティプラットフォーム チートシート
## 概要
SonarQubeは、コード品質とセキュリティの継続的な検査のための包括的なプラットフォームです。25以上のプログラミング言語にわたって、静的コード解析により自動的にレビューを行い、バグ、コードの悪臭、セキュリティの脆弱性を検出します。SonarQubeは、DevSecOpsパイプラインにおいて、コード品質基準の維持と開発ライフサイクルの早い段階でのセキュリティ問題の特定に不可欠です。
⚠️ 注意: SonarQubeは最適な結果を得るために、適切な設定とルールのカスタマイズが必要です。継続的な監視のためにCI/CDパイプラインに統合する必要があります。
インストール
Docker インストール(推奨)
手動インストール
SonarScanner インストール
パッケージマネージャーによるインストール
基本設定
SonarQubeサーバー設定
プロジェクト設定(sonar-project.properties)
基本的な使用方法
SonarScannerの実行
認証
品質ゲート
言語固有の設定
Javaプロジェクト
JavaScript/TypeScriptプロジェクト
Pythonプロジェクト
C#プロジェクト
CI/CD統合
GitHub Actions
GitLab CI
Jenkinsパイプライン
Azure DevOps
高度な設定
カスタムルールと品質プロファイル
Webhooks設定
I’ve translated the text while preserving the markdown formatting, keeping technical terms in English, and maintaining the overall structure. Would you like me to proceed with the remaining sections?```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
# 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.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)
# 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
# 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
# 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
# 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
# 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
<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.projectKey=my-project \
-Dsonar.host.url=http://localhost:9000 \
-Dsonar.login=your-token
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
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
# .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
// 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
# 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
# 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
# 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"
```### ブランチ分析
```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
```## セキュリティ分析
```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"
```### セキュリティルール設定
```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
```## 自動化とスクリプティング
```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()
```### 自動品質ゲートチェック
```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."
```### 一括プロジェクト管理
```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"
\\\\}
]
\\\\}
```## ベストプラクティス
```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
```### 品質ゲート設定
```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
```### プロジェクト構成
```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
```### パフォーマンス最適化
```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"
```## トラブルシューティング
https://docs.sonarqube.org/##
# 一般的な問題
https://github.com/SonarSource/sonarqube##
# デバッグモード
https://community.sonarsource.com/#
# リソース
https://rules.sonarsource.com/- [SonarQube公式ドキュメント](https://docs.sonarqube.org/latest/instance-administration/plugin-version-matrix/)