Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion python/ql/examples/snippets/builtin_object.ql
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* builtin
* object
*/

import python

from Expr e, string name
Expand Down
2 changes: 1 addition & 1 deletion python/ql/examples/snippets/call.ql
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* @tags call
* function
*/

import python

from Value len, CallNode call
Expand Down
4 changes: 2 additions & 2 deletions python/ql/examples/snippets/catch_exception.ql
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
* try
* exception
*/

import python

from ExceptStmt ex, ClassValue cls
where
where
cls.getName() = "MyExceptionClass" and
ex.getType().pointsTo(cls)
select ex
7 changes: 4 additions & 3 deletions python/ql/examples/snippets/conditional_expression.ql
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
import python

from IfExp e, ClassObject cls1, ClassObject cls2
where
e.getBody().refersTo(_, cls1, _) and e.getOrelse().refersTo(_, cls2, _) and
where
e.getBody().refersTo(_, cls1, _) and
e.getOrelse().refersTo(_, cls2, _) and
cls1 != cls2
select e
select e
2 changes: 1 addition & 1 deletion python/ql/examples/snippets/emptyblock.ql
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* block
* statement
*/

import python

from StmtList blk
Expand Down
6 changes: 3 additions & 3 deletions python/ql/examples/snippets/emptythen.ql
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
/**
* @id py/examples/emptythen
* @name If statements with empty then branch
* @description Finds 'if' statements where the "then" branch
* @description Finds 'if' statements where the "then" branch
* consists entirely of Pass statements
* @tags if
* then
* empty
* conditional
* branch
*/

import python

from If i
where
not exists(Stmt s |
i.getStmt(_) = s and
i.getStmt(_) = s and
not s instanceof Pass
)
select i
2 changes: 1 addition & 1 deletion python/ql/examples/snippets/eq_true.ql
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* test
* boolean
*/

import python

from Compare eq
Expand Down
2 changes: 1 addition & 1 deletion python/ql/examples/snippets/equalitystmt.ql
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* equality
* expression statement
*/

import python

from ExprStmt e, Compare eq
Expand Down
4 changes: 2 additions & 2 deletions python/ql/examples/snippets/extend_class.ql
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
* subtype
* supertype
*/

import python

from ClassObject sub, ClassObject base
where
where
base.getName() = "MyClass" and
sub.getABaseType() = base
select sub
2 changes: 1 addition & 1 deletion python/ql/examples/snippets/filename.ql
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* @description Finds files called `spam.py`
* @tags file
*/

import python

from File f
Expand Down
2 changes: 1 addition & 1 deletion python/ql/examples/snippets/generator.ql
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ import python

from Function f
where f.isGenerator()
select f
select f
2 changes: 1 addition & 1 deletion python/ql/examples/snippets/integer_literal.ql
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* @tags integer
* literal
*/

import python

from IntegerLiteral literal
Expand Down
4 changes: 2 additions & 2 deletions python/ql/examples/snippets/method_call.ql
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
* @tags call
* method
*/

import python

from AstNode call, PythonFunctionValue method
where
where
method.getQualifiedName() = "MyClass.methodName" and
method.getACall().getNode() = call
select call
2 changes: 1 addition & 1 deletion python/ql/examples/snippets/new_instance.ql
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* constructor
* new
*/

import python

from Call new, ClassValue cls
Expand Down
2 changes: 1 addition & 1 deletion python/ql/examples/snippets/override_method.ql
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* @tags method
* override
*/

import python

from FunctionObject override, FunctionObject base
Expand Down
4 changes: 2 additions & 2 deletions python/ql/examples/snippets/print.ql
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
* @description Find print statements or calls to the builtin function 'print'
* @tags print
*/

import python

from AstNode print
where
where
/* Python 2 without `from __future__ import print_function` */
print instanceof Print
or
Expand Down
11 changes: 6 additions & 5 deletions python/ql/examples/snippets/private_access.ql
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
/**
* @id py/examples/private-access
* @name Private access
* @description Find accesses to "private" attributes (those starting with an underscore)
* @description Find accesses to "private" attributes (those starting with an underscore)
* @tags access
* private
*/

import python

predicate is_private(Attribute a) {
a.getName().matches("\\_%") and
a.getName().matches("\\_%") and
not a.getName().matches("\\_\\_%\\_\\_")
}

from Attribute access
where is_private(access) and
not access.getObject().(Name).getId() = "self"
where
is_private(access) and
not access.getObject().(Name).getId() = "self"
select access
6 changes: 2 additions & 4 deletions python/ql/examples/snippets/raise_exception.ql
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@
* raise
* exception
*/

import python

from Raise raise, ClassValue ex
where
ex.getName() = "AnException" and
(
raise.getException().pointsTo(ex.getASuperType())
)
raise.getException().pointsTo(ex.getASuperType())
select raise, "Don't raise instances of 'AnException'"
4 changes: 2 additions & 2 deletions python/ql/examples/snippets/raw_string.ql
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/**
* @id py/examples/raw-string
* @name Raw string literals
* @description Finds string literals with an 'r' prefix
* @description Finds string literals with an 'r' prefix
* @tags string
* raw
*/

import python

from StrConst s
Expand Down
4 changes: 2 additions & 2 deletions python/ql/examples/snippets/store_none.ql
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
* collection
* add
*/

import python

