# SonarQube Integration Guide for AI Agents

This guide explains how AI agents can integrate with SonarQube/SonarCloud for automated code quality analysis, security scanning, and PR review workflows. Target audience: AI code assistants working with repositories that use SonarQube for quality gates.

## Overview

SonarQube provides automated code quality and security analysis integrated into CI/CD workflows. AI agents can download analysis results programmatically, review findings, and fix issues automatically without requiring manual SonarCloud UI access.

**Key capabilities:**

- Download complete analysis results via GoTask commands
- Parse issues and security hotspots from downloaded reports
- Address findings programmatically (fix real issues, suppress false positives)
- Integrate with `/pr:fix` workflow for automated PR remediation
- Track quality gate status and blocking issues

**Supported workflows:**

- **PR-based analysis**: Get issues specific to a pull request
- **Branch analysis**: Get issues for default branch
- **Local scanning**: Run sonar-scanner locally before pushing
- **Automated fixes**: Bulk remediation of security hotspots and code smells

## Prerequisites

### Repository Requirements

**Minimum setup** (may not exist in all repositories):

- `sonar-project.properties` file in repository root
- `SONAR_TOKEN` environment variable or secret
- GitHub Actions workflow with SonarQube step (optional)

**Typical structure:**

```text
repository/
├── sonar-project.properties    # SonarQube configuration
├── .github/
│   ├── workflows/
│   │   └── ci.yml             # CI workflow with SonarQube job
│   └── actions/
│       └── sonar/
│           └── action.yml      # Reusable SonarQube action
├── .env                        # Local secrets (gitignored)
└── tasks/
    └── sonar.yml              # GoTask automation (if using GoTask)
```

**If repository lacks SonarQube setup:**

The repository may not have SonarQube configured. Check for these files before attempting analysis:

```bash
# Check for sonar-project.properties
[ -f sonar-project.properties ] && echo "SonarQube configured" || echo "No SonarQube"

# Check for SonarQube GitHub Action
grep -r "sonar" .github/workflows/ 2>/dev/null
```

