Dependabot fires an alert. It says Critical 9.8. The developer drops everything, merges the patch PR, and marks it done — without reading the advisory, without checking whether the vulnerable package is even reachable in their deployment, without asking whether an exploit exists in the wild. The fire drill takes two hours and disrupts the sprint. Or the opposite happens: after the fifteenth Critical alert this month, the developer dismisses it without reading, and a genuinely exploitable vulnerability sits open in a public-facing API for six weeks. Both failures trace back to the same root cause — treating a CVSS score as a verdict rather than a starting point.
The score is not a triage decision. It's a standardized severity estimate calculated against an imaginary worst-case deployment. The number tells you how bad the vulnerability could be in ideal attack conditions. It says nothing about your infrastructure, your network topology, your authentication requirements, or whether a working exploit even exists. Once you understand how the score is constructed, you stop panic-patching on every 9.8 and stop dismissing alerts because you're fatigued. You read the vector string, check your context, and make a call in two minutes instead of two hours.
What CVSS Actually Is
CVSS — the Common Vulnerability Scoring System — is a framework maintained by FIRST (Forum of Incident Response and Security Teams) for communicating the characteristics and severity of software vulnerabilities in a standardized, vendor-neutral way. The current version you'll encounter in practice is CVSS v3.1. A v4.0 spec exists, but the GitHub Advisory Database, NVD (National Vulnerability Database), and most security tooling including Dependabot still report v3.1 scores. That's what this post covers.
CVSS defines three metric groups:
- Base Metrics — the intrinsic characteristics of the vulnerability: how it's exploited, what it affects, and how severely. This is the number your tooling shows you. It's static — it doesn't change based on time, patches, or your environment.
- Temporal Metrics — how the threat landscape has evolved since disclosure: whether exploit code exists publicly, whether a patch or workaround is available. These change over time and can be applied on top of the Base Score to get a more current picture.
- Environmental Metrics — your organization's specific context: whether the affected component is internet-facing, how much you actually care about confidentiality of that data, what compensating controls you have in place.
The Base Score answers: "How bad could this be in the worst possible context?" It does not answer: "How bad is this for my application?"
Most tools show only the Base Score because it's universal — it requires no knowledge of your environment. Environmental and Temporal scores require input your tooling doesn't have. That makes the Base Score useful for comparison across vulnerabilities and useless as a standalone triage signal. It's the beginning of the analysis, not the end.
Decoding the Vector String
Every CVSS score is accompanied by a vector string — a compact, human-readable encoding of all the metrics that produced the score. If you only take one thing from this post, take this: the vector string is where the real information lives. The number is a summary. The string is the data.
Here's a real-world example of a Critical score:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
Score: 10.0 Critical. This is the ceiling — every metric is at its worst. Here's what each component means:
| Metric | Code | Value | Meaning |
|---|---|---|---|
| Attack Vector | AV | N (Network) | Exploitable remotely over the network |
| Attack Complexity | AC | L (Low) | No special conditions required |
| Privileges Required | PR | N (None) | Attacker needs no authentication |
| User Interaction | UI | N (None) | No user action needed |
| Scope | S | C (Changed) | Exploit crosses security boundaries |
| Confidentiality | C | H (High) | Complete data disclosure possible |
| Integrity | I | H (High) | Complete data modification possible |
| Availability | A | H (High) | Complete service disruption possible |
Every metric at its most severe produces a 10.0. Now look at what happens when three metrics shift:
CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:U/C:H/I:H/A:H
The Confidentiality, Integrity, and Availability impacts are identical. The potential damage ceiling is the same. But AC:H means the attacker needs specific, non-default conditions to land the exploit — a race condition, a particular configuration, a timing window. PR:H means they need admin-level credentials on the target system first. UI:R means a legitimate user has to take an action — click a link, open a file, trigger a specific code path.
The score on that second vector drops to 6.4 Medium, despite the same damage potential. That drop reflects how much harder the exploitation chain is in practice. The gap between a 10.0 and a 6.6 isn't about how bad the impact is — it's about how accessible the attack path is.
The two metrics that do the most work in changing real-world exploitability are AC (does this require unusual conditions?) and PR (does the attacker need existing access?). Learn to read those two first.
Why the Base Score Lies About Your Risk
This is the most important section. The Base Score is calculated against a theoretical target with no defenses and maximum exposure. Your deployment is not that target. The delta between the two is where triage happens.
Example A: The Network-Reachable API
A Critical 9.8 in an npm package used by your public-facing REST API. The vector shows AV:N/AC:L/PR:N — network exploitable, no special conditions, no authentication required. Your API is reachable from the internet. The package handles request parsing and runs on every inbound request. The Base Score is accurate here: this is a genuine fire drill. Patch immediately. The theoretical worst case and your actual case are close to the same thing.
Example B: The Same CVE in a Build Tool
The exact same CVE — same package, same vector string — but this time the package only runs during your local npm run build step or inside a CI job with no external network exposure. AV:N in the vector means "network" is the attack vector under ideal conditions. If the package never processes data from an untrusted network source and the machine running it isn't exposed to one, that attack vector doesn't apply to your deployment. The 9.8 is still on the advisory. Your actual risk is dramatically lower. This belongs in the next sprint, not in an emergency change window tonight.
Example C: The "Critical" Without a Public Exploit
A 9.8 Base Score with no known public proof-of-concept. The Temporal metric Exploit Code Maturity would show E:U (Unproven) if applied — but most tooling doesn't apply Temporal metrics, so you only see the Base Score. Check the advisory References section manually. If no PoC exists, the window of realistic exploitation is much narrower. This doesn't mean "ignore it" — it means "don't drop everything at 5pm on a Friday to merge an untested patch."
The Environmental Score: The Fix Nobody Uses
CVSS provides Environmental Metrics precisely for this problem. Your organization can configure values for Modified Attack Vector, Modified Confidentiality, and others to reflect the actual deployment context, producing an adjusted score that accurately represents your exposure. An AV:N vulnerability running inside a network segment with no external access can have its Attack Vector modified to AV:L in the environmental calculation, producing a score that reflects reality.
Almost no teams do this because tooling support is inconsistent and the process isn't automated. Understanding that it exists changes how you read advisories — you know you can mentally apply the same logic even when the tool doesn't do it for you.
Reading a Real GitHub Advisory
When a Dependabot alert fires, the advisory it links to contains more useful information than the score. Here's how to read it efficiently.
The CVSS section shows the full vector string, not just the number. Click through to it. The vector string is the data; the number is just a summary. Read the metrics directly rather than trying to reverse-engineer them from the score.
The Weaknesses field (CWE) tells you the type of vulnerability — CWE-79 (Cross-Site Scripting), CWE-89 (SQL Injection), CWE-400 (Uncontrolled Resource Consumption). CVSS tells you how severe; CWE tells you what it actually is. This matters for assessing whether your code actually exercises the vulnerable path. A CWE-79 in a server-side rendering library matters a lot if you're rendering user-supplied content and nothing if you're using the library in a static site generator that never processes external input.
Affected versions and Patched versions are more immediately useful than the score for deciding urgency. If a patched version exists, the question becomes "how hard is this upgrade?" — often the answer is "trivially easy," and you should just do it regardless of score. If no patched version exists, you need mitigations and monitoring, and that's true whether the score is 4.0 or 9.8.
The References section is where exploit signal lives. Look for links to GitHub repositories, exploit-db entries, proof-of-concept write-ups, or Metasploit modules. A published PoC changes the urgency calculation immediately — regardless of Base Score, the barrier to exploitation just dropped to near-zero for anyone with basic skills.
A Triage Framework
Apply this as a decision sequence, not a scoring rubric. Work through it in order and stop when you have enough signal.
-
Is the vulnerable package reachable from an untrusted network in production? Check your deployment: does this package process data from external sources? If no → deprioritize, schedule for next sprint or next maintenance window. If yes → continue.
-
Does Attack Complexity require conditions you don't have? An
AC:Hvulnerability requires non-default configuration or specific runtime conditions. If your deployment doesn't match those conditions → reduce urgency. IfAC:L→ continue. -
Does it require privileges your attack surface doesn't expose?
PR:Hmeans an admin-level authenticated attacker. If your vulnerable endpoint requires authentication and your threat model doesn't include compromised admin accounts → reduce urgency. IfPR:N→ continue. -
Is there a known public exploit? Check the advisory References section and the CVE detail pages on NVD and Mitre. A published proof-of-concept means treat it as immediate regardless of score. An
E:UTemporal rating (no public exploit) means you have more runway. -
Is a patched version available? If yes → patch now. Even for lower-urgency vulnerabilities, if the upgrade path is straightforward, just do it. The cost is low and the future you will be grateful. If no → document a mitigation (firewall rule, input validation layer, feature flag) and monitor for patch availability.
CVSS Quick Reference
The metrics that most change real-world exploitability:
- AV:N = network-exploitable (worst for server apps) — ask whether the package actually processes network input in your deployment
- AV:L = local access required — much lower risk for any server-side or cloud-hosted workload
- AC:L = no special conditions needed (worst) — the attack path is straightforward
- AC:H = requires specific configuration or conditions — assess whether your deployment matches
- PR:N = no authentication required (worst) — unauthenticated remote exploitation
- PR:H = admin credentials required — material reduction in exploitability
- S:C (Scope Changed) = the exploit crosses security boundaries — container escapes, privilege escalation, cross-tenant impact — always serious regardless of other metrics
- Base Score alone is not a triage decision — always check: is the package reachable in production? Is there a public exploit? Is a patch available?
Closing
CVSS scores are a standardized starting point for a conversation, not the end of one. The number exists to make vulnerabilities comparable across software and vendors. It was never designed to replace context — it was designed to communicate in the absence of it.
A 9.8 in a package your public API depends on is a fire drill. The same 9.8 in a build-time tool that never processes network input is a scheduled maintenance item. Both are real vulnerabilities. Only one of them should interrupt your day.
Teams that treat every Critical as a five-alarm emergency burn out and start ignoring alerts. Teams that read the vector string, check their deployment context, and apply the five-step triage sequence above make better decisions faster — and build the kind of judgment that means the actual emergencies get the response they deserve.
The vector string is eight components. It takes sixty seconds to read. Start there.
Have questions about vulnerability triage, CVSS environmental scoring, or building a security response process that doesn't burn out your team? Reach out.