mirror of
https://github.com/github/codeql.git
synced 2026-05-01 03:35:13 +02:00
Merge branch 'master' into cpp340a
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|----------------------------|------------------------|------------------------------------------------------------------|
|
||||
| Dead code due to goto or break statement (`cpp/dead-code-goto`) | Fewer false positive results | Functions containing preprocessor logic are now excluded from this analysis. |
|
||||
| Mismatching new/free or malloc/delete (`cpp/new-free-mismatch`) | Fewer false positive results | Fixed an issue where functions were being identified as allocation functions inappropriately. Also affects `cpp/new-array-delete-mismatch` and `cpp/new-delete-array-mismatch`. |
|
||||
| Overflow in uncontrolled allocation size (`cpp/uncontrolled-allocation-size`) | More correct results | This query has been reworked so that it can find a wider variety of results. |
|
||||
| Memory may not be freed (`cpp/memory-may-not-be-freed`) | More correct results | Support added for more Microsoft-specific allocation functions, including `LocalAlloc`, `GlobalAlloc`, `HeapAlloc` and `CoTaskMemAlloc`. |
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
## General improvements
|
||||
|
||||
* Support for the following frameworks and libraries has been improved:
|
||||
- [koa](https://github.com/koajs/koa)
|
||||
- [socket.io](http://socket.io)
|
||||
- [Firebase](https://firebase.google.com/)
|
||||
|
||||
* The security queries now track data flow through Base64 decoders such as the Node.js `Buffer` class, the DOM function `atob`, and a number of npm packages intcluding [`abab`](https://www.npmjs.com/package/abab), [`atob`](https://www.npmjs.com/package/atob), [`btoa`](https://www.npmjs.com/package/btoa), [`base-64`](https://www.npmjs.com/package/base-64), [`js-base64`](https://www.npmjs.com/package/js-base64), [`Base64.js`](https://www.npmjs.com/package/Base64) and [`base64-js`](https://www.npmjs.com/package/base64-js).
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @name Hub classes
|
||||
* @description Shows coupling between classes. Large, red, boxes are hub types that depend on many other classes
|
||||
* and are depended on by many other classes.
|
||||
* @kind treemap
|
||||
* @kind table
|
||||
* @id cpp/architecture/hub-classes
|
||||
* @treemap.warnOn highValues
|
||||
* @tags maintainability
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.Exclusions
|
||||
|
||||
Stmt getNextRealStmt(Block b, int i) {
|
||||
result = b.getStmt(i + 1) and
|
||||
@@ -30,4 +31,6 @@ where b.getStmt(i) = js
|
||||
// the next statement isn't a loop that can be jumped into
|
||||
and not exists (LabelStmt ls | s.(Loop).getStmt().getAChild*() = ls)
|
||||
and not exists (SwitchCase sc | s.(Loop).getStmt().getAChild*() = sc)
|
||||
// no preprocessor logic applies
|
||||
and not functionContainsPreprocCode(js.getEnclosingFunction())
|
||||
select js, "This statement makes $@ unreachable.", s, s.toString()
|
||||
|
||||
@@ -12,5 +12,4 @@ import cpp
|
||||
from Function f, int c
|
||||
where c = f.getMetrics().getCyclomaticComplexity() and
|
||||
c > 20
|
||||
select f, c as CyclomaticComplexity,
|
||||
"AV Rule 3: All functions shall have a cyclomatic complexity number of 20 or less."
|
||||
select f, "AV Rule 3: All functions shall have a cyclomatic complexity number of 20 or less."
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description The assignment operator shall handle self-assignment correctly.
|
||||
* @kind problem
|
||||
* @id cpp/jsf/av-rule-81
|
||||
* @precision low
|
||||
* @problem.severity error
|
||||
* @tags correctness
|
||||
* external/jsf
|
||||
@@ -77,4 +78,4 @@ where hasResource(op.getDeclaringType())
|
||||
and not exists(op.getASelfEqualityTest())
|
||||
and not exists(op.getASwapCall())
|
||||
and exists(op.getADeleteExpr())
|
||||
select op
|
||||
select op, "AV Rule 81: The assignment operator shall handle self-assignment correctly."
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name AST Sanity Check
|
||||
* @description Performs sanity checks on the Abstract Syntax Tree. This query should have no results.
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @id cpp/ast-sanity-check
|
||||
*/
|
||||
|
||||
|
||||
@@ -23,11 +23,15 @@ private bindingset[comment] predicate autogeneratedComment(string comment) {
|
||||
|
||||
// changes made in this file will be lost
|
||||
"(changes made in this file will be lost)|" +
|
||||
|
||||
// do not edit/modify
|
||||
|
||||
// do not edit/modify (not mid-sentence)
|
||||
"(^ do(n't|nt| not) (hand-?)?(edit|modify))|" +
|
||||
"(! do(n't|nt| not) (hand-?)?(edit|modify))" and
|
||||
|
||||
"(! do(n't|nt| not) (hand-?)?(edit|modify))|" +
|
||||
|
||||
// do not edit/modify + generated
|
||||
"(do(n't|nt| not) (hand-?)?(edit|modify).*generated)|" +
|
||||
"(generated.*do(n't|nt| not) (hand-?)?(edit|modify))" and
|
||||
|
||||
comment.regexpMatch("(?si).*(" +
|
||||
// replace `generated` with a regexp that also catches things like
|
||||
// `auto-generated`.
|
||||
|
||||
@@ -58,3 +58,16 @@ predicate functionContainsDisabledCode(Function f) {
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the function `f` contains code that could be excluded by the preprocessor.
|
||||
*/
|
||||
predicate functionContainsPreprocCode(Function f) {
|
||||
// `f` contains a preprocessor branch
|
||||
exists(PreprocessorBranchDirective pbd, string file, int pbdStartLine, int fBlockStartLine, int fBlockEndLine |
|
||||
functionLocation(f, file, fBlockStartLine, fBlockEndLine) and
|
||||
pbdLocation(pbd, file, pbdStartLine) and
|
||||
pbdStartLine <= fBlockEndLine and
|
||||
pbdStartLine >= fBlockStartLine
|
||||
)
|
||||
}
|
||||
|
||||
@@ -572,9 +572,8 @@ class FormatLiteral extends Literal {
|
||||
((len="hh" and result instanceof IntType)
|
||||
or (len="h" and result instanceof IntType)
|
||||
or (len="l" and result = this.getLongType())
|
||||
or ((len="ll" or len="q")
|
||||
or ((len="ll" or len="L" or len="q")
|
||||
and result instanceof LongLongType)
|
||||
or (len="L" and result instanceof IntType) // doesn't affect integral conversion
|
||||
or (len="j" and result = this.getIntmax_t())
|
||||
or ((len="z" or len="Z")
|
||||
and (result = this.getSize_t() or result = this.getSsize_t()))
|
||||
@@ -599,9 +598,8 @@ class FormatLiteral extends Literal {
|
||||
((len="hh" and result instanceof CharType)
|
||||
or (len="h" and result instanceof ShortType)
|
||||
or (len="l" and result = this.getLongType())
|
||||
or ((len="ll" or len="q")
|
||||
or ((len="ll" or len="L" or len="q")
|
||||
and result instanceof LongLongType)
|
||||
or (len="L" and result instanceof IntType) // doesn't affect integral conversion
|
||||
or (len="j" and result = this.getIntmax_t())
|
||||
or ((len="z" or len="Z")
|
||||
and (result = this.getSize_t() or result = this.getSsize_t()))
|
||||
@@ -622,7 +620,7 @@ class FormatLiteral extends Literal {
|
||||
*/
|
||||
FloatingPointType getFloatingPointConversion(int n) {
|
||||
exists(string len | len = this.getLength(n) and
|
||||
if len="L" then
|
||||
if (len="L" or len="ll") then
|
||||
result instanceof LongDoubleType
|
||||
else
|
||||
result instanceof DoubleType)
|
||||
@@ -638,7 +636,7 @@ class FormatLiteral extends Literal {
|
||||
(len="hh" and base instanceof CharType)
|
||||
or (len="h" and base instanceof ShortType)
|
||||
or (len="l" and base = this.getLongType())
|
||||
or (len="ll" and base instanceof LongLongType)
|
||||
or ((len="ll" or len="L") and base instanceof LongLongType)
|
||||
or (len="q" and base instanceof LongLongType)
|
||||
)
|
||||
and base.isSigned() and base = result.getBaseType()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name IR Sanity Check
|
||||
* @description Performs sanity checks on the Intermediate Representation. This query should have no results.
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @id cpp/ir-sanity-check
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name Aliased SSA IR Sanity Check
|
||||
* @description Performs sanity checks on the Intermediate Representation. This query should have no results.
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @id cpp/aliased-ssa-ir-sanity-check
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name Raw IR Sanity Check
|
||||
* @description Performs sanity checks on the Intermediate Representation. This query should have no results.
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @id cpp/raw-ir-sanity-check
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name SSA IR Sanity Check
|
||||
* @description Performs sanity checks on the Intermediate Representation. This query should have no results.
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @id cpp/ssa-ir-sanity-check
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name Padding Sanity Check
|
||||
* @description Performs sanity checks for the padding library. This query should have no results.
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @id cpp/padding-sanity-check
|
||||
*/
|
||||
|
||||
|
||||
@@ -81,3 +81,29 @@ void test7(int x, int cond) {
|
||||
}
|
||||
end:
|
||||
}
|
||||
|
||||
#define CONFIG_DEFINE
|
||||
|
||||
void test8() {
|
||||
int x = 0;
|
||||
|
||||
#ifdef CONFIG_DEFINE
|
||||
goto skip; // GOOD (the `x++` is still reachable in some configurations)
|
||||
#endif
|
||||
x++;
|
||||
|
||||
skip:
|
||||
}
|
||||
|
||||
void test9() {
|
||||
int x = 0;
|
||||
|
||||
#ifdef CONFIG_NOTDEFINED
|
||||
goto mid;
|
||||
#endif
|
||||
goto end; // GOOD (the `x++` is still reachable in some configurations)
|
||||
mid:
|
||||
x++;
|
||||
|
||||
end:
|
||||
}
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
| printf1.h:44:18:44:20 | ull | This argument should be of type 'int' but is of type 'unsigned long long' |
|
||||
| printf1.h:45:18:45:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
|
||||
| printf1.h:46:18:46:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
|
||||
| printf1.h:47:19:47:21 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
|
||||
| printf1.h:113:17:113:17 | d | This argument should be of type 'long double' but is of type 'double' |
|
||||
| printf1.h:114:18:114:18 | d | This argument should be of type 'long double' but is of type 'double' |
|
||||
| real_world.h:61:21:61:22 | & ... | This argument should be of type 'int *' but is of type 'short *' |
|
||||
| real_world.h:62:22:62:23 | & ... | This argument should be of type 'short *' but is of type 'int *' |
|
||||
| real_world.h:63:22:63:24 | & ... | This argument should be of type 'short *' but is of type 'unsigned int *' |
|
||||
|
||||
@@ -44,7 +44,7 @@ void f(char *s, int i, unsigned char *us, const char *cs, signed char *ss, char
|
||||
printf("%d", ull); // not ok (unsigned long long -> int)
|
||||
printf("%u", ull); // not ok (unsigned long long -> unsigned int)
|
||||
printf("%x", ull); // not ok (unsigned long long -> unsigned int)
|
||||
printf("%Lx", ull); // not ok (unsigned long long -> unsigned int)
|
||||
printf("%Lx", ull); // ok
|
||||
printf("%llx", ull); // ok
|
||||
}
|
||||
|
||||
@@ -101,3 +101,36 @@ void fun1(unsigned char* a, unsigned char* b) {
|
||||
printf("%td\n", pdt); // GOOD
|
||||
printf("%td\n", a-b); // GOOD
|
||||
}
|
||||
|
||||
void extensions()
|
||||
{
|
||||
{
|
||||
long double ld;
|
||||
double d;
|
||||
|
||||
printf("%Lg", ld); // GOOD
|
||||
printf("%llg", ld); // GOOD (nonstandard equivalent to %Lg)
|
||||
printf("%Lg", d); // BAD (should be %g)
|
||||
printf("%llg", d); // BAD (should be %g)
|
||||
}
|
||||
|
||||
{
|
||||
long long int lli;
|
||||
long int li;
|
||||
|
||||
printf("%lld", lli); // GOOD
|
||||
printf("%Ld", lli); // GOOD (nonstandard equivalent to %lld)
|
||||
printf("%Ld", li); // BAD (should be %ld) [NOT DETECTED]
|
||||
printf("%lld", li); // BAD (should be %ld) [NOT DETECTED]
|
||||
}
|
||||
|
||||
{
|
||||
unsigned long long int ulli;
|
||||
unsigned long int uli;
|
||||
|
||||
printf("%llu", ulli); // GOOD
|
||||
printf("%Lu", ulli); // GOOD (nonstandard equivalent to %llu)
|
||||
printf("%Lu", uli); // BAD (should be %lu) [NOT DETECTED]
|
||||
printf("%llu", uli); // BAD (should be %lu) [NOT DETECTED]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
| printf1.h:44:18:44:20 | ull | This argument should be of type 'int' but is of type 'unsigned long long' |
|
||||
| printf1.h:45:18:45:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
|
||||
| printf1.h:46:18:46:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
|
||||
| printf1.h:47:19:47:21 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
|
||||
| printf1.h:126:18:126:19 | wc | This argument should be of type 'char *' but is of type 'wchar_t *' |
|
||||
| printf1.h:127:18:127:18 | c | This argument should be of type 'wchar_t *' but is of type 'char *' |
|
||||
| real_world.h:61:21:61:22 | & ... | This argument should be of type 'int *' but is of type 'short *' |
|
||||
|
||||
@@ -44,7 +44,7 @@ void f(char *s, int i, unsigned char *us, const char *cs, signed char *ss, char
|
||||
printf("%d", ull); // not ok (unsigned long long -> int)
|
||||
printf("%u", ull); // not ok (unsigned long long -> unsigned int)
|
||||
printf("%x", ull); // not ok (unsigned long long -> unsigned int)
|
||||
printf("%Lx", ull); // not ok (unsigned long long -> unsigned int)
|
||||
printf("%Lx", ull); // ok
|
||||
printf("%llx", ull); // ok
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
| printf1.h:44:18:44:20 | ull | This argument should be of type 'int' but is of type 'unsigned long long' |
|
||||
| printf1.h:45:18:45:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
|
||||
| printf1.h:46:18:46:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
|
||||
| printf1.h:47:19:47:21 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
|
||||
| printf1.h:71:19:71:20 | st | This argument should be of type 'ssize_t' but is of type 'unsigned long long' |
|
||||
| printf1.h:72:19:72:20 | ST | This argument should be of type 'ssize_t' but is of type 'unsigned long long' |
|
||||
| printf1.h:73:19:73:22 | c_st | This argument should be of type 'ssize_t' but is of type 'unsigned long long' |
|
||||
|
||||
@@ -44,7 +44,7 @@ void f(char *s, int i, unsigned char *us, const char *cs, signed char *ss, char
|
||||
printf("%d", ull); // not ok (unsigned long long -> int)
|
||||
printf("%u", ull); // not ok (unsigned long long -> unsigned int)
|
||||
printf("%x", ull); // not ok (unsigned long long -> unsigned int)
|
||||
printf("%Lx", ull); // not ok (unsigned long long -> unsigned int)
|
||||
printf("%Lx", ull); // ok
|
||||
printf("%llx", ull); // ok
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
| printf1.h:44:18:44:20 | ull | This argument should be of type 'int' but is of type 'unsigned long long' |
|
||||
| printf1.h:45:18:45:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
|
||||
| printf1.h:46:18:46:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
|
||||
| printf1.h:47:19:47:21 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
|
||||
| printf1.h:71:19:71:20 | st | This argument should be of type 'ssize_t' but is of type 'unsigned long long' |
|
||||
| printf1.h:72:19:72:20 | ST | This argument should be of type 'ssize_t' but is of type 'unsigned long long' |
|
||||
| printf1.h:73:19:73:22 | c_st | This argument should be of type 'ssize_t' but is of type 'unsigned long long' |
|
||||
|
||||
@@ -44,7 +44,7 @@ void f(char *s, int i, unsigned char *us, const char *cs, signed char *ss, char
|
||||
printf("%d", ull); // not ok (unsigned long long -> int)
|
||||
printf("%u", ull); // not ok (unsigned long long -> unsigned int)
|
||||
printf("%x", ull); // not ok (unsigned long long -> unsigned int)
|
||||
printf("%Lx", ull); // not ok (unsigned long long -> unsigned int)
|
||||
printf("%Lx", ull); // ok
|
||||
printf("%llx", ull); // ok
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
* @id cs/dispose-not-called-on-throw
|
||||
* @tags efficiency
|
||||
* maintainability
|
||||
* security
|
||||
* external/cwe/cwe-404
|
||||
* external/cwe/cwe-459
|
||||
* external/cwe/cwe-460
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
* @id cs/local-not-disposed
|
||||
* @tags efficiency
|
||||
* maintainability
|
||||
* security
|
||||
* external/cwe/cwe-404
|
||||
* external/cwe/cwe-459
|
||||
* external/cwe/cwe-460
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
* @id cs/loss-of-precision
|
||||
* @tags reliability
|
||||
* correctness
|
||||
* security
|
||||
* external/cwe/cwe-190
|
||||
* external/cwe/cwe-192
|
||||
* external/cwe/cwe-197
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Failure to use secure cookies
|
||||
* @description Insecured cookies may be sent in cleartext, which makes them vulnerable to
|
||||
* @description Insecure cookies may be sent in cleartext, which makes them vulnerable to
|
||||
* interception.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
|
||||
@@ -253,4 +253,86 @@ module DOM {
|
||||
reason = "must not contain any space characters"
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a call that queries the DOM for a collection of DOM nodes. */
|
||||
private DataFlow::SourceNode domElementCollection() {
|
||||
exists(string collectionName |
|
||||
collectionName = "getElementsByClassName" or
|
||||
collectionName = "getElementsByName" or
|
||||
collectionName = "getElementsByTagName" or
|
||||
collectionName = "getElementsByTagNameNS" or
|
||||
collectionName = "querySelectorAll"
|
||||
|
|
||||
(
|
||||
result = documentRef().getAMethodCall(collectionName) or
|
||||
result = DataFlow::globalVarRef(collectionName).getACall()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a call that creates a DOM node or queries the DOM for a DOM node. */
|
||||
private DataFlow::SourceNode domElementCreationOrQuery() {
|
||||
exists(string methodName |
|
||||
methodName = "createElement" or
|
||||
methodName = "createElementNS" or
|
||||
methodName = "createRange" or
|
||||
methodName = "getElementById" or
|
||||
methodName = "querySelector"
|
||||
|
|
||||
result = documentRef().getAMethodCall(methodName) or
|
||||
result = DataFlow::globalVarRef(methodName).getACall()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a data flow node that refers directly to a value from the DOM. */
|
||||
DataFlow::SourceNode domValueSource() {
|
||||
result.asExpr().(VarAccess).getVariable() instanceof DOMGlobalVariable or
|
||||
result = domValueRef().getAPropertyRead() or
|
||||
result = domElementCreationOrQuery() or
|
||||
result = domElementCollection()
|
||||
}
|
||||
|
||||
/** Gets a data flow node that may refer to a value from the DOM. */
|
||||
private DataFlow::SourceNode domValueRef(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = domValueSource()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = domValueRef(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a data flow node that may refer to a value from the DOM. */
|
||||
DataFlow::SourceNode domValueRef() { result = domValueRef(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** Gets a data flow node that directly refers to a DOM `location` object. */
|
||||
DataFlow::SourceNode locationSource() {
|
||||
result = domValueRef().getAPropertyRead("location")
|
||||
or
|
||||
result = DataFlow::globalVarRef("location")
|
||||
}
|
||||
|
||||
/** Gets a reference to a DOM `location` object. */
|
||||
private DataFlow::SourceNode locationRef(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = locationSource()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = locationRef(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to a DOM `location` object. */
|
||||
DataFlow::SourceNode locationRef() { result = locationRef(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/**
|
||||
* Gets a reference to the `document` object.
|
||||
*/
|
||||
private DataFlow::SourceNode documentRef(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = DataFlow::globalVarRef("document")
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = documentRef(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the 'document' object.
|
||||
*/
|
||||
DataFlow::SourceNode documentRef() { result = documentRef(DataFlow::TypeTracker::end()) }
|
||||
}
|
||||
|
||||
@@ -215,6 +215,40 @@ module Firebase {
|
||||
result = getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a Firebase method that sets up a route.
|
||||
*/
|
||||
private class RouteSetup extends HTTP::Servers::StandardRouteSetup, CallExpr {
|
||||
RouteSetup() { this = namespace().getAPropertyRead("https").getAMemberCall("onRequest").asExpr() }
|
||||
|
||||
override DataFlow::SourceNode getARouteHandler() {
|
||||
result = getARouteHandler(DataFlow::TypeBackTracker::end())
|
||||
}
|
||||
|
||||
private DataFlow::SourceNode getARouteHandler(DataFlow::TypeBackTracker t) {
|
||||
t.start() and
|
||||
result = getArgument(0).flow().getALocalSource()
|
||||
or
|
||||
exists(DataFlow::TypeBackTracker t2 | result = getARouteHandler(t2).backtrack(t2, t))
|
||||
}
|
||||
|
||||
override Expr getServer() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A function used as a route handler.
|
||||
*/
|
||||
private class RouteHandler extends Express::RouteHandler, HTTP::Servers::StandardRouteHandler,
|
||||
DataFlow::ValueNode {
|
||||
RouteHandler() { this = any(RouteSetup setup).getARouteHandler() }
|
||||
|
||||
override SimpleParameter getRouteHandlerParameter(string kind) {
|
||||
kind = "request" and result = this.(DataFlow::FunctionNode).getParameter(0).getParameter() or
|
||||
kind = "response" and result = this.(DataFlow::FunctionNode).getParameter(1).getParameter()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
/**
|
||||
* Provides classes for working with [Koa](https://koajs.com) applications.
|
||||
*/
|
||||
@@ -24,7 +25,7 @@ module Koa {
|
||||
|
||||
HeaderDefinition() {
|
||||
// ctx.set('Cache-Control', 'no-cache');
|
||||
astNode.calls(rh.getAContextExpr(), "set")
|
||||
astNode.calls(rh.getAResponseOrContextExpr(), "set")
|
||||
or
|
||||
// ctx.response.header('Cache-Control', 'no-cache')
|
||||
astNode.calls(rh.getAResponseExpr(), "header")
|
||||
@@ -58,6 +59,18 @@ module Koa {
|
||||
* route handler.
|
||||
*/
|
||||
Expr getAContextExpr() { result.(ContextExpr).getRouteHandler() = this }
|
||||
|
||||
/**
|
||||
* Gets an expression that contains the context or response
|
||||
* object of a route handler invocation.
|
||||
*/
|
||||
Expr getAResponseOrContextExpr() { result = getAResponseExpr() or result = getAContextExpr() }
|
||||
|
||||
/**
|
||||
* Gets an expression that contains the context or request
|
||||
* object of a route handler invocation.
|
||||
*/
|
||||
Expr getARequestOrContextExpr() { result = getARequestExpr() or result = getAContextExpr() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,35 +172,39 @@ module Koa {
|
||||
string kind;
|
||||
|
||||
RequestInputAccess() {
|
||||
exists(Expr request | request = rh.getARequestExpr() |
|
||||
// `ctx.request.body`
|
||||
kind = "body" and
|
||||
this.asExpr().(PropAccess).accesses(request, "body")
|
||||
or
|
||||
kind = "parameter" and
|
||||
this = getAQueryParameterAccess(rh)
|
||||
or
|
||||
kind = "parameter" and
|
||||
this = getAQueryParameterAccess(rh)
|
||||
or
|
||||
exists(Expr e | rh.getARequestOrContextExpr() = e |
|
||||
// `ctx.request.url`, `ctx.request.originalUrl`, or `ctx.request.href`
|
||||
exists(string propName |
|
||||
// `ctx.request.url`, `ctx.request.originalUrl`, or `ctx.request.href`
|
||||
kind = "url" and
|
||||
this.asExpr().(PropAccess).accesses(request, propName)
|
||||
this.asExpr().(PropAccess).accesses(e, propName)
|
||||
|
|
||||
propName = "url" or
|
||||
propName = "originalUrl" or
|
||||
propName = "url"
|
||||
or
|
||||
propName = "originalUrl"
|
||||
or
|
||||
propName = "href"
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(PropAccess cookies |
|
||||
or
|
||||
// `ctx.request.body`
|
||||
e instanceof RequestExpr and
|
||||
kind = "body" and
|
||||
this.asExpr().(PropAccess).accesses(e, "body")
|
||||
or
|
||||
// `ctx.cookies.get(<name>)`
|
||||
kind = "cookie" and
|
||||
cookies.accesses(rh.getAContextExpr(), "cookies") and
|
||||
this.asExpr().(MethodCallExpr).calls(cookies, "get")
|
||||
)
|
||||
or
|
||||
exists(RequestHeaderAccess access | access = this |
|
||||
rh = access.getRouteHandler() and
|
||||
kind = "header"
|
||||
exists(PropAccess cookies |
|
||||
e instanceof ContextExpr and
|
||||
kind = "cookie" and
|
||||
cookies.accesses(e, "cookies") and
|
||||
this = cookies.flow().(DataFlow::SourceNode).getAMethodCall("get")
|
||||
)
|
||||
or
|
||||
exists(RequestHeaderAccess access | access = this |
|
||||
rh = access.getRouteHandler() and
|
||||
kind = "header"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -199,8 +216,11 @@ module Koa {
|
||||
}
|
||||
|
||||
private DataFlow::Node getAQueryParameterAccess(RouteHandler rh) {
|
||||
// `ctx.request.query.name`
|
||||
result.asExpr().(PropAccess).getBase().(PropAccess).accesses(rh.getARequestExpr(), "query")
|
||||
// `ctx.query.name` or `ctx.request.query.name`
|
||||
exists(PropAccess q |
|
||||
q.accesses(rh.getARequestOrContextExpr(), "query") and
|
||||
result = q.flow().(DataFlow::SourceNode).getAPropertyRead()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,18 +230,18 @@ module Koa {
|
||||
RouteHandler rh;
|
||||
|
||||
RequestHeaderAccess() {
|
||||
exists(Expr request | request = rh.getARequestExpr() |
|
||||
exists(Expr e | e = rh.getARequestOrContextExpr() |
|
||||
exists(string propName, PropAccess headers |
|
||||
// `ctx.request.header.<name>`, `ctx.request.headers.<name>`
|
||||
headers.accesses(request, propName) and
|
||||
this.asExpr().(PropAccess).accesses(headers, _)
|
||||
headers.accesses(e, propName) and
|
||||
this = headers.flow().(DataFlow::SourceNode).getAPropertyRead()
|
||||
|
|
||||
propName = "header" or
|
||||
propName = "headers"
|
||||
)
|
||||
or
|
||||
// `ctx.request.get(<name>)`
|
||||
this.asExpr().(MethodCallExpr).calls(request, "get")
|
||||
this.asExpr().(MethodCallExpr).calls(e, "get")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -264,10 +284,25 @@ module Koa {
|
||||
|
||||
ResponseSendArgument() {
|
||||
exists(DataFlow::PropWrite pwn |
|
||||
pwn.writes(DataFlow::valueNode(rh.getAResponseExpr()), "body", DataFlow::valueNode(this))
|
||||
pwn
|
||||
.writes(DataFlow::valueNode(rh.getAResponseOrContextExpr()), "body",
|
||||
DataFlow::valueNode(this))
|
||||
)
|
||||
}
|
||||
|
||||
override RouteHandler getRouteHandler() { result = rh }
|
||||
}
|
||||
|
||||
/**
|
||||
* An invocation of the `redirect` method of an HTTP response object.
|
||||
*/
|
||||
private class RedirectInvocation extends HTTP::RedirectInvocation, MethodCallExpr {
|
||||
RouteHandler rh;
|
||||
|
||||
RedirectInvocation() { this.(MethodCallExpr).calls(rh.getAResponseOrContextExpr(), "redirect") }
|
||||
|
||||
override Expr getUrlArgument() { result = getArgument(0) }
|
||||
|
||||
override RouteHandler getRouteHandler() { result = rh }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,10 +121,9 @@ module ClientSideUrlRedirect {
|
||||
)
|
||||
or
|
||||
// A call to `location.replace` or `location.assign`
|
||||
exists(MethodCallExpr locationCall, string name |
|
||||
isLocation(locationCall.getReceiver()) and
|
||||
name = locationCall.getMethodName() and
|
||||
astNode = locationCall.getArgument(0)
|
||||
exists(DataFlow::MethodCallNode locationCall, string name |
|
||||
locationCall = DOM::locationRef().getAMethodCall(name) and
|
||||
this = locationCall.getArgument(0)
|
||||
|
|
||||
name = "replace" or name = "assign"
|
||||
)
|
||||
@@ -134,8 +133,7 @@ module ClientSideUrlRedirect {
|
||||
or
|
||||
// An assignment to `location.href`, `location.protocol` or `location.hostname`
|
||||
exists(DataFlow::PropWrite pw, string propName |
|
||||
isLocation(pw.getBase().asExpr()) and
|
||||
propName = pw.getPropertyName() and
|
||||
pw = DOM::locationRef().getAPropertyWrite(propName) and
|
||||
this = pw.getRhs()
|
||||
|
|
||||
propName = "href" or propName = "protocol" or propName = "hostname"
|
||||
|
||||
@@ -18,73 +18,36 @@ class DOMGlobalVariable extends GlobalVariable {
|
||||
}
|
||||
}
|
||||
|
||||
private DataFlow::SourceNode domElementCollection() {
|
||||
exists(string collectionName |
|
||||
collectionName = "getElementsByClassName" or
|
||||
collectionName = "getElementsByName" or
|
||||
collectionName = "getElementsByTagName" or
|
||||
collectionName = "getElementsByTagNameNS" or
|
||||
collectionName = "querySelectorAll"
|
||||
|
|
||||
(
|
||||
result = document().getAMethodCall(collectionName) or
|
||||
result = DataFlow::globalVarRef(collectionName).getACall()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private DataFlow::SourceNode domElementCreationOrQuery() {
|
||||
exists(string methodName |
|
||||
methodName = "createElement" or
|
||||
methodName = "createElementNS" or
|
||||
methodName = "createRange" or
|
||||
methodName = "getElementById" or
|
||||
methodName = "querySelector"
|
||||
|
|
||||
result = document().getAMethodCall(methodName) or
|
||||
result = DataFlow::globalVarRef(methodName).getACall()
|
||||
)
|
||||
}
|
||||
|
||||
private DataFlow::SourceNode domValueSource() {
|
||||
result.asExpr().(VarAccess).getVariable() instanceof DOMGlobalVariable or
|
||||
result = domValueSource().getAPropertyRead(_) or
|
||||
result = domElementCreationOrQuery() or
|
||||
result = domElementCollection()
|
||||
}
|
||||
|
||||
/** Holds if `e` could hold a value that comes from the DOM. */
|
||||
predicate isDomValue(Expr e) { domValueSource().flowsToExpr(e) }
|
||||
predicate isDomValue(Expr e) { DOM::domValueRef().flowsToExpr(e) }
|
||||
|
||||
/** Holds if `e` could refer to the `location` property of a DOM node. */
|
||||
predicate isLocation(Expr e) {
|
||||
e = domValueSource().getAPropertyReference("location").asExpr() or
|
||||
e = DOM::domValueRef().getAPropertyReference("location").asExpr()
|
||||
or
|
||||
e.accessesGlobal("location")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the 'document' object.
|
||||
*/
|
||||
DataFlow::SourceNode document() { result = DataFlow::globalVarRef("document") }
|
||||
DataFlow::SourceNode document() { result = DOM::documentRef() }
|
||||
|
||||
/** Holds if `e` could refer to the `document` object. */
|
||||
predicate isDocument(Expr e) { document().flowsToExpr(e) }
|
||||
predicate isDocument(Expr e) { DOM::documentRef().flowsToExpr(e) }
|
||||
|
||||
/** Holds if `e` could refer to the document URL. */
|
||||
predicate isDocumentURL(Expr e) {
|
||||
exists(Expr base, string propName | e.(PropAccess).accesses(base, propName) |
|
||||
isDocument(base) and
|
||||
(
|
||||
propName = "documentURI" or
|
||||
propName = "documentURIObject" or
|
||||
propName = "location" or
|
||||
propName = "referrer" or
|
||||
propName = "URL"
|
||||
)
|
||||
or
|
||||
isDomValue(base) and propName = "baseUri"
|
||||
exists(string propName | e = DOM::documentRef().getAPropertyRead(propName).asExpr() |
|
||||
propName = "documentURI" or
|
||||
propName = "documentURIObject" or
|
||||
propName = "location" or
|
||||
propName = "referrer" or
|
||||
propName = "URL"
|
||||
)
|
||||
or
|
||||
e = DOM::domValueRef().getAPropertyRead("baseUri").asExpr()
|
||||
or
|
||||
e.accessesGlobal("location")
|
||||
}
|
||||
|
||||
@@ -94,7 +57,7 @@ predicate isDocumentURL(Expr e) {
|
||||
* `href`, `hash` and `search`.
|
||||
*/
|
||||
predicate isSafeLocationProperty(PropAccess pacc) {
|
||||
exists(Expr loc, string prop | isLocation(loc) and pacc.accesses(loc, prop) |
|
||||
exists(string prop | pacc = DOM::locationRef().getAPropertyRead(prop).asExpr() |
|
||||
prop != "href" and prop != "hash" and prop != "search"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ module PropertyInjection {
|
||||
node = DataFlow::globalObjectRef()
|
||||
or
|
||||
// document.write can be accessed
|
||||
isDocument(node.asExpr())
|
||||
node = DOM::documentRef()
|
||||
or
|
||||
// 'constructor' property leads to the Function constructor.
|
||||
node.analyze().getAValue() instanceof AbstractCallable
|
||||
|
||||
@@ -75,7 +75,7 @@ module XpathInjection {
|
||||
class DomXpathSink extends Sink {
|
||||
DomXpathSink() {
|
||||
exists(string m | m = "evaluate" or m = "createExpression" |
|
||||
this = document().getAMethodCall(m).getArgument(0)
|
||||
this = DOM::documentRef().getAMethodCall(m).getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
| tst.js:72:52:72:65 | req.params.foo |
|
||||
@@ -0,0 +1,4 @@
|
||||
import javascript
|
||||
|
||||
from HTTP::RequestInputAccess ria
|
||||
select ria
|
||||
@@ -0,0 +1 @@
|
||||
| tst.js:72:52:72:65 | req.params.foo |
|
||||
@@ -0,0 +1,4 @@
|
||||
import javascript
|
||||
|
||||
from HTTP::ResponseSendArgument send
|
||||
select send
|
||||
@@ -0,0 +1 @@
|
||||
| tst.js:72:27:72:69 | (req, r ... foo); } |
|
||||
@@ -0,0 +1,4 @@
|
||||
import javascript
|
||||
|
||||
from HTTP::RouteHandler rh
|
||||
select rh
|
||||
@@ -68,3 +68,5 @@ class Box {
|
||||
let box1 = new Box(fb.database());
|
||||
let box2 = new Box(whatever());
|
||||
box2.x.ref(); // not a firebase ref
|
||||
|
||||
functions.https.onRequest((req, res) => { res.send(req.params.foo); });
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
import javascript
|
||||
|
||||
query predicate test_RedirectInvocation(
|
||||
HTTP::RedirectInvocation redirect, Expr url, HTTP::RouteHandler rh
|
||||
) {
|
||||
redirect.getUrlArgument() = url and
|
||||
redirect.getRouteHandler() = rh
|
||||
}
|
||||
@@ -26,3 +26,31 @@ app2.use(function handler2(ctx){ // HTTP::RouteHandler
|
||||
ctx.request.get('bar');
|
||||
ctx.cookies.get('baz');
|
||||
});
|
||||
|
||||
app2.use(async ctx => {
|
||||
ctx.body = x;
|
||||
ctx.body;
|
||||
ctx.query.foo;
|
||||
ctx.url;
|
||||
ctx.originalUrl;
|
||||
ctx.href;
|
||||
ctx.header.bar;
|
||||
ctx.headers.bar;
|
||||
ctx.set('bar');
|
||||
ctx.get('bar');
|
||||
|
||||
var url = ctx.query.target;
|
||||
ctx.redirect(url);
|
||||
ctx.response.redirect(url);
|
||||
});
|
||||
|
||||
app2.use(async ctx => {
|
||||
var cookies = ctx.cookies;
|
||||
cookies.get();
|
||||
|
||||
var query = ctx.query;
|
||||
query.foo;
|
||||
|
||||
var headers = ctx.headers;
|
||||
headers.foo;
|
||||
});
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
test_RouteSetup
|
||||
| src/koa.js:8:1:8:18 | app2.use(handler1) |
|
||||
| src/koa.js:10:1:28:2 | app2.us ... z');\\n}) |
|
||||
| src/koa.js:30:1:45:2 | app2.us ... rl);\\n}) |
|
||||
| src/koa.js:47:1:56:2 | app2.us ... foo;\\n}) |
|
||||
test_RequestInputAccess
|
||||
| src/koa.js:19:3:19:18 | ctx.request.body | body | src/koa.js:10:10:28:1 | functio ... az');\\n} |
|
||||
| src/koa.js:20:3:20:23 | ctx.req ... ery.foo | parameter | src/koa.js:10:10:28:1 | functio ... az');\\n} |
|
||||
@@ -11,6 +13,17 @@ test_RequestInputAccess
|
||||
| src/koa.js:25:3:25:25 | ctx.req ... ers.bar | header | src/koa.js:10:10:28:1 | functio ... az');\\n} |
|
||||
| src/koa.js:26:3:26:24 | ctx.req ... ('bar') | header | src/koa.js:10:10:28:1 | functio ... az');\\n} |
|
||||
| src/koa.js:27:3:27:24 | ctx.coo ... ('baz') | cookie | src/koa.js:10:10:28:1 | functio ... az');\\n} |
|
||||
| src/koa.js:33:2:33:14 | ctx.query.foo | parameter | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
| src/koa.js:34:2:34:8 | ctx.url | url | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
| src/koa.js:35:2:35:16 | ctx.originalUrl | url | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
| src/koa.js:36:2:36:9 | ctx.href | url | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
| src/koa.js:37:2:37:15 | ctx.header.bar | header | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
| src/koa.js:38:2:38:16 | ctx.headers.bar | header | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
| src/koa.js:40:2:40:15 | ctx.get('bar') | header | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
| src/koa.js:42:12:42:27 | ctx.query.target | parameter | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
| src/koa.js:49:2:49:14 | cookies.get() | cookie | src/koa.js:47:10:56:1 | async c ... .foo;\\n} |
|
||||
| src/koa.js:52:2:52:10 | query.foo | parameter | src/koa.js:47:10:56:1 | async c ... .foo;\\n} |
|
||||
| src/koa.js:55:2:55:12 | headers.foo | header | src/koa.js:47:10:56:1 | async c ... .foo;\\n} |
|
||||
test_RouteHandler_getAResponseHeader
|
||||
| src/koa.js:10:10:28:1 | functio ... az');\\n} | header1 | src/koa.js:11:3:11:25 | this.se ... 1', '') |
|
||||
| src/koa.js:10:10:28:1 | functio ... az');\\n} | header2 | src/koa.js:12:3:12:37 | this.re ... 2', '') |
|
||||
@@ -29,6 +42,7 @@ test_ResponseExpr
|
||||
| src/koa.js:15:13:15:24 | ctx.response | src/koa.js:10:10:28:1 | functio ... az');\\n} |
|
||||
| src/koa.js:16:3:16:5 | rsp | src/koa.js:10:10:28:1 | functio ... az');\\n} |
|
||||
| src/koa.js:18:3:18:14 | ctx.response | src/koa.js:10:10:28:1 | functio ... az');\\n} |
|
||||
| src/koa.js:44:2:44:13 | ctx.response | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
test_RouteHandler_getAContextExpr
|
||||
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:11:3:11:6 | this |
|
||||
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:12:3:12:6 | this |
|
||||
@@ -45,15 +59,34 @@ test_RouteHandler_getAContextExpr
|
||||
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:25:3:25:5 | ctx |
|
||||
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:26:3:26:5 | ctx |
|
||||
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:27:3:27:5 | ctx |
|
||||
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:31:2:31:4 | ctx |
|
||||
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:32:2:32:4 | ctx |
|
||||
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:33:2:33:4 | ctx |
|
||||
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:34:2:34:4 | ctx |
|
||||
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:35:2:35:4 | ctx |
|
||||
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:36:2:36:4 | ctx |
|
||||
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:37:2:37:4 | ctx |
|
||||
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:38:2:38:4 | ctx |
|
||||
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:39:2:39:4 | ctx |
|
||||
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:40:2:40:4 | ctx |
|
||||
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:42:12:42:14 | ctx |
|
||||
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:43:2:43:4 | ctx |
|
||||
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:44:2:44:4 | ctx |
|
||||
| src/koa.js:47:10:56:1 | async c ... .foo;\\n} | src/koa.js:48:16:48:18 | ctx |
|
||||
| src/koa.js:47:10:56:1 | async c ... .foo;\\n} | src/koa.js:51:14:51:16 | ctx |
|
||||
| src/koa.js:47:10:56:1 | async c ... .foo;\\n} | src/koa.js:54:16:54:18 | ctx |
|
||||
test_HeaderDefinition
|
||||
| src/koa.js:11:3:11:25 | this.se ... 1', '') | src/koa.js:10:10:28:1 | functio ... az');\\n} |
|
||||
| src/koa.js:12:3:12:37 | this.re ... 2', '') | src/koa.js:10:10:28:1 | functio ... az');\\n} |
|
||||
| src/koa.js:13:3:13:24 | ctx.set ... 3', '') | src/koa.js:10:10:28:1 | functio ... az');\\n} |
|
||||
| src/koa.js:14:3:14:36 | ctx.res ... 4', '') | src/koa.js:10:10:28:1 | functio ... az');\\n} |
|
||||
| src/koa.js:16:3:16:27 | rsp.hea ... 5', '') | src/koa.js:10:10:28:1 | functio ... az');\\n} |
|
||||
| src/koa.js:39:2:39:15 | ctx.set('bar') | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
test_RouteSetup_getServer
|
||||
| src/koa.js:8:1:8:18 | app2.use(handler1) | src/koa.js:5:12:5:20 | new Koa() |
|
||||
| src/koa.js:10:1:28:2 | app2.us ... z');\\n}) | src/koa.js:5:12:5:20 | new Koa() |
|
||||
| src/koa.js:30:1:45:2 | app2.us ... rl);\\n}) | src/koa.js:5:12:5:20 | new Koa() |
|
||||
| src/koa.js:47:1:56:2 | app2.us ... foo;\\n}) | src/koa.js:5:12:5:20 | new Koa() |
|
||||
test_HeaderDefinition_getAHeaderName
|
||||
| src/koa.js:11:3:11:25 | this.se ... 1', '') | header1 |
|
||||
| src/koa.js:12:3:12:37 | this.re ... 2', '') | header2 |
|
||||
@@ -64,23 +97,33 @@ test_HeaderAccess
|
||||
| src/koa.js:24:3:24:24 | ctx.req ... der.bar | bar |
|
||||
| src/koa.js:25:3:25:25 | ctx.req ... ers.bar | bar |
|
||||
| src/koa.js:26:3:26:24 | ctx.req ... ('bar') | bar |
|
||||
| src/koa.js:37:2:37:15 | ctx.header.bar | bar |
|
||||
| src/koa.js:38:2:38:16 | ctx.headers.bar | bar |
|
||||
| src/koa.js:40:2:40:15 | ctx.get('bar') | bar |
|
||||
| src/koa.js:55:2:55:12 | headers.foo | foo |
|
||||
test_RouteHandler_getAResponseExpr
|
||||
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:12:3:12:15 | this.response |
|
||||
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:14:3:14:14 | ctx.response |
|
||||
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:15:13:15:24 | ctx.response |
|
||||
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:16:3:16:5 | rsp |
|
||||
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:18:3:18:14 | ctx.response |
|
||||
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:44:2:44:13 | ctx.response |
|
||||
test_ResponseSendArgument
|
||||
| src/koa.js:18:23:18:23 | x | src/koa.js:10:10:28:1 | functio ... az');\\n} |
|
||||
| src/koa.js:31:13:31:13 | x | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
test_RouteSetup_getARouteHandler
|
||||
| src/koa.js:8:1:8:18 | app2.use(handler1) | src/koa.js:7:1:7:22 | functio ... r1() {} |
|
||||
| src/koa.js:10:1:28:2 | app2.us ... z');\\n}) | src/koa.js:10:10:28:1 | functio ... az');\\n} |
|
||||
| src/koa.js:30:1:45:2 | app2.us ... rl);\\n}) | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
| src/koa.js:47:1:56:2 | app2.us ... foo;\\n}) | src/koa.js:47:10:56:1 | async c ... .foo;\\n} |
|
||||
test_AppDefinition
|
||||
| src/koa.js:2:12:2:33 | new (re ... oa'))() |
|
||||
| src/koa.js:5:12:5:20 | new Koa() |
|
||||
test_RouteHandler
|
||||
| src/koa.js:7:1:7:22 | functio ... r1() {} | src/koa.js:5:12:5:20 | new Koa() |
|
||||
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:5:12:5:20 | new Koa() |
|
||||
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:5:12:5:20 | new Koa() |
|
||||
| src/koa.js:47:10:56:1 | async c ... .foo;\\n} | src/koa.js:5:12:5:20 | new Koa() |
|
||||
test_RequestExpr
|
||||
| src/koa.js:19:3:19:13 | ctx.request | src/koa.js:10:10:28:1 | functio ... az');\\n} |
|
||||
| src/koa.js:20:3:20:13 | ctx.request | src/koa.js:10:10:28:1 | functio ... az');\\n} |
|
||||
@@ -115,3 +158,22 @@ test_ContextExpr
|
||||
| src/koa.js:25:3:25:5 | ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} |
|
||||
| src/koa.js:26:3:26:5 | ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} |
|
||||
| src/koa.js:27:3:27:5 | ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} |
|
||||
| src/koa.js:31:2:31:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
| src/koa.js:32:2:32:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
| src/koa.js:33:2:33:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
| src/koa.js:34:2:34:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
| src/koa.js:35:2:35:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
| src/koa.js:36:2:36:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
| src/koa.js:37:2:37:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
| src/koa.js:38:2:38:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
| src/koa.js:39:2:39:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
| src/koa.js:40:2:40:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
| src/koa.js:42:12:42:14 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
| src/koa.js:43:2:43:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
| src/koa.js:44:2:44:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
| src/koa.js:48:16:48:18 | ctx | src/koa.js:47:10:56:1 | async c ... .foo;\\n} |
|
||||
| src/koa.js:51:14:51:16 | ctx | src/koa.js:47:10:56:1 | async c ... .foo;\\n} |
|
||||
| src/koa.js:54:16:54:18 | ctx | src/koa.js:47:10:56:1 | async c ... .foo;\\n} |
|
||||
test_RedirectInvocation
|
||||
| src/koa.js:43:2:43:18 | ctx.redirect(url) | src/koa.js:43:15:43:17 | url | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
| src/koa.js:44:2:44:27 | ctx.res ... ct(url) | src/koa.js:44:24:44:26 | url | src/koa.js:30:10:45:1 | async c ... url);\\n} |
|
||||
|
||||
@@ -16,3 +16,4 @@ import RouteHandler
|
||||
import RequestExpr
|
||||
import RouteHandler_getARequestExpr
|
||||
import ContextExpr
|
||||
import RedirectInvocation
|
||||
@@ -22,6 +22,12 @@ nodes
|
||||
| express.js:135:23:135:37 | req.params.user |
|
||||
| express.js:136:16:136:36 | 'u' + r ... ms.user |
|
||||
| express.js:136:22:136:36 | req.params.user |
|
||||
| koa.js:6:6:6:27 | url |
|
||||
| koa.js:6:12:6:27 | ctx.query.target |
|
||||
| koa.js:7:15:7:17 | url |
|
||||
| koa.js:8:15:8:26 | `${url}${x}` |
|
||||
| koa.js:8:18:8:20 | url |
|
||||
| koa.js:14:16:14:18 | url |
|
||||
| node.js:6:7:6:52 | target |
|
||||
| node.js:6:16:6:39 | url.par ... , true) |
|
||||
| node.js:6:16:6:45 | url.par ... ).query |
|
||||
@@ -60,6 +66,24 @@ edges
|
||||
| express.js:134:22:134:36 | req.params.user | express.js:134:16:134:36 | '/' + r ... ms.user |
|
||||
| express.js:135:23:135:37 | req.params.user | express.js:135:16:135:37 | '//' + ... ms.user |
|
||||
| express.js:136:22:136:36 | req.params.user | express.js:136:16:136:36 | 'u' + r ... ms.user |
|
||||
| koa.js:6:6:6:27 | url | koa.js:7:15:7:17 | url |
|
||||
| koa.js:6:6:6:27 | url | koa.js:8:18:8:20 | url |
|
||||
| koa.js:6:6:6:27 | url | koa.js:10:40:10:42 | url |
|
||||
| koa.js:6:6:6:27 | url | koa.js:10:40:10:42 | url |
|
||||
| koa.js:6:6:6:27 | url | koa.js:10:51:10:51 | url |
|
||||
| koa.js:6:6:6:27 | url | koa.js:11:6:11:8 | url |
|
||||
| koa.js:6:6:6:27 | url | koa.js:14:16:14:18 | url |
|
||||
| koa.js:6:12:6:27 | ctx.query.target | koa.js:6:6:6:27 | url |
|
||||
| koa.js:8:18:8:20 | url | koa.js:8:15:8:26 | `${url}${x}` |
|
||||
| koa.js:10:40:10:42 | url | koa.js:10:51:10:51 | url |
|
||||
| koa.js:10:40:10:42 | url | koa.js:10:51:10:51 | url |
|
||||
| koa.js:10:40:10:42 | url | koa.js:11:6:11:8 | url |
|
||||
| koa.js:10:40:10:42 | url | koa.js:11:6:11:8 | url |
|
||||
| koa.js:10:40:10:42 | url | koa.js:14:16:14:18 | url |
|
||||
| koa.js:10:40:10:42 | url | koa.js:14:16:14:18 | url |
|
||||
| koa.js:10:51:10:51 | url | koa.js:11:6:11:8 | url |
|
||||
| koa.js:10:51:10:51 | url | koa.js:14:16:14:18 | url |
|
||||
| koa.js:11:6:11:8 | url | koa.js:14:16:14:18 | url |
|
||||
| node.js:6:7:6:52 | target | node.js:7:34:7:39 | target |
|
||||
| node.js:6:16:6:39 | url.par ... , true) | node.js:6:16:6:45 | url.par ... ).query |
|
||||
| node.js:6:16:6:45 | url.par ... ).query | node.js:6:16:6:52 | url.par ... .target |
|
||||
@@ -95,6 +119,9 @@ edges
|
||||
| express.js:134:16:134:36 | '/' + r ... ms.user | express.js:134:22:134:36 | req.params.user | express.js:134:16:134:36 | '/' + r ... ms.user | Untrusted URL redirection due to $@. | express.js:134:22:134:36 | req.params.user | user-provided value |
|
||||
| express.js:135:16:135:37 | '//' + ... ms.user | express.js:135:23:135:37 | req.params.user | express.js:135:16:135:37 | '//' + ... ms.user | Untrusted URL redirection due to $@. | express.js:135:23:135:37 | req.params.user | user-provided value |
|
||||
| express.js:136:16:136:36 | 'u' + r ... ms.user | express.js:136:22:136:36 | req.params.user | express.js:136:16:136:36 | 'u' + r ... ms.user | Untrusted URL redirection due to $@. | express.js:136:22:136:36 | req.params.user | user-provided value |
|
||||
| koa.js:7:15:7:17 | url | koa.js:6:12:6:27 | ctx.query.target | koa.js:7:15:7:17 | url | Untrusted URL redirection due to $@. | koa.js:6:12:6:27 | ctx.query.target | user-provided value |
|
||||
| koa.js:8:15:8:26 | `${url}${x}` | koa.js:6:12:6:27 | ctx.query.target | koa.js:8:15:8:26 | `${url}${x}` | Untrusted URL redirection due to $@. | koa.js:6:12:6:27 | ctx.query.target | user-provided value |
|
||||
| koa.js:14:16:14:18 | url | koa.js:6:12:6:27 | ctx.query.target | koa.js:14:16:14:18 | url | Untrusted URL redirection due to $@. | koa.js:6:12:6:27 | ctx.query.target | user-provided value |
|
||||
| node.js:7:34:7:39 | target | node.js:6:26:6:32 | req.url | node.js:7:34:7:39 | target | Untrusted URL redirection due to $@. | node.js:6:26:6:32 | req.url | user-provided value |
|
||||
| node.js:15:34:15:45 | '/' + target | node.js:11:26:11:32 | req.url | node.js:15:34:15:45 | '/' + target | Untrusted URL redirection due to $@. | node.js:11:26:11:32 | req.url | user-provided value |
|
||||
| node.js:32:34:32:55 | target ... =" + me | node.js:29:26:29:32 | req.url | node.js:32:34:32:55 | target ... =" + me | Untrusted URL redirection due to $@. | node.js:29:26:29:32 | req.url | user-provided value |
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
const Koa = require('koa');
|
||||
const url = require('url');
|
||||
const app = new Koa();
|
||||
|
||||
app.use(async ctx => {
|
||||
var url = ctx.query.target;
|
||||
ctx.redirect(url); // NOT OK
|
||||
ctx.redirect(`${url}${x}`); // NOT OK
|
||||
|
||||
var isCrossDomainRedirect = url.parse(url || '', false, true).hostname;
|
||||
if(!url || isCrossDomainRedirect) {
|
||||
ctx.redirect('/'); // OK
|
||||
} else {
|
||||
ctx.redirect(url); // NOT OK
|
||||
}
|
||||
|
||||
if(!url || isCrossDomainRedirect || ! url.match(VALID)) {
|
||||
ctx.redirect('/'); // OK
|
||||
} else {
|
||||
ctx.redirect(url); // OK
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
@@ -130,7 +130,7 @@ class CheckClass extends ClassObject {
|
||||
|
||||
|
||||
private Object object_getattribute() {
|
||||
py_cmembers_versioned(theObjectType(), "__getattribute__", result, major_version().toString())
|
||||
result.asBuiltin() = theObjectType().asBuiltin().getMember("__getattribute__")
|
||||
}
|
||||
|
||||
private predicate auto_name(string name) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import python
|
||||
|
||||
private import semmle.python.types.Builtins
|
||||
|
||||
/** An alias in an import statement, the `mod as name` part of `import mod as name`. May be artificial;
|
||||
`import x` is transformed into `import x as x` */
|
||||
@@ -14,7 +14,7 @@ class Alias extends Alias_ {
|
||||
private predicate valid_module_name(string name) {
|
||||
exists(Module m | m.getName() = name)
|
||||
or
|
||||
exists(Object cmod | py_cobjecttypes(cmod, theModuleType()) and py_cobjectnames(cmod, name))
|
||||
exists(Builtin cmod | cmod.getClass() = theModuleType().asBuiltin() and cmod.getName() = name)
|
||||
}
|
||||
|
||||
/** An artificial expression representing an import */
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
import python
|
||||
import semmle.python.dataflow.SsaDefinitions
|
||||
private import semmle.python.types.Builtins
|
||||
|
||||
module BasePointsTo {
|
||||
/** INTERNAL -- Use n.refersTo(value, _, origin) instead */
|
||||
@@ -49,7 +50,7 @@ ClassObject simple_types(Object obj) {
|
||||
or
|
||||
obj.getOrigin() instanceof Module and result = theModuleType()
|
||||
or
|
||||
result = builtin_object_type(obj)
|
||||
result.asBuiltin() = obj.asBuiltin().getClass()
|
||||
or
|
||||
obj = unknownValue() and result = theUnknownType()
|
||||
}
|
||||
@@ -126,34 +127,6 @@ predicate baseless_is_new_style(ClassObject cls) {
|
||||
* analysis.
|
||||
*/
|
||||
|
||||
/** Gets the base class of built-in class `cls` */
|
||||
pragma [noinline]
|
||||
ClassObject builtin_base_type(ClassObject cls) {
|
||||
/* The extractor uses the special name ".super." to indicate the super class of a builtin class */
|
||||
py_cmembers_versioned(cls, ".super.", result, _)
|
||||
}
|
||||
|
||||
/** Gets the `name`d attribute of built-in class `cls` */
|
||||
pragma [noinline]
|
||||
Object builtin_class_attribute(ClassObject cls, string name) {
|
||||
not name = ".super." and
|
||||
py_cmembers_versioned(cls, name, result, _)
|
||||
}
|
||||
|
||||
/** Holds if the `name`d attribute of built-in module `m` is `value` of `cls` */
|
||||
pragma [noinline]
|
||||
predicate builtin_module_attribute(ModuleObject m, string name, Object value, ClassObject cls) {
|
||||
py_cmembers_versioned(m, name, value, _) and cls = builtin_object_type(value)
|
||||
}
|
||||
|
||||
/** Gets the (built-in) class of the built-in object `obj` */
|
||||
pragma [noinline]
|
||||
ClassObject builtin_object_type(Object obj) {
|
||||
py_cobjecttypes(obj, result) and not obj = unknownValue()
|
||||
or
|
||||
obj = unknownValue() and result = theUnknownType()
|
||||
}
|
||||
|
||||
/** Holds if this class (not on a super-class) declares name */
|
||||
pragma [noinline]
|
||||
predicate class_declares_attribute(ClassObject cls, string name) {
|
||||
@@ -162,11 +135,11 @@ predicate class_declares_attribute(ClassObject cls, string name) {
|
||||
class_defines_name(defn, name)
|
||||
)
|
||||
or
|
||||
exists(Object o |
|
||||
o = builtin_class_attribute(cls, name) and
|
||||
not exists(ClassObject sup |
|
||||
sup = builtin_base_type(cls) and
|
||||
o = builtin_class_attribute(sup, name)
|
||||
exists(Builtin o |
|
||||
o = cls.asBuiltin().getMember(name) and
|
||||
not exists(Builtin sup |
|
||||
sup = cls.asBuiltin().getBaseClass() and
|
||||
o = sup.getMember(name)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -558,11 +531,11 @@ Object undefinedVariable() {
|
||||
|
||||
/** Gets the pseudo-object representing an unknown value */
|
||||
Object unknownValue() {
|
||||
py_special_objects(result, "_1")
|
||||
result.asBuiltin() = Builtin::unknown()
|
||||
}
|
||||
|
||||
BuiltinCallable theTypeNewMethod() {
|
||||
py_cmembers_versioned(theTypeType(), "__new__", result, major_version().toString())
|
||||
result.asBuiltin() = theTypeType().asBuiltin().getMember("__new__")
|
||||
}
|
||||
|
||||
/** Gets the `value, cls, origin` that `f` would refer to if it has not been assigned some other value */
|
||||
@@ -578,7 +551,7 @@ predicate potential_builtin_points_to(NameNode f, Object value, ClassObject cls,
|
||||
|
||||
pragma [noinline]
|
||||
predicate builtin_name_points_to(string name, Object value, ClassObject cls) {
|
||||
value = Object::builtin(name) and py_cobjecttypes(value, cls)
|
||||
value = Object::builtin(name) and cls.asBuiltin() = value.asBuiltin().getClass()
|
||||
}
|
||||
|
||||
module BaseFlow {
|
||||
|
||||
@@ -27,6 +27,7 @@ import python
|
||||
private import PointsToContext
|
||||
private import Base
|
||||
private import semmle.python.types.Extensions
|
||||
private import semmle.python.types.Builtins
|
||||
private import Filters as BaseFilters
|
||||
import semmle.dataflow.SSA
|
||||
private import MRO
|
||||
@@ -311,7 +312,7 @@ module PointsTo {
|
||||
exists(SubscriptNode sub, Object sys_modules |
|
||||
sub.getValue() = sys_modules_flow and
|
||||
points_to(sys_modules_flow, _, sys_modules, _, _) and
|
||||
builtin_module_attribute(theSysModuleObject(), "modules", sys_modules, _) and
|
||||
sys_modules.asBuiltin() = Builtin::special("sys").getMember("modules") and
|
||||
sub.getIndex() = n and
|
||||
n.getNode().(StrConst).getText() = name and
|
||||
sub.(DefinitionNode).getValue() = mod and
|
||||
@@ -435,7 +436,7 @@ module PointsTo {
|
||||
}
|
||||
|
||||
private boolean module_exports_boolean(ModuleObject mod, string name) {
|
||||
py_cmembers_versioned(mod, name, _, major_version().toString()) and
|
||||
exists(mod.asBuiltin().getMember(name)) and
|
||||
name.charAt(0) != "_" and result = true
|
||||
or
|
||||
result = package_exports_boolean(mod, name)
|
||||
@@ -494,7 +495,8 @@ module PointsTo {
|
||||
or
|
||||
package_attribute_points_to(mod, name, value, cls, origin)
|
||||
or
|
||||
builtin_module_attribute(mod, name, value, cls) and origin = CfgOrigin::unknown()
|
||||
value.asBuiltin() = mod.asBuiltin().getMember(name) and
|
||||
cls.asBuiltin() = value.asBuiltin().getClass() and origin = CfgOrigin::unknown()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2458,7 +2460,7 @@ module PointsTo {
|
||||
is_new_style(cls) and not exists(cls_expr.getBase(0)) and result = theObjectType() and n = 0
|
||||
)
|
||||
or
|
||||
result = builtin_base_type(cls) and n = 0
|
||||
result.asBuiltin() = cls.asBuiltin().getBaseClass() and n = 0
|
||||
or
|
||||
cls = theUnknownType() and result = theObjectType() and n = 0
|
||||
}
|
||||
@@ -2482,7 +2484,7 @@ module PointsTo {
|
||||
or
|
||||
cls = theObjectType() and result = 0
|
||||
or
|
||||
exists(builtin_base_type(cls)) and cls != theObjectType() and result = 1
|
||||
exists(cls.asBuiltin().getBaseClass()) and cls != theObjectType() and result = 1
|
||||
or
|
||||
cls = theUnknownType() and result = 1
|
||||
}
|
||||
@@ -2646,8 +2648,8 @@ module PointsTo {
|
||||
ssa_variable_points_to(var, _, value, vcls, origin)
|
||||
)
|
||||
or
|
||||
value = builtin_class_attribute(owner, name) and class_declares_attribute(owner, name) and
|
||||
origin = CfgOrigin::unknown() and vcls = builtin_object_type(value)
|
||||
value.asBuiltin() = owner.asBuiltin().getMember(name) and class_declares_attribute(owner, name) and
|
||||
origin = CfgOrigin::unknown() and vcls.asBuiltin() = value.asBuiltin().getClass()
|
||||
}
|
||||
|
||||
private predicate interesting_class_attribute(ClassList mro, string name) {
|
||||
@@ -2754,7 +2756,11 @@ module PointsTo {
|
||||
obj = unknownValue() and result = theUnknownType()
|
||||
)
|
||||
or
|
||||
py_cobjecttypes(cls, result) and is_c_metaclass(result)
|
||||
exists(Builtin meta |
|
||||
result.asBuiltin() = meta and
|
||||
meta = cls.asBuiltin().getClass() and
|
||||
meta.inheritsFromType()
|
||||
)
|
||||
or
|
||||
exists(ControlFlowNode meta |
|
||||
Types::six_add_metaclass(_, cls, meta) and
|
||||
@@ -2777,7 +2783,7 @@ module PointsTo {
|
||||
}
|
||||
|
||||
private boolean has_declared_metaclass(ClassObject cls) {
|
||||
py_cobjecttypes(cls, _) and result = true
|
||||
exists(cls.asBuiltin().getClass()) and result = true
|
||||
or
|
||||
result = has_six_add_metaclass(cls).booleanOr(has_metaclass_var_metaclass(cls))
|
||||
}
|
||||
|
||||
110
python/ql/src/semmle/python/types/Builtins.qll
Normal file
110
python/ql/src/semmle/python/types/Builtins.qll
Normal file
@@ -0,0 +1,110 @@
|
||||
import python
|
||||
|
||||
class Builtin extends @py_cobject {
|
||||
|
||||
Builtin() {
|
||||
not (
|
||||
/* @py_cobjects for modules which have a corresponding Python module */
|
||||
exists(@py_cobject mod_type | py_special_objects(mod_type, "ModuleType") and py_cobjecttypes(this, mod_type)) and
|
||||
exists(Module m | py_cobjectnames(this, m.getName()))
|
||||
)
|
||||
and (
|
||||
/* Exclude unmatched builtin objects in the library trap files */
|
||||
py_cobjectnames(this, _) or
|
||||
py_cobjecttypes(this, _) or
|
||||
py_special_objects(this, _)
|
||||
)
|
||||
}
|
||||
|
||||
string toString() {
|
||||
not this = undefinedVariable().asBuiltin() and not this = Builtin::unknown() and
|
||||
exists(Builtin type, string typename, string objname |
|
||||
py_cobjecttypes(this, type) and py_cobjectnames(this, objname) and typename = type.getName() |
|
||||
result = typename + " " + objname
|
||||
)
|
||||
}
|
||||
|
||||
Builtin getClass() {
|
||||
py_cobjecttypes(this, result) and not this = Builtin::unknown()
|
||||
or
|
||||
this = Builtin::unknown() and result = Builtin::unknownType()
|
||||
}
|
||||
|
||||
Builtin getMember(string name) {
|
||||
not name = ".super." and
|
||||
py_cmembers_versioned(this, name, result, major_version().toString())
|
||||
}
|
||||
|
||||
Builtin getItem(int index) {
|
||||
py_citems(this, index, result)
|
||||
}
|
||||
|
||||
Builtin getBaseClass() {
|
||||
/* The extractor uses the special name ".super." to indicate the super class of a builtin class */
|
||||
py_cmembers_versioned(this, ".super.", result, major_version().toString())
|
||||
}
|
||||
|
||||
predicate inheritsFromType() {
|
||||
this = Builtin::special("type")
|
||||
or
|
||||
this.getBaseClass().inheritsFromType()
|
||||
}
|
||||
|
||||
string getName() {
|
||||
py_cobjectnames(this, result)
|
||||
}
|
||||
|
||||
predicate isClass() {
|
||||
py_cobjecttypes(_, this) or
|
||||
this = Builtin::unknownType() or
|
||||
exists(Builtin meta | meta.inheritsFromType() and py_cobjecttypes(this, meta))
|
||||
}
|
||||
|
||||
predicate isFunction() {
|
||||
this.getClass() = Builtin::special("BuiltinFunctionType") and
|
||||
exists(Builtin mod |
|
||||
mod.isModule() and
|
||||
mod.getMember(_) = this
|
||||
)
|
||||
}
|
||||
|
||||
predicate isModule() {
|
||||
this.getClass() = Builtin::special("ModuleType")
|
||||
}
|
||||
|
||||
predicate isMethod() {
|
||||
this.getClass() = Builtin::special("MethodDescriptorType")
|
||||
or
|
||||
this.getClass() = Builtin::special("BuiltinFunctionType") and
|
||||
exists(Builtin cls | cls.isClass() and cls.getMember(_) = this)
|
||||
or
|
||||
this.getClass().getName() = "wrapper_descriptor"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module Builtin {
|
||||
|
||||
Builtin builtinModule() {
|
||||
py_special_objects(result, "builtin_module_2") and major_version() = 2
|
||||
or
|
||||
py_special_objects(result, "builtin_module_3") and major_version() = 3
|
||||
}
|
||||
|
||||
Builtin builtin(string name) {
|
||||
result = builtinModule().getMember(name)
|
||||
}
|
||||
|
||||
Builtin special(string name) {
|
||||
py_special_objects(result, name)
|
||||
}
|
||||
|
||||
Builtin unknown() {
|
||||
py_special_objects(result, "_1")
|
||||
}
|
||||
|
||||
Builtin unknownType() {
|
||||
py_special_objects(result, "_semmle_unknown_type")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,12 +2,7 @@ import python
|
||||
private import semmle.python.pointsto.PointsTo
|
||||
private import semmle.python.pointsto.Base
|
||||
private import semmle.python.pointsto.MRO as MRO
|
||||
|
||||
predicate is_c_metaclass(Object o) {
|
||||
py_special_objects(o, "type")
|
||||
or
|
||||
exists(Object sup | py_cmembers_versioned(o, ".super.", sup, major_version().toString()) and is_c_metaclass(sup))
|
||||
}
|
||||
private import semmle.python.types.Builtins
|
||||
|
||||
|
||||
/** A class whose instances represents Python classes.
|
||||
@@ -27,22 +22,20 @@ class ClassObject extends Object {
|
||||
|
||||
ClassObject() {
|
||||
this.getOrigin() instanceof ClassExpr or
|
||||
py_cobjecttypes(_, this) or
|
||||
exists(Object meta | py_cobjecttypes(this, meta) and is_c_metaclass(meta)) or
|
||||
py_special_objects(this, "_semmle_unknown_type")
|
||||
this.asBuiltin().isClass()
|
||||
}
|
||||
|
||||
private predicate isStr() {
|
||||
py_special_objects(this, "bytes") and major_version() = 2
|
||||
this.asBuiltin() = Builtin::special("bytes") and major_version() = 2
|
||||
or
|
||||
py_special_objects(this, "unicode") and major_version() = 3
|
||||
this.asBuiltin() = Builtin::special("unicode") and major_version() = 3
|
||||
}
|
||||
|
||||
/** Gets the short (unqualified) name of this class */
|
||||
string getName() {
|
||||
this.isStr() and result = "str"
|
||||
or
|
||||
not this.isStr() and py_cobjectnames(this, result) and not this = theUnknownType()
|
||||
not this.isStr() and result = this.asBuiltin().getName() and not this = theUnknownType()
|
||||
or
|
||||
result = this.getPyClass().getName()
|
||||
}
|
||||
@@ -51,7 +44,7 @@ class ClassObject extends Object {
|
||||
* Should return the same name as the `__qualname__` attribute on classes in Python 3.
|
||||
*/
|
||||
string getQualifiedName() {
|
||||
py_cobjectnames(this, _) and result = this.getName()
|
||||
this.isBuiltin() and result = this.getName()
|
||||
or
|
||||
result = this.getPyClass().getQualifiedName()
|
||||
}
|
||||
@@ -70,8 +63,7 @@ class ClassObject extends Object {
|
||||
predicate hasABase() {
|
||||
exists(ClassExpr cls | this.getOrigin() = cls | exists(cls.getABase()))
|
||||
or
|
||||
/* The extractor uses the special name ".super." to indicate the super class of a builtin class */
|
||||
py_cmembers_versioned(this, ".super.", _, major_version().toString())
|
||||
exists(this.asBuiltin().getBaseClass())
|
||||
}
|
||||
|
||||
/** Gets a super class of this class (includes transitive super classes) */
|
||||
@@ -418,79 +410,79 @@ ClassObject theAbcMetaClassObject() {
|
||||
|
||||
/** The built-in class NoneType*/
|
||||
ClassObject theNoneType() {
|
||||
py_special_objects(result, "NoneType")
|
||||
result.asBuiltin() = Builtin::special("NoneType")
|
||||
}
|
||||
|
||||
/** The built-in class 'bool' */
|
||||
ClassObject theBoolType() {
|
||||
py_special_objects(result, "bool")
|
||||
result.asBuiltin() = Builtin::special("bool")
|
||||
}
|
||||
|
||||
/** The builtin class 'type' */
|
||||
ClassObject theTypeType() {
|
||||
py_special_objects(result, "type")
|
||||
result.asBuiltin() = Builtin::special("type")
|
||||
}
|
||||
|
||||
/** The builtin object ClassType (for old-style classes) */
|
||||
ClassObject theClassType() {
|
||||
py_special_objects(result, "ClassType")
|
||||
result.asBuiltin() = Builtin::special("ClassType")
|
||||
}
|
||||
|
||||
/** The builtin object InstanceType (for old-style classes) */
|
||||
ClassObject theInstanceType() {
|
||||
py_special_objects(result, "InstanceType")
|
||||
result.asBuiltin() = Builtin::special("InstanceType")
|
||||
}
|
||||
|
||||
/** The builtin class 'tuple' */
|
||||
ClassObject theTupleType() {
|
||||
py_special_objects(result, "tuple")
|
||||
result.asBuiltin() = Builtin::special("tuple")
|
||||
}
|
||||
|
||||
/** The builtin class 'int' */
|
||||
ClassObject theIntType() {
|
||||
py_special_objects(result, "int")
|
||||
result.asBuiltin() = Builtin::special("int")
|
||||
}
|
||||
|
||||
/** The builtin class 'long' (Python 2 only) */
|
||||
ClassObject theLongType() {
|
||||
py_special_objects(result, "long")
|
||||
result.asBuiltin() = Builtin::special("long")
|
||||
}
|
||||
|
||||
/** The builtin class 'float' */
|
||||
ClassObject theFloatType() {
|
||||
py_special_objects(result, "float")
|
||||
result.asBuiltin() = Builtin::special("float")
|
||||
}
|
||||
|
||||
/** The builtin class 'complex' */
|
||||
ClassObject theComplexType() {
|
||||
py_special_objects(result, "complex")
|
||||
result.asBuiltin() = Builtin::special("complex")
|
||||
}
|
||||
|
||||
/** The builtin class 'object' */
|
||||
ClassObject theObjectType() {
|
||||
py_special_objects(result, "object")
|
||||
result.asBuiltin() = Builtin::special("object")
|
||||
}
|
||||
|
||||
/** The builtin class 'list' */
|
||||
ClassObject theListType() {
|
||||
py_special_objects(result, "list")
|
||||
result.asBuiltin() = Builtin::special("list")
|
||||
}
|
||||
|
||||
/** The builtin class 'dict' */
|
||||
|
||||
ClassObject theDictType() {
|
||||
py_special_objects(result, "dict")
|
||||
result.asBuiltin() = Builtin::special("dict")
|
||||
}
|
||||
|
||||
/** The builtin class 'Exception' */
|
||||
|
||||
ClassObject theExceptionType() {
|
||||
py_special_objects(result, "Exception")
|
||||
result.asBuiltin() = Builtin::special("Exception")
|
||||
}
|
||||
|
||||
/** The builtin class for unicode. unicode in Python2, str in Python3 */
|
||||
ClassObject theUnicodeType() {
|
||||
py_special_objects(result, "unicode")
|
||||
result.asBuiltin() = Builtin::special("unicode")
|
||||
}
|
||||
|
||||
/** The builtin class '(x)range' */
|
||||
@@ -502,83 +494,83 @@ ClassObject theRangeType() {
|
||||
|
||||
/** The builtin class for bytes. str in Python2, bytes in Python3 */
|
||||
ClassObject theBytesType() {
|
||||
py_special_objects(result, "bytes")
|
||||
result.asBuiltin() = Builtin::special("bytes")
|
||||
}
|
||||
|
||||
/** The builtin class 'set' */
|
||||
ClassObject theSetType() {
|
||||
py_special_objects(result, "set")
|
||||
result.asBuiltin() = Builtin::special("set")
|
||||
}
|
||||
|
||||
/** The builtin class 'property' */
|
||||
ClassObject thePropertyType() {
|
||||
py_special_objects(result, "property")
|
||||
result.asBuiltin() = Builtin::special("property")
|
||||
}
|
||||
|
||||
/** The builtin class 'BaseException' */
|
||||
ClassObject theBaseExceptionType() {
|
||||
py_special_objects(result, "BaseException")
|
||||
result.asBuiltin() = Builtin::special("BaseException")
|
||||
}
|
||||
|
||||
/** The class of builtin-functions */
|
||||
ClassObject theBuiltinFunctionType() {
|
||||
py_special_objects(result, "BuiltinFunctionType")
|
||||
result.asBuiltin() = Builtin::special("BuiltinFunctionType")
|
||||
}
|
||||
|
||||
/** The class of Python functions */
|
||||
ClassObject thePyFunctionType() {
|
||||
py_special_objects(result, "FunctionType")
|
||||
result.asBuiltin() = Builtin::special("FunctionType")
|
||||
}
|
||||
|
||||
/** The builtin class 'classmethod' */
|
||||
ClassObject theClassMethodType() {
|
||||
py_special_objects(result, "ClassMethod")
|
||||
result.asBuiltin() = Builtin::special("ClassMethod")
|
||||
}
|
||||
|
||||
/** The builtin class 'staticmethod' */
|
||||
ClassObject theStaticMethodType() {
|
||||
py_special_objects(result, "StaticMethod")
|
||||
result.asBuiltin() = Builtin::special("StaticMethod")
|
||||
}
|
||||
|
||||
/** The class of modules */
|
||||
ClassObject theModuleType() {
|
||||
py_special_objects(result, "ModuleType")
|
||||
result.asBuiltin() = Builtin::special("ModuleType")
|
||||
}
|
||||
|
||||
/** The class of generators */
|
||||
ClassObject theGeneratorType() {
|
||||
py_special_objects(result, "generator")
|
||||
result.asBuiltin() = Builtin::special("generator")
|
||||
}
|
||||
|
||||
/** The builtin class 'TypeError' */
|
||||
ClassObject theTypeErrorType() {
|
||||
py_special_objects(result, "TypeError")
|
||||
result.asBuiltin() = Builtin::special("TypeError")
|
||||
}
|
||||
|
||||
/** The builtin class 'AttributeError' */
|
||||
ClassObject theAttributeErrorType() {
|
||||
py_special_objects(result, "AttributeError")
|
||||
result.asBuiltin() = Builtin::special("AttributeError")
|
||||
}
|
||||
|
||||
/** The builtin class 'KeyError' */
|
||||
ClassObject theKeyErrorType() {
|
||||
py_special_objects(result, "KeyError")
|
||||
result.asBuiltin() = Builtin::special("KeyError")
|
||||
}
|
||||
|
||||
/** The builtin class of bound methods */
|
||||
pragma [noinline]
|
||||
ClassObject theBoundMethodType() {
|
||||
py_special_objects(result, "MethodType")
|
||||
result.asBuiltin() = Builtin::special("MethodType")
|
||||
}
|
||||
|
||||
/** The builtin class of builtin properties */
|
||||
ClassObject theGetSetDescriptorType() {
|
||||
py_special_objects(result, "GetSetDescriptorType")
|
||||
result.asBuiltin() = Builtin::special("GetSetDescriptorType")
|
||||
}
|
||||
|
||||
/** The method descriptor class */
|
||||
ClassObject theMethodDescriptorType() {
|
||||
py_special_objects(result, "MethodDescriptorType")
|
||||
result.asBuiltin() = Builtin::special("MethodDescriptorType")
|
||||
}
|
||||
|
||||
/** The class of builtin properties */
|
||||
|
||||
@@ -3,6 +3,7 @@ import semmle.python.types.Exceptions
|
||||
private import semmle.python.pointsto.PointsTo
|
||||
private import semmle.python.libraries.Zope
|
||||
private import semmle.python.pointsto.Base
|
||||
private import semmle.python.types.Builtins
|
||||
|
||||
/** A function object, whether written in Python or builtin */
|
||||
abstract class FunctionObject extends Object {
|
||||
@@ -254,23 +255,21 @@ abstract class BuiltinCallable extends FunctionObject {
|
||||
class BuiltinMethodObject extends BuiltinCallable {
|
||||
|
||||
BuiltinMethodObject() {
|
||||
py_cobjecttypes(this, theMethodDescriptorType())
|
||||
this.asBuiltin().getClass() = theMethodDescriptorType().asBuiltin()
|
||||
or
|
||||
py_cobjecttypes(this, theBuiltinFunctionType()) and exists(ClassObject c | py_cmembers_versioned(c, _, this, major_version().toString()))
|
||||
this.asBuiltin().getClass() = theBuiltinFunctionType().asBuiltin() and
|
||||
exists(Builtin cls | cls.isClass() and cls.getMember(_) = this.asBuiltin())
|
||||
or
|
||||
exists(ClassObject wrapper_descriptor |
|
||||
py_cobjecttypes(this, wrapper_descriptor) and
|
||||
py_cobjectnames(wrapper_descriptor, "wrapper_descriptor")
|
||||
)
|
||||
this.asBuiltin().getClass().getName() = "wrapper_descriptor"
|
||||
}
|
||||
|
||||
override string getQualifiedName() {
|
||||
exists(ClassObject cls |
|
||||
py_cmembers_versioned(cls, _, this, major_version().toString()) |
|
||||
cls.asBuiltin().getMember(_) = this.asBuiltin() |
|
||||
result = cls.getName() + "." + this.getName()
|
||||
)
|
||||
or
|
||||
not exists(ClassObject cls | py_cmembers_versioned(cls, _, this, major_version().toString())) and
|
||||
not exists(ClassObject cls | cls.asBuiltin().getMember(_) = this.asBuiltin()) and
|
||||
result = this.getName()
|
||||
}
|
||||
|
||||
@@ -279,7 +278,7 @@ class BuiltinMethodObject extends BuiltinCallable {
|
||||
}
|
||||
|
||||
override string getName() {
|
||||
py_cobjectnames(this, result)
|
||||
result = this.asBuiltin().getName()
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
@@ -305,7 +304,7 @@ class BuiltinMethodObject extends BuiltinCallable {
|
||||
}
|
||||
|
||||
override ClassObject getAReturnType() {
|
||||
ext_rettype(this, result)
|
||||
ext_rettype(this.asBuiltin(), result.asBuiltin())
|
||||
}
|
||||
|
||||
}
|
||||
@@ -313,11 +312,12 @@ class BuiltinMethodObject extends BuiltinCallable {
|
||||
class BuiltinFunctionObject extends BuiltinCallable {
|
||||
|
||||
BuiltinFunctionObject() {
|
||||
py_cobjecttypes(this, theBuiltinFunctionType()) and exists(ModuleObject m | py_cmembers_versioned(m, _, this, major_version().toString()))
|
||||
this.asBuiltin().getClass() = theBuiltinFunctionType().asBuiltin() and
|
||||
exists(ModuleObject m | m.asBuiltin().getMember(_) = this.asBuiltin())
|
||||
}
|
||||
|
||||
override string getName() {
|
||||
py_cobjectnames(this, result)
|
||||
result = this.asBuiltin().getName()
|
||||
}
|
||||
|
||||
override string getQualifiedName() {
|
||||
@@ -352,7 +352,7 @@ class BuiltinFunctionObject extends BuiltinCallable {
|
||||
this = Object::builtin("intern") and result = theStrType()
|
||||
or
|
||||
/* Fix a few minor inaccuracies in the CPython analysis */
|
||||
ext_rettype(this, result) and not (
|
||||
ext_rettype(this.asBuiltin(), result.asBuiltin()) and not (
|
||||
this = Object::builtin("__import__") and result = theNoneType()
|
||||
or
|
||||
this = Object::builtin("compile") and result = theNoneType()
|
||||
|
||||
@@ -8,7 +8,7 @@ abstract class ModuleObject extends Object {
|
||||
ModuleObject () {
|
||||
exists(Module m | m.getEntryNode() = this)
|
||||
or
|
||||
py_cobjecttypes(this, theModuleType())
|
||||
this.asBuiltin().getClass() = theModuleType().asBuiltin()
|
||||
}
|
||||
|
||||
/** Gets the scope corresponding to this module, if this is a Python module */
|
||||
@@ -103,19 +103,19 @@ abstract class ModuleObject extends Object {
|
||||
class BuiltinModuleObject extends ModuleObject {
|
||||
|
||||
BuiltinModuleObject () {
|
||||
py_cobjecttypes(this, theModuleType())
|
||||
this.asBuiltin().getClass() = theModuleType().asBuiltin()
|
||||
}
|
||||
|
||||
override string getName() {
|
||||
py_cobjectnames(this, result)
|
||||
result = this.asBuiltin().getName()
|
||||
}
|
||||
|
||||
override Object getAttribute(string name) {
|
||||
py_cmembers_versioned(this, name, result, major_version().toString())
|
||||
result.asBuiltin() = this.asBuiltin().getMember(name)
|
||||
}
|
||||
|
||||
override predicate hasAttribute(string name) {
|
||||
py_cmembers_versioned(this, name, _, major_version().toString())
|
||||
exists(this.asBuiltin().getMember(name))
|
||||
}
|
||||
|
||||
override predicate attributeRefersTo(string name, Object value, ControlFlowNode origin) {
|
||||
@@ -204,9 +204,9 @@ class PythonModuleObject extends ModuleObject {
|
||||
* for each module name, with the name b'text' or u'text' (including the quotes).
|
||||
*/
|
||||
Object object_for_string(string text) {
|
||||
py_cobjecttypes(result, theStrType()) and
|
||||
result.asBuiltin().getClass() = theStrType().asBuiltin() and
|
||||
exists(string repr |
|
||||
py_cobjectnames(result, repr) and
|
||||
repr = result.asBuiltin().getName() and
|
||||
repr.charAt(1) = "'" |
|
||||
/* Strip quotes off repr */
|
||||
text = repr.substring(2, repr.length()-1)
|
||||
|
||||
@@ -1,22 +1,13 @@
|
||||
import python
|
||||
private import semmle.python.pointsto.Base
|
||||
private import semmle.python.types.Builtins
|
||||
|
||||
private cached predicate is_an_object(@py_object obj) {
|
||||
/* CFG nodes for numeric literals, all of which have a @py_cobject for the value of that literal */
|
||||
obj instanceof ControlFlowNode and
|
||||
not obj.(ControlFlowNode).getNode() instanceof ImmutableLiteral
|
||||
and
|
||||
not (
|
||||
/* @py_cobjects for modules which have a corresponding Python module */
|
||||
exists(@py_cobject mod_type | py_special_objects(mod_type, "ModuleType") and py_cobjecttypes(obj, mod_type)) and
|
||||
exists(Module m | py_cobjectnames(obj, m.getName()))
|
||||
)
|
||||
and (
|
||||
/* Exclude unmatched builtin objects in the library trap files */
|
||||
obj instanceof ControlFlowNode or
|
||||
py_cobjectnames(obj, _) or
|
||||
py_cobjecttypes(obj, _) or
|
||||
py_special_objects(obj, _)
|
||||
)
|
||||
or
|
||||
obj instanceof Builtin
|
||||
}
|
||||
|
||||
/** Instances of this class represent objects in the Python program. However, since
|
||||
@@ -46,7 +37,7 @@ class Object extends @py_object {
|
||||
ClassObject getAnInferredType() {
|
||||
exists(ControlFlowNode somewhere | somewhere.refersTo(this, result, _))
|
||||
or
|
||||
py_cobjecttypes(this, result) and not this = unknownValue()
|
||||
this.asBuiltin().getClass() = result.asBuiltin() and not this = unknownValue()
|
||||
or
|
||||
this = unknownValue() and result = theUnknownType()
|
||||
}
|
||||
@@ -54,7 +45,7 @@ class Object extends @py_object {
|
||||
/** Whether this a builtin object. A builtin object is one defined by the implementation,
|
||||
such as the integer 4 or by a native extension, such as a NumPy array class. */
|
||||
predicate isBuiltin() {
|
||||
py_cobjects(this)
|
||||
exists(this.asBuiltin())
|
||||
}
|
||||
|
||||
/** Retained for backwards compatibility. See Object.isBuiltin() */
|
||||
@@ -79,14 +70,18 @@ class Object extends @py_object {
|
||||
this.hasOrigin() and this.getOrigin().getLocation().hasLocationInfo(filepath, bl, bc, el, ec)
|
||||
or
|
||||
not this.hasOrigin() and filepath = ":Compiled Code" and bl = 0 and bc = 0 and el = 0 and ec = 0
|
||||
}
|
||||
}
|
||||
|
||||
/** INTERNAL -- Do not use */
|
||||
Builtin asBuiltin() {
|
||||
result = this
|
||||
}
|
||||
|
||||
string toString() {
|
||||
this.isC() and
|
||||
not this = undefinedVariable() and not this = unknownValue() and
|
||||
exists(ClassObject type, string typename, string objname |
|
||||
py_cobjecttypes(this, type) and py_cobjectnames(this, objname) and typename = type.getName() |
|
||||
result = typename + " " + objname
|
||||
exists(ClassObject type |
|
||||
type.asBuiltin() = this.asBuiltin().getClass() |
|
||||
result = type.getName() + " " + this.asBuiltin().getName()
|
||||
)
|
||||
or
|
||||
result = this.getOrigin().toString()
|
||||
@@ -108,7 +103,7 @@ class Object extends @py_object {
|
||||
or
|
||||
this.getOrigin() instanceof Module and result = theModuleType()
|
||||
or
|
||||
py_cobjecttypes(this, result)
|
||||
result.(Object).asBuiltin() = this.asBuiltin().getClass()
|
||||
}
|
||||
|
||||
private
|
||||
@@ -240,9 +235,9 @@ private Object findByName3(string longName) {
|
||||
class NumericObject extends Object {
|
||||
|
||||
NumericObject() {
|
||||
py_cobjecttypes(this, theIntType()) or
|
||||
py_cobjecttypes(this, theLongType()) or
|
||||
py_cobjecttypes(this, theFloatType())
|
||||
this.asBuiltin().getClass() = theIntType().asBuiltin() or
|
||||
this.asBuiltin().getClass() = theLongType().asBuiltin() or
|
||||
this.asBuiltin().getClass() = theFloatType().asBuiltin()
|
||||
}
|
||||
|
||||
/** Gets the Boolean value that this object
|
||||
@@ -261,23 +256,26 @@ class NumericObject extends Object {
|
||||
|
||||
/** Gets the value of this object if it is a constant integer and it fits in a QL int */
|
||||
int intValue() {
|
||||
(py_cobjecttypes(this, theIntType()) or py_cobjecttypes(this, theLongType()))
|
||||
(
|
||||
this.asBuiltin().getClass() = theIntType().asBuiltin() or
|
||||
this.asBuiltin().getClass() = theLongType().asBuiltin()
|
||||
)
|
||||
and
|
||||
exists(string s | py_cobjectnames(this, s) and result = s.toInt())
|
||||
result = this.asBuiltin().getName().toInt()
|
||||
}
|
||||
|
||||
/** Gets the value of this object if it is a constant float */
|
||||
float floatValue() {
|
||||
(py_cobjecttypes(this, theFloatType()))
|
||||
this.asBuiltin().getClass() = theFloatType().asBuiltin()
|
||||
and
|
||||
exists(string s | py_cobjectnames(this, s) and result = s.toFloat())
|
||||
result = this.asBuiltin().getName().toFloat()
|
||||
}
|
||||
|
||||
/** Gets the string representation of this object, equivalent to calling repr() in Python */
|
||||
string repr() {
|
||||
exists(string s |
|
||||
py_cobjectnames(this, s) |
|
||||
if py_cobjecttypes(this, theLongType()) then
|
||||
exists(string s |
|
||||
s = this.asBuiltin().getName() |
|
||||
if this.asBuiltin().getClass() = theLongType().asBuiltin() then
|
||||
result = s + "L"
|
||||
else
|
||||
result = s
|
||||
@@ -293,8 +291,8 @@ class NumericObject extends Object {
|
||||
class StringObject extends Object {
|
||||
|
||||
StringObject() {
|
||||
py_cobjecttypes(this, theUnicodeType()) or
|
||||
py_cobjecttypes(this, theBytesType())
|
||||
this.asBuiltin().getClass() = theUnicodeType().asBuiltin() or
|
||||
this.asBuiltin().getClass() = theBytesType().asBuiltin()
|
||||
}
|
||||
|
||||
/** Whether this string is composed entirely of ascii encodable characters */
|
||||
@@ -311,7 +309,8 @@ class StringObject extends Object {
|
||||
/** Gets the text for this string */
|
||||
cached string getText() {
|
||||
exists(string quoted_string |
|
||||
py_cobjectnames(this, quoted_string) and
|
||||
quoted_string = this.asBuiltin().getName()
|
||||
and
|
||||
result = quoted_string.regexpCapture("[bu]'([\\s\\S]*)'", 1)
|
||||
)
|
||||
}
|
||||
@@ -333,7 +332,7 @@ abstract class SequenceObject extends Object {
|
||||
|
||||
/** Gets the nth item of this builtin sequence */
|
||||
Object getBuiltinElement(int n) {
|
||||
py_citems(this, n, result)
|
||||
result.asBuiltin() = this.asBuiltin().getItem(n)
|
||||
}
|
||||
|
||||
/** Gets the nth source element of this sequence */
|
||||
@@ -352,7 +351,7 @@ abstract class SequenceObject extends Object {
|
||||
class TupleObject extends SequenceObject {
|
||||
|
||||
TupleObject() {
|
||||
py_cobjecttypes(this, theTupleType())
|
||||
this.asBuiltin().getClass() = theTupleType().asBuiltin()
|
||||
or
|
||||
this instanceof TupleNode
|
||||
or
|
||||
@@ -364,7 +363,11 @@ class TupleObject extends SequenceObject {
|
||||
module TupleObject {
|
||||
|
||||
TupleObject empty() {
|
||||
py_cobjecttypes(result, theTupleType()) and not py_citems(result, _, _)
|
||||
exists(Builtin empty |
|
||||
empty = result.asBuiltin() and
|
||||
empty.getClass() = theTupleType().asBuiltin() and
|
||||
not exists(empty.getItem(_))
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -385,7 +388,7 @@ class NonEmptyTupleObject extends TupleObject {
|
||||
class ListObject extends SequenceObject {
|
||||
|
||||
ListObject() {
|
||||
py_cobjecttypes(this, theListType())
|
||||
this.asBuiltin().getClass() = theListType().asBuiltin()
|
||||
or
|
||||
this instanceof ListNode
|
||||
}
|
||||
@@ -394,14 +397,12 @@ class ListObject extends SequenceObject {
|
||||
|
||||
/** The `builtin` module */
|
||||
BuiltinModuleObject theBuiltinModuleObject() {
|
||||
py_special_objects(result, "builtin_module_2") and major_version() = 2
|
||||
or
|
||||
py_special_objects(result, "builtin_module_3") and major_version() = 3
|
||||
result.asBuiltin() = Builtin::builtinModule()
|
||||
}
|
||||
|
||||
/** The `sys` module */
|
||||
BuiltinModuleObject theSysModuleObject() {
|
||||
py_special_objects(result, "sys")
|
||||
result.asBuiltin() = Builtin::special("sys")
|
||||
}
|
||||
|
||||
/** DEPRECATED -- Use `Object::builtin(name)` instead. */
|
||||
@@ -412,17 +413,17 @@ Object builtin_object(string name) {
|
||||
|
||||
/** The built-in object None */
|
||||
Object theNoneObject() {
|
||||
py_special_objects(result, "None")
|
||||
result.asBuiltin() = Builtin::special("None")
|
||||
}
|
||||
|
||||
/** The built-in object True */
|
||||
Object theTrueObject() {
|
||||
py_special_objects(result, "True")
|
||||
result.asBuiltin() = Builtin::special("True")
|
||||
}
|
||||
|
||||
/** The built-in object False */
|
||||
Object theFalseObject() {
|
||||
py_special_objects(result, "False")
|
||||
result.asBuiltin() = Builtin::special("False")
|
||||
}
|
||||
|
||||
/** The NameError class */
|
||||
@@ -466,7 +467,7 @@ Object theEmptyTupleObject() {
|
||||
module Object {
|
||||
|
||||
Object builtin(string name) {
|
||||
py_cmembers_versioned(theBuiltinModuleObject(), name, result, major_version().toString())
|
||||
result.asBuiltin() = Builtin::builtin(name)
|
||||
}
|
||||
|
||||
/** The named quitter object (quit or exit) in the builtin namespace */
|
||||
@@ -510,6 +511,6 @@ private ClassObject string_literal(Expr e) {
|
||||
}
|
||||
|
||||
Object theUnknownType() {
|
||||
py_special_objects(result, "_semmle_unknown_type")
|
||||
result.asBuiltin() = Builtin::unknownType()
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ abstract class PropertyObject extends Object {
|
||||
PropertyObject() {
|
||||
property_getter(this, _)
|
||||
or
|
||||
py_cobjecttypes(this, theBuiltinPropertyType())
|
||||
this.asBuiltin().getClass() = theBuiltinPropertyType().asBuiltin()
|
||||
}
|
||||
|
||||
/** Gets the name of this property */
|
||||
@@ -78,16 +78,16 @@ class PythonPropertyObject extends PropertyObject {
|
||||
class BuiltinPropertyObject extends PropertyObject {
|
||||
|
||||
BuiltinPropertyObject() {
|
||||
py_cobjecttypes(this, theBuiltinPropertyType())
|
||||
this.asBuiltin().getClass() = theBuiltinPropertyType().asBuiltin()
|
||||
}
|
||||
|
||||
override string getName() {
|
||||
py_cobjectnames(this, result)
|
||||
result = this.asBuiltin().getName()
|
||||
}
|
||||
|
||||
/** Gets the getter method wrapper of this property */
|
||||
override Object getGetter() {
|
||||
py_cmembers_versioned(this, "__get__", result, major_version().toString())
|
||||
result.asBuiltin() = this.asBuiltin().getMember("__get__")
|
||||
}
|
||||
|
||||
override ClassObject getInferredPropertyType() {
|
||||
@@ -96,12 +96,12 @@ class BuiltinPropertyObject extends PropertyObject {
|
||||
|
||||
/** Gets the setter method wrapper of this property */
|
||||
override Object getSetter() {
|
||||
py_cmembers_versioned(this, "__set__", result, major_version().toString())
|
||||
result.asBuiltin() = this.asBuiltin().getMember("__set__")
|
||||
}
|
||||
|
||||
/** Gets the deleter method wrapper of this property */
|
||||
override Object getDeleter() {
|
||||
py_cmembers_versioned(this, "__delete__", result, major_version().toString())
|
||||
result.asBuiltin() = this.asBuiltin().getMember("__delete__")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name All must have locations
|
||||
* @description
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @problem.severity error
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name Naive
|
||||
* @description Insert description here...
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @problem.severity warning
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name All must have locations
|
||||
* @description
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @problem.severity error
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name ImportTimeScope
|
||||
* @description ImportTimeScope Test
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @problem.severity warning
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name UseFromDefinition
|
||||
* @description Insert description here...
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @problem.severity warning
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name SSA
|
||||
* @description Insert description here...
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @problem.severity warning
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name CompareTest
|
||||
* @description CompareTest
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @problem.severity warning
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name Check all non-scope nodes have an immediate dominator
|
||||
* @description Check all non-scope nodes have an immediate dominator
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @problem.severity warning
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name Branch
|
||||
* @description Insert description here...
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @problem.severity warning
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name TrueFalseSuccessors Test
|
||||
* @description Tests true/false successors
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @problem.severity warning
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name TrueFalseSuccessors Test
|
||||
* @description Tests true/false successors
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @problem.severity warning
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name Definitions
|
||||
* @description Insert description here...
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @problem.severity warning
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name Usages
|
||||
* @description Insert description here...
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @problem.severity warning
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name Duplicate
|
||||
* @description Insert description here...
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @problem.severity warning
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name Similar
|
||||
* @description Insert description here...
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @problem.severity warning
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name LocalPointsTo
|
||||
* @description Insert description here...
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @problem.severity warning
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name commented_out_code
|
||||
* @description Insert description here...
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @problem.severity warning
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name Categories
|
||||
* @description Insert description here...
|
||||
* @kind problem
|
||||
* @kind table
|
||||
* @problem.severity warning
|
||||
*/
|
||||
|
||||
|
||||
Reference in New Issue
Block a user