Spaces:
Runtime error
Runtime error
| import os | |
| import sys | |
| import time | |
| import asyncio | |
| import torch | |
| import torch.nn.functional as F | |
| import timm | |
| from torchvision import transforms | |
| from PIL import Image | |
| from playwright.async_api import async_playwright | |
| class ProductionAnalyzer: | |
| def __init__(self, model_path="models/production_resnet_ema.pth", model_name="resnet18", num_classes=2): | |
| """ | |
| Initializes the ResNet18 production model on available hardware. | |
| """ | |
| self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") | |
| print(f"🖥️ Initializing backend hardware: {self.device}") | |
| # 1. Initialize the ResNet18 architecture base | |
| print(f"🏗️ Building network architecture: {model_name}...") | |
| self.model = timm.create_model(model_name, pretrained=False, num_classes=num_classes) | |
| # 2. Safety check for the weights file | |
| if not os.path.exists(model_path): | |
| raise FileNotFoundError(f"❌ Could not find weight file at: {model_path}\n" | |
| f"Please ensure it is placed inside the 'models' folder.") | |
| print(f"📥 Loading ResNet18 weights from {model_path}...") | |
| state_dict = torch.load(model_path, map_location=self.device, weights_only=True) | |
| # 3. 🛠️ FIXED: Strip wrapper prefixes and safely drop training metadata keys | |
| clean_state_dict = {} | |
| for key, value in state_dict.items(): | |
| if key == "n_averaged": | |
| continue # Skip the training counter metadata so PyTorch doesn't throw an error | |
| clean_key = key.replace('module.', '') | |
| clean_state_dict[clean_key] = value | |
| self.model.load_state_dict(clean_state_dict) | |
| # 4. Lock the model for evaluation mode | |
| self.model.eval() | |
| self.model.to(self.device) | |
| print("✅ ResNet18 Engine successfully loaded and locked for inference.") | |
| # 5. Define standard normalizations expected by ResNet18 | |
| self.transform = transforms.Compose([ | |
| transforms.Resize((224, 224)), | |
| transforms.ToTensor(), | |
| transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), | |
| ]) | |
| # Alphabetical class mapping (0: Legit, 1: Phishing) | |
| self.class_names = ["Legit", "Phishing"] | |
| def analyze_image(self, image_path): | |
| """ | |
| Feeds the captured screenshot into the ResNet18 neural network. | |
| """ | |
| try: | |
| image = Image.open(image_path).convert('RGB') | |
| input_tensor = self.transform(image).unsqueeze(0).to(self.device) | |
| with torch.no_grad(): | |
| logits = self.model(input_tensor) | |
| probabilities = F.softmax(logits[0], dim=0) | |
| confidence, predicted_idx = torch.max(probabilities, 0) | |
| return { | |
| "prediction": self.class_names[predicted_idx.item()], | |
| "confidence": f"{confidence.item() * 100:.2f}%" | |
| } | |
| except Exception as e: | |
| return {"error": f"Model inference failed: {str(e)}"} | |
| async def capture_screenshot(url, output_path="temp_inference.png"): | |
| """ | |
| Launches a headless browser to safely capture a screenshot of the live URL. | |
| """ | |
| if not url.startswith('http://') and not url.startswith('https://'): | |
| url = 'https://' + url | |
| print(f"🌐 Navigating to: {url} ...") | |
| async with async_playwright() as p: | |
| browser = await p.chromium.launch(headless=True) | |
| context = await browser.new_context( | |
| viewport={'width': 1280, 'height': 720}, | |
| ignore_https_errors=True, # Bypasses broken SSL certifications on malicious sites | |
| accept_downloads=False | |
| ) | |
| page = await context.new_page() | |
| try: | |
| # 12-second timeout to handle slow/malicious servers | |
| await page.goto(url, timeout=12000, wait_until='domcontentloaded') | |
| await asyncio.sleep(1) # Brief pause to let visual components load completely | |
| await page.screenshot(path=output_path) | |
| return output_path | |
| except Exception as e: | |
| print(f"❌ Failed to reach or capture the website: {e}") | |
| return None | |
| finally: | |
| await page.close() | |
| await browser.close() | |
| async def main(): | |
| print("=" * 45) | |
| print("🛡️ CHIMERA 2.0 LIVE URL DETECTOR ENGINE") | |
| print("=" * 45) | |
| try: | |
| analyzer = ProductionAnalyzer() | |
| except Exception as e: | |
| print(e) | |
| return | |
| TEMP_IMG = "temp_inference.png" | |
| try: | |
| while True: | |
| print("\n" + "-" * 45) | |
| url_input = input("🔗 Enter URL to inspect (or type 'exit' to quit): ").strip() | |
| if url_input.lower() == 'exit': | |
| print("Shutting down engine...") | |
| break | |
| if not url_input: | |
| continue | |
| start_time = time.time() | |
| # Step 1: Capture screenshot via Playwright | |
| screenshot_file = await capture_screenshot(url_input, TEMP_IMG) | |
| if screenshot_file and os.path.exists(screenshot_file): | |
| # Step 2: Pass screenshot into ResNet18 | |
| print("🔍 Running Deep Learning Visual Inspection...") | |
| result = analyzer.analyze_image(screenshot_file) | |
| total_time = time.time() - start_time | |
| # Step 3: Output results safely | |
| print("\n" + "=" * 35) | |
| print("📊 LIVE DETECTION REPORT") | |
| print("=" * 35) | |
| if "error" in result: | |
| print(f"Result: {result['error']}") | |
| else: | |
| status_prefix = "🚨 ALERT!!" if result['prediction'] == "Phishing" else "✅ CLEAR:" | |
| print(f"Verdict : {status_prefix} {result['prediction']}") | |
| print(f"Confidence : {result['confidence']}") | |
| print(f"Total Time : {total_time:.2f} seconds") | |
| print("=" * 35) | |
| if os.path.exists(TEMP_IMG): | |
| os.remove(TEMP_IMG) | |
| else: | |
| print("❌ Inspection aborted. Visual fingerprint could not be gathered.") | |
| except KeyboardInterrupt: | |
| print("\nExiting execution gracefully...") | |
| finally: | |
| if os.path.exists(TEMP_IMG): | |
| os.remove(TEMP_IMG) | |
| if __name__ == "__main__": | |
| if sys.platform == 'win32': | |
| asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) | |
| asyncio.run(main()) |