diff --git a/local-app/python-tools/cross-organization/assess_check_ecr.py b/local-app/python-tools/cross-organization/assess_check_ecr.py index d0b12c94..714eeb82 100755 --- a/local-app/python-tools/cross-organization/assess_check_ecr.py +++ b/local-app/python-tools/cross-organization/assess_check_ecr.py @@ -4,10 +4,11 @@ from collections import Counter, defaultdict # --- VERSIONING --- -__version__ = "1.3.4" +__version__ = "1.3.5" def find_latest_file(pattern): - files = glob.glob("audit_results.check_ecr.*.json") + """Locates the most recent file matching the given pattern.""" + files = glob.glob(pattern) return max(files, key=os.path.getctime) if files else None def get_days_ago(iso_str): @@ -28,20 +29,24 @@ def bucket_age(days, counters): else: counters['<30'] += 1 def main(): - parser = argparse.ArgumentParser(description="ECR Full Spectrum Assessor & Reporter") + parser = argparse.ArgumentParser(description="ECR Full Spectrum Assessor & Reporter - v1.3.5") parser.add_argument("--input", help="JSON audit file") args = parser.parse_args() - input_file = args.input or find_latest_file() - if not input_file: print("Error: No file found."); sys.exit(1) + # FIX: Pass the required 'pattern' argument to the function call + search_pattern = "audit_results.check_ecr.*.json" + input_file = args.input or find_latest_file(search_pattern) + + if not input_file: + print(f"Error: No file found matching {search_pattern}"); sys.exit(1) - # Generate the output filename + # Generate the output filename for the results file base_name, _ = os.path.splitext(input_file) output_file = f"{base_name}_results.txt" with open(input_file, 'r') as f: data = json.load(f) - # We use a custom print function to output to both screen and file + # Helper function to print to both screen and the results file report_file = open(output_file, 'w') def report_print(msg=""): print(msg) @@ -104,7 +109,7 @@ def report_print(msg=""): report_print(f"{idx:<4} | {account['account_id']:<15} | {region:<12} | {val['repo_name']:<40} | {repo_size_accum/(1024**3):<10.2f} | {repo_mut:<11} | {'YES' if has_lc else 'NO':<7} | {repo_vulns['CRITICAL']:<8} | {repo_vulns['HIGH']:<8}") - # Footers + # Footers & Summaries avg_img_mb = (stats["total_bytes"] / stats["total_images"]) / (1024**2) if stats["total_images"] > 0 else 0 avg_push_age = stats["total_push_days"] / stats["push_count"] if stats["push_count"] > 0 else 0 @@ -113,7 +118,7 @@ def report_print(msg=""): report_print(f" Total Data: {stats['total_bytes']/(1024**3):.2f} GB | Average Image Size: {avg_img_mb:.2f} MB") report_print(f" Security: " + " | ".join([f"{s}: {stats['org_vulns'][s]:,}" for s in ["CRITICAL", "HIGH", "MEDIUM"]])) - # Duplicate Audit Restored + # DUPLICATE IMAGE AUDIT SUMMARY duplicates = {d: c for d, c in digest_tracker.items() if c > 1} report_print(f"\n --- Duplicate Image Audit ---") report_print(f" Total Duplicated Instances: {sum(duplicates.values())} across {len(duplicates)} unique digests")