Vulnerabilities API
HTTP reference for findings — taxonomy, validation, triage, comments, evidence files, provenance, history, bulk updates, summary, quality gates and export.
All paths are relative to https://api.aleex-rank.ai/api/v2 and authenticate with X-API-Key: rk_... (see REST API). Findings hang off a pentest, so most paths begin with /pentests/{id}/vulnerabilities. For the lifecycle model — states, severity, SLAs and evidence — read Vulnerabilities.
Related surfaces live on their own pages: Retest & remediation (retest a closure, extra quality-gate rules, team policy), Evidence & audit (sealed artifacts and the audit chain), Catalogs (CWE and ATT&CK identifiers).
The finding object
GET and PATCH return the full finding inside data. The title field is vulnerability (not title). Taxonomy, validation and identity sit alongside the triage fields.
validation_status | Meaning |
|---|---|
unvalidated | Just created; no validator has spoken yet |
validated | Deterministic proof the issue is present. Starts the remediation SLA |
failed | Positive refutation — a validator proved the issue is not present. Dropped from quality gates, reports and integrations. Never means “could not reproduce” |
needs_manual_review | A validator applies but did not conclude (WAF, missing auth). Still counts in the gate; no SLA yet |
not_validatable | No deterministic validator for the class (for example blind SSRF/XXE). Still counts in the gate |
legacy | Filed before taxonomy/validation existed. Treated like validated for SLA |
Only failed findings leave the quality gate. SLA clocks start only for validated and legacy. Human review (POST …/review) is how needs_manual_review and not_validatable become validated or failed.
{
"success": true,
"data": {
"id": 42,
"operation_id": 1203,
"agent_id": 101,
"pentest_id": 15,
"vulnerability": "SQL Injection in /search",
"description": "Boolean-based blind SQLi on the q parameter returns differential row counts.",
"severity": "critical",
"resolution": null,
"status": "open",
"priority": "urgent",
"due_date": "2026-08-11 17:04:03",
"sla_breached_at": null,
"assigned_to": null,
"assigned_by": null,
"assigned_at": null,
"resolved_by": null,
"resolved_at": null,
"resolution_type": null,
"false_positive_reason": null,
"created_at": "2026-08-10 17:04:03",
"updated_at": "2026-08-10 17:10:11",
"cvss_version": "4.0",
"cvss_vector": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N",
"cvss_base_score": 9.3,
"cvss_severity": "Critical",
"severity_override_reason": null,
"cwe_id": "CWE-89",
"cwe_name": "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')",
"cve_ids": ["CVE-2023-1234"],
"mitre_attack_techniques": ["T1190"],
"mitre_attack_technique_names": {"T1190": "Exploit Public-Facing Application"},
"owasp_wstg_ids": ["WSTG-INPV-05"],
"owasp_asvs_ids": ["V5.3.4"],
"epss_score": 0.4321,
"cisa_kev": false,
"validation_status": "validated",
"validation_method": "sqli-differential/v1",
"validated_at": "2026-08-10 17:10:11",
"confidence_score": 90,
"confidence_rationale": "Reproduced via boolean-based blind differential on parameter 'q'.",
"finding_fingerprint": "c9b1a0e3f2d4b5c678901234567890abcdef1234567890abcdef1234567890ab",
"affected_asset_id": 45,
"affected_endpoint": "/search",
"affected_parameter": "q",
"reproduction_steps": {
"steps": ["Open /search", "Submit q=1' OR '1'='1"]
},
"request_response": {
"request": "GET /search?q=1%27+OR+%271%27%3D%271",
"status": 200
},
"reference_urls": ["https://cwe.mitre.org/data/definitions/89.html"],
"evidence_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"is_regression": false,
"regression_of_id": null
}
}
finding_fingerprint is a stable identity Rank computes (scope + CWE + endpoint path + parameter). The same weakness on a later pentest against the same asset updates the same ticket instead of opening a duplicate. epss_score and cisa_kev are published signals (FIRST.org / CISA); they are not writable. The host string used at ingest (affected_asset_value) is not stored — it only resolves affected_asset_id.
Look up legal CWE and ATT&CK identifiers in Catalogs.
List vulnerabilities
GET /pentests/{id}/vulnerabilities
Returns compact table rows for a pentest: identity, lifecycle, SLA and badge-sized taxonomy. Narrative, evidence JSON and replay live on GET …/{vulnId}. Supports page and per_page (default 50), and can be filtered by severity.
{
"success": true,
"data": {
"items": [
{
"id": 42,
"vulnerability": "SQL Injection in /search",
"severity": "critical",
"status": "open",
"priority": "urgent",
"assigned_to": null,
"affected_asset_id": 45,
"created_at": "2026-08-10 17:04:03",
"due_date": "2026-08-11 17:04:03",
"sla_breached_at": null,
"cwe_id": "CWE-89",
"cwe_name": "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')",
"cvss_base_score": 9.3,
"cvss_severity": "Critical",
"validation_status": "validated",
"confidence_score": 90,
"cisa_kev": false,
"epss_score": 0.4321,
"cve_ids": ["CVE-2023-1234"],
"is_regression": false
}
],
"pagination": {"total": 1, "count": 1, "per_page": 50, "current_page": 1, "total_pages": 1},
"risk_score": {"score": 93, "level": "critical"}
}
}
Retrieve, update, delete
GET /pentests/{id}/vulnerabilities/{vulnId}
PATCH /pentests/{id}/vulnerabilities/{vulnId}
DELETE /pentests/{id}/vulnerabilities/{vulnId}
GET returns the full finding above. DELETE soft-deletes it. PATCH updates editable text, lifecycle notes and taxonomy. Status changes go through the dedicated endpoints below; assignment uses POST/DELETE …/assign.
Writable taxonomy (alongside vulnerability, description, severity, resolution, priority):
{
"cvss_version": "4.0",
"cvss_vector": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N",
"cvss_base_score": 9.3,
"cvss_severity": "Critical",
"severity_override_reason": null,
"cwe_id": "CWE-89",
"cve_ids": ["CVE-2023-1234"],
"mitre_attack_techniques": ["T1190"],
"owasp_wstg_ids": ["WSTG-INPV-05"],
"owasp_asvs_ids": ["V5.3.4"],
"affected_endpoint": "/search",
"affected_parameter": "q",
"reproduction_steps": {"steps": ["Open /search"]},
"request_response": {"request": "GET /search?q=1", "status": 200},
"reference_urls": ["https://cwe.mitre.org/data/definitions/89.html"]
}
If severity disagrees with cvss_severity, send severity_override_reason. epss_score and cisa_kev are not writable — they are external published signals. Sending them is rejected.
Human review
POST /pentests/{id}/vulnerabilities/{vulnId}/review
Settle a finding the validator left inconclusive. Accepted only when the current validation_status is needs_manual_review or not_validatable. The conclusion is validated or failed; reason is required and replaces the validator’s rationale. Rank records validation_method as human_review and publishes vulnerability.validated or vulnerability.validation_failed (with triggered_by).
{
"validation_status": "validated",
"reason": "Confirmed on a staging replica with production-equivalent data"
}
{
"success": true,
"data": {
"message": "Validation review recorded",
"vulnerability_id": 42,
"previous_validation_status": "needs_manual_review",
"validation_status": "validated",
"reviewed_by": 42
}
}
This endpoint cannot rewrite an existing validated or failed verdict. If the finding is already resolved, false_positive or accepted_risk, the conclusion is stored but does not start an SLA or publish an event.
Provenance
GET /pentests/{id}/vulnerabilities/{vulnId}/provenance
Where the finding came from, and whether its structured evidence digest still matches. evidence_intact is null when the finding never had a digest.
{
"success": true,
"data": {
"vulnerability_id": 42,
"pentest_id": 15,
"evidence_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"evidence_intact": true,
"validation": {
"status": "validated",
"method": "sqli-differential/v1",
"validated_at": "2026-08-10 17:10:11",
"confidence_score": 90,
"confidence_rationale": "Reproduced via boolean-based blind differential on parameter 'q'."
},
"origin": {
"operation_id": 1203,
"agent_id": 101,
"agent_role": "exploitation",
"model_id": 7,
"model_alias": "gemini-2.5-flash",
"provider": "google",
"prompt_ref": "vulnerability_taxonomy_v1.md",
"prompt_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"tool_invocations": [{"tool": "sqlmap", "target": "https://acme.example.com/search"}],
"reasoning_digest": {"agents": {"101": {"stop_reason": "goal_reached"}}},
"discovered_at": "2026-08-10T17:04:03Z",
"recorded_at": "2026-08-10 17:04:10"
}
}
}
Sealed binary artifacts, the Merkle manifest and the control audit chain are documented in Evidence & audit.
State transitions
A finding is always open, in_progress, resolved, false_positive or accepted_risk. The dedicated endpoints below carry the extra data each transition needs; the generic status endpoint only moves a finding to open or in_progress.
Resolve
POST /pentests/{id}/vulnerabilities/{vulnId}/resolve
{
"resolution_type": "evidenced",
"comment": "Upgraded nginx and added output encoding"
}
resolution_type is evidenced (requires at least one uploaded evidence file, else 400) or without_evidence. Closing a finding can enqueue an automatic retest depending on remediation policy.
Reopen
POST /pentests/{id}/vulnerabilities/{vulnId}/reopen
Sends a closed finding back to open and recomputes its due date. Reopening a false positive clears the stored reason.
False positive
POST /pentests/{id}/vulnerabilities/{vulnId}/false-positive
{
"reason": "Endpoint is behind authentication and not externally reachable",
"comment": "Confirmed with the infrastructure team"
}
reason is required (max 2000 characters). This is a human triage decision, distinct from validation_status: failed.
Accept risk
POST /pentests/{id}/vulnerabilities/{vulnId}/accept-risk
{"reason": "Accepted for this release; tracked in RISK-128"}
Generic status and priority
PATCH /pentests/{id}/vulnerabilities/{vulnId}/status
PATCH /pentests/{id}/vulnerabilities/{vulnId}/priority
{"status": "in_progress"}
{"priority": "urgent"}
The status endpoint accepts only open and in_progress. Use the dedicated endpoints above for resolved, false_positive and accepted_risk.
Assignment
POST /pentests/{id}/vulnerabilities/{vulnId}/assign
DELETE /pentests/{id}/vulnerabilities/{vulnId}/assign
The pentest must belong to a team and the assignee must be a member of it. Assigning an open finding moves it to in_progress automatically and emails the assignee.
{"user_id": 42, "comment": "Please verify the patch"}
DELETE …/assign unassigns (no body).
Comments
GET /pentests/{id}/vulnerabilities/{vulnId}/comments
POST /pentests/{id}/vulnerabilities/{vulnId}/comments
PATCH /pentests/{id}/vulnerabilities/{vulnId}/comments/{commentId}
DELETE /pentests/{id}/vulnerabilities/{vulnId}/comments/{commentId}
GET returns a combined feed of user comments and automatic system entries. Filter with ?type=comment, ?type=status_change or ?type=assignment. You may only edit and delete your own comment entries (max 5000 characters).
{"comment": "Verified the fix is deployed to production"}
Evidence files
Triage uploads that prove a finding has been mitigated — distinct from sealed run evidence and from GET …/evidence (the original discovery operation).
POST /pentests/{id}/vulnerabilities/{vulnId}/evidence-files
GET /pentests/{id}/vulnerabilities/{vulnId}/evidence-files
GET /pentests/{id}/vulnerabilities/{vulnId}/evidence-files/{fileId}
DELETE /pentests/{id}/vulnerabilities/{vulnId}/evidence-files/{fileId}
Upload up to 20 files per finding, each up to 10 MB, of type jpg, jpeg, png, gif, webp, pdf, txt or csv. Each file is validated by extension, declared MIME type and actual content. Upload as multipart/form-data with files[], or as base64 JSON:
{
"files": [
{"name": "patch-proof.png", "data": "data:image/png;base64,iVBORw0KGgo..."}
]
}
Fetching a file returns a time-limited signed download URL:
{
"id": 1,
"vulnerability_id": 42,
"file_name": "patch-proof.png",
"file_type": "image/png",
"file_size": 245760,
"download_url": "https://storage.googleapis.com/...",
"created_at": "2026-02-10 12:00:00"
}
GET /pentests/{id}/vulnerabilities/{vulnId}/evidence (singular) returns the original discovery evidence the agents produced, distinct from these resolution evidence files.
History
GET /pentests/{id}/vulnerabilities/{vulnId}/history
An immutable audit trail of every status change.
{
"success": true,
"data": {
"history": [
{
"id": 3,
"changed_by": 1,
"old_status": "false_positive",
"new_status": "open",
"reason": "Confirmed the issue is real after retest",
"created_at": "2026-02-10 14:30:00"
}
],
"total": 1,
"page": 1,
"per_page": 50
}
}
Bulk status update
PATCH /pentests/{id}/vulnerabilities/bulk-status
Update many findings in one transaction — ideal for pipeline triage. Up to 100 ids, transitions limited to open and in_progress. Findings that can’t transition are skipped, not failed.
{
"vulnerability_ids": [1, 2, 3, 5, 8],
"status": "in_progress",
"comment": "Triaging after scan v2.4.1",
"reason": "Pipeline automated triage"
}
{
"success": true,
"data": {
"message": "5 vulnerability(ies) updated",
"updated": 5,
"skipped": 0,
"total_requested": 5,
"errors": []
}
}
Summary
GET /pentests/{id}/vulnerabilities/summary
A rollup for dashboards. The summary object is returned directly inside data. This counts all findings, including validation_status: failed. Quality-gate summary numbers exclude refuted findings — they are not the same denominator.
{
"total": 45,
"by_status": {"open": 12, "in_progress": 8, "resolved": 20, "false_positive": 3, "accepted_risk": 2},
"by_severity": {"critical": 2, "high": 10, "medium": 18, "low": 15, "info": 0},
"risk_score": {"score": 41, "level": "medium"}
}
Global summary
GET /vulnerabilities/summary
The same rollup across every pentest you can see, plus how many pentests that covers.
{
"success": true,
"data": {
"total": 128,
"by_severity": {"critical": 4, "high": 22, "medium": 51, "low": 40, "info": 11},
"by_status": {"open": 30, "in_progress": 18, "resolved": 70, "false_positive": 6, "accepted_risk": 4},
"risk_score": {"score": 38, "level": "medium"},
"pentests_count": 12
}
}
Validation metrics
GET /vulnerabilities/validation-metrics
False-positive rates by CWE across all pentests the caller can see. automated_fp_rate is failed / (validated + failed) for that class (null when nothing has been decided). human_false_positive counts triage false_positive status, a separate signal.
{
"success": true,
"data": {
"by_class": [
{
"cwe_id": "CWE-89",
"cwe_name": "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')",
"total": 14,
"validated": 11,
"failed": 2,
"needs_manual_review": 0,
"not_validatable": 0,
"unvalidated": 1,
"legacy": 0,
"human_false_positive": 1,
"automated_fp_rate": 0.154
}
],
"pentests_count": 12
}
}
This endpoint is rate-limited more tightly than pentest CRUD (30/60).
Quality gate
POST /pentests/{id}/vulnerabilities/quality-gate
Evaluate pass/fail rules against the current findings — use it to block a deploy. Only failed (refuted) findings are excluded; unvalidated, needs_manual_review and not_validatable still count.
{
"rules": [
{"severity": "critical", "max_open": 0},
{"severity": "high", "max_open": 5},
{"min_resolution_rate": 0.8},
{"max_overdue": 0}
]
}
{
"passed": false,
"failures": [
{"rule": "critical max_open 0", "actual": 2, "expected": 0}
],
"summary": {
"open_by_severity": {"critical": 2, "high": 4, "medium": 10, "low": 8, "info": 0},
"resolution_rate": 0.556,
"overdue_count": 4
}
}
Built-in rule types: severity + max_open, min_resolution_rate (0–1), and max_overdue. Optional retest-aware rules — min_verified_resolution_rate, max_unverified_resolved, max_regressed, max_pending_retests — are documented on Retest & remediation. Team defaults for those rules live on GET/PUT /teams/{id}/remediation-policy.
Export
GET /pentests/{id}/vulnerabilities/export?format=csv
GET /pentests/{id}/vulnerabilities/export?format=json
GET /pentests/{id}/vulnerabilities/export?format=sarif
GET /pentests/{id}/vulnerabilities/export?format=defectdojo
Download every finding for a pentest.
format | Response |
|---|---|
json | Standard envelope; data.vulnerabilities is the array |
csv | Download (Content-Disposition: attachment). Fields include id, title, description, severity, status, priority, assigned_to, resolution_type, false_positive_reason, due_date, created_at, resolved_at and resolved_by |
sarif | SARIF document (application/sarif+json) for GitHub Code Scanning / similar. Fingerprints are stable across runs |
defectdojo | DefectDojo import JSON |
SARIF and DefectDojo are finding exports, not sealed report formats. Sealed PDF/HTML/Markdown/DOCX/JSON reports are Reports.
Availability
Bulk status, quality gate and webhooks are gated by tier. Assignment to team members requires a team plan; individual Ultra accounts have full triage but no team assignment. See Teams & tiers. To get notified when findings change, subscribe at the tenant with Webhooks, or file tickets through Integrations.