mirror of
https://github.com/bellingcat/octosuite.git
synced 2026-06-08 03:18:35 +03:00
Update octosuite
This commit is contained in:
@@ -25,14 +25,14 @@ def usage():
|
||||
octosuite --method user_repos --username <username>
|
||||
|
||||
|
||||
Get Organi[sz]ation Profile Info
|
||||
Get Organisation Profile Info
|
||||
-----------------------------
|
||||
octosuite --method org_profile --organization <organization_name>
|
||||
octosuite --method org_profile --organisation <organisation_name>
|
||||
|
||||
|
||||
Get Organi[sz]ation Repos
|
||||
-----------------------------
|
||||
octosuite --method org_repos --organization <organization_name>
|
||||
octosuite --method org_repos --organisation <organisation_name>
|
||||
|
||||
|
||||
Get Repo Profile Info
|
||||
@@ -134,7 +134,7 @@ def create_parser():
|
||||
'clear_logs', 'view_csv', 'read_csv', 'delete_csv', 'clear_csv', 'about', 'author'])
|
||||
parser.add_argument('-u', '--username', help='username')
|
||||
parser.add_argument('-uB', '--username_b', help='username_B (used with user_follows)')
|
||||
parser.add_argument('-o', '--organization', '--organisation', help='organi[sz]ation name')
|
||||
parser.add_argument('-o', '--organisation', '--organization', help='organisation name')
|
||||
parser.add_argument('-r', '--repository', help='repository name')
|
||||
parser.add_argument('-p', '--path_name', help='path name (used with repo_path_contents)')
|
||||
parser.add_argument('-q', '--query', help='query (used with search methods)')
|
||||
|
||||
@@ -11,14 +11,14 @@ from octosuite.message_prefixes import PROMPT, WARNING, POSITIVE, NEGATIVE, INFO
|
||||
def log_org_profile(response):
|
||||
org_profile_fields = ['Profile photo', 'Name', 'Username', 'ID', 'Node ID', 'Email', 'About', 'Location', 'Blog',
|
||||
'Followers', 'Following', 'Twitter handle', 'Gists', 'Repositories', 'Account type',
|
||||
'Is verified?', 'Has organization projects?', 'Has repository projects?', 'Created at',
|
||||
'Is verified?', 'Has organisation projects?', 'Has repository projects?', 'Created at',
|
||||
'Updated at']
|
||||
org_profile_row = [response.json()['avatar_url'], response.json()['name'], response.json()['login'],
|
||||
response.json()['id'], response.json()['node_id'], response.json()['email'],
|
||||
response.json()['description'], response.json()['location'], response.json()['blog'],
|
||||
response.json()['followers'], response.json()['following'], response.json()['twitter_username'],
|
||||
response.json()['public_gists'], response.json()['public_repos'], response.json()['type'],
|
||||
response.json()['is_verified'], response.json()['has_organization_projects'],
|
||||
response.json()['is_verified'], response.json()['has_organisation_projects'],
|
||||
response.json()['has_repository_projects'], response.json()['created_at'],
|
||||
response.json()['updated_at']]
|
||||
|
||||
@@ -34,7 +34,7 @@ def log_org_profile(response):
|
||||
# Creating a .csv file of a user' profile
|
||||
def log_user_profile(response):
|
||||
user_profile_fields = ['Profile photo', 'Name', 'Username', 'ID', 'Node ID', 'Bio', 'Blog', 'Location', 'Followers',
|
||||
'Following', 'Twitter handle', 'Gists', 'Repositories', 'Organization', 'Is hireable?',
|
||||
'Following', 'Twitter handle', 'Gists', 'Repositories', 'organisation', 'Is hireable?',
|
||||
'Is site admin?', 'Joined at', 'Updated at']
|
||||
user_profile_row = [response.json()['avatar_url'], response.json()['name'], response.json()['login'],
|
||||
response.json()['id'], response.json()['node_id'], response.json()['bio'],
|
||||
@@ -184,12 +184,12 @@ def log_repo_contributors(contributor, repo_name):
|
||||
xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}")
|
||||
|
||||
|
||||
# Create .csv for organization' events
|
||||
def log_repo_events(event, organization):
|
||||
# Create .csv for organisation' events
|
||||
def log_repo_events(event, organisation):
|
||||
org_event_fields = ['ID', 'Type', 'Created at', 'Payload']
|
||||
org_event_row = [event['id'], event['type'], event['created_at'], event['payload']]
|
||||
|
||||
with open(os.path.join("output", f"{organization}_event_{event['id']}.csv"), 'w') as file:
|
||||
with open(os.path.join("output", f"{organisation}_event_{event['id']}.csv"), 'w') as file:
|
||||
write_csv = csv.writer(file)
|
||||
write_csv.writerow(org_event_fields)
|
||||
write_csv.writerow(org_event_row)
|
||||
@@ -198,8 +198,8 @@ def log_repo_events(event, organization):
|
||||
xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}")
|
||||
|
||||
|
||||
# Create .csv for organization' repositories
|
||||
def log_org_repos(repository, organization):
|
||||
# Create .csv for organisation' repositories
|
||||
def log_org_repos(repository, organisation):
|
||||
org_repo_fields = ['Name', 'ID', 'About', 'Forks', 'Stars', 'Watchers', 'License', 'Branch', 'Visibility',
|
||||
'Language(s)', 'Open issues', 'Topics', 'Homepage', 'Clone URL', 'SSH URL', 'Is fork?',
|
||||
'Is forkable?', 'Is private?', 'Is archived?', 'Is template?', 'Has wiki?', 'Has pages?',
|
||||
@@ -213,7 +213,7 @@ def log_org_repos(repository, organization):
|
||||
repository['has_projects'], repository['has_issues'], repository['has_downloads'],
|
||||
repository['pushed_at'], repository['created_at'], repository['updated_at']]
|
||||
|
||||
with open(os.path.join("output", f"{repository['name']}_repository_of_{organization}.csv"), 'w') as file:
|
||||
with open(os.path.join("output", f"{repository['name']}_repository_of_{organisation}.csv"), 'w') as file:
|
||||
write_csv = csv.writer(file)
|
||||
write_csv.writerow(org_repo_fields)
|
||||
write_csv.writerow(org_repo_row)
|
||||
@@ -335,13 +335,13 @@ def log_user_subscriptions(repository, username):
|
||||
xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}")
|
||||
|
||||
|
||||
# .csv for user organizations
|
||||
def log_user_orgs(organization, username):
|
||||
# .csv for user organisations
|
||||
def log_user_orgs(organisation, username):
|
||||
user_org_fields = ['Profile photo', 'Name', 'ID', 'Node ID', 'URL', 'About']
|
||||
user_org_row = [organization['avatar_url'], organization['login'], organization['id'], organization['node_id'],
|
||||
organization['url'], organization['description']]
|
||||
user_org_row = [organisation['avatar_url'], organisation['login'], organisation['id'], organisation['node_id'],
|
||||
organisation['url'], organisation['description']]
|
||||
|
||||
with open(os.path.join("output", f"{organization['login']}_{username}.csv"), 'w') as file:
|
||||
with open(os.path.join("output", f"{organisation['login']}_{username}.csv"), 'w') as file:
|
||||
write_csv = csv.writer(file)
|
||||
write_csv.writerow(user_org_fields)
|
||||
write_csv.writerow(user_org_row)
|
||||
|
||||
@@ -70,7 +70,7 @@ def user_command():
|
||||
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("orgs", "Return organizations that a target belongs to/owns")
|
||||
user_cmd_table.add_row("orgs", "Return organisations that a target belongs to/owns")
|
||||
user_cmd_table.add_row("repos", "Return a target's repositories")
|
||||
user_cmd_table.add_row("events", "Return a target's events")
|
||||
user_cmd_table.add_row("follows", "Check if user(A) follows user(B)")
|
||||
@@ -87,13 +87,13 @@ def org_command():
|
||||
org_cmd_table = Table(show_header=True, header_style=header_title)
|
||||
org_cmd_table.add_column("Command", style="dim")
|
||||
org_cmd_table.add_column("Description")
|
||||
org_cmd_table.add_row("profile", "Get a target organization' profile info")
|
||||
org_cmd_table.add_row("repos", "Return a target organization' repositories")
|
||||
org_cmd_table.add_row("events", "Return a target organization' events")
|
||||
org_cmd_table.add_row("member", "Check if a specified user is a public member of the target organization")
|
||||
org_cmd_table.add_row("profile", "Get a target organisation' profile info")
|
||||
org_cmd_table.add_row("repos", "Return a target organisation' repositories")
|
||||
org_cmd_table.add_row("events", "Return a target organisation' events")
|
||||
org_cmd_table.add_row("member", "Check if a specified user is a public member of the target organisation")
|
||||
|
||||
syntax = f"{green}org:<command>{reset}"
|
||||
xprint(f"{usage_text.format(syntax, 'organization investigation(s)')}")
|
||||
xprint(f"{usage_text.format(syntax, 'organisation investigation(s)')}")
|
||||
xprint(org_cmd_table)
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ def help_command():
|
||||
help_sub_cmd_table.add_column("Description")
|
||||
help_sub_cmd_table.add_row("csv", "List all csv management commands")
|
||||
help_sub_cmd_table.add_row("logs", "List all logs management commands")
|
||||
help_sub_cmd_table.add_row("org", "List all organization investigation commands")
|
||||
help_sub_cmd_table.add_row("org", "List all organisation investigation commands")
|
||||
help_sub_cmd_table.add_row("user", "List all users investigation commands")
|
||||
help_sub_cmd_table.add_row("repo", "List all repository investigation commands")
|
||||
help_sub_cmd_table.add_row("search", "List all target discovery commands")
|
||||
|
||||
@@ -14,7 +14,7 @@ file_downloading = "Downloading: {}"
|
||||
file_downloaded = "Downloaded: downloads/{}"
|
||||
info_not_found = "Information not found: {}, {}, {}"
|
||||
user_not_found = "User not found: @{}"
|
||||
org_not_found = "Organization not found: @{}"
|
||||
org_not_found = "organisation not found: @{}"
|
||||
repo_or_user_not_found = "Repository or User not found: {}, @{}"
|
||||
prompt_log_csv = "Would you like to log this output to a .csv file?"
|
||||
logged_to_csv = "Output logged: {}"
|
||||
|
||||
@@ -202,7 +202,7 @@ def about():
|
||||
about_text = f"""
|
||||
OCTOSUITE © 2023 Richard Mwewa
|
||||
|
||||
An advanced and lightning fast framework for gathering open-source intelligence on GitHub users and organizations.
|
||||
An advanced and lightning fast framework for gathering open-source intelligence on GitHub users and organisations.
|
||||
|
||||
Read the wiki: https://github.com/bellingcat/octosuite/wiki
|
||||
GitHub REST API documentation: https://docs.github.com/rest
|
||||
@@ -338,12 +338,12 @@ class Octosuite:
|
||||
'sha': 'SHA',
|
||||
'html_url': 'URL'}
|
||||
|
||||
# Organization attributes
|
||||
# organisation attributes
|
||||
self.org_attrs = ['avatar_url', 'login', 'id', 'node_id', 'email', 'description', 'blog', 'location',
|
||||
'followers',
|
||||
'following', 'twitter_username', 'public_gists', 'public_repos', 'type', 'is_verified',
|
||||
'has_organization_projects', 'has_repository_projects', 'created_at', 'updated_at']
|
||||
# Organization attribute dictionary
|
||||
'has_organisation_projects', 'has_repository_projects', 'created_at', 'updated_at']
|
||||
# organisation attribute dictionary
|
||||
self.org_attr_dict = {'avatar_url': 'Profile Photo',
|
||||
'login': 'Username',
|
||||
'id': 'ID',
|
||||
@@ -359,7 +359,7 @@ class Octosuite:
|
||||
'public_repos': 'Repositories',
|
||||
'type': 'Account type',
|
||||
'is_verified': 'Is verified?',
|
||||
'has_organization_projects': 'Has organization projects?',
|
||||
'has_organisation_projects': 'Has organisation projects?',
|
||||
'has_repository_projects': 'Has repository projects?',
|
||||
'created_at': 'Created at',
|
||||
'updated_at': 'Updated at'}
|
||||
@@ -435,7 +435,7 @@ class Octosuite:
|
||||
'twitter_username': 'Twitter Handle',
|
||||
'public_gists': 'Gists (public)',
|
||||
'public_repos': 'Repositories (public)',
|
||||
'company': 'Organization',
|
||||
'company': 'organisation',
|
||||
'hireable': 'Is hireable?',
|
||||
'site_admin': 'Is site admin?',
|
||||
'created_at': 'Joined at',
|
||||
@@ -519,7 +519,7 @@ class Octosuite:
|
||||
'created_at': 'Created at',
|
||||
'updated_at': 'Updated at'}
|
||||
|
||||
# User organizations attributes
|
||||
# User organisations attributes
|
||||
self.user_orgs_attrs = ['avatar_url', 'id', 'node_id', 'url', 'description']
|
||||
self.user_orgs_attr_dict = {'avatar_url': 'Profile Photo',
|
||||
'id': 'ID',
|
||||
@@ -555,15 +555,15 @@ class Octosuite:
|
||||
xprint(f"{username}: {email}")
|
||||
break
|
||||
|
||||
# Fetching organization info
|
||||
# Fetching organisation info
|
||||
def org_profile(self):
|
||||
if args.organization:
|
||||
organization = args.organization
|
||||
if args.organisation:
|
||||
organisation = args.organisation
|
||||
else:
|
||||
organization = Prompt.ask(f"{white}@{green}Organi[sz]ation{reset}")
|
||||
response = requests.get(f"{self.endpoint}/orgs/{organization}")
|
||||
organisation = Prompt.ask(f"{white}@{green}Organi[sz]ation{reset}")
|
||||
response = requests.get(f"{self.endpoint}/orgs/{organisation}")
|
||||
if response.status_code == 404:
|
||||
xprint(f"{NEGATIVE} {org_not_found.format(organization)}")
|
||||
xprint(f"{NEGATIVE} {org_not_found.format(organisation)}")
|
||||
elif response.status_code == 200:
|
||||
org_profile_tree = Tree("\n{response.json()['name']}")
|
||||
for attr in self.org_attrs:
|
||||
@@ -775,17 +775,17 @@ class Octosuite:
|
||||
else:
|
||||
xprint(response.json())
|
||||
|
||||
# Fetching organization repositories
|
||||
# Fetching organisation repositories
|
||||
def org_repos(self):
|
||||
if args.organization and args.limit:
|
||||
organization = args.organization
|
||||
if args.organisation and args.limit:
|
||||
organisation = args.organisation
|
||||
limit = args.limit
|
||||
else:
|
||||
organization = Prompt.ask(f"{white}@{green}Organi[sz]ation{reset}")
|
||||
limit = Prompt.ask(limit_output.format("organization repositories"))
|
||||
response = requests.get(f"{self.endpoint}/orgs/{organization}/repos?per_page={limit}")
|
||||
organisation = Prompt.ask(f"{white}@{green}Organi[sz]ation{reset}")
|
||||
limit = Prompt.ask(limit_output.format("organisation repositories"))
|
||||
response = requests.get(f"{self.endpoint}/orgs/{organisation}/repos?per_page={limit}")
|
||||
if response.status_code == 404:
|
||||
xprint(f"{NEGATIVE} {org_not_found.format(organization)}")
|
||||
xprint(f"{NEGATIVE} {org_not_found.format(organisation)}")
|
||||
elif response.status_code == 200:
|
||||
for repository in response.json():
|
||||
repos_tree = Tree("\n" + repository['full_name'])
|
||||
@@ -794,21 +794,21 @@ class Octosuite:
|
||||
xprint(repos_tree)
|
||||
|
||||
if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}") == "yes":
|
||||
log_org_repos(repository, organization)
|
||||
log_org_repos(repository, organisation)
|
||||
else:
|
||||
xprint(response.json())
|
||||
|
||||
# organization events
|
||||
# organisation events
|
||||
def org_events(self):
|
||||
if args.organization and args.limit:
|
||||
organization = args.organization
|
||||
if args.organisation and args.limit:
|
||||
organisation = args.organisation
|
||||
limit = args.limit
|
||||
else:
|
||||
organization = Prompt.ask(f"{white}@{green}Organi[sz]ation{reset}")
|
||||
limit = Prompt.ask(limit_output.format("organization events"))
|
||||
response = requests.get(f"{self.endpoint}/orgs/{organization}/events?per_page={limit}")
|
||||
organisation = Prompt.ask(f"{white}@{green}Organi[sz]ation{reset}")
|
||||
limit = Prompt.ask(limit_output.format("organisation events"))
|
||||
response = requests.get(f"{self.endpoint}/orgs/{organisation}/events?per_page={limit}")
|
||||
if response.status_code == 404:
|
||||
xprint(f"{NEGATIVE} {org_not_found.format(organization)}")
|
||||
xprint(f"{NEGATIVE} {org_not_found.format(organisation)}")
|
||||
elif response.status_code == 200:
|
||||
for event in response.json():
|
||||
events_tree = Tree("\n" + event['id'])
|
||||
@@ -816,21 +816,21 @@ class Octosuite:
|
||||
events_tree.add(f"Created at: {event['created_at']}")
|
||||
xprint(events_tree)
|
||||
xprint(event['payload'])
|
||||
# log_org_events(event, organization)
|
||||
# log_org_events(event, organisation)
|
||||
else:
|
||||
xprint(response.json())
|
||||
|
||||
# organization member
|
||||
# organisation member
|
||||
def org_member(self):
|
||||
if args.organization and args.username:
|
||||
organization = args.organization
|
||||
if args.organisation and args.username:
|
||||
organisation = args.organisation
|
||||
username = args.username
|
||||
else:
|
||||
organization = Prompt.ask(f"{white}@{green}Organi[sz]ation{reset}")
|
||||
organisation = Prompt.ask(f"{white}@{green}Organi[sz]ation{reset}")
|
||||
username = Prompt.ask(f"{white}@{green}Username{reset}")
|
||||
response = requests.get(f"{self.endpoint}/orgs/{organization}/public_members/{username}")
|
||||
response = requests.get(f"{self.endpoint}/orgs/{organisation}/public_members/{username}")
|
||||
if response.status_code == 204:
|
||||
xprint(f"{POSITIVE} User ({username}) is a public member of the organization -> ({organization})")
|
||||
xprint(f"{POSITIVE} User ({username}) is a public member of the organisation -> ({organisation})")
|
||||
else:
|
||||
xprint(f"{NEGATIVE} {response.json()['message']}")
|
||||
|
||||
@@ -882,28 +882,28 @@ class Octosuite:
|
||||
else:
|
||||
xprint(response.json())
|
||||
|
||||
# Fetching a list of organizations that a user owns or belongs to
|
||||
# Fetching a list of organisations that a user owns or belongs to
|
||||
def user_orgs(self):
|
||||
if args.username and args.limit:
|
||||
username = args.username
|
||||
limit = args.limit
|
||||
else:
|
||||
username = Prompt.ask(f"{white}@{green}Username{reset}")
|
||||
limit = Prompt.ask(limit_output.format("user organizations"))
|
||||
limit = Prompt.ask(limit_output.format("user organisations"))
|
||||
response = requests.get(f"{self.endpoint}/users/{username}/orgs?per_page={limit}")
|
||||
if not response.json():
|
||||
xprint(f"{NEGATIVE} User ({username}) does not (belong to/own) any organizations.")
|
||||
xprint(f"{NEGATIVE} User ({username}) does not (belong to/own) any organisations.")
|
||||
elif response.status_code == 404:
|
||||
xprint(f"{NEGATIVE} {user_not_found.format(username)}")
|
||||
elif response.status_code == 200:
|
||||
for organization in response.json():
|
||||
org_tree = Tree("\n" + organization['login'])
|
||||
for organisation in response.json():
|
||||
org_tree = Tree("\n" + organisation['login'])
|
||||
for attr in self.user_orgs_attrs:
|
||||
org_tree.add(f"{self.user_orgs_attr_dict[attr]}: {organization[attr]}")
|
||||
org_tree.add(f"{self.user_orgs_attr_dict[attr]}: {organisation[attr]}")
|
||||
xprint(org_tree)
|
||||
|
||||
if args.log_csv or Confirm.ask(f"\n{PROMPT} {prompt_log_csv}"):
|
||||
log_user_orgs(organization, username)
|
||||
log_user_orgs(organisation, username)
|
||||
else:
|
||||
xprint(response.json())
|
||||
|
||||
|
||||
Reference in New Issue
Block a user