import os
import subprocess
import uuid
import datetime
import json
import errno
import platform
from typing import List, Any, Union, Tuple
try:
import psutil
except ImportError:
psutil = None
def run_gofang(url: str, args: str = "") -> Tuple[str, int]:
"""
Launch gofang web crawler against `url` with optional `args`,
writing JSON output to a timestamped file, and returns
the file path plus the scan process PID.
"""
executable = os.path.join("/usr/local/bin", "gofang.exe") if os.name == 'nt' else os.path.join("/usr/local/bin", "gofang")
output_dir = os.path.join(os.getcwd(), "gofang_output")
os.makedirs(output_dir, exist_ok=True)
ts = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
uid = uuid.uuid4().hex
filename = f"{ts}_{uid}.txt"
output_path = os.path.join(output_dir, filename)
args_list = args.split() if args else []
cmd = [executable, "-u", url] + args_list + ["-o", output_path]
if os.name == 'nt':
proc = subprocess.Popen(
cmd,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
)
else:
proc = subprocess.Popen(
cmd,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
start_new_session=True
)
pid = proc.pid
if psutil and platform.system() == "Windows":
try:
parent = psutil.Process(proc.pid)
for child in parent.children(recursive=True):
if child.name().lower().startswith("gofang"):
pid = child.pid
break
except Exception:
pass
return output_path, pid
def _is_process_running(pid: int) -> bool:
if psutil:
return psutil.pid_exists(pid)
try:
os.kill(pid, 0)
except OSError as e:
if getattr(e, 'errno', None) == errno.ESRCH:
return False
if getattr(e, 'errno', None) == errno.EPERM:
return True
return False
except ValueError:
return True
return True
def get_gofang_output(file_path: str, pid: int) -> Union[str, List[str]]:
"""
If the scan is still running (process exists or file incomplete),
returns a "still running" message. Once finished, reads the file
and returns the content lines.
"""
running = _is_process_running(pid)
if not os.path.isfile(file_path):
if running:
return f"Scan running with PID {pid}"
raise FileNotFoundError(
f"Output file not found (scan PID {pid} has exited): {file_path}"
)
if running:
return f"Scan still in progress... PID {pid}"
with open(file_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
return lines