mirror of
https://github.com/bellingcat/octosuite.git
synced 2026-06-10 20:38:34 +03:00
Compare commits
73 Commits
1.5.0
...
2.0.1-alph
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62a9556432 | ||
|
|
705966135d | ||
|
|
85e5c28a33 | ||
|
|
19165e1d48 | ||
|
|
c669aa87af | ||
|
|
7d0c462842 | ||
|
|
10e2d2dae7 | ||
|
|
d0f9ff2dd0 | ||
|
|
c88ec5f5ed | ||
|
|
161da5f5be | ||
|
|
5dd0ce300d | ||
|
|
89b98b7281 | ||
|
|
3e750bced7 | ||
|
|
8de7b0d939 | ||
|
|
6afc37d9a4 | ||
|
|
b030d48152 | ||
|
|
508df0ea77 | ||
|
|
fb7ad2cda8 | ||
|
|
b16eb762e0 | ||
|
|
8b7ac9e428 | ||
|
|
66e0da38b6 | ||
|
|
fbf7e40404 | ||
|
|
73bcb68012 | ||
|
|
850d9bb5ab | ||
|
|
17cfb5920c | ||
|
|
5ffb7758a9 | ||
|
|
2d6a808100 | ||
|
|
02feed27a8 | ||
|
|
77ddfa4b02 | ||
|
|
66192c2a63 | ||
|
|
f591764343 | ||
|
|
f6dbd8ec49 | ||
|
|
99deaf04f3 | ||
|
|
56eead2f26 | ||
|
|
73e7c339e6 | ||
|
|
3cd6442d91 | ||
|
|
7ebb90790a | ||
|
|
ca0d2dc268 | ||
|
|
51babb3c45 | ||
|
|
12e9dbcae9 | ||
|
|
be4e88d99b | ||
|
|
f7ac0fb8d5 | ||
|
|
fc7a65c593 | ||
|
|
51f2a46c65 | ||
|
|
4afbc5adaa | ||
|
|
d7980c0726 | ||
|
|
f8221d8f58 | ||
|
|
9d86c4cc7b | ||
|
|
67db855322 | ||
|
|
1b5cfa2ebf | ||
|
|
0d2a939388 | ||
|
|
52c572f255 | ||
|
|
eeda47723e | ||
|
|
1a6e3bb3c9 | ||
|
|
82b6a3fd3c | ||
|
|
c994678fa1 | ||
|
|
9ef14f3e4f | ||
|
|
23270e8da9 | ||
|
|
619070348b | ||
|
|
5718c5f229 | ||
|
|
b4f009c7d4 | ||
|
|
82a95d84b0 | ||
|
|
425525bf94 | ||
|
|
24c6b54d19 | ||
|
|
6d1e163d9a | ||
|
|
a8730864e9 | ||
|
|
315cb7ec6f | ||
|
|
2afac6c0d4 | ||
|
|
b226f1b53b | ||
|
|
bb38d95e57 | ||
|
|
eae41866ec | ||
|
|
1e71c2d3bd | ||
|
|
d91f88d31b |
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Richard Mwewa (Telegram)
|
||||
url: https://t.me/rly0nheart/
|
||||
about: Please ask and answer questions here.
|
||||
- name: Richard Mwewa (Twitter)
|
||||
url: https://m.twitter.com/rly0nheart/
|
||||
about: Please report security vulnerabilities or bugs here.
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
5
LICENSE
5
LICENSE
@@ -1,3 +1,4 @@
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
@@ -631,8 +632,8 @@ to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
octosuite Advanced Github OSINT Framework
|
||||
Copyright (C) 2022 Richard Mwewa
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
132
README.md
132
README.md
@@ -1,98 +1,78 @@
|
||||

|
||||

