Skip to content

Commit

Permalink
🗓 Jun 19, 2024 12:04:51 AM
Browse files Browse the repository at this point in the history
🐙 get_by_index supports multiple indexes
🐙 get_by_key supports multiple keys
🤖 types added/updated
🐙 rot_13 updated to support cyberchef implementation
  • Loading branch information
securisec committed Jun 19, 2024
1 parent 0f4a236 commit ae1de75
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 19 deletions.
2 changes: 1 addition & 1 deletion chepy/__version__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__version__ = "7.1.0" # pragma: no cover
__version__ = "7.2.0" # pragma: no cover
__author__ = "@securisec" # pragma: no cover
26 changes: 18 additions & 8 deletions chepy/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -751,24 +751,28 @@ def out(self) -> Any:
return self.state

@ChepyDecorators.call_stack
def get_by_index(self, index: int):
"""Get an item by specifying an index
def get_by_index(self, *indexes: int):
"""Get an item by specifying an index. If only one index is specified, the obj is return else a new list is returned
Args:
index (int): Index number to get
*indexes (int): Index numbers to get.
Returns:
Chepy: The Chepy object.
"""
self.state = self.state[index]
if len(indexes) == 1:
self.state = self.state[int(indexes[0])]
else:
self.state = [self.state[int(index)] for index in indexes]
return self

@ChepyDecorators.call_stack
def get_by_key(self, key: str, split_key: str = "."):
def get_by_key(self, *keys: str, split_key: str = "."):
"""Get value from a dict. Supports nested keys and arrays.
If only one key is specified, the obj is return else a new list is returned
Args:
key (Union[Hashable, None]): Keys to extract.
keys (Tuple[Union[Hashable, None]]): Keys to extract.
split_key (str, optional): Split nested keys. Defaults to "."
nested (bool, optional): If the specified keys are nested. Supports array indexing. Defaults to True
Expand All @@ -777,7 +781,13 @@ def get_by_key(self, key: str, split_key: str = "."):
"""
assert isinstance(self.state, dict), "State is not a dictionary"

self.state = self._get_nested_value(self.state, key, split_by=split_key)
if len(keys) == 1:
self.state = self._get_nested_value(self.state, keys[0], split_by=split_key)
else:
self.state = [
self._get_nested_value(self.state, key, split_by=split_key)
for key in keys
]
return self

@ChepyDecorators.call_stack
Expand Down Expand Up @@ -997,7 +1007,7 @@ def load_dir(self, pattern: str = "*"):
return self

@ChepyDecorators.call_stack
def load_file(self, binary_mode: bool = False, encoding: Union[str, None]=None):
def load_file(self, binary_mode: bool = False, encoding: Union[str, None] = None):
"""If a path is provided, load the file
Args:
Expand Down
6 changes: 3 additions & 3 deletions chepy/core.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ class ChepyCore:
def state(self): ...
@state.setter
def state(self: ChepyCoreT, val: Any) -> None: ...
def fork(self: ChepyCoreT, methods: List[Union[Tuple[Union[str, Callable], dict], Tuple[Union[str, Callable],]]]) -> ChepyCoreT: ...
def for_each(self: ChepyCoreT, methods: List[Union[Tuple[Union[str, Callable], dict], Tuple[Union[str, Callable],]]], merge: Union[str, bytes, None]=None) -> ChepyCoreT: ...
def fork(self: ChepyCoreT, methods: List[Union[Tuple[Union[str, Callable], dict], Tuple[Union[str, Callable[None, ChepyCoreT]],]]]) -> ChepyCoreT: ...
def for_each(self: ChepyCoreT, methods: List[Union[Tuple[Union[str, Callable], Dict[str, Any]], Tuple[Union[str, Callable[None, ChepyCoreT]],]]], merge: Union[str, bytes, None]=None) -> ChepyCoreT: ...
def set_state(self: ChepyCoreT, data: Any) -> ChepyCoreT: ...
def create_state(self: ChepyCoreT): ...
def copy_state(self: ChepyCoreT, index: int=...) -> ChepyCoreT: ...
Expand All @@ -53,7 +53,7 @@ class ChepyCore:
@property
def out(self: ChepyCoreT) -> ChepyCoreT: ...
def out_as_str(self: ChepyCoreT) -> str: ...
def get_by_index(self: ChepyCoreT, index: int) -> ChepyCoreT: ...
def get_by_index(self: ChepyCoreT, *indexes: int) -> ChepyCoreT: ...
def get_by_key(self: ChepyCoreT, key: str, split_key: Union[str, None] = '.') -> ChepyCoreT: ...
def copy_to_clipboard(self: ChepyCoreT) -> None: ...
def copy(self: ChepyCoreT) -> None: ...
Expand Down
26 changes: 20 additions & 6 deletions chepy/modules/encryptionencoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,16 +162,30 @@ def rotate_bruteforce(self) -> EncryptionEncodingT:
return self

