Safeguard
Vulnerability Management

The 10 most common code-level vulnerability classes, ranked by real-world data

MITRE's 2025 CWE Top 25 scored 39,080 CVEs — cross-site scripting still ranks #1, but Missing Authorization jumped five spots. Here's how to prevent each class.

Safeguard Research Team
Research
8 min read

Every year, MITRE and CISA score the most dangerous software weaknesses by mining real disclosure data, and the 2025 CWE Top 25 analyzed 39,080 CVE Records published between June 2024 and June 2025 to do it. Each entry is cross-referenced against CISA's Known Exploited Vulnerabilities (KEV) catalog, so the list doesn't just measure how often a weakness gets reported — it measures how often attackers actually use it in the wild. The results are consistent with prior years in some ways and surprising in others: Cross-Site Scripting (CWE-79) holds the top spot for the second consecutive cycle with a score of 60.38, more than double second-place SQL Injection (CWE-89) at 28.72. But the bigger story is Missing Authorization (CWE-862), which climbed five spots to rank 4 as API-driven and cloud-native architectures push more access-control logic out of the framework and into application code, where it's easier to get wrong. Meanwhile OS Command Injection (CWE-78), ranked 9th overall, carries 20 KEV entries — the highest real-world exploitation count of any weakness in the top ten. This post walks through the highest-scoring classes and the specific coding patterns that prevent each one.

Why does Cross-Site Scripting still rank #1 after two decades of known fixes?

XSS still tops the list because output encoding is easy to explain and easy to skip under deadline pressure, and modern frameworks only close the gap partially. The vulnerability lets attacker-supplied input execute as script in a victim's browser when an application reflects, stores, or writes untrusted data into HTML, JavaScript, or DOM contexts without neutralizing it. CWE-79 scored 60.38 in the 2025 Top 25 with 7 entries in the CISA KEV catalog, meaning real attackers actively exploited seven distinct XSS CVEs. React and Vue auto-escape template interpolation by default, which eliminates the most common reflected-XSS pattern, but both still expose an intentional escape hatch — dangerouslySetInnerHTML in React and v-html in Vue — for cases where a developer needs to render raw HTML, and that escape hatch is where most modern XSS findings concentrate. Prevention means context-aware output encoding (HTML-entity encoding in HTML bodies, JavaScript-string encoding inside <script> blocks, URL encoding in href attributes), a strict Content-Security-Policy header that blocks inline script execution, and treating any use of an HTML-injection API as a security review trigger, not a routine code change.

Why is SQL Injection still #2 when parameterized queries have existed for decades?

