Skip to content

Commit 9d50918

Browse files
authored
Core fixes. Provide global variables. Update runner. Upgrade module-package 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
1 parent 01b2b3e commit 9d50918

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+712
-411
lines changed

README.md

+69
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,79 @@
66
virtualenv -p python3 venv
77
(or python3 -m venv venv)
88
pip3 install -r requirements.txt
9+
```
10+
11+
## Running as a framework
12+
_First of all: provide some arguments in the `main.py` file to collect information based on your data (WIP now, will be improved later)_
13+
14+
To run the framework:
15+
```bash
916
python3 main.py
1017
```
18+
To run the tests:
19+
```bash
20+
chmod +x run_tests.sh
21+
./run_tests.sh
22+
```
23+
_or you can run them like this, of course:_
24+
```bash
25+
python3 -m unittest discover -v
26+
```
27+
## Running as a separated module
28+
Basic:
29+
```python3
30+
python3 -m src.scripts.<category>.<name> any_arguments_here
31+
```
32+
Example command:
33+
```bash
34+
python3 -m src.scripts.other.user_greeting JohnDoe
35+
```
36+
Example output:
37+
```
38+
{'message': "Successfully finished! (args: (), kwargs: {'username': "
39+
"'johndoe'})",
40+
'result': 'Hello, JohnDoe!',
41+
'status': 'success'}
42+
43+
```
1144

1245
## Create your own script
1346
Use the following structure:
47+
1. Create your own module directory in the following way:
48+
```
49+
/src/scripts/<choose_your_category_here>/<your_script_name>/<script_files>
50+
```
51+
2. Provide the following structure of your script directory:
52+
```
53+
your_script_name
54+
├── __init__.py - use this module to set the default parent directory (you can copy this file from any other script)
55+
├── __main__.py - use this module to provide some basic interface to use your script as a module (the same as if __name__ == "__main__")
56+
├── module.py - use this module to describe the basic logic of your module (you can import it in the __main__.py to provide interface)
57+
└── test_module.py - use this module for unittest tests
58+
```
59+
3. Create the `__init__.py` file. An example of the `__init__.py` boilerplate structure can be seen below:
60+
```python3
61+
import sys
62+
from pathlib import Path
63+
64+
__root_dir = Path(__file__).parents[4]
65+
sys.path.append(str(__root_dir))
66+
67+
```
68+
4. Create the `__main__.py` file. An example of the `__main__.py` boilerplate structure can be seen below:
69+
```python3
70+
#!/usr/bin/env python3
71+
72+
from pprint import pprint
73+
from sys import argv
74+
75+
from src.core.utils.module import run_module
76+
from .module import Runner
77+
78+
result = run_module(Runner, args=argv, arg_name="username", arg_default="johndoe")
79+
pprint(result)
80+
```
81+
5. Create the module itself. An example of the basic `module.py` file can be seen below:
1482
```python3
1583
#!/usr/bin/env python3
1684

