Skip to content

Commit

Permalink
Core fixes. Provide global variables. Update runner. Upgrade module-p…
Browse files Browse the repository at this point in the history
…ackage system. (#14)

* Clean init all the modules with the __init__.py file

* Remove unused gitkeeps

* Add base inits for script packages

* Update main file

* Set __file__ variable at the module level

* Add inits for the modules

* Modify all the modules

* Delete one more .gitkeep

* Add WIP test for user_greeting

* Update test module for user_greeting

* Fix module runner a bit

* Add relative imports to the modules

* Add run tests script

* Update README.md

* Fix module-package runner

* Update requirements

* Format everything with Black

* Handle module and execution errors with runner
  • Loading branch information
manmolecular authored Jul 18, 2020
1 parent 01b2b3e commit 9d50918
Show file tree
Hide file tree
Showing 44 changed files with 712 additions and 411 deletions.
69 changes: 69 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,79 @@
virtualenv -p python3 venv
(or python3 -m venv venv)
pip3 install -r requirements.txt
```

## Running as a framework
_First of all: provide some arguments in the `main.py` file to collect information based on your data (WIP now, will be improved later)_

To run the framework:
```bash
python3 main.py
```
To run the tests:
```bash
chmod +x run_tests.sh
./run_tests.sh
```
_or you can run them like this, of course:_
```bash
python3 -m unittest discover -v
```
## Running as a separated module
Basic:
```python3
python3 -m src.scripts.<category>.<name> any_arguments_here
```
Example command:
```bash
python3 -m src.scripts.other.user_greeting JohnDoe
```
Example output:
```
{'message': "Successfully finished! (args: (), kwargs: {'username': "
"'johndoe'})",
'result': 'Hello, JohnDoe!',
'status': 'success'}
```

## Create your own script
Use the following structure:
1. Create your own module directory in the following way:
```
/src/scripts/<choose_your_category_here>/<your_script_name>/<script_files>
```
2. Provide the following structure of your script directory:
```
your_script_name
├── __init__.py - use this module to set the default parent directory (you can copy this file from any other script)
├── __main__.py - use this module to provide some basic interface to use your script as a module (the same as if __name__ == "__main__")
├── module.py - use this module to describe the basic logic of your module (you can import it in the __main__.py to provide interface)
└── test_module.py - use this module for unittest tests
```
3. Create the `__init__.py` file. An example of the `__init__.py` boilerplate structure can be seen below:
```python3
import sys
from pathlib import Path

__root_dir = Path(__file__).parents[4]
sys.path.append(str(__root_dir))

```
4. Create the `__main__.py` file. An example of the `__main__.py` boilerplate structure can be seen below:
```python3
#!/usr/bin/env python3

from pprint import pprint
from sys import argv

from src.core.utils.module import run_module
from .module import Runner

result = run_module(Runner, args=argv, arg_name="username", arg_default="johndoe")
pprint(result)
```
5. Create the module itself. An example of the basic `module.py` file can be seen below:
```python3
#!/usr/bin/env python3

Expand Down Expand Up @@ -60,3 +128,4 @@ class Runner(OsintRunner):
argument = kwargs.get("my_argument", "Arguments were not provided!")
return ScriptResponse.success(message=f"Script finished with argument {argument}")
```
6. For `test_module.py` you can use any required tests (as you wish). A test case for your module is required to keep the project clean.
47 changes: 27 additions & 20 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,33 @@
from src.core.case.osint import OsintCase
from src.core.case.recon import ReconCase
from src.core.case.base import BaseCase
from src.core.utils.log import Logger


logger = Logger.get_logger(name="osint-framework")


def run_case(case_class: type, *args, **kwargs):
"""
Define and smoke run the BaseCase
:param case_class: original class of the case
:param args: some args
:param kwargs: some kwargs
:return: result of the execution
"""
logger.info(f"start {case_class.__name__} case processing")
case = case_class()
case.process(*args, **kwargs)
return case.get_results()


if __name__ == "__main__":
# Will return 2 results from "other" category scripts
other_case = BaseCase()
other_case.process(
username="johndoe", email="johndoe@gmail.com", fullname="John Doe",
)
other_case_results = other_case.get_results()

# Will return 1 result from "recon" category scripts
recon_case = ReconCase()
recon_case.process(url="https://facebook.com")
recon_case_results = recon_case.get_results()

# Will return nothing (no scripts for now, sorry!)
osint_case = OsintCase()
osint_case.process(username="any_value_here")
osint_case_results = osint_case.get_results()

# Print out all the results
for result in other_case_results, recon_case_results, osint_case_results:
pprint(result)
# fmt: off
base_case = run_case(case_class=BaseCase, username="johndoe", email="johndoe@gmail.com", fullname="John Doe")
osint_case = run_case(case_class=OsintCase, username="johndoe", email="johndoe@gmail.com", fullname="John Doe")
recon_case = run_case(case_class=ReconCase, url="https://facebook.com")

pprint(base_case)
pprint(osint_case)
pprint(recon_case)
# fmt: on
13 changes: 13 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
aiodns==2.0.0
aiohttp==3.6.2
aiosmtpd==1.2
async-timeout==3.0.1
atpublic==1.0
attrs==19.3.0
certifi==2020.6.20
cffi==1.14.0
chardet==3.0.4
idna==2.10
mmh3==2.5.1
multidict==4.7.6
pycares==3.1.1
pycparser==2.20
requests==2.24.0
urllib3==1.25.9
verify-email==2.4.1
yarl==1.4.2
3 changes: 3 additions & 0 deletions run_tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env bash

python3 -m unittest discover -v
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Empty file added src/core/handlers/__init__.py
Empty file.
Empty file added src/core/runner/__init__.py
Empty file.
37 changes: 29 additions & 8 deletions src/core/runner/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@ def __init__(self):
self.scripts = {}
self.results = {}

@staticmethod
def exec_script(
self,
path: str or Path,
script_class: str = "Runner",
function: str = "run",
args: list or None = None,
kwargs: dict or None = None,
) -> ScriptResponse:
) -> ScriptResponse or dict:
"""
Load and exec python script
:param path: name of the script to load
Expand All @@ -55,13 +55,34 @@ def exec_script(
kwargs = {}
loader = SourceFileLoader(fullname=script_class, path=str(path))
module = ModuleType(name=loader.name)
loader.exec_module(module)
class_instance = getattr(module, script_class)(logger=path.parent.stem)
result = {"script": Path(path).parent.stem}

# Check if don't forget to install all the dependencies, and that module can
# be successfully loaded.

# fmt: off
try:
loader.exec_module(module)
except Exception as unexp_err:
result.update(ScriptResponse.error(message=f"Unexpected module error: {str(unexp_err)}"))
return result
# fmt: on

# Module successfully loaded. We can set some module-scope variables that
# we missed.
module.__file__ = path

# Execute the runner and check if something goes wrong.

# fmt: off
try:
result = getattr(class_instance, function)(*args, **kwargs)
class_instance = getattr(module, script_class)(logger=path.parent.stem)
result.update(getattr(class_instance, function)(*args, **kwargs))
except Exception as unexp_err:
result = ScriptResponse.error(message=str(unexp_err))
result.update({"script": Path(path).parent.stem})
result.update(ScriptResponse.error(message=f"Unexpected execution error: {str(unexp_err)}"))
# fmt: on

# In any possible case, return result from the module or ScriptResponse.error + script name
return result

def get_scripts(self) -> dict:
Expand All @@ -76,7 +97,7 @@ def get_scripts(self) -> dict:
ScriptRunnerPaths.CONVERT,
]:
self.scripts.update({directory.stem: list()})
for file in directory.glob("*/__main__.py"):
for file in directory.glob("*/module.py"):
self.scripts[directory.stem].append(file)
return self.scripts

Expand Down
Empty file added src/core/utils/__init__.py
Empty file.
16 changes: 16 additions & 0 deletions src/core/utils/module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env python3


def run_module(
runner: type, args: list, arg_name: str, arg_default: str or None = None
) -> dict:
"""
Use module as a 'python3 -m ...' module
:param runner: runner object
:param args: list of args from CLI
:param arg_name: name of arg to use
:param arg_default: default arg value
:return: results
"""
runner = runner()
return runner.run(**{arg_name: args[1] if len(args) >= 2 else arg_default})
Empty file added src/scripts/__init__.py
Empty file.
Empty file added src/scripts/convert/__init__.py
Empty file.
Empty file added src/scripts/osint/__init__.py
Empty file.
5 changes: 5 additions & 0 deletions src/scripts/osint/check_nickname/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import sys
from pathlib import Path

__root_dir = Path(__file__).parents[4]
sys.path.append(str(__root_dir))
100 changes: 6 additions & 94 deletions src/scripts/osint/check_nickname/__main__.py
Original file line number Diff line number Diff line change
@@ -1,98 +1,10 @@
#!/usr/bin/env python3

import asyncio
from pathlib import Path
from pprint import pprint
from sys import argv

import aiohttp
import requests
from src.core.utils.module import run_module
from .module import Runner

from src.core.base.osint import OsintRunner
from src.core.utils.response import ScriptResponse


class Defaults:
NETWORKS_LIST = "social_networks.txt"


class Networks:
def __init__(self):
with open(Path("./src/scripts/osint/check_nickname/data/social_networks.txt")) as file:
self.net = file.read().splitlines()


def check_nickname_sync(nickname: str) -> list:
"""
checks nicknames from social networks(sync)
:param nickname: just nickname :)
:return: list with links to user from social
networks which have this nickname
"""
ans = []
social = Networks().net
for site in social:
try:
url = "https://{site}{nickname}".format(site=site, nickname=nickname)
response = requests.get(url)
if response.status_code == 200:
ans.append(url)
except:
pass
return ans


async def check_nickname_async(nickname: str, social) -> list:
"""
checks nicknames from social networks(async)
:param nickname: just nickname :)
:param social: social
:return: list with links to user from social
networks which have this nickname
"""
ans = []
async with aiohttp.ClientSession() as session:
while not social.empty():
url = await social.get()
try:
async with session.get(url) as response:
if response.status == 200:
ans.append(url)
except:
pass
return ans


class Runner(OsintRunner):
def __init__(self, logger: str = __name__):
super().__init__(logger=logger)

@staticmethod
async def __run(*args, **kwargs):
try:
username = kwargs.get("username")
social = asyncio.Queue()
for site in Networks().net:
await social.put(
"https://{site}{username}".format(site=site, username=username)
)
temp_result = await asyncio.gather(
*[
asyncio.create_task(check_nickname_async(username, social))
for _ in range(10)
]
)
result = {username: []}
for sub_massive in temp_result:
for site in sub_massive:
result[username].append(site)
return ScriptResponse.success(
result=result,
message="Found {count} user accounts".format(
count=len(result[username])
),
)
except Exception as err:
return ScriptResponse.error(message=str(err))

def run(self, *args, **kwargs):
username = kwargs.get("username")
return asyncio.run(self.__run(username=username))
result = run_module(Runner, args=argv, arg_name="username", arg_default="johndoe")
pprint(result)
Loading

0 comments on commit 9d50918

Please sign in to comment.