Skip to content

fix(security): parse CVSS v2 fallback and return UNKNOWfix(security): parse CVSS v2 fallback and return UNKNOWN for unscored CVEs #385N for unscored…#450

Open
Shubham-Gotawade wants to merge 2 commits into
agnivo988:mainfrom
Shubham-Gotawade:fix-security-severity-385
Open

fix(security): parse CVSS v2 fallback and return UNKNOWfix(security): parse CVSS v2 fallback and return UNKNOWN for unscored CVEs #385N for unscored…#450
Shubham-Gotawade wants to merge 2 commits into
agnivo988:mainfrom
Shubham-Gotawade:fix-security-severity-385

Conversation

@Shubham-Gotawade

@Shubham-Gotawade Shubham-Gotawade commented Jun 4, 2026

Copy link
Copy Markdown

Contributed as part of GSSoC '26.

Problem Description

The getSeverity function in internal/analyzer/security_scan.go previously defaulted any un-scored, text-only, or older vulnerability records that lacked a CVSS v3 metrics block directly to "MEDIUM". This silently downgraded older historical CVEs that might be highly critical (9.0+ base score under CVSS v2), leading to skewed risk calculations and potentially exposing users to unmitigated supply chain risks. Fixes #385.

Solution Implemented

  1. CVSS v2 Parsing Fallback: Enhanced getSeverity to scan for "CVSS_V2" data schemas when "CVSS_V3" fields are unavailable. If both are found, CVSS v3 dynamically retains precedence.
  2. Accurate Threshold Mapping: Standardized the base metric evaluation using official NVD ranges:
    • 9.0 - 10.0"CRITICAL"
    • 7.0 - 8.9"HIGH"
    • 4.0 - 6.9"MEDIUM"
    • 0.1 - 3.9"LOW"
  3. Explicit Unknown Metric Handling: Replaced the unsafe hardcoded "MEDIUM" fallback with "UNKNOWN" for unscored, un-vectored, or text-only entries.
  4. Emoji Mapping Extensions: Expanded GetSeverityEmoji to dynamically include support for the new "UNKNOWN" severity string utilizing a clean, neutral identifier.
  5. Robust Test Engineering: Designed a complete tabular test suite inside internal/analyzer/security_scan_test.go checking all conditions: CVSS v3 parsing, CVSS v2 fallback matching, proper precedence prioritization, and correct resolution of text-only records to "UNKNOWN".

Verification & Test Logs

Validated locally via Go's testing toolchain with zero failures:

  • TestGetSeverity/CVSS_V3_High: PASSED
  • TestGetSeverity/CVSS_V2_Fallback_High: PASSED
  • TestGetSeverity/CVSS_V2_Fallback_Medium: PASSED
  • TestGetSeverity/CVSS_V2_Fallback_Low: PASSED
  • TestGetSeverity/CVSS_V3_Precedence_over_V2: PASSED
  • TestGetSeverity/No_Score_Unknown: PASSED
  • TestGetSeverity/Text-only_record_Unknown: PASSED

Summary by CodeRabbit

  • Bug Fixes
    • Improved vulnerability severity assessment by properly using CVSS v3 scores and falling back to CVSS v2 when necessary.
    • Fixed incorrect default severity assignment for vulnerabilities with missing CVSS score data; now properly reports "UNKNOWN".

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Fixes getSeverity to parse CVSS v2 and v3 scores with correct precedence, returning "UNKNOWN" instead of "MEDIUM" when no CVSS data exists, preventing critical CVEs from being misclassified as medium. Adds emoji mapping for UNKNOWN severity and comprehensive test coverage.

Changes

CVSS Score Severity Parsing

Layer / File(s) Summary
CVSS score parsing and severity resolution
internal/analyzer/security_scan.go
Refactored getSeverity to independently parse CVSS v3 and v2 base scores with v3 given precedence when both exist, apply the same threshold-based severity mapping for both versions, and return "UNKNOWN" instead of "MEDIUM" when no recognized CVSS data is present. Extended GetSeverityEmoji to handle the "UNKNOWN" severity value explicitly.
Severity parsing test coverage
internal/analyzer/security_scan_test.go
Table-driven test validates getSeverity across CVSS v3 only, v2 fallback, v3 precedence when both exist, and unknown/missing severity scenarios, each asserting correct normalization to CRITICAL, HIGH, MEDIUM, LOW, or UNKNOWN.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • agnivo988/Repo-lyzer#81: Earlier PR that introduced the main severity scanning feature, directly complemented by this PR's v2/v3 precedence and UNKNOWN handling.
  • agnivo988/Repo-lyzer#312: Related severity parsing improvements in the same getSeverity CVSS score handling logic.

Suggested labels

