Skip to content

Commit

Permalink
e
Browse files Browse the repository at this point in the history
  • Loading branch information
exersalza committed Dec 13, 2023
1 parent 72621fa commit b48e71d
Show file tree
Hide file tree
Showing 11 changed files with 104 additions and 59 deletions.
4 changes: 2 additions & 2 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
[FORMAT]

# Maximum number of characters on a single line.
max-line-length=79
max-line-length=85

# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
Expand Down Expand Up @@ -507,4 +507,4 @@ check-str-concat-over-line-jumps=no


[CODE_STYLE]
max-line-length-suggestions=79
max-line-length-suggestions=85
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- [Installation](#installation)
- [Usage](#Usage)
- [Troubleshooting](#Troubleshooting)
- [Plugins](#Plugins)
- [Contributing](#Contributing)
- [Todo](#todo)
- [Disclaimer](#Disclaimer)
Expand Down Expand Up @@ -98,6 +99,11 @@ If you encounter any issues with FivemCipherFinder, here are some troubleshootin
- `EasyAdmin`
- Encrypted/obfuscated scripts

## Plugins

If you would like to have a Plugin that fetches data while the cipherfinder is running you can read into it further under [Plugins](plugins/README.md).
If you want a Pre-Written plugin that sends a message onto an Webhook

## Contributing

If you would like to contribute to FivemCipherFinder, you can open a pull request with your changes. The project has checks in place to ensure that the pull request passes without any issues. You can use the manual installation guide provided in the [Installation](#Installation) section to set up the project locally.
Expand Down
1 change: 0 additions & 1 deletion big.model

This file was deleted.

8 changes: 5 additions & 3 deletions cipherFinder/deleter.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,11 @@ def deleter_main(del_lines: list) -> int:
if bool(os.getenv("DEBUG")):
print(e)

print(f"ERROR: Can't delete cipher from {path!r} due to illegal "
f"characters, please delete it yourself.\nYou'll find it "
f"on line: {ln}")
print(
f"ERROR: Can't delete cipher from {path!r} due to illegal "
f"characters, please delete it yourself.\nYou'll find it "
f"on line: {ln}"
)
return 1

del lines[ln - 1]
Expand Down
49 changes: 27 additions & 22 deletions cipherFinder/finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def get_big_model_file() -> int:

with open("big.model", "wb") as _file:
for chunk in requests.get(
_RAW_BIG_MODEL, stream=True, timeout=5
_RAW_BIG_MODEL, stream=True, timeout=5
).iter_content(chunk_size=8192):
if not chunk:
continue
Expand Down Expand Up @@ -247,7 +247,7 @@ def prepare_log_line(**kw) -> int:


def check_file(
d: str, file: str, count: int, args: argparse.Namespace
d: str, file: str, count: int, args: argparse.Namespace
) -> tuple[int, int]:
"""Iterate over a file and check the lines
Expand Down Expand Up @@ -279,12 +279,17 @@ def check_file(
print(e)

_counter["failed"] += 1
print(f"Can't decode `{d}/{file}`. File has an unknown encoding"
f"or it can't be determined."
f"Consider looking into it by yourself.",
(f" -> Encoding: {enc!r} "
f"Confidence: {confidence * 100:.0f}%"
if args.verbose else ""))
print(
f"Can't decode `{d}/{file}`. File has an unknown encoding"
f"or it can't be determined."
f"Consider looking into it by yourself.",
(
f" -> Encoding: {enc!r} "
f"Confidence: {confidence * 100:.0f}%"
if args.verbose
else ""
),
)
return 1, count

match = validate_lines(lines)
Expand Down Expand Up @@ -424,7 +429,7 @@ def main(arg_list: list) -> int:
nargs="?",
default=".",
help="give the path to search, when no path is given"
', the current working directory will be used "."',
', the current working directory will be used "."',
)

parser.add_argument(
Expand Down Expand Up @@ -453,27 +458,27 @@ def main(arg_list: list) -> int:
"--v2",
action="store_true",
help="uses an extra algorithm to find gibberish "
"or randomly generated variable/function/table "
"names. it can introduce more false-positives "
"because of obfuscated scripts but "
"can help find ciphers.",
"or randomly generated variable/function/table "
"names. it can introduce more false-positives "
"because of obfuscated scripts but "
"can help find ciphers.",
)

parser.add_argument(
"-o",
"--output",
nargs=1,
help="define the output path/filename of the logfile. "
"syntax: path/[filename]. please note to add an / "
"to the end of the path when you don't want to use"
" a custom filename.",
"syntax: path/[filename]. please note to add an / "
"to the end of the path when you don't want to use"
" a custom filename.",
)

parser.add_argument(
"--no-del",
action="store_true",
help="debug command to not delete the big.model "
"file after the script finishes.",
"file after the script finishes.",
)

parser.add_argument(
Expand All @@ -486,8 +491,8 @@ def main(arg_list: list) -> int:
"--plug-dir",
nargs=1,
help="give a directory that stores plugins for the cipherfinder."
"read the documentation or inside the cipherfinder/plugins.py"
" on how to write custom plugins.",
"read the documentation or inside the cipherfinder/plugins.py"
" on how to write custom plugins.",
)

parser.add_argument(
Expand Down Expand Up @@ -566,9 +571,9 @@ def main(arg_list: list) -> int:
return 0

if y_n_validator(
input( # pylint: disable=bad-builtin
"Do you want to start the eraser wizard? [y/N] "
)
input( # pylint: disable=bad-builtin
"Do you want to start the eraser wizard? [y/N] "
)
):
deleter_main(_del_lines)

Expand Down
42 changes: 34 additions & 8 deletions cipherFinder/plugins.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os
import sys
import importlib
import inspect
import requests


Expand All @@ -9,6 +10,21 @@
"FivemCipherFinder/main/plugins/sendWebhook.py",
]

VALID_HOOKS = [
"Init",
"GetValidatedLines",
"GetGibberishCheckMatches",
"GetLoggingValues",
"GetFileContents",
"GetRawFileContents",
"GetLogFilename",
]


def is_valid_hook(item_name: str) -> bool:
"""Returns if item_name is in VALID_HOOKS"""
return item_name in VALID_HOOKS


class PluginInterface:
"""Use this class to create plugins
Expand Down Expand Up @@ -37,6 +53,12 @@ class PluginInterface:
"""

hook_name = ""

def __init__(self) -> None:
if not self.hook_name:
self.hook_name = self.__class__.__name__

def execute(self, *args, **kw):
raise NotImplementedError("Plugins must implement an 'execute' method")

Expand All @@ -49,7 +71,8 @@ class _PluginDummy(PluginInterface):
"""

def execute(self, *args, **kw):
# Enter code here, I mean not here here, but should you copy it
# Enter code here, I mean not here here, but should you copy
# it and add it to your own plugin :D
...


Expand Down Expand Up @@ -84,14 +107,17 @@ def load_plugs(plug_dir: str = ".") -> dict:
for item_name in dir(module):
item = getattr(module, item_name)

# WHY TF DO I HAVE TO DO IT TWICE LINTER
if getattr(module, item_name) == PluginInterface:
if item_name == PluginInterface.__name__ or not inspect.isclass(
item
):
continue

# Check if the Hook is inhereting the PluginInterface class.
# looks weird. THATS THE REASON I DONT LIKE OOP
if isinstance(item, type) and issubclass(item, PluginInterface):
_hooks[item.__name__] = item()
hook_name = (
item.hook_name if item.hook_name else item.__name__
)

if isinstance(item, type) and is_valid_hook(hook_name):
_hooks[hook_name] = item()

return _hooks

Expand Down Expand Up @@ -132,4 +158,4 @@ def get_remote_plugins() -> int:


if __name__ == "__main__":
get_remote_plugins()
print(load_plugs("../plugins"))
6 changes: 2 additions & 4 deletions cipherFinder/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@

import chardet

ENCODING_TRANS = {
"Windows-1254": "cp"
}
ENCODING_TRANS = {"Windows-1254": "cp"}


def find_encoding(name: str) -> codecs.CodecInfo | int:
Expand All @@ -22,7 +20,7 @@ def detect_encoding(file_path) -> (str, float):
with open(file_path, "rb") as f:
result = chardet.detect(f.read())

if enc := result['encoding']:
if enc := result["encoding"]:
if x := find_encoding(enc):
enc = x.name

Expand Down
5 changes: 5 additions & 0 deletions plugins/dummy_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from cipherFinder.plugins import PluginInterface

class Init(PluginInterface):
def execute(self, *args, **kw):
return "dummy result"
13 changes: 0 additions & 13 deletions plugins/init.py
Original file line number Diff line number Diff line change
@@ -1,13 +0,0 @@
from cipherFinder.plugins import PluginInterface


class Init(PluginInterface):
"""This hook gets called when the program starts"""

def __init__(self):
pass

def execute(self, *args, **kw):
# This is a test hook
# you can put in here theoretically everything you want.
pass
22 changes: 19 additions & 3 deletions plugins/sendWebhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
def prepare_webhook_content(count: int, failed: int) -> dict:
content = None

if role_id := ALERT_ROLE_ID:
content = f"<@&{role_id}>"
if ALERT_ROLE_ID:
content = f"<@&{ALERT_ROLE_ID}>"

webhook_content = {
"content": content,
Expand Down Expand Up @@ -45,16 +45,32 @@ def prepare_webhook_content(count: int, failed: int) -> dict:
return webhook_content


class Init(PluginInterface):
def execute(self, *args, **kw):
pass


# You might ask yourself now, where tf does this name come from?
# good question, the name of classes are used to create the hooks
# that means, when you name your class Init, it will be called
# when the programs inits itself, or when you start it.
# Also when you're creating your own plugins, keep in mind
# to inherit the 'PluginInterface' class, otherwise it wont start.
class GetRawFileContents(PluginInterface):
class FetchMeTheirSouls(PluginInterface):
# Create an attribute like this, to change the hook name
# it will always default to the classes name, so you don't have
# to define this.
hook_name = "GetRawFileContents"

# The following function is REQUIRED, it will be run by the
# hook trigger
def execute(self, *args, **kw):
if not WEBHOOL_URL:
print(
f"PLEASE ENTER A WEBHOOK URL INTO YOUR PLUGIN "
f"{self.__class__.__name__:!r}"
)

values = args[0] # just isolate the values from the args
failed = kw.pop("failed") # failed files to open
webhook_content = prepare_webhook_content(values[0]["count"], failed)
Expand Down
7 changes: 4 additions & 3 deletions tests/test_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def test_load_plugs():
"""
from cipherFinder.plugins import PluginInterface
class DummyPlugin(PluginInterface):
class Init(PluginInterface):
def execute(self, *args, **kw):
return "dummy result"
"""
Expand All @@ -21,6 +21,7 @@ def execute(self, *args, **kw):
# Load the plugins from the temporary directory
plugins = load_plugs(tmpdir)


# Check that the DummyPlugin was loaded and returns the expected result
assert "DummyPlugin" in plugins
assert plugins["DummyPlugin"].execute() == "dummy result"
assert "Init" in plugins
assert plugins["Init"].execute() == "dummy result"

0 comments on commit b48e71d

Please sign in to comment.