(args)
| 1295 | |
| 1296 | |
| 1297 | def _cmd_patch(args) -> int: |
| 1298 | root: Path = args.results_dir |
| 1299 | if not root.is_dir(): |
| 1300 | print(f"error: {root} is not a directory", file=sys.stderr) |
| 1301 | return 1 |
| 1302 | agent_env = _resolve_auth_env() |
| 1303 | if agent_env is None: |
| 1304 | print(NO_AUTH_MSG, file=sys.stderr) |
| 1305 | return 1 |
| 1306 | if not args.model: |
| 1307 | print("error: --model required (or set VULN_PIPELINE_MODEL)", file=sys.stderr) |
| 1308 | return 1 |
| 1309 | |
| 1310 | groups = dedup(root) |
| 1311 | if not groups: |
| 1312 | print("No crashes under results dir.", file=sys.stderr) |
| 1313 | return 2 |
| 1314 | |
| 1315 | # Same ordering as _cmd_report so bug_NN here matches reports/bug_NN/ |
| 1316 | def _has_passed(ents): return any(s == "crash_found" for _p, s, _r in ents) |
| 1317 | ordered = sorted(groups.items(), |
| 1318 | key=lambda kv: (0 if _has_passed(kv[1]) else 1, kv[0])) |
| 1319 | items = [(i, sig, ents) for i, (sig, ents) in enumerate(ordered) |
| 1320 | if args.bug is None or i == args.bug] |
| 1321 | if not items: |
| 1322 | print(f"No bug matching --bug {args.bug}.", file=sys.stderr) |
| 1323 | return 2 |
| 1324 | |
| 1325 | first_path = next(p for _i, _s, ents in items for p, _st, _r in ents) |
| 1326 | target_name = json.loads(first_path.read_text())["target"] |
| 1327 | target = TargetConfig.load(args.targets_dir / target_name) |
| 1328 | if not target.build_command: |
| 1329 | print(f"error: target {target.name!r} has no build_command in config.yaml — " |
| 1330 | f"the patch grader needs an in-container rebuild step", file=sys.stderr) |
| 1331 | return 1 |
| 1332 | global _current_target_name |
| 1333 | _current_target_name = target.name |
| 1334 | |
| 1335 | if not docker_ops.image_exists(target.image_tag): |
| 1336 | print(f"[build] Building {target.image_tag} ...") |
| 1337 | docker_ops.build(target.dockerfile_dir, target.image_tag) |
| 1338 | |
| 1339 | reports_root = root / "reports" |
| 1340 | system_prompt = build_system_prompt(args.engagement_context) |
| 1341 | |
| 1342 | print(color(f"[patch] {len(items)} bug(s) → {reports_root}/bug_NN/{{patch.diff,patch_result.json}}", "patch")) |
| 1343 | print(f" model: {args.model} reattack: {'off' if args.no_reattack else 'on'} " |
| 1344 | f"iterations≤{args.max_iterations}\n") |
| 1345 | |
| 1346 | async def _one(idx: int, entries) -> dict: |
| 1347 | out_dir = reports_root / f"bug_{idx:02d}" |
| 1348 | out_dir.mkdir(parents=True, exist_ok=True) |
| 1349 | rep_path, _result, crash_dict = _pick_representative(entries) |
| 1350 | crash = CrashArtifact.from_dict(crash_dict) |
| 1351 | report_json = out_dir / "report.json" |
| 1352 | report_text = (json.loads(report_json.read_text()).get("report") |
| 1353 | if report_json.exists() else None) |
| 1354 | try: |
no test coverage detected