File size: 6,994 Bytes
b3a532e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
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())