"""Comprueba la existencia de un username en sitios publicos (sherlock ligero). OSINT pasivo: para un username dado, consulta la URL de perfil de una lista de sitios conocidos y deduce si la cuenta existe por el codigo de estado HTTP (200 = existe, 404 = no existe). Cada sitio se consulta de forma aislada: un fallo (timeout, error de red) no aborta el resto. """ import requests # Cada entrada describe un sitio: como construir la URL de perfil a partir # del username. La deteccion es por codigo de estado (200 existe / 404 no). DEFAULT_SITES = [ {"site": "github", "url": "https://github.com/{u}"}, {"site": "twitter", "url": "https://x.com/{u}"}, {"site": "instagram", "url": "https://www.instagram.com/{u}/"}, {"site": "tiktok", "url": "https://www.tiktok.com/@{u}"}, {"site": "reddit", "url": "https://www.reddit.com/user/{u}"}, {"site": "gitlab", "url": "https://gitlab.com/{u}"}, {"site": "keybase", "url": "https://keybase.io/{u}"}, {"site": "medium", "url": "https://medium.com/@{u}"}, {"site": "telegram", "url": "https://t.me/{u}"}, {"site": "youtube", "url": "https://www.youtube.com/@{u}"}, {"site": "pinterest", "url": "https://www.pinterest.com/{u}/"}, {"site": "about_me", "url": "https://about.me/{u}"}, ] _BROWSER_UA = ( "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" ) def enumerate_username_sites( username: str, timeout_s: float = 8.0, sites: list | None = None, ) -> list: """Comprueba si un username existe en una lista de sitios publicos. Para cada sitio construye la URL de perfil con el username y hace un GET con User-Agent de navegador siguiendo redirecciones. Interpreta el codigo de estado final: 200 -> existe, 404 -> no existe, cualquier otro -> indeterminado (exists=None). Los errores de red por sitio (timeout, conexion) se capturan y se reportan con status=None y exists=None sin interrumpir la enumeracion del resto. Args: username: nombre de usuario a buscar (sin arroba ni URL). timeout_s: timeout en segundos por peticion. Default 8.0. sites: lista opcional de dicts {"site", "url"} donde "url" contiene el placeholder "{u}". Si es None se usa DEFAULT_SITES. Returns: lista de dicts {"site", "url", "exists", "status"} en el mismo orden que la lista de sitios. "exists" es True/False/None y "status" es el codigo HTTP (int) o None si la peticion fallo. """ targets = sites if sites is not None else DEFAULT_SITES headers = {"User-Agent": _BROWSER_UA} results = [] for entry in targets: site = entry.get("site", "") url = entry["url"].format(u=username) status = None exists = None try: resp = requests.get( url, headers=headers, timeout=timeout_s, allow_redirects=True, ) status = resp.status_code if status == 200: exists = True elif status == 404: exists = False else: exists = None except requests.RequestException: # Timeout, error de conexion, demasiados redirects, etc. status = None exists = None results.append( {"site": site, "url": url, "exists": exists, "status": status} ) return results