SC: Verifier — False Positive Elimination & Confidence Scoring
Purpose
The verifier skill processes all raw findings from Phase 2 vulnerability skills, eliminates false positives through multi-criteria analysis, assigns confidence scores, merges duplicate findings, and produces a curated list of verified security issues. This is the quality gate that ensures the final report contains actionable, high-signal findings.
Activation
Runs in Phase 3 of the pipeline, after all Phase 2 vulnerability skills have completed.
Input
All files matching security-report/*-results.md
Output
File: security-report/verified-findings.md
Verification Process
Step 1: Finding Collection
- Read all
*-results.md files from security-report/
- Parse each finding into a structured format (title, severity, confidence, file, line, type, description)
- Skip files containing "No issues found"
- Create a unified finding list with source skill attribution
Step 2: Reachability Analysis
For each finding, determine if the vulnerable code is actually reachable:
Check if code is in an executable path:
- Is the file imported/included by any other file?
- Is the function called from an entry point (HTTP handler, CLI command, etc.)?
- Is the file part of the build output (not excluded by build config)?
- Trace the call chain from entry point to vulnerable code
Reachability scoring:
- Directly reachable from HTTP handler: +30 confidence
- Reachable through 1-2 function calls: +20 confidence
- Reachable through 3+ function calls: +10 confidence
- No clear call path found: -20 confidence
- Dead code (no imports/calls): -40 confidence
Step 3: Sanitization Check
For each finding involving user input, check if input is sanitized:
Sanitization indicators:
- Input passes through validation library (Zod, Joi, Pydantic, Bean Validation)
- Input is parameterized (prepared statements, ORM methods)
- Input passes through encoding/escaping function (htmlspecialchars, html/template, DOMPurify)
- Input is type-cast to safe type (parseInt, strconv.Atoi)
Sanitization scoring:
- No sanitization found: +0 (no change)
- Partial sanitization (some paths sanitized, others not): -10 confidence
- Full sanitization before reaching sink: -40 confidence
- Framework auto-sanitization active: -30 confidence
Step 4: Framework Protection Check
Check if the framework provides automatic protection against the reported vulnerability:
| Vulnerability |
Framework Protection |
| XSS |
React JSX auto-escaping, Angular sanitization, Django template auto-escaping, Blade {{ }} escaping |
| SQL Injection |
ORM parameterized queries (Prisma, GORM, Hibernate, EF), prepared statement wrappers |
| CSRF |
Django CSRF middleware, Spring Security CSRF, Laravel VerifyCsrfToken, Express csurf |
| SSTI |
Jinja2 sandbox mode, restricted template engines |
| Path Traversal |
Framework static file servers with built-in path validation |
| Header Injection |
Modern HTTP libraries that reject newlines in headers |
Framework protection scoring:
- Framework auto-protection confirmed active: -30 confidence
- Framework protection exists but may be bypassed: -10 confidence
- No framework protection for this vulnerability type: +0
Step 5: Configuration Override Check
Check if configuration-level protections mitigate the finding:
- CSP headers mitigating XSS findings
- CORS strict configuration mitigating cross-origin findings
- WAF rules potentially blocking exploitation
- Network segmentation limiting SSRF impact
- File system permissions limiting path traversal impact
Configuration scoring:
- Strong configuration mitigation: -20 confidence
- Partial configuration mitigation: -10 confidence
- No configuration-level mitigation: +0
Step 6: Context Analysis
Determine the context of the vulnerable code:
Test code:
- File is in
test/, tests/, __tests__/, spec/, *_test.go, *_test.py, *.test.ts
- File name contains
test, spec, mock, fixture
- Finding in test code: -50 confidence (but keep as informational if it demonstrates a pattern)
Dead code:
- Function is never called from any reachable path
- File is not imported anywhere
- Code is commented out
- Finding in dead code: -40 confidence
Example/Documentation code:
- File is in
examples/, docs/, demo/, sample/
- Finding in example code: -50 confidence
Generated code:
- File is in
generated/, gen/, __generated__/
- File has
// Code generated or @Generated annotation
- Finding in generated code: -30 confidence (flag for upstream fix)
Vendor/third-party code:
- File is in
vendor/, node_modules/, third_party/
- Finding in vendored code: -40 confidence (should be covered by sc-dependency-audit)
Step 7: Duplicate Detection & Merging
Identify and merge findings that share the same root cause:
Duplicate criteria:
- Same file + same line number → merge, keep highest severity
- Same vulnerability type + same source variable → merge if same data flow
- Same vulnerability pattern across multiple files → group as one finding with multiple locations
- Findings from different skills about the same code → merge, note both perspectives
Merge rules:
- Keep the highest severity rating
- Keep the highest confidence score
- Combine descriptions from multiple skills
- List all affected files/lines
Step 8: Final Confidence Scoring
Calculate final confidence score for each finding:
Base confidence from the reporting skill: 0-100
Apply modifiers from steps 2-6:
final_confidence = base_confidence
+ reachability_modifier (-40 to +30)
+ sanitization_modifier (-40 to +0)
+ framework_modifier (-30 to +0)
+ configuration_modifier (-20 to +0)
+ context_modifier (-50 to +0)
Clamp to 0-100 range.
Confidence classification:
- 90-100: Confirmed — Directly exploitable, high certainty
- 70-89: High Probability — Very likely vulnerable, minor conditions may apply
- 50-69: Probable — Likely vulnerable, additional manual verification recommended
- 30-49: Possible — May be a false positive, requires manual review
- 0-29: Low Confidence — Likely informational, marked as such in report
Step 9: Severity Recalculation
After confidence scoring, recalculate severity:
- Findings with confidence < 30: downgrade severity to "Info" regardless of original rating
- Findings with confidence 30-49: cap severity at "Medium"
- Findings with confidence 50-69: cap severity at "High"
- Findings with confidence 70+: keep original severity
Output Format
# Verified Security Findings
## Summary
- Total raw findings from Phase 2: {N}
- After duplicate merging: {N}
- After false positive elimination: {N}
- Final verified findings: {N}
## Confidence Distribution
- Confirmed (90-100): {N}
- High Probability (70-89): {N}
- Probable (50-69): {N}
- Possible (30-49): {N}
- Low Confidence (0-29): {N}
## Verified Findings
### VULN-001: {Title}
- **Severity:** Critical | High | Medium | Low | Info
- **Confidence:** {score}/100 ({classification})
- **Original Skill:** {skill-name}
- **Vulnerability Type:** CWE-XXX
- **File:** file/path:line
- **Reachability:** Direct | Indirect | Unknown
- **Sanitization:** None | Partial | Full
- **Framework Protection:** None | Partial | Active
- **Description:** Verified description
- **Verification Notes:** What was checked, why this is/isn't a false positive
- **Remediation:** How to fix
## Eliminated Findings (False Positives)
Brief list of eliminated findings with reason for elimination.
Common False Positive Patterns
- ORM methods flagged as SQL injection — ORMs auto-parameterize; only
raw() or RawSQL methods are risky
- Template auto-escaping not recognized — React JSX, Django
{{ }}, Blade {{ }} auto-escape by default
- Test fixtures flagged as hardcoded secrets — test API keys, mock tokens are expected in test code
- Localhost URLs flagged as SSRF — development URLs (localhost:3000) in config are not exploitable
- Error messages in development config — debug=True in dev config, not in production
- Type-safe languages reducing injection risk — Go's strconv, Rust's type system prevent many injection types
- Environment variable reads flagged as secrets —
os.Getenv("SECRET") reads at runtime, not hardcoded
1---2name: sc-verifier3description: False positive elimination and confidence scoring for all security findings4license: MIT5---67# SC: Verifier — False Positive Elimination & Confidence Scoring89## Purpose1011The verifier skill processes all raw findings from Phase 2 vulnerability skills, eliminates false positives through multi-criteria analysis, assigns confidence scores, merges duplicate findings, and produces a curated list of verified security issues. This is the quality gate that ensures the final report contains actionable, high-signal findings.1213## Activation1415Runs in Phase 3 of the pipeline, after all Phase 2 vulnerability skills have completed.1617## Input1819All files matching `security-report/*-results.md`2021## Output2223File: `security-report/verified-findings.md`2425## Verification Process2627### Step 1: Finding Collection28291. Read all `*-results.md` files from `security-report/`302. Parse each finding into a structured format (title, severity, confidence, file, line, type, description)313. Skip files containing "No issues found"324. Create a unified finding list with source skill attribution3334### Step 2: Reachability Analysis3536For each finding, determine if the vulnerable code is actually reachable:3738**Check if code is in an executable path:**39- Is the file imported/included by any other file?40- Is the function called from an entry point (HTTP handler, CLI command, etc.)?41- Is the file part of the build output (not excluded by build config)?42- Trace the call chain from entry point to vulnerable code4344**Reachability scoring:**45- Directly reachable from HTTP handler: +30 confidence46- Reachable through 1-2 function calls: +20 confidence47- Reachable through 3+ function calls: +10 confidence48- No clear call path found: -20 confidence49- Dead code (no imports/calls): -40 confidence5051### Step 3: Sanitization Check5253For each finding involving user input, check if input is sanitized:5455**Sanitization indicators:**56- Input passes through validation library (Zod, Joi, Pydantic, Bean Validation)57- Input is parameterized (prepared statements, ORM methods)58- Input passes through encoding/escaping function (htmlspecialchars, html/template, DOMPurify)59- Input is type-cast to safe type (parseInt, strconv.Atoi)6061**Sanitization scoring:**62- No sanitization found: +0 (no change)63- Partial sanitization (some paths sanitized, others not): -10 confidence64- Full sanitization before reaching sink: -40 confidence65- Framework auto-sanitization active: -30 confidence6667### Step 4: Framework Protection Check6869Check if the framework provides automatic protection against the reported vulnerability:7071| Vulnerability | Framework Protection |72|--------------|---------------------|73| XSS | React JSX auto-escaping, Angular sanitization, Django template auto-escaping, Blade {{ }} escaping |74| SQL Injection | ORM parameterized queries (Prisma, GORM, Hibernate, EF), prepared statement wrappers |75| CSRF | Django CSRF middleware, Spring Security CSRF, Laravel VerifyCsrfToken, Express csurf |76| SSTI | Jinja2 sandbox mode, restricted template engines |77| Path Traversal | Framework static file servers with built-in path validation |78| Header Injection | Modern HTTP libraries that reject newlines in headers |7980**Framework protection scoring:**81- Framework auto-protection confirmed active: -30 confidence82- Framework protection exists but may be bypassed: -10 confidence83- No framework protection for this vulnerability type: +08485### Step 5: Configuration Override Check8687Check if configuration-level protections mitigate the finding:8889- **CSP headers** mitigating XSS findings90- **CORS strict configuration** mitigating cross-origin findings91- **WAF rules** potentially blocking exploitation92- **Network segmentation** limiting SSRF impact93- **File system permissions** limiting path traversal impact9495**Configuration scoring:**96- Strong configuration mitigation: -20 confidence97- Partial configuration mitigation: -10 confidence98- No configuration-level mitigation: +099100### Step 6: Context Analysis101102Determine the context of the vulnerable code:103104**Test code:**105- File is in `test/`, `tests/`, `__tests__/`, `spec/`, `*_test.go`, `*_test.py`, `*.test.ts`106- File name contains `test`, `spec`, `mock`, `fixture`107- Finding in test code: -50 confidence (but keep as informational if it demonstrates a pattern)108109**Dead code:**110- Function is never called from any reachable path111- File is not imported anywhere112- Code is commented out113- Finding in dead code: -40 confidence114115**Example/Documentation code:**116- File is in `examples/`, `docs/`, `demo/`, `sample/`117- Finding in example code: -50 confidence118119**Generated code:**120- File is in `generated/`, `gen/`, `__generated__/`121- File has `// Code generated` or `@Generated` annotation122- Finding in generated code: -30 confidence (flag for upstream fix)123124**Vendor/third-party code:**125- File is in `vendor/`, `node_modules/`, `third_party/`126- Finding in vendored code: -40 confidence (should be covered by sc-dependency-audit)127128### Step 7: Duplicate Detection & Merging129130Identify and merge findings that share the same root cause:131132**Duplicate criteria:**133- Same file + same line number → merge, keep highest severity134- Same vulnerability type + same source variable → merge if same data flow135- Same vulnerability pattern across multiple files → group as one finding with multiple locations136- Findings from different skills about the same code → merge, note both perspectives137138**Merge rules:**139- Keep the highest severity rating140- Keep the highest confidence score141- Combine descriptions from multiple skills142- List all affected files/lines143144### Step 8: Final Confidence Scoring145146Calculate final confidence score for each finding:147148**Base confidence from the reporting skill:** 0-100149**Apply modifiers from steps 2-6:**150151```152final_confidence = base_confidence153 + reachability_modifier (-40 to +30)154 + sanitization_modifier (-40 to +0)155 + framework_modifier (-30 to +0)156 + configuration_modifier (-20 to +0)157 + context_modifier (-50 to +0)158```159160**Clamp to 0-100 range.**161162**Confidence classification:**163- 90-100: **Confirmed** — Directly exploitable, high certainty164- 70-89: **High Probability** — Very likely vulnerable, minor conditions may apply165- 50-69: **Probable** — Likely vulnerable, additional manual verification recommended166- 30-49: **Possible** — May be a false positive, requires manual review167- 0-29: **Low Confidence** — Likely informational, marked as such in report168169### Step 9: Severity Recalculation170171After confidence scoring, recalculate severity:172173- Findings with confidence < 30: downgrade severity to "Info" regardless of original rating174- Findings with confidence 30-49: cap severity at "Medium"175- Findings with confidence 50-69: cap severity at "High"176- Findings with confidence 70+: keep original severity177178## Output Format179180```markdown181# Verified Security Findings182183## Summary184- Total raw findings from Phase 2: {N}185- After duplicate merging: {N}186- After false positive elimination: {N}187- Final verified findings: {N}188189## Confidence Distribution190- Confirmed (90-100): {N}191- High Probability (70-89): {N}192- Probable (50-69): {N}193- Possible (30-49): {N}194- Low Confidence (0-29): {N}195196## Verified Findings197198### VULN-001: {Title}199- **Severity:** Critical | High | Medium | Low | Info200- **Confidence:** {score}/100 ({classification})201- **Original Skill:** {skill-name}202- **Vulnerability Type:** CWE-XXX203- **File:** file/path:line204- **Reachability:** Direct | Indirect | Unknown205- **Sanitization:** None | Partial | Full206- **Framework Protection:** None | Partial | Active207- **Description:** Verified description208- **Verification Notes:** What was checked, why this is/isn't a false positive209- **Remediation:** How to fix210211## Eliminated Findings (False Positives)212Brief list of eliminated findings with reason for elimination.213```214215## Common False Positive Patterns2162171. **ORM methods flagged as SQL injection** — ORMs auto-parameterize; only `raw()` or `RawSQL` methods are risky2182. **Template auto-escaping not recognized** — React JSX, Django `{{ }}`, Blade `{{ }}` auto-escape by default2193. **Test fixtures flagged as hardcoded secrets** — test API keys, mock tokens are expected in test code2204. **Localhost URLs flagged as SSRF** — development URLs (localhost:3000) in config are not exploitable2215. **Error messages in development config** — debug=True in dev config, not in production2226. **Type-safe languages reducing injection risk** — Go's strconv, Rust's type system prevent many injection types2237. **Environment variable reads flagged as secrets** — `os.Getenv("SECRET")` reads at runtime, not hardcoded