Prioritizing Risk: Why Only 36 of 39 CVEs in WebGoat Were Actually Reachable
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
MyCoCo Reduces AI-Generated IaC Security Findings by 94% with OPA Guardrails
Only 9% of AI-generated IaC meets security compliance. MyCoCo slashed security findings by 94% using OPA policies while retaining 70% of speed gains.
The Secret Behind SAST: The Security Blind Spot Developers Can’t Ignore (Part 1)
SAST detects OWASP Top 10 vulnerabilities early, reducing remediation costs by up to 70%.
RiskScore: Streamlining CVE Prioritization with Composite Risk Scoring
RiskScore simplifies vulnerability triage by combining CVSS, EPSS, and CISA KEV into a single 0–100 composite score.