@@ -60,3 +128,4 @@ class Runner(OsintRunner):
60128
argument = kwargs.get("my_argument", "Arguments were not provided!")
61129
return ScriptResponse.success(message=f"Script finished with argument {argument}")
62130
```
131+
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.

main.py

+27-20
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,33 @@
1313
from src.core.case.osint import OsintCase
1414
from src.core.case.recon import ReconCase
1515
from src.core.case.base import BaseCase
16+
from src.core.utils.log import Logger
17+
18+
19+
logger = Logger.get_logger(name="osint-framework")
20+
21+
22+
def run_case(case_class: type, *args, **kwargs):
23+
"""
24+
Define and smoke run the BaseCase
25+
:param case_class: original class of the case
26+
:param args: some args
27+
:param kwargs: some kwargs
28+
:return: result of the execution
29+
"""
30+
logger.info(f"start {case_class.__name__} case processing")
31+
case = case_class()
32+
case.process(*args, **kwargs)
33+
return case.get_results()
1634

1735

1836
if __name__ == "__main__":
19-
# Will return 2 results from "other" category scripts
20-
other_case = BaseCase()
21-
other_case.process(
22-
username="johndoe", email="[email protected]", fullname="John Doe",
23-
)
24-
other_case_results = other_case.get_results()
25-
26-
# Will return 1 result from "recon" category scripts
27-
recon_case = ReconCase()
28-
recon_case.process(url="https://facebook.com")
29-
recon_case_results = recon_case.get_results()
30-
31-
# Will return nothing (no scripts for now, sorry!)
32-
osint_case = OsintCase()
33-
osint_case.process(username="any_value_here")
34-
osint_case_results = osint_case.get_results()
35-
36-
# Print out all the results
37-
for result in other_case_results, recon_case_results, osint_case_results:
38-
pprint(result)
37+
# fmt: off
38+
base_case = run_case(case_class=BaseCase, username="johndoe", email="[email protected]", fullname="John Doe")
39+
osint_case = run_case(case_class=OsintCase, username="johndoe", email="[email protected]", fullname="John Doe")
40+
recon_case = run_case(case_class=ReconCase, url="https://facebook.com")
41+
42+
pprint(base_case)
43+
pprint(osint_case)
44+
pprint(recon_case)
45+
# fmt: on

requirements.txt

+13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
1+
aiodns==2.0.0
2+
aiohttp==3.6.2
3+
aiosmtpd==1.2
4+
async-timeout==3.0.1
5+
atpublic==1.0
6+
attrs==19.3.0
17
certifi==2020.6.20
8+
cffi==1.14.0
29
chardet==3.0.4
310
idna==2.10
11+
mmh3==2.5.1
12+
multidict==4.7.6
13+
pycares==3.1.1
14+
pycparser==2.20
415
requests==2.24.0
516
urllib3==1.25.9
17+
verify-email==2.4.1
18+
yarl==1.4.2

run_tests.sh

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env bash
2+
3+
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.

src/core/handlers/__init__.py

Whitespace-only changes.

src/core/runner/__init__.py

Whitespace-only changes.

src/core/runner/runner.py

+29-8
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ def __init__(self):
3232
self.scripts = {}
3333
self.results = {}
3434

35+
@staticmethod
3536
def exec_script(
36-
self,
3737
path: str or Path,
3838
script_class: str = "Runner",
3939
function: str = "run",
4040
args: list or None = None,
4141
kwargs: dict or None = None,
42-
) -> ScriptResponse:
42+
) -> ScriptResponse or dict:
4343
"""
4444
Load and exec python script
4545
:param path: name of the script to load
@@ -55,13 +55,34 @@ def exec_script(
5555
kwargs = {}
5656
loader = SourceFileLoader(fullname=script_class, path=str(path))
5757
module = ModuleType(name=loader.name)
58-
loader.exec_module(module)
59-
class_instance = getattr(module, script_class)(logger=path.parent.stem)
58+
result = {"script": Path(path).parent.stem}
59+
60+
# Check if don't forget to install all the dependencies, and that module can
61+
# be successfully loaded.
62+
63+
# fmt: off
64+
try:
65+
loader.exec_module(module)
66+
except Exception as unexp_err:
67+
result.update(ScriptResponse.error(message=f"Unexpected module error: {str(unexp_err)}"))
68+
return result
69+
# fmt: on
70+
71+
# Module successfully loaded. We can set some module-scope variables that
72+
# we missed.
73+
module.__file__ = path
74+
75+
# Execute the runner and check if something goes wrong.
76+
77+
# fmt: off
6078
try:
61-
result = getattr(class_instance, function)(*args, **kwargs)
79+
class_instance = getattr(module, script_class)(logger=path.parent.stem)
80+
result.update(getattr(class_instance, function)(*args, **kwargs))
6281
except Exception as unexp_err:
63-
result = ScriptResponse.error(message=str(unexp_err))
64-
result.update({"script": Path(path).parent.stem})
82+
result.update(ScriptResponse.error(message=f"Unexpected execution error: {str(unexp_err)}"))
83+
# fmt: on
84+
85+
# In any possible case, return result from the module or ScriptResponse.error + script name
6586
return result
6687

6788
def get_scripts(self) -> dict:
@@ -76,7 +97,7 @@ def get_scripts(self) -> dict:
7697
ScriptRunnerPaths.CONVERT,
7798
]:
7899
self.scripts.update({directory.stem: list()})
79-
for file in directory.glob("*/__main__.py"):
100+
for file in directory.glob("*/module.py"):
80101
self.scripts[directory.stem].append(file)
81102
return self.scripts
82103

src/core/utils/__init__.py

Whitespace-only changes.

src/core/utils/module.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/usr/bin/env python3
2+
3+
4+
def run_module(
5+
runner: type, args: list, arg_name: str, arg_default: str or None = None
6+
) -> dict:
7+
"""
8+
Use module as a 'python3 -m ...' module
9+
:param runner: runner object
10+
:param args: list of args from CLI
11+
:param arg_name: name of arg to use
12+
:param arg_default: default arg value
13+
:return: results
14+
"""
15+
runner = runner()
16+
return runner.run(**{arg_name: args[1] if len(args) >= 2 else arg_default})

src/scripts/__init__.py

Whitespace-only changes.

src/scripts/convert/__init__.py

Whitespace-only changes.

src/scripts/osint/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import sys
2+
from pathlib import Path
3+
4+
__root_dir = Path(__file__).parents[4]
5+
sys.path.append(str(__root_dir))
+6-94
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,10 @@
11
#!/usr/bin/env python3
22

3-
import asyncio
4-
from pathlib import Path
3+
from pprint import pprint
4+
from sys import argv
55

6-
import aiohttp
7-
import requests
6+
from src.core.utils.module import run_module
7+
from .module import Runner
88

9-
from src.core.base.osint import OsintRunner
10-
from src.core.utils.response import ScriptResponse
11-
12-
13-
class Defaults:
14-
NETWORKS_LIST = "social_networks.txt"
15-
16-
17-
class Networks:
18-
def __init__(self):
19-
with open(Path("./src/scripts/osint/check_nickname/data/social_networks.txt")) as file:
20-
self.net = file.read().splitlines()
21-
22-
23-
def check_nickname_sync(nickname: str) -> list:
24-
"""
25-
checks nicknames from social networks(sync)
26-
:param nickname: just nickname :)
27-
:return: list with links to user from social
28-
networks which have this nickname
29-
"""
30-
ans = []
31-
social = Networks().net
32-
for site in social:
33-
try:
34-
url = "https://{site}{nickname}".format(site=site, nickname=nickname)
35-
response = requests.get(url)
36-
if response.status_code == 200:
37-
ans.append(url)
38-
except:
39-
pass
40-
return ans
41-
42-
43-
async def check_nickname_async(nickname: str, social) -> list:
44-
"""
45-
checks nicknames from social networks(async)
46-
:param nickname: just nickname :)
47-
:param social: social
48-
:return: list with links to user from social
49-
networks which have this nickname
50-
"""
51-
ans = []
52-
async with aiohttp.ClientSession() as session:
53-
while not social.empty():
54-
url = await social.get()
55-
try:
56-
async with session.get(url) as response:
57-
if response.status == 200:
58-
ans.append(url)
59-
except:
60-
pass
61-
return ans
62-
63-
64-
class Runner(OsintRunner):
65-
def __init__(self, logger: str = __name__):
66-
super().__init__(logger=logger)
67-
68-
@staticmethod
69-
async def __run(*args, **kwargs):
70-
try:
71-
username = kwargs.get("username")
72-
social = asyncio.Queue()
73-
for site in Networks().net:
74-
await social.put(
75-
"https://{site}{username}".format(site=site, username=username)
76-
)
77-
temp_result = await asyncio.gather(
78-
*[
79-
asyncio.create_task(check_nickname_async(username, social))
80-
for _ in range(10)
81-
]
82-
)
83-
result = {username: []}
84-
for sub_massive in temp_result:
85-
for site in sub_massive:
86-
result[username].append(site)
87-
return ScriptResponse.success(
88-
result=result,
89-
message="Found {count} user accounts".format(
90-
count=len(result[username])
91-
),
92-
)
93-
except Exception as err:
94-
return ScriptResponse.error(message=str(err))
95-
96-
def run(self, *args, **kwargs):
97-
username = kwargs.get("username")
98-
return asyncio.run(self.__run(username=username))
9+
result = run_module(Runner, args=argv, arg_name="username", arg_default="johndoe")
10+
pprint(result)

0 commit comments

Comments
 (0)