Skip to content

Commit ad2481f

Browse files
committed
Python: Hide 'CheckClass' class which is old and should only be used by those queries it is specifically designed for.
1 parent f147b63 commit ad2481f

File tree

4 files changed

+141
-135
lines changed

4 files changed

+141
-135
lines changed
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import python
2+
private import semmle.python.pointsto.PointsTo
3+
4+
/** Helper class for UndefinedClassAttribute.ql and MaybeUndefinedClassAttribute.ql */
5+
class CheckClass extends ClassObject {
6+
7+
private predicate ofInterest() {
8+
not this.unknowableAttributes() and
9+
not this.getPyClass().isProbableMixin() and
10+
this.getPyClass().isPublic() and
11+
not this.getPyClass().getScope() instanceof Function and
12+
not this.probablyAbstract() and
13+
not this.declaresAttribute("__new__") and
14+
not this.selfDictAssigns() and
15+
not this.lookupAttribute("__getattribute__") != object_getattribute() and
16+
not this.hasAttribute("__getattr__") and
17+
not this.selfSetattr() and
18+
/* If class overrides object.__init__, but we can't resolve it to a Python function then give up */
19+
forall(ClassObject sup |
20+
sup = this.getAnImproperSuperType() and
21+
sup.declaresAttribute("__init__") and
22+
not sup = theObjectType() |
23+
sup.declaredAttribute("__init__") instanceof PyFunctionObject
24+
)
25+
}
26+
27+
predicate alwaysDefines(string name) {
28+
auto_name(name) or
29+
this.hasAttribute(name) or
30+
this.getAnImproperSuperType().assignedInInit(name) or
31+
this.getMetaClass().assignedInInit(name)
32+
}
33+
34+
predicate sometimesDefines(string name) {
35+
this.alwaysDefines(name) or
36+
exists(SelfAttributeStore sa |
37+
sa.getScope().getScope+() = this.getAnImproperSuperType().getPyClass() |
38+
name = sa.getName()
39+
)
40+
}
41+
42+
private predicate selfDictAssigns() {
43+
exists(Assign a, SelfAttributeRead self_dict, Subscript sub |
44+
self_dict.getName() = "__dict__" and
45+
(
46+
self_dict = sub.getObject()
47+
or
48+
/* Indirect assignment via temporary variable */
49+
exists(SsaVariable v |
50+
v.getAUse() = sub.getObject().getAFlowNode() and
51+
v.getDefinition().(DefinitionNode).getValue() = self_dict.getAFlowNode()
52+
)
53+
) and
54+
a.getATarget() = sub and
55+
exists(FunctionObject meth | meth = this.lookupAttribute(_) and a.getScope() = meth.getFunction())
56+
)
57+
}
58+
59+
pragma [nomagic]
60+
private predicate monkeyPatched(string name) {
61+
exists(Attribute a |
62+
a.getCtx() instanceof Store and
63+
PointsTo::points_to(a.getObject().getAFlowNode(), _, this, _, _) and a.getName() = name
64+
)
65+
}
66+
67+
private predicate selfSetattr() {
68+
exists(Call c, Name setattr, Name self, Function method |
69+
( method.getScope() = this.getPyClass() or
70+
method.getScope() = this.getASuperType().getPyClass()
71+
) and
72+
c.getScope() = method and
73+
c.getFunc() = setattr and
74+
setattr.getId() = "setattr" and
75+
c.getArg(0) = self and
76+
self.getId() = "self"
77+
)
78+
}
79+
80+
predicate interestingUndefined(SelfAttributeRead a) {
81+
exists(string name | name = a.getName() |
82+
interestingContext(a, name) and
83+
not this.definedInBlock(a.getAFlowNode().getBasicBlock(), name)
84+
)
85+
}
86+
87+
private predicate interestingContext(SelfAttributeRead a, string name) {
88+
name = a.getName() and
89+
this.ofInterest() and
90+
this.getPyClass() = a.getScope().getScope() and
91+
not a.locallyDefined() and
92+
not a.guardedByHasattr() and
93+
a.getScope().isPublic() and
94+
not this.monkeyPatched(name) and
95+
not attribute_assigned_in_method(lookupAttribute("setUp"), name)
96+
}
97+
98+
private predicate probablyAbstract() {
99+
this.getName().matches("Abstract%")
100+
or
101+
this.isAbstract()
102+
}
103+
104+
private pragma[nomagic] predicate definitionInBlock(BasicBlock b, string name) {
105+
exists(SelfAttributeStore sa |
106+
sa.getAFlowNode().getBasicBlock() = b and sa.getName() = name and sa.getClass() = this.getPyClass()
107+
)
108+
or
109+
exists(FunctionObject method | this.lookupAttribute(_) = method |
110+
attribute_assigned_in_method(method, name) and
111+
b = method.getACall().getBasicBlock()
112+
)
113+
}
114+
115+
private pragma[nomagic] predicate definedInBlock(BasicBlock b, string name) {
116+
// manual specialisation: this is only called from interestingUndefined,
117+
// so we can push the context in from there, which must apply to a
118+
// SelfAttributeRead in the same scope
119+
exists(SelfAttributeRead a |
120+
a.getScope() = b.getScope() and name = a.getName() |
121+
interestingContext(a, name)
122+
)
123+
and
124+
this.definitionInBlock(b, name)
125+
or
126+
exists(BasicBlock prev | this.definedInBlock(prev, name) and prev.getASuccessor() = b)
127+
}
128+
129+
}
130+
131+
132+
private Object object_getattribute() {
133+
py_cmembers_versioned(theObjectType(), "__getattribute__", result, major_version().toString())
134+
}
135+
136+
private predicate auto_name(string name) {
137+
name = "__class__" or name = "__dict__"
138+
}
139+

