@@ -528,6 +528,81 @@ def _download_tasks(
528
528
azcopy_sync (to_download [name ], outdir )
529
529
530
530
531
+ class Repro (Endpoint ):
532
+ """Interact with repro files"""
533
+
534
+ endpoint = "repro_vms"
535
+
536
+ def get_files (
537
+ self ,
538
+ report_container : primitives .Container ,
539
+ report_name : str ,
540
+ include_setup : bool = False ,
541
+ output_dir : primitives .Directory = primitives .Directory ("." ),
542
+ ) -> None :
543
+ """downloads the files necessary to locally repro the crash from a given report"""
544
+ report_bytes = self .onefuzz .containers .files .get (report_container , report_name )
545
+ report = json .loads (report_bytes )
546
+
547
+ crash_info = {
548
+ "input_blob_container" : primitives .Container ("" ),
549
+ "input_blob_name" : "" ,
550
+ "job_id" : "" ,
551
+ }
552
+ if "input_blob" in report :
553
+ crash_info ["input_blob_container" ] = report ["input_blob" ]["container" ]
554
+ crash_info ["input_blob_name" ] = report ["input_blob" ]["name" ]
555
+ crash_info ["job_id" ] = report ["job_id" ]
556
+ elif "crash_test_result" in report and "original_crash_test_result" in report :
557
+ if report ["original_crash_test_result" ]["crash_report" ] is None :
558
+ self .logger .error (
559
+ "No crash report found in the original crash test result, repro files cannot be retrieved"
560
+ )
561
+ return
562
+ elif report ["crash_test_result" ]["crash_report" ] is None :
563
+ self .logger .info (
564
+ "No crash report found in the new crash test result, falling back on the original crash test result for job_id"
565
+ "Note: if using --include_setup, the downloaded fuzzer binaries may be out-of-date"
566
+ )
567
+
568
+ original_report = report ["original_crash_test_result" ]["crash_report" ]
569
+ new_report = (
570
+ report ["crash_test_result" ]["crash_report" ] or original_report
571
+ ) # fallback on original_report
572
+
573
+ crash_info ["input_blob_container" ] = original_report ["input_blob" ][
574
+ "container"
575
+ ]
576
+ crash_info ["input_blob_name" ] = original_report ["input_blob" ]["name" ]
577
+ crash_info ["job_id" ] = new_report ["job_id" ]
578
+ else :
579
+ self .logger .error (
580
+ "Encountered an unhandled report format, repro files cannot be retrieved"
581
+ )
582
+ return
583
+
584
+ self .logger .info (
585
+ "downloading files necessary to locally repro crash %s" ,
586
+ crash_info ["input_blob_name" ],
587
+ )
588
+ self .onefuzz .containers .files .download (
589
+ primitives .Container (crash_info ["input_blob_container" ]),
590
+ crash_info ["input_blob_name" ],
591
+ os .path .join (output_dir , crash_info ["input_blob_name" ]),
592
+ )
593
+
594
+ if include_setup :
595
+ setup_container = list (
596
+ self .onefuzz .jobs .containers .list (
597
+ crash_info ["job_id" ], enums .ContainerType .setup
598
+ )
599
+ )[0 ]
600
+
601
+ self .onefuzz .containers .files .download_dir (
602
+ primitives .Container (setup_container ), output_dir
603
+ )
604
+
605
+
531
606
class Notifications (Endpoint ):
532
607
"""Interact with models.Notifications"""
533
608
@@ -1588,6 +1663,7 @@ def __init__(
1588
1663
client_secret = client_secret ,
1589
1664
)
1590
1665
self .containers = Containers (self )
1666
+ self .repro = Repro (self )
1591
1667
self .notifications = Notifications (self )
1592
1668
self .tasks = Tasks (self )
1593
1669
self .jobs = Jobs (self )
0 commit comments