Security Triage Skill
Purpose
This skill enables Claude Code to perform AI-powered vulnerability triage on security findings. You will classify findings, score risk, generate explanations, and cluster related vulnerabilities.
When to Use This Skill
- •When invoked by
/flow:security_triagecommand - •When user asks to triage security findings
- •When analyzing scanner output (Semgrep, CodeQL, etc.)
Input
Read security findings from one of:
- •
docs/security/findings.json- Unified Finding Format (UFFormat) - •Path specified by user
- •SARIF format files (auto-detect)
Expected format:
[
{
"id": "SEMGREP-CWE-89-001",
"scanner": "semgrep",
"severity": "high",
"title": "SQL Injection in login query",
"description": "User input concatenated into SQL query",
"location": {
"file": "src/auth.py",
"line_start": 42,
"line_end": 45,
"code_snippet": "query = f\"SELECT * FROM users WHERE username = '{username}'\""
},
"cwe_id": "CWE-89",
"cvss_score": 8.5
}
]
Classification Process
For EACH finding, perform these steps:
Step 1: Read Code Context
Read the file to get 5 lines before and after the finding:
# Read context file_path = finding["location"]["file"] line_start = max(1, finding["location"]["line_start"] - 5) line_end = finding["location"]["line_end"] + 5
Step 2: Classify as TP/FP/NI
Apply classification rules from memory/security/triage-guidelines.md:
TRUE_POSITIVE (TP) when:
- •User input flows to dangerous sink without sanitization
- •No security controls present (parameterized queries, escaping)
- •Attack scenario is feasible
FALSE_POSITIVE (FP) when:
- •Proper validation/sanitization exists
- •Framework provides automatic protection
- •Context makes exploitation impossible
NEEDS_INVESTIGATION (NI) when:
- •Insufficient context (confidence <0.7)
- •Complex dataflow requiring human analysis
Step 3: Assign Confidence Score
- •0.9-1.0: Clear vulnerability pattern, no mitigations → TP
- •0.7-0.89: Vulnerability likely, some ambiguity → TP with lower confidence
- •<0.7: Insufficient confidence → mark as NEEDS_INVESTIGATION (NI)
Step 4: Calculate Risk Score
Use the risk scoring formula: risk_score = (impact × exploitability) / detection_time
Impact (0-10)
Map from severity or CWE (see memory/security/cwe-knowledge.md):
- •CRITICAL: 9-10 (RCE, auth bypass)
- •HIGH: 7-8 (SQL injection, XSS)
- •MEDIUM: 4-6 (info disclosure)
- •LOW: 1-3 (minor issues)
Exploitability (0-10)
Assess based on:
- •Public exploit available: 10
- •Easy to exploit: 8-9
- •Requires auth: 6-7
- •Very difficult: 1-3
Detection Time (days)
Use git blame to find when code was written:
import subprocess
result = subprocess.run(
["git", "blame", f"-L{line_start},{line_end}", "--porcelain", file_path],
capture_output=True, text=True, check=True
)
committer_time = next(
(line for line in result.stdout.splitlines() if line.startswith("committer-time")),
None
)
# If needed, extract the timestamp from committer_time
Calculate days since commit. If git unavailable, default to 30 days.
Step 5: Generate Plain-English Explanation
Create 4-part explanation (see memory/security/triage-guidelines.md):
What (1 sentence):
- •Clear description for junior developers
- •Avoid jargon
Why It Matters (2-3 sentences):
- •Real-world security impact
- •Concrete consequences
How to Exploit (TP only, 2-3 sentences):
- •Specific attack scenario
- •Example payload
How to Fix (2-4 sentences):
- •Concrete fix with code example
- •Link to documentation
Keep each section under 200 characters.
Step 6: Determine Reasoning
Explain your classification decision in 1-2 sentences for debugging/feedback.
Example: "Classified as TP because user input from request.POST flows directly to cursor.execute() with string formatting, and no parameterized queries are used."
Clustering Process
After classifying ALL findings:
CWE Clustering
Group findings by CWE when:
- •3+ findings share the same CWE
- •Cluster ID:
CLUSTER-CWE-{cwe_id}
Example: 5 findings with CWE-89 → CLUSTER-CWE-CWE-89
File Clustering
Group findings by file when:
- •2+ findings in same file (different CWEs or unclustered by CWE)
- •Cluster ID:
CLUSTER-FILE-{filename_stem}
Example: 3 findings in auth.py → CLUSTER-FILE-auth
Pattern Clustering (Optional)
If you identify architectural patterns across multiple files:
- •Create
CLUSTER-PATTERN-{pattern_name} - •Examples: "CLUSTER-PATTERN-missing-input-validation", "CLUSTER-PATTERN-insecure-deserialization"
Output Format
Write triage results to docs/security/triage-results.json:
[
{
"finding_id": "SEMGREP-CWE-89-001",
"classification": "TP",
"confidence": 0.95,
"risk_score": 4.2,
"explanation": {
"what": "SQL query built with string concatenation of user input",
"why_it_matters": "Attacker can inject SQL commands to steal passwords, delete data, or bypass authentication",
"how_to_exploit": "Enter ' OR 1=1 -- in username field to bypass login",
"how_to_fix": "Use parameterized query: cursor.execute('SELECT * FROM users WHERE username = ?', (username,))"
},
"cluster_id": "CLUSTER-CWE-CWE-89",
"cluster_type": "cwe",
"ai_reasoning": "User input from request.POST flows to cursor.execute with f-string. No parameterized queries used.",
"metadata": {
"impact": 8,
"exploitability": 9,
"detection_time": 17
}
}
]
Sort results by risk_score descending (highest risk first).
Interactive Mode (Optional)
If user requests interactive confirmation:
- •
Display each finding with:
- •File:line location (syntax highlighted)
- •AI classification + confidence
- •AI reasoning
- •
Prompt: "Accept (y), Override (o), Skip (s), More info (?)"
- •
If override:
- •Ask for correct classification (TP/FP/NI)
- •Ask for reason
- •Save feedback to
~/.specify/triage_feedback.jsonl
- •
Update classification based on user input
Knowledge Base
Reference the following files for CWE patterns and triage guidelines:
- •
memory/security/triage-guidelines.md- Classification rules and explanation format - •
memory/security/cwe-knowledge.md- CWE patterns and remediation guidance
Success Criteria
- •Classify all findings as TP/FP/NI with confidence scores
- •Calculate risk scores using risk scoring formula
- •Generate clear, actionable explanations
- •Cluster related findings (3+ per cluster)
- •Sort by risk score (highest first)
- •Target >85% accuracy on benchmark dataset
Example Workflow
# User runs triage command /flow:security_triage # You execute: 1. Read docs/security/findings.json 2. For each finding: - Read code context from file - Classify using triage guidelines - Calculate risk score using formula - Generate explanation 3. Cluster findings by CWE/file/pattern 4. Sort by risk_score descending 5. Write to docs/security/triage-results.json 6. Report summary to user
Expert Personas
For enhanced analysis, you can invoke specialized security personas:
- •@security-analyst - Deep OWASP/CVSS expertise, compliance mapping, business impact assessment
- •@patch-engineer - Security fix validation, code quality review, testing strategies
- •@fuzzing-strategist - Dynamic testing guidance, fuzzing strategy development
- •@exploit-researcher - Attack scenarios, exploitability assessment, vulnerability chaining
Use personas for:
- •Critical/high severity findings (detailed analysis)
- •Complex vulnerabilities requiring specialized knowledge
- •Executive reporting (business impact narratives)
- •Fix validation and quality assurance
Notes
- •This is a SKILL, not Python code. You (Claude Code) execute the logic natively.
- •No LLM API calls from Python. All AI reasoning happens in this skill.
- •Python code only handles data structures (Finding, TriageResult) and file I/O.
- •Use Read tool to view code context, Bash tool for git blame.
- •Apply domain knowledge from memory/security/ guidelines.
- •For specialized analysis, invoke expert personas (@security-analyst, @patch-engineer, @fuzzing-strategist, @exploit-researcher)