#!/usr/bin/env python3 """Analyze WordPress plugins for security vulnerabilities using security ratings.""" import argparse import json import sys from datetime import datetime, timezone from pathlib import Path # Import security_rating functions from security_rating import get_plugin_info, calculate_security_score def parse_args() -> argparse.Namespace: """Parse command-line arguments. Returns: Parsed arguments namespace. """ parser = argparse.ArgumentParser( description="Analyze WordPress plugins for security issues using security ratings." ) parser.add_argument( "--input", "-i", required=True, help="Path to input JSON file (from output_recursive_plugins)" ) parser.add_argument( "--output", "-o", help="Path to output JSON file (if not specified, prints to stdout)", default=None ) parser.add_argument( "--detector-name", "-d", help="Name of detector to use in output (default: Security Rating Analyzer)", default="Security Rating Analyzer" ) return parser.parse_args() def analyze_plugin(slug: str) -> dict: """Analyze a single plugin and return its status. Args: slug: Plugin slug to analyze. Returns: Dict with status and reason fields. """ plugin_info = get_plugin_info(slug) if not plugin_info: return { "status": "unknown", "reason": f"Could not fetch plugin information from WordPress.org API" } score = calculate_security_score(plugin_info) # Determine status based on score # Safe: score >= 65 (Excellent or Good) # Vulnerable: score < 65 (Moderate or Poor) if score >= 85: status = "safe" reason = f"Excellent security rating ({score}/100) — Actively maintained and trusted" elif score >= 65: status = "safe" reason = f"Good security rating ({score}/100) — Generally safe but monitor updates" elif score >= 45: status = "vulnerable" reason = f"Moderate security rating ({score}/100) — May be outdated or less popular" else: status = "vulnerable" reason = f"Poor security rating ({score}/100) — High risk of security issues" result = { "status": status, "reason": reason, "score": score } # Add version if available version = plugin_info.get("version") if version: result["version"] = version return result def analyze_directory(directory_name: str, plugins_dict: dict, detector_name: str) -> dict: """Analyze all plugins in a directory. Args: directory_name: Name of the directory being analyzed. plugins_dict: Dict mapping plugin slug to absolute path. detector_name: Name of the detector. Returns: Analysis result in the specified format. """ timestamp = datetime.now(timezone.utc).isoformat() analyzed_plugins = {} print(f"\nAnalyzing plugins for directory: {directory_name}") print(f"Found {len(plugins_dict)} plugins to analyze") for slug, path in plugins_dict.items(): print(f" Analyzing {slug}...", end=" ", flush=True) result = analyze_plugin(slug) analyzed_plugins[slug] = result print(f"{result['status']}") return { "directory": directory_name, "wordpress": True, "name_of_detector": detector_name, "timestamp": timestamp, "plugins": analyzed_plugins } def main(): """Main entry point for the plugin analyzer.""" args = parse_args() # Load input JSON try: with open(args.input, "r", encoding="utf-8") as f: input_data = json.load(f) except FileNotFoundError: print(f"Error: Input file '{args.input}' not found.", file=sys.stderr) sys.exit(1) except json.JSONDecodeError as e: print(f"Error: Invalid JSON in '{args.input}': {e}", file=sys.stderr) sys.exit(1) # Analyze each directory results = [] for directory_name, plugins_dict in input_data.items(): if not plugins_dict: print(f"Skipping {directory_name} (no plugins found)") continue analysis = analyze_directory(directory_name, plugins_dict, args.detector_name) results.append(analysis) # Output results if args.output: with open(args.output, "w", encoding="utf-8") as f: json.dump(results, f, ensure_ascii=False, indent=2) print(f"\nWrote analysis results to {args.output}") else: print("\nAnalysis results:") print(json.dumps(results, ensure_ascii=False, indent=2)) if __name__ == "__main__": main()