from SubscriptNode store
where
where
store.isStore() and
store.getIndex().pointsTo(Value::named("None"))
select store
5 changes: 3 additions & 2 deletions python/ql/examples/snippets/tryfinally.ql
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import python

from Try t
where exists(t.getFinalbody())
and not exists(t.getAHandler())
where
exists(t.getFinalbody()) and
not exists(t.getAHandler())
select t
46 changes: 25 additions & 21 deletions python/ql/src/Classes/ConflictingAttributesInBaseClasses.ql
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ import python

predicate does_nothing(PyFunctionObject f) {
not exists(Stmt s | s.getScope() = f.getFunction() |
not s instanceof Pass and not ((ExprStmt)s).getValue() = f.getFunction().getDocString()
not s instanceof Pass and not s.(ExprStmt).getValue() = f.getFunction().getDocString()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wow that is SUPER NICE ❤️

)
}

/* If a method performs a super() call then it is OK as the 'overridden' method will get called */
predicate calls_super(FunctionObject f) {
exists(Call sup, Call meth, Attribute attr, GlobalVariable v |
exists(Call sup, Call meth, Attribute attr, GlobalVariable v |
meth.getScope() = f.getFunction() and
meth.getFunc() = attr and
attr.getObject() = sup and
Expand All @@ -33,25 +33,29 @@ predicate calls_super(FunctionObject f) {

/** Holds if the given name is white-listed for some reason */
predicate whitelisted(string name) {
/* The standard library specifically recommends this :(
* See https://docs.python.org/3/library/socketserver.html#asynchronous-mixins */
/*
* The standard library specifically recommends this :(
* See https://docs.python.org/3/library/socketserver.html#asynchronous-mixins
*/

name = "process_request"
}

from ClassObject c, ClassObject b1, ClassObject b2, string name,
int i1, int i2, Object o1, Object o2
where c.getBaseType(i1) = b1 and
c.getBaseType(i2) = b2 and
i1 < i2 and o1 != o2 and
o1 = b1.lookupAttribute(name) and
o2 = b2.lookupAttribute(name) and
not name.matches("\\_\\_%\\_\\_") and
not calls_super(o1) and
not does_nothing(o2) and
not whitelisted(name) and
not o1.overrides(o2) and
not o2.overrides(o1) and
not c.declaresAttribute(name)

select c, "Base classes have conflicting values for attribute '" + name + "': $@ and $@.", o1, o1.toString(), o2, o2.toString()

from
ClassObject c, ClassObject b1, ClassObject b2, string name, int i1, int i2, Object o1, Object o2
where
c.getBaseType(i1) = b1 and
c.getBaseType(i2) = b2 and
i1 < i2 and
o1 != o2 and
o1 = b1.lookupAttribute(name) and
o2 = b2.lookupAttribute(name) and
not name.matches("\\_\\_%\\_\\_") and
not calls_super(o1) and
not does_nothing(o2) and
not whitelisted(name) and
not o1.overrides(o2) and
not o2.overrides(o1) and
not c.declaresAttribute(name)
select c, "Base classes have conflicting values for attribute '" + name + "': $@ and $@.", o1,
o1.toString(), o2, o2.toString()
25 changes: 15 additions & 10 deletions python/ql/src/Classes/DefineEqualsWhenAddingAttributes.ql
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import semmle.python.SelfAttribute
import Equality

predicate class_stores_to_attribute(ClassObject cls, SelfAttributeStore store, string name) {
exists(FunctionObject f | f = cls.declaredAttribute(_) and store.getScope() = f.getFunction() and store.getName() = name) and
exists(FunctionObject f |
f = cls.declaredAttribute(_) and store.getScope() = f.getFunction() and store.getName() = name
) and
/* Exclude classes used as metaclasses */
not cls.getASuperType() = theTypeType()
}
Expand All @@ -30,23 +32,26 @@ predicate should_override_eq(ClassObject cls, Object base_eq) {
)
}

/** Does the non-overridden __eq__ method access the attribute,
/**
* Does the non-overridden __eq__ method access the attribute,
* which implies that the __eq__ method does not need to be overridden.
*/
predicate superclassEqExpectsAttribute(ClassObject cls, PyFunctionObject base_eq, string attrname) {
not cls.declaresAttribute("__eq__") and
exists(ClassObject sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq |
exists(SelfAttributeRead store |
store.getName() = attrname |
exists(SelfAttributeRead store | store.getName() = attrname |
store.getScope() = base_eq.getFunction()
)
)
}

from ClassObject cls, SelfAttributeStore store, Object base_eq
where class_stores_to_attribute(cls, store, _) and should_override_eq(cls, base_eq) and
/* Don't report overridden unittest.TestCase. -- TestCase overrides __eq__, but subclasses do not really need to. */
not cls.getASuperType().getName() = "TestCase" and
not superclassEqExpectsAttribute(cls, base_eq, store.getName())

select cls, "The class '" + cls.getName() + "' does not override $@, but adds the new attribute $@.", base_eq, "'__eq__'", store, store.getName()
where
class_stores_to_attribute(cls, store, _) and
should_override_eq(cls, base_eq) and
/* Don't report overridden unittest.TestCase. -- TestCase overrides __eq__, but subclasses do not really need to. */
not cls.getASuperType().getName() = "TestCase" and
not superclassEqExpectsAttribute(cls, base_eq, store.getName())
select cls,
"The class '" + cls.getName() + "' does not override $@, but adds the new attribute $@.", base_eq,
"'__eq__'", store, store.getName()
Loading