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

pip install rank-sdk
export RANK_API_KEY=rk_...
export RANK_PENTEST_ID=123

Steps

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}")

Where to go next