Vulnerability Management Lifecycle: Triage, Prioritization, and Remediation
Running a vulnerability scanner is the easy part. The harder problem is what comes next: a report with 200 findings, ranging from SQL injection to missing X-Frame-Options headers, lands on a security engineer's desk. Developers are asking which issue to fix first. The CTO wants a timeline. The auditor wants evidence that you have a process.
Vulnerability management is the operational discipline that converts scanner output into fixed code and measurable risk reduction. This guide covers the full lifecycle: triaging findings, building a prioritization model that developers trust, setting SLAs, managing the handoff to engineering, verifying fixes, and reporting program health to stakeholders.
The Vulnerability Lifecycle
Every vulnerability goes through a defined lifecycle:
- Discovery — scanner, pentest, bug bounty, or internal report
- Triage — validate the finding, assess context and severity
- Prioritization — rank against other open findings by risk
- Assignment — route to the owning team with enough context to fix
- Remediation — developer fixes the issue in code
- Verification — security confirms the fix is effective
- Closure — issue resolved; exception or accepted risk documented if not fixed
Without this lifecycle, findings sit in a spreadsheet aging toward the next audit. With it, you have a measurable process and an audit trail.
Phase 1: Triage
Not every scanner finding is a real vulnerability. Triage separates true positives from false positives and adds context the scanner can't provide.
False Positive Identification
# Confirm before assigning to developers:
# 1. Reproduce the finding manually
# 2. Verify it's in a production or production-equivalent environment
# 3. Check if the finding is behind authentication the scanner couldn't complete
# Common scanner false positive patterns:
# - "SQL injection" on error pages that just reflect input in error messages
# - "Reflected XSS" where CSP blocks execution in practice
# - "Insecure direct object reference" where the ID is cryptographically random (UUID)
# - "Missing HttpOnly cookie flag" on a cookie that contains no sensitive data
# - "Weak TLS" on an internal-only endpoint not reachable from the internet
# Risk context questions:
# Is this endpoint internet-facing or internal-only?
# Is authentication required to reach it?
# What data does this endpoint expose or modify?
# Is there compensating control (WAF, network restriction) that reduces likelihood?
Triage Decision Matrix
| Finding Type | Triage Action | Rationale |
|---|---|---|
| Confirmed exploit, production exposure | Accept as-is, escalate to P1 | No additional validation needed — fix now |
| Scanner output, unconfirmed | Manual validate before assigning | Avoid crying wolf with developers |
| Finding behind auth scanner couldn't reach | Re-test with authenticated scan profile | Incomplete scan coverage — not a false positive |
| Informational / low-impact | Log and batch for quarterly review | Not worth interrupting engineering sprints |
| Duplicate | Link to existing issue, discard | Avoid duplicate work tickets |
| Out of scope / accepted risk | Document decision and close | Creates audit-defensible exception record |
Phase 2: Prioritization
CVSS scores are a starting point, not a final answer. A CVSS 9.8 critical on an internal-only endpoint with no internet access is less urgent than a CVSS 7.0 authentication bypass on your public API. Prioritization must account for context.
Risk-Based Prioritization Model
Effective prioritization combines three factors:
- Severity — what an attacker can do if they exploit it (CVSS base score as a proxy)
- Exposure — how reachable the vulnerability is (internet-facing vs. internal, authentication required)
- Exploitability — is a public exploit available? Is active exploitation observed in the wild?
# Risk scoring formula:
# Risk = Severity × Exposure × Exploitability
# Severity (1-10): Use CVSS base score, adjusted for business impact
# SQL injection on orders DB → 10 (data breach potential)
# SQL injection on public search with no data → 6 (less business impact)
# Exposure multiplier:
# Internet-facing, no auth required: 1.0
# Internet-facing, auth required: 0.7
# Internal only, no auth: 0.5
# Internal only, auth required: 0.3
# Exploitability multiplier:
# Known exploit / active exploitation: 1.0
# PoC available publicly: 0.8
# Technically exploitable, no public exploit: 0.6
# Theoretical / requires complex conditions: 0.3
# Example:
# Finding: SQLi on public search endpoint (no auth), CVSS 8.1
# Exposure: internet-facing, no auth → 1.0
# Exploitability: SQLi tools readily available → 0.8
# Risk score: 8.1 × 1.0 × 0.8 = 6.5
# Another finding: SQLi on internal admin panel, CVSS 9.1
# Exposure: internal, auth required → 0.3
# Exploitability: no public exploit → 0.6
# Risk score: 9.1 × 0.3 × 0.6 = 1.6
# The public endpoint is higher priority despite lower CVSS
Using EPSS for Exploitability
EPSS (Exploit Prediction Scoring System) is a data-driven probability score for how likely a CVE will be exploited in the next 30 days. Use it to prioritize known CVEs over theoretical ones.
# Check EPSS scores via API:
curl "https://api.first.org/data/v1/epss?cve=CVE-2023-44487" | jq '.data[].epss'
# Returns probability 0.0-1.0
# CVEs with EPSS > 0.1 (10% exploitation probability) should be treated as P1-P2
# CVEs with EPSS < 0.01 can be treated as low priority
# CISA Known Exploited Vulnerabilities catalog:
curl https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json | \
jq '.vulnerabilities[] | select(.cveID == "CVE-YOUR-ID")'
# If listed: treat as P0, fix immediately
Priority Tiers
| Priority | Criteria | SLA |
|---|---|---|
| P0 — Emergency | Active exploitation, or critical with internet exposure and no auth | 24 hours |
| P1 — Critical | High severity, internet-facing, exploitable | 7 days |
| P2 — High | High severity with auth, or medium severity internet-facing | 30 days |
| P3 — Medium | Medium severity internal, or low severity internet-facing | 90 days |
| P4 — Low | Low severity, defense-in-depth improvements | Next major release or quarterly batch |
Phase 3: Assignment and Developer Handoff
The quality of the handoff to developers determines how quickly and accurately issues get fixed. Security findings with poor context lead to developers closing tickets as "can't reproduce" or implementing a fix that doesn't address the root cause.
Effective Vulnerability Ticket Structure
## Title
[Vulnerability Type] in [Feature/Endpoint] — [Brief Impact]
Example: "SQL Injection in /api/search — unauthenticated read of orders table"
## Severity and SLA
Priority: P1 | Due: 2026-07-04
## Summary
One paragraph. What is the issue? Where is it? What does it enable?
## Steps to Reproduce
1. Send this request:
POST /api/v2/search HTTP/1.1
Content-Type: application/json
{"query": "test' OR '1'='1"}
2. Observe the response includes all order records regardless of user
## Evidence
- Screenshot: [link]
- HTTP request/response: [file]
## Root Cause
The search endpoint uses string concatenation to build the SQL query.
In search_controller.py line 47:
query = f"SELECT * FROM orders WHERE description LIKE '%{user_input}%'"
## Remediation
Use parameterized queries:
cursor.execute("SELECT * FROM orders WHERE description LIKE %s", (f"%{user_input}%",))
Reference: https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
## Verification
Security to re-test after fix deployment to verify exploitation no longer works.
Routing to the Right Team
- Map services and endpoints to their owning engineering teams before incidents occur
- Keep a service ownership registry:
api.example.com/payments → Payments team (Slack: #payments-eng) - Use a standard triage channel, not email, to avoid findings getting lost
- Security engineers should be available to answer technical questions from developers — the ticket is a starting point, not the entire context
Phase 4: SLA Tracking and Escalation
SLAs only work if there is an escalation path when they're missed. Without escalation, SLAs become suggestions.
# SLA tracking queries (example for a Jira/GitHub-based tracker):
# Find P1 findings older than 7 days with open status:
SELECT id, title, assigned_to, created_at, (NOW() - created_at) AS age
FROM vulnerabilities
WHERE priority = 'P1' AND status IN ('open', 'in_progress')
AND created_at < NOW() - INTERVAL 7 DAYS
ORDER BY created_at;
# Find all findings missing their SLA:
SELECT id, title, priority, created_at, sla_due,
(sla_due - NOW()) AS days_remaining
FROM vulnerabilities
WHERE status NOT IN ('closed', 'accepted_risk')
AND sla_due < NOW()
ORDER BY sla_due;
Escalation Paths
| SLA Status | Action | Escalation |
|---|---|---|
| On track (> 50% time remaining) | Weekly check-in | None |
| At risk (< 25% time remaining) | Proactive ping to assignee + team lead | Engineering manager aware |
| Breached (past due) | Escalate to VP/Director of Engineering | CISO informed for P0/P1 breaches |
| Breach > 30 days (critical) | Risk acceptance decision required from CISO | Board-level if customer data at risk |
Phase 5: Verification Testing
Developer "fixed" is not the same as security "verified". Every P0/P1/P2 finding must be verified by security before closure. P3/P4 can often be verified by a follow-up scan.
# Verification checklist:
# 1. Verify the specific request that reproduced the issue no longer works
# 2. Test bypass variants — fix the root cause, not just the test case
# 3. Check related endpoints for the same vulnerability class
# SQL injection fix verification:
# Original: POST /api/search {"query": "test' OR '1'='1"}
# Verify: same payload now returns 0 results or an error
# Also test: POST /api/search {"query": "1; DROP TABLE orders--"}
# And: POST /api/search {"query": "UNION SELECT username,password FROM users"}
# IDOR fix verification:
# Original: GET /api/invoices/12345 (as user with invoice 99999)
# Verify: returns 403 or 404, not invoice 12345 data
# Also test: invoice IDs just above and below the requesting user's IDs
# Authentication bypass fix:
# Try the original bypass technique
# Try variations: different HTTP methods, adding/removing headers, alternate endpoints
# Run focused rescan on the fixed endpoint:
nuclei -u https://app.example.com/api/search \
-t ~/nuclei-templates/vulnerabilities/sqli/ \
-H "Authorization: Bearer ${test_token}"
Phase 6: Exceptions and Accepted Risk
Not every vulnerability will be fixed within SLA. Business constraints, legacy systems, and architectural dependencies create situations where the security team must formally document an accepted risk rather than endlessly re-opening the ticket.
# Risk acceptance criteria (must be documented for each acceptance):
# 1. Finding ID, title, severity, and risk score
# 2. Reason the fix cannot be completed within SLA
# (architectural dependency, legacy system, business constraint)
# 3. Compensating controls in place (WAF rule, network restriction, monitoring)
# 4. Residual risk assessment
# 5. Re-evaluation date (typically 90 days, no more than 1 year)
# 6. Approval from CISO or designated security authority
# Risk acceptance is NOT:
# - Closing tickets because developers said "we'll fix it later"
# - Marking medium/low findings as accepted without review
# - Accepting critical findings without C-level sign-off
Phase 7: Program Metrics and Reporting
A vulnerability management program without metrics is unmanageable. Security leadership needs data to justify budget, demonstrate improvement, and report to the board. Engineering leadership needs data to resource remediation work correctly.
Key Metrics
| Metric | Definition | Target |
|---|---|---|
| Mean Time to Remediate (MTTR) | Average days from discovery to verification closure | P1: <14 days, P2: <45 days |
| SLA compliance rate | % of findings closed within their SLA | >90% overall; 100% for P0/P1 |
| Open critical findings | Count of P0/P1 open at any given time | 0 at end of each month |
| Vulnerability age (P1+) | Age distribution of open critical findings | No P1 older than 30 days |
| Repeat finding rate | % of findings in the same class as a previously fixed finding | <20% (measures root cause fix quality) |
| False positive rate | % of scanner findings triaged as false positives | <15% (high rates → scanner tuning needed) |
Reporting Cadences
- Weekly: SLA breach alerts to engineering managers for their teams' overdue findings
- Monthly: MTTR trends, open P0/P1 count, SLA compliance rate to CISO and VP Engineering
- Quarterly: Full program health review — MTTR by team, repeat finding rate, vulnerability class trends, resource needs
- Annual: Program maturity assessment, tooling review, policy updates
Integrating Automated Scanning into the Lifecycle
Manual pentests find deep business logic issues; automated scanners find recurring configuration and code-level vulnerabilities efficiently. The combination is more effective than either alone. Scan on every deployment (DAST in CI/CD), weekly or daily for production, and on-demand after significant changes.
# CI/CD integration pattern:
# 1. Trigger DAST scan on staging after deployment
# 2. Block on P0/P1 findings — fail the pipeline
# 3. Warn on P2 — deploy but create tickets automatically
# 4. Report P3/P4 — log for weekly triage batch
# Configuration for fail-on-critical:
# (most DAST tools support exit codes for severity thresholds)
ironimo scan --target https://staging.example.com \
--fail-on critical,high \
--report-format json \
--output scan-results.json
# Parse results into ticket creation:
jq '.findings[] | select(.severity | IN("critical","high"))' scan-results.json | \
create-jira-tickets.sh
Vulnerability management starts with reliable discovery. Ironimo provides continuous web application scanning built on Kali Linux tools — giving you accurate findings with context, on a schedule that fits your deployment cadence. Feed your vuln management program with data you can trust.
Start free scanKey Takeaways
- Triage before assigning — confirm findings before sending developers on false positive hunts
- CVSS alone is not prioritization — add exposure and exploitability factors to produce actionable risk scores
- Ticket quality directly affects fix quality — give developers steps to reproduce, root cause analysis, and a remediation suggestion
- SLAs require escalation paths; without them they become aspirational decoration
- Security verifies fixes — developer "done" is not the same as verified closed
- Risk acceptance is a formal process requiring documented justification and approval, not an escape hatch
- MTTR and SLA compliance rate are the core health metrics — improve them by improving the triage-to-assignment pipeline, not by lowering standards