Skip to content

Commit 8ee804a

Browse files
committed
Python: Add toml modeling
1 parent 14bc297 commit 8ee804a

File tree

5 files changed

+117
-12
lines changed

5 files changed

+117
-12
lines changed

docs/codeql/support/reusables/frameworks.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ Python built-in support
163163
PyYAML, Serialization
164164
ruamel.yaml, Serialization
165165
simplejson, Serialization
166+
toml, Serialization
166167
ujson, Serialization
167168
fabric, Utility library
168169
idna, Utility library
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* Added modeling of the PyPI package `toml`, which provides encoding/decoding of TOML documents, leading to new taint-tracking steps.

python/ql/lib/semmle/python/Frameworks.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ private import semmle.python.frameworks.RuamelYaml
3030
private import semmle.python.frameworks.Simplejson
3131
private import semmle.python.frameworks.SqlAlchemy
3232
private import semmle.python.frameworks.Stdlib
33+
private import semmle.python.frameworks.Toml
3334
private import semmle.python.frameworks.Tornado
3435
private import semmle.python.frameworks.Twisted
3536
private import semmle.python.frameworks.Ujson
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/**
2+
* Provides classes modeling security-relevant aspects of the `toml` PyPI package.
3+
*
4+
* See
5+
* - https://pypi.org/project/toml/
6+
* - https://github.com/uiri/toml#api-reference
7+
*/
8+
9+
private import python
10+
private import semmle.python.dataflow.new.DataFlow
11+
private import semmle.python.Concepts
12+
private import semmle.python.ApiGraphs
13+
14+
/**
15+
* Provides classes modeling security-relevant aspects of the `toml` PyPI package
16+
*
17+
* See
18+
* - https://pypi.org/project/toml/
19+
* - https://github.com/uiri/toml#api-reference
20+
*/
21+
private module Toml {
22+
/**
23+
* A call to `toml.loads`
24+
*
25+
* See https://github.com/uiri/toml#api-reference
26+
*/
27+
private class TomlLoadsCall extends Decoding::Range, DataFlow::CallCfgNode {
28+
TomlLoadsCall() {
29+
this = API::moduleImport("toml").getMember("loads").getACall()
30+
or
31+
this = API::moduleImport("toml").getMember("decoder").getMember("loads").getACall()
32+
}
33+
34+
override predicate mayExecuteInput() { none() }
35+
36+
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("s")] }
37+
38+
override DataFlow::Node getOutput() { result = this }
39+
40+
override string getFormat() { result = "TOML" }
41+
}
42+
43+
/**
44+
* A call to `toml.load`
45+
*
46+
* See https://github.com/uiri/toml#api-reference
47+
*/
48+
private class TomlLoadCall extends Decoding::Range, DataFlow::CallCfgNode {
49+
TomlLoadCall() {
50+
this = API::moduleImport("toml").getMember("load").getACall()
51+
or
52+
this = API::moduleImport("toml").getMember("decoder").getMember("load").getACall()
53+
}
54+
55+
override predicate mayExecuteInput() { none() }
56+
57+
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("f")] }
58+
59+
override DataFlow::Node getOutput() { result = this }
60+
61+
override string getFormat() { result = "TOML" }
62+
}
63+
64+
/**
65+
* A call to `toml.dumps`
66+
*
67+
* See https://github.com/uiri/toml#api-reference
68+
*/
69+
private class TomlDumpsCall extends Encoding::Range, DataFlow::CallCfgNode {
70+
TomlDumpsCall() {
71+
this = API::moduleImport("toml").getMember("dumps").getACall()
72+
or
73+
this = API::moduleImport("toml").getMember("encoder").getMember("dumps").getACall()
74+
}
75+
76+
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("o")] }
77+
78+
override DataFlow::Node getOutput() { result = this }
79+
80+
override string getFormat() { result = "TOML" }
81+
}
82+
83+
/**
84+
* A call to `toml.dump`
85+
*
86+
* See https://github.com/uiri/toml#api-reference
87+
*/
88+
private class TomlDumpCall extends Encoding::Range, DataFlow::CallCfgNode {
89+
TomlDumpCall() {
90+
this = API::moduleImport("toml").getMember("dump").getACall()
91+
or
92+
this = API::moduleImport("toml").getMember("encoder").getMember("dump").getACall()
93+
}
94+
95+
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("o")] }
96+
97+
override DataFlow::Node getOutput() { result in [this.getArg(1), this.getArgByName("f")] }
98+
99+
override string getFormat() { result = "TOML" }
100+
}
101+
}

