Skip to content

Commit

Permalink
✅ ℹ️
Browse files Browse the repository at this point in the history
🔅 ability to loop over a list or a dict and apply chepy method
ℹ️ minor changes in readme and main
ℹ️ minor arguments renaming
✅ loop_list method added
✅ loop_dict method added
  • Loading branch information
securisecctf committed Nov 29, 2019
1 parent a408b07 commit afb43fb
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 18 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<img src="https://raw.githubusercontent.com/securisec/chepy/master/logo.png" width="300px">

![](https://github.com/securisec/chepy/workflows/tests/badge.svg)
[![](https://img.shields.io/travis/securisec/chepy.svg?label=Travis&logo=travis&branch=master)](https://travis-ci.com/securisec/chepy)
[![](https://codecov.io/gh/securisec/chepy/branch/master/graph/badge.svg?token=q3pRktSVBu)](https://codecov.io/gh/securisec/chepy)
![Travis (.com)](https://img.shields.io/travis/com/securisec/chepy?logo=travis)
[![](https://img.shields.io/docker/cloud/build/securisec/chepy?logo=docker)](https://hub.docker.com/r/securisec/chepy)
[![](https://codecov.io/gh/securisec/chepy/branch/master/graph/badge.svg?token=q3pRktSVBu)](https://codecov.io/gh/securisec/chepy)

[![](https://img.shields.io/readthedocs/chepy.svg?logo=read-the-docs&label=Docs)](http://chepy.readthedocs.io/en/latest/)
[![](https://img.shields.io/pypi/v/chepy.svg?logo=pypi&label=pypi)](https://pypi.python.org/pypi/chepy)
Expand Down
8 changes: 4 additions & 4 deletions chepy/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ def get_style():
"completion-menu.completion.current": "bg:#00aaaa #000000",
# "completion-menu.completion": "bg:#008888 #ffffff",
"completion-menu.completion.fuzzymatch.outside": "fg:#00aaaa",
"name1": "#00ffff bold",
"name2": "#ff0000 bold",
"name3": "#ffd700 bold",
"prompt1": "#00ffff bold",
"prompt2": "#ff0000 bold",
"prompt3": "#ffd700 bold",
"state_index": "#ffd700",
"file": "#00ff48",
"rprompt": "fg:#00ff48",
Expand Down Expand Up @@ -92,7 +92,7 @@ def get_options():


def prompt_message(fire_obj):
elements = [("class:name1", ">"), ("class:name2", ">"), ("class:name3", ">")]
elements = [("class:prompt1", ">"), ("class:prompt2", ">"), ("class:prompt3", ">")]
try:
elements.append(
(
Expand Down
123 changes: 115 additions & 8 deletions chepy/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import jsonpickle
import ujson
import io
import itertools
import regex as re
from decorator import decorator
from typing import Any, Tuple, List, Union
Expand All @@ -19,6 +20,9 @@


class ChepyDecorators(object):
"""A class to house all the decorators for Chepy
"""

@staticmethod
@decorator
def call_stack(func, *args, **kwargs):
Expand Down Expand Up @@ -711,11 +715,11 @@ def read_file(self):
self.load_file()
return self

def write_to_file(self, file_path: str, as_binary: bool = False) -> None:
def write_to_file(self, path: str, as_binary: bool = False) -> None:
"""Save the state to disk. Return None.
Args:
file_path (str): The file path to save in.
path (str): The file path to save in.
as_binary (bool, optional): If file should be saved as a binary file. Defaults to False.
Returns:
Expand All @@ -730,26 +734,26 @@ def write_to_file(self, file_path: str, as_binary: bool = False) -> None:
mode = "wb+"
else:
mode = "w+"
with open(str(self._abs_path(file_path)), mode) as f:
with open(str(self._abs_path(path)), mode) as f:
f.write(self.state)
self._info_logger("File written to {}".format(self._abs_path(file_path)))
self._info_logger("File written to {}".format(self._abs_path(path)))
return None

def write_binary(self, file_path: str) -> None: # pragma: no cover
def write_binary(self, path: str) -> None: # pragma: no cover
"""Save the state to disk. Return None.
Args:
file_path (str): The file path to save in.
path (str): The file path to save in.
Returns:
None: Returns None
Examples:
>>> c = Chepy("some data").write_binary('/some/path/file')
"""
with open(str(self._abs_path(file_path)), "wb+") as f:
with open(str(self._abs_path(path)), "wb+") as f:
f.write(self.state)
self._info_logger("File written to {}".format(self._abs_path(file_path)))
self._info_logger("File written to {}".format(self._abs_path(path)))
return None

def save_recipe(self, path: str):
Expand Down Expand Up @@ -798,3 +802,106 @@ def load_recipe(self, path: str):
else:
getattr(self, function)()
return self

@ChepyDecorators.call_stack
def loop_list(self, callback: str, args: dict = {}):
"""Loop over an array and run a Chepy method on it
Args:
callback (str): Chepy method as string
args (dict, optional): Dictionary of args. Defaults to {}.
Returns:
Chepy: The Chepy object
Examples:
This method is capable of running a callable from either
a string, or a chepy method.
>>> c = Chepy(["an", "array"])
>>> c.loop_list('to_hex').loop_list('hmac_hash', {'key': 'secret'})
['5cbe6ca2a66b380aec1449d4ebb0d40ac5e1b92e', '30d75bf34740e8781cd4ec7b122e3efd8448e270']
"""
assert isinstance(self.state, list), "State is not a list"
assert isinstance(callback, str), 'Callback must be a'
hold = []
current_state = self.state
# find the last index that this method was run
stack_loop_index = next(
itertools.dropwhile(
lambda x: self._stack[x]["function"] != "loop_list",
reversed(range(len(self._stack))),
)
)
try:
for index, data in enumerate(current_state):
self.state = current_state[index]
if args:
hold.append(getattr(self, callback)(**args).o)
else:
hold.append(getattr(self, callback)().o)
self._stack = self._stack[: stack_loop_index + 1]
self.state = hold
return self
except: # pragma: no cover
self.state = current_state
raise

@ChepyDecorators.call_stack
def loop_dict(self, keys: list, callback: str, args: dict = {}):
"""Loop over a dictionary and apply the callback to the value
Args:
keys (list): List of keys to match
callback (str): Chepy method as string
args (dict, optional): Dictionary of args. Defaults to {}.
Returns:
Chepy: The Chepy object.
Examples:
>>> c = Chepy({'some': 'hahahaha', 'lol': 'aahahah'})
>>> c.loop_dict(['some'], 'hmac_hash', {'key': 'secret'}).o
{'some': '99f77ec06a3c69a4a95371a7888245ba57f47f55', 'lol': 'aahahah'}
We can combine `loop_list` and `loop_dict` to loop over a list of dictionaries.
>>> data = [{"some": "val"}, {"some": "another"}, {"lol": "lol"}, {"another": "aaaa"}]
>>> c = Chepy(data)
>>> c.loop_list("loop_dict", {"keys": ["some", "lol"], "callback": "to_upper_case"})
[
{"some": "VAL"},
{"some": "ANOTHER"},
{"lol": "LOL"},
{"another": "aaaa"},
]
"""
assert isinstance(keys, list), "Keys needs to be a list of keys"
assert isinstance(args, dict), "Args needs to be a dict"
assert isinstance(callback, str), 'Callback must be a string'
hold = {}
current_state = self.state
# find the last index that this method was run
stack_loop_index = next(
itertools.dropwhile(
lambda x: self._stack[x]["function"] != "loop_dict",
reversed(range(len(self._stack))),
)
)
try:
dict_keys = current_state.keys()
for key in keys:
if current_state.get(key) is not None:
self.state = current_state.get(key)
if args:
hold[key] = getattr(self, callback)(**args).o
else:
hold[key] = getattr(self, callback)().o
for unmatched_key in list(set(dict_keys) - set(keys)):
hold[unmatched_key] = current_state[unmatched_key]
self._stack = self._stack[: stack_loop_index + 1]
self.state = hold
return self
except: # pragma: no cover
self.state = current_state
raise
4 changes: 2 additions & 2 deletions chepy/extras/bruteforce.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ def _expand_path(path):
return str(Path(path).expanduser().absolute())


def zip_password_bruteforce(zip_path: str, wordlist: str) -> str:
z = zipfile.ZipFile(_expand_path(zip_path))
def zip_password_bruteforce(path: str, wordlist: str) -> str:
z = zipfile.ZipFile(_expand_path(path))
with open(_expand_path(wordlist)) as f:
for password in f:
password = password.strip().encode()
Expand Down
3 changes: 2 additions & 1 deletion chepy/extras/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ def shannon_entropy(data: Any, unit="shannon") -> float:
"""Calculate the Shannon entropy of any data type. Units
could be `natural`, `shannon` or `hartley`
- 0 represents no randomness (i.e. all the bytes in the data have the same value) whereas 8, the maximum, represents a completely random string.
- 0 represents no randomness (i.e. all the bytes in the data have the same value)
whereas 8, the maximum, represents a completely random string.
- Standard English text usually falls somewhere between 3.5 and 5.
- Properly encrypted or compressed data of a reasonable length should have an entropy of over 7.5.
Expand Down
7 changes: 6 additions & 1 deletion docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,12 @@ Now the state contains `41` while the buffer still contains `A`
### Recipes
Chepy has the concept of recipes which means it can load an run chepy methods from a JSON file. This makes sharing Chepy recipes very easy and prevents code reuse.

Some caveats to recipes are that they do not include some of the methods from `ChepyCore`. For instance, a recipe cannot write to file, or load or read recipes. Recipes cannot do `fork` operations also. Chepy plugins does support recipes also.
Some caveats to recipes are that they do not include some of the methods from `ChepyCore`. For instance, a recipe cannot write to file, or load or read recipes. Chepy plugins does support recipes also.

Recipes do not support the following operations:
- `fork`

If these operations are present on the instance, the recipe will not work.

There are two main methods that handle recipes and they are both part of the Core.

Expand Down
35 changes: 35 additions & 0 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,38 @@ def test_recipe():
== "StormCTF{Spot3:DcEC6181F48e3B9D3dF77Dd827BF34e0}"
)
Path(temp).unlink()


def test_loop_list():
c = Chepy(["an", "array"])
c.loop_list("to_hex").loop_list("hmac_hash", {"key": "secret"})
assert c.o == [
"5cbe6ca2a66b380aec1449d4ebb0d40ac5e1b92e",
"30d75bf34740e8781cd4ec7b122e3efd8448e270",
]
c1 = Chepy(["an", "array"])
c1.loop_list("to_hex").loop_list("hmac_hash", {"key": "secret"})
assert c1.o == [
"5cbe6ca2a66b380aec1449d4ebb0d40ac5e1b92e",
"30d75bf34740e8781cd4ec7b122e3efd8448e270",
]


def test_loop_dict():
data = [{"some": "val"}, {"some": "another"}, {"lol": "lol"}, {"another": "aaaa"}]
c = Chepy(data)
c.loop_list("loop_dict", {"keys": ["some", "lol"], "callback": "to_upper_case"})
assert c.o == [
{"some": "VAL"},
{"some": "ANOTHER"},
{"lol": "LOL"},
{"another": "aaaa"},
]

d = Chepy({"some": "hahahaha", "lol": "aahahah"})
d.loop_dict(["some"], "to_upper_case")
assert d.o == {"some": "HAHAHAHA", "lol": "aahahah"}

e = Chepy({"some": "hahahaha", "lol": "aahahah"})
e.loop_dict(["some"], "hmac_hash", {"key": "secret"})
assert e.o == {"some": "99f77ec06a3c69a4a95371a7888245ba57f47f55", "lol": "aahahah"}

0 comments on commit afb43fb

Please sign in to comment.