-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtext_obfuscator.py
More file actions
118 lines (104 loc) · 5.21 KB
/
text_obfuscator.py
File metadata and controls
118 lines (104 loc) · 5.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import random
from typing import List
import unicodedata
class TextObfuscator:
def __init__(self):
self.zero_width_space = ""
self.invisible_chars = ["", "", "", "", "", ""] # Various invisible unicode chars
# Extended character mappings
self.similar_chars = {
'.': ['.', '。', '。', '․', '⋅', '∙', '⸱', '⸳'],
'a': ['а', 'a', '𝚊', 'a', 'ᴀ', 'ⓐ', '𝒂', '𝓪'],
's': ['ѕ', 's', '𝚜', 'ѕ', 'ꜱ', 'ⓢ', '𝒔', '𝓼'],
'i': ['і', 'i', '𝚒', 'ı', 'ɪ', 'ⓘ', '𝒊', '𝓲'],
'z': ['z', '𝚣', 'ᴢ', 'ⓩ', '𝒛', '𝓏'],
'e': ['е', 'e', '𝚎', 'ᴇ', 'ⓔ', '𝒆', '𝓮'],
'm': ['m', '𝚖', 'ᴍ', 'ⓜ', '𝒎', '𝓶'],
't': ['t', '𝚝', 'ᴛ', 'ⓣ', '𝒕', '𝓽'],
'r': ['r', '𝚛', 'ʀ', 'ⓡ', '𝒓', '𝓻'],
'f': ['f', '𝚏', 'ꜰ', 'ⓕ', '𝒇', '𝓯'],
'u': ['u', '𝚞', 'ᴜ', 'ⓤ', '𝒖', '𝓾'],
'n': ['n', '𝚗', 'ɴ', 'ⓝ', '𝒏', '𝓷']
}
# Common words to replace
self.word_replacements = {
'size': ['ѕιzє', 'ꜱɪᴢᴇ', 'ˢⁱᶻᵉ', '𝓼𝓲𝔃𝓮', '𝕤𝕚𝕫𝕖'],
'matters': ['ᴍᴀᴛᴛᴇʀꜱ', 'мαттєяѕ', 'ᵐᵃᵗᵗᵉʳˢ', '𝓶𝓪𝓽𝓽𝓮𝓻𝓼', '𝕞𝕒𝕥𝕥𝕖𝕣𝕤'],
'fun': ['ғᴜɴ', 'ꜰᴜɴ', 'ᶠᵘⁿ', '𝓯𝓾𝓷', '𝕗𝕦𝕟'],
'token': ['ᴛᴏᴋᴇɴ', 'токєη', 'ᵗᵒᵏᵉⁿ', '𝓽𝓸𝓴𝓮𝓷', '𝕥𝕠𝕜𝕖𝕟'],
'utility': ['ᴜᴛɪʟɪᴛʏ', 'υтιℓιту', 'ᵘᵗⁱˡⁱᵗʸ', '𝓾𝓽𝓲𝓵𝓲𝓽𝔂', '𝕦𝕥𝕚𝕝𝕚𝕥𝕪']
}
def obfuscate_url(self, text: str) -> str:
techniques: List[callable] = [
self._stylize_text,
self._mix_invisible_chars,
self._full_width_text,
self._mathematical_bold,
self._bubble_text,
self._combine_techniques
]
return random.choice(techniques)(text)
def _stylize_text(self, text: str) -> str:
"""Replace words with stylized versions"""
words = text.lower().split()
result = []
for word in words:
if word in self.word_replacements:
result.append(random.choice(self.word_replacements[word]))
else:
result.append(word)
return " ".join(result)
def _mix_invisible_chars(self, text: str) -> str:
"""Mix invisible characters into text"""
result = ""
for char in text:
result += char
if random.random() < 0.3: # 30% chance to add invisible char
result += random.choice(self.invisible_chars)
return result
def _full_width_text(self, text: str) -> str:
"""Convert to full-width characters"""
# Mapping of normal to full-width characters
full_width_map = {
'a': 'a', 'b': 'b', 'c': 'c', 'd': 'd', 'e': 'e',
'f': 'f', 'g': 'g', 'h': 'h', 'i': 'i', 'j': 'j',
'k': 'k', 'l': 'l', 'm': 'm', 'n': 'n', 'o': 'o',
'p': 'p', 'q': 'q', 'r': 'r', 's': 's', 't': 't',
'u': 'u', 'v': 'v', 'w': 'w', 'x': 'x', 'y': 'y',
'z': 'z', '0': '0', '1': '1', '2': '2', '3': '3',
'4': '4', '5': '5', '6': '6', '7': '7', '8': '8',
'9': '9', ' ': ' ', '.': '.'
}
return ''.join(full_width_map.get(c.lower(), c) for c in text)
def _mathematical_bold(self, text: str) -> str:
"""Convert to mathematical bold characters"""
normal = 'abcdefghijklmnopqrstuvwxyz'
math_bold = '𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳'
trans = str.maketrans(normal, math_bold)
return text.translate(trans)
def _bubble_text(self, text: str) -> str:
"""Convert to bubble characters"""
normal = 'abcdefghijklmnopqrstuvwxyz'
bubble = 'ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ'
trans = str.maketrans(normal, bubble)
return text.translate(trans)
def _combine_techniques(self, text: str) -> str:
"""Combine multiple techniques"""
# Split into words
words = text.split()
result = []
for word in words:
if word.lower() in self.word_replacements:
# Use stylized version for known words
result.append(random.choice(self.word_replacements[word.lower()]))
else:
# Apply random technique for other words
technique = random.choice([
self._mathematical_bold,
self._bubble_text,
self._full_width_text
])
result.append(technique(word))
# Add some invisible characters
final_text = " ".join(result)
return self._mix_invisible_chars(final_text)