Skip to main content

On This Page

Prioritizing Risk: Why Only 36 of 39 CVEs in WebGoat Were Actually Reachable

2 min read
Share

These articles are AI-generated summaries. Please check the original sources for full details.

39 CVEs in WebGoat. Only 36 Were Reachable.

Engineer Ekene Ejike developed a reachability engine to address CI/CD pipeline blocks caused by excessive security alerts in a Spring Boot application. By running the engine against OWASP WebGoat, the analysis mapped 158,000 methods to identify which vulnerabilities could actually be executed. The result was a high-fidelity risk assessment that reduced manual triage from 39 potential issues to just one unknown case.

Why This Matters

Software Composition Analysis (SCA) tools typically flag any vulnerable version found in a dependency tree, regardless of whether the application ever invokes the affected code. This creates a massive gap between perceived risk and actual risk, leading to ‘alert fatigue’ and unnecessary deployment delays. Reachability analysis bridges this gap by using static call graphs to determine if a path exists from application entry points to the vulnerable method. While a reachable CVE does not guarantee exploitability, an unreachable one guarantees the code cannot execute, allowing engineering teams to focus limited resources on high-risk paths.

Key Insights

  • NetShield Analyzer identified 36 reachable, 2 unreachable, and 1 unknown CVE in OWASP WebGoat (2026).
  • Adjacency list implementation reduced call graph traversal complexity from O(V*E) to O(V+E), cutting analysis time from 33 minutes to 29 seconds.
  • Framework-aware analysis is required to detect hidden entry points like Spring @Controller methods and Jakarta Servlet doGet/doPost calls.
  • Static analysis of Java applications must perform class hierarchy traversal to resolve virtual dispatch and interface implementations.
  • The engine uses OSV.dev (open) which flagged 39 CVEs, while Snyk (proprietary) identified 48, highlighting a database coverage gap rather than a reachability failure.

Working Examples

Direct call to a vulnerable deserialization entry point in WebGoat’s lesson code.

XStream xstream = new XStream();
Object obj = xstream.fromXML(payload);

Adjacency list optimization used to improve DFS performance by orders of magnitude.

func (cg *CallGraph) AddEdge(from, to string, callType CallType) {
  cg.Edges = append(cg.Edges, &CallEdge{From: from, To: to, Type: callType})
  cg.AdjList[from] = append(cg.AdjList[from], to)
}

Practical Applications

  • Use Case: CI/CD integration where NetShield fails builds only for REACHABLE vulnerabilities, preventing unnecessary release blocks.
  • Pitfall: Treating UNKNOWN results as safe; manual review is critical when reflection (Class.forName) or dynamic loading is involved.
  • Use Case: Automating entry point discovery for REST APIs (JAX-RS @Path) and event-driven consumers (Kafka onMessage) to ensure call graph coverage.

References:

Continue reading

Next article

Andrew Ng's Team Launches Context Hub to Solve Coding Agent API Drift

Related Content