| 144 | return source |
| 145 | |
| 146 | def _unwrap_eval_exec(self, source: str) -> str: |
| 147 | # Pattern 1: eval/exec("literal string") |
| 148 | pattern = re.compile( |
| 149 | r'(?:eval|exec)\s*\(\s*' |
| 150 | r'(?:' |
| 151 | r' (b?"""[\s\S]*?""")' |
| 152 | r"| (b?'''[\s\S]*?''')" |
| 153 | r'| (b?"(?:[^"\\]|\\.)*")' |
| 154 | r"| (b?'(?:[^'\\]|\\.)*')" |
| 155 | r')\s*\)', |
| 156 | re.VERBOSE, |
| 157 | ) |
| 158 | def replace_match(m): |
| 159 | raw = next(g for g in m.groups() if g is not None) |
| 160 | try: |
| 161 | val = ast.literal_eval(raw) |
| 162 | if isinstance(val, (str, bytes)): |
| 163 | return val if isinstance(val, str) else val.decode('utf-8', errors='replace') |
| 164 | except Exception: |
| 165 | pass |
| 166 | return m.group(0) |
| 167 | source = pattern.sub(replace_match, source) |
| 168 | |
| 169 | # Pattern 2: eval(compile("literal", ...)) or exec(compile("literal", ...)) |
| 170 | pattern2 = re.compile( |
| 171 | r'(?:eval|exec)\s*\(\s*compile\s*\(\s*' |
| 172 | r'("(?:[^"\\]|\\.)*"|\'(?:[^\'\\]|\\.)*\')' |
| 173 | r'\s*,\s*[^)]+\)\s*\)' |
| 174 | ) |
| 175 | def replace_compile(m): |
| 176 | try: |
| 177 | val = ast.literal_eval(m.group(1)) |
| 178 | if isinstance(val, str): |
| 179 | return val |
| 180 | except Exception: |
| 181 | pass |
| 182 | return m.group(0) |
| 183 | source = pattern2.sub(replace_compile, source) |
| 184 | return source |
| 185 | |
| 186 | def _decode_base64_calls(self, source: str) -> str: |
| 187 | pattern = re.compile( |