Skip to content

Commit 5ec8511

Browse files
committed
Python: Port PyYAML model to API graphs
1 parent 14e9bda commit 5ec8511

File tree

1 file changed

+7
-69
lines changed
  • python/ql/src/semmle/python/frameworks

1 file changed

+7
-69
lines changed

python/ql/src/semmle/python/frameworks/Yaml.qll

Lines changed: 7 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ private import python
1111
private import semmle.python.dataflow.new.DataFlow
1212
private import semmle.python.dataflow.new.RemoteFlowSources
1313
private import semmle.python.Concepts
14+
private import semmle.python.ApiGraphs
1415

1516
/**
1617
* Provides classes modeling security-relevant aspects of the PyYAML package (obtained
@@ -20,78 +21,15 @@ private import semmle.python.Concepts
2021
* - https://pyyaml.org/wiki/PyYAMLDocumentation
2122
* - https://pyyaml.docsforge.com/master/documentation/
2223
*/
23-
private module Yaml {
24-
/** Gets a reference to the `yaml` module. */
25-
private DataFlow::Node yaml(DataFlow::TypeTracker t) {
26-
t.start() and
27-
result = DataFlow::importNode("yaml")
28-
or
29-
exists(DataFlow::TypeTracker t2 | result = yaml(t2).track(t2, t))
30-
}
31-
32-
/** Gets a reference to the `yaml` module. */
33-
DataFlow::Node yaml() { result = yaml(DataFlow::TypeTracker::end()) }
34-
35-
/** Provides models for the `yaml` module. */
36-
module yaml {
37-
/**
38-
* Gets a reference to the attribute `attr_name` of the `yaml` module.
39-
* WARNING: Only holds for a few predefined attributes.
40-
*
41-
* For example, using `attr_name = "load"` will get all uses of `yaml.load`.
42-
*/
43-
private DataFlow::Node yaml_attr(DataFlow::TypeTracker t, string attr_name) {
44-
attr_name in [
45-
// functions
46-
"load", "load_all", "full_load", "full_load_all", "unsafe_load", "unsafe_load_all",
47-
"safe_load", "safe_load_all",
48-
// Classes
49-
"SafeLoader", "BaseLoader"
50-
] and
51-
(
52-
t.start() and
53-
result = DataFlow::importNode("yaml." + attr_name)
54-
or
55-
t.startInAttr(attr_name) and
56-
result = yaml()
57-
)
58-
or
59-
// Due to bad performance when using normal setup with `yaml_attr(t2, attr_name).track(t2, t)`
60-
// we have inlined that code and forced a join
61-
exists(DataFlow::TypeTracker t2 |
62-
exists(DataFlow::StepSummary summary |
63-
yaml_attr_first_join(t2, attr_name, result, summary) and
64-
t = t2.append(summary)
65-
)
66-
)
67-
}
68-
69-
pragma[nomagic]
70-
private predicate yaml_attr_first_join(
71-
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary
72-
) {
73-
DataFlow::StepSummary::step(yaml_attr(t2, attr_name), res, summary)
74-
}
75-
76-
/**
77-
* Gets a reference to the attribute `attr_name` of the `yaml` module.
78-
* WARNING: Only holds for a few predefined attributes.
79-
*
80-
* For example, using `attr_name = "load"` will get all uses of `yaml.load`.
81-
*/
82-
DataFlow::Node yaml_attr(string attr_name) {
83-
result = yaml_attr(DataFlow::TypeTracker::end(), attr_name)
84-
}
85-
}
86-
}
24+
private module Yaml { }
8725

8826
/**
8927
* A call to any of the loading functions in `yaml` (`load`, `load_all`, `full_load`,
9028
* `full_load_all`, `unsafe_load`, `unsafe_load_all`, `safe_load`, `safe_load_all`)
9129
*
9230
* See https://pyyaml.org/wiki/PyYAMLDocumentation (you will have to scroll down).
9331
*/
94-
private class YamlLoadCall extends Decoding::Range, DataFlow::CfgNode {
32+
private class YamlLoadCall extends Decoding::Range, DataFlow::CallCfgNode {
9533
override CallNode node;
9634
string func_name;
9735

@@ -100,7 +38,7 @@ private class YamlLoadCall extends Decoding::Range, DataFlow::CfgNode {
10038
"load", "load_all", "full_load", "full_load_all", "unsafe_load", "unsafe_load_all",
10139
"safe_load", "safe_load_all"
10240
] and
103-
node.getFunction() = Yaml::yaml::yaml_attr(func_name).asCfgNode()
41+
this = API::moduleImport("yaml").getMember(func_name).getACall()
10442
}
10543

10644
/**
@@ -117,13 +55,13 @@ private class YamlLoadCall extends Decoding::Range, DataFlow::CfgNode {
11755
// If the `Loader` is not set to either `SafeLoader` or `BaseLoader` or not set at all,
11856
// then the default loader will be used, which is not safe.
11957
not exists(DataFlow::Node loader_arg |
120-
loader_arg.asCfgNode() in [node.getArg(1), node.getArgByName("Loader")]
58+
loader_arg in [this.getArg(1), this.getArgByName("Loader")]
12159
|
122-
loader_arg = Yaml::yaml::yaml_attr(["SafeLoader", "BaseLoader"])
60+
loader_arg = API::moduleImport("yaml").getMember(["SafeLoader", "BaseLoader"]).getAUse()
12361
)
12462
}
12563

126-
override DataFlow::Node getAnInput() { result.asCfgNode() = node.getArg(0) }
64+
override DataFlow::Node getAnInput() { result = this.getArg(0) }
12765

12866
override DataFlow::Node getOutput() { result = this }
12967

0 commit comments

Comments
 (0)