SonarCloud Security Triage Skill
This skill applies triage decisions to SonarCloud security issues (vulnerabilities and hotspots) by reading a CSV file with review decisions and updating the statuses in SonarCloud via the API.
Prerequisites
- •Node.js v18 or higher
- •SonarCloud API token with Administer Security Hotspots and Administer Issues permissions
- •CSV file with triage decisions (output from
sonarcloud-security-auditskill with added columns)
Workflow
This skill complements the sonarcloud-security-audit skill:
- •Export security issues →
sonarcloud-security-auditskill generates CSV - •Review in spreadsheet → User adds triage columns (Action, Resolution, Comment, Reviewer)
- •Apply decisions → This skill updates SonarCloud with the triage decisions
CSV Format Required
The input CSV must have these columns (typically the output of sonarcloud-security-audit plus 4 additional columns):
Original columns from audit:
- •Project, Type, Severity, Status, Rule, Message, Component, Line, Created, URL
Added triage columns:
- •Action - For SECURITY_HOTSPOT:
REVIEWED| For VULNERABILITY:confirm,falsepositive,wontfix,resolve - •Resolution - For SECURITY_HOTSPOT only:
SAFEorFIXED - •Comment - Review explanation (optional but recommended)
- •Reviewer - Email or name of reviewer (optional, for tracking)
Example rows:
Project,Type,Severity,Status,Rule,Message,Component,Line,Created,URL,Action,Resolution,Comment,Reviewer NASA-PDS_doi-ui,SECURITY_HOTSPOT,,TO_REVIEW,,Using http protocol...,src/file.jsx,119,2021-01-28T19:38:04+0000,https://sonarcloud.io/project/security_hotspots?id=NASA-PDS_doi-ui&hotspots=AZPV1fTprahIrD-njDRb,REVIEWED,SAFE,"False positive. This is a URI not a URL.",jordan@jpl.nasa.gov NASA-PDS_data-upload,VULNERABILITY,MAJOR,OPEN,python:S7608,Add ExpectedBucketOwner...,src/sync.py,134,2025-10-10T20:33:50+0000,https://sonarcloud.io/project/issues?open=AZnP1S0b_yFrdYV3Iu6e&id=NASA-PDS_data-upload,wontfix,,"Scheduled for future sprint",jane@jpl.nasa.gov
Execution Steps
Step 1: Check Prerequisites
Verify the SonarCloud token is set:
env | grep SONARCLOUD_TOKEN
If not set, prompt the user to set it (same token used for audit skill).
Step 2: Validate CSV
Check that the CSV file exists and has the required columns:
- •Must have URL column (to extract issue/hotspot keys)
- •Must have Action column (identifies rows to process)
- •For SECURITY_HOTSPOT rows with Action=REVIEWED, must have Resolution column
Step 3: Run the Triage Script
Execute the script:
cd sonarcloud-security-triage node scripts/apply-triage.mjs <path-to-csv> [--dry-run]
Parameters:
- •
<path-to-csv>(required): Path to CSV file with triage decisions - •
--dry-run(optional): Preview changes without actually updating SonarCloud
The script will:
- •Parse the CSV file
- •Filter rows where Action column is not empty
- •Extract issue/hotspot key from the URL column
- •For each row:
- •SECURITY_HOTSPOT → Call
POST /api/hotspots/change_status- •Parameters:
hotspot,status=REVIEWED,resolution(SAFE/FIXED),comment
- •Parameters:
- •VULNERABILITY → Call
POST /api/issues/do_transition- •Parameters:
issue,transition(confirm/falsepositive/wontfix/resolve),comment
- •Parameters:
- •SECURITY_HOTSPOT → Call
- •Log success/failure for each update
- •Generate summary report
Step 4: Review Results
The script outputs:
- •Success count: Number of issues successfully updated
- •Failed count: Number of issues that failed to update (with error messages)
- •Skipped count: Number of rows skipped (empty Action or already in target status)
- •Summary CSV: Optional output file with status of each update
Step 5: Verify in SonarCloud
After running, verify a few updates in the SonarCloud UI:
- •Navigate to the project's Security Hotspots or Issues page
- •Check that statuses were updated correctly
- •Verify comments were added
API Endpoints Used
For Security Hotspots (TO_REVIEW → REVIEWED)
Endpoint: POST /api/hotspots/change_status
Parameters:
- •
hotspot(required): Hotspot key extracted from URL (e.g.,AZPV1fTprahIrD-njDRb) - •
status(required): AlwaysREVIEWED - •
resolution(required):SAFEorFIXED - •
comment(optional): Review explanation
Example:
curl -X POST 'https://sonarcloud.io/api/hotspots/change_status' \ -H 'Authorization: Bearer <token>' \ -d 'hotspot=AZPV1fTprahIrD-njDRb' \ -d 'status=REVIEWED' \ -d 'resolution=SAFE' \ -d 'comment=False positive. This is a URI not a URL.'
For Vulnerabilities (OPEN → Other statuses)
Endpoint: POST /api/issues/do_transition
Parameters:
- •
issue(required): Issue key extracted from URL (e.g.,AZnP1S0b_yFrdYV3Iu6e) - •
transition(required): One ofconfirm,falsepositive,wontfix,resolve - •
comment(optional): Explanation for the transition
Example:
curl -X POST 'https://sonarcloud.io/api/issues/do_transition' \ -H 'Authorization: Bearer <token>' \ -d 'issue=AZnP1S0b_yFrdYV3Iu6e' \ -d 'transition=wontfix' \ -d 'comment=Scheduled for future sprint'
Extracting Keys from URLs
The script extracts issue/hotspot keys from the URL column:
Security Hotspot URL:
https://sonarcloud.io/project/security_hotspots?id=NASA-PDS_doi-ui&hotspots=AZPV1fTprahIrD-njDRb
^^^^^^^^^^^^^^^^^^^^^^
Extract this part
Vulnerability URL:
https://sonarcloud.io/project/issues?open=AZnP1S0b_yFrdYV3Iu6e&id=NASA-PDS_data-upload
^^^^^^^^^^^^^^^^^^^^^
Extract this part
Error Handling
Authentication Errors (401)
- •Token is invalid or expired
- •Token doesn't have required permissions
- •Regenerate token with correct permissions
Permission Errors (403)
- •Token doesn't have Administer Security Hotspots permission for hotspots
- •Token doesn't have Administer Issues permission for vulnerabilities
- •Check token scopes in SonarCloud settings
Not Found Errors (404)
- •Issue/hotspot key is invalid or was deleted
- •Skip and continue with next item
Invalid Transition Errors (400)
- •Transition not allowed from current status
- •Resolution required but not provided
- •Log error and skip item
Rate Limiting (429)
- •Script automatically waits 60 seconds and retries
- •If persistent, reduce batch size or run during off-peak hours
Dry Run Mode
Before applying changes, use --dry-run to preview:
node scripts/apply-triage.mjs triage.csv --dry-run
Dry run output:
[DRY RUN] Would update hotspot AZPV1fTprahIrD-njDRb: Project: NASA-PDS_doi-ui Type: SECURITY_HOTSPOT Action: Change status to REVIEWED (SAFE) Comment: "False positive. This is a URI not a URL." [DRY RUN] Would update issue AZnP1S0b_yFrdYV3Iu6e: Project: NASA-PDS_data-upload Type: VULNERABILITY Action: Transition to wontfix Comment: "Scheduled for future sprint" Summary: 2 updates would be applied (0 errors)
Best Practices
- •Always run dry-run first to preview changes
- •Add meaningful comments to explain triage decisions (helps future reviewers)
- •Start small - test with 5-10 issues before processing thousands
- •Track reviewer - include email/name in Reviewer column for accountability
- •Backup CSV - keep original audit CSV before adding triage columns
- •Verify sample - Check a few issues in SonarCloud UI after bulk update
Troubleshooting
"Column 'Action' not found"
- •CSV is missing the Action column
- •Ensure you added the 4 triage columns: Action, Resolution, Comment, Reviewer
"Could not extract hotspot key from URL"
- •URL format may have changed
- •Verify URL column contains valid SonarCloud URLs
- •Check if URL contains
hotspots=oropen=parameter
"No rows to process"
- •All Action columns are empty
- •Add triage decisions to at least one row
"Invalid resolution: must be SAFE or FIXED"
- •For SECURITY_HOTSPOT with Action=REVIEWED, Resolution must be
SAFEorFIXED - •Check spelling and capitalization
Example Workflow
1. Export security issues
cd sonarcloud-security-audit node scripts/fetch-security-issues.mjs nasa-pds security-audit.csv
2. Review and add triage columns
Open security-audit.csv in Excel/Google Sheets and add 4 columns:
| Action | Resolution | Comment | Reviewer |
|---|---|---|---|
| REVIEWED | SAFE | False positive. Uses URI not URL. | jordan@jpl.nasa.gov |
| wontfix | Low priority. Scheduled for Q2. | jane@jpl.nasa.gov |
Save as security-triage.csv
3. Dry run to preview
cd sonarcloud-security-triage node scripts/apply-triage.mjs ../security-triage.csv --dry-run
4. Apply triage decisions
node scripts/apply-triage.mjs ../security-triage.csv
5. Verify in SonarCloud
- •Open a few updated issues in SonarCloud UI
- •Confirm status changes and comments appear correctly
Output
Console output:
🔧 SonarCloud Security Triage Input file: security-triage.csv Dry run: NO Total rows: 4647 Rows with triage decisions: 127 Processing... [1/127] ✅ Hotspot AZPV1fTprahIrD-njDRb → REVIEWED (SAFE) [2/127] ✅ Issue AZnP1S0b_yFrdYV3Iu6e → wontfix [3/127] ⚠️ Hotspot ABC123 → 404 Not Found (skipped) ... 📊 Summary: ✅ Successfully updated: 125 ❌ Failed: 1 ⏭️ Skipped: 1 Failed updates: - Row 45: Hotspot XYZ789 - 403 Forbidden (insufficient permissions) All done!
Notes
- •Only rows with non-empty Action column are processed
- •Empty Action = no update (useful for keeping already-reviewed items in the CSV)
- •The Reviewer column is for tracking purposes only (not sent to SonarCloud)
- •Comments in SonarCloud will show timestamp and API user (token owner), not the Reviewer value
- •Updates are applied sequentially with small delays to respect rate limits
- •Script is idempotent: running twice won't cause issues (SonarCloud will reject invalid transitions)