Skip to content

Commit 2222138

Browse files
authored
Merge pull request #22747 from github/tausbn/python315-model-new-binascii-codecs
Python: Model new `binascii` codecs
2 parents 27a8a7e + 3272fd5 commit 2222138

3 files changed

Lines changed: 129 additions & 0 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Added modeling for the Base32, Base85, and Ascii85 encoding and decoding functions introduced in Python 3.15's `binascii` module, including taint flow through custom alphabets.

‎python/ql/lib/semmle/python/frameworks/Stdlib.qll‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1760,6 +1760,59 @@ module StdlibPrivate {
17601760
}
17611761
}
17621762

1763+
// ---------------------------------------------------------------------------
1764+
// binascii
1765+
// ---------------------------------------------------------------------------
1766+
/** A call to a supported encoding function in the `binascii` module. */
1767+
private class BinasciiEncodeCall extends Encoding::Range, DataFlow::CallCfgNode {
1768+
string codec;
1769+
1770+
BinasciiEncodeCall() {
1771+
codec in ["base32", "base85", "ascii85"] and
1772+
this = API::moduleImport("binascii").getMember("b2a_" + codec).getACall()
1773+
}
1774+
1775+
override DataFlow::Node getAnInput() {
1776+
result = this.getArg(0)
1777+
or
1778+
codec in ["base32", "base85"] and
1779+
result = this.getArgByName("alphabet")
1780+
}
1781+
1782+
override DataFlow::Node getOutput() { result = this }
1783+
1784+
override string getFormat() {
1785+
result in ["Base32", "Base85", "Ascii85"] and
1786+
result.toLowerCase() = codec
1787+
}
1788+
}
1789+
1790+
/** A call to a supported decoding function in the `binascii` module. */
1791+
private class BinasciiDecodeCall extends Decoding::Range, DataFlow::CallCfgNode {
1792+
string codec;
1793+
1794+
BinasciiDecodeCall() {
1795+
codec in ["base32", "base85", "ascii85"] and
1796+
this = API::moduleImport("binascii").getMember("a2b_" + codec).getACall()
1797+
}
1798+
1799+
override predicate mayExecuteInput() { none() }
1800+
1801+
override DataFlow::Node getAnInput() {
1802+
result = this.getArg(0)
1803+
or
1804+
codec in ["base32", "base85"] and
1805+
result = this.getArgByName("alphabet")
1806+
}
1807+
1808+
override DataFlow::Node getOutput() { result = this }
1809+
1810+
override string getFormat() {
1811+
result in ["Base32", "Base85", "Ascii85"] and
1812+
result.toLowerCase() = codec
1813+
}
1814+
}
1815+
17631816
// ---------------------------------------------------------------------------
17641817
// json
17651818
// ---------------------------------------------------------------------------
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import binascii
2+
from binascii import b2a_base32 as encode32, a2b_base85 as decode85
3+
4+
data = TAINTED_BYTES
5+
6+
encoded32 = binascii.b2a_base32(data, padded=False) # $ encodeInput=data encodeOutput=binascii.b2a_base32(..) encodeFormat=Base32
7+
decoded32 = binascii.a2b_base32(encoded32, padded=False, canonical=True) # $ decodeInput=encoded32 decodeOutput=binascii.a2b_base32(..) decodeFormat=Base32
8+
9+
encoded85 = binascii.b2a_base85(data, pad=True) # $ encodeInput=data encodeOutput=binascii.b2a_base85(..) encodeFormat=Base85
10+
decoded85 = binascii.a2b_base85(encoded85, canonical=True) # $ decodeInput=encoded85 decodeOutput=binascii.a2b_base85(..) decodeFormat=Base85
11+
12+
encoded_ascii85 = binascii.b2a_ascii85(data, foldspaces=True, adobe=True) # $ encodeInput=data encodeOutput=binascii.b2a_ascii85(..) encodeFormat=Ascii85
13+
decoded_ascii85 = binascii.a2b_ascii85(encoded_ascii85, foldspaces=True, adobe=True) # $ decodeInput=encoded_ascii85 decodeOutput=binascii.a2b_ascii85(..) decodeFormat=Ascii85
14+
15+
ensure_tainted(
16+
encoded32, # $ tainted
17+
decoded32, # $ tainted
18+
encoded85, # $ tainted
19+
decoded85, # $ tainted
20+
encoded_ascii85, # $ tainted
21+
decoded_ascii85, # $ tainted
22+
)
23+
24+
aliased_encoded = encode32(data) # $ encodeInput=data encodeOutput=encode32(..) encodeFormat=Base32
25+
aliased_decoded = decode85(encoded85) # $ decodeInput=encoded85 decodeOutput=decode85(..) decodeFormat=Base85
26+
ensure_tainted(
27+
aliased_encoded, # $ tainted
28+
aliased_decoded, # $ tainted
29+
)
30+
31+
32+
def custom_alphabets():
33+
alphabet32 = binascii.BASE32HEX_ALPHABET
34+
alphabet85 = binascii.Z85_ALPHABET
35+
taint(alphabet32, alphabet85)
36+
37+
clean = b"abcd"
38+
clean32 = b"00000000"
39+
clean85 = b"00000"
40+
encoded32 = binascii.b2a_base32(clean, alphabet=alphabet32) # $ encodeInput=clean encodeInput=alphabet32 encodeOutput=binascii.b2a_base32(..) encodeFormat=Base32
41+
decoded32 = binascii.a2b_base32(clean32, alphabet=alphabet32) # $ decodeInput=clean32 decodeInput=alphabet32 decodeOutput=binascii.a2b_base32(..) decodeFormat=Base32
42+
encoded85 = binascii.b2a_base85(clean, alphabet=alphabet85) # $ encodeInput=clean encodeInput=alphabet85 encodeOutput=binascii.b2a_base85(..) encodeFormat=Base85
43+
decoded85 = binascii.a2b_base85(clean85, alphabet=alphabet85) # $ decodeInput=clean85 decodeInput=alphabet85 decodeOutput=binascii.a2b_base85(..) decodeFormat=Base85
44+
ensure_tainted(
45+
encoded32, # $ tainted
46+
decoded32, # $ tainted
47+
encoded85, # $ tainted
48+
decoded85, # $ tainted
49+
)
50+
51+
52+
def clean_inputs():
53+
clean = b"abcd"
54+
empty = b""
55+
wrapcol = 80
56+
ignorechars = b" \n"
57+
taint(wrapcol, ignorechars)
58+
59+
encoded32 = binascii.b2a_base32(clean, wrapcol=wrapcol) # $ encodeInput=clean encodeOutput=binascii.b2a_base32(..) encodeFormat=Base32
60+
encoded85 = binascii.b2a_base85(clean, wrapcol=wrapcol) # $ encodeInput=clean encodeOutput=binascii.b2a_base85(..) encodeFormat=Base85
61+
encoded_ascii85 = binascii.b2a_ascii85(clean, wrapcol=wrapcol) # $ encodeInput=clean encodeOutput=binascii.b2a_ascii85(..) encodeFormat=Ascii85
62+
decoded32 = binascii.a2b_base32(empty, ignorechars=ignorechars) # $ decodeInput=empty decodeOutput=binascii.a2b_base32(..) decodeFormat=Base32
63+
decoded85 = binascii.a2b_base85(empty, ignorechars=ignorechars) # $ decodeInput=empty decodeOutput=binascii.a2b_base85(..) decodeFormat=Base85
64+
decoded_ascii85 = binascii.a2b_ascii85(empty, ignorechars=ignorechars) # $ decodeInput=empty decodeOutput=binascii.a2b_ascii85(..) decodeFormat=Ascii85
65+
ensure_not_tainted(
66+
encoded32,
67+
encoded85,
68+
encoded_ascii85,
69+
decoded32,
70+
decoded85,
71+
decoded_ascii85,
72+
)

0 commit comments

Comments
 (0)