|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
> *Simply gather OSINT on Github users and organizations like a god🔥*
|
||||
> *Simply gather OSINT on Github users & organizations like a God🔥*
|
||||
|
||||
# INSTALLATION
|
||||
Installation instructions are on the wiki, in addition to all other documentation.
|
||||
|
||||
[Refer to the Wiki](https://github.com/rly0nheart/octosuite/wiki)
|
||||
|
||||
|
||||
# FEATURES
|
||||
- [x] Fetches organization info
|
||||
- [x] Fetches user info
|
||||
- [x] Fetches repository info
|
||||
- [x] Returns contents of a path from a repository
|
||||
- [x] Returns a list of repos owned by an organization
|
||||
- [x] Returns a list of repos owned by a user
|
||||
- [x] Returns a list of gists owned by a user
|
||||
- [x] Returns a list of a user's followers
|
||||
- [x] Checks whether user A follows user B
|
||||
- [x] Fetches an organization's profile information
|
||||
- [x] Fetches an oganization's events
|
||||
- [x] Returns an organization's repositories
|
||||
- [x] Returns an organization's public members
|
||||
- [x] Fetches a repository's information
|
||||
- [x] Returns a repository's contributors
|
||||
- [x] Returns a repository's languages
|
||||
- [x] Fetches a repository's stargazers
|
||||
- [x] Fetches a repository's forks
|
||||
- [x] Fetches a repository's releases
|
||||
- [x] Returns a list of files in a specified path of a repository
|
||||
- [x] Fetches a user's profile information
|
||||
- [x] Returns a user's gists
|
||||
- [x] Returns organizations that a user owns/belongs to
|
||||
- [x] Fetches a user's events
|
||||
- [x] Fetches a user's followers
|
||||
- [x] Checks if user A follows user B
|
||||
- [x] Checks if user is a public member of an organizations
|
||||
- [x] Returns a user's subscriptions
|
||||
- [x] Gets a user's subscriptions
|
||||
- [x] Gets a user's events
|
||||
- [x] Searches users
|
||||
- [x] Searches repositories
|
||||
- [x] Searches topics
|
||||
- [x] Searches issues
|
||||
- [x] Searches commits
|
||||
- [x] Easily updates with the 'update' command
|
||||
- [x] Easily updates with the 'update:install' command
|
||||
- [x] Automatically logs network activity (.logs folder)
|
||||
- [x] User can view, read and delete logs
|
||||
|
||||
# INSTALLATION
|
||||
**clone project**:
|
||||
|
||||
```
|
||||
git clone https://github.com/rly0nheart/octosuite.git
|
||||
```
|
||||
|
||||
```
|
||||
cd octosuite
|
||||
```
|
||||
|
||||
```
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
# USAGE
|
||||
**Linux**:
|
||||
```
|
||||
sudo chmod +x octosuite
|
||||
```
|
||||
|
||||
```
|
||||
sudo ./octosuite
|
||||
```
|
||||
|
||||
**Windows**:
|
||||
```
|
||||
python3 octosuite
|
||||
```
|
||||
|
||||
**Mac**:
|
||||
```
|
||||
python3 octosuite
|
||||
```
|
||||
|
||||
# AVAILABLE COMMANDS
|
||||
| Command | Usage|
|
||||
| ------------- |:---------:|
|
||||
| ``orginfo`` | *get organization info* |
|
||||
| ``userinfo`` | *get user profile info* |
|
||||
| ``repoinfo`` | *get repository info* |
|
||||
| ``pathcontents`` | *get contents of a path from a specified repository* |
|
||||
| ``orgrepos`` | *get a list of repositories owned by a specified organization* |
|
||||
| ``userrepos`` | *get a list of repositories owned by a specified user* |
|
||||
| ``usergists`` | *get a list of gists owned by a specified user* |
|
||||
| ``userfollowers`` | *get a list of a user's followers* |
|
||||
| ``userfollowing`` | *check whether user A follows user B* |
|
||||
| ``usersearch`` | *search user(s)* |
|
||||
| ``reposearch`` | *search repositor(y)(ies)* |
|
||||
| ``topicsearch`` | *search topics(s)* |
|
||||
| ``issuesearch`` | *search issue(s)* |
|
||||
| ``commitsearch`` | *search commit(s)* |
|
||||
| ``update`` | *update octosuite* |
|
||||
| ``changelog`` | *show changelog* |
|
||||
| ``author`` | *show author info* |
|
||||
| ``help`` | *show usage/help* |
|
||||
| ``exit`` | *exit session* |
|
||||
|
||||
|
||||
# NOTE
|
||||
# NOTES
|
||||
* *octosuite automatically logs network and minor user activity. The logs are saved by date and time in .logs folder*
|
||||
* *Although octosuite was developed to work on **Mac**, **Windows**, or any **Linux** *Distribution*, it has only been tested on **Termux** *and* **Kali Linux***
|
||||
* *If you believe octosuite can be better, feel free to open a pull request with your improvements* ✌🏾🙂
|
||||
|
||||
|
||||
# PYPI
|
||||
[PyPI Package](https://pypi.org/project/octosuite)
|
||||
|
||||

|
||||
|
||||
|
||||
# LICENSE
|
||||

|
||||
|
||||
# ABOUT AUTHOR
|
||||
|
||||
# ABOUT DEVELOPER
|
||||
[About.me](https://about.me/rly0nheart)
|
||||
|
||||
|
||||
# SUPPORTERS
|
||||
[](https://github.com/rly0nheart/octosuite/stargazers)
|
||||
[](https://github.com/rly0nheart/octosuite/members)
|
||||
|
||||
|
||||
# DONATIONS
|
||||
Love octosuite? Please consider buying me a coffee, I will really appreciate it. ☕👌🏾😊
|
||||
|
||||
<a href="https://www.buymeacoffee.com/189381184" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174"></a>
|
||||
|
||||
736
core/main.py
Normal file
736
core/main.py
Normal file
@@ -0,0 +1,736 @@
|
||||
'''
|
||||
OCTOSUITE Advanced Github OSINT Framework
|
||||
Copyright (C) 2022 Richard Mwewa
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
'''
|
||||
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import getpass
|
||||
import requests
|
||||
import platform
|
||||
import subprocess
|
||||
from tqdm import tqdm
|
||||
from pprint import pprint
|
||||
from utilities.misc import Banner
|
||||
from utilities.helper import Help
|
||||
from utilities.colors import Color
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
# logMsg
|
||||
# This class is where the main notification strings/messages are held,
|
||||
# and are being used in two different cases (they're beig used by logging to be written to log files, and being printed out to the screen).
|
||||
class logMsg:
|
||||
Ctrl = 'Session terminated with (Ctrl+C).'
|
||||
Error = 'Session terminated on error: {}'
|
||||
sessionStart = 'Started new session on {}:{}'
|
||||
sessionClosed = 'Session closed with (exit) command.'
|
||||
fileDeleted = 'Deleted log: {}'
|
||||
readingFile = 'Reading log: {}'
|
||||
viewingLogs = 'Viewing logs...'
|
||||
checkingUpdates = 'Checking for update(s)...'
|
||||
installingUpdates = 'Installing update(s)...'
|
||||
installedUpdates = '{} Update(s) installed.'
|
||||
|
||||
|
||||
# firstBlood
|
||||
# *I couldn't think of a good name for this.*
|
||||
# The firstBlood is responsible for creating/checking the availability of the .logs folder
|
||||
# and enabling logging to automatically log network/user activity to a file.
|
||||
class firstBlood:
|
||||
# If .logs folder exists, we ignore (pass)
|
||||
if os.path.exists('.logs'):
|
||||
pass
|
||||
else:
|
||||
# Creating the .logs directory
|
||||
# If the current system is Windows based, we run mkdir command (without sudo?)
|
||||
# Else we run the mkdir command (with sudo)
|
||||
# As of writing, I have absolutely no idea if Windows users also use sudo to run commands as root/admin
|
||||
if sys.platform.lower().startswith(('win','darwin')):
|
||||
subprocess.run(['mkdir','.logs'])
|
||||
else:
|
||||
subprocess.run(['sudo','mkdir','.logs'],shell=False)
|
||||
|
||||
# Set to automatically monitor and log network and user activity into the .logs folder
|
||||
now = datetime.now()
|
||||
now_formatted = now.strftime('%Y-%m-%d %H:%M:%S%p')
|
||||
logging.basicConfig(filename=f'.logs/{now_formatted}.log',format='[%(asctime)s] [%(levelname)s] %(message)s',datefmt='%Y-%m-%d %H:%M:%S%p',level=logging.DEBUG)
|
||||
|
||||
|
||||
# Attributes
|
||||
# *Even here, I couldn't think of a good name.*
|
||||
# The Attributes class holds the signs/symbols that show what a notification in OctoSuite might be all about.
|
||||
# This might not be very important or necessary in some cases, but I think it's better to know the severerity of the notifications you get in a program.
|
||||
class Attributes:
|
||||
prompt = f'\n{Color.white}[{Color.green}?{Color.white}]{Color.reset}'
|
||||
warning = f'\n{Color.white}[{Color.red}!{Color.white}]{Color.reset}'
|
||||
error = f'\n{Color.white}[{Color.red}x{Color.white}]{Color.reset}'
|
||||
positive = f'\n{Color.white}[{Color.green}+{Color.white}]{Color.reset}'
|
||||
negative = f'\n{Color.white}[{Color.red}-{Color.white}]{Color.reset}'
|
||||
info = f'\n{Color.white}[{Color.green}*{Color.white}]{Color.reset}'
|
||||
|
||||
|
||||
# Octosuite
|
||||
# This class is the backbone of the program.
|
||||
# It holds all important methods/information for the program.
|
||||
class Octosuite:
|
||||
def __init__(self):
|
||||
firstBlood()
|
||||
# A list of tuples, mapping commands to their respective functionalities
|
||||
self.commands_map = [('org:events', self.orgEvents),
|
||||
('org:profile', self.orgProfile),
|
||||
('org:repos', self.orgRepos),
|
||||
('org:member', self.orgMember),
|
||||
('repo:pathcontents', self.pathContents),
|
||||
('repo:profile', self.repoProfile),
|
||||
('repo:contributors', self.repoContributors),
|
||||
('repo:languages', self.repoLanguages),
|
||||
('repo:stargazers', self.repoStargazers),
|
||||
('repo:forks', self.repoForks),
|
||||
('repo:releases', self.repoReleases),
|
||||
('user:repos', self.userRepos),
|
||||
('user:gists', self.userGists),
|
||||
('user:orgs', self.userOrgs),
|
||||
('user:profile', self.userProfile),
|
||||
('user:events', self.userEvents),
|
||||
('user:followers', self.userFollowers),
|
||||
('user:following', self.userFollowing),
|
||||
('user:subscriptions', self.userSubscriptions),
|
||||
('search:users', self.userSearch),
|
||||
('search:repos', self.repoSearch),
|
||||
('search:topics', self.topicSearch),
|
||||
('search:issues', self.issueSearch),
|
||||
('search:commits', self.commitsSearch),
|
||||
('logs:view',self.viewLogs),
|
||||
('logs:read',self.readLog),
|
||||
('logs:delete',self.deleteLog),
|
||||
('update:check', self.checkUpdate),
|
||||
('update:install', self.installUpdate),
|
||||
('help', Help.helpCommand),
|
||||
('help:search', Help.searchCommand),
|
||||
('help:user', Help.userCommand),
|
||||
('help:repo', Help.repoCommand),
|
||||
('help:logs', Help.logsCommand),
|
||||
('help:org', Help.orgCommand),
|
||||
('help:update', Help.updateCommand),
|
||||
('author', self.author),
|
||||
('about', self.about),
|
||||
('clear',self.clearScreen),
|
||||
('version', self.versionInfo),
|
||||
('exit', self.exitSession)]
|
||||
|
||||
|
||||
# Path attribute
|
||||
self.path_attrs =['size','type','path','sha','html_url']
|
||||
# Path attribute dictionary
|
||||
self.path_attr_dict = {'size': 'Size (bytes)',
|
||||
'type': 'Type',
|
||||
'path': 'Path',
|
||||
'sha': 'SHA',
|
||||
'html_url': 'URL'}
|
||||
|
||||
|
||||
# Organization 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
|
||||
self.org_attr_dict = {'avatar_url': 'Profile Photo',
|
||||
'login': 'Username',
|
||||
'id': 'ID#',
|
||||
'node_id': 'Node ID',
|
||||
'email': 'Email',
|
||||
'description': 'About',
|
||||
'location': 'Location',
|
||||
'blog': 'Blog',
|
||||
'followers': 'Followers',
|
||||
'following': 'Following',
|
||||
'twitter_username': 'Twitter Handle',
|
||||
'public_gists': 'Gists (public)',
|
||||
'public_repos': 'Repositories (public)',
|
||||
'type': 'Account type',
|
||||
'is_verified': 'Is verified?',
|
||||
'has_organization_projects': 'Has organization projects?',
|
||||
'has_repository_projects': 'Has repository projects?',
|
||||
'created_at': 'Created at',
|
||||
'updated_at': 'Updated at'}
|
||||
|
||||
|
||||
# Repository attributes
|
||||
self.repo_attrs = ['id','description','forks','stargazers_count','watchers','license','default_branch','visibility','language','open_issues','topics','homepage','clone_url','ssh_url','fork','allow_forking','private','archived','has_downloads','has_issues','has_pages','has_projects','has_wiki','pushed_at','created_at','updated_at']
|
||||
# Repository attribute dictionary
|
||||
self.repo_attr_dict = {'id': 'ID#',
|
||||
'description': 'About',
|
||||
'forks': 'Forks',
|
||||
'stargazers_count': 'Stars',
|
||||
'watchers': 'Watchers',
|
||||
'license': 'License',
|
||||
'default_branch': 'Branch',
|
||||
'visibility': 'Visibility',
|
||||
'language': 'Language(s)',
|
||||
'open_issues': 'Open issues',
|
||||
'topics': 'Topics',
|
||||
'homepage': 'Homepage',
|
||||
'clone_url': 'Clone URL',
|
||||
'ssh_url': 'SSH URL',
|
||||
'fork': 'Is fork?',
|
||||
'allow_forking': 'Is forkable?',
|
||||
'private': 'Is private?',
|
||||
'archived': 'Is archived?',
|
||||
'is_template': 'Is template?',
|
||||
'has_wiki': 'Has wiki?',
|
||||
'has_pages': 'Has pages?',
|
||||
'has_projects': 'Has projects?',
|
||||
'has_issues': 'Has issues?',
|
||||
'has_downloads': 'Has downloads?',
|
||||
'pushed_at': 'Pushed at',
|
||||
'created_at': 'Created at',
|
||||
'updated_at': 'Updated at'}
|
||||
|
||||
|
||||
# Repo releases attributes
|
||||
self.repo_releases_attrs = ['node_id','tag_name','target_commitish','assets','draft','prerelease','created_at','published_at']
|
||||
# Repo releases attribute dictionary
|
||||
self.repo_releases_attr_dict = {'node_id': 'Node ID',
|
||||
'tag_name': 'Tag',
|
||||
'target_commitish': 'Branch',
|
||||
'assets': 'Assets',
|
||||
'draft': 'Is draft?',
|
||||
'prerelease': 'Is prerelease?',
|
||||
'created_at': 'Created at',
|
||||
'published_at': 'Published at'}
|
||||
|
||||
|
||||
# Profile attributes
|
||||
self.profile_attrs = ['avatar_url','login','id','node_id','bio','blog','location','followers','following','twitter_username','public_gists','public_repos','company','hireable','site_admin','created_at','updated_at']
|
||||
# Profile attribute dictionary
|
||||
self.profile_attr_dict = {'avatar_url': 'Profile Photo',
|
||||
'login': 'Username',
|
||||
'id': 'ID#',
|
||||
'node_id': 'Node ID',
|
||||
'bio': 'Bio',
|
||||
'blog': 'Blog',
|
||||
'location': 'Location',
|
||||
'followers': 'Followers',
|
||||
'following': 'Following',
|
||||
'twitter_username': 'Twitter Handle',
|
||||
'public_gists': 'Gists (public)',
|
||||
'public_repos': 'Repositories (public)',
|
||||
'company': 'Organization',
|
||||
'hireable': 'Is hireable?',
|
||||
'site_admin': 'Is site admin?',
|
||||
'created_at': 'Joined at',
|
||||
'updated_at': 'Updated at'}
|
||||
|
||||
|
||||
# User attributes
|
||||
self.user_attrs = ['avatar_url','id','node_id','gravatar_id','site_admin','type','html_url']
|
||||
# User attribute dictionary
|
||||
self.user_attr_dict = {'avatar_url': 'Profile Photo',
|
||||
'id': 'ID#',
|
||||
'node_id': 'Node ID',
|
||||
'gravatar_id': 'Gravatar ID',
|
||||
'site_admin': 'Is site admin?',
|
||||
'type': 'Account type',
|
||||
'html_url': 'URL'}
|
||||
|
||||
|
||||
# Topic atrributes
|
||||
self.topic_attrs = ['score','curated','featured','display_name','created_by','created_at','updated_at']
|
||||
# Topic attribute dictionary
|
||||
self.topic_attr_dict = {'score': 'Score',
|
||||
'curated': 'Curated',
|
||||
'featured': 'Featured',
|
||||
'display_name': 'Display Name',
|
||||
'created_by': 'Created by',
|
||||
'created_at': 'Created at',
|
||||
'updated_at': 'Updated at'}
|
||||
|
||||
|
||||
# Gists attributes
|
||||
self.gists_attrs = ['node_id','description','comments','files','git_push_url','public','truncated','updated_at']
|
||||
# Gists attribute dictionary
|
||||
self.gists_attr_dict = {'node_id': 'Node ID',
|
||||
'description': 'About',
|
||||
'comments': 'Comments',
|
||||
'files': 'Files',
|
||||
'git_push_url': 'Git Push URL',
|
||||
'public': 'Is public?',
|
||||
'truncated': 'Is truncated?',
|
||||
'updated_at': 'Updated at'}
|
||||
|
||||
|
||||
# Issue attributes
|
||||
self.issue_attrs = ['id','node_id','score','state','number','comments','milestone','assignee','assignees','labels','locked','draft','closed_at','body']
|
||||
# Issue attribute dict
|
||||
self.issue_attr_dict = {'id': 'ID#',
|
||||
'node_id': 'Node ID',
|
||||
'score': 'Score',
|
||||
'state': 'State',
|
||||
'closed_at': 'Closed at',
|
||||
'number': 'Number',
|
||||
'comments': 'Comments',
|
||||
'milestone': 'Milestone',
|
||||
'assignee': 'Assignee',
|
||||
'assignees': 'Assignees',
|
||||
'labels': 'Labels',
|
||||
'draft': 'Is draft?',
|
||||
'locked': 'Is locked?',
|
||||
'created_at': 'Created at',
|
||||
'body': 'Body'}
|
||||
|
||||
|
||||
# User organizations attributes
|
||||
self.user_orgs_attrs = ['avatar_url','id','node_id','url','description']
|
||||
self.user_orgs_attr_dict = {'avatar_url': 'Profile Photo',
|
||||
'id': 'ID#',
|
||||
'node_id': 'Node ID',
|
||||
'url': 'URL',
|
||||
'description': 'About'}
|
||||
|
||||
|
||||
# Author dictionary
|
||||
self.author_dict = {'Alias': 'rly0nheart',
|
||||
'Country': 'Zambia, Africa',
|
||||
'About.me': 'https://about.me/rly0nheart',
|
||||
'BuyMeACoffee': 'https://buymeacoffee.com/189381184'}
|
||||
|
||||
# About dictionary
|
||||
self.about_dict = {'Version': Banner.versionTag,'Category': 'Open Source Intelligence'}
|
||||
|
||||
|
||||
|
||||
def onStart(self):
|
||||
# Log the beginning of a session
|
||||
logging.info(logMsg.sessionStart.format(platform.node(), os.getlogin()))
|
||||
|
||||
# Use 'cls' to clear screen on Windows based machines
|
||||
# Otherwise, use 'clear'
|
||||
while True:
|
||||
command_input = input(f'''\n{Color.white}┌──({Color.red}{getpass.getuser()}{Color.white}@{Color.red}octosuite{Color.white})-[{Color.green}{os.getcwd()}{Color.white}]\n└╼[{Color.green}:_{Color.white}]{Color.reset} ''')
|
||||
# Looping through the commands base to check if the user input command matches any command in the commands base, and return its functionality
|
||||
# If no match is found, we ignore it
|
||||
for command, functionality in self.commands_map:
|
||||
if command_input.lower() == command:
|
||||
functionality()
|
||||
else:
|
||||
pass
|
||||
|
||||
|
||||
# Fetching organization info
|
||||
def orgProfile(self):
|
||||
organization = input(f'\n{Color.white}--> @{Color.green}organization{Color.white} (username){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/orgs/{organization}')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} Organization {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
print(f"\n{Color.white}{response.json()['name']}{Color.reset}")
|
||||
for attr in self.org_attrs:
|
||||
print(f'{Color.white}├─ {self.org_attr_dict[attr]}: {Color.green}{response.json()[attr]}{Color.reset}')
|
||||
|
||||
|
||||
# Fetching user information
|
||||
def userProfile(self):
|
||||
username = input(f'\n{Color.white}--> @{Color.green}username{Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/users/{username}')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} User {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
print(f"\n{Color.white}{response.json()['name']}{Color.reset}")
|
||||
for attr in self.profile_attrs:
|
||||
print(f'{Color.white}├─ {self.profile_attr_dict[attr]}: {Color.green}{response.json()[attr]}{Color.reset}')
|
||||
|
||||
|
||||
# Fetching repository information
|
||||
def repoProfile(self):
|
||||
repo_name = input(f'\n{Color.white}--> %{Color.green}reponame{Color.reset} ')
|
||||
username = input(f'{Color.white}--> @{Color.green}owner{Color.white} (username){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/repos/{username}/{repo_name}')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} Repository or user {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
print(f"\n{Color.white}{response.json()['full_name']}{Color.reset}")
|
||||
for attr in self.repo_attrs:
|
||||
print(f"{Color.white}├─ {self.repo_attr_dict[attr]}: {Color.green}{response.json()[attr]}{Color.reset}")
|
||||
|
||||
|
||||
# Get path contents
|
||||
def pathContents(self):
|
||||
repo_name = input(f'\n{Color.white}--> %{Color.green}reponame{Color.reset} ')
|
||||
username = input(f'{Color.white}--> @{Color.green}owner{Color.white} (username){Color.reset} ')
|
||||
path_name = input(f'{Color.white}--> ~{Color.green}/path/name{Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/repos/{username}/{repo_name}/contents/{path_name}')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} Information {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
for item in response.json():
|
||||
print(f"\n{Color.white}{item['name']}{Color.reset}")
|
||||
for attr in self.path_attrs:
|
||||
print(f'{Color.white}├─ {self.path_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}')
|
||||
|
||||
|
||||
# repo contributors
|
||||
def repoContributors(self):
|
||||
repo_name = input(f'\n{Color.white}--> %{Color.green}reponame{Color.reset} ')
|
||||
username = input(f'{Color.white}--> @{Color.green}owner{Color.white} (username){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/repos/{username}/{repo_name}/contributors')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} Repository or user {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
for item in response.json():
|
||||
print(f"\n{Color.white}{item['login']}{Color.reset}")
|
||||
for attr in self.user_attrs:
|
||||
print(f'{Color.white}├─ {self.user_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}')
|
||||
|
||||
|
||||
# repo downloads
|
||||
def repoLanguages(self):
|
||||
repo_name = input(f'\n{Color.white}--> %{Color.green}reponame{Color.reset} ')
|
||||
username = input(f'{Color.white}--> @{Color.green}owner{Color.white} (username){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/repos/{username}/{repo_name}/languages')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} Repository or user {response.json()['message']}{Color.reset}")
|
||||
elif response.json() == {}:
|
||||
print(f'{Attributes.negative} Repository has no supported language(s){Color.reset}')
|
||||
else:
|
||||
for language in response.json():
|
||||
print(language)
|
||||
|
||||
|
||||
# repo stargazers
|
||||
def repoStargazers(self):
|
||||
repo_name = input(f'\n{Color.white}--> %{Color.green}reponame{Color.reset} ')
|
||||
username = input(f'{Color.white}--> @{Color.green}owner{Color.white} (username){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/repos/{username}/{repo_name}/stargazers')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} Repository or user {response.json()['message']}{Color.reset}")
|
||||
elif response.json() == {}:
|
||||
print(f'{Attributes.negative} Repository does not have stargazers.{Color.reset}')
|
||||
else:
|
||||
for item in response.json():
|
||||
print(f"\n{Color.white}{item['login']}{Color.reset}")
|
||||
for attr in self.user_attrs:
|
||||
print(f'{Color.white}├─ {self.user_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}')
|
||||
|
||||
|
||||
# repo forks
|
||||
def repoForks(self):
|
||||
repo_name = input(f'\n{Color.white}--> %{Color.green}reponame{Color.reset} ')
|
||||
username = input(f'{Color.white}--> @{Color.green}owner{Color.white} (username){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/repos/{username}/{repo_name}/forks')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} Repository or user {response.json()['message']}{Color.reset}")
|
||||
elif response.json() == {}:
|
||||
print(f'{Attributes.negative} Repository does not have forks.{Color.reset}')
|
||||
else:
|
||||
for item in response.json():
|
||||
print(f"\n{Color.white}{item['full_name']}{Color.reset}")
|
||||
for attr in self.repo_attrs:
|
||||
print(f'{Color.white}├─ {self.repo_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}')
|
||||
|
||||
|
||||
# Repo releases
|
||||
def repoReleases(self):
|
||||
repo_name = input(f'\n{Color.white}--> %{Color.green}reponame{Color.reset} ')
|
||||
username = input(f'{Color.white}--> @{Color.green}owner{Color.white} (username){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/repos/{username}/{repo_name}/releases')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} Repository or user not found{Color.reset}")
|
||||
elif response.json() == []:
|
||||
print(f"\n{Attributes.negative} Repository does not have releases{Color.reset}")
|
||||
else:
|
||||
for item in response.json():
|
||||
print(f"\n{Color.white}{item['name']}{Color.reset}")
|
||||
for attr in self.repo_releases_attrs:
|
||||
print(f'{Color.white}├─ {self.repo_releases_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}')
|
||||
print(item['body'])
|
||||
|
||||
|
||||
# Fetching organization repositories
|
||||
def orgRepos(self):
|
||||
organization = input(f'\n{Color.white}--> @{Color.green}organization{Color.white} (username){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/orgs/{organization}/repos?per_page=100')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} Organization {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
for repo in response.json():
|
||||
print(f"\n{Color.white}{repo['full_name']}{Color.reset}")
|
||||
for attr in self.repo_attrs:
|
||||
print(f"{Color.white}├─ {self.repo_attr_dict[attr]}: {Color.green}{repo[attr]}{Color.reset}")
|
||||
|
||||
|
||||
# organization events
|
||||
def orgEvents(self):
|
||||
organization = input(f"\n{Color.white}--> @{Color.green}organization{Color.white} (username){Color.reset} ")
|
||||
response = requests.get(f'https://api.github.com/orgs/{organization}/events')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} Organization {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
for item in response.json():
|
||||
print(f"\n{Color.white}{item['id']}{Color.reset}")
|
||||
print(f"{Color.white}├─ Type: {Color.green}{item['type']}{Color.reset}\n{Color.white}├─ Created at: {Color.green}{item['created_at']}{Color.green}")
|
||||
pprint(item['payload'])
|
||||
print(f"{Color.reset}\n")
|
||||
|
||||
|
||||
# organization member
|
||||
def orgMember(self):
|
||||
organization = input(f"\n{Color.white}--> @{Color.green}organization{Color.white} (username){Color.reset} ")
|
||||
username = input(f'{Color.white}--> @{Color.green}username{Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/orgs/{organization}/public_members/{username}')
|
||||
if response.status_code == 204:
|
||||
print(f"{Attributes.positive} User is a public member of the organization{Color.reset}")
|
||||
else:
|
||||
print(f"{Attributes.negative} {response.json()['message']}{Color.reset}")
|
||||
|
||||
|
||||
# Fetching user repositories
|
||||
def userRepos(self):
|
||||
username = input(f'\n{Color.white}--> @{Color.green}username{Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/users/{username}/repos?per_page=100')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} User {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
for repo in response.json():
|
||||
print(f"\n{Color.white}{repo['full_name']}{Color.reset}")
|
||||
for attr in self.repo_attrs:
|
||||
print(f"{Color.white}├─ {self.repo_attr_dict[attr]}: {Color.green}{repo[attr]}{Color.reset}")
|
||||
|
||||
|
||||
# Fetching user's gists
|
||||
def userGists(self):
|
||||
username = input(f'\n{Color.white}--> @{Color.green}username{Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/users/{username}/gists')
|
||||
if response.json() == []:
|
||||
print(f'{Attributes.negative} User does not have any active gists{Color.reset}')
|
||||
elif response.status_code == 404:
|
||||
print(f"{Attributes.negative} User {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
for item in response.json():
|
||||
print(f"\n{Color.white}{item['id']}{Color.reset}")
|
||||
for attr in self.gists_attrs:
|
||||
print(f"{Color.white}├─ {self.gists_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}")
|
||||
|
||||
|
||||
# Fetching a list of organizations that a user owns or belongs to
|
||||
def userOrgs(self):
|
||||
username = input(f'\n{Color.white}--> @{Color.green}username{Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/users/{username}/orgs')
|
||||
if response.json() == []:
|
||||
print(f'{Attributes.negative} User does not belong to or own any organizations.{Color.reset}')
|
||||
elif response.status_code == 404:
|
||||
print(f"{Attributes.negative} User {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
for item in response.json():
|
||||
print(f'\n{Color.white}{item["login"]}{Color.reset}')
|
||||
for attr in self.user_orgs_attrs:
|
||||
print(f'{Color.white}├─ {self.user_orgs_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}')
|
||||
|
||||
|
||||
# Fetching a users events
|
||||
def userEvents(self):
|
||||
username = input(f'\n{Color.white}--> @{Color.green}username{Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/users/{username}/events/public')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} User {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
for item in response.json():
|
||||
print(f"\n{Color.white}{item['id']}{Color.reset}")
|
||||
print(f"{Color.white}├─ Type: {Color.green}{item['type']}{Color.reset}\n{Color.white}├─ Created at: {Color.green}{item['created_at']}{Color.green}")
|
||||
pprint(item['payload'])
|
||||
print(reset)
|
||||
|
||||
|
||||
# Fetching a target user's subscriptions
|
||||
def userSubscriptions(self):
|
||||
username = input(f'\n{Color.white}--> @{Color.green}username{Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/users/{username}/subscriptions')
|
||||
if response.json() == []:
|
||||
print(f"{Attributes.negative} User does not have any subscriptions.{Color.reset}")
|
||||
elif response.status_code == 404:
|
||||
print(f"{Attributes.negative} User {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
for item in response.json():
|
||||
print(f"\n{Color.white}{item['full_name']}{Color.reset}")
|
||||
for attr in self.repo_attrs:
|
||||
print(f"{Color.white}├─ {self.repo_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}")
|
||||
|
||||
|
||||
# Fetching user's followera'
|
||||
def userFollowers(self):
|
||||
username = input(f'\n{Color.white}--> @{Color.green}username{Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/users/{username}/followers?per_page=100')
|
||||
if response.json() == []:
|
||||
print(f'{Attributes.negative} User does not have followers.{Color.reset}')
|
||||
elif response.status_code == 404:
|
||||
print(f"{Attributes.negative} User {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
for item in response.json():
|
||||
print(f"\n{Color.white}@{item['login']}{Color.reset}")
|
||||
for attr in self.user_attrs:
|
||||
print(f"{Color.white}├─ {self.user_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}")
|
||||
|
||||
|
||||
# Checking whether or not user[A] follows user[B]
|
||||
def userFollowing(self):
|
||||
user_a = input(f'\n{Color.white}--> @{Color.green}user{Color.white}[A] (username){Color.reset} ')
|
||||
user_b = input(f'{Color.white}--> @{Color.green}user{Color.white}[B] (username){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/users/{user_a}/following/{user_b}')
|
||||
if response.status_code == 204:
|
||||
print(f'{Attributes.positive} @{user_a} follows @{user_b}{Color.reset}')
|
||||
else:
|
||||
print(f'{Attributes.negative} @{user_a} does not follow @{user_b}{Color.reset}')
|
||||
|
||||
|
||||
# User search
|
||||
def userSearch(self):
|
||||
query = input(f'\n{Color.white}--> @{Color.green}query{Color.white} (eg. john){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/search/users?q={query}&per_page=100').json()
|
||||
for item in response['items']:
|
||||
print(f"\n{Color.white}@{item['login']}{Color.reset}")
|
||||
for attr in self.user_attrs:
|
||||
print(f"{Color.white}├─ {self.user_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}")
|
||||
|
||||
|
||||
# Repository search
|
||||
def repoSearch(self):
|
||||
query = input(f'\n{Color.white}--> %{Color.green}query{Color.white} (eg. git){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/search/repositories?q={query}&per_page=100').json()
|
||||
for item in response['items']:
|
||||
print(f"\n{Color.white}{item['full_name']}{Color.reset}")
|
||||
for attr in self.repo_attrs:
|
||||
print(f"{Color.white}├─ {self.repo_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}")
|
||||
|
||||
|
||||
# Topics search
|
||||
def topicSearch(self):
|
||||
query = input(f'\n{Color.white}--> #{Color.green}query{Color.white} (eg. osint){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/search/topics?q={query}&per_page=100').json()
|
||||
for item in response['items']:
|
||||
print(f"\n{Color.white}{item['name']}{Color.reset}")
|
||||
for attr in self.topic_attrs:
|
||||
print(f"{Color.white}├─ {self.topic_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}")
|
||||
|
||||
|
||||
# Issue search
|
||||
def issueSearch(self):
|
||||
query = input(f'\n{Color.white}--> !{Color.green}query{Color.white} (eg. error){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/search/issues?q={query}&per_page=100').json()
|
||||
for item in response['items']:
|
||||
print(f"\n{Color.white}{item['title']}{Color.reset}")
|
||||
for attr in self.issue_attrs:
|
||||
print(f"{Color.white}├─ {self.issue_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}")
|
||||
|
||||
|
||||
# Commits search
|
||||
def commitsSearch(self):
|
||||
query = input(f'\n{Color.white}--> :{Color.green}query{Color.white} (eg. filename:index.php){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/search/commits?q={query}&per_page=100').json()
|
||||
number=0
|
||||
for item in response['items']:
|
||||
number+=1
|
||||
print(f'\n{Color.white}-> {number}.{Color.reset}')
|
||||
pprint(item['commit'])
|
||||
|
||||
|
||||
# View octosuite log files
|
||||
def viewLogs(self):
|
||||
logging.info(logMsg.viewingLogs)
|
||||
logs = os.listdir('.logs')
|
||||
print(f'''\n{Color.white}Log Size{Color.reset}
|
||||
--- ---------''')
|
||||
for log in logs:
|
||||
print(f"{log}\t ",os.path.getsize(".logs/"+log),"bytes")
|
||||
|
||||
|
||||
# Delete a specified log file
|
||||
def deleteLog(self):
|
||||
log_file = input(f"\n{Color.white}--> logfile (eg. 2022-04-27 10:09:36AM.log){Color.reset} ")
|
||||
if sys.platform.lower().startswith(('win','darwin')):
|
||||
subprocess.run(['del',f'{os.getcwd()}/.logs/{log_file}'])
|
||||
else:
|
||||
subprocess.run(['sudo','rm',f'.logs/{log_file}'],shell=False)
|
||||
|
||||
logging.info(logMsg.fileDeleted.format(log_file))
|
||||
print(Attributes.positive, logMsg.fileDeleted)
|
||||
|
||||
|
||||
# Read a specified log file
|
||||
def readLog(self):
|
||||
log_file = input(f"\n{Color.white}--> logfile (eg. 2022-04-27 10:09:36AM.log){Color.reset} ")
|
||||
with open(f'.logs/{log_file}', 'r') as log:
|
||||
logging.info(logMsg.readingFile.format(log_file))
|
||||
print("\n"+log.read())
|
||||
|
||||
|
||||
# Update program
|
||||
def installUpdate(self):
|
||||
files_to_update = ['core/main.py','utilities/helper.py','utilities/misc.py','utilities/colors.py','octosuite','.github/dependabot.yml','.github/ISSUE_TEMPLATE/bug_report.md','.github/ISSUE_TEMPLATE/feature_request.md','.github/ISSUE_TEMPLATE/config.yml','LICENSE','README.md','requirements.txt']
|
||||
logging.info(logMsg.installingUpdates)
|
||||
for file in tqdm(files_to_update,desc = logMsg.installingUpdates):
|
||||
data = requests.get(f'https://raw.githubusercontent.com/rly0nheart/octosuite/master/{file}')
|
||||
with open(file, 'wb') as code:
|
||||
code.write(data.content)
|
||||
code.close()
|
||||
|
||||
logging.info(logMsg.installedUpdates.format(len(files_to_update)))
|
||||
print(Attributes.positive, logMsg.installedUpdates.format(len(files_to_update)));exit()
|
||||
|
||||
|
||||
def checkUpdate(self):
|
||||
logging.info(logMsg.checkingUpdates)
|
||||
response = requests.get("https://api.github.com/repos/rly0nheart/octosuite/releases/latest")
|
||||
if response.json()['tag_name'] == Banner.versionTag:
|
||||
print(f"{Attributes.positive} OctoSuite is up to date. Check again soon :)")
|
||||
else:
|
||||
print(f"{Attributes.info} A new release is available ({response.json()['tag_name']}). Use command {Color.green}update:install{Color.white} to download and install the updates.{Color.reset}")
|
||||
|
||||
|
||||
# Show version information
|
||||
def versionInfo(self):
|
||||
# Yes... the changelog is actually hard coded
|
||||
# It's actually frustrating having to change this everytime I publish a new release lol
|
||||
print(f'''
|
||||
Tag: {Banner.versionTag}
|
||||
Released at: 2022-05-25 11:05AM
|
||||
{'-'*31}
|
||||
|
||||
What's changed?
|
||||
{'-'*15}
|
||||
[✓] Fixed a bug in issue #2''')
|
||||
|
||||
|
||||
# Author info
|
||||
def author(self):
|
||||
print(f'\n{Color.white}Richard Mwewa (Ritchie){Color.reset}')
|
||||
for key,value in self.author_dict.items():
|
||||
print(f'{Color.white}├─ {key}: {Color.green}{value}{Color.reset}')
|
||||
|
||||
|
||||
def about(self):
|
||||
print('''
|
||||
OCTOSUITE (C) 2022 Richard Mwewa
|
||||
|
||||
is an advanced and lightning fast framework for gathering open-source intelligence on GitHub users and organizations.''')
|
||||
|
||||
|
||||
# Close session
|
||||
def exitSession(self):
|
||||
logging.info(logMsg.sessionClosed)
|
||||
print(Attributes.info, logMsg.sessionClosed);exit()
|
||||
|
||||
|
||||
def clearScreen(self):
|
||||
if sys.platform.lower().startswith(('win','darwin')):
|
||||
subprocess.run(['cls'])
|
||||
else:
|
||||
subprocess.run(['clear'],shell=False)
|
||||
1
downloads/.downloads
Normal file
1
downloads/.downloads
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
1
images/.images
Normal file
1
images/.images
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
BIN
images/logo.png
Normal file
BIN
images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
@@ -1,19 +0,0 @@
|
||||
from lib.colors import red,white,green,reset
|
||||
banner = f'''{red}
|
||||
▒█████ ▄████▄ ▄▄▄█████▓ ▒█████ ██████ █ ██ ██▓▄▄▄█████▓▓█████
|
||||
▒██▒ ██▒▒██▀ ▀█ ▓ ██▒ ▓▒▒██▒ ██▒▒██ ▒ ██ ▓██▒▓██▒▓ ██▒ ▓▒▓█ ▀
|
||||
▒██░ ██▒▒▓█ ▄ ▒ ▓██░ ▒░▒██░ ██▒░ ▓██▄ ▓██ ▒██░▒██▒▒ ▓██░ ▒░▒███
|
||||
▒██ ██░▒▓▓▄ ▄██▒░ ▓██▓ ░ ▒██ ██░ ▒ ██▒▓▓█ ░██░░██░░ ▓██▓ ░ ▒▓█ ▄
|
||||
░ ████▓▒░▒ ▓███▀ ░ ▒██▒ ░ ░ ████▓▒░▒██████▒▒▒█████▓ ░██░ ▒██▒ ░ ░▒████▒
|
||||
░ ▒░▒░▒░ ░ ░▒ ▒ ░ ▒ ░░ ░ ▒░▒░▒░ ▒ ▒▓▒ ▒ ░░▒▓▒ ▒ ▒ ░▓ ▒ ░░ ░░ ▒░ ░
|
||||
░ ▒ ▒░ ░ ▒ ░ ░ ▒ ▒░ ░ ░▒ ░ ░░░▒░ ░ ░ ▒ ░ ░ ░ ░ ░
|
||||
░ ░ ░ ▒ ░ ░ ░ ░ ░ ▒ ░ ░ ░ ░░░ ░ ░ ▒ v1.5.0
|
||||
░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
|
||||
░ {white}— Advanced Github {red}OSINT{white} Framework{reset}
|
||||
|
||||
|
||||
> {white}use '{green}help{white}' command for usage{reset}
|
||||
> {white}commands are case sensitive
|
||||
{'-'*30}
|
||||
{reset}
|
||||
'''
|
||||
@@ -1,16 +0,0 @@
|
||||
import sys
|
||||
|
||||
# Colors will be unavailable on non-linux machines
|
||||
colors = True
|
||||
machine = sys.platform
|
||||
if machine.lower().startswith(("os", "win", "darwin")):
|
||||
colors = False
|
||||
|
||||
if not colors:
|
||||
reset = red = white = green = ""
|
||||
|
||||
else:
|
||||
white = "\033[97m"
|
||||
red = "\033[91m"
|
||||
reset = "\033[0m"
|
||||
green = "\033[92m"
|
||||
29
octosuite
29
octosuite
@@ -1,20 +1,19 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import logging
|
||||
from src.main import *
|
||||
from lib.colors import red,white,green,reset
|
||||
from utilities.colors import Color
|
||||
from utilities.misc import Banner
|
||||
from core.main import Octosuite, Attributes, logMsg
|
||||
|
||||
print(Banner.nameLogo)
|
||||
if __name__ == '__main__':
|
||||
while True:
|
||||
try:
|
||||
octosuite().on_start()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logging.info('Process interrupted with Ctrl+C')
|
||||
exit(f'\n{white}[{red}x{white}] Process interrupted with {red}Ctrl{white}+{red}C{reset}')
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f'An error occured: {e}')
|
||||
print(f'{white}[{red}!{white}] An error occured: {red}{e}{reset}')
|
||||
input(f'\n{white}^ Press any key to continue{reset} ')
|
||||
try:
|
||||
Octosuite().onStart()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logging.warning(logMsg.Ctrl)
|
||||
print(Attributes.warning, logMsg.Ctrl)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(logMsg.Error.format(e))
|
||||
print(Attributes.error, logMsg.Error.format(f'{Color.red}{e}{Color.reset}'))
|
||||
|
||||
469
src/main.py
469
src/main.py
@@ -1,469 +0,0 @@
|
||||
import os
|
||||
import logging
|
||||
import requests
|
||||
import platform
|
||||
import subprocess
|
||||
import urllib.request
|
||||
from tqdm import tqdm
|
||||
from pprint import pprint
|
||||
from lib.banner import banner
|
||||
from datetime import datetime
|
||||
from lib.colors import red, white, green, reset
|
||||
|
||||
class octosuite:
|
||||
def __init__(self):
|
||||
# Path attribute
|
||||
self.path_attrs =['size','type','path','sha','html_url']
|
||||
# Path attribute dictionary
|
||||
self.path_attr_dict = {'size': 'Size (bytes)',
|
||||
'type': 'Type',
|
||||
'path': 'Path',
|
||||
'sha': 'SHA',
|
||||
'html_url': 'URL'}
|
||||
|
||||
# Organization 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
|
||||
self.org_attr_dict = {'avatar_url': 'Profile Photo',
|
||||
'login': 'Username',
|
||||
'id': 'ID#',
|
||||
'node_id': 'Node ID',
|
||||
'email': 'Email',
|
||||
'description': 'About',
|
||||
'location': 'Location',
|
||||
'blog': 'Blog',
|
||||
'followers': 'Followers',
|
||||
'following': 'Following',
|
||||
'twitter_username': 'Twitter Handle',
|
||||
'public_gists': 'Gists (public)',
|
||||
'public_repos': 'Repositories (public)',
|
||||
'type': 'Account type',
|
||||
'is_verified': 'Is verified?',
|
||||
'has_organization_projects': 'Has organization projects?',
|
||||
'has_repository_projects': 'Has repository projects?',
|
||||
'created_at': 'Created at',
|
||||
'updated_at': 'Updated at'}
|
||||
# Repository attributes
|
||||
self.repo_attrs = ['id','description','forks','allow_forking','fork','stargazers_count','watchers','license','default_branch','visibility','language','open_issues','topics','homepage','clone_url','ssh_url','private','archived','has_downloads','has_issues','has_pages','has_projects','has_wiki','pushed_at','created_at','updated_at']
|
||||
# Repository attribute dictionary
|
||||
self.repo_attr_dict = {'id': 'ID#',
|
||||
'description': 'About',
|
||||
'forks': 'Forks',
|
||||
'allow_forking': 'Is forkable?',
|
||||
'fork': 'Is fork?',
|
||||
'stargazers_count': 'Stars',
|
||||
'watchers': 'Watchers',
|
||||
'license': 'License',
|
||||
'default_branch': 'Branch',
|
||||
'visibility': 'Visibility',
|
||||
'language': 'Language(s)',
|
||||
'open_issues': 'Open issues',
|
||||
'topics': 'Topics',
|
||||
'homepage': 'Homepage',
|
||||
'clone_url': 'Clone URL',
|
||||
'ssh_url': 'SSH URL',
|
||||
'private': 'Is private?',
|
||||
'archived': 'Is archived?',
|
||||
'is_template': 'Is template?',
|
||||
'has_wiki': 'Has wiki?',
|
||||
'has_pages': 'Has pages?',
|
||||
'has_projects': 'Has projects?',
|
||||
'has_issues': 'Has issues?',
|
||||
'has_downloads': 'Has downloads?',
|
||||
'pushed_at': 'Pushed at',
|
||||
'created_at': 'Created at',
|
||||
'updated_at': 'Updated at'}
|
||||
|
||||
# Profile attributes
|
||||
self.profile_attrs = ['avatar_url','login','id','node_id','bio','blog','location','followers','following','twitter_username','public_gists','public_repos','company','hireable','site_admin','created_at','updated_at']
|
||||
# Profile attribute dictionary
|
||||
self.profile_attr_dict = {'avatar_url': 'Profile Photo',
|
||||
'login': 'Username',
|
||||
'id': 'ID#',
|
||||
'node_id': 'Node ID',
|
||||
'bio': 'Bio',
|
||||
'blog': 'Blog',
|
||||
'location': 'Location',
|
||||
'followers': 'Followers',
|
||||
'following': 'Following',
|
||||
'twitter_username': 'Twitter Handle',
|
||||
'public_gists': 'Gists (public)',
|
||||
'public_repos': 'Repositories (public)',
|
||||
'company': 'Organization',
|
||||
'hireable': 'Is hireable?',
|
||||
'site_admin': 'Is site admin?',
|
||||
'created_at': 'Joined at',
|
||||
'updated_at': 'Updated at'}
|
||||
|
||||
# User attributes
|
||||
self.user_attrs = ['avatar_url','id','node_id','gravatar_id','site_admin','type','html_url']
|
||||
# User attribute dictionary
|
||||
self.user_attr_dict = {'avatar_url': 'Profile Photo',
|
||||
'id': 'ID#',
|
||||
'node_id': 'Node ID',
|
||||
'gravatar_id': 'Gravatar ID',
|
||||
'site_admin': 'Is site admin?',
|
||||
'type': 'Account type',
|
||||
'html_url': 'URL'}
|
||||
|
||||
# Topic atrributes
|
||||
self.topic_attrs = ['score','curated','featured','display_name','created_by','created_at','updated_at']
|
||||
# Topic attribute dictionary
|
||||
self.topic_attr_dict = {'score': 'Score',
|
||||
'curated': 'Curated',
|
||||
'featured': 'Featured',
|
||||
'display_name': 'Display Name',
|
||||
'created_by': 'Created by',
|
||||
'created_at': 'Created at',
|
||||
'updated_at': 'Updated at'}
|
||||
|
||||
# Gists attributes
|
||||
self.gists_attrs = ['node_id','description','comments','files','git_push_url','public','truncated','updated_at']
|
||||
# Gists attribute dictionary
|
||||
self.gists_attr_dict = {'node_id': 'Node ID',
|
||||
'description': 'About',
|
||||
'comments': 'Comments',
|
||||
'files': 'Files',
|
||||
'git_push_url': 'Git Push URL',
|
||||
'public': 'Is public?',
|
||||
'truncated': 'Is truncated?',
|
||||
'updated_at': 'Updated at'}
|
||||
|
||||
# Issue attributes
|
||||
self.issue_attrs = ['id','node_id','score','state','number','comments','milestone','assignee','assignees','labels','locked','draft','closed_at','body']
|
||||
# Issue attribute dict
|
||||
self.issue_attr_dict = {'id': 'ID#',
|
||||
'node_id': 'Node ID',
|
||||
'score': 'Score',
|
||||
'state': 'State',
|
||||
'closed_at': 'Closed at',
|
||||
'number': 'Number',
|
||||
'comments': 'Comments',
|
||||
'milestone': 'Milestone',
|
||||
'assignee': 'Assignee',
|
||||
'assignees': 'Assignees',
|
||||
'labels': 'Labels',
|
||||
'draft': 'Is draft?',
|
||||
'locked': 'Is locked?',
|
||||
'created_at': 'Created at',
|
||||
'body': 'Body'}
|
||||
|
||||
# Author dictionary
|
||||
self.author_dict = {'Alias': 'rly0nheart',
|
||||
'Country': 'Zambia, Africa',
|
||||
'About.me': 'https://about.me/rly0nheart'}
|
||||
|
||||
def on_start(self):
|
||||
logging.info(f'Started new session on {platform.node()}')
|
||||
while True:
|
||||
if platform.system() == 'Windows':
|
||||
subprocess.run(['cls'])
|
||||
else:
|
||||
subprocess.run(['clear'],shell=False)
|
||||
|
||||
print(banner)
|
||||
command = input(f'''{white}┌─({red}{platform.node()}{white}@{red}octosuite{white})-[{green}{os.getcwd()}{white}]\n└─╼[{green}:~{white}]{reset} ''')
|
||||
if command == 'orginfo':
|
||||
self.org_info()
|
||||
elif command == 'userinfo':
|
||||
self.user_profile()
|
||||
elif command == 'repoinfo':
|
||||
self.repo_info()
|
||||
elif command == 'pathcontents':
|
||||
self.path_contents()
|
||||
elif command == 'orgrepos':
|
||||
self.org_repos()
|
||||
elif command == 'userrepos':
|
||||
self.user_repos()
|
||||
elif command == 'usergists':
|
||||
self.user_gists()
|
||||
elif command == 'userfollowers':
|
||||
self.followers()
|
||||
elif command == 'userfollowing':
|
||||
self.following()
|
||||
elif command == 'usersearch':
|
||||
self.user_search()
|
||||
elif command == 'reposearch':
|
||||
self.repo_search()
|
||||
elif command == 'topicsearch':
|
||||
self.topic_search()
|
||||
elif command == 'issuesearch':
|
||||
self.issue_search()
|
||||
elif command == 'commitsearch':
|
||||
self.commits_search()
|
||||
elif command == 'update':
|
||||
self.update()
|
||||
elif command == 'changelog':
|
||||
print(self.changelog())
|
||||
elif command == 'author':
|
||||
self.author()
|
||||
elif command == 'help':
|
||||
print(self.help())
|
||||
elif command == 'exit':
|
||||
logging.info('Session terminated.')
|
||||
exit(f'\n{white}[{red}-{white}] Session terminated.{reset}')
|
||||
else:
|
||||
print(f'\n{white}[{red}!{white}] Unknown command: ‘{command}’{reset}')
|
||||
logging.warning(f'Unknown command: ‘{command}’')
|
||||
|
||||
input(f'\n{white}^ Press any key to continue{reset} ')
|
||||
|
||||
|
||||
def org_info(self):
|
||||
organization = input(f'{white}@{green}Organization{white} >> {reset}')
|
||||
api = f'https://api.github.com/orgs/{organization}'
|
||||
response = requests.get(api)
|
||||
if response.status_code != 200:
|
||||
print(f'\n{white}[{red}-{white}] Organization @{organization} {red}Not Found{reset}')
|
||||
else:
|
||||
response = response.json()
|
||||
print(f"\n{white}{response['name']}{reset}")
|
||||
for attr in self.org_attrs:
|
||||
print(f'{white}├─ {self.org_attr_dict[attr]}: {green}{response[attr]}{reset}')
|
||||
|
||||
|
||||
# Fetching user information
|
||||
def user_profile(self):
|
||||
username = input(f'{white}@{green}Username{white} >> {reset}')
|
||||
api = f'https://api.github.com/users/{username}'
|
||||
response = requests.get(api)
|
||||
if response.status_code != 200:
|
||||
print(f'\n{white}[{red}-{white}] User @{username} {red}Not Found{reset}')
|
||||
else:
|
||||
response = response.json()
|
||||
print(f"\n{white}{response['name']}{reset}")
|
||||
for attr in self.profile_attrs:
|
||||
print(f'{white}├─ {self.profile_attr_dict[attr]}: {green}{response[attr]}{reset}')
|
||||
|
||||
|
||||
# Fetching repository information
|
||||
def repo_info(self):
|
||||
username = input(f'{white}@{green}Owner-username{white} >> {reset}')
|
||||
repo_name = input(f'{white}%{green}reponame{white} >> {reset}')
|
||||
api = f'https://api.github.com/repos/{username}/{repo_name}'
|
||||
response = requests.get(api)
|
||||
if response.status_code != 200:
|
||||
print(f'\n{white}[{red}-{white}] Repository %{repo_name} {red}Not Found{reset}')
|
||||
else:
|
||||
response = response.json()
|
||||
print(f"\n{white}{response['full_name']}{reset}")
|
||||
for attr in self.repo_attrs:
|
||||
print(f"{white}├─ {self.repo_attr_dict[attr]}: {green}{response[attr]}{reset}")
|
||||
|
||||
|
||||
# Get path contents
|
||||
def path_contents(self):
|
||||
username = input(f'{white}@{green}Owner-username{white} >> {reset}')
|
||||
repo_name = input(f'{white}%{green}reponame{white} >> {reset}')
|
||||
path_name = input(f'{white}/path/name >>{reset} ')
|
||||
api = f'https://api.github.com/repos/{username}/{repo_name}/contents/{path_name}'
|
||||
response = requests.get(api)
|
||||
if response.status_code != 200:
|
||||
print(f'\n{white}[{red}-{white}] Information {red}Not Found{reset}')
|
||||
else:
|
||||
response = response.json()
|
||||
for item in response:
|
||||
print(f"\n{white}{item['name']}{reset}")
|
||||
for attr in self.path_attrs:
|
||||
print(f'{white}├─ {self.path_attr_dict[attr]}: {green}{item[attr]}{reset}')
|
||||
|
||||
|
||||
# Fetching organozation repositories
|
||||
def org_repos(self):
|
||||
organization = input(f'{white}@{green}Organization{white} >> {reset}')
|
||||
api = f'https://api.github.com/orgs/{organization}/repos?per_page=100'
|
||||
response = requests.get(api)
|
||||
if response.status_code != 200:
|
||||
print(f'\n{white}[{red}-{white}] Organization @{organization} {red}Not Found{reset}')
|
||||
else:
|
||||
response = response.json()
|
||||
for repo in response:
|
||||
print(f"\n{white}{repo['full_name']}{reset}")
|
||||
for attr in self.repo_attrs:
|
||||
print(f"{white}├─ {self.repo_attr_dict[attr]}: {green}{repo[attr]}{reset}")
|
||||
print('\n')
|
||||
|
||||
|
||||
# Fetching user repositories
|
||||
def user_repos(self):
|
||||
username = input(f'{white}@{green}Username{white} >> {reset}')
|
||||
api = f'https://api.github.com/users/{username}/repos?per_page=100'
|
||||
response = requests.get(api)
|
||||
if response.status_code != 200:
|
||||
print(f'\n{white}[{red}-{white}] User @{username} {red}Not Found{reset}')
|
||||
else:
|
||||
response = response.json()
|
||||
for repo in response:
|
||||
print(f"\n{white}{repo['full_name']}{reset}")
|
||||
for attr in self.repo_attrs:
|
||||
print(f"{white}├─ {self.repo_attr_dict[attr]}: {green}{repo[attr]}{reset}")
|
||||
print('\n')
|
||||
|
||||
|
||||
# Fetching user's gists
|
||||
def user_gists(self):
|
||||
username = input(f'{white}@{green}Username{white} >> {reset}')
|
||||
api = f'https://api.github.com/users/{username}/gists'
|
||||
response = requests.get(api).json()
|
||||
if response == []:
|
||||
print(f'{white}[{red}-{white}]User @{username} does not have any active gists.{reset}')
|
||||
else:
|
||||
for item in response:
|
||||
print(f"\n{white}{item['id']}{reset}")
|
||||
for attr in self.gists_attrs:
|
||||
print(f"{white}├─ {self.gists_attr_dict[attr]}: {green}{item[attr]}{reset}")
|
||||
print('\n')
|
||||
|
||||
|
||||
# Fetching user's followera'
|
||||
def followers(self):
|
||||
username = input(f'{white}@{green}Username{white} >> {reset}')
|
||||
api = f'https://api.github.com/users/{username}/followers?per_page=100'
|
||||
response = requests.get(api).json()
|
||||
if response == []:
|
||||
print(f'\n{white}[{red}-{white}]User @{username} does not have followers.{reset}')
|
||||
else:
|
||||
for item in response:
|
||||
print(f"\n{white}@{item['login']}{reset}")
|
||||
for attr in self.user_attrs:
|
||||
print(f"{white}├─ {self.user_attr_dict[attr]}: {green}{item[attr]}{reset}")
|
||||
print('\n')
|
||||
|
||||
|
||||
# Checking whether or not user[A] follows user[B]
|
||||
def following(self):
|
||||
user_a = input(f'{white}@{green}User[A]{white} >> {reset}')
|
||||
user_b = input(f'{white}@{green}User[B]{white} >> {reset}')
|
||||
api = f'https://api.github.com/users/{user_a}/following/{user_b}'
|
||||
response = requests.get(api)
|
||||
if response.status_code == 204:
|
||||
print(f'{white}[{green}+{white}] @{user_a} follows @{user_b}.{reset}')
|
||||
else:
|
||||
print(f'{white}[{red}-{white}] @{user_a} does not follow @{user_b}.{reset}')
|
||||
|
||||
|
||||
# User search
|
||||
def user_search(self):
|
||||
query = input(f'{white}#{green}Query{white} >> {reset}')
|
||||
api = f'https://api.github.com/search/users?q={query}&per_page=100'
|
||||
response = requests.get(api).json()
|
||||
for item in response['items']:
|
||||
print(f"\n{white}@{item['login']}{reset}")
|
||||
for attr in self.user_attrs:
|
||||
print(f"{white}├─ {self.user_attr_dict[attr]}: {green}{item[attr]}{reset}")
|
||||
print('\n')
|
||||
|
||||
|
||||
# Repository search
|
||||
def repo_search(self):
|
||||
query = input(f'{white}#{green}Query{white} >> {reset}')
|
||||
api = f'https://api.github.com/search/repositories?q={query}&per_page=100'
|
||||
response = requests.get(api).json()
|
||||
for item in response['items']:
|
||||
print(f"\n{white}{item['full_name']}{reset}")
|
||||
for attr in self.repo_attrs:
|
||||
print(f"{white}├─ {self.repo_attr_dict[attr]}: {green}{item[attr]}{reset}")
|
||||
print('\n')
|
||||
|
||||
|
||||
# Topics search
|
||||
def topic_search(self):
|
||||
query = input(f'{white}#{green}Query{white} >> {reset}')
|
||||
api = f'https://api.github.com/search/topics?q={query}&per_page=100'
|
||||
response = requests.get(api).json()
|
||||
for item in response['items']:
|
||||
print(f"\n{white}{item['name']}{reset}")
|
||||
for attr in self.topic_attrs:
|
||||
print(f"{white}├─ {self.topic_attr_dict[attr]}: {green}{item[attr]}{reset}")
|
||||
print('\n')
|
||||
|
||||
|
||||
# Issue search
|
||||
def issue_search(self):
|
||||
query = input(f'{white}#{green}Query{white} >> {reset}')
|
||||
api = f'https://api.github.com/search/issues?q={query}&per_page=100'
|
||||
response = requests.get(api).json()
|
||||
for item in response['items']:
|
||||
print(f"\n{white}{item['title']}{reset}")
|
||||
for attr in self.issue_attrs:
|
||||
print(f"{white}├─ {self.issue_attr_dict[attr]}: {green}{item[attr]}{reset}")
|
||||
print('\n')
|
||||
|
||||
|
||||
# Commits search
|
||||
def commits_search(self):
|
||||
query = input(f'{white}#{green}Query{white} >> {reset}')
|
||||
api = f'https://api.github.com/search/commits?q={query}&per_page=100'
|
||||
response = requests.get(api).json()
|
||||
n=0
|
||||
for item in response['items']:
|
||||
n+=1
|
||||
print(f'{white}{n}.{reset}')
|
||||
pprint(item['commit'])
|
||||
print('\n')
|
||||
|
||||
|
||||
# Update program
|
||||
def update(self):
|
||||
logging.info('Fetching updates...')
|
||||
files_to_update = ['src/main.py','lib/banner.py','lib/colors.py','octosuite','.github/dependabot.yml','LICENSE','README.md','requirements.txt']
|
||||
for file in tqdm(files_to_update,desc=f'{white}[{green}*{white}] Updating{reset}'):
|
||||
data = urllib.request.urlopen(f'https://raw.githubusercontent.com/rly0nheart/octosuite/master/{file}').read()
|
||||
with open(file, 'wb') as code:
|
||||
code.write(data)
|
||||
code.close()
|
||||
|
||||
logging.info('Update complete.')
|
||||
exit(f'{white}[{green}+{white}] Updated successfully. Re-run octosuite.{reset}')
|
||||
|
||||
|
||||
# Show changelog
|
||||
def changelog(self):
|
||||
# lol yes the changelog is hard coded
|
||||
changelog_text = '''
|
||||
v1.5.0 Changelog:
|
||||
|
||||
• Fixed import error in src/main.py
|
||||
'''
|
||||
return changelog_text
|
||||
|
||||
|
||||
# Author info
|
||||
def author(self):
|
||||
print(f'\n{white}Richard Mwewa (Ritchie){reset}')
|
||||
for key,value in self.author_dict.items():
|
||||
print(f'{white}├─ {key}: {green}{value}{reset}')
|
||||
|
||||
|
||||
def help(self):
|
||||
help = '''
|
||||
|
||||
help:
|
||||
|
||||
Command Descritption
|
||||
------------ ---------------------------------------------------------
|
||||
orginfo --> Get target organization info
|
||||
userinfo --> Get target user profile info
|
||||
repoinfo --> Get target repository info
|
||||
pathcontents --> Get contents of a specified path from a target repository
|
||||
orgrepos --> Get a list of repositories owned by a target organization
|
||||
userrepos --> Get a list of repositories owned by a target user
|
||||
usergists --> Get a list of gists owned by a target user
|
||||
userfollowers --> Get a list of the target's followers
|
||||
userfollowing --> Check whether or not User[A] follows User[B]
|
||||
usersearch --> Search user(s)
|
||||
reposearch --> Search repositor[y][ies]
|
||||
topicsearch --> Search topic(s)
|
||||
issuesearch --> Search issue(s)
|
||||
commitsearch --> Search commit(s)
|
||||
update --> Update octosuite
|
||||
changelog --> Show changelog
|
||||
author --> Show author info
|
||||
help --> Show usage/help
|
||||
exit --> Exit session
|
||||
'''
|
||||
return help
|
||||
|
||||
|
||||
# Set to automatically monitor and log network and user activity into the .logs folder
|
||||
logging.basicConfig(filename=f'.logs/{datetime.now()}.log',format='[%(asctime)s] %(message)s',level=logging.DEBUG)
|
||||
57
utilities/colors.py
Normal file
57
utilities/colors.py
Normal file
@@ -0,0 +1,57 @@
|
||||
import sys
|
||||
import getpass
|
||||
import platform
|
||||
from datetime import datetime
|
||||
|
||||
# The Color class is responsible for enabling/disabling colors in OctoSuite
|
||||
# This class gets called first at start up before any other class/method gets called (makes one think why this is not the firstBlood class)
|
||||
# Color class is the reason why users get to choose whether to enable/disable colors
|
||||
# Unfortunately for our friends the 'non-Linux' users, they will not yet have the opportunity to see what OctoSuite looks like with colors enabled lol
|
||||
class Color:
|
||||
colors = True
|
||||
# Colors will be unavailable on non-linux machines
|
||||
if sys.platform.lower().startswith(("os", "win", "darwin","ios")):
|
||||
colors = False
|
||||
|
||||
if not colors:
|
||||
reset = red = white = green = red_bg = ""
|
||||
|
||||
else:
|
||||
# Printing system information was completely unnecessary (just like most things in this program :D)
|
||||
# But at least users will get to know things they did not know about their machines ;)
|
||||
date_time = datetime.now()
|
||||
sys_info = [("Processor",platform.processor),
|
||||
("Node", platform.node),
|
||||
("Release", platform.release),
|
||||
("Architecture", platform.architecture),
|
||||
("Version", platform.version)]
|
||||
|
||||
banner = f"""
|
||||
OCTOSUITE © 2022 Richard Mwewa
|
||||
{date_time.strftime('%A %d %B %Y, %H:%M:%S%p')}
|
||||
|
||||
|
||||
|
||||
{platform.system()}"""
|
||||
print(banner)
|
||||
for key, value in sys_info:
|
||||
print(f"\t├─ {key}: {value()}")
|
||||
print("\n")
|
||||
while True:
|
||||
try:
|
||||
color_chooser = input(f"[ ? ] Welcome {getpass.getuser()}, would you like to enable colors for this session? (y/n) ")
|
||||
if color_chooser.lower() == "y":
|
||||
white = "\033[97m"
|
||||
red = "\033[91m"
|
||||
reset = "\033[0m"
|
||||
green = "\033[92m"
|
||||
red_bg = "\033[41;37m"
|
||||
break
|
||||
elif color_chooser.lower() == "n":
|
||||
red = white = green = red_bg = reset = ""
|
||||
break
|
||||
else:
|
||||
print(f"\n[ ! ] Your response ({color_chooser}) is invalid (expected y or n) ")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
exit(f"[ ! ] Process interrupted with (Ctrl+C).")
|
||||
118
utilities/helper.py
Normal file
118
utilities/helper.py
Normal file
@@ -0,0 +1,118 @@
|
||||
from utilities.colors import Color
|
||||
|
||||
class Help:
|
||||
usageText = 'Use {} to get started'
|
||||
def updateCommand():
|
||||
print(f'''
|
||||
{Color.white}Update subcommands{Color.reset}
|
||||
{'='*18}
|
||||
{Help.usageText.format('update:<subcommand>')}
|
||||
|
||||
Command Description
|
||||
------- -----------
|
||||
check Check for updates
|
||||
install Download and install updates''')
|
||||
|
||||
|
||||
def searchCommand():
|
||||
print(f'''
|
||||
{Color.white}Search subcommands{Color.reset}
|
||||
{'='*18}
|
||||
{Help.usageText.format('search:<subcommand>')}
|
||||
|
||||
Command Description
|
||||
------- -----------
|
||||
users Search user(s)
|
||||
repos Search repositor[yies]
|
||||
topics Search topic(s)
|
||||
issues Search issue(s)
|
||||
commits Search commit(s)''')
|
||||
|
||||
|
||||
def userCommand():
|
||||
print(f'''
|
||||
{Color.white}User subcommands{Color.reset}
|
||||
{'='*17}
|
||||
{Help.usageText.format('user:<subcommand>')}
|
||||
|
||||
Command Description
|
||||
------- -----------
|
||||
profile Get a user's profile info
|
||||
gists Return a users's gists
|
||||
orgs Return organizations that a user belongs to/owns
|
||||
repos Return a user's repositories
|
||||
events Return a user's events
|
||||
followers Return a user's followers
|
||||
following Check if user[A] follows user[B]
|
||||
subscriptions Return a user's subscriptions''')
|
||||
|
||||
|
||||
def orgCommand():
|
||||
print(f'''
|
||||
{Color.white}Org subcommands{Color.reset}
|
||||
{'='*16}
|
||||
{Help.usageText.format('org:<subcommand>')}
|
||||
|
||||
Command Description
|
||||
------- -----------
|
||||
profile Get an organization's info
|
||||
repos Return an organization's repositories
|
||||
events Return an organization's events
|
||||
member Check if a specified user is a public member of the target organization''')
|
||||
|
||||
|
||||
def repoCommand():
|
||||
print(f'''
|
||||
{Color.white}Repo subcommands{Color.reset}
|
||||
{'='*17}
|
||||
{Help.usageText.format('repo:<subcommand>')}
|
||||
|
||||
Command Description
|
||||
------- -----------
|
||||
profile Get a repository's info
|
||||
forks Return a repository's forks
|
||||
releases Return a repository's releases
|
||||
languages Return a repository's languages
|
||||
stargazers Return a repository's stargazers
|
||||
pathcontents List contents in a path of a repository''')
|
||||
|
||||
|
||||
def logsCommand():
|
||||
print(f'''
|
||||
{Color.white}Logs subcommands{Color.reset}
|
||||
{'='*17}
|
||||
{Help.usageText.format('logs:<subcommand>')}
|
||||
|
||||
Command Description
|
||||
------- -----------
|
||||
view View logs
|
||||
read Read log
|
||||
delete Delete log''')
|
||||
|
||||
|
||||
def helpCommand():
|
||||
print(f'''
|
||||
{Color.white}Core commands{Color.reset}
|
||||
{'='*13}
|
||||
|
||||
{Color.white}Command Description{Color.reset}
|
||||
------- -----------
|
||||
help Help menu
|
||||
exit Close session
|
||||
clear Clear screen
|
||||
about Program's info
|
||||
author Developer's info
|
||||
version Version info
|
||||
|
||||
|
||||
{Color.white}Help subcommands{Color.reset}
|
||||
{'='*16}
|
||||
{Help.usageText.format('help:<subcommand>')}
|
||||
|
||||
{Color.white}Command Description{Color.reset}
|
||||
------- -----------
|
||||
logs List all logs management commands
|
||||
repo List all repository investigation commands
|
||||
user List all users investigation commands
|
||||
search List all target discovery commands
|
||||
update List all program updates managememt commands''')
|
||||
20
utilities/misc.py
Normal file
20
utilities/misc.py
Normal file
@@ -0,0 +1,20 @@
|
||||
import getpass
|
||||
from utilities.colors import Color
|
||||
|
||||
class Banner:
|
||||
versionTag = '2.0.1-alpha'
|
||||
nameLogo = f'''{Color.white}
|
||||
_______ __ _______ __ __
|
||||
| |.----.| |_.-----.| __|.--.--.|__| |_.-----.
|
||||
| - || __|| _| _ ||__ || | || | _| -__|
|
||||
|_______||____||____|_____||_______||_____||__|____|_____|
|
||||
v{versionTag}
|
||||
{Color.white}— Advanced Github {Color.red}OSINT{Color.white} Framework{Color.reset}
|
||||
|
||||
|
||||
.:{Color.white}{Color.green}{getpass.getuser()}{Color.reset}:.
|
||||
|
||||
- {Color.white}Use {Color.green}help{Color.reset}{Color.white} command for usage{Color.reset}
|
||||
- {Color.white}Commands are case insensitive{Color.reset}
|
||||
{'-'*32}
|
||||
'''
|
||||
Reference in New Issue
Block a user