If not configured, skip SonarQube analysis or suggest setup (see [Setting Up SonarQube](#setting-up-sonarqube)).

### Token Setup

**For local analysis:**

Add to `.env` file (gitignored):

```bash
# SonarQube/SonarCloud authentication token
SONAR_TOKEN=your-token-here

# Or use 1Password reference
SONAR_TOKEN=op://Private/SonarCloud/API Token/token
```

**For CI/CD:**

Token should be configured as GitHub secret:

- **Secret name**: `SONAR_TOKEN`
- **Where**: Repository Settings → Secrets and variables → Actions

## GoTask Integration

### Available Commands

**Analysis commands:**

```bash
# Download complete analysis results (recommended)
task sonar:download

# View most recent downloaded report
task sonar:issues

# Run local scan (requires sonar-scanner installed)
task sonar:scan
```

**Setup commands:**

```bash
# Install sonar-scanner CLI (macOS/Linux)
task sonar:setup

# Show all available commands
task sonar:help
```

### Download Analysis Results

The `sonar:download` command fetches complete analysis results from SonarQube API and generates a human-readable report.

**What it does:**

1. Detects PR context (or default branch if no PR)
2. Fetches issues, security hotspots, and quality gate status
3. Generates timestamped report in `.logs/sonar/`
4. Opens report in VSCode (if available)

**Example output:**

```bash
$ task sonar:download

ℹ Analyzing PR #4: feat(installer): Phase 2 - Agent installation
ℹ Project: northbridge-security_ai-toolkit
ℹ Branch: feat/installer-ph2 → main

❌ Quality Gate: ERROR
ℹ Downloaded 237 issues + 49 hotspots → .logs/sonar/issues-20251118-115543.txt
```

**Report structure:**

```text
SonarQube Analysis Report
=========================
Project: northbridge-security_ai-toolkit
Analysis: PR #4
Date: 2025-11-18 11:55:43

Quality Gate: ERROR
Failed Conditions:
  - coverage: 65.2% (threshold: 80%)
  - duplicated_lines_density: 9.1% (threshold: 3%)

Issues: 237
By Severity:
  Blocker: 4
  Critical: 73
  Major: 76
  Minor: 78

Security Hotspots: 49
  To Review: 49
  Reviewed: 0

═══════════════════════════════════════════════════════════════
DETAILED ISSUES
═══════════════════════════════════════════════════════════════

[BLOCKER] .github/workflows/approve.yml:24
Type: CODE_SMELL
Rule: javascript:S2076
Message: Make sure that executing this OS command is safe here.
───────────────────────────────────────────────────────────────
...
```

### Programmatic Analysis

**Read downloaded reports:**

```bash
# Most recent report
REPORT=$(ls -t .logs/sonar/issues-*.txt 2>/dev/null | head -1)

# Count issues by severity
BLOCKER=$(grep "^\[BLOCKER\]" "$REPORT" | wc -l)
CRITICAL=$(grep "^\[CRITICAL\]" "$REPORT" | wc -l)

# Extract specific file issues
grep "src/tasks/bash/utils.ts" "$REPORT"
```

**Parse raw JSON data:**

```bash
# Raw API responses are also saved
RAW_JSON=$(ls -t .logs/sonar/issues-*-raw.json 2>/dev/null | head -1)

# Extract high-severity issues
jq '.issues[] | select(.severity == "BLOCKER" or .severity == "CRITICAL")' "$RAW_JSON"

# Group by file
jq -r '.issues[] | "\(.component | split(":")[1]):\(.line)"' "$RAW_JSON" | sort | uniq -c
```

## Analyzing Findings

### Issue Categories

**By severity:**

- **BLOCKER**: Breaks core functionality, must fix immediately
- **CRITICAL**: High-impact bugs or security vulnerabilities
- **MAJOR**: Moderate-impact issues (complexity, maintainability)
- **MINOR**: Low-impact code smells (style, minor improvements)

**By type:**

- **BUG**: Logic errors, incorrect behavior
- **VULNERABILITY**: Security weaknesses
- **CODE_SMELL**: Maintainability issues
- **SECURITY_HOTSPOT**: Code requiring security review

### Security Hotspots

Security hotspots require manual review to determine if they're real vulnerabilities or false positives.

**Hotspot workflow:**

1. **Download report**: `task sonar:download`
2. **Review each hotspot**: Check the code context
3. **Determine action**:
   - **Real issue**: Fix the vulnerability
   - **False positive**: Add suppression comment with justification
4. **Document decision**: Add inline comments explaining why

**Hotspot severity levels:**

- **HIGH**: Likely security vulnerability, immediate attention
- **MEDIUM**: Potential security issue, review carefully
- **LOW**: Minor security concern, low risk

**Example hotspot review:**

```typescript
// Security hotspot: Command injection warning
// Line 37: execSync('shfmt -d "' + file + '"')

// Analysis:
// - file variable comes from discoverFiles() function
// - discoverFiles() returns file paths from filesystem
// - Filenames could contain special characters like quotes, semicolons
// - This creates a command injection vulnerability

// FIX: Use proper shell escaping
import { escapeShellArg } from './utils';
execSync('shfmt -d ' + escapeShellArg(file));
```

**Example false positive:**

```typescript
// Security hotspot: Hard-coded password
// Line 76: const token = 'github_pat_1234567890abcd';

// Analysis:
// - This is a test file (tests/integration/github-mcp.test.ts)
// - Token is clearly a fake placeholder (sequential characters)
// - Not a real secret

// SUPPRESSION: Document false positive
// nosemgrep: generic.secrets.security.detected-generic-secret.detected-generic-secret
const token = 'github_pat_1234567890abcd'; // Test fixture, not real secret
```

### Suppression Patterns

**When to suppress:**

- Test fixtures with fake credentials
- Hardcoded commands with no user input
- Simple regex patterns flagged as ReDoS
- Permission checks in test cleanup code
- Math.random() used for non-security purposes

**Suppression format:**

```typescript
// SECURITY: [Explanation of why this is safe]
// nosemgrep: [rule-id]
[code that triggered warning]
```

**Common suppression rules:**

| Issue Type         | Semgrep Rule ID                                                                      |
| ------------------ | ------------------------------------------------------------------------------------ |
| Generic secrets    | `generic.secrets.security.detected-generic-secret.detected-generic-secret`           |
| Command injection  | `javascript.lang.security.audit.unsafe-exec.unsafe-exec`                             |
| ReDoS              | `javascript.lang.security.audit.detect-non-literal-regexp.detect-non-literal-regexp` |
| Weak crypto        | `javascript.lang.security.audit.detect-pseudorandom-usage.detect-pseudorandom-usage` |
| Unsafe permissions | `javascript.lang.security.audit.unsafe-permissions.unsafe-permissions`               |

## The /pr:fix Workflow

The `/pr:fix` slash command integrates SonarQube analysis into automated PR remediation.

**Workflow overview:**

1. **Detect PR context** - Identifies current branch and PR number
2. **Download PR comments** - Fetches GitHub PR review comments
3. **Download workflow logs** - Gets CI/CD failure details
4. **Download SonarQube analysis** - Runs `task sonar:download`
5. **Analyze findings** - Categorizes issues by priority
6. **Fix blocking issues** - Addresses critical/blocker issues automatically
7. **Update PR review documentation** - Tracks progress in `.pr.review.local.md`

**Usage:**

```bash
# Run in repository with open PR
/pr:fix
```

**What it does with SonarQube:**

```typescript
// Phase 3: Download SonarQube analysis
await bash('task sonar:download');

// Read downloaded report
const report = readFile('.logs/sonar/issues-*.txt');

// Parse findings
const blockers = parseIssues(report, 'BLOCKER');
const criticals = parseIssues(report, 'CRITICAL');
const hotspots = parseSecurityHotspots(report);

// Prioritize fixes
const mustFix = [...blockers, ...hotspots.filter((h) => h.severity === 'HIGH')];

// Fix each issue programmatically
for (const issue of mustFix) {
  await fixIssue(issue);
}

// Update documentation
await updatePRReview({ fixed: mustFix.length, remaining: criticals.length });
```

**Example `/pr:fix` output:**

```text
Phase 3: Downloading SonarQube analysis...
✓ Downloaded 237 issues + 49 hotspots

Analyzing findings:
- 4 BLOCKER issues (immediate fix required)
- 73 CRITICAL issues (high priority)
- 49 security hotspots (11 HIGH, 8 MEDIUM, 30 LOW)

Fixing BLOCKER issues:
✓ Fixed: .github/workflows/approve.yml:24 (command injection)
✓ Fixed: .github/actions/sonar/action.yml:92 (command injection)
✓ Fixed: src/tasks/bash/utils.ts:24 (command injection)
✓ Fixed: src/tasks/json/utils.ts:208 (incomplete escaping)

Addressing HIGH priority security hotspots:
✓ Fixed: src/tasks/bash/format-check.ts:37 (added escapeShellArg)
✓ Fixed: src/tasks/bash/format.ts:44 (added escapeShellArg)
✓ Fixed: src/tasks/bash/lint.ts:34 (added escapeShellArg)
✓ Documented: tests/integration/github-mcp.test.ts:76 (test fixture)

Updated: .pr.review.local.md
```

**Result:**

- All blocking issues fixed
- Progress tracked in `.pr.review.local.md`
- PR ready for review with clean quality gate

## Setting Up SonarQube

If repository doesn't have SonarQube configured, here's how to set it up.

### SonarCloud Project Setup

**1. Create SonarCloud project:**

- Go to https://sonarcloud.io
- Log in with GitHub account
- Click "+ " → "Analyze new project"
- Select repository
- Choose "With GitHub Actions" setup method

**2. Configure GitHub secret:**

- Copy SonarCloud token from setup page
- Go to GitHub repo → Settings → Secrets and variables → Actions
- Create new secret:
  - **Name**: `SONAR_TOKEN`
  - **Value**: [paste token]

### Configuration File

Create `sonar-project.properties` in repository root:

```properties
# SonarCloud Configuration
sonar.projectKey=organization_repository-name
sonar.organization=organization-name

# Project metadata
sonar.projectName=Repository Name
sonar.projectVersion=1.0

# Source code
sonar.sources=src
sonar.tests=tests
sonar.sourceEncoding=UTF-8

# Language-specific settings
sonar.javascript.lcov.reportPaths=.logs/coverage/lcov.info
sonar.testExecutionReportPaths=.logs/test-results/junit.xml

# Exclusions
sonar.exclusions=\
  **/node_modules/**,\
  **/dist/**,\
  **/coverage/**,\
  **/.logs/**,\
  **/tests/**/*.test.ts

sonar.test.inclusions=\
  tests/**/*.test.ts,\
  **/*.test.ts

# Coverage exclusions
sonar.coverage.exclusions=\
  tests/**,\
  **/*.test.ts,\
  **/types/**

# Quality gate
sonar.qualitygate.wait=false
```

**Key settings:**

| Property                            | Purpose                 | Example                    |
| ----------------------------------- | ----------------------- | -------------------------- |
| `sonar.projectKey`                  | Unique identifier       | `org_repo-name`            |
| `sonar.organization`                | SonarCloud organization | `your-org`                 |
| `sonar.sources`                     | Source code directory   | `src`                      |
| `sonar.tests`                       | Test directory          | `tests`                    |
| `sonar.javascript.lcov.reportPaths` | Coverage report path    | `.logs/coverage/lcov.info` |

### GitHub Actions Integration

**Create reusable action** (`.github/actions/sonar/action.yml`):

```yaml
name: 'SonarQube Analysis'
description: 'Runs SonarQube/SonarCloud analysis with sonar-scanner CLI'

inputs:
  github-token:
    description: 'GitHub token for authentication'
    required: true
  sonar-token:
    description: 'SonarQube/SonarCloud token for authentication'
    required: true
  sonar-host-url:
    description: 'SonarQube/SonarCloud host URL'
    required: false
    default: 'https://sonarcloud.io'

runs:
  using: 'composite'
  steps:
    - name: Install sonar-scanner
      shell: bash
      run: |
        curl -sSL https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.8.0.2856-linux.zip -o /tmp/sonar-scanner.zip
        unzip -q /tmp/sonar-scanner.zip -d /tmp/
        echo "/tmp/sonar-scanner-4.8.0.2856-linux/bin" >> $GITHUB_PATH

    - name: Run SonarQube analysis
      shell: bash
      env:
        GITHUB_TOKEN: ${{ inputs.github-token }}
        SONAR_TOKEN: ${{ inputs.sonar-token }}
        HEAD_REF: ${{ github.head_ref }}
        BASE_REF: ${{ github.base_ref }}
        PR_NUMBER: ${{ github.event.pull_request.number }}
      run: |
        SCANNER_ARGS="-Dsonar.host.url=${{ inputs.sonar-host-url }}"

        # Add PR-specific parameters
        if [ -n "$PR_NUMBER" ]; then
          SCANNER_ARGS="$SCANNER_ARGS \
            -Dsonar.pullrequest.key=${PR_NUMBER} \
            -Dsonar.pullrequest.branch=${HEAD_REF} \
            -Dsonar.pullrequest.base=${BASE_REF}"
        fi

        sonar-scanner $SCANNER_ARGS
```

**Add to CI workflow** (`.github/workflows/ci.yml`):

```yaml
jobs:
  # Run SonarQube code quality analysis
  sonarqube:
    name: SonarQube Analysis
    runs-on: ubuntu-latest
    needs: [lint, typecheck, test]
    if: github.event_name == 'pull_request' && secrets.SONAR_TOKEN != ''
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0 # Full history for better analysis

      - name: Setup Bun
        uses: oven-sh/setup-bun@v2
        with:
          bun-version: latest

      - name: Install dependencies
        run: bun install --frozen-lockfile

      - name: Run tests with coverage
        run: bun test --coverage

      - name: Run SonarQube analysis
        uses: ./.github/actions/sonar
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          sonar-token: ${{ secrets.SONAR_TOKEN }}
```

### GoTask Integration

**Create task file** (`tasks/sonar.yml`):

```yaml
version: '3'

vars:
  SONAR_HOST: '{{.SONAR_HOST | default "https://sonarcloud.io"}}'

tasks:
  download:
    desc: Download SonarQube analysis results
    dotenv: ['.env']
    cmds:
      - |
        # Read project key from sonar-project.properties
        PROJECT_KEY=$(grep "^sonar.projectKey=" sonar-project.properties | cut -d'=' -f2)

        # Detect PR context
        PR_NUMBER=$(gh pr list --head "$(git branch --show-current)" --json number --jq '.[0].number' 2>/dev/null || echo "")

        # Fetch issues from SonarQube API
        if [ -n "$PR_NUMBER" ]; then
          ISSUES_API="{{.SONAR_HOST}}/api/issues/search?pullRequest=$PR_NUMBER&componentKeys=$PROJECT_KEY&ps=500"
        else
          ISSUES_API="{{.SONAR_HOST}}/api/issues/search?componentKeys=$PROJECT_KEY&ps=500"
        fi

        curl -s -X GET "$ISSUES_API" -H "Authorization: Bearer $SONAR_TOKEN" > .logs/sonar/issues.json
```

## Best Practices

### For AI Agents

**Always use task commands:**

```bash
# ✓ GOOD - Use task commands (logged, cached, consistent)
task sonar:download

# ✗ BAD - Direct API calls (no caching, harder to debug)
curl -X GET "https://sonarcloud.io/api/issues/search?..." -H "Authorization: Bearer $SONAR_TOKEN"
```

**Check for SonarQube setup before using:**

```typescript
// Check if sonar-project.properties exists
const hasSonar = existsSync('sonar-project.properties');

if (!hasSonar) {
  console.log('⚠️  SonarQube not configured in this repository');
  return; // Skip SonarQube analysis
}

// Proceed with analysis
await bash('task sonar:download');
```

**Parse reports programmatically:**

```typescript
// Read downloaded report
const report = readFileSync('.logs/sonar/issues-20251118-115543.txt', 'utf-8');

// Extract issues by pattern matching
const blockers = report
  .split('\n')
  .filter((line) => line.startsWith('[BLOCKER]'))
  .map((line) => parseIssueLine(line));

// Fix each issue
for (const issue of blockers) {
  await fixIssue(issue);
}
```

**Document all suppressions:**

```typescript
// ALWAYS add explanation for suppressions
// ✓ GOOD
// SECURITY: Test fixture with fake GitHub token format, not a real secret
// nosemgrep: generic.secrets.security.detected-generic-secret.detected-generic-secret
const token = 'github_pat_test123';

// ✗ BAD
// nosemgrep: generic.secrets.security.detected-generic-secret.detected-generic-secret
const token = 'github_pat_test123';
```

**Prioritize fixes:**

1. **BLOCKER**: Fix immediately (breaks core functionality)
2. **HIGH security hotspots**: Review and fix real vulnerabilities
3. **CRITICAL**: Fix after blockers
4. **MEDIUM/LOW hotspots**: Document false positives, defer real issues
5. **MAJOR/MINOR**: Address in follow-up PRs

### For Repository Maintainers

**Quality gate targets:**

- **Coverage**: ≥80% code coverage
- **Duplication**: ≤3% duplicated code
- **Security**: 0 vulnerabilities, 0 HIGH hotspots
- **Maintainability**: No BLOCKER/CRITICAL code smells

**Custom quality gates:**

Edit in SonarCloud UI → Project → Administration → Quality Gate

**Branch protection:**

Require SonarQube check to pass before merging:

1. GitHub → Settings → Branches
2. Add rule for `main` branch
3. Check "Require status checks to pass"
4. Select "SonarQube Analysis"

## Troubleshooting

### Token Not Set

**Error:**

```text
❌ SONAR_TOKEN not set
Add to .env: SONAR_TOKEN=your-token
```

**Solution:**

```bash
# Add to .env file (gitignored)
echo "SONAR_TOKEN=your-token-here" >> .env

# Or use 1Password
echo "SONAR_TOKEN=op://Private/SonarCloud/API Token/token" >> .env
```

### No Reports Found

**Error:**

```text
❌ No reports found
Run: task sonar:download
```

**Solution:**

```bash
# Download analysis first
task sonar:download

# Then view results
task sonar:issues
```

### PR Analysis Not Found

**Warning:**

```text
⚠️  PR analysis not found, showing default branch analysis
```

**Why:** SonarQube PR analysis only runs after CI completes. If viewing results before CI finishes, default branch analysis is shown instead.

**Solution:** Wait for CI to complete, then run `task sonar:download` again.

### Quality Gate Failed

**Error:**

```text
❌ Quality Gate: ERROR
Failed Conditions:
  - coverage: 65.2% (threshold: 80%)
```

**Solution:**

```bash
# Check what needs to be fixed
task sonar:download

# Fix coverage
bun test --coverage

# Fix duplication
# (Refactor duplicated code blocks)

# Re-run analysis
task sonar:scan
```

### sonar-scanner Not Found

**Error:**

```text
sonar-scanner: command not found
```

**Solution:**

```bash
# Install sonar-scanner
task sonar:setup

# Verify installation
sonar-scanner --version
```

## Examples

### Example 1: Fix All Blocker Issues

```bash
# Download analysis
task sonar:download

# Read report
REPORT=$(ls -t .logs/sonar/issues-*.txt | head -1)

# Extract blocker issues
grep "^\[BLOCKER\]" "$REPORT" | while read -r line; do
  # Parse file and line number
  FILE=$(echo "$line" | sed 's/\[BLOCKER\] \(.*\):.*/\1/')
  LINE=$(echo "$line" | sed 's/.*:\([0-9]*\).*/\1/')

  echo "Fixing $FILE:$LINE"

  # Open in editor or apply automated fix
  # ... implementation ...
done
```

### Example 2: Review Security Hotspots by Priority

```bash
# Download analysis
task sonar:download

# Find HIGH priority hotspots in report
REPORT=$(ls -t .logs/sonar/issues-*.txt | head -1)

# Extract HIGH severity hotspots
grep -A3 "^\[HIGH\]" "$REPORT" | while read -r line; do
  case "$line" in
    \[HIGH\]*)
      FILE=$(echo "$line" | sed 's/\[HIGH\] \(.*\):.*/\1/')
      echo "Review: $FILE"
      ;;
    Message:*)
      echo "  → ${line#Message: }"
      ;;
  esac
done
```

### Example 3: Track Fix Progress

```typescript
// Track fixes in PR review documentation
interface FixProgress {
  total: number;
  fixed: number;
  remaining: number;
  categories: {
    blocker: number;
    critical: number;
    hotspots: { high: number; medium: number; low: number };
  };
}

async function trackProgress(): Promise<FixProgress> {
  // Download latest analysis
  await bash('task sonar:download');

  // Read report
  const report = readFileSync('.logs/sonar/issues-latest.txt', 'utf-8');

  // Count by severity
  const blockers = (report.match(/^\[BLOCKER\]/gm) || []).length;
  const criticals = (report.match(/^\[CRITICAL\]/gm) || []).length;
  const highHotspots = (report.match(/^\[HIGH\].*hotspot/gim) || []).length;

  return {
    total: blockers + criticals + highHotspots,
    fixed: 0, // Updated as fixes are applied
    remaining: blockers + criticals + highHotspots,
    categories: {
      blocker: blockers,
      critical: criticals,
      hotspots: { high: highHotspots, medium: 0, low: 0 },
    },
  };
}
```

## Related Documentation

- [GoTask Usage Guide](./agents.gotask.md) - Task automation patterns
- [Markdown Standards](./agents.markdown.md) - Documentation formatting
- [GitHub MCP Integration](./github-mcp.md) - PR and issue management
- [QA Standards](../QA.md) - Quality assurance guidelines
- [SonarCloud Documentation](https://docs.sonarcloud.io) - Official docs

---

**For AI Agents**: Always use `task sonar:download` to fetch analysis results programmatically. Never require manual UI access. Parse downloaded reports to address findings automatically.
