Retest a fixed finding
Enqueue a retest of a resolved finding, poll the run, and configure remediation policy. Auth cookies are never returned on GET.
What you’ll build
A retest of a resolved finding (or of every eligible resolved finding on a pentest). You enqueue the run, poll until it finishes, and optionally update remediation policy (SLAs, auto-retest, extra quality-gate rules).
A retest does not rewrite validation_status. still_vulnerable reopens the finding to
open; fixed, inconclusive and skipped leave triage status alone. This is not a
scheduled re-run of the whole pentest.
Optional cookies / headers are used only for that run and are never returned on GET.
Prerequisites
- A completed pentest with at least one resolved finding (see Vulnerability triage & CI gate).
- An API token (see Authentication).
pip install rank-sdk
export RANK_API_KEY=rk_...
export RANK_PENTEST_ID=123
Steps
-
Enqueue a retest of one resolved finding. Pass optional auth context for authenticated replay — it will not appear on later reads.
409if a run for that finding is already in flight.import rank client = rank.Rank() pentest_id = 123 vulns = client.pentests.vulnerabilities.list(pentest_id) resolved = next(v for v in vulns.items if v.status == "resolved") run = client.pentests.vulnerabilities.retest( pentest_id, resolved.id, cookies="session=abc", headers={"Authorization": "Bearer eyJ..."}, ) print(run.id, run.status, run.origin)To retest every eligible resolved finding on the pentest:
run = client.pentests.retest(pentest_id) -
Poll the run until it leaves
queued. Detail includes per-finding results and omits cookies/headers.import time while True: detail = client.pentests.retests.retrieve(pentest_id, run.id) print(detail.status, detail.summary) if detail.status not in ("queued", "running", "pending"): break time.sleep(3) for finding in detail.findings: print(finding) -
Read or replace remediation policy (caller default). Team override:
client.teams.remediation_policy.policy = client.remediation_policy.retrieve() print(policy.policy or policy.system_default) client.remediation_policy.update( retest_sla_hours=72, auto_retest_on_resolve=False, auto_retest_on_ticket_close=True, retest_cooldown_minutes=15, quality_gate_rules=[ {"severity": "critical", "max_open": 0}, {"min_verified_resolution_rate": 0.9}, ], )
-
Enqueue a retest. Optional auth context is stored for the run only.
curl https://api.aleex-rank.ai/api/v2/pentests/123/vulnerabilities/42/retest \ -H "X-API-Key: $RANK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"cookies": "session=abc", "headers": {"Authorization": "Bearer eyJ..."}}'All eligible findings on the pentest:
POST /api/v2/pentests/123/retest X-API-Key: rk_... -
List runs and fetch detail.
GETnever echoescookies/headers.GET /api/v2/pentests/123/retests GET /api/v2/pentests/123/retests/77 X-API-Key: rk_...Cancel is accepted only while the run is still
queued:POST /api/v2/pentests/123/retests/77/cancel. -
Remediation policy.
GET /api/v2/remediation-policy PUT /api/v2/remediation-policy Content-Type: application/json { "retest_sla_hours": 72, "auto_retest_on_resolve": false, "auto_retest_on_ticket_close": true, "retest_cooldown_minutes": 15, "quality_gate_rules": [ {"severity": "critical", "max_open": 0}, {"min_verified_resolution_rate": 0.9} ] }Team override:
GET/PUT /api/v2/teams/{id}/remediation-policy.
Run it
Save the following as retest_finding.py, set RANK_API_KEY and RANK_PENTEST_ID, then run
python retest_finding.py.
"""Enqueue a retest, poll the run, and print remediation policy.
What this script does:
1. Lists findings and enqueues a retest of the first resolved one
(or of every eligible finding if none are resolved).
2. Polls the run until it leaves queued/running.
3. Prints per-finding results. Auth cookies are never on GET.
4. Prints the caller remediation policy.
Run:
pip install rank-sdk
export RANK_API_KEY=rk_...
export RANK_PENTEST_ID=123
python retest_finding.py
"""
from __future__ import annotations
import os
import time
import rank
PENTEST_ID = int(os.environ.get("RANK_PENTEST_ID", "0"))
def main() -> None:
if PENTEST_ID <= 0:
raise SystemExit("ERROR: set RANK_PENTEST_ID to a completed pentest ID.")
with rank.Rank() as client:
vulns = client.pentests.vulnerabilities.list(PENTEST_ID)
resolved = next((v for v in vulns.items if v.status == "resolved"), None)
if resolved:
run = client.pentests.vulnerabilities.retest(PENTEST_ID, resolved.id)
print(f"Retest of #{resolved.id}: run {run.id} status={run.status}")
else:
run = client.pentests.retest(PENTEST_ID)
print(f"Pentest retest: run {run.id} status={run.status}")
while True:
detail = client.pentests.retests.retrieve(PENTEST_ID, run.id)
print(f" status={detail.status} summary={detail.summary}")
if detail.status not in ("queued", "running", "pending"):
break
time.sleep(3)
for finding in detail.findings:
print(f" {finding}")
policy = client.remediation_policy.retrieve()
current = policy.policy or policy.system_default
if current is not None:
print(
f"Policy: retest_sla_hours={current.retest_sla_hours} "
f"auto_retest_on_ticket_close={current.auto_retest_on_ticket_close}"
)
if __name__ == "__main__":
try:
main()
except rank.AuthenticationError:
print("ERROR: invalid or missing API key. Set RANK_API_KEY.")
except rank.NotFoundError:
print(f"ERROR: pentest #{PENTEST_ID} not found.")
except rank.APIError as exc:
print(f"API error ({exc.status_code}): {exc.message}")