Skip to content

Commit b4a31e5

Browse files
authored
Python: Add global attribute writes
1 parent f71cf2e commit b4a31e5

File tree

2 files changed

+27
-1
lines changed

2 files changed

+27
-1
lines changed

python/ql/lib/semmle/python/dataflow/new/internal/Attributes.qll

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,32 @@ private class AttributeAssignmentAsAttrWrite extends AttrWrite, CfgNode {
9292
override string getAttributeName() { result = node.getName() }
9393
}
9494

95+
/**
96+
* An attribute assignment where the object is a global variable: `global_var.attr = value`.
97+
*
98+
* Syntactically, this is identical to the situation that is covered by
99+
* `AttributeAssignmentAsAttrWrite`, however in this case we want to behave as if the object that is
100+
* being written is the underlying `ModuleVariableNode`.
101+
*/
102+
private class GlobalAttributeAssignmentAsAttrWrite extends AttrWrite, CfgNode {
103+
override AttributeAssignmentNode node;
104+
105+
override Node getValue() { result.asCfgNode() = node.getValue() }
106+
107+
override Node getObject() {
108+
result.(ModuleVariableNode).getVariable() = node.getObject().getNode().(Name).getVariable()
109+
}
110+
111+
override ExprNode getAttributeNameExpr() {
112+
// Attribute names don't exist as `Node`s in the control flow graph, as they can only ever be
113+
// identifiers, and are therefore represented directly as strings.
114+
// Use `getAttributeName` to access the name of the attribute.
115+
none()
116+
}
117+
118+
override string getAttributeName() { result = node.getName() }
119+
}
120+
95121
/** Represents `CallNode`s that may refer to calls to built-in functions or classes. */
96122
private class BuiltInCallNode extends CallNode {
97123
string name;

python/ql/test/experimental/dataflow/typetracking/attribute_tests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def test_global_attribute_assignment():
5454
global_var.foo = tracked # $ tracked tracked=foo
5555

5656
def test_global_attribute_read():
57-
x = global_var.foo # $ MISSING: tracked tracked=foo
57+
x = global_var.foo # $ tracked tracked=foo
5858

5959
def test_local_attribute_assignment():
6060
# Same as `test_global_attribute_assignment`, but the assigned variable is not global

0 commit comments

Comments
 (0)