Disable Ren'Py Save Protection
Disable Ren'Py Save Protection
py
import os
import re
from typing import Callable, Dict, Any, Optional
import logging
logger = [Link](__name__)
def safe_read_file(path: str, read_bytes: bool = False, max_size: int = 1024) ->
Optional[Any]:
"""Safely read a file with error handling."""
try:
mode = 'rb' if read_bytes else 'r'
with open(path, mode) as f:
return [Link](max_size)
except (IOError, OSError) as e:
[Link](f"Cannot read file {path}: {str(e)}")
return None
# Rule definitions
GDPR_RULES = {
"DATA_ENCRYPTION": {
"description": "Unencrypted sensitive data detected",
"check": check_data_encryption,
"recommendation": "Encrypt using AES-256",
"severity": "High"
},
"ACCESS_CONTROL": {
"description": "Insecure file permissions",
"check": check_access_control,
"recommendation": "Set permissions to 600",
"severity": "Medium"
}
}
HIPAA_RULES = {
"PHI_PROTECTION": {
"description": "PHI data exposure",
"check": check_phi_protection,
"recommendation": "Remove/encrypt PHI",
"severity": "Critical"
}
}
PCI_DSS_RULES = {
"CARDHOLDER_DATA": {
"description": "Cardholder data in plaintext",
"check": check_cardholder_data,
"recommendation": "Encrypt data",
"severity": "High"
}
}
NIST_RULES = {
"PASSWORD_COMPLEXITY": {
"description": "Weak password policy",
"check": check_password_complexity,
"recommendation": "Enforce NIST guidelines",
"severity": "Medium"
}
}
#file_tracker.py
import os
import json
import time
from typing import List, Dict
class FileTracker:
"""
FileTracker maintains a cache of file modification times to support incremental
scans.
The cache is stored as a JSON file in the provided cache directory.
"""
def __init__(self, cache_dir: str):
self.cache_dir = cache_dir
if not [Link](self.cache_dir):
[Link](self.cache_dir, exist_ok=True)
self.cache_file = [Link](self.cache_dir, "file_cache.json")
self.file_cache: Dict[str, float] = self._load_cache()
#helper_utils.py
import os
import sys
import platform
import re
import json
import logging
from typing import Dict, List, Optional, Any, Tuple
logger = [Link](__name__)
return text
try:
with open(requirements_file, "r") as f:
for line in f:
line = [Link]()
if not line or [Link]("#"):
continue
return packages
try:
if [Link](config_file):
with open(config_file, "r") as f:
config = [Link](f)
else:
[Link](f"Config file not found: {config_file}")
except Exception as e:
[Link](f"Error loading config file: {str(e)}")
return config
return 'Unknown'
#password_checker.py
import os
import re
import logging
from typing import List, Dict, Any
logger = [Link](__name__)
class PasswordChecker:
def __init__(self, directory: str):
[Link] = directory
try:
# Skip binary files and large files
if [Link](file_path) > 10 * 1024 * 1024: # 10MB
[Link](f"Skipping large file: {file_path}")
return []
if not self._is_strong_password(password):
sensitive_info.append({
"file": file_path,
"line": line_num,
"type": credential_type,
"value": password[:3] + "***" + password[-2:] if
len(password) > 5 else "***",
# Mask most of the password
"is_strong": False
})
except Exception as e:
[Link](f"Error processing file {file_path}: {str(e)}")
return sensitive_info
if not [Link]([Link]):
[Link](f"Directory not found: {[Link]}")
return [{"error": f"Directory '{[Link]}' not found"}]
try:
# First pass: check known password files
for root, _, files in [Link]([Link]):
for file in files:
file_path = [Link](root, file)
if self._is_password_file(file_path):
[Link](f"Checking password file: {file_path}")
sensitive_entries = self._detect_sensitive_info(file_path)
except Exception as e:
[Link](f"Error during password check: {str(e)}")
[Link]({"error": f"Error during password check: {str(e)}"})
#permission_checker.py
import os
import re
import logging
from typing import List, Dict, Any
logger = [Link](__name__)
class PasswordChecker:
def __init__(self, directory: str):
[Link] = directory
try:
# Skip binary files and large files
if [Link](file_path) > 10 * 1024 * 1024: # 10MB
[Link](f"Skipping large file: {file_path}")
return []
if not self._is_strong_password(password):
sensitive_info.append({
"file": file_path,
"line": line_num,
"type": credential_type,
"value": password[:3] + "***" + password[-2:] if
len(password) > 5 else "***",
# Mask most of the password
"is_strong": False
})
except Exception as e:
[Link](f"Error processing file {file_path}: {str(e)}")
return sensitive_info
if not [Link]([Link]):
[Link](f"Directory not found: {[Link]}")
return [{"error": f"Directory '{[Link]}' not found"}]
try:
# First pass: check known password files
for root, _, files in [Link]([Link]):
for file in files:
file_path = [Link](root, file)
if self._is_password_file(file_path):
[Link](f"Checking password file: {file_path}")
sensitive_entries = self._detect_sensitive_info(file_path)
except Exception as e:
[Link](f"Error during password check: {str(e)}")
[Link]({"error": f"Error during password check: {str(e)}"})
#import csv
import json
import os
from typing import Dict, List, Optional
from [Link] import letter
from [Link] import colors
from [Link] import SimpleDocTemplate, Table, TableStyle, Paragraph
from [Link] import getSampleStyleSheet
from colorama import Fore, Style
from helper_utils import sanitize_output
class ReportGenerator:
@staticmethod
def _ensure_dir(output_file: str):
"""Ensure the output directory exists."""
if output_file:
[Link]([Link](output_file), exist_ok=True)
@staticmethod
def _get_rule(item: Dict) -> str:
"""Get the rule name from an issue item."""
return [Link]("rule", "Unknown Rule")
@staticmethod
def _get_severity(issue: str) -> str:
"""Categorize issue severity."""
critical_issues = ["Encryption Failure", "Weak Password", "Unpatched
Vulnerability"]
moderate_issues = ["Access Control Violation", "Outdated Software"]
return "Critical" if issue in critical_issues else "Moderate" if issue in
moderate_issues else "Low"
@staticmethod
def generate_csv(issues: List[Dict], output_file: str):
"""Generate a CSV report."""
ReportGenerator._ensure_dir(output_file)
with open(output_file, "w", newline="", encoding="utf-8") as file:
[Link](ReportGenerator.generate_csv_string(issues))
@staticmethod
def generate_csv_string(issues: List[Dict]) -> str:
"""Generate CSV content as a string."""
from io import StringIO
output = StringIO()
writer = [Link](output)
[Link](["Type", "Item", "Issue", "Severity", "Rule",
"Recommendation", "Details"])
for item in issues:
details = [Link]("details", "N/A")
severity = ReportGenerator._get_severity([Link]("issue", "Unknown
Issue"))
[Link]([
"Software" if "package" in item else "File",
[Link]("package", [Link]("file", "N/A")),
[Link]("issue", "Unknown Issue"),
severity,
ReportGenerator._get_rule(item),
[Link]("recommendation", "N/A"),
details
])
return [Link]()
@staticmethod
def generate_json(issues: List[Dict], output_file: str):
"""Generate a JSON report."""
ReportGenerator._ensure_dir(output_file)
with open(output_file, "w", encoding="utf-8") as file:
[Link](ReportGenerator.generate_json_string(issues))
@staticmethod
def generate_json_string(issues: List[Dict]) -> str:
"""Generate JSON content as a string."""
report_data = {
"issues": issues,
"summary": {
"total_issues": len(issues),
"critical_issues": sum(
1 for i in issues if
ReportGenerator._get_severity([Link]("issue")) == "Critical"),
"moderate_issues": sum(
1 for i in issues if
ReportGenerator._get_severity([Link]("issue")) == "Moderate"),
"low_issues": sum(1 for i in issues if
ReportGenerator._get_severity([Link]("issue")) == "Low")
}
}
return [Link](report_data, indent=4)
@staticmethod
def generate_pdf(issues: List[Dict], output_file: str):
"""Generate a PDF report."""
ReportGenerator._ensure_dir(output_file)
doc = SimpleDocTemplate(output_file, pagesize=letter)
styles = getSampleStyleSheet()
story = []
# Title
[Link](Paragraph("Compliance Report", styles["Title"]))
# Summary
summary_text = f"Total Issues Found: {len(issues)}"
[Link](Paragraph(summary_text, styles["BodyText"]))
# Table data
table_data = [["Type", "Item", "Issue", "Severity", "Rule",
"Recommendation", "Details"]]
for item in issues:
details = [Link]("details", "N/A")
severity = ReportGenerator._get_severity([Link]("issue", "Unknown
Issue"))
table_data.append([
"Software" if "package" in item else "File",
[Link]("package", [Link]("file", "N/A")),
[Link]("issue", "Unknown Issue"),
severity,
ReportGenerator._get_rule(item),
[Link]("recommendation", "N/A"),
details
])
# Table style
table = Table(table_data)
[Link](TableStyle([
("BACKGROUND", (0, 0), (-1, 0), [Link]("#003366")),
("TEXTCOLOR", (0, 0), (-1, 0), [Link]),
("ALIGN", (0, 0), (-1, -1), "CENTER"),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, 0), 10),
("BOTTOMPADDING", (0, 0), (-1, 0), 12),
("BACKGROUND", (0, 1), (-1, -1), [Link]("#F0F8FF")),
("GRID", (0, 0), (-1, -1), 1, [Link]),
("WORDWRAP", (0, 0), (-1, -1)),
]))
[Link](table)
[Link](story)
@staticmethod
@staticmethod
def generate_text(issues: List[Dict], output_file: Optional[str] = None,
**kwargs) -> str:
report = [
f"{[Link]}=== Compliance Report ==={Style.RESET_ALL}",
f"{[Link]}Total Issues: {len(issues)}{Style.RESET_ALL}\n"
]
current_file = None
for item in issues:
if "file" in item:
if item["file"] != current_file:
[Link](f"\n{[Link]}File: {item['file']}
{Style.RESET_ALL}")
current_file = item["file"]
[Link]([
f"{[Link]}• Issue: {[Link]('issue', 'Unknown')}",
f" Severity: {[Link]('severity', 'Low')}",
f" Rule: {[Link]('rule', 'Unknown')}",
f" Recommendation: {[Link]('recommendation', 'N/A')}",
"-" * 50
])
report_str = "\n".join(report)
if output_file:
with open(output_file, "w") as f:
[Link](report_str.replace([Link], "").replace(Style.RESET_ALL,
""))
return f"Report saved to {output_file}"
return sanitize_output(report_str)
@staticmethod
def generate_report(issues: List[Dict], output_file: str, report_format: str =
"text"):
"""Unified report generation interface."""
report_format = report_format.lower() # Fix: Use the parameter name
correctly
if report_format == "csv":
ReportGenerator.generate_csv(issues, output_file)
elif report_format == "json":
ReportGenerator.generate_json(issues, output_file)
elif report_format == "pdf":
ReportGenerator.generate_pdf(issues, output_file)
elif report_format == "text":
ReportGenerator.generate_text(issues, output_file)
else:
raise ValueError(f"Unsupported format: {report_format}")
#[Link]
import os
import logging
from [Link] import ThreadPoolExecutor, as_completed
from tqdm import tqdm
from typing import Dict, List, Any
from compliance_rules import ALL_RULES
logger = [Link](__name__)
class FileScanner:
def __init__(self, directory: str, framework: str, max_workers: int = 4,
max_file_size: int =10 * 1024 * 1024):
[Link] = directory
[Link] = [Link]()
self.max_workers = max(1, min(max_workers, 16)) # Limit between 1 and 16
self.max_file_size = max_file_size # Default: 10MB
self.file_queue = []
# Validate framework
if [Link] not in ALL_RULES:
raise ValueError(
f"Unsupported framework: {[Link]}. Available frameworks:
{', '.join(ALL_RULES.keys())}")
[Link] = ALL_RULES[[Link]]
[Link](f"Initialized scanner for {[Link]} framework with
{self.max_workers} workers")
# Validate directory
if not [Link]([Link]):
[Link](f"Directory not found: {[Link]}")
return [{"error": f"Directory '{[Link]}' not found"}]
if not [Link]([Link]):
[Link](f"Not a directory: {[Link]}")
return [{"error": f"'{[Link]}' is not a directory"}]
if not self.file_queue:
[Link](f"No files found in {[Link]}")
return [{"warning": "No files found to scan"}]
return file_issues
#software_checker.py
import subprocess
import platform
import json
import logging
import re
from typing import Dict, List, Any, Optional, Tuple
from [Link] import parse as parse_version, Version, InvalidVersion
from tqdm import tqdm
logger = [Link](__name__)
class SoftwareChecker:
def __init__(self, verbose: bool = False):
[Link] = verbose
[Link] = {} # Cache for API responses
# Configure logger
if verbose:
[Link]([Link])
else:
[Link]([Link])
try:
# Get installed packages
packages = self.get_installed_software()
total_packages = len(packages)
if not packages:
[Link]("No software packages found to check")
return [{
"warning": "No software packages found to check",
"rule": "Software Updates",
"recommendation": "Ensure package managers are properly
installed"
}]
except Exception as e:
[Link](f"Error checking {name}: {str(e)}")
finally:
[Link](1)
except Exception as e:
[Link](f"Error during software check: {str(e)}")
return [{
"error": f"Error during software check: {str(e)}",
"rule": "Software Updates",
"recommendation": "Check logs for more details"
}]
return outdated_packages
if os_type == "Linux":
return self._get_linux_packages()
elif os_type == "Windows":
return self._get_windows_packages()
elif os_type == "Darwin": # macOS
return self._get_macos_packages()
else:
[Link](f"Unsupported OS: {os_type}")
return {}
if packages:
return packages
except Exception as e:
[Link](f"Error getting apt packages: {str(e)}")
if packages:
return packages
except Exception as e:
[Link](f"Error getting dpkg packages: {str(e)}")
if packages:
return packages
except Exception as e:
[Link](f"Error getting rpm packages: {str(e)}")
if packages:
return packages
except Exception as e:
[Link](f"Error getting pacman packages: {str(e)}")
pip_packages = [Link](output)
for pkg in pip_packages:
packages[pkg["name"]] = pkg["version"]
if packages:
return packages
except Exception as e:
[Link](f"Error getting pip packages: {str(e)}")
return packages
try:
# Use PowerShell to get installed applications
output = subprocess.check_output(
["powershell", "-Command",
"Get-WmiObject -Class Win32_Product | Select-Object Name, Version
| ConvertTo-Json"],
text=True,
encoding="utf-8",
errors="replace"
)
if [Link]():
try:
apps = [Link](output)
if isinstance(apps, dict): # Single app
packages[apps["Name"]] = apps["Version"]
else: # Multiple apps
for app in apps:
if app["Name"] and app["Version"]:
packages[app["Name"]] = app["Version"]
except Exception as e:
[Link](f"Error parsing Windows packages: {str(e)}")
except Exception as e:
[Link](f"Error getting Windows packages: {str(e)}")
return packages
if packages:
return packages
except Exception as e:
[Link](f"Error getting brew packages: {str(e)}")
apps_data = [Link](output)
if "SPApplicationsDataType" in apps_data:
for app in apps_data["SPApplicationsDataType"]:
if "_name" in app and "version" in app:
packages[app["_name"]] = app["version"]
except Exception as e:
[Link](f"Error getting macOS apps: {str(e)}")
return packages
except Exception as e:
[Link](f"PyPI check failed for {package_name}: {str(e)}")
data = [Link](output)
if "tag_name" in data:
version = data["tag_name"].lstrip("v")
return version, "GitHub"
except Exception as e:
[Link](f"GitHub check failed for {package_name}: {str(e)}")
if [Link]():
return [Link](), "NPM"
except Exception as e:
[Link](f"NPM check failed for {package_name}: {str(e)}")
return "Low"
except (InvalidVersion, AttributeError):
# If version parsing fails, assume medium severity
return "Medium"
#[Link]
import argparse
import os
import time
import sys
import logging
from typing import List, Dict, Any
from [Link] import ThreadPoolExecutor
from abc import ABC, abstractmethod
from colorama import Fore, Style, init
VERSION = "2.2.0"
CONFIG_FILE = "config/compliance_settings.json"
@abstractmethod
def check(self) -> List[Dict[str, Any]]:
"""Run the compliance check and return list of issues."""
pass
# Configure logging
def setup_logging(log_file: str = '[Link]', verbose: bool = False) -> None:
"""Set up logging configuration."""
log_level = [Link] if verbose else [Link]
[Link](
filename=log_file,
level=log_level,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
[Link]().addFilter(SanitizationFilter())
def print_status(message: str, color: str = [Link]) -> None:
"""Print status message with timestamp and color."""
safe_message = sanitize_output(message)
print(f"{color}[{[Link]('%H:%M:%S')}] {safe_message}")
# Scan options
scan_group = parser.add_argument_group("Scan options")
scan_group.add_argument("-s", "--scan", type=str, metavar="PATH",
help="Directory to scan")
scan_group.add_argument("-f", "--framework", type=str,
choices=["GDPR", "HIPAA", "PCI-DSS", "NIST", "SOC2",
"ISO27001"],
default="GDPR",
help="Compliance framework (default: GDPR)")
scan_group.add_argument("-t", "--threads", type=int, default=4,
help="Number of threads for scanning (default: 4)")
scan_group.add_argument("--exclude", type=str, metavar="PATTERNS",
help="Comma-separated patterns to exclude from
scanning")
scan_group.add_argument("--max-file-size", type=int, default=10,
help="Maximum file size to scan in MB (default: 10)")
scan_group.add_argument("--incremental", action="store_true",
help="Only scan files modified since last scan")
# Check options
check_group = parser.add_argument_group("Check options")
check_group.add_argument("-p", "--check-passwords", action="store_true",
help="Enable password checks")
check_group.add_argument("-u", "--check-outdated", action="store_true",
help="Check for outdated software packages")
check_group.add_argument("--check-permissions", action="store_true",
help="Check file and directory permissions")
check_group.add_argument("--check-all", action="store_true",
help="Enable all checks")
check_group.add_argument("--check-dependencies", action="store_true",
help="Check project dependencies against requirements
file")
check_group.add_argument("--min-severity", type=str,
choices=["info", "low", "medium", "high", "critical"],
default="low",
help="Minimum severity level to report (default:
low)")
# Output options
output_group = parser.add_argument_group("Output options")
output_group.add_argument("-o", "--output", type=str, metavar="FILE",
help="Output report file")
output_group.add_argument("-F", "--output-format", type=str,
choices=["csv", "json", "pdf", "text", "html",
"xml"],
default="text",
help="Report format (default: text)")
output_group.add_argument("--output-all", action="store_true",
help="Generate reports in all available formats")
output_group.add_argument("-v", "--verbose", action="store_true",
help="Show detailed processing information")
output_group.add_argument("--log-file", type=str,
default="logs/[Link]",
help="Log file location (default:
logs/[Link])")
output_group.add_argument("--version", action="version",
version=f"%(prog)s {VERSION}",
help="Show version information")
output_group.add_argument("--save-config", action="store_true",
help="Save current settings as default
configuration")
output_group.add_argument("--profile", type=str, default="default",
help="Configuration profile to use or save (default:
default)")
output_group.add_argument("--progress", action="store_true",
help="Show progress bar for long operations")
# Advanced options
advanced_group = parser.add_argument_group("Advanced options")
advanced_group.add_argument("--interactive", action="store_true",
help="Run in interactive mode")
advanced_group.add_argument("--debug", action="store_true",
help="Enable debug mode")
advanced_group.add_argument("--cache-dir", type=str, default=".cache",
help="Cache directory for incremental scans")
return parser.parse_args()
try:
if not [Link](requirements_file):
print_status(f"Requirements file not found: {requirements_file}",
[Link])
return issues
if verbose:
print_status(f"Found {len(packages)} required packages", [Link])
except Exception as e:
[Link](f"Error checking dependencies: {str(e)}")
print_status(f"Error checking dependencies: {str(e)}", [Link])
return issues
return file_types
if not scan_path:
print_status("No scan path specified. Use --scan to specify a directory.",
[Link])
[Link](1)
if not [Link](scan_path):
print_status(f"Path not found: {scan_path}", [Link])
[Link](1)
# Normalize path
scan_path = [Link](scan_path)
try:
# Get OS info for context
os_info = get_os_info()
[Link](f"Running on {os_info['system']} {os_info['release']}
{os_info['machine']}")
if args.check_passwords or args.check_all:
if [Link]:
print_status("Checking for password issues...", [Link])
from password_checker import PasswordChecker
checker = PasswordChecker(scan_path)
[Link]([Link](checker.check_passwords))
if args.check_permissions or args.check_all:
if [Link]:
print_status("Checking file and directory permissions...",
[Link])
from permission_checker import PermissionChecker
perm_checker = PermissionChecker(scan_path, [Link])
[Link]([Link](perm_checker.check_permissions))
if args.check_outdated or args.check_all:
if [Link]:
print_status("Checking for outdated software...", [Link])
from software_checker import SoftwareChecker
sw_checker = SoftwareChecker([Link])
[Link]([Link](sw_checker.check_outdated_software))
if args.check_dependencies or args.check_all:
[Link]([Link](check_project_dependencies,
"[Link]", [Link]))
# Collect results
for future in futures:
try:
result = [Link]()
if result:
all_issues.extend(result)
except Exception as e:
[Link](f"Error in compliance check: {str(e)}")
print_status(f"Error in compliance check: {str(e)}", [Link])
if args.min_severity:
severity_levels = {"info": 0, "low": 1, "medium": 2, "high": 3,
"critical": 4}
min_level = severity_levels.get(args.min_severity, 1)
all_issues = [issue for issue in all_issues if
severity_levels.get([Link]("severity", "low"), 1) >= min_level]
if [Link]:
tracker.update_file_cache(scan_path)
except KeyboardInterrupt:
print_status("\nOperation cancelled by user", [Link])
[Link](1)
except Exception as e:
[Link]("Critical error during checks")
print_status(f"Critical error: {str(e)}", [Link])
[Link](1)
return all_issues
def generate_all_reports(issues: List[Dict[str, Any]], base_filename: str, context:
Dict[str, Any]) -> None:
"""Generate reports in all available formats."""
from report_generator import ReportGenerator
check_all = False
if "6" in check_choice or not check_choice.strip():
check_all = True
else:
if "2" in check_choice:
[Link]("password")
if "3" in check_choice:
[Link]("permission")
if "4" in check_choice:
[Link]("software")
if "5" in check_choice:
[Link]("dependency")
formats = ["text", "csv", "json", "pdf", "html", "xml"]
print(f"{[Link]}Available output formats:{Style.RESET_ALL}")
for i, fmt in enumerate(formats, 1):
print(f" {i}. {fmt}")
format_choice = input(f"{[Link]}Select output format [1-{len(formats)}]
(default: 1): {Style.RESET_ALL}")
try:
format_idx = int(format_choice) - 1 if format_choice.strip() else 0
output_format = formats[format_idx]
except (ValueError, IndexError):
output_format = "text"
print(f"{[Link]}Invalid choice. Using default: {output_format}
{Style.RESET_ALL}")
output_file = input(f"{[Link]}Enter output file (leave empty for console
output): {Style.RESET_ALL}")
[Link] = scan_path
[Link] = framework
args.check_all = check_all
args.check_passwords = check_all or "password" in checks
args.check_permissions = check_all or "permission" in checks
args.check_outdated = check_all or "software" in checks
args.check_dependencies = check_all or "dependency" in checks
args.output_format = output_format
[Link] = output_file if output_file.strip() else None
start_time = [Link]()
try:
if not ([Link] or args.check_outdated):
print_status(
"No actions specified. Use --scan, --check-passwords, --check-
outdated, or --check-permissions",
[Link]
)
[Link](1)
os_info = get_os_info()
report_context = {
"os_info": os_info,
"tool_version": VERSION,
"timestamp": [Link]("%Y-%m-%d %H:%M:%S"),
"scan_duration": [Link]() - start_time,
"framework": [Link],
"scan_path": [Link],
"checks_performed": {
"file_scan": True if [Link] else False,
"password_check": args.check_passwords or args.check_all,
"permission_check": args.check_permissions or args.check_all,
"software_check": args.check_outdated or args.check_all,
"dependency_check": args.check_dependencies or args.check_all
}
}
if all_issues:
if [Link]:
if args.output_all:
base_filename = [Link]([Link])[0]
generate_all_reports(all_issues, base_filename, report_context)
else:
from report_generator import ReportGenerator
ReportGenerator.generate_report(all_issues, [Link],
args.output_format, context=report_context)
print_status(f"Report saved to {[Link]}", [Link])
else:
from report_generator import ReportGenerator
print(ReportGenerator.generate_text(all_issues,
context=report_context))
else:
print_status("No issues found. Compliance check passed!", [Link])
except KeyboardInterrupt:
print_status("\nOperation cancelled by user", [Link])
[Link]("Operation cancelled by user")
[Link](1)
except Exception as e:
[Link]("Critical error")
print_status(f"Critical error: {str(e)}", [Link])
[Link](1)
if __name__ == "__main__":
main()