type:bug, level:critical, quality:clean

Poem

🐰 A CVSS score or two, we now parse through,
No more hiding CRITICAL as MEDIUM's hue,
With v3 and v2 in proper array,
Unknown stays unknown—no defaults astray! 🔐

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is partially related to the changeset but contains truncated/duplicated text ('UNKNOWfix' instead of 'UNKNOWN') that obscures the main point, making it unclear despite addressing the actual changes. Revise the title to be concise and error-free: 'fix(security): parse CVSS v2 fallback and return UNKNOWN for unscored CVEs #385' without duplication.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed All coding requirements from issue #385 are met: CVSS v2 fallback parsing implemented, threshold mapping to severity bands applied, UNKNOWN fallback added instead of hardcoded MEDIUM, and test suite covers all scenarios.
Out of Scope Changes check ✅ Passed All changes are directly scoped to requirements in issue #385: getSeverity refactoring, severity emoji mapping for UNKNOWN, and corresponding test suite with no extraneous modifications.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
internal/analyzer/security_scan_test.go (1)

5-116: ⚡ Quick win

Add regression cases for invalid-v3-with-valid-v2 and explicit 0.0 (NONE).

Current table misses the key edge where CVSS_V3 exists but is unparsable while CVSS_V2 is valid, plus the explicit zero-score path. Adding both would lock in the intended precedence/fallback contract.

Suggested additional test rows
 		{
+			name: "Invalid CVSS V3 falls back to valid V2",
+			vuln: osvVuln{
+				Severity: []struct {
+					Type  string `json:"type"`
+					Score string `json:"score"`
+				}{
+					{Type: "CVSS_V3", Score: "not-a-score"},
+					{Type: "CVSS_V2", Score: "9.0"},
+				},
+			},
+			expected: "CRITICAL",
+		},
+		{
+			name: "Explicit zero score maps to NONE",
+			vuln: osvVuln{
+				Severity: []struct {
+					Type  string `json:"type"`
+					Score string `json:"score"`
+				}{
+					{Type: "CVSS_V3", Score: "0.0"},
+				},
+			},
+			expected: "NONE",
+		},
+		{
 			name: "No Score Unknown",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/analyzer/security_scan_test.go` around lines 5 - 116, Add two
regression test cases to TestGetSeverity in security_scan_test.go: one named
"Invalid V3 with Valid V2" where osvVuln.Severity contains a CVSS_V3 entry with
an unparsable Score (e.g., non-numeric) and a valid CVSS_V2 entry (numeric) to
verify getSeverity() falls back to CVSS_V2; and one named "Explicit 0.0 NONE"
where osvVuln.Severity contains a CVSS_V3 entry with Score "0.0" (or equivalent)
to verify getSeverity() returns "NONE" for zero score; place them alongside the
other test cases so they exercise getSeverity(osvVuln) behavior and assert the
expected strings.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/analyzer/security_scan.go`:
- Around line 171-186: The current logic marks hasV3 purely by seeing s.Type ==
"CVSS_V3" so a malformed/unparseable v3 score (parseCvssScore failing) still
forces the v3 branch and blocks v2 fallback; change the flow in the loop that
handles s.Type to set hasV3/hasV2 only when parseCvssScore actually returns a
valid score (e.g., non-empty/non-NaN/parse-success) and assign v3Score/v2Score
accordingly, and update the selection logic before returning so it prefers v3
only if v3Score was successfully parsed, otherwise falls back to a valid v2Score
(and only return "UNKNOWN" if neither parsed successfully).

---

Nitpick comments:
In `@internal/analyzer/security_scan_test.go`:
- Around line 5-116: Add two regression test cases to TestGetSeverity in
security_scan_test.go: one named "Invalid V3 with Valid V2" where
osvVuln.Severity contains a CVSS_V3 entry with an unparsable Score (e.g.,
non-numeric) and a valid CVSS_V2 entry (numeric) to verify getSeverity() falls
back to CVSS_V2; and one named "Explicit 0.0 NONE" where osvVuln.Severity
contains a CVSS_V3 entry with Score "0.0" (or equivalent) to verify
getSeverity() returns "NONE" for zero score; place them alongside the other test
cases so they exercise getSeverity(osvVuln) behavior and assert the expected
strings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 29cf3d75-1b8f-43d9-a338-01a559f5f6ed

📥 Commits

Reviewing files that changed from the base of the PR and between 6d6fa89 and 8b0d2d5.

📒 Files selected for processing (2)
  • internal/analyzer/security_scan.go
  • internal/analyzer/security_scan_test.go

Comment thread internal/analyzer/security_scan.go
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug][Security] getSeverity defaults unknown CVEs to MEDIUM, silently hiding CRITICAL severity findings

1 participant