@ChepyDecorators.call_stack
def rot_13(self) -> EncryptionEncodingT:
"""ROT-13 encoding
def rot_13(self, amount=13, rotate_lower=True, rotate_upper=True, rotate_numbers=False) -> EncryptionEncodingT:
"""Rot 13
A simple caesar substitution cipher which rotates alphabet
characters by the specified amount (default 13).
Args:
amount (int, optional): Rotate amount. Defaults to 13.
rotate_lower (bool, optional): Rotate lowercase. Defaults to True.
rotate_upper (bool, optional): Rotate uppercase. Defaults to True.
rotate_numbers (bool, optional): Rotate numbers. Defaults to False.
Returns:
Chepy: The Chepy object.
Chepy: The Chepy object.
"""
self.state = codecs.encode(self._convert_to_str(), "rot_13")
text = self._convert_to_str()
result = []
for char in text:
if rotate_lower and 'a' <= char <= 'z': # Lowercase letters
result.append(chr((ord(char) - ord('a') + amount) % 26 + ord('a')))
elif rotate_upper and 'A' <= char <= 'Z': # Uppercase letters
result.append(chr((ord(char) - ord('A') + amount) % 26 + ord('A')))
elif rotate_numbers and '0' <= char <= '9': # Numbers
result.append(chr((ord(char) - ord('0') + amount) % 10 + ord('0')))
else:
result.append(char) # Non-alphabetical characters remain unchanged
self.state = ''.join(result)
return self

@ChepyDecorators.call_stack
Expand Down
2 changes: 1 addition & 1 deletion chepy/modules/encryptionencoding.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class EncryptionEncoding(ChepyCore):
state: Any = ...
def rotate(self: EncryptionEncodingT, rotate_by: int) -> EncryptionEncodingT: ...
def rotate_bruteforce(self: EncryptionEncodingT) -> EncryptionEncodingT: ...
def rot_13(self: EncryptionEncodingT) -> EncryptionEncodingT: ...
def rot_13(self: EncryptionEncodingT, amount: int=13, rotate_lower: bool=True, rotate_upper: bool=True, rotate_numbers: bool=False) -> EncryptionEncodingT: ...
def rot_47(self: EncryptionEncodingT, amount: int=...) -> EncryptionEncodingT: ...
def rot_47_bruteforce(self: EncryptionEncodingT) -> EncryptionEncodingT: ...
def rot_8000(self: EncryptionEncodingT) -> EncryptionEncodingT: ...
Expand Down
8 changes: 8 additions & 0 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ def test_get_by_key():
}
}
assert Chepy(data2).get_by_key("menu.popup.menuitem[1].value").o == b"Open"
assert Chepy(data2).get_by_key(
"menu.popup.menuitem[1].value", "menu.popup.menuitem[2].value"
).o == ["Open", "Close"]
assert (
Chepy(data2).get_by_key("menu..popup..menuitem[0]..value", split_key="..").o
== b"New"
Expand Down Expand Up @@ -273,6 +276,11 @@ def cb(data):
assert Chepy("abc").callback(cb).o == b"abcabc"


def test_get_by_index():
assert Chepy("abc").get_by_index(0).o == b"a"
assert Chepy("abc").get_by_index(0, 2).o == ["a", "c"]


def test_register():
# test set register
c1 = Chepy("hello")
Expand Down
4 changes: 4 additions & 0 deletions tests/test_encryptionencoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -906,3 +906,7 @@ def test_railfence():
assert Chepy("hello world").railfence_encode(key=4, offset=4).o == b"lrelolhowd"
assert Chepy("hloel").railfence_decode().o == b"hello"
assert Chepy(b"lelho").railfence_decode(key=4, offset="4").o == b"hello"


def test_rot13():
assert Chepy("Abc1").rot_13(rotate_numbers=True).o == b"Nop4"

0 comments on commit ae1de75

Please sign in to comment.