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:

  1. Discovery — scanner, pentest, bug bounty, or internal report
  2. Triage — validate the finding, assess context and severity
  3. Prioritization — rank against other open findings by risk
  4. Assignment — route to the owning team with enough context to fix
  5. Remediation — developer fixes the issue in code
  6. Verification — security confirms the fix is effective
  7. 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:

  1. Severity — what an attacker can do if they exploit it (CVSS base score as a proxy)
  2. Exposure — how reachable the vulnerability is (internet-facing vs. internal, authentication required)
  3. 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

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

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 scan

Key Takeaways

← Back to Blog