diff --git a/README.md b/README.md index a0c7fb6..be990c0 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,43 @@ A framework for gathering open-source intelligence on GitHub users, repositories - [x] All the above can be used with command-line arguments (PyPI Package only) - [x] ...And more +**Used the following implementation from [Somdev Sangwan](https://github.com/s0md3v)'s [Zen](https://github.com/s0md3v/zen) to get an email from a username** +```python +def findReposFromUsername(username): + response = get('https://api.github.com/users/%s/repos?per_page=100&sort=pushed' % username, auth=HTTPBasicAuth(uname, '')).text + repos = re.findall(r'"full_name":"%s/(.*?)",.*?"fork":(.*?),' % username, response) + nonForkedRepos = [] + for repo in repos: + if repo[1] == 'false': + nonForkedRepos.append(repo[0]) + return nonForkedRepos + +def findEmailFromContributor(username, repo, contributor): + response = get('https://github.com/%s/%s/commits?author=%s' % (username, repo, contributor), auth=HTTPBasicAuth(uname, '')).text + latestCommit = re.search(r'href="/%s/%s/commit/(.*?)"' % (username, repo), response) + if latestCommit: + latestCommit = latestCommit.group(1) + else: + latestCommit = 'dummy' + commitDetails = get('https://github.com/%s/%s/commit/%s.patch' % (username, repo, latestCommit), auth=HTTPBasicAuth(uname, '')).text + email = re.search(r'<(.*)>', commitDetails) + if email: + email = email.group(1) + if breach: + jsonOutput[contributor] = {} + jsonOutput[contributor]['email'] = email + else: + jsonOutput[contributor] = email + return email + +def findEmailFromUsername(username): + repos = findReposFromUsername(username) + for repo in repos: + email = findEmailFromContributor(username, repo, username) + if email: + print (username + ' : ' + email) + break +``` ## Note > Octosuite automatically logs network and user activity of each session, the logs are saved by date and time in the .logs folder diff --git a/octosuite/banner.py b/octosuite/banner.py index a635ad1..8771078 100644 --- a/octosuite/banner.py +++ b/octosuite/banner.py @@ -4,7 +4,7 @@ from octosuite.config import red, white, green, reset, Tree # banner.py # This file holds the program's banner and version tag -version_tag = "3.0.4" +version_tag = "3.1.0" def banner(): diff --git a/octosuite/config.py b/octosuite/config.py index 04c5700..0bd0054 100644 --- a/octosuite/config.py +++ b/octosuite/config.py @@ -124,7 +124,7 @@ def usage(): def create_parser(): parser = argparse.ArgumentParser(description='OCTOSUITE: Advanced GitHub osint framework — by Richard Mwewa | https://about.me/rly0nheart', usage=usage()) - parser.add_argument('-m', '--method', help='method', choices=['user_profile', 'user_repos', 'user_gists', 'user_orgs', 'user_events', + parser.add_argument('-m', '--method', help='method', choices=['user_email', 'user_profile', 'user_repos', 'user_gists', 'user_orgs', 'user_events', 'user_subscriptions', 'user_following', 'user_followers', 'user_follows', 'org_profile', 'org_repos', 'org_events', 'org_member', 'repo_profile', 'repo_contributors', 'repo_stargazers', 'repo_forks', diff --git a/octosuite/helper.py b/octosuite/helper.py index fbb8277..65f1166 100644 --- a/octosuite/helper.py +++ b/octosuite/helper.py @@ -67,6 +67,7 @@ def user_command(): user_cmd_table = Table(show_header=True, header_style=header_title) user_cmd_table.add_column("Command", style="dim") user_cmd_table.add_column("Description") + user_cmd_table.add_row("email", "Return a target's email") user_cmd_table.add_row("profile", "Get a target's profile info") user_cmd_table.add_row("gists", "Return a users's gists") user_cmd_table.add_row("org", "Return organizations that a target belongs to/owns") diff --git a/octosuite/octosuite.py b/octosuite/octosuite.py index f97a1d0..307847a 100644 --- a/octosuite/octosuite.py +++ b/octosuite/octosuite.py @@ -1,5 +1,6 @@ #!usr/bin/python +import re import os import sys import shutil @@ -9,6 +10,7 @@ import requests import platform import subprocess from datetime import datetime +from requests.auth import HTTPBasicAuth from octosuite.banner import version_tag, banner from octosuite.config import Tree, Text, Table, Prompt, Confirm, xprint, create_parser, args, red, white, green, yellow, header_title, reset from octosuite.message_prefixes import ERROR, WARNING, PROMPT, POSITIVE, NEGATIVE, INFO # wondering why I name all the variables instead of just using the * wildcard?, because it's the pythonic way lol @@ -29,7 +31,7 @@ if os.name == "nt": try: from pyreadline3 import Readline except ImportError: - subprocess.run(['pip3', 'install', 'pyreadline3']) + subprocess.run(['pip3', 'install', 'pyreadline3'], shell=False) readline = Readline() else: import readline @@ -199,7 +201,7 @@ An advanced and lightning fast framework for gathering open-source intelligence Whats new in v{version_tag}? -[{green}FIXED{reset}] Merged pull request from #9: bad indentation leading to reference before assignment error #9 +[{green}IMPROVED{reset}] Added a subcommand to the 'user' commands, that will be used to get a user's email (user:email) Read the wiki: https://github.com/bellingcat/octosuite/wiki GitHub REST API documentation: https://docs.github.com/rest @@ -207,6 +209,25 @@ GitHub REST API documentation: https://docs.github.com/rest xprint(about_text) +def get_email_from_contributor(username, repo, contributor): + json_output = {} + response = requests.get(f"https://github.com/{username}/{repo}/commits?author={contributor}", + auth=HTTPBasicAuth(username, '')).text + latest_commit = re.search(rf'href="/{username}/{repo}/commit/(.*?)"', response) + if latest_commit: + latest_commit = latest_commit.group(1) + else: + latest_commit = 'dummy' + commit_details = requests.get(f"https://github.com/{username}/{repo}/commit/{latest_commit}.patch", + auth=HTTPBasicAuth(username, '')).text + email = re.search(r'<(.*)>', commit_details) + if email: + email = email.group(1) + json_output[contributor] = {} + json_output[contributor] = email + return email + + class Octosuite: def __init__(self): # API endpoint @@ -243,6 +264,7 @@ class Octosuite: ("repo:issues", self.repo_issues), ("repo:releases", self.repo_releases), ("user", user), + ("user:email", self.get_user_email), ("user:repos", self.user_repos), ("user:gists", self.user_gists), ("user:orgs", self.user_orgs), @@ -271,6 +293,7 @@ class Octosuite: # Arguments map will be used to run Octosuite with argparse self.argument_map = [("user_profile", self.user_profile), + ("user_email", self.get_user_email), ("user_repos", self.user_repos), ("user_gists", self.user_gists), ("user_orgs", self.user_orgs), @@ -510,6 +533,28 @@ class Octosuite: 'About.me': 'https://about.me/rly0nheart', 'Buy Me A Coffee': 'https://buymeacoffee.com/189381184'} + def get_repos_from_username(self, username): + response = requests.get(f"{self.endpoint}/users/{username}/repos?per_page=100&sort=pushed", + auth=HTTPBasicAuth(username, '')).text + repositories = re.findall(rf'"full_name":"{username}/(.*?)",.*?"fork":(.*?),', response) + unforked_repos = [] + for repository in repositories: + if repository[1] == 'false': + unforked_repos.append(repository[0]) + return unforked_repos + + def get_user_email(self): + if args.username: + username = args.username + else: + username = Prompt.ask(f"{white}@{green}Username{reset}") + repos = self.get_repos_from_username(username) + for repo in repos: + email = get_email_from_contributor(username, repo, username) + if email: + xprint(f"{username}: {email}") + break + # Fetching organization info def org_profile(self): if args.organization: diff --git a/setup.py b/setup.py index d1830dc..2327aeb 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as file: setuptools.setup( name="octosuite", - version="3.0.4", + version="3.1.0", author="Richard Mwewa", author_email="rly0nheart@duck.com", packages=["octosuite"],