File size: 5,418 Bytes
cae7b7c | 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 | """
URLAZ Phishing Predictor CLI (P0 IDN Bias Fix Engine Enabled)
============================================================
Run interactive URL classification using:
- Tier 1: High-Confidence Allowlist Pre-Filter (Clean Official Domains)
- Tier 2: 38 Structural Feature LightGBM Model (P0 IDN Bias Fix Engine + UTS #39 Confusable Skeleton)
- Operational Threshold t = 0.95 (Exact FPR < 0.09%, Production Precision 52.46%)
"""
import os
import sys
import json
import joblib
import numpy as np
# OPSEC Clean Dynamic Relative Paths
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
BASE_DIR = os.path.dirname(SCRIPT_DIR)
DATA_DIR = os.path.join(BASE_DIR, "data")
sys.path.append(os.path.join(BASE_DIR, "scripts"))
from train_production_model_v6 import (
extract_features_v5,
check_allowlist_bypass,
extract_domain_from_url,
get_registered_domain,
detect_homoglyph_typosquatting,
decode_punycode_and_to_skeleton,
FEATURE_NAMES_V5,
FULL_ALLOWLIST_DOMAINS,
AZ_BRAND_PATTERNS
)
DIST_MODEL_PATH = os.path.join(BASE_DIR, "data_dist", "urlaz_phishing_detector.joblib")
DATA_MODEL_PATH = os.path.join(DATA_DIR, "az_phish_model_v6.joblib")
MODEL_PATH = DIST_MODEL_PATH if os.path.exists(DIST_MODEL_PATH) else DATA_MODEL_PATH
if not os.path.exists(MODEL_PATH):
raise FileNotFoundError(f"Model weights file not found at {MODEL_PATH}")
clf = joblib.load(MODEL_PATH)
OPERATIONAL_THRESHOLD = 0.95
def analyze_url(url: str) -> dict:
url_clean = str(url).strip()
domain = extract_domain_from_url(url_clean)
registered_domain = get_registered_domain(domain)
# Step 1: Check Allowlist Bypass
is_allowlisted = check_allowlist_bypass(url_clean)
if is_allowlisted:
risk_prob = 0.0
verdict = "BENIGN / SAFE π’"
decision_path = "Tier 1 Allowlist Pre-Filter (0ms Latency Bypass)"
key_signals = [f"Registered Domain '{registered_domain}' is in High-Confidence Allowlist"]
else:
# Step 2: Tier 2 ML Classifier (38 Features)
features = extract_features_v5(url_clean)
risk_prob = float(clf.predict_proba([features])[0][1])
if risk_prob >= OPERATIONAL_THRESHOLD:
verdict = "PHISHING / HIGH RISK π΄"
elif risk_prob >= 0.50:
verdict = "SUSPICIOUS / MEDIUM RISK π‘"
else:
verdict = "BENIGN / LOW RISK π’"
decision_path = f"Tier 2 LightGBM Classifier (Operational Threshold = {OPERATIONAL_THRESHOLD})"
# Identify key signals cleanly
key_signals = []
skeleton_dom = decode_punycode_and_to_skeleton(domain)
brand_matches = [b for b in AZ_BRAND_PATTERNS if b in skeleton_dom]
if brand_matches:
key_signals.append(f"Target Brand Keyword Match: {brand_matches}")
# Check Homoglyph / Typosquatting / Leetspeak Signal
typo_flag, typo_dist, is_homoglyph = detect_homoglyph_typosquatting(domain)
if is_homoglyph:
key_signals.append(f"UTS #39 Homoglyph / IDN Punycode Attack Detected in '{domain}' -> skeleton: '{skeleton_dom}'")
elif typo_flag > 0 and 1 <= typo_dist <= 2:
key_signals.append(f"Leetspeak / Typosquatting Impersonation Detected in '{domain}' (Edit Dist = {typo_dist})")
if domain != registered_domain and any(b in skeleton_dom for b in AZ_BRAND_PATTERNS):
key_signals.append(f"Subdomain Brand Spoofing detected in '{domain}'")
if any(domain.endswith(t) for t in ['.xyz', '.top', '.site', '.cc', '.fun', '.online', '.cfd', '.vip']):
key_signals.append(f"High-Risk TLD observed: '{domain.split('.')[-1]}'")
if len(url_clean) > 75:
key_signals.append(f"Suspiciously long URL ({len(url_clean)} chars)")
risk_score = int(round(risk_prob * 100))
return {
"url": url_clean,
"domain": domain,
"registered_domain": registered_domain,
"verdict": verdict,
"risk_score": risk_score,
"risk_probability": round(risk_prob, 4),
"decision_path": decision_path,
"key_signals": key_signals if key_signals else ["Normal structural patterns observed"]
}
def print_result(res: dict):
print("\n" + "β" * 70)
print(f" π URL ANALYSIS REPORT: {res['url']}")
print("β" * 70)
print(f" VERDICT : {res['verdict']}")
print(f" RISK SCORE (0-100) : {res['risk_score']} / 100 (Probability: {res['risk_probability']*100:.2f}%)")
print(f" DECISION PATH : {res['decision_path']}")
print(f" DOMAIN INFO : {res['domain']} (Registered: {res['registered_domain']})")
print(" KEY DETECTED SIGNALS:")
for sig in res['key_signals']:
print(f" β’ {sig}")
print("β" * 70 + "\n")
if __name__ == "__main__":
if len(sys.argv) > 1:
test_url = sys.argv[1]
res = analyze_url(test_url)
print_result(res)
else:
print("URLAZ Phishing Predictor CLI Ready. Enter a URL to analyze (or 'q' to quit):")
while True:
try:
inp = input("\nURL > ").strip()
if not inp or inp.lower() in ('q', 'exit', 'quit'):
break
res = analyze_url(inp)
print_result(res)
except (KeyboardInterrupt, EOFError):
break
|