(
connection: sqlite3.Connection, args: argparse.Namespace
)
| 2353 | |
| 2354 | |
| 2355 | def set_finding_remediation( |
| 2356 | connection: sqlite3.Connection, args: argparse.Namespace |
| 2357 | ) -> dict[str, Any]: |
| 2358 | request_id = require_uuid(args.request_id, "request-id") |
| 2359 | action_token = require_uuid(args.action_token, "action-token") |
| 2360 | summary = optional_text(args.summary, maximum=2400) |
| 2361 | verification_summary = optional_text(args.verification_summary, maximum=2400) |
| 2362 | try: |
| 2363 | occurrence = require_occurrence(connection, args.occurrence_id) |
| 2364 | require_finding_open(connection, occurrence["id"]) |
| 2365 | scan = require_scan(connection, occurrence["scan_id"]) |
| 2366 | current = connection.execute( |
| 2367 | "SELECT * FROM finding_remediation_attempts WHERE request_id = ?", |
| 2368 | (request_id,), |
| 2369 | ).fetchone() |
| 2370 | if current is None or current["occurrence_id"] != occurrence["id"]: |
| 2371 | raise SystemExit("Codex Security finding remediation request not found.") |
| 2372 | if current["version"] != args.expected_version: |
| 2373 | raise SystemExit( |
| 2374 | "This remediation request changed. Refresh it before recording an update." |
| 2375 | ) |
| 2376 | if current["pending_action_claim_token"] is None: |
| 2377 | raise SystemExit( |
| 2378 | "This remediation attempt does not have an owned pending host request." |
| 2379 | ) |
| 2380 | if current["pending_action_claim_token"] != action_token: |
| 2381 | raise SystemExit("This remediation host request is owned by a different action token.") |
| 2382 | require_remediation_transition(current["state"], args.state) |
| 2383 | require_pending_remediation_action(current, args.state) |
| 2384 | patch_path = current["patch_path"] |
| 2385 | if args.patch_path is not None: |
| 2386 | requested_patch_path = require_scan_relative_file(scan, args.patch_path) |
| 2387 | if patch_path is not None and requested_patch_path != patch_path: |
| 2388 | raise SystemExit("A remediation attempt cannot replace its reviewed patch path.") |
| 2389 | patch_path = requested_patch_path |
| 2390 | patch_digest = current["patch_digest"] |
| 2391 | if args.patch_digest is not None: |
| 2392 | requested_patch_digest = require_sha256_digest(args.patch_digest, "patch-digest") |
| 2393 | if patch_digest is not None and requested_patch_digest != patch_digest: |
| 2394 | raise SystemExit("A remediation attempt cannot replace its reviewed patch digest.") |
| 2395 | patch_digest = requested_patch_digest |
| 2396 | base_revision = optional_text(args.base_revision, maximum=512) |
| 2397 | if args.state in {"generated", "applied", "verifying", "verified"}: |
| 2398 | if patch_path is None or patch_digest is None: |
| 2399 | raise SystemExit( |
| 2400 | "Generated remediation states require a scan-local patch path and digest." |
| 2401 | ) |
| 2402 | require_matching_patch_digest(scan, patch_path, patch_digest) |
| 2403 | if args.state == "generated": |
| 2404 | require_remediation_checkout_unchanged(scan, current, require_base_content=True) |
| 2405 | if args.state in {"applied", "verifying", "verified"}: |
| 2406 | if base_revision != current["base_revision"]: |
| 2407 | raise SystemExit( |
| 2408 | "The remediation base revision changed. Regenerate the patch before applying it." |
| 2409 | ) |
| 2410 | if args.state in {"verifying", "verified"}: |
| 2411 | require_remediation_checkout_unchanged( |
| 2412 | scan, |
no test coverage detected