763e06c127
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
119 lines
5.0 KiB
Python
119 lines
5.0 KiB
Python
"""Lanza Chromium en un perfil del catalogo osint_db y expone sus cuentas/secret_refs.
|
|
|
|
Wrapper que compone `browser_profile_show` (para leer la metadata del perfil y sus
|
|
cuentas desde el service osint_db) y luego lanza Chromium en ese perfil. Devuelve las
|
|
cuentas con sus `secret_ref` (REFERENCIAS a secretos, nunca el password) para que el
|
|
operador sepa que credenciales usar.
|
|
|
|
GOTCHAS de este entorno (Linux nativo de enmanuel):
|
|
- El wrapper `/usr/bin/chromium` ya inyecta `--user-data-dir=$HOME/.config/chromium-cdp`
|
|
y `--remote-debugging-port=9222` via `/etc/chromium.d/cdp`. Por eso, si el
|
|
`user_data_dir` resuelto ES ese default, NO se pasa `--user-data-dir` (se hereda del
|
|
wrapper); si es OTRO directorio, se pasa explicito.
|
|
- Lanzar chromium directamente como hijo da exit-144 en este entorno. Se lanza SIEMPRE
|
|
via `systemd-run --user --scope --` (proceso aislado), en background, sin esperar.
|
|
|
|
Funcion impura: hace red (HTTP al service) y lanza un proceso. No lanza excepciones;
|
|
devuelve un dict de estado. Con `dry_run=True` no abre nada (devuelve el comando).
|
|
"""
|
|
|
|
import os
|
|
import subprocess
|
|
|
|
from browser.browser_profile_show import browser_profile_show
|
|
|
|
# Default del wrapper /etc/chromium.d/cdp en esta maquina (se compara expandido).
|
|
_DEFAULT_USER_DATA_DIR = os.path.expanduser("~/.config/chromium-cdp")
|
|
|
|
|
|
def browser_profile_open(
|
|
profile_dir: str,
|
|
url: str | None = None,
|
|
base_url: str = "http://127.0.0.1:8771",
|
|
dry_run: bool = False,
|
|
) -> dict:
|
|
"""Lanza Chromium en el perfil indicado y devuelve sus cuentas/secret_refs.
|
|
|
|
Args:
|
|
profile_dir: nombre del directorio real del perfil Chromium (ej. "Profile 1",
|
|
"osint_01"). Debe existir en el catalogo osint_db.
|
|
url: URL a abrir al arrancar (ej. "https://mail.google.com"). None -> sin URL.
|
|
base_url: base del service osint_db. Default http://127.0.0.1:8771.
|
|
dry_run: si True, NO lanza nada; devuelve el comando que lanzaria. Util para test
|
|
y para revisar el comando antes de abrir el navegador.
|
|
|
|
Returns:
|
|
Caso dry_run ok: {"status":"ok", "profile_dir": str, "cmd": list[str] (argv que
|
|
se lanzaria), "accounts": list de dicts {service, identity, secret_ref, role}}.
|
|
Caso real ok: {"status":"ok", "profile_dir": str, "launched": True,
|
|
"cmd": list[str], "accounts": list de dicts {service, identity, secret_ref, role}}.
|
|
Caso perfil no existe / service caido: {"status":"error", "error": str} (no lanza).
|
|
"""
|
|
try:
|
|
meta = browser_profile_show(profile_dir, base_url=base_url)
|
|
if meta.get("status") != "ok":
|
|
# Perfil inexistente o service caido: propaga el error sin lanzar nada.
|
|
return meta
|
|
|
|
profile = meta.get("profile", {})
|
|
raw_accounts = meta.get("accounts", [])
|
|
accounts = [
|
|
{
|
|
"service": a.get("service"),
|
|
"identity": a.get("identity"),
|
|
"secret_ref": a.get("secret_ref"),
|
|
"role": a.get("role"),
|
|
}
|
|
for a in raw_accounts
|
|
]
|
|
|
|
# Resolver user_data_dir: el de la fila si no esta vacio; si no, el default del wrapper.
|
|
row_udd = (profile.get("user_data_dir") or "").strip()
|
|
resolved_udd = os.path.expanduser(row_udd) if row_udd else _DEFAULT_USER_DATA_DIR
|
|
|
|
chromium_args = ["chromium", f'--profile-directory={profile_dir}']
|
|
# Solo pasar --user-data-dir si NO es el default del wrapper (que ya lo inyecta).
|
|
if os.path.normpath(resolved_udd) != os.path.normpath(_DEFAULT_USER_DATA_DIR):
|
|
chromium_args.append(f"--user-data-dir={resolved_udd}")
|
|
if url:
|
|
chromium_args.append(url)
|
|
|
|
# Lanzamiento aislado para evitar exit-144 (ver gotcha del modulo).
|
|
cmd = ["systemd-run", "--user", "--scope", "--", *chromium_args]
|
|
|
|
if dry_run:
|
|
return {
|
|
"status": "ok",
|
|
"profile_dir": profile_dir,
|
|
"cmd": cmd,
|
|
"accounts": accounts,
|
|
}
|
|
|
|
# Background, sin esperar: no bloquear al operador ni capturar el navegador.
|
|
subprocess.Popen(
|
|
cmd,
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.DEVNULL,
|
|
start_new_session=True,
|
|
)
|
|
|
|
return {
|
|
"status": "ok",
|
|
"profile_dir": profile_dir,
|
|
"launched": True,
|
|
"cmd": cmd,
|
|
"accounts": accounts,
|
|
}
|
|
except Exception as e: # noqa: BLE001 - contrato: nunca lanzar
|
|
return {"status": "error", "error": f"{type(e).__name__}: {e}"}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# Smoke contra un puerto muerto: el service caido -> browser_profile_show falla,
|
|
# browser_profile_open propaga el error sin abrir navegador.
|
|
res = browser_profile_open("Profile 1", url="https://example.com",
|
|
base_url="http://127.0.0.1:1", dry_run=True)
|
|
assert res["status"] == "error", res
|
|
print("browser_profile_open smoke OK (service caido -> status error, sin lanzar)")
|
|
print(f" {res}")
|