python/ql/test/library-tests/frameworks/toml/test.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,34 @@
55
decoded = {"title" : "example"}
66

77
# LOADING
8-
assert decoded == toml.loads(encoded) # $ MISSING: decodeInput=encoded
9-
assert decoded == toml.loads(s=encoded) # $ MISSING: decodeInput=encoded
8+
assert decoded == toml.loads(encoded) # $ decodeInput=encoded decodeFormat=TOML decodeOutput=toml.loads(..)
9+
assert decoded == toml.loads(s=encoded) # $ decodeInput=encoded decodeFormat=TOML decodeOutput=toml.loads(..)
1010

1111
# this is not the official way to do things, but it works
12-
assert decoded == toml.decoder.loads(encoded) # $ MISSING: decodeInput=encoded
12+
assert decoded == toml.decoder.loads(encoded) # $ decodeInput=encoded decodeFormat=TOML decodeOutput=toml.decoder.loads(..)
1313

1414
f_encoded = StringIO(encoded)
15-
assert decoded == toml.load(f_encoded) # $ MISSING: decodeInput=f_encoded
15+
assert decoded == toml.load(f_encoded) # $ decodeInput=f_encoded decodeFormat=TOML decodeOutput=toml.load(..)
1616

1717
f_encoded = StringIO(encoded)
18-
assert decoded == toml.load(f=f_encoded) # $ MISSING: decodeInput=f_encoded
18+
assert decoded == toml.load(f=f_encoded) # $ decodeInput=f_encoded decodeFormat=TOML decodeOutput=toml.load(..)
1919

2020
f_encoded = StringIO(encoded)
21-
assert decoded == toml.decoder.load(f_encoded) # $ MISSING: decodeInput=f_encoded
21+
assert decoded == toml.decoder.load(f_encoded) # $ decodeInput=f_encoded decodeFormat=TOML decodeOutput=toml.decoder.load(..)
2222

2323
# DUMPING
24-
assert encoded == toml.dumps(decoded) # $ MISSING: encodeInput=decoded
25-
assert encoded == toml.dumps(o=decoded) # $ MISSING: encodeInput=decoded
26-
assert encoded == toml.encoder.dumps(decoded) # $ MISSING: encodeInput=decoded
24+
assert encoded == toml.dumps(decoded) # $ encodeInput=decoded encodeFormat=TOML encodeOutput=toml.dumps(..)
25+
assert encoded == toml.dumps(o=decoded) # $ encodeInput=decoded encodeFormat=TOML encodeOutput=toml.dumps(..)
26+
assert encoded == toml.encoder.dumps(decoded) # $ encodeInput=decoded encodeFormat=TOML encodeOutput=toml.encoder.dumps(..)
2727

2828
f_encoded = StringIO()
29-
toml.dump(decoded, f_encoded) # $ MISSING: encodeInput=decoded
29+
toml.dump(decoded, f_encoded) # $ encodeInput=decoded encodeFormat=TOML encodeOutput=f_encoded
3030
assert encoded == f_encoded.getvalue()
3131

3232
f_encoded = StringIO()
33-
toml.dump(o=decoded, f=f_encoded) # $ MISSING: encodeInput=decoded
33+
toml.dump(o=decoded, f=f_encoded) # $ encodeInput=decoded encodeFormat=TOML encodeOutput=f_encoded
3434
assert encoded == f_encoded.getvalue()
3535

3636
f_encoded = StringIO()
37-
toml.encoder.dump(decoded, f_encoded) # $ MISSING: encodeInput=decoded
37+
toml.encoder.dump(decoded, f_encoded) # $ encodeInput=decoded encodeFormat=TOML encodeOutput=f_encoded
3838
assert encoded == f_encoded.getvalue()

0 commit comments

Comments
 (0)