Run GHAS, Snyk, and SonarQube security scans in parallel on the current branch and report findings on the CLI
How this command is triggered — by the user, by Claude, or both
Slash command
/security-coding-agent:security-reviewThe summary Claude sees in its command listing — used to decide when to auto-load this command
# /security-review ## Prerequisites Before running, verify the required environment: - `gh` CLI authenticated (for GHAS) - `SNYK_TOKEN` environment variable set (for Snyk) - `SONARQUBE_TOKEN` and `SONARQUBE_URL` environment variables set (for SonarQube) - `SONARQUBE_PROJECT_KEY` environment variable or `sonar-project.properties` present ## Steps ### Phase 1 — Setup 1. **Load configuration** via `config-resolver` skill. 2. **Detect current branch and repo:** 3. **Detect active scanners** via `scanner-detection` skill. Skip any scanner whose markers are absent; warn the user but con...
Before running, verify the required environment:
gh CLI authenticated (for GHAS)SNYK_TOKEN environment variable set (for Snyk)SONARQUBE_TOKEN and SONARQUBE_URL environment variables set (for SonarQube)SONARQUBE_PROJECT_KEY environment variable or sonar-project.properties presentconfig-resolver skill.BRANCH=$(git rev-parse --abbrev-ref HEAD)
REPO=$(gh repo view --json nameWithOwner --jq '.nameWithOwner')
scanner-detection skill. Skip any scanner whose markers are absent; warn the user but continue with whatever scanners are available.Launch three Agent sub-agents concurrently (one per scanner). Each agent runs independently on the current branch and writes its results to a temp JSON file. Use the Agent tool with all three calls in a single message so they execute in parallel.
Fetch all open alerts from GitHub Advanced Security using the gh CLI:
# Code scanning alerts (CodeQL SAST)
gh api repos/${REPO}/code-scanning/alerts \
--jq '[.[] | select(.state=="open") | {
id: ("GHAS-CS-" + (.number | tostring)),
scanner: "ghas",
feature: "code-scanning",
rule_id: .rule.id,
cwe: (.rule.tags // [] | map(select(startswith("cwe"))) | first // "unknown"),
severity: .rule.security_severity_level,
file: .most_recent_instance.location.path,
line: .most_recent_instance.location.start_line,
description: .rule.description,
remediation: .rule.help
}]' > "$TMPDIR/ghas-cs.json"
# Secret scanning alerts
gh api repos/${REPO}/secret-scanning/alerts \
--jq '[.[] | select(.state=="open") | {
id: ("GHAS-SS-" + (.number | tostring)),
scanner: "ghas",
feature: "secret-scanning",
rule_id: .secret_type,
cwe: "cwe-798",
severity: "critical",
file: .locations_url,
line: 0,
description: ("Exposed secret: " + .secret_type),
remediation: "Revoke and rotate the secret immediately"
}]' > "$TMPDIR/ghas-ss.json"
# Dependabot alerts
gh api repos/${REPO}/dependabot/alerts \
--jq '[.[] | select(.state=="open") | {
id: ("GHAS-DA-" + (.number | tostring)),
scanner: "ghas",
feature: "dependabot",
rule_id: (.security_advisory.cve_id // .security_advisory.ghsa_id),
cwe: ((.security_advisory.cwes // [{}])[0].cwe_id // "unknown"),
severity: .security_vulnerability.severity,
file: (.dependency.manifest_path // "unknown"),
line: 0,
description: .security_advisory.summary,
remediation: ("Upgrade " + .security_vulnerability.package.name + " to " + (.security_vulnerability.first_patched_version.identifier // "latest"))
}]' > "$TMPDIR/ghas-da.json"
Merge the three files into $TMPDIR/ghas-results.json. If any gh api call returns a 404 or error (feature not enabled), log the warning and continue with the other GHAS features.
Run all applicable Snyk products against the working directory:
# SAST — application code
snyk code test --json > "$TMPDIR/snyk-code.json" 2>&1 || true
# SCA — open-source dependencies
snyk test --json > "$TMPDIR/snyk-oss.json" 2>&1 || true
# IaC — only if .tf, cloudformation, or k8s files exist
if compgen -G "*.tf" > /dev/null || compgen -G "**/*.tf" > /dev/null; then
snyk iac test . --json > "$TMPDIR/snyk-iac.json" 2>&1 || true
fi
# Container — only if Dockerfile exists
if [ -f Dockerfile ]; then
IMAGE_NAME=$(basename "$(pwd)")
snyk container test "$IMAGE_NAME" --json > "$TMPDIR/snyk-container.json" 2>&1 || true
fi
Normalize each Snyk output to the common finding schema (id, scanner: "snyk", feature, rule_id, cwe, severity, file, line, description, remediation). Merge into $TMPDIR/snyk-results.json.
snyk code test (SARIF-style output): iterate runs[].results[], map ruleId → rule_id, locations[0].physicalLocation → file + line, level → severity (error=critical, warning=high, note=medium).snyk test / snyk iac test / snyk container test: iterate vulnerabilities[], map id → rule_id, severity directly, fixedIn[0] → remediation.Trigger a SonarQube analysis on the current branch and fetch results:
# Resolve project key
PROJECT_KEY="${SONARQUBE_PROJECT_KEY:-$(grep 'sonar.projectKey' sonar-project.properties 2>/dev/null | cut -d= -f2)}"
# Trigger analysis (if sonar-scanner is available)
if command -v sonar-scanner &> /dev/null; then
sonar-scanner \
-Dsonar.host.url="$SONARQUBE_URL" \
-Dsonar.token="$SONARQUBE_TOKEN" \
-Dsonar.branch.name="$BRANCH" \
-Dsonar.qualitygate.wait=true 2>&1 || true
fi
# Fetch quality gate status
curl -s -H "Authorization: Bearer $SONARQUBE_TOKEN" \
"$SONARQUBE_URL/api/qualitygates/project_status?projectKey=$PROJECT_KEY" \
> "$TMPDIR/sonar-gate.json"
# Fetch all open vulnerabilities and security hotspots (paginated)
PAGE=1
echo '[]' > "$TMPDIR/sonar-issues.json"
while true; do
RESPONSE=$(curl -s -H "Authorization: Bearer $SONARQUBE_TOKEN" \
"$SONARQUBE_URL/api/issues/search?componentKeys=$PROJECT_KEY&branch=$BRANCH&statuses=OPEN,CONFIRMED&types=VULNERABILITY,SECURITY_HOTSPOT&ps=500&p=$PAGE")
# Append issues
echo "$RESPONSE" | jq '.issues' > "$TMPDIR/sonar-page-$PAGE.json"
jq -s '.[0] + .[1]' "$TMPDIR/sonar-issues.json" "$TMPDIR/sonar-page-$PAGE.json" > "$TMPDIR/sonar-issues-tmp.json"
mv "$TMPDIR/sonar-issues-tmp.json" "$TMPDIR/sonar-issues.json"
TOTAL=$(echo "$RESPONSE" | jq '.paging.total')
FETCHED=$((PAGE * 500))
[ "$FETCHED" -ge "$TOTAL" ] && break
PAGE=$((PAGE + 1))
done
Normalize each SonarQube issue to the common finding schema: map rule → rule_id, severity (BLOCKER/CRITICAL→critical, MAJOR→high, MINOR→medium, INFO→low), component → file, line → line, message → description. Write to $TMPDIR/sonar-results.json.
After all three agents complete, apply the triage-and-prioritize skill:
ghas-results.json + snyk-results.json + sonar-results.json into a single findings list.file:line + same CWE → merge, keep highest severitygovernance.autoFixPolicy, autoFixDenyList, autoFixConfidenceThreshold).Print the full report directly to the CLI. Do not write a file unless the user asks.
┌─────────────────────────────────────────────────────────────────┐
│ Security Review — <REPO> @ <BRANCH> │
│ Date: <YYYY-MM-DD HH:MM> │
├──────────┬──────────┬──────────┬──────────┬──────────┬──────────┤
│ Scanner │ Critical │ High │ Medium │ Low │ Total │
├──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
│ GHAS │ <n> │ <n> │ <n> │ <n> │ <n> │
│ Snyk │ <n> │ <n> │ <n> │ <n> │ <n> │
│ SonarQube│ <n> │ <n> │ <n> │ <n> │ <n> │
├──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
│ Unique │ <n> │ <n> │ <n> │ <n> │ <n> │
│ (deduped)│ │ │ │ │ │
└──────────┴──────────┴──────────┴──────────┴──────────┴──────────┘
Quality gate: <PASSED | FAILED> (SonarQube)
Duplicates removed: <n>
For each priority level (P0 first, then P1, P2, P3), list every finding:
## P0 — Critical (<n> findings, action: auto-fix)
1. [GHAS-CS-42] SQL Injection — src/api/users.js:87
Rule: java:S2077 | CWE-89 | CVSS 9.8
→ Remediation: Use parameterized queries instead of string concatenation
2. [GHAS-SS-3] Exposed AWS Secret Key — .env:12
Rule: aws-access-key | CWE-798 | CVSS 10.0
→ Remediation: Revoke and rotate the secret immediately
## P1 — High (<n> findings, action: auto-fix)
...
## P2 — Medium (<n> findings, action: jira-ticket)
...
## P3 — Low (<n> findings, action: jira-ticket)
...
If a scanner was skipped (not detected or missing credentials), print a warning line:
⚠ Wiz: skipped (no markers detected)
⚠ Snyk Container: skipped (no Dockerfile found)
After printing the report, ask:
Fix P0/P1 findings now? (yes / no / select)
- yes → auto-remediate all P0 and P1 findings using the appropriate scanner remediation skills (
ghas-codeql-remediation,snyk-code-remediation,sonarqube-remediation)- select → present a numbered checklist of P0/P1 findings and let the user pick which to fix
- no → optionally write findings to
docs/security/findings-<date>.mdif user wants a persistent record
npx claudepluginhub gagandeepp/software-agent-teams --plugin security-coding-agent/security-reviewRuns a CWE-based security review with optional STRIDE threat modeling, dependency scanning, and report generation. Supports --auto, --quick, --cwe, --stride, --deps, and --report flags.
/security-reviewScans the codebase for security vulnerabilities (hardcoded secrets, SQL injection, XSS, auth issues) and generates a prioritized markdown improvement plan. Supports scoping to a path, current/recent changes, or a specific PR.
/security-reviewPerforms STRIDE-based security review on code paths or instructions with optional framework, generating Mermaid threat diagrams and assessment reports.