Skip to content

Commit 07079d4

Browse files
committed
feat: enhance token generation robustness and add input validation
1 parent 978e1bf commit 07079d4

2 files changed

Lines changed: 96 additions & 13 deletions

File tree

src/mistode/core.py

Lines changed: 88 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,36 +59,68 @@ def __init__(self, length: int = 16, style: str = "similar", seed=None):
5959
)
6060
self.random_letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
6161

62+
# Define homograph confusables to avoid
63+
self.homograph_confusables = {
64+
"a": ["а", "а"], # Latin 'a' vs Cyrillic 'а'
65+
"b": ["Ь", "ь"], # Latin 'b' vs Cyrillic 'ь'
66+
"c": ["с", "с"], # Latin 'c' vs Cyrillic 'с'
67+
"e": ["е", "е"], # Latin 'e' vs Cyrillic 'е'
68+
"h": ["н", "н"], # Latin 'h' vs Cyrillic 'н'
69+
"i": ["і", "і"], # Latin 'i' vs Ukrainian 'і'
70+
"k": ["к", "к"], # Latin 'k' vs Cyrillic 'к'
71+
"m": ["м", "м"], # Latin 'm' vs Cyrillic 'м'
72+
"o": ["о", "о"], # Latin 'o' vs Cyrillic 'о'
73+
"p": ["р", "р"], # Latin 'p' vs Cyrillic 'р'
74+
"s": ["ѕ", "ѕ"], # Latin 's' vs Cyrillic 'ѕ'
75+
"x": ["х", "х"], # Latin 'x' vs Cyrillic 'х'
76+
"y": ["у", "у"], # Latin 'y' vs Cyrillic 'у'
77+
"0": ["о", "о"], # Digit '0' vs Cyrillic 'о'
78+
"1": ["і", "і"], # Digit '1' vs Ukrainian 'і'
79+
}
80+
6281
def generate(self) -> str:
6382
"""
6483
Generate unique obfuscated identifier
6584
"""
66-
max_attempts = 100
85+
max_attempts = 1000 # Increased attempts to handle more complex cases
6786
for attempt in range(max_attempts):
6887
token = self._generate_single()
6988

70-
# Check for duplicates
71-
if token not in self.generated_tokens:
89+
# Check for duplicates and homograph issues
90+
if (
91+
token not in self.generated_tokens
92+
and self._validate_token_for_homographs(token)
93+
):
7294
self.generated_tokens.add(token)
7395
self.counter += 1
7496
return token
7597

7698
self.collision_count += 1
7799

100+
# If we still can't generate unique tokens, try increasing length
101+
if self.length < 32:
102+
old_length = self.length
103+
self.length = min(
104+
self.length + 4, 32
105+
) # Increase length to reduce collisions
106+
try:
107+
token = self._generate_single()
108+
if (
109+
token not in self.generated_tokens
110+
and self._validate_token_for_homographs(token)
111+
):
112+
self.generated_tokens.add(token)
113+
self.counter += 1
114+
return token
115+
finally:
116+
self.length = old_length # Restore original length after trying
117+
78118
raise RuntimeError(
79119
f"Unable to generate unique token, collision persists after "
80-
f"{max_attempts} attempts"
120+
f"{max_attempts} attempts with length {self.length}. "
121+
f"Consider using a different style or increasing token length."
81122
)
82123

83-
def _generate_single(self) -> str:
84-
"""
85-
Generate single token (no duplicate check)
86-
"""
87-
if self.style == "similar":
88-
return self._generate_similar()
89-
else:
90-
return self._generate_random()
91-
92124
def _generate_similar(self) -> str:
93125
"""
94126
Generate similar character style identifier with random first character
@@ -102,6 +134,21 @@ def _generate_similar(self) -> str:
102134

103135
return first_char + "".join(remaining_chars)
104136

137+
def _validate_token_for_homographs(self, token: str) -> bool:
138+
"""
139+
Check if token contains potentially confusing homograph characters
140+
"""
141+
# Convert to lowercase for comparison
142+
lower_token = token.lower()
143+
144+
for char in lower_token:
145+
if char in self.homograph_confusables:
146+
# Check if any confusable character exists in the token
147+
for confusable in self.homograph_confusables[char]:
148+
if confusable in token:
149+
return False
150+
return True
151+
105152
def _generate_random(self) -> str:
106153
"""
107154
Generate pure random style token
@@ -114,12 +161,35 @@ def _generate_random(self) -> str:
114161

115162
return first_char + "".join(remaining_chars)
116163

164+
def _generate_single(self) -> str:
165+
"""
166+
Generate single token (no duplicate check)
167+
"""
168+
if self.style == "similar":
169+
token = self._generate_similar()
170+
else:
171+
token = self._generate_random()
172+
173+
# Ensure the token doesn't contain homograph confusables
174+
while not self._validate_token_for_homographs(token):
175+
# If the token contains homograph confusables, regenerate
176+
if self.style == "similar":
177+
token = self._generate_similar()
178+
else:
179+
token = self._generate_random()
180+
181+
return token
182+
117183
def set_length(self, length: int):
118184
"""
119185
Set token length
120186
"""
187+
if not isinstance(length, int):
188+
raise TypeError("Length must be an integer")
121189
if length < 8 or length > 32:
122190
raise ValueError(f"Length must be between 8 and 32, current: {length}")
191+
if length < 1 or length > 255: # Additional check for extreme values
192+
raise ValueError(f"Length must be between 1 and 255, current: {length}")
123193
self.length = length
124194

125195
def set_style(self, style: str):
@@ -243,6 +313,11 @@ def __init__(self, key=None):
243313
# Generate random key between 1-255
244314
self.key = random.randint(1, 255)
245315
else:
316+
# Validate the key is an integer between 1 and 255
317+
if not isinstance(key, int):
318+
raise TypeError("Key must be an integer")
319+
if key < 1 or key > 255:
320+
raise ValueError("Key must be between 1 and 255")
246321
self.key = key
247322

248323
def encrypt(self, text):

src/mistode/encrypt.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ def __init__(self, key: str):
2121
"""
2222
if not key:
2323
raise ValueError("Key cannot be empty")
24+
if not isinstance(key, str):
25+
raise TypeError("Key must be a string")
26+
if (
27+
len(key) > 1024
28+
): # Prevent extremely long keys that could cause memory issues
29+
raise ValueError(
30+
"Key length exceeds maximum allowed length of 1024 characters"
31+
)
2432
# SHA-256 to get a fixed-length key from arbitrary string
2533
self.key_hash = hashlib.sha256(key.encode("utf-8")).digest()
2634

0 commit comments

Comments
 (0)