Root cause analysis
Root cause analysis asks why a vulnerability was possible, not only where it was. It traces a fixed bug back to the missing requirement, unsafe pattern, absent check or knowledge gap that let it through, so the fix covers the whole class of bug rather than one instance.
Fixing one instance leaves its siblings in place. If a cross-tenant leak came from an endpoint that skipped the shared tenant-scoped query helper, other endpoints may skip it too, and the next report will find one. SSDF practice RV.3 asks teams to record root causes, look for patterns and change the process, and CISA's Secure by Design pledge suggests tracking the root cause (CWE) of CVEs over time.
- Who
- The engineers who fixed the bug, facilitated by AppSec or a security champion, with the product owner for the actions
- When
- Once the fix has shipped for a significant vulnerability: exploited, high severity, reported from outside or a repeat
- How often
- Per significant vulnerability, plus a periodic review of causes across all findings, often each quarter
- You end up with
- A root-cause record with a CWE, a class-level fix such as a shared helper or a new check, and owned backlog items
Do this
Decide which findings get an analysis
Set the triggers in advance, for example any exploited vulnerability, any critical or high finding, anything reported from outside, and any repeat of a weakness already fixed in the same service. OWASP SAMM's Incident Management practice asks for root cause analysis of high-severity incidents at maturity level 2 and of all security incidents at level 3; NIST SP 800-61 Rev. 3 asks incident analysis to find underlying or systemic root causes.
Classify the weakness with a CWE
Map the vulnerability to the weakness that caused it, not to its impact: "remote code execution" is an outcome, while CWE-639, Authorization Bypass Through User-Controlled Key, is a cause. MITRE's root cause mapping guidance asks for the most specific Base or Variant entry that fits, and never a Category. Store the CWE on the finding so you can count it later.
Ask why each phase let it through
Walk the lifecycle with evidence. Was there a requirement (Security requirements)? Did the threat model (Threat modeling) cover this boundary? Was a safe pattern available, and was it used? Should code review, SAST or a test have caught it? Did the author know the risk? Record each answer; there is often more than one cause.
Describe the system, not the person
Write it blamelessly: what people knew and could see at the time, and which gaps in design, tooling or process allowed the mistake. Google's SRE workbook notes that changing human behavior is less reliable than changing automated systems and processes, so treat "the developer forgot" as the start of the question, not the answer.
Fix the class, not only the instance
Search the codebase for siblings of the same pattern and fix them now, as SSDF task RV.3.3 asks. Then pick the change that stops recurrence: a shared helper or secure default (Secure design patterns), a SAST rule or regression test in CI (CI security checks), a new requirement, or training for the teams that wrote the code (Role-based training).
Track each action like a bug
File each action in the backlog of the team that owns it, with a priority, one owner and a due date. The SRE workbook warns that action items without clear owners are less likely to be resolved, and that untracked ones are often forgotten. Review open actions with the product owner until they close, and verify the class fix with a test.
Look for patterns over time
Periodically count findings by CWE, service and the phase that should have caught them, and flag any CWE that returns in a service that already fixed it. SSDF task RV.3.2 asks for this pattern analysis, and the FIRST PSIRT Services Framework suggests feeding CWE data back into the development lifecycle and training. Report repeats with your program metrics (Exceptions and metrics).
Done when
Each significant vulnerability has a recorded root cause, and the changes it calls for are owned backlog items for the earlier phases.
Mark it in the gap checkCommon mistakes
- Stopping at "human error" or naming a person. Blame makes people guarded about what they report, and the gap that allowed the mistake stays open.
- Fixing only the reported endpoint. The same missing check in the download, preview and bulk export paths waits for the next researcher.
- Making retraining everyone the default action without evidence that a knowledge gap caused the bug. A shared helper and a regression test often prevent more.
- Writing actions with no owner or due date, so they are often never done and the same class of bug returns.
- Recording causes as free text. Without a CWE or a fixed list of causes, the periodic review has nothing to count.
Common root causes and the fix that removes them
Each cause points back to the phase where a lasting fix belongs. These pairings are common starting points, not a complete list.
| Cause | Example | Class-level fix |
|---|---|---|
| Missing requirement | No rule that exports are scoped to the caller's tenant | A testable requirement and abuse case (Security requirements) |
| No safe pattern | Each endpoint builds its own tenant filter by hand | A shared, tested data-access helper that applies it by default (Secure design patterns) |
| Missing check | No test for cross-tenant access, and no SAST rule for the pattern | A regression test or custom rule that fails the build (CI security checks) |
| Knowledge gap | The team did not know a framework default was unsafe | Targeted training for that team, measured by repeats (Role-based training) |
| Skipped process | A high-risk change went out without a design review | A review trigger in the change process (Security design review) |
| Stale component | A vulnerable library shipped long after a fix was released | Automated update pull requests and due dates (Dependency patching) |
Tools and guides
- Workflow: Fix a finding
- Fix cards: tested fixes by stack
- Respond to product security incidents: plan, tabletop and postmortem
An incident response plan for product security incidents (exploited vulnerabilities, compromised releases, leaked signing keys, malicious dependencies shipped to customers), aligned with NIST SP 800-61 Rev. 3, with reporting clocks, a two-scenario tabletop exercise template and a blameless postmortem template.
- Set remediation SLAs and measure AppSec outcomes
Set vulnerability SLAs by exploitation and exposure, route misses through expiring exceptions, and measure the program with cohort-based, censoring-aware metrics that resist gaming.
- Write testable security requirements and a security Definition of Done
Derive security requirements from OWASP ASVS 5.0, abuse cases and the threat model, write them as acceptance criteria a test can fail, trace each one to a test or evidence, and close stories against a short security Definition of Done.
- Run a developer security training program that changes code
Build role-based, hands-on developer security training from this site's learning paths, labs and fix cards, meet PCI DSS 6.2.2, NIST SSDF PO.2.2 and ISO/IEC 27001 A.6.3 with real evidence, and measure the effect with repeat findings instead of completion rates.
Standards
- NIST SSDF 1.1
- RV.3 Analyze Vulnerabilities to Identify Their Root Causes
- OWASP SAMM 2
- Incident ManagementDefect Management
Sources
- NIST SP 800-218: Secure Software Development Framework (SSDF) Version 1.1 (RV.3). February 2022.
- MITRE CWE: Root cause mapping guidance
- MITRE CWE-639: Authorization Bypass Through User-Controlled Key. CWE 4.20.
- Google SRE workbook: Postmortem Culture
- Google SRE book: Postmortem Culture: Learning from Failure
- OWASP SAMM: Incident Management, Incident Response stream
- OWASP SAMM: Defect Management, Metrics and Feedback stream
- CISA: Secure by Design Pledge
- FIRST: PSIRT Services Framework 1.1
- NIST SP 800-61 Rev. 3: Incident Response Recommendations and Considerations for Cybersecurity Risk Management. April 2025.