python/ql/src/Classes/MaybeUndefinedClassAttribute.ql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import python
1414
import semmle.python.SelfAttribute
15+
import ClassAttributes
1516

1617
predicate guarded_by_other_attribute(SelfAttributeRead a, CheckClass c) {
1718
c.sometimesDefines(a.getName()) and

python/ql/src/Classes/UndefinedClassAttribute.ql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import python
1414
import semmle.python.SelfAttribute
15+
import ClassAttributes
1516

1617
predicate undefined_class_attribute(SelfAttributeRead a, CheckClass c, int line, string name) {
1718
name = a.getName() and

python/ql/src/semmle/python/SelfAttribute.qll

Lines changed: 0 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -85,137 +85,6 @@ class SelfAttributeStore extends SelfAttribute {
8585

8686
}
8787

88-
private Object object_getattribute() {
89-
py_cmembers_versioned(theObjectType(), "__getattribute__", result, major_version().toString())
90-
}
91-
92-
/** Helper class for UndefinedClassAttribute.ql and MaybeUndefinedClassAttribute.ql */
93-
class CheckClass extends ClassObject {
94-
95-
private predicate ofInterest() {
96-
not this.unknowableAttributes() and
97-
not this.getPyClass().isProbableMixin() and
98-
this.getPyClass().isPublic() and
99-
not this.getPyClass().getScope() instanceof Function and
100-
not this.probablyAbstract() and
101-
not this.declaresAttribute("__new__") and
102-
not this.selfDictAssigns() and
103-
not this.lookupAttribute("__getattribute__") != object_getattribute() and
104-
not this.hasAttribute("__getattr__") and
105-
not this.selfSetattr() and
106-
/* If class overrides object.__init__, but we can't resolve it to a Python function then give up */
107-
forall(ClassObject sup |
108-
sup = this.getAnImproperSuperType() and
109-
sup.declaresAttribute("__init__") and
110-
not sup = theObjectType() |
111-
sup.declaredAttribute("__init__") instanceof PyFunctionObject
112-
)
113-
}
114-
115-
predicate alwaysDefines(string name) {
116-
auto_name(name) or
117-
this.hasAttribute(name) or
118-
this.getAnImproperSuperType().assignedInInit(name) or
119-
this.getMetaClass().assignedInInit(name)
120-
}
121-
122-
predicate sometimesDefines(string name) {
123-
this.alwaysDefines(name) or
124-
exists(SelfAttributeStore sa |
125-
sa.getScope().getScope+() = this.getAnImproperSuperType().getPyClass() |
126-
name = sa.getName()
127-
)
128-
}
129-
130-
private predicate selfDictAssigns() {
131-
exists(Assign a, SelfAttributeRead self_dict, Subscript sub |
132-
self_dict.getName() = "__dict__" and
133-
(
134-
self_dict = sub.getObject()
135-
or
136-
/* Indirect assignment via temporary variable */
137-
exists(SsaVariable v |
138-
v.getAUse() = sub.getObject().getAFlowNode() and
139-
v.getDefinition().(DefinitionNode).getValue() = self_dict.getAFlowNode()
140-
)
141-
) and
142-
a.getATarget() = sub and
143-
exists(FunctionObject meth | meth = this.lookupAttribute(_) and a.getScope() = meth.getFunction())
144-
)
145-
}
146-
147-
pragma [nomagic]
148-
private predicate monkeyPatched(string name) {
149-
exists(Attribute a |
150-
a.getCtx() instanceof Store and
151-
PointsTo::points_to(a.getObject().getAFlowNode(), _, this, _, _) and a.getName() = name
152-
)
153-
}
154-
155-
private predicate selfSetattr() {
156-
exists(Call c, Name setattr, Name self, Function method |
157-
( method.getScope() = this.getPyClass() or
158-
method.getScope() = this.getASuperType().getPyClass()
159-
) and
160-
c.getScope() = method and
161-
c.getFunc() = setattr and
162-
setattr.getId() = "setattr" and
163-
c.getArg(0) = self and
164-
self.getId() = "self"
165-
)
166-
}
167-
168-
predicate interestingUndefined(SelfAttributeRead a) {
169-
exists(string name | name = a.getName() |
170-
interestingContext(a, name) and
171-
not this.definedInBlock(a.getAFlowNode().getBasicBlock(), name)
172-
)
173-
}
174-
175-
private predicate interestingContext(SelfAttributeRead a, string name) {
176-
name = a.getName() and
177-
this.ofInterest() and
178-
this.getPyClass() = a.getScope().getScope() and
179-
not a.locallyDefined() and
180-
not a.guardedByHasattr() and
181-
a.getScope().isPublic() and
182-
not this.monkeyPatched(name) and
183-
not attribute_assigned_in_method(lookupAttribute("setUp"), name)
184-
}
185-
186-
private predicate probablyAbstract() {
187-
this.getName().matches("Abstract%")
188-
or
189-
this.isAbstract()
190-
}
191-
192-
private pragma[nomagic] predicate definitionInBlock(BasicBlock b, string name) {
193-
exists(SelfAttributeStore sa |
194-
sa.getAFlowNode().getBasicBlock() = b and sa.getName() = name and sa.getClass() = this.getPyClass()
195-
)
196-
or
197-
exists(FunctionObject method | this.lookupAttribute(_) = method |
198-
attribute_assigned_in_method(method, name) and
199-
b = method.getACall().getBasicBlock()
200-
)
201-
}
202-
203-
private pragma[nomagic] predicate definedInBlock(BasicBlock b, string name) {
204-
// manual specialisation: this is only called from interestingUndefined,
205-
// so we can push the context in from there, which must apply to a
206-
// SelfAttributeRead in the same scope
207-
exists(SelfAttributeRead a |
208-
a.getScope() = b.getScope() and name = a.getName() |
209-
interestingContext(a, name)
210-
)
211-
and
212-
this.definitionInBlock(b, name)
213-
or
214-
exists(BasicBlock prev | this.definedInBlock(prev, name) and prev.getASuccessor() = b)
215-
}
216-
217-
}
218-
21988
private predicate attr_assigned_in_method_arg_n(FunctionObject method, string name, int n) {
22089
exists(SsaVariable param |
22190
method.getFunction().getArg(n).asName() = param.getDefinition().getNode()
@@ -235,7 +104,3 @@ private predicate attr_assigned_in_method_arg_n(FunctionObject method, string na
235104
predicate attribute_assigned_in_method(FunctionObject method, string name) {
236105
attr_assigned_in_method_arg_n(method, name, 0)
237106
}
238-
239-
private predicate auto_name(string name) {
240-
name = "__class__" or name = "__dict__"
241-
}

0 commit comments

Comments
 (0)