SonarQube
SonarQube Community Edition runs static code analysis on the crawbl-backend Go codebase. It detects bugs, code smells, and duplicated code. Security vulnerability scanning is provided by gosec (a free Go security linter) whose findings are imported as external issues.
SonarQube is dev-only — it is not deployed in the production cluster.
Connection Details
| Property | Value |
|---|---|
| URL | https://sonar-dev.crawbl.com |
| Service name | sonarqube-sonarqube |
| Namespace | sonarqube |
| Port | 9000 |
| Edition | Community (free) |
| Image | sonarqube:26.3.0.120487-community |
| Database | sonarqube db in backend-postgresql |
| Storage | 5 Gi persistent volume |
| Memory | 2 Gi request / 4 Gi limit |
| JVM heaps | Web 512m + CE 512m + ES 512m = 1.5 Gi |
Credentials
| User | Password | Notes |
|---|---|---|
admin | Set on first login | Change immediately after first boot |
Database credentials
The SonarQube database password is stored in AWS Secrets Manager at crawbl/dev/sonarqube/postgresql and synced to the Kubernetes secret sonarqube-db-secret by External Secrets:
kubectl get secret sonarqube-db-secret -n sonarqube \
-o jsonpath='{.data.jdbc-password}' | base64 -d
Architecture
┌──────────────┐ ┌──────────────────┐ ┌─────────────────────┐
│ Developer │ │ sonar-scanner │ │ SonarQube Server │
│ workstation │────▶│ + gosec │────▶│ (sonarqube ns) │
└──────────────┘ └──────────────────┘ └──────────┬──────────┘
│
┌──────────▼──────────┐
│ backend-postgresql │
│ (backend ns) │
└─────────────────────┘
- gosec runs locally, outputs a SonarQube-compatible JSON report
- sonar-scanner uploads the report + runs its own bug/smell analysis
- SonarQube stores results in the
sonarqubedatabase inside the sharedbackend-postgresqlinstance - The
init-dbPreSync Job automatically creates the database and user on first deploy
Running a Scan
Using the crawbl CLI (recommended)
crawbl app scan
This runs:
- gosec — Go security scanner (OWASP vulnerabilities, hardcoded secrets, weak crypto)
- sonar-scanner — uploads gosec report + SonarQube's own analysis
Results appear at https://sonar-dev.crawbl.com/dashboard?id=crawbl-backend.
Automatic scanning on deploy
crawbl app deploy platform runs the scan automatically after pushing (enabled by default):
crawbl app deploy platform # includes scan + gc
crawbl app deploy platform --scan=false # skip scan
Prerequisites
Both tools are managed by mise:
mise install # installs sonar-scanner-cli + gosec
The SONARQUBE_TOKEN environment variable must be set (already in .env):
set -a && source .env && set +a
crawbl app scan
What Gets Scanned
SonarQube native rules (36 Go rules)
| Type | Count | Examples |
|---|---|---|
| Bug | 7 | Unreachable code, self-assignment, identical conditions |
| Code Smell | 29 | Cognitive complexity, empty functions, deep nesting, duplicated strings |
gosec imported rules (external issues)
| Category | Examples |
|---|---|
| Injection | SQL injection, command injection, LDAP injection |
| Crypto | Weak hash (MD5/SHA1), hardcoded credentials, insecure TLS |
| File I/O | Path traversal, file permissions, directory traversal |
| Network | Unvalidated redirects, SSRF patterns |
| Error handling | Unchecked errors on security-critical operations |
Excluded from scanning
| Path | Reason |
|---|---|
vendor/** | Third-party dependencies |
api/** | Generated CRD types (kubebuilder) |
**/*.pb.go | Protobuf generated code |
**/*_generated.go | Controller-gen output |
**/zz_generated.*.go | Deep copy generated code |
**/testdata/** | Test fixtures |
migrations/** | SQL migrations |
proto/** | Protobuf definitions |
.cache/**, .github/** | Build cache, CI config |
Configuration lives in sonar-project.properties at the repo root.
Quality Gate
The project uses a custom Crawbl quality gate (not the default "Sonar way"):
| Condition | Threshold | Notes |
|---|---|---|
| New violations | > 0 fails | Any new bug or code smell fails the gate |
| New duplication | > 3% fails | Copy-paste detection on new code |
| Security hotspots reviewed | < 100% fails | All hotspots must be reviewed |
No coverage requirement — coverage tracking is not enforced.
The quality gate applies only to new code (code changed since the previous version tag). Existing code does not trigger gate failures.
Security Scanning Strategy
SonarQube Community Edition has zero security rules for Go. Security coverage comes from two free tools:
| Tool | What it finds | Where results appear |
|---|---|---|
| gosec | OWASP vulnerabilities, hardcoded secrets, weak crypto, injection | SonarQube dashboard (external issues) |
| Snyk | Dependency vulnerabilities, first-party code issues, license compliance | Snyk dashboard + Claude Code MCP |
Together they provide better coverage than SonarQube Developer Edition alone.
ArgoCD Configuration
SonarQube is deployed via ArgoCD as a standard component:
| Resource | Path |
|---|---|
| Root Application CR | crawbl-argocd-apps/root/sonarqube.yaml |
| Helm chart (vendored) | crawbl-argocd-apps/components/sonarqube/chart/ |
| Dev values | crawbl-argocd-apps/components/sonarqube/envs/dev.yaml |
| ExternalSecret (DB password) | crawbl-argocd-apps/components/sonarqube/resources/es-sonarqube-secrets.yaml |
| ExternalSecret (PG superuser) | crawbl-argocd-apps/components/sonarqube/resources/es-pg-superuser.yaml |
| Init DB Job | crawbl-argocd-apps/components/sonarqube/resources/init-db-job.yaml |
| HTTPRoute | crawbl-argocd-apps/components/sonarqube/resources/httproute.yaml |
Sync wave: 6 (same as orchestrator — after PostgreSQL at wave 5).
Dev only: The root CR exists in root/ but not root-prod/.
Secrets Reference
| Secret | Location (AWS SM) | K8s Secret | Namespace | Used by |
|---|---|---|---|---|
| SonarQube DB password | crawbl/dev/sonarqube/postgresql | sonarqube-db-secret | sonarqube | StatefulSet (JDBC) |
| PG superuser password | crawbl/dev/backend/postgresql | sonarqube-pg-superuser | sonarqube | Init DB Job |
| Scanner token | .env (SONARQUBE_TOKEN) | N/A | local | crawbl app scan |
Troubleshooting
"SonarQube pod OOM-killed"
SonarQube runs Elasticsearch + Web + Compute Engine in one container. If any JVM heap is too large, the pod exceeds its memory limit.
Current heaps: Web 512m + CE 512m + ES 512m = 1.5 Gi (within 4 Gi limit).
kubectl describe pod sonarqube-sonarqube-0 -n sonarqube | grep -A 3 "Last State"
If OOM-killed, check SONAR_WEB_JAVAOPTS, SONAR_CE_JAVAOPTS, and SONAR_SEARCH_JAVAOPTS in envs/dev.yaml.
"Scan fails with 503"
The SonarQube pod is restarting (usually after an ArgoCD sync). Wait for readiness:
kubectl get pods -n sonarqube -l app=sonarqube -w
Then retry crawbl app scan.
"Quality gate shows ERROR"
Check the dashboard for which condition failed. Common causes:
- New code has a code smell or bug → fix it or mark as "won't fix" in the UI
- All existing code treated as "new" → set new code period to "previous version" in Project Settings > New Code
"Badges show 'project not found' on GitHub"
The sonar.forceAuthentication setting must be false in envs/dev.yaml so badge APIs are public. If the pod restarted and lost the setting, verify:
kubectl port-forward svc/sonarqube-sonarqube 9001:9000 -n sonarqube
curl -s "http://localhost:9001/api/settings/values?keys=sonar.forceAuthentication" \
-u "admin:<password>"
"gosec report not showing in SonarQube"
Verify gosec-report.json was generated:
ls -la gosec-report.json
cat gosec-report.json | python3 -c "import sys,json; d=json.load(sys.stdin); print(f'Issues: {len(d.get(\"issues\",[]))}')"
If the file is empty or missing, run gosec manually:
gosec -fmt=sonarqube -out=gosec-report.json -exclude-dir=vendor -exclude-dir=api ./...
Then re-run crawl app scan.