SQL Injection persists at rank 2 with a score of 28.72 and 4 KEV entries because string-concatenated queries remain the path of least resistance in ad hoc scripts, ORMs used incorrectly, and legacy code nobody has revisited. The fix has been well understood since prepared statements became mainstream in the early 2000s: parameterized queries separate SQL structure from user-supplied data at the driver level, so an attacker's ' OR '1'='1 payload is bound as a literal string value rather than parsed as SQL syntax. The failure mode that keeps CWE-89 in the top three is usually one of two patterns — raw string formatting (f"SELECT * FROM users WHERE id={user_id}") instead of a parameterized call, or an ORM's raw-query escape hatch (Django's .raw(), SQLAlchemy's text()) used with unsanitized interpolation. Prevention requires banning string-built SQL in code review and lint rules (Bandit's B608 check exists specifically for this), enforcing parameterized queries or ORM query builders everywhere, and applying least-privilege database accounts so that even a successful injection can't read tables outside its intended scope.

Why did Missing Authorization jump five spots to rank 4?

Missing Authorization (CWE-862) rose five positions in the 2025 list to reach rank 4, and the shift tracks a real architectural change: as applications decompose into microservices and API-first backends, authorization logic that used to live in a handful of centralized MVC controllers now gets re-implemented independently across dozens of endpoints. The weakness occurs when a function or API route performs an action without verifying the requesting user actually has permission to perform it — distinct from CWE-306 (Missing Authentication), which is about proving identity at all. A common real pattern is an endpoint that checks a valid session token exists but never checks whether that session's user owns the specific resource being accessed or modified, letting any authenticated user read or edit another user's data by changing an ID in the request (insecure direct object reference, itself a special case of CWE-862). Prevention means centralizing authorization decisions in shared middleware or a policy-engine layer rather than re-implementing ownership checks per-route, writing tests that assert an authenticated-but-unauthorized user is rejected on every state-changing endpoint, and defaulting new routes to deny-by-default rather than allow-by-default.

Why do memory-safety weaknesses like Out-of-bounds Write still rank in the top 10?

Out-of-bounds Write (CWE-787) ranks 5th with a score of 12.68 and 12 KEV entries, and Use After Free (CWE-416) ranks 7th with 14 KEV entries — the two highest KEV counts of any weakness outside command injection — because C and C++ still underpin operating system kernels, browsers, and embedded firmware where memory is managed manually. An out-of-bounds write happens when code writes past the boundary of an allocated buffer, corrupting adjacent memory in ways that range from a crash to arbitrary code execution; a use-after-free happens when code accesses memory through a pointer after that memory has already been freed and potentially reallocated for something else. Modern mitigation strategy has largely shifted from "write safer C" to "write less C": Google's Chrome security team has reported that roughly 70% of high-severity Chrome vulnerabilities have historically been memory-safety issues, which is the direct rationale behind the browser's ongoing migration of new code to Rust, a language that enforces bounds checking and ownership rules at compile time. Where legacy C/C++ can't be replaced outright, fuzzing (libFuzzer, AFL++), AddressSanitizer instrumentation in CI, and bounds-checked container types (std::vector::at() instead of operator[]) meaningfully reduce the exploitable surface.

Why does OS Command Injection have the highest real-world exploitation rate in the top 10?

OS Command Injection (CWE-78) ranks 9th by overall score but carries 20 CISA KEV entries, more than any other weakness in the top ten — meaning it is scored lower on raw frequency but attackers exploit it more reliably once it appears. The weakness occurs when application code builds a shell command by concatenating untrusted input and passes it to a function like os.system(), subprocess.run(cmd, shell=True), or a Node.js child_process.exec() call, letting an attacker append shell metacharacters (;, |, &&) to run arbitrary commands on the host. It's a favorite in real exploitation because a single successful injection typically grants full remote code execution, not partial data exposure — which is exactly the profile that gets a CVE added to KEV. Prevention means avoiding shell invocation entirely wherever possible: call the target binary directly with an argument array (subprocess.run([cmd, arg1, arg2], shell=False)) so the OS never re-parses a string for shell metacharacters, validate any input that must reach a shell against a strict allowlist, and run the process with the minimum OS privileges it needs so a successful injection can't pivot further.

How should a team prioritize fixing these across a large codebase?

Prioritization should combine the CWE Top 25's severity scoring with two things unique to your own environment: whether the vulnerable code path is actually reachable from untrusted input, and whether it sits in a component already known to be under active attack. The Top 25 methodology itself does this at the ecosystem level by cross-referencing every CWE against CISA KEV — a CWE-78 finding is treated as more urgent than a CWE-352 finding partly because 20 real command-injection CVEs have been weaponized against 0 real CSRF CVEs in that dataset. The same logic applies inside a single codebase: not every SQL Injection finding a scanner flags sits on a path a real request can reach, and not every dependency carrying a known CVE is invoked by your application at runtime. This is where reachability analysis and exploit-prediction scoring (EPSS) earn their keep once a code-level weakness introduces a vulnerable dependency into your supply chain — platforms like Safeguard combine EPSS, CISA KEV status, and call-path reachability to separate the handful of dependency vulnerabilities that are genuinely exploitable in your build from the hundreds that are present but never executed, so a security team triaging a long backlog can spend its time on the fixes that actually reduce risk.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.