From ae1de755572f78fcb4e7e71836cda9876172261c Mon Sep 17 00:00:00 2001 From: securisec Date: Wed, 19 Jun 2024 00:06:22 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=97=93=20Jun=2019,=202024=2012:04:51?= =?UTF-8?q?=E2=80=AFAM?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🐙 get_by_index supports multiple indexes 🐙 get_by_key supports multiple keys 🤖 types added/updated 🐙 rot_13 updated to support cyberchef implementation --- chepy/__version__.py | 2 +- chepy/core.py | 26 ++++++++++++++++++-------- chepy/core.pyi | 6 +++--- chepy/modules/encryptionencoding.py | 26 ++++++++++++++++++++------ chepy/modules/encryptionencoding.pyi | 2 +- tests/test_core.py | 8 ++++++++ tests/test_encryptionencoding.py | 4 ++++ 7 files changed, 55 insertions(+), 19 deletions(-) diff --git a/chepy/__version__.py b/chepy/__version__.py index e57f66b..594b065 100644 --- a/chepy/__version__.py +++ b/chepy/__version__.py @@ -1,2 +1,2 @@ -__version__ = "7.1.0" # pragma: no cover +__version__ = "7.2.0" # pragma: no cover __author__ = "@securisec" # pragma: no cover diff --git a/chepy/core.py b/chepy/core.py index 9922df8..844ca0f 100644 --- a/chepy/core.py +++ b/chepy/core.py @@ -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 @@ -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 @@ -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: diff --git a/chepy/core.pyi b/chepy/core.pyi index 648e490..5c177d8 100644 --- a/chepy/core.pyi +++ b/chepy/core.pyi @@ -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: ... @@ -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: ... diff --git a/chepy/modules/encryptionencoding.py b/chepy/modules/encryptionencoding.py index c090bb4..b52bf46 100644 --- a/chepy/modules/encryptionencoding.py +++ b/chepy/modules/encryptionencoding.py @@ -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 diff --git a/chepy/modules/encryptionencoding.pyi b/chepy/modules/encryptionencoding.pyi index 367ccfe..3e08d3b 100644 --- a/chepy/modules/encryptionencoding.pyi +++ b/chepy/modules/encryptionencoding.pyi @@ -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: ... diff --git a/tests/test_core.py b/tests/test_core.py index 9125506..3b5b5b4 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -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" @@ -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") diff --git a/tests/test_encryptionencoding.py b/tests/test_encryptionencoding.py index b839eee..1c2d440 100644 --- a/tests/test_encryptionencoding.py +++ b/tests/test_encryptionencoding.py @@ -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"