diff --git a/README.md b/README.md index 2747b9c..92bff8e 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,17 @@ $ pipx install gitfive ``` It will automatically use venvs to avoid dependency conflicts with other projects. +## Docker + +```bash +$ docker build -t gitfive:latest . +$ docker run -it --name gitfive gitfive:latest python3 main.py login +$ docker start gitfive +$ docker exec -it gitfive python3 main.py user +``` + +**Note:** Using a named container (`--name gitfive`) allows you to persist credentials between runs. The `-it` flags are required for interactive input (login prompts). Use `docker start gitfive` to start the container, then `docker exec -it gitfive` to run commands in it. + # Usage First, login to GitHub *(preferably with a secondary account)* : ```bash diff --git a/dockerfile b/dockerfile new file mode 100644 index 0000000..3d36855 --- /dev/null +++ b/dockerfile @@ -0,0 +1,19 @@ +FROM python:3.11-slim + +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 + +WORKDIR /app + +RUN apt-get update && apt-get install -y \ + git \ + build-essential \ + && rm -rf /var/lib/apt/lists/* + +COPY requirements.txt . +RUN pip install --upgrade pip \ + && pip install -r requirements.txt + +COPY . . + +CMD ["python", "main.py"] \ No newline at end of file diff --git a/gitfive/lib/objects.py b/gitfive/lib/objects.py index 6654a7c..43190a3 100644 --- a/gitfive/lib/objects.py +++ b/gitfive/lib/objects.py @@ -4,7 +4,7 @@ from pathlib import Path from time import sleep from datetime import datetime - +import re from rich.console import Console from rich import print as rprint import httpx @@ -227,8 +227,11 @@ async def login(self, force=False): req = await self._as_client.get("https://github.com/sessions/two-factor/app", follow_redirects=False) body = bs(req.text, 'html.parser') form = body.find("form", {"action": "/sessions/two-factor"}) + if not form: + exit("[-] Could not find two-factor authentication form. Please try again.") authenticity_token = form.find("input", {"name": "authenticity_token"}).attrs["value"] - msg = form.find("div", {"class": "mt-3"}).text + msg_div = form.find("div", {"class": "mt-3"}) + msg = msg_div.text if msg_div else "Enter your two-factor authentication code" rprint(f'[bold]🗨️ Github :[/bold] [italic]"{msg}"') app_otp = pwinput("📱 Code => ") data = { @@ -307,7 +310,10 @@ async def login(self, force=False): req = await self._as_client.get("https://github.com/sessions/two-factor/app") body = bs(req.text, 'html.parser') authenticity_token = body.find("form", {"action": "/sessions/two-factor"}).find("input", {"name": "authenticity_token"}).attrs["value"] - msg = body.find("form", {"action": "/sessions/two-factor"}).find("div", {"class": "mt-3"}).text.strip().split("\n")[0] + twofa_form = body.find("form", attrs={"action": re.compile(r"/sessions/two-factor")}) + if not twofa_form: + twofa_form = body.find("form", id=re.compile(r"two-factor", re.I)) + msg = (twofa_form.get_text(" ", strip=True) if twofa_form else "Two-factor challenge").strip() rprint(f'[bold]🗨️ Github :[/bold] [italic]"{msg}"') otp = pwinput("📱 Code => ") data = { diff --git a/gitfive/lib/xray.py b/gitfive/lib/xray.py index 659d954..d016e67 100644 --- a/gitfive/lib/xray.py +++ b/gitfive/lib/xray.py @@ -42,9 +42,20 @@ def get_repo(token: str, target_username: str, target_id: int, repos_folder: Pat # Repo example to git clone : https://github.com/novitae/Aet-s-Tools - pass # In fact, it fails to checkout but commits are cloned, so we don't care. 💅 - - repo = Repo(repo_path) + # Check if repo_path exists (clone might have partially succeeded) + if not repo_path.exists(): + # Clone completely failed (e.g., disabled repo, 403 error, etc.) + return results + # In fact, it fails to checkout but commits are cloned, so we don't care. + repo = Repo(repo_path) + else: + # Other GitCommandError (e.g., 403 Forbidden for disabled repos) + # Return empty results if clone completely failed + return results + except Exception as error: + # Catch any other exceptions (e.g., NoSuchPathError if path doesn't exist) + # Return empty results if we can't access the repo + return results if not repo.refs: # Empty repo, no branch @@ -359,4 +370,7 @@ async def analyze_ext_contribs(runner: GitfiveRunner): if not name in runner.target.usernames_history[username]["names"]: runner.target._add_name(name) # Previous names are valid informations (unless target spoof it) runner.target.usernames_history[username]["names"][name] = {"repos": set()} - runner.target.usernames_history[username]["names"][name]["repos"].add(repo_name) \ No newline at end of file + try: + runner.target.usernames_history[username]["names"][name]["repos"].add(repo_name) + except KeyError as error: + continue