]>
Commit | Line | Data |
---|---|---|
1 | # Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved. | |
2 | # Licensed to PSF under a Contributor Agreement. | |
3 | ||
4 | """Safely evaluate Python string literals without using eval().""" | |
5 | ||
6 | import re | |
7 | from typing import Dict, Match | |
8 | ||
9 | simple_escapes: Dict[str, str] = { | |
10 | "a": "\a", | |
11 | "b": "\b", | |
12 | "f": "\f", | |
13 | "n": "\n", | |
14 | "r": "\r", | |
15 | "t": "\t", | |
16 | "v": "\v", | |
17 | "'": "'", | |
18 | '"': '"', | |
19 | "\\": "\\", | |
20 | } | |
21 | ||
22 | ||
23 | def escape(m: Match[str]) -> str: | |
24 | all, tail = m.group(0, 1) | |
25 | assert all.startswith("\\") | |
26 | esc = simple_escapes.get(tail) | |
27 | if esc is not None: | |
28 | return esc | |
29 | if tail.startswith("x"): | |
30 | hexes = tail[1:] | |
31 | if len(hexes) < 2: | |
32 | raise ValueError("invalid hex string escape ('\\%s')" % tail) | |
33 | try: | |
34 | i = int(hexes, 16) | |
35 | except ValueError: | |
36 | raise ValueError("invalid hex string escape ('\\%s')" % tail) from None | |
37 | else: | |
38 | try: | |
39 | i = int(tail, 8) | |
40 | except ValueError: | |
41 | raise ValueError("invalid octal string escape ('\\%s')" % tail) from None | |
42 | return chr(i) | |
43 | ||
44 | ||
45 | def evalString(s: str) -> str: | |
46 | assert s.startswith("'") or s.startswith('"'), repr(s[:1]) | |
47 | q = s[0] | |
48 | if s[:3] == q * 3: | |
49 | q = q * 3 | |
50 | assert s.endswith(q), repr(s[-len(q) :]) | |
51 | assert len(s) >= 2 * len(q) | |
52 | s = s[len(q) : -len(q)] | |
53 | return re.sub(r"\\(\'|\"|\\|[abfnrtv]|x.{0,2}|[0-7]{1,3})", escape, s) | |
54 | ||
55 | ||
56 | def test() -> None: | |
57 | for i in range(256): | |
58 | c = chr(i) | |
59 | s = repr(c) | |
60 | e = evalString(s) | |
61 | if e != c: | |
62 | print(i, c, s, e) | |
63 | ||
64 | ||
65 | if __name__ == "__main__": | |
66 | test() |