import pandas as pd from typing import List import random from data_collection.collector import PhishingDataCollector from features.url_features import URLFeatureExtractor class DatasetBuilder: def __init__(self, urlhaus_key: str = None): self.collector = PhishingDataCollector(urlhaus_key=urlhaus_key) self.extractor = URLFeatureExtractor() def build_phishing_dataset(self, limit: int = None) -> pd.DataFrame: print("Fetching phishing URLs...") data = self.collector.collect() phishing_urls = data['all_phishing'] if limit: phishing_urls = phishing_urls[:limit] print(f"Extracting features from {len(phishing_urls)} phishing URLs...") features = self.extractor.extract_batch(phishing_urls) df = pd.DataFrame(features) df['url'] = phishing_urls df['label'] = 1 return df def build_legitimate_dataset(self, legitimate_urls: List[str]) -> pd.DataFrame: print(f"Extracting features from {len(legitimate_urls)} legitimate URLs...") features = self.extractor.extract_batch(legitimate_urls) df = pd.DataFrame(features) df['url'] = legitimate_urls df['label'] = 0 return df def build_full_dataset(self, legitimate_urls: List[str], limit: int = None) -> pd.DataFrame: phishing_df = self.build_phishing_dataset(limit=limit) legitimate_df = self.build_legitimate_dataset(legitimate_urls) df = pd.concat([phishing_df, legitimate_df], ignore_index=True) df = df.sample(frac=1, random_state=42).reset_index(drop=True) print(f"\nDataset built:") print(f" Total: {len(df)}") print(f" Phishing: {len(df[df['label'] == 1])}") print(f" Legitimate: {len(df[df['label'] == 0])}") return df def generate_typosquatting_urls(self, legitimate_urls: List[str], count: int = 200) -> List[str]: """Generate realistic fake typosquatting versions of real shops""" import random typos = [] # Subtle modifications that are hard to spot subtle_mods = [ # Character substitution (homoglyphs) lambda s: s.replace('a', 'а'), # Cyrillic а (looks identical) lambda s: s.replace('e', 'е'), # Cyrillic е lambda s: s.replace('o', 'о'), # Cyrillic о lambda s: s.replace('p', 'р'), # Cyrillic р lambda s: s.replace('c', 'с'), # Cyrillic с # Common typos lambda s: s.replace('a', 'aa', 1), lambda s: s.replace('e', 'ee', 1), lambda s: s.replace('n', 'nn', 1), lambda s: s.replace('l', '1', 1), lambda s: s.replace('o', '0', 1), lambda s: s.replace('i', 'j', 1), # Add subtle suffixes/prefixes lambda s: s + '-dk', lambda s: s + '-shop', lambda s: s + '-online', lambda s: 'secure-' + s, lambda s: 'login-' + s, # TLD swaps lambda s: s, # Will add different TLD ] used = set() attempts = 0 while len(typos) < count and attempts < count * 10: attempts += 1 base = random.choice(legitimate_urls) domain = base.replace('https://', '').replace('http://', '').replace('www.', '').split('.')[0] # Apply 1-2 subtle modifications mod = random.choice(subtle_mods) fake = mod(domain) # If no change happened, try another if fake == domain: fake = domain + random.choice(['dk', 'shop', 'store', 'online']) # Add realistic TLD tld = random.choice(['.dk', '.com', '.net', '.shop', '.store', '.online']) fake_url = f"https://www.{fake}{tld}" # Avoid duplicates if fake_url not in used and fake_url not in legitimate_urls: used.add(fake_url) typos.append(fake_url) return typos[:count] def build_synthetic_phishing_dataset(self, legitimate_urls: List[str], count: int = 200) -> pd.DataFrame: """Build phishing dataset from SYNTHETIC typosquatting URLs""" print(f"Generating {count} synthetic typosquatting URLs...") synthetic_urls = self.generate_typosquatting_urls(legitimate_urls, count=count) print(f"Extracting features from {len(synthetic_urls)} synthetic phishing URLs...") features = self.extractor.extract_batch(synthetic_urls) df = pd.DataFrame(features) df['url'] = synthetic_urls df['label'] = 1 print(f"Synthetic phishing dataset: {len(df)} samples") return df def build_full_dataset_with_synthetic(self, legitimate_urls: List[str], real_phishing_limit: int = None, synthetic_count: int = 200) -> pd.DataFrame: """ULTIMATE DATASET: Real phishing + Synthetic phishing + Legitimate""" real_phishing_df = self.build_phishing_dataset(limit=real_phishing_limit) synthetic_phishing_df = self.build_synthetic_phishing_dataset(legitimate_urls, count=synthetic_count) legitimate_df = self.build_legitimate_dataset(legitimate_urls) df = pd.concat([real_phishing_df, synthetic_phishing_df, legitimate_df], ignore_index=True) df = df.sample(frac=1, random_state=42).reset_index(drop=True) print(f"\n{'='*50}") print(f"FULL DATASET WITH SYNTHETIC DATA:") print(f" Total: {len(df)}") print(f" Real Phishing: {len(real_phishing_df)}") print(f" Synthetic Phishing: {len(synthetic_phishing_df)}") print(f" Legitimate: {len(legitimate_df)}") print(f"{'='*50}\n") return df SAMPLE_LEGITIMATE_URLS = [ "https://www.amazon.com", "https://www.ebay.com", "https://www.paypal.com", "https://www.apple.com", "https://www.microsoft.com", "https://www.google.com", "https://www.facebook.com", "https://www.instagram.com", "https://www.twitter.com", "https://www.linkedin.com", "https://www.youtube.com", "https://www.twitch.tv", "https://www.discord.com", "https://www.slack.com", "https://www.zoom.us", "https://www.dropbox.com", "https://www.github.com", "https://www.gitlab.com", "https://www.stackoverflow.com", "https://www.reddit.com", "https://www.pinterest.com", "https://www.tiktok.com", "https://www.snapchat.com", "https://www.whatsapp.com", "https://www.telegram.org", "https://www.signal.org", "https://www.mozilla.org", "https://www.opera.com", "https://www.brave.com", "https://www.vivaldi.com", "https://www.nike.com", "https://www.adidas.com", "https://www.zara.com", "https://www.walmart.com", "https://www.etsy.com", "https://www.shopify.com", "https://www.alibaba.com", "https://www.aliexpress.com", "https://www.rakuten.com", "https://www.wayfair.com", "https://www.overstock.com", "https://www.newegg.com", "https://www.bestbuy.com", "https://www.target.com", "https://www.costco.com", "https://www.homedepot.com", "https://www.lowes.com", "https://www.ikea.com", "https://www.chewy.com", "https://www.zappos.com", "https://www.asos.com", "https://www.boohoo.com", "https://www.shein.com", "https://www.fashionnova.com", "https://www.prettylittlething.com", "https://www.netflix.com", "https://www.spotify.com", "https://www.hulu.com", "https://www.disneyplus.com", "https://www.hbomax.com", "https://www.peacocktv.com", "https://www.paramountplus.com", "https://www.appletv.com", "https://www.crunchyroll.com", "https://www.funimation.com", "https://www.vudu.com", "https://www.tubitv.com", "https://www.pluto.tv", "https://www.roku.com", "https://www.steampowered.com", "https://www.playstation.com", "https://www.roblox.com", "https://www.xbox.com", "https://www.nintendo.com", "https://www.epicgames.com", "https://www.riotgames.com", "https://www.blizzard.com", "https://www.ubisoft.com", "https://www.ea.com", "https://www.activision.com", "https://www.bethesda.net", "https://www.gog.com", "https://www.humblebundle.com", "https://www.itch.io", "https://www.chase.com", "https://www.bankofamerica.com", "https://www.wellsfargo.com", "https://www.citi.com", "https://www.capitalone.com", "https://www.americanexpress.com", "https://www.discover.com", "https://www.usbank.com", "https://www.pnc.com", "https://www.td.com", "https://www.bbt.com", "https://www.suntrust.com", "https://www.schwab.com", "https://www.fidelity.com", "https://www.vanguard.com", "https://www.robinhood.com", "https://www.coinbase.com", "https://www.binance.com", "https://www.kraken.com", "https://www.gemini.com", "https://www.blockfi.com", "https://www.airbnb.com", "https://www.booking.com", "https://www.expedia.com", "https://www.hotels.com", "https://www.agoda.com", "https://www.trip.com", "https://www.kayak.com", "https://www.priceline.com", "https://www.trivago.com", "https://www.skyscanner.com", "https://www.google.com/travel", "https://www.tripadvisor.com", "https://www.uber.com", "https://www.lyft.com", "https://www.grab.com", "https://www.bolt.eu", "https://www.ola.com", "https://www.didiglobal.com", "https://www.bilka.dk", "https://www.foetex.dk", "https://www.salling.dk", "https://www.matas.dk", "https://www.elgiganten.dk", "https://www.power.dk", "https://www.proshop.dk", "https://www.komplett.dk", "https://www.coolshop.dk", "https://www.2trendy.dk", "https://www.zalando.dk", "https://www2.hm.com/da_dk", "https://www.boozt.com", "https://www.amazon.de", "https://www.ebay.de", "https://www.thomann.de", "https://www.bygma.dk", "https://www.silvan.dk", "https://www.stark.dk", "https://www.jemogfix.dk", "https://www.ikea.com/dk", "https://www.bauhaus.dk", "https://www.xl-byg.dk", "https://www.dba.dk", "https://www.guloggratis.dk", "https://www.qxl.dk", "https://www.telenor.dk", "https://www.telia.dk", "https://www.yousee.dk", "https://www.3.dk", "https://www.energinet.dk", "https://www.dsb.dk", "https://www.rejseplanen.dk", "https://www.sundhed.dk", "https://www.borger.dk", "https://www.skat.dk", "https://www.virk.dk", "https://www.cvr.dk", "https://www.dr.dk", "https://www.tv2.dk", "https://www.bt.dk", "https://www.ekstrabladet.dk", "https://www.politiken.dk", "https://www.information.dk", "https://www.berlingske.dk", "https://www.jyllands-posten.dk", "https://www.kristeligt-dagblad.dk", "https://www.alt.dk", "https://www.femina.dk", "https://www.seoghoer.dk", "https://www.billedbladet.dk", "https://www.tipsbladet.dk", "https://www.bold.dk", "https://www.transfermarkt.dk", "https://www.fck.dk", "https://www.bif.dk", "https://www.fcm.dk", "https://www.agf.dk", "https://www.aab.dk", "https://www.rfc.dk", "https://www.ob.dk", "https://www.sif.dk", "https://www.achorsens.dk", "https://www.hobroik.dk", "https://www.vff.dk", "https://www.lbk.dk", "https://www.sonderjyske.dk", "https://www.nordvestfc.dk", ] if __name__ == "__main__": builder = DatasetBuilder() synthetic = builder.generate_typosquatting_urls(SAMPLE_LEGITIMATE_URLS, count=10) print("Sample synthetic URLs:") for url in synthetic: print(f" {url}")