From f23eb0432e9bdb57cc149249a740b80af8b2253b Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 11 Jun 2020 11:44:50 +0200 Subject: [PATCH 01/40] Java: Improve qldoc for JavadocTag. --- java/ql/src/semmle/code/java/Javadoc.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/semmle/code/java/Javadoc.qll b/java/ql/src/semmle/code/java/Javadoc.qll index 26d1bba3f93..14b59f243ec 100755 --- a/java/ql/src/semmle/code/java/Javadoc.qll +++ b/java/ql/src/semmle/code/java/Javadoc.qll @@ -79,7 +79,7 @@ abstract class JavadocElement extends @javadocElement, Top { abstract string getText(); } -/** A Javadoc tag. */ +/** A Javadoc block tag. This does not include inline tags. */ class JavadocTag extends JavadocElement, JavadocParent, @javadocTag { /** Gets the name of this Javadoc tag. */ string getTagName() { javadocTag(this, result, _, _) } From c961a31789981d5095fc789d66e12b9af8d2cbe2 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 11 Jun 2020 13:46:12 +0200 Subject: [PATCH 02/40] Java: Add Expr.getAnEnclosingStmt. --- java/ql/src/semmle/code/java/Expr.qll | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/java/ql/src/semmle/code/java/Expr.qll b/java/ql/src/semmle/code/java/Expr.qll index c175894588c..8aca2697907 100755 --- a/java/ql/src/semmle/code/java/Expr.qll +++ b/java/ql/src/semmle/code/java/Expr.qll @@ -60,6 +60,12 @@ class Expr extends ExprParent, @expr { /** Gets the statement containing this expression, if any. */ Stmt getEnclosingStmt() { statementEnclosingExpr(this, result) } + /** + * Gets a statement that transitively contains this expression, if any. + * This is equivalent to `this.getEnclosingStmt().getEnclosingStmt*()`. + */ + Stmt getAnEnclosingStmt() { result = this.getEnclosingStmt().getEnclosingStmt*() } + /** Gets a child of this expression. */ Expr getAChildExpr() { exprs(result, _, _, this, _) } From 421a548e4232a798c1c8661160ccc709db546c91 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Fri, 12 Jun 2020 09:24:37 +0200 Subject: [PATCH 03/40] Update java/ql/src/semmle/code/java/Expr.qll --- java/ql/src/semmle/code/java/Expr.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/semmle/code/java/Expr.qll b/java/ql/src/semmle/code/java/Expr.qll index 8aca2697907..12a232bc4b0 100755 --- a/java/ql/src/semmle/code/java/Expr.qll +++ b/java/ql/src/semmle/code/java/Expr.qll @@ -61,7 +61,7 @@ class Expr extends ExprParent, @expr { Stmt getEnclosingStmt() { statementEnclosingExpr(this, result) } /** - * Gets a statement that transitively contains this expression, if any. + * Gets a statement that directly or transitively contains this expression, if any. * This is equivalent to `this.getEnclosingStmt().getEnclosingStmt*()`. */ Stmt getAnEnclosingStmt() { result = this.getEnclosingStmt().getEnclosingStmt*() } From 80981ec8f562d64301053c5c8929b6df39f99f16 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Tue, 30 Jun 2020 12:01:02 +0200 Subject: [PATCH 04/40] Update UnsafeHtmlExpansion-transformed.html --- .../CWE-116/examples/UnsafeHtmlExpansion-transformed.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/Security/CWE-116/examples/UnsafeHtmlExpansion-transformed.html b/javascript/ql/src/Security/CWE-116/examples/UnsafeHtmlExpansion-transformed.html index 1c88927e8ca..e9a2a85a2a5 100644 --- a/javascript/ql/src/Security/CWE-116/examples/UnsafeHtmlExpansion-transformed.html +++ b/javascript/ql/src/Security/CWE-116/examples/UnsafeHtmlExpansion-transformed.html @@ -1,3 +1,3 @@ -
+<div alt= "/> From ed48efe5b49a8707fc613ffd61400865dcf8ec47 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 30 Jun 2020 15:52:08 +0200 Subject: [PATCH 05/40] recognize access to a query object through function calls --- .../semmle/javascript/frameworks/Express.qll | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/Express.qll b/javascript/ql/src/semmle/javascript/frameworks/Express.qll index 503474e8c3c..e608cd969ac 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Express.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Express.qll @@ -463,6 +463,16 @@ module Express { override RequestSource src; } + /** + * Gets a reference to the "query" or "params" object from a request-object originating from route-handler `rh`. + */ + DataFlow::SourceNode getAQueryObjectReference(DataFlow::TypeTracker t, RouteHandler rh) { + t.startInProp(["params", "query"]) and + result = rh.getARequestSource() + or + exists(DataFlow::TypeTracker t2 | result = getAQueryObjectReference(t2, rh).track(t2, t)) + } + /** * An access to a user-controlled Express request input. */ @@ -471,13 +481,12 @@ module Express { string kind; RequestInputAccess() { + kind = "parameter" and + this = getAQueryObjectReference(DataFlow::TypeTracker::end(), rh).getAPropertyRead() + or exists(DataFlow::SourceNode request | request = rh.getARequestSource().ref() | kind = "parameter" and - ( - this = request.getAMethodCall("param") - or - this = request.getAPropertyRead(["params", "query"]).getAPropertyRead() - ) + this = request.getAMethodCall("param") or // `req.originalUrl` kind = "url" and From 82270104639f4ffc06e8883d2f31ae005fd76ce4 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 1 Jul 2020 11:32:51 +0200 Subject: [PATCH 06/40] also use new type-tracking in isUserControlledObject --- .../semmle/javascript/frameworks/Express.qll | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/Express.qll b/javascript/ql/src/semmle/javascript/frameworks/Express.qll index e608cd969ac..a46723784d6 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Express.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Express.qll @@ -466,11 +466,14 @@ module Express { /** * Gets a reference to the "query" or "params" object from a request-object originating from route-handler `rh`. */ - DataFlow::SourceNode getAQueryObjectReference(DataFlow::TypeTracker t, RouteHandler rh) { - t.startInProp(["params", "query"]) and + DataFlow::SourceNode getAQueryObjectReference( + DataFlow::TypeTracker t, RouteHandler rh, string prop + ) { + prop = ["params", "query"] and + t.startInProp(prop) and result = rh.getARequestSource() or - exists(DataFlow::TypeTracker t2 | result = getAQueryObjectReference(t2, rh).track(t2, t)) + exists(DataFlow::TypeTracker t2 | result = getAQueryObjectReference(t2, rh, prop).track(t2, t)) } /** @@ -482,7 +485,7 @@ module Express { RequestInputAccess() { kind = "parameter" and - this = getAQueryObjectReference(DataFlow::TypeTracker::end(), rh).getAPropertyRead() + this = getAQueryObjectReference(DataFlow::TypeTracker::end(), rh, _).getAPropertyRead() or exists(DataFlow::SourceNode request | request = rh.getARequestSource().ref() | kind = "parameter" and @@ -527,13 +530,11 @@ module Express { kind = "parameter" and exists(DataFlow::Node request | request = DataFlow::valueNode(rh.getARequestExpr()) | this.(DataFlow::MethodCallNode).calls(request, "param") - or - exists(DataFlow::PropRead base | - // `req.query.name` - base.accesses(request, "query") and - this = base.getAPropertyReference(_) - ) ) + or + // `req.query.name` + kind = "parameter" and + this = getAQueryObjectReference(DataFlow::TypeTracker::end(), rh, "query").getAPropertyRead() } } From bace2994c39639ba4201d70ca4b77923b7a88868 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 1 Jul 2020 11:38:54 +0200 Subject: [PATCH 07/40] add test for type-tracking req.params --- .../query-tests/Security/CWE-079/ReflectedXss.expected | 9 +++++++++ .../test/query-tests/Security/CWE-079/ReflectedXss.js | 10 ++++++++-- .../CWE-079/ReflectedXssWithCustomSanitizer.expected | 1 + 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected index 02751c8cfb6..b0b73f732c3 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected @@ -3,6 +3,10 @@ nodes | ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | | ReflectedXss.js:8:33:8:45 | req.params.id | | ReflectedXss.js:8:33:8:45 | req.params.id | +| ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id | +| ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id | +| ReflectedXss.js:17:31:17:39 | params.id | +| ReflectedXss.js:17:31:17:39 | params.id | | ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id | | ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id | | ReflectedXssContentTypes.js:10:24:10:36 | req.params.id | @@ -95,6 +99,10 @@ edges | ReflectedXss.js:8:33:8:45 | req.params.id | ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | | ReflectedXss.js:8:33:8:45 | req.params.id | ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | | ReflectedXss.js:8:33:8:45 | req.params.id | ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | +| ReflectedXss.js:17:31:17:39 | params.id | ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id | +| ReflectedXss.js:17:31:17:39 | params.id | ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id | +| ReflectedXss.js:17:31:17:39 | params.id | ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id | +| ReflectedXss.js:17:31:17:39 | params.id | ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id | | ReflectedXssContentTypes.js:10:24:10:36 | req.params.id | ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id | | ReflectedXssContentTypes.js:10:24:10:36 | req.params.id | ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id | | ReflectedXssContentTypes.js:10:24:10:36 | req.params.id | ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id | @@ -173,6 +181,7 @@ edges | tst2.js:14:9:14:9 | p | tst2.js:14:7:14:24 | p | #select | ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | ReflectedXss.js:8:33:8:45 | req.params.id | ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:8:33:8:45 | req.params.id | user-provided value | +| ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id | ReflectedXss.js:17:31:17:39 | params.id | ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:17:31:17:39 | params.id | user-provided value | | ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id | ReflectedXssContentTypes.js:10:24:10:36 | req.params.id | ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXssContentTypes.js:10:24:10:36 | req.params.id | user-provided value | | ReflectedXssContentTypes.js:20:14:20:36 | "FOO: " ... rams.id | ReflectedXssContentTypes.js:20:24:20:36 | req.params.id | ReflectedXssContentTypes.js:20:14:20:36 | "FOO: " ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXssContentTypes.js:20:24:20:36 | req.params.id | user-provided value | | ReflectedXssContentTypes.js:39:13:39:35 | "FOO: " ... rams.id | ReflectedXssContentTypes.js:39:23:39:35 | req.params.id | ReflectedXssContentTypes.js:39:13:39:35 | "FOO: " ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXssContentTypes.js:39:23:39:35 | req.params.id | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss.js b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss.js index 3268204fe6e..4a7c04a989c 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss.js +++ b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss.js @@ -3,10 +3,16 @@ var express = require('express'); var app = express(); app.get('/user/:id', function(req, res) { - if (!isValidUserId(req.params.id)) + if (!isValidUserId(req.params.id)) { // BAD: a request parameter is incorporated without validation into the response res.send("Unknown user: " + req.params.id); - else + moreBadStuff(req.params, res); + } else { // TODO: do something exciting ; + } }); + +function moreBadStuff(params, res) { + res.send("Unknown user: " + params.id); // NOT OK +} diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssWithCustomSanitizer.expected b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssWithCustomSanitizer.expected index 70c53101515..e1b55b7b825 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssWithCustomSanitizer.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssWithCustomSanitizer.expected @@ -1,4 +1,5 @@ | ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:8:33:8:45 | req.params.id | user-provided value | +| ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:17:31:17:39 | params.id | user-provided value | | ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXssContentTypes.js:10:24:10:36 | req.params.id | user-provided value | | ReflectedXssContentTypes.js:20:14:20:36 | "FOO: " ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXssContentTypes.js:20:24:20:36 | req.params.id | user-provided value | | ReflectedXssContentTypes.js:39:13:39:35 | "FOO: " ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXssContentTypes.js:39:23:39:35 | req.params.id | user-provided value | From 3157cd724d756b55603ac5c20cceb0ccf6145e6e Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 1 Jul 2020 11:45:09 +0200 Subject: [PATCH 08/40] add noSQL tests for type-tracking req.query --- .../CWE-089/untyped/DatabaseAccesses.expected | 2 ++ .../CWE-089/untyped/SqlInjection.expected | 18 ++++++++++++ .../Security/CWE-089/untyped/mongodb.js | 28 +++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/DatabaseAccesses.expected b/javascript/ql/test/query-tests/Security/CWE-089/untyped/DatabaseAccesses.expected index 1e5ddde14ec..8adf353a51a 100644 --- a/javascript/ql/test/query-tests/Security/CWE-089/untyped/DatabaseAccesses.expected +++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/DatabaseAccesses.expected @@ -11,6 +11,8 @@ | mongodb.js:65:3:65:17 | doc.find(query) | | mongodb.js:73:5:77:27 | client\\n ... tag }) | | mongodb.js:81:3:85:25 | importe ... tag }) | +| mongodb.js:98:5:98:19 | doc.find(query) | +| mongodb.js:112:5:112:19 | doc.find(query) | | mongodb_bodySafe.js:18:7:18:21 | doc.find(query) | | mongodb_bodySafe.js:29:7:29:21 | doc.find(query) | | mongoose.js:63:2:63:34 | Documen ... then(X) | diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected b/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected index ed8fd72141e..30e4e0da6eb 100644 --- a/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected @@ -56,6 +56,12 @@ nodes | mongodb.js:85:12:85:24 | { tags: tag } | | mongodb.js:85:12:85:24 | { tags: tag } | | mongodb.js:85:20:85:22 | tag | +| mongodb.js:106:9:106:18 | query | +| mongodb.js:106:17:106:18 | {} | +| mongodb.js:107:17:107:29 | queries.title | +| mongodb.js:107:17:107:29 | queries.title | +| mongodb.js:112:14:112:18 | query | +| mongodb.js:112:14:112:18 | query | | mongodb_bodySafe.js:23:11:23:20 | query | | mongodb_bodySafe.js:23:19:23:20 | {} | | mongodb_bodySafe.js:24:19:24:33 | req.query.title | @@ -244,6 +250,17 @@ edges | mongodb.js:77:22:77:24 | tag | mongodb.js:77:14:77:26 | { tags: tag } | | mongodb.js:85:20:85:22 | tag | mongodb.js:85:12:85:24 | { tags: tag } | | mongodb.js:85:20:85:22 | tag | mongodb.js:85:12:85:24 | { tags: tag } | +| mongodb.js:106:9:106:18 | query | mongodb.js:112:14:112:18 | query | +| mongodb.js:106:9:106:18 | query | mongodb.js:112:14:112:18 | query | +| mongodb.js:106:17:106:18 | {} | mongodb.js:106:9:106:18 | query | +| mongodb.js:107:17:107:29 | queries.title | mongodb.js:106:9:106:18 | query | +| mongodb.js:107:17:107:29 | queries.title | mongodb.js:106:9:106:18 | query | +| mongodb.js:107:17:107:29 | queries.title | mongodb.js:106:17:106:18 | {} | +| mongodb.js:107:17:107:29 | queries.title | mongodb.js:106:17:106:18 | {} | +| mongodb.js:107:17:107:29 | queries.title | mongodb.js:112:14:112:18 | query | +| mongodb.js:107:17:107:29 | queries.title | mongodb.js:112:14:112:18 | query | +| mongodb.js:107:17:107:29 | queries.title | mongodb.js:112:14:112:18 | query | +| mongodb.js:107:17:107:29 | queries.title | mongodb.js:112:14:112:18 | query | | mongodb_bodySafe.js:23:11:23:20 | query | mongodb_bodySafe.js:29:16:29:20 | query | | mongodb_bodySafe.js:23:11:23:20 | query | mongodb_bodySafe.js:29:16:29:20 | query | | mongodb_bodySafe.js:23:19:23:20 | {} | mongodb_bodySafe.js:23:11:23:20 | query | @@ -428,6 +445,7 @@ edges | mongodb.js:65:12:65:16 | query | mongodb.js:60:16:60:30 | req.query.title | mongodb.js:65:12:65:16 | query | This query depends on $@. | mongodb.js:60:16:60:30 | req.query.title | a user-provided value | | mongodb.js:77:14:77:26 | { tags: tag } | mongodb.js:70:13:70:25 | req.query.tag | mongodb.js:77:14:77:26 | { tags: tag } | This query depends on $@. | mongodb.js:70:13:70:25 | req.query.tag | a user-provided value | | mongodb.js:85:12:85:24 | { tags: tag } | mongodb.js:70:13:70:25 | req.query.tag | mongodb.js:85:12:85:24 | { tags: tag } | This query depends on $@. | mongodb.js:70:13:70:25 | req.query.tag | a user-provided value | +| mongodb.js:112:14:112:18 | query | mongodb.js:107:17:107:29 | queries.title | mongodb.js:112:14:112:18 | query | This query depends on $@. | mongodb.js:107:17:107:29 | queries.title | a user-provided value | | mongodb_bodySafe.js:29:16:29:20 | query | mongodb_bodySafe.js:24:19:24:33 | req.query.title | mongodb_bodySafe.js:29:16:29:20 | query | This query depends on $@. | mongodb_bodySafe.js:24:19:24:33 | req.query.title | a user-provided value | | mongoose.js:24:24:24:30 | [query] | mongoose.js:21:19:21:26 | req.body | mongoose.js:24:24:24:30 | [query] | This query depends on $@. | mongoose.js:21:19:21:26 | req.body | a user-provided value | | mongoose.js:27:20:27:24 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:27:20:27:24 | query | This query depends on $@. | mongoose.js:21:19:21:26 | req.body | a user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/mongodb.js b/javascript/ql/test/query-tests/Security/CWE-089/untyped/mongodb.js index 5963273171e..fc786da87ab 100644 --- a/javascript/ql/test/query-tests/Security/CWE-089/untyped/mongodb.js +++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/mongodb.js @@ -84,3 +84,31 @@ app.post("/logs/count-by-tag", (req, res) => { // NOT OK: query is tainted by user-provided object value .count({ tags: tag }); }); + + +app.get('/:id', (req, res) => { + useParams(req.param); +}); +function useParams(params) { + let query = { id: params.id }; + MongoClient.connect('mongodb://localhost:27017/test', (err, db) => { + let doc = db.collection('doc'); + + // OK: query is tainted, but only by string value + doc.find(query); + }); +} + +app.post('/documents/find', (req, res) => { + useQuery(req.query); +}); +function useQuery(queries) { + const query = {}; + query.title = queries.title; + MongoClient.connect('mongodb://localhost:27017/test', (err, db) => { + let doc = db.collection('doc'); + + // NOT OK: query is tainted by user-provided object value + doc.find(query); + }); +} \ No newline at end of file From 2b0a091921d67fe40be0d9f47b3af2ed15f3ea92 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 2 Jul 2020 14:28:28 +0200 Subject: [PATCH 09/40] split out type-tracking into two predicates, to avoid catastrophic join-order --- .../semmle/javascript/frameworks/Express.qll | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/Express.qll b/javascript/ql/src/semmle/javascript/frameworks/Express.qll index a46723784d6..7a735b6b82a 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Express.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Express.qll @@ -464,16 +464,23 @@ module Express { } /** - * Gets a reference to the "query" or "params" object from a request-object originating from route-handler `rh`. + * Gets a reference to the "query" object from a request-object originating from route-handler `rh`. */ - DataFlow::SourceNode getAQueryObjectReference( - DataFlow::TypeTracker t, RouteHandler rh, string prop - ) { - prop = ["params", "query"] and - t.startInProp(prop) and + DataFlow::SourceNode getAQueryObjectReference(DataFlow::TypeTracker t, RouteHandler rh) { + t.startInProp("query") and result = rh.getARequestSource() or - exists(DataFlow::TypeTracker t2 | result = getAQueryObjectReference(t2, rh, prop).track(t2, t)) + exists(DataFlow::TypeTracker t2 | result = getAQueryObjectReference(t2, rh).track(t2, t)) + } + + /** + * Gets a reference to the "params" object from a request-object originating from route-handler `rh`. + */ + DataFlow::SourceNode getAParamsObjectReference(DataFlow::TypeTracker t, RouteHandler rh) { + t.startInProp("params") and + result = rh.getARequestSource() + or + exists(DataFlow::TypeTracker t2 | result = getAParamsObjectReference(t2, rh).track(t2, t)) } /** @@ -485,7 +492,9 @@ module Express { RequestInputAccess() { kind = "parameter" and - this = getAQueryObjectReference(DataFlow::TypeTracker::end(), rh, _).getAPropertyRead() + this = + [getAQueryObjectReference(DataFlow::TypeTracker::end(), rh), + getAParamsObjectReference(DataFlow::TypeTracker::end(), rh)].getAPropertyRead() or exists(DataFlow::SourceNode request | request = rh.getARequestSource().ref() | kind = "parameter" and @@ -534,7 +543,7 @@ module Express { or // `req.query.name` kind = "parameter" and - this = getAQueryObjectReference(DataFlow::TypeTracker::end(), rh, "query").getAPropertyRead() + this = getAQueryObjectReference(DataFlow::TypeTracker::end(), rh).getAPropertyRead() } } From 078b6a8df2a0413cb740ade6ca32104f8b9e8e80 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Fri, 3 Jul 2020 00:21:55 +0200 Subject: [PATCH 10/40] autoformat --- javascript/ql/src/semmle/javascript/frameworks/Express.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/Express.qll b/javascript/ql/src/semmle/javascript/frameworks/Express.qll index 7a735b6b82a..a94db5ed28d 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Express.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Express.qll @@ -494,7 +494,7 @@ module Express { kind = "parameter" and this = [getAQueryObjectReference(DataFlow::TypeTracker::end(), rh), - getAParamsObjectReference(DataFlow::TypeTracker::end(), rh)].getAPropertyRead() + getAParamsObjectReference(DataFlow::TypeTracker::end(), rh)].getAPropertyRead() or exists(DataFlow::SourceNode request | request = rh.getARequestSource().ref() | kind = "parameter" and From bb01dbd2aec1f1ebbfd83f5f2555cb9cc96e5d76 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Fri, 3 Jul 2020 13:47:24 +0200 Subject: [PATCH 11/40] CodeQL: exclude queries from LGTM suites --- misc/suite-helpers/lgtm-selectors.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/misc/suite-helpers/lgtm-selectors.yml b/misc/suite-helpers/lgtm-selectors.yml index 340f119a081..c83484cb1a4 100644 --- a/misc/suite-helpers/lgtm-selectors.yml +++ b/misc/suite-helpers/lgtm-selectors.yml @@ -21,3 +21,5 @@ - file-classifier - exclude: deprecated: // +- exclude: + query path: /^experimental\/.*/ From 2b248fb24f4df415116b8e518fe7117e33287b71 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Fri, 3 Jul 2020 14:03:00 +0200 Subject: [PATCH 12/40] CodeQL: exclude queries from CodeScanning suites --- misc/suite-helpers/code-scanning-selectors.yml | 2 ++ misc/suite-helpers/security-and-quality-selectors.yml | 3 +++ misc/suite-helpers/security-extended-selectors.yml | 2 ++ 3 files changed, 7 insertions(+) diff --git a/misc/suite-helpers/code-scanning-selectors.yml b/misc/suite-helpers/code-scanning-selectors.yml index ffa40d8e4b1..6178e187ee9 100644 --- a/misc/suite-helpers/code-scanning-selectors.yml +++ b/misc/suite-helpers/code-scanning-selectors.yml @@ -13,4 +13,6 @@ - security - exclude: deprecated: // +- exclude: + query path: /^experimental\/.*/ diff --git a/misc/suite-helpers/security-and-quality-selectors.yml b/misc/suite-helpers/security-and-quality-selectors.yml index 1372973324e..881bcdfa3ac 100644 --- a/misc/suite-helpers/security-and-quality-selectors.yml +++ b/misc/suite-helpers/security-and-quality-selectors.yml @@ -16,3 +16,6 @@ - warning - exclude: deprecated: // +- exclude: + query path: /^experimental\/.*/ + diff --git a/misc/suite-helpers/security-extended-selectors.yml b/misc/suite-helpers/security-extended-selectors.yml index 7e82e03d93c..a19639c1eb3 100644 --- a/misc/suite-helpers/security-extended-selectors.yml +++ b/misc/suite-helpers/security-extended-selectors.yml @@ -21,4 +21,6 @@ - security - exclude: deprecated: // +- exclude: + query path: /^experimental\/.*/ From 4c06eb8bfec5b1fbac8f04557cf7c64b1d0da0ca Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 3 Jul 2020 14:26:10 +0100 Subject: [PATCH 13/40] JS: Add test showing FPs --- .../ClientSideUrlRedirect.expected | 65 +++++++++++++++++++ .../ClientSideUrlRedirect/sanitizer.js | 39 +++++++++++ 2 files changed, 104 insertions(+) create mode 100644 javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/sanitizer.js diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected index ba2a4236c78..542731ea9e4 100644 --- a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected +++ b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected @@ -1,4 +1,31 @@ nodes +| sanitizer.js:2:9:2:25 | url | +| sanitizer.js:2:15:2:25 | window.name | +| sanitizer.js:2:15:2:25 | window.name | +| sanitizer.js:4:27:4:29 | url | +| sanitizer.js:4:27:4:29 | url | +| sanitizer.js:7:27:7:29 | url | +| sanitizer.js:7:27:7:29 | url | +| sanitizer.js:10:27:10:29 | url | +| sanitizer.js:10:27:10:29 | url | +| sanitizer.js:13:27:13:29 | url | +| sanitizer.js:13:27:13:29 | url | +| sanitizer.js:16:27:16:29 | url | +| sanitizer.js:16:27:16:29 | url | +| sanitizer.js:19:27:19:29 | url | +| sanitizer.js:19:27:19:29 | url | +| sanitizer.js:22:27:22:29 | url | +| sanitizer.js:22:27:22:29 | url | +| sanitizer.js:25:27:25:29 | url | +| sanitizer.js:25:27:25:29 | url | +| sanitizer.js:28:27:28:29 | url | +| sanitizer.js:28:27:28:29 | url | +| sanitizer.js:31:27:31:29 | url | +| sanitizer.js:31:27:31:29 | url | +| sanitizer.js:34:27:34:29 | url | +| sanitizer.js:34:27:34:29 | url | +| sanitizer.js:37:27:37:29 | url | +| sanitizer.js:37:27:37:29 | url | | tst2.js:2:7:2:33 | href | | tst2.js:2:7:2:33 | href | | tst2.js:2:14:2:28 | window.location | @@ -80,6 +107,32 @@ nodes | tst.js:6:34:6:50 | document.location | | tst.js:6:34:6:55 | documen ... on.href | edges +| sanitizer.js:2:9:2:25 | url | sanitizer.js:4:27:4:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:4:27:4:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:7:27:7:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:7:27:7:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:10:27:10:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:10:27:10:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:13:27:13:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:13:27:13:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:16:27:16:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:16:27:16:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:19:27:19:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:19:27:19:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:22:27:22:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:22:27:22:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:25:27:25:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:25:27:25:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:28:27:28:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:28:27:28:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:31:27:31:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:31:27:31:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:34:27:34:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:34:27:34:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:37:27:37:29 | url | +| sanitizer.js:2:9:2:25 | url | sanitizer.js:37:27:37:29 | url | +| sanitizer.js:2:15:2:25 | window.name | sanitizer.js:2:9:2:25 | url | +| sanitizer.js:2:15:2:25 | window.name | sanitizer.js:2:9:2:25 | url | | tst2.js:2:7:2:33 | href | tst2.js:4:21:4:24 | href | | tst2.js:2:7:2:33 | href | tst2.js:4:21:4:24 | href | | tst2.js:2:14:2:28 | window.location | tst2.js:2:14:2:33 | window.location.href | @@ -155,6 +208,18 @@ edges | tst.js:6:34:6:50 | document.location | tst.js:6:34:6:55 | documen ... on.href | | tst.js:6:34:6:55 | documen ... on.href | tst.js:6:20:6:56 | indirec ... n.href) | #select +| sanitizer.js:4:27:4:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:4:27:4:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | +| sanitizer.js:7:27:7:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:7:27:7:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | +| sanitizer.js:10:27:10:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:10:27:10:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | +| sanitizer.js:13:27:13:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:13:27:13:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | +| sanitizer.js:16:27:16:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:16:27:16:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | +| sanitizer.js:19:27:19:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:19:27:19:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | +| sanitizer.js:22:27:22:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:22:27:22:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | +| sanitizer.js:25:27:25:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:25:27:25:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | +| sanitizer.js:28:27:28:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:28:27:28:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | +| sanitizer.js:31:27:31:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:31:27:31:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | +| sanitizer.js:34:27:34:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:34:27:34:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | +| sanitizer.js:37:27:37:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:37:27:37:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | | tst2.js:4:21:4:55 | href.su ... '?')+1) | tst2.js:2:14:2:28 | window.location | tst2.js:4:21:4:55 | href.su ... '?')+1) | Untrusted URL redirection due to $@. | tst2.js:2:14:2:28 | window.location | user-provided value | | tst6.js:4:21:4:28 | redirect | tst6.js:2:18:2:45 | $locati ... irect') | tst6.js:4:21:4:28 | redirect | Untrusted URL redirection due to $@. | tst6.js:2:18:2:45 | $locati ... irect') | user-provided value | | tst6.js:6:17:6:24 | redirect | tst6.js:2:18:2:45 | $locati ... irect') | tst6.js:6:17:6:24 | redirect | Untrusted URL redirection due to $@. | tst6.js:2:18:2:45 | $locati ... irect') | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/sanitizer.js b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/sanitizer.js new file mode 100644 index 00000000000..247f668f9b8 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/sanitizer.js @@ -0,0 +1,39 @@ +function f() { + let url = window.name; + if (url.startsWith('https://example.com')) { + window.location = url; // NOT OK - can be example.com.evil.com + } + if (url.startsWith('https://example.com/')) { + window.location = url; // OK - but flagged anyway + } + if (url.startsWith('https://example.com//')) { + window.location = url; // OK - but flagged anyway + } + if (url.startsWith('https://example.com/foo')) { + window.location = url; // OK - but flagged anyway + } + if (url.startsWith('https://')) { + window.location = url; // NOT OK - does not restrict hostname + } + if (url.startsWith('https:/')) { + window.location = url; // NOT OK - does not restrict hostname + } + if (url.startsWith('https:')) { + window.location = url; // NOT OK - does not restrict hostname + } + if (url.startsWith('/')) { + window.location = url; // NOT OK - can be //evil.com + } + if (url.startsWith('//')) { + window.location = url; // NOT OK - can be //evil.com + } + if (url.startsWith('//example.com')) { + window.location = url; // NOT OK - can be //example.com.evil.com + } + if (url.startsWith('//example.com/')) { + window.location = url; // OK - but flagged anyway + } + if (url.endsWith('https://example.com/')) { + window.location = url; // NOT OK - could be evil.com?x=https://example.com/ + } +} From b5104ae42d545bc75927fcb0453a1c85420cd89f Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 3 Jul 2020 14:34:59 +0100 Subject: [PATCH 14/40] JS: Add StartsWith sanitizer --- .../dataflow/ClientSideUrlRedirect.qll | 4 ++++ .../ClientSideUrlRedirectCustomizations.qll | 2 +- .../dataflow/ServerSideUrlRedirect.qll | 3 ++- .../ServerSideUrlRedirectCustomizations.qll | 2 +- .../security/dataflow/UrlConcatenation.qll | 14 +++++++++++++ .../ClientSideUrlRedirect.expected | 20 ------------------- .../ClientSideUrlRedirect/sanitizer.js | 12 +++++++---- 7 files changed, 30 insertions(+), 27 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirect.qll b/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirect.qll index 5102b53f0ce..f7493f44131 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirect.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirect.qll @@ -50,5 +50,9 @@ module ClientSideUrlRedirect { g instanceof DocumentUrl and succ.(DataFlow::PropRead).accesses(pred, "href") } + + override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) { + guard instanceof HostnameSanitizerGuard + } } } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll index 8c2211b086f..bb213e227e2 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll @@ -6,7 +6,7 @@ import javascript import semmle.javascript.security.dataflow.RemoteFlowSources -import UrlConcatenation +private import UrlConcatenation module ClientSideUrlRedirect { private import Xss::DomBasedXss as DomBasedXss diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirect.qll b/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirect.qll index ac69497eada..f2d6cfb1652 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirect.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirect.qll @@ -34,7 +34,8 @@ module ServerSideUrlRedirect { } override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) { - guard instanceof LocalUrlSanitizingGuard + guard instanceof LocalUrlSanitizingGuard or + guard instanceof HostnameSanitizerGuard } } } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirectCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirectCustomizations.qll index 8806e1811e1..3b6e7db7322 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirectCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirectCustomizations.qll @@ -6,7 +6,7 @@ import javascript import RemoteFlowSources -import UrlConcatenation +private import UrlConcatenation module ServerSideUrlRedirect { /** diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UrlConcatenation.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UrlConcatenation.qll index 8d6a3014822..e32826b79be 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UrlConcatenation.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UrlConcatenation.qll @@ -96,3 +96,17 @@ predicate hostnameSanitizingPrefixEdge(DataFlow::Node source, DataFlow::Node sin hasHostnameSanitizingSubstring(StringConcatenation::getOperand(operator, [0 .. n - 1])) ) } + +/** + * A check that sanitizes the hostname of a URL. + */ +class HostnameSanitizerGuard extends TaintTracking::SanitizerGuardNode, StringOps::StartsWith { + HostnameSanitizerGuard() { + hasHostnameSanitizingSubstring(getSubstring()) + } + + override predicate sanitizes(boolean outcome, Expr e) { + outcome = getPolarity() and + e = getBaseString().asExpr() + } +} diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected index 542731ea9e4..02cf0c015d5 100644 --- a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected +++ b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected @@ -4,12 +4,6 @@ nodes | sanitizer.js:2:15:2:25 | window.name | | sanitizer.js:4:27:4:29 | url | | sanitizer.js:4:27:4:29 | url | -| sanitizer.js:7:27:7:29 | url | -| sanitizer.js:7:27:7:29 | url | -| sanitizer.js:10:27:10:29 | url | -| sanitizer.js:10:27:10:29 | url | -| sanitizer.js:13:27:13:29 | url | -| sanitizer.js:13:27:13:29 | url | | sanitizer.js:16:27:16:29 | url | | sanitizer.js:16:27:16:29 | url | | sanitizer.js:19:27:19:29 | url | @@ -22,8 +16,6 @@ nodes | sanitizer.js:28:27:28:29 | url | | sanitizer.js:31:27:31:29 | url | | sanitizer.js:31:27:31:29 | url | -| sanitizer.js:34:27:34:29 | url | -| sanitizer.js:34:27:34:29 | url | | sanitizer.js:37:27:37:29 | url | | sanitizer.js:37:27:37:29 | url | | tst2.js:2:7:2:33 | href | @@ -109,12 +101,6 @@ nodes edges | sanitizer.js:2:9:2:25 | url | sanitizer.js:4:27:4:29 | url | | sanitizer.js:2:9:2:25 | url | sanitizer.js:4:27:4:29 | url | -| sanitizer.js:2:9:2:25 | url | sanitizer.js:7:27:7:29 | url | -| sanitizer.js:2:9:2:25 | url | sanitizer.js:7:27:7:29 | url | -| sanitizer.js:2:9:2:25 | url | sanitizer.js:10:27:10:29 | url | -| sanitizer.js:2:9:2:25 | url | sanitizer.js:10:27:10:29 | url | -| sanitizer.js:2:9:2:25 | url | sanitizer.js:13:27:13:29 | url | -| sanitizer.js:2:9:2:25 | url | sanitizer.js:13:27:13:29 | url | | sanitizer.js:2:9:2:25 | url | sanitizer.js:16:27:16:29 | url | | sanitizer.js:2:9:2:25 | url | sanitizer.js:16:27:16:29 | url | | sanitizer.js:2:9:2:25 | url | sanitizer.js:19:27:19:29 | url | @@ -127,8 +113,6 @@ edges | sanitizer.js:2:9:2:25 | url | sanitizer.js:28:27:28:29 | url | | sanitizer.js:2:9:2:25 | url | sanitizer.js:31:27:31:29 | url | | sanitizer.js:2:9:2:25 | url | sanitizer.js:31:27:31:29 | url | -| sanitizer.js:2:9:2:25 | url | sanitizer.js:34:27:34:29 | url | -| sanitizer.js:2:9:2:25 | url | sanitizer.js:34:27:34:29 | url | | sanitizer.js:2:9:2:25 | url | sanitizer.js:37:27:37:29 | url | | sanitizer.js:2:9:2:25 | url | sanitizer.js:37:27:37:29 | url | | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:2:9:2:25 | url | @@ -209,16 +193,12 @@ edges | tst.js:6:34:6:55 | documen ... on.href | tst.js:6:20:6:56 | indirec ... n.href) | #select | sanitizer.js:4:27:4:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:4:27:4:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | -| sanitizer.js:7:27:7:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:7:27:7:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | -| sanitizer.js:10:27:10:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:10:27:10:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | -| sanitizer.js:13:27:13:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:13:27:13:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | | sanitizer.js:16:27:16:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:16:27:16:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | | sanitizer.js:19:27:19:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:19:27:19:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | | sanitizer.js:22:27:22:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:22:27:22:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | | sanitizer.js:25:27:25:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:25:27:25:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | | sanitizer.js:28:27:28:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:28:27:28:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | | sanitizer.js:31:27:31:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:31:27:31:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | -| sanitizer.js:34:27:34:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:34:27:34:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | | sanitizer.js:37:27:37:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:37:27:37:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | | tst2.js:4:21:4:55 | href.su ... '?')+1) | tst2.js:2:14:2:28 | window.location | tst2.js:4:21:4:55 | href.su ... '?')+1) | Untrusted URL redirection due to $@. | tst2.js:2:14:2:28 | window.location | user-provided value | | tst6.js:4:21:4:28 | redirect | tst6.js:2:18:2:45 | $locati ... irect') | tst6.js:4:21:4:28 | redirect | Untrusted URL redirection due to $@. | tst6.js:2:18:2:45 | $locati ... irect') | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/sanitizer.js b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/sanitizer.js index 247f668f9b8..f8d5b805f4b 100644 --- a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/sanitizer.js +++ b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/sanitizer.js @@ -4,13 +4,13 @@ function f() { window.location = url; // NOT OK - can be example.com.evil.com } if (url.startsWith('https://example.com/')) { - window.location = url; // OK - but flagged anyway + window.location = url; // OK } if (url.startsWith('https://example.com//')) { - window.location = url; // OK - but flagged anyway + window.location = url; // OK } if (url.startsWith('https://example.com/foo')) { - window.location = url; // OK - but flagged anyway + window.location = url; // OK } if (url.startsWith('https://')) { window.location = url; // NOT OK - does not restrict hostname @@ -31,9 +31,13 @@ function f() { window.location = url; // NOT OK - can be //example.com.evil.com } if (url.startsWith('//example.com/')) { - window.location = url; // OK - but flagged anyway + window.location = url; // OK } if (url.endsWith('https://example.com/')) { window.location = url; // NOT OK - could be evil.com?x=https://example.com/ } + let basedir = whatever() ? 'foo' : 'bar'; + if (url.startsWith('https://example.com/' + basedir)) { + window.location = url; // OK - the whole prefix is not known, but enough to restrict hostname + } } From a07af79fff165f6a10ec853a1096af86e6dd9fc7 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Thu, 2 Jul 2020 16:05:09 +0200 Subject: [PATCH 15/40] Java: model java.util.Arrays --- .../code/java/dataflow/internal/ContainerFlow.qll | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll index 5a631a2fdff..4af429644dc 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll @@ -180,6 +180,12 @@ private predicate taintPreservingArgumentToMethod(Method method, int arg) { or method.hasName(["nCopies", "singletonMap"]) and arg = 1 ) + or + method.getDeclaringType().hasQualifiedName("java.util", "Arrays") and + ( + method.hasName(["copyOf", "copyOfRange", "deepToString", "spliterator", "stream", "toString"]) and + arg = 0 + ) } /** @@ -195,6 +201,13 @@ private predicate taintPreservingArgToArg(Method method, int input, int output) or method.hasName("replaceAll") and input = 2 and output = 0 ) + or + method.getDeclaringType().hasQualifiedName("java.util", "Arrays") and + ( + method.hasName(["fill", "parallelPrefix", "parallelSetAll", "setAll"]) and + output = 0 and + input = method.getNumberOfParameters() - 1 + ) } private predicate argToQualifierStep(Expr tracked, Expr sink) { From 0b89efbee49d2d31d7817b04d2fe8ce3c65bb52d Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Thu, 2 Jul 2020 16:05:56 +0200 Subject: [PATCH 16/40] Java: model Arrays::addList --- .../code/java/dataflow/internal/ContainerFlow.qll | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll index 4af429644dc..8625bad0089 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll @@ -221,10 +221,18 @@ private predicate argToQualifierStep(Expr tracked, Expr sink) { /** Access to a method that passes taint from an argument. */ private predicate argToMethodStep(Expr tracked, MethodAccess sink) { - exists(Method m, int i | + exists(Method m | m = sink.getMethod() and - taintPreservingArgumentToMethod(m, i) and - tracked = sink.getArgument(i) + ( + exists(int i | + taintPreservingArgumentToMethod(m, i) and + tracked = sink.getArgument(i) + ) + or + m.getDeclaringType().hasQualifiedName("java.util", "Arrays") and + m.hasName("asList") and + tracked = sink.getAnArgument() + ) ) } From 19a481f809b842321f9d4b98d530d7cfdb187f11 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Thu, 2 Jul 2020 19:48:19 +0200 Subject: [PATCH 17/40] Java: Arrays: add tests --- .../local-additional-taint/ArraysTest.java | 23 ++++++++++++++++ .../localAdditionalTaintStep.expected | 26 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 java/ql/test/library-tests/dataflow/local-additional-taint/ArraysTest.java diff --git a/java/ql/test/library-tests/dataflow/local-additional-taint/ArraysTest.java b/java/ql/test/library-tests/dataflow/local-additional-taint/ArraysTest.java new file mode 100644 index 00000000000..f016cb63fd3 --- /dev/null +++ b/java/ql/test/library-tests/dataflow/local-additional-taint/ArraysTest.java @@ -0,0 +1,23 @@ +import java.util.Arrays; +import java.util.List; + +class ArraysTest { + public static void taintSteps(String[] source) { + Arrays.asList(); + Arrays.asList("one"); + Arrays.asList("two", "three"); + Arrays.copyOf(source, 10); + Arrays.copyOfRange(source, 0, 10); + Arrays.deepToString(source); + Arrays.spliterator(source); + Arrays.stream(source); + Arrays.toString(source); + Arrays.fill(source, "value"); + Arrays.fill(source, 0, 10, "data"); + Arrays.parallelPrefix(source, (x, y) -> x + y); + Arrays.parallelPrefix(source, 0, 10, (x, y) -> x + y); + Arrays.parallelSetAll(source, x -> Integer.toString(x)); + Arrays.setAll(source, x -> Integer.toString(x)); + } +} + diff --git a/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected b/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected index 43e17969515..392a9312097 100644 --- a/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected +++ b/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected @@ -1,3 +1,29 @@ +| ArraysTest.java:7:17:7:21 | "one" | ArraysTest.java:7:3:7:22 | asList(...) | +| ArraysTest.java:7:17:7:21 | "one" | ArraysTest.java:7:3:7:22 | new ..[] { .. } | +| ArraysTest.java:8:17:8:21 | "two" | ArraysTest.java:8:3:8:31 | asList(...) | +| ArraysTest.java:8:17:8:21 | "two" | ArraysTest.java:8:3:8:31 | new ..[] { .. } | +| ArraysTest.java:8:24:8:30 | "three" | ArraysTest.java:8:3:8:31 | asList(...) | +| ArraysTest.java:8:24:8:30 | "three" | ArraysTest.java:8:3:8:31 | new ..[] { .. } | +| ArraysTest.java:9:17:9:22 | source | ArraysTest.java:9:3:9:27 | copyOf(...) | +| ArraysTest.java:10:22:10:27 | source | ArraysTest.java:10:3:10:35 | copyOfRange(...) | +| ArraysTest.java:11:23:11:28 | source | ArraysTest.java:11:3:11:29 | deepToString(...) | +| ArraysTest.java:12:22:12:27 | source | ArraysTest.java:12:3:12:28 | spliterator(...) | +| ArraysTest.java:13:17:13:22 | source | ArraysTest.java:13:3:13:23 | stream(...) | +| ArraysTest.java:14:19:14:24 | source | ArraysTest.java:14:3:14:25 | toString(...) | +| ArraysTest.java:15:23:15:29 | "value" | ArraysTest.java:15:15:15:20 | source [post update] | +| ArraysTest.java:16:30:16:35 | "data" | ArraysTest.java:16:15:16:20 | source [post update] | +| ArraysTest.java:17:33:17:47 | ...->... | ArraysTest.java:17:25:17:30 | source [post update] | +| ArraysTest.java:17:43:17:43 | x | ArraysTest.java:17:43:17:47 | ... + ... | +| ArraysTest.java:17:47:17:47 | y | ArraysTest.java:17:43:17:47 | ... + ... | +| ArraysTest.java:18:40:18:54 | ...->... | ArraysTest.java:18:25:18:30 | source [post update] | +| ArraysTest.java:18:50:18:50 | x | ArraysTest.java:18:50:18:54 | ... + ... | +| ArraysTest.java:18:54:18:54 | y | ArraysTest.java:18:50:18:54 | ... + ... | +| ArraysTest.java:19:33:19:56 | ...->... | ArraysTest.java:19:25:19:30 | source [post update] | +| ArraysTest.java:19:38:19:44 | Integer | ArraysTest.java:19:38:19:56 | toString(...) | +| ArraysTest.java:19:55:19:55 | x | ArraysTest.java:19:38:19:56 | toString(...) | +| ArraysTest.java:20:25:20:48 | ...->... | ArraysTest.java:20:17:20:22 | source [post update] | +| ArraysTest.java:20:30:20:36 | Integer | ArraysTest.java:20:30:20:48 | toString(...) | +| ArraysTest.java:20:47:20:47 | x | ArraysTest.java:20:30:20:48 | toString(...) | | CollectionsTest.java:8:28:8:32 | "one" | CollectionsTest.java:8:3:8:33 | new ..[] { .. } | | CollectionsTest.java:8:28:8:32 | "one" | CollectionsTest.java:8:22:8:25 | list [post update] | | CollectionsTest.java:9:28:9:32 | "two" | CollectionsTest.java:9:3:9:42 | new ..[] { .. } | From f8e474f89afa969e493b4074339d1f44b51ded41 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Fri, 26 Jun 2020 23:56:01 +0200 Subject: [PATCH 18/40] Add missing java.nio.file.Files methods to FileReadWrite.qll --- .../semmle/code/java/security/FileReadWrite.qll | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/java/ql/src/semmle/code/java/security/FileReadWrite.qll b/java/ql/src/semmle/code/java/security/FileReadWrite.qll index 68cd987532c..85020f60fd4 100644 --- a/java/ql/src/semmle/code/java/security/FileReadWrite.qll +++ b/java/ql/src/semmle/code/java/security/FileReadWrite.qll @@ -9,9 +9,9 @@ private predicate fileRead(VarAccess fileAccess, Expr fileReadingExpr) { cie = fileReadingExpr and cie.getArgument(0) = fileAccess | - cie.getConstructedType().hasQualifiedName("java.io", "RandomAccessFile") or - cie.getConstructedType().hasQualifiedName("java.io", "FileReader") or - cie.getConstructedType().hasQualifiedName("java.io", "FileInputStream") + cie + .getConstructedType() + .hasQualifiedName("java.io", ["RandomAccessFile", "FileReader", "FileInputStream"]) ) or exists(MethodAccess ma, Method filesMethod | @@ -22,13 +22,9 @@ private predicate fileRead(VarAccess fileAccess, Expr fileReadingExpr) { // represented by the first argument. filesMethod.getDeclaringType().hasQualifiedName("java.nio.file", "Files") and fileAccess = ma.getArgument(0) and - ( - filesMethod.hasName("readAllBytes") or - filesMethod.hasName("readAllLines") or - filesMethod.hasName("newBufferedReader") or - filesMethod.hasName("newInputReader") or - filesMethod.hasName("newByteChannel") - ) + filesMethod + .hasName(["readAllBytes", "readAllLines", "readString", "lines", "newBufferedReader", + "newInputReader", "newByteChannel"]) ) ) or From 13ffd7307c29946ff5b398ef2ad4ed834f06cdd4 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Sun, 5 Jul 2020 19:20:42 +0200 Subject: [PATCH 19/40] Update query console links in types-class-hierarchy.rst Removes 'gradle/gradle' from the queried projects because it cannot be queried currently, and instead queries all demo projects which are currently available. --- .../language/learn-ql/java/types-class-hierarchy.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/language/learn-ql/java/types-class-hierarchy.rst b/docs/language/learn-ql/java/types-class-hierarchy.rst index 76c9d803925..a8a6c17f0cb 100644 --- a/docs/language/learn-ql/java/types-class-hierarchy.rst +++ b/docs/language/learn-ql/java/types-class-hierarchy.rst @@ -32,7 +32,7 @@ To determine ancestor types (including immediate super types, and also *their* s where B.hasName("B") select B.getASupertype+() -➤ `See this in the query console on LGTM.com `__. If this query were run on the example snippet above, the query would return ``A``, ``I``, and ``java.lang.Object``. +➤ `See this in the query console on LGTM.com `__. If this query were run on the example snippet above, the query would return ``A``, ``I``, and ``java.lang.Object``. .. pull-quote:: @@ -78,7 +78,7 @@ This recipe is not too difficult to translate into a query: target.getElementType().(RefType).getASupertype+() = source.getElementType() select ce, "Potentially problematic array downcast." -➤ `See this in the query console on LGTM.com `__. Many projects return results for this query. +➤ `See this in the query console on LGTM.com `__. Many projects return results for this query. Note that by casting ``target.getElementType()`` to a ``RefType``, we eliminate all cases where the element type is a primitive type, that is, ``target`` is an array of primitive type: the problem we are looking for cannot arise in that case. Unlike in Java, a cast in QL never fails: if an expression cannot be cast to the desired type, it is simply excluded from the query results, which is exactly what we want. @@ -97,7 +97,7 @@ In code that does not use generics, this method is often used in the following w Here, ``l`` has the raw type ``List``, so ``l.toArray`` has return type ``Object[]``, independent of the type of its argument array. Hence the cast goes from ``Object[]`` to ``A[]`` and will be flagged as problematic by our query, although at runtime this cast can never go wrong. -To identify these cases, we can create two CodeQL classes that represent, respectively, the ``Collection.toArray`` class, and calls to this method or any method that overrides it: +To identify these cases, we can create two CodeQL classes that represent, respectively, the ``Collection.toArray`` method, and calls to this method or any method that overrides it: .. code-block:: ql @@ -148,7 +148,7 @@ Example: Finding mismatched contains checks We'll now develop a query that finds uses of ``Collection.contains`` where the type of the queried element is unrelated to the element type of the collection, which guarantees that the test will always return ``false``. -For example, `Apache Zookeeper `__ used to have a snippet of code similar to the following in class ``QuorumPeerConfig``: +For example, `Apache Zookeeper `__ used to have a snippet of code similar to the following in class ``QuorumPeerConfig``: .. code-block:: java @@ -267,7 +267,7 @@ Now we are ready to write a first version of our query: not haveCommonDescendant(collEltType, argType) select juccc, "Element type " + collEltType + " is incompatible with argument type " + argType -➤ `See this in the query console on LGTM.com `__. +➤ `See this in the query console on LGTM.com `__. Improvements ~~~~~~~~~~~~ @@ -294,7 +294,7 @@ Adding these three improvements, our final query becomes: not argType.hasName("") select juccc, "Element type " + collEltType + " is incompatible with argument type " + argType -➤ `See the full query in the query console on LGTM.com `__. +➤ `See the full query in the query console on LGTM.com `__. Further reading --------------- From ab2456630ce26c4b7616f7fe6a4f0bf928fc4012 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Sun, 5 Jul 2020 19:43:48 +0200 Subject: [PATCH 20/40] Update query console links in annotations.rst Removes 'eclipse-cdt/cdt' and 'gradle/gradle' from the queried projects because they cannot be queried currently, and instead queries all demo projects which are currently available. --- docs/language/learn-ql/java/annotations.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/language/learn-ql/java/annotations.rst b/docs/language/learn-ql/java/annotations.rst index 4591dc6aca1..861cbb74268 100644 --- a/docs/language/learn-ql/java/annotations.rst +++ b/docs/language/learn-ql/java/annotations.rst @@ -49,7 +49,7 @@ We could then write this query to find all ``@SuppressWarnings`` annotations att anntp.hasQualifiedName("java.lang", "SuppressWarnings") select ann, ann.getValue("value") -➤ `See the full query in the query console on LGTM.com `__. Several of the LGTM.com demo projects use the ``@SuppressWarnings`` annotation. Looking at the ``value``\ s of the annotation element returned by the query, we can see that the *apache/activemq* project uses the ``"rawtypes"`` value described above. +➤ `See the full query in the query console on LGTM.com `__. Several of the LGTM.com demo projects use the ``@SuppressWarnings`` annotation. Looking at the ``value``\ s of the annotation element returned by the query, we can see that the *apache/activemq* project uses the ``"rawtypes"`` value described above. As another example, this query finds all annotation types that only have a single annotation element, which has name ``value``: @@ -64,7 +64,7 @@ As another example, this query finds all annotation types that only have a singl ) select anntp -➤ `See the full query in the query console on LGTM.com `__. +➤ `See the full query in the query console on LGTM.com `__. Example: Finding missing ``@Override`` annotations -------------------------------------------------- @@ -122,7 +122,7 @@ This makes it very easy to write our query for finding methods that override ano not overriding.getAnAnnotation() instanceof OverrideAnnotation select overriding, "Method overrides another method, but does not have an @Override annotation." -➤ `See this in the query console on LGTM.com `__. In practice, this query may yield many results from compiled library code, which aren't very interesting. It's therefore a good idea to add another conjunct ``overriding.fromSource()`` to restrict the result to only report methods for which source code is available. +➤ `See this in the query console on LGTM.com `__. In practice, this query may yield many results from compiled library code, which aren't very interesting. It's therefore a good idea to add another conjunct ``overriding.fromSource()`` to restrict the result to only report methods for which source code is available. Example: Finding calls to deprecated methods -------------------------------------------- @@ -192,13 +192,13 @@ For instance, consider this slightly updated example: .. code-block:: java class A { - @Deprecated void m() {} + @Deprecated void m() {} - @Deprecated void n() { - m(); - } + @Deprecated void n() { + m(); + } - @SuppressWarnings("deprecated") + @SuppressWarnings("deprecated") void r() { m(); } @@ -235,7 +235,7 @@ Now we can extend our query to filter out calls in methods carrying a ``Suppress and not call.getCaller().getAnAnnotation() instanceof SuppressDeprecationWarningAnnotation select call, "This call invokes a deprecated method." -➤ `See this in the query console on LGTM.com `__. It's fairly common for projects to contain calls to methods that appear to be deprecated. +➤ `See this in the query console on LGTM.com `__. It's fairly common for projects to contain calls to methods that appear to be deprecated. Further reading --------------- From c10a5986709748433a482f871c4ba75e0b311133 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Sun, 5 Jul 2020 19:54:27 +0200 Subject: [PATCH 21/40] Update query console links in call-graph.rst Removes 'eclipse-cdt/cdt' and 'gradle/gradle' from the queried projects because they cannot be queried currently, and instead queries all demo projects which are currently available. --- docs/language/learn-ql/java/call-graph.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/language/learn-ql/java/call-graph.rst b/docs/language/learn-ql/java/call-graph.rst index 4aeb3906ae1..f55cf3b0524 100644 --- a/docs/language/learn-ql/java/call-graph.rst +++ b/docs/language/learn-ql/java/call-graph.rst @@ -78,7 +78,7 @@ We can use the ``Callable`` class to write a query that finds methods that are n where not exists(Callable caller | caller.polyCalls(callee)) select callee -➤ `See this in the query console on LGTM.com `__. This simple query typically returns a large number of results. +➤ `See this in the query console on LGTM.com `__. This simple query typically returns a large number of results. .. pull-quote:: @@ -97,7 +97,7 @@ Running this query on a typical Java project results in lots of hits in the Java callee.getCompilationUnit().fromSource() select callee, "Not called." -➤ `See this in the query console on LGTM.com `__. This change reduces the number of results returned for most projects. +➤ `See this in the query console on LGTM.com `__. This change reduces the number of results returned for most projects. We might also notice several unused methods with the somewhat strange name ````: these are class initializers; while they are not explicitly called anywhere in the code, they are called implicitly whenever the surrounding class is loaded. Hence it makes sense to exclude them from our query. While we are at it, we can also exclude finalizers, which are similarly invoked implicitly: @@ -111,7 +111,7 @@ We might also notice several unused methods with the somewhat strange name ``") and not callee.hasName("finalize") select callee, "Not called." -➤ `See this in the query console on LGTM.com `__. This also reduces the number of results returned by most projects. +➤ `See this in the query console on LGTM.com `__. This also reduces the number of results returned by most projects. We may also want to exclude public methods from our query, since they may be external API entry points: @@ -126,7 +126,7 @@ We may also want to exclude public methods from our query, since they may be ext not callee.isPublic() select callee, "Not called." -➤ `See this in the query console on LGTM.com `__. This should have a more noticeable effect on the number of results returned. +➤ `See this in the query console on LGTM.com `__. This should have a more noticeable effect on the number of results returned. A further special case is non-public default constructors: in the singleton pattern, for example, a class is provided with private empty default constructor to prevent it from being instantiated. Since the very purpose of such constructors is their not being called, they should not be flagged up: @@ -142,7 +142,7 @@ A further special case is non-public default constructors: in the singleton patt not callee.(Constructor).getNumberOfParameters() = 0 select callee, "Not called." -➤ `See this in the query console on LGTM.com `__. This change has a large effect on the results for some projects but little effect on the results for others. Use of this pattern varies widely between different projects. +➤ `See this in the query console on LGTM.com `__. This change has a large effect on the results for some projects but little effect on the results for others. Use of this pattern varies widely between different projects. Finally, on many Java projects there are methods that are invoked indirectly by reflection. So, while there are no calls invoking these methods, they are, in fact, used. It is in general very hard to identify such methods. A very common special case, however, is JUnit test methods, which are reflectively invoked by a test runner. The CodeQL library for Java has support for recognizing test classes of JUnit and other testing frameworks, which we can employ to filter out methods defined in such classes: @@ -159,7 +159,7 @@ Finally, on many Java projects there are methods that are invoked indirectly by not callee.getDeclaringType() instanceof TestClass select callee, "Not called." -➤ `See this in the query console on LGTM.com `__. This should give a further reduction in the number of results returned. +➤ `See this in the query console on LGTM.com `__. This should give a further reduction in the number of results returned. Further reading --------------- From 2b3b64cdbca849e7b79e476540accc2c4b1de3d3 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Sun, 5 Jul 2020 20:04:36 +0200 Subject: [PATCH 22/40] Update query console links in expressions-statements.rst Removes 'eclipse-cdt/cdt' and 'gradle/gradle' from the queried projects because they cannot be queried currently, and instead queries all demo projects which are currently available. --- docs/language/learn-ql/java/expressions-statements.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/language/learn-ql/java/expressions-statements.rst b/docs/language/learn-ql/java/expressions-statements.rst index 63092d005da..e1af4ad4f2d 100644 --- a/docs/language/learn-ql/java/expressions-statements.rst +++ b/docs/language/learn-ql/java/expressions-statements.rst @@ -42,7 +42,7 @@ We'll start by writing a query that finds less-than expressions (CodeQL class `` expr.getRightOperand().getType().hasName("long") select expr -➤ `See this in the query console on LGTM.com `__. This query usually finds results on most projects. +➤ `See this in the query console on LGTM.com `__. This query usually finds results on most projects. Notice that we use the predicate ``getType`` (available on all subclasses of ``Expr``) to determine the type of the operands. Types, in turn, define the ``hasName`` predicate, which allows us to identify the primitive types ``int`` and ``long``. As it stands, this query finds *all* less-than expressions comparing ``int`` and ``long``, but in fact we are only interested in comparisons that are part of a loop condition. Also, we want to filter out comparisons where either operand is constant, since these are less likely to be real bugs. The revised query looks like this: @@ -57,7 +57,7 @@ Notice that we use the predicate ``getType`` (available on all subclasses of ``E not expr.getAnOperand().isCompileTimeConstant() select expr -➤ `See this in the query console on LGTM.com `__. Notice that fewer results are found. +➤ `See this in the query console on LGTM.com `__. Notice that fewer results are found. The class ``LoopStmt`` is a common superclass of all loops, including, in particular, ``for`` loops as in our example above. While different kinds of loops have different syntax, they all have a loop condition, which can be accessed through predicate ``getCondition``. We use the reflexive transitive closure operator ``*`` applied to the ``getAChildExpr`` predicate to express the requirement that ``expr`` should be nested inside the loop condition. In particular, it can be the loop condition itself. @@ -120,7 +120,7 @@ Now we rewrite our query to make use of these new classes: not expr.getAnOperand().isCompileTimeConstant() select expr -➤ `See the full query in the query console on LGTM.com `__. +➤ `See the full query in the query console on LGTM.com `__. Further reading --------------- From b835d7879c7047300525c04cc1e9b73c11da78f0 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Sun, 5 Jul 2020 20:51:00 +0200 Subject: [PATCH 23/40] Update query console links in introduce-libraries-java.rst Removes 'eclipse-cdt/cdt' and 'gradle/gradle' from the queried projects because they cannot be queried currently, and instead queries all demo projects which are currently available. --- .../java/introduce-libraries-java.rst | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/docs/language/learn-ql/java/introduce-libraries-java.rst b/docs/language/learn-ql/java/introduce-libraries-java.rst index cb89c178472..ae7f97d6bea 100644 --- a/docs/language/learn-ql/java/introduce-libraries-java.rst +++ b/docs/language/learn-ql/java/introduce-libraries-java.rst @@ -49,7 +49,7 @@ Types Class ``Type`` has a number of subclasses for representing different kinds of types: -- ``PrimitiveType`` represents a `primitive type `__, that is, one of ``boolean``, ``byte``, ``char``, ``double``, ``float``, ``int``, ``long``, ``short``; QL also classifies ``void`` and ```` (the type of the ``null`` literal) as primitive types. +- ``PrimitiveType`` represents a `primitive type `__, that is, one of ``boolean``, ``byte``, ``char``, ``double``, ``float``, ``int``, ``long``, ``short``; QL also classifies ``void`` and ```` (the type of the ``null`` literal) as primitive types. - ``RefType`` represents a reference (that is, non-primitive) type; it in turn has several subclasses: - ``Class`` represents a Java class. @@ -68,7 +68,7 @@ For example, the following query finds all variables of type ``int`` in the prog pt.hasName("int") select v -➤ `See this in the query console on LGTM.com `__. You're likely to get many results when you run this query because most projects contain many variables of type ``int``. +➤ `See this in the query console on LGTM.com `__. You're likely to get many results when you run this query because most projects contain many variables of type ``int``. Reference types are also categorized according to their declaration scope: @@ -85,15 +85,15 @@ For instance, this query finds all top-level types whose name is not the same as where tl.getName() != tl.getCompilationUnit().getName() select tl -➤ `See this in the query console on LGTM.com `__. This pattern is seen in many projects. When we ran it on the LGTM.com demo projects, most of the projects had at least one instance of this problem in the source code. There were many more instances in the files referenced by the source code. +➤ `See this in the query console on LGTM.com `__. This pattern is seen in many projects. When we ran it on the LGTM.com demo projects, most of the projects had at least one instance of this problem in the source code. There were many more instances in the files referenced by the source code. Several more specialized classes are available as well: - ``TopLevelClass`` represents a class declared at the top-level of a compilation unit. -- ``NestedClass`` represents `a class declared inside another type `__, such as: +- ``NestedClass`` represents `a class declared inside another type `__, such as: - - A ``LocalClass``, which is `a class declared inside a method or constructor `__. - - An ``AnonymousClass``, which is an `anonymous class `__. + - A ``LocalClass``, which is `a class declared inside a method or constructor `__. + - An ``AnonymousClass``, which is an `anonymous class `__. Finally, the library also has a number of singleton classes that wrap frequently used Java standard library classes: ``TypeObject``, ``TypeCloneable``, ``TypeRuntime``, ``TypeSerializable``, ``TypeString``, ``TypeSystem`` and ``TypeClass``. Each CodeQL class represents the standard Java class suggested by its name. @@ -107,7 +107,7 @@ As an example, we can write a query that finds all nested classes that directly where nc.getASupertype() instanceof TypeObject select nc -➤ `See this in the query console on LGTM.com `__. You're likely to get many results when you run this query because many projects include nested classes that extend ``Object`` directly. +➤ `See this in the query console on LGTM.com `__. You're likely to get many results when you run this query because many projects include nested classes that extend ``Object`` directly. Generics ~~~~~~~~ @@ -141,7 +141,7 @@ For instance, we could use the following query to find all parameterized instanc pt.getSourceDeclaration() = map select pt -➤ `See this in the query console on LGTM.com `__. None of the LGTM.com demo projects contain parameterized instances of ``java.util.Map`` in their source code, but they all have results in reference files. +➤ `See this in the query console on LGTM.com `__. None of the LGTM.com demo projects contain parameterized instances of ``java.util.Map`` in their source code, but they all have results in reference files. In general, generic types may restrict which types a type parameter can be bound to. For instance, a type of maps from strings to numbers could be declared as follows: @@ -164,7 +164,7 @@ As an example, the following query finds all type variables with type bound ``Nu tb.getType().hasQualifiedName("java.lang", "Number") select tv -➤ `See this in the query console on LGTM.com `__. When we ran it on the LGTM.com demo projects, the *neo4j/neo4j*, *gradle/gradle* and *hibernate/hibernate-orm* projects all contained examples of this pattern. +➤ `See this in the query console on LGTM.com `__. When we ran it on the LGTM.com demo projects, the *neo4j/neo4j*, *hibernate/hibernate-orm* and *apache/hadoop* projects all contained examples of this pattern. For dealing with legacy code that is unaware of generics, every generic type has a "raw" version without any type parameters. In the CodeQL libraries, raw types are represented using class ``RawType``, which has the expected subclasses ``RawClass`` and ``RawInterface``. Again, there is a predicate ``getSourceDeclaration`` for obtaining the corresponding generic type. As an example, we can find variables of (raw) type ``Map``: @@ -177,7 +177,7 @@ For dealing with legacy code that is unaware of generics, every generic type has rt.getSourceDeclaration().hasQualifiedName("java.util", "Map") select v -➤ `See this in the query console on LGTM.com `__. Many projects have variables of raw type ``Map``. +➤ `See this in the query console on LGTM.com `__. Many projects have variables of raw type ``Map``. For example, in the following code snippet this query would find ``m1``, but not ``m2``: @@ -186,7 +186,7 @@ For example, in the following code snippet this query would find ``m1``, but not Map m1 = new HashMap(); Map m2 = new HashMap(); -Finally, variables can be declared to be of a `wildcard type `__: +Finally, variables can be declared to be of a `wildcard type `__: .. code-block:: java @@ -201,7 +201,7 @@ For more information on working with types, see the :doc:`article on Java types Variables ~~~~~~~~~ -Class ``Variable`` represents a variable `in the Java sense `__, which is either a member field of a class (whether static or not), or a local variable, or a parameter. Consequently, there are three subclasses catering to these special cases: +Class ``Variable`` represents a variable `in the Java sense `__, which is either a member field of a class (whether static or not), or a local variable, or a parameter. Consequently, there are three subclasses catering to these special cases: - ``Field`` represents a Java field. - ``LocalVariableDecl`` represents a local variable. @@ -228,7 +228,7 @@ For example, the following query finds all expressions whose parents are ``retur where e.getParent() instanceof ReturnStmt select e -➤ `See this in the query console on LGTM.com `__. Many projects have examples of ``return`` statements with child statements. +➤ `See this in the query console on LGTM.com `__. Many projects have examples of ``return`` statements with child expressions. Therefore, if the program contains a return statement ``return x + y;``, this query will return ``x + y``. @@ -242,7 +242,7 @@ As another example, the following query finds statements whose parent is an ``if where s.getParent() instanceof IfStmt select s -➤ `See this in the query console on LGTM.com `__. Many projects have examples of ``if`` statements with child statements. +➤ `See this in the query console on LGTM.com `__. Many projects have examples of ``if`` statements with child statements. This query will find both ``then`` branches and ``else`` branches of all ``if`` statements in the program. @@ -256,7 +256,7 @@ Finally, here is a query that finds method bodies: where s.getParent() instanceof Method select s -➤ `See this in the query console on LGTM.com `__. Most projects have many method bodies. +➤ `See this in the query console on LGTM.com `__. Most projects have many method bodies. As these examples show, the parent node of an expression is not always an expression: it may also be a statement, for example, an ``IfStmt``. Similarly, the parent node of a statement is not always a statement: it may also be a method or a constructor. To capture this, the QL Java library provides two abstract class ``ExprParent`` and ``StmtParent``, the former representing any node that may be the parent node of an expression, and the latter any node that may be the parent node of a statement. @@ -265,7 +265,7 @@ For more information on working with AST classes, see the :doc:`article on overf Metadata -------- -Java programs have several kinds of metadata, in addition to the program code proper. In particular, there are `annotations `__ and `Javadoc `__ comments. Since this metadata is interesting both for enhancing code analysis and as an analysis subject in its own right, the QL library defines classes for accessing it. +Java programs have several kinds of metadata, in addition to the program code proper. In particular, there are `annotations `__ and `Javadoc `__ comments. Since this metadata is interesting both for enhancing code analysis and as an analysis subject in its own right, the QL library defines classes for accessing it. For annotations, class ``Annotatable`` is a superclass of all program elements that can be annotated. This includes packages, reference types, fields, methods, constructors, and local variable declarations. For every such element, its predicate ``getAnAnnotation`` allows you to retrieve any annotations the element may have. For example, the following query finds all annotations on constructors: @@ -276,7 +276,7 @@ For annotations, class ``Annotatable`` is a superclass of all program elements t from Constructor c select c.getAnAnnotation() -➤ `See this in the query console on LGTM.com `__. The LGTM.com demo projects all use annotations, you can see examples where they are used to suppress warnings and mark code as deprecated. +➤ `See this in the query console on LGTM.com `__. The LGTM.com demo projects all use annotations, you can see examples where they are used to suppress warnings and mark code as deprecated. These annotations are represented by class ``Annotation``. An annotation is simply an expression whose type is an ``AnnotationType``. For example, you can amend this query so that it only reports deprecated constructors: @@ -290,7 +290,7 @@ These annotations are represented by class ``Annotation``. An annotation is simp anntp.hasQualifiedName("java.lang", "Deprecated") select ann -➤ `See this in the query console on LGTM.com `__. Only constructors with the ``@deprecated`` annotation are reported this time. +➤ `See this in the query console on LGTM.com `. @@ -305,7 +305,7 @@ For Javadoc, class ``Element`` has a member predicate ``getDoc`` that returns a jdoc = f.getDoc().getJavadoc() select jdoc -➤ `See this in the query console on LGTM.com `__. You can see this pattern in many projects. +➤ `See this in the query console on LGTM.com `__. You can see this pattern in many projects. Class ``Javadoc`` represents an entire Javadoc comment as a tree of ``JavadocElement`` nodes, which can be traversed using member predicates ``getAChild`` and ``getParent``. For instance, you could edit the query so that it finds all ``@author`` tags in Javadoc comments on private fields: @@ -319,7 +319,7 @@ Class ``Javadoc`` represents an entire Javadoc comment as a tree of ``JavadocEle at.getParent+() = jdoc select at -➤ `See this in the query console on LGTM.com `__. None of the LGTM.com demo projects uses the ``@author`` tag on private fields. +➤ `See this in the query console on LGTM.com `__. None of the LGTM.com demo projects uses the ``@author`` tag on private fields. .. pull-quote:: @@ -336,7 +336,7 @@ The standard QL Java library provides extensive support for computing metrics on Altogether, there are six such classes: ``MetricElement``, ``MetricPackage``, ``MetricRefType``, ``MetricField``, ``MetricCallable``, and ``MetricStmt``. The corresponding element classes each provide a member predicate ``getMetrics`` that can be used to obtain an instance of the delegate class, on which metric computations can then be performed. -For example, the following query finds methods with a `cyclomatic complexity `__ greater than 40: +For example, the following query finds methods with a `cyclomatic complexity `__ greater than 40: .. code-block:: ql @@ -347,7 +347,7 @@ For example, the following query finds methods with a `cyclomatic complexity 40 select m -➤ `See this in the query console on LGTM.com `__. Most large projects include some methods with a very high cyclomatic complexity. These methods are likely to be difficult to understand and test. +➤ `See this in the query console on LGTM.com `__. Most large projects include some methods with a very high cyclomatic complexity. These methods are likely to be difficult to understand and test. Call graph ---------- @@ -367,7 +367,7 @@ We can use predicate ``Call.getCallee`` to find out which method or constructor m.hasName("println") select c -➤ `See this in the query console on LGTM.com `__. The LGTM.com demo projects all include many calls to methods of this name. +➤ `See this in the query console on LGTM.com `__. The LGTM.com demo projects all include many calls to methods of this name. Conversely, ``Callable.getAReference`` returns a ``Call`` that refers to it. So we can find methods and constructors that are never called using this query: @@ -379,7 +379,7 @@ Conversely, ``Callable.getAReference`` returns a ``Call`` that refers to it. So where not exists(c.getAReference()) select c -➤ `See this in the query console on LGTM.com `__. The LGTM.com demo projects all appear to have many methods that are not called directly, but this is unlikely to be the whole story. To explore this area further, see :doc:`Navigating the call graph `. +➤ `See this in the query console on LGTM.com `__. The LGTM.com demo projects all appear to have many methods that are not called directly, but this is unlikely to be the whole story. To explore this area further, see :doc:`Navigating the call graph `. For more information about callables and calls, see the :doc:`article on the call graph `. From 7b4960c9a70971e8d02e04a7f3aab6d58bd85d8f Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Sun, 5 Jul 2020 20:54:05 +0200 Subject: [PATCH 24/40] Update query console links in javadoc.rst Removes 'gradle/gradle' from the queried projects because it cannot be queried currently, and instead queries all demo projects which are currently available. --- docs/language/learn-ql/java/javadoc.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/language/learn-ql/java/javadoc.rst b/docs/language/learn-ql/java/javadoc.rst index 5039439212a..1097d93c826 100644 --- a/docs/language/learn-ql/java/javadoc.rst +++ b/docs/language/learn-ql/java/javadoc.rst @@ -147,7 +147,7 @@ Now we can write a query for finding all callables ``c`` and ``@throws`` tags `` not mayThrow(c, exn) select tt, "Spurious @throws tag." -➤ `See this in the query console on LGTM.com `__. This finds several results in the LGTM.com demo projects. +➤ `See this in the query console on LGTM.com `__. This finds several results in the LGTM.com demo projects. Improvements ~~~~~~~~~~~~ @@ -214,7 +214,7 @@ The first case can be covered by changing ``getDocumentedException`` to use the (result.hasName(tt.getExceptionName()) and visibleIn(tt.getFile(), result)) } -➤ `See this in the query console on LGTM.com `__. This finds many fewer, more interesting results in the LGTM.com demo projects. +➤ `See this in the query console on LGTM.com `__. This finds many fewer, more interesting results in the LGTM.com demo projects. Currently, ``visibleIn`` only considers single-type imports, but you could extend it with support for other kinds of imports. From 2d9b52f750169a5aa193b1cc24c56d45eceb0bc0 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Sun, 5 Jul 2020 22:32:53 +0200 Subject: [PATCH 25/40] Update query console links in source-locations.rst, replace deprecated predicates Removes 'eclipse-cdt/cdt' and 'gradle/gradle' from the queried projects because they cannot be queried currently, and instead queries all demo projects which are currently available. --- .../learn-ql/java/source-locations.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/language/learn-ql/java/source-locations.rst b/docs/language/learn-ql/java/source-locations.rst index cba2881f740..3ab90906d99 100644 --- a/docs/language/learn-ql/java/source-locations.rst +++ b/docs/language/learn-ql/java/source-locations.rst @@ -54,17 +54,17 @@ In our example, the expression statement starts at line 5, column 3 (the first t Class ``File`` defines these member predicates: -- ``getFullName`` returns the fully qualified name of the file. +- ``getAbsolutePath`` returns the fully qualified name of the file. - ``getRelativePath`` returns the path of the file relative to the base directory of the source code. - ``getExtension`` returns the extension of the file. -- ``getShortName`` returns the base name of the file, without its extension. +- ``getStem`` returns the base name of the file, without its extension. In our example, assume file ``A.java`` is located in directory ``/home/testuser/code/pkg``, where ``/home/testuser/code`` is the base directory of the program being analyzed. Then, a ``File`` object for ``A.java`` returns: -- ``getFullName`` is ``/home/testuser/code/pkg/A.java``. +- ``getAbsolutePath`` is ``/home/testuser/code/pkg/A.java``. - ``getRelativePath`` is ``pkg/A.java``. - ``getExtension`` is ``java``. -- ``getShortName`` is ``A``. +- ``getStem`` is ``A``. Determining white space around an operator ------------------------------------------ @@ -110,7 +110,7 @@ Here's a first version of our query: wsinner > wsouter select outer, "Whitespace around nested operators contradicts precedence." -➤ `See this in the query console on LGTM.com `__. This query is likely to find results on most projects. +➤ `See this in the query console on LGTM.com `__. This query is likely to find results on most projects. The first conjunct of the ``where`` clause restricts ``inner`` to be an operand of ``outer``, the second conjunct binds ``wsinner`` and ``wsouter``, while the last conjunct selects the suspicious cases. @@ -141,9 +141,9 @@ Note that our predicate ``operatorWS`` computes the **total** amount of white sp wsinner > wsouter select outer, "Whitespace around nested operators contradicts precedence." -➤ `See this in the query console on LGTM.com `__. Any results will be refined by our changes to the query. +➤ `See this in the query console on LGTM.com `__. Any results will be refined by our changes to the query. -Another source of false positives are associative operators: in an expression of the form ``x + y+z``, the first plus is syntactically nested inside the second, since + in Java associates to the left; hence the expression is flagged as suspicious. But since + is associative to begin with, it does not matter which way around the operators are nested, so this is a false positive.To exclude these cases, let us define a new class identifying binary expressions with an associative operator: +Another source of false positives are associative operators: in an expression of the form ``x + y+z``, the first plus is syntactically nested inside the second, since + in Java associates to the left; hence the expression is flagged as suspicious. But since + is associative to begin with, it does not matter which way around the operators are nested, so this is a false positive. To exclude these cases, let us define a new class identifying binary expressions with an associative operator: .. code-block:: ql @@ -173,9 +173,9 @@ Now we can extend our query to discard results where the outer and the inner exp wsinner > wsouter select outer, "Whitespace around nested operators contradicts precedence." -➤ `See this in the query console on LGTM.com `__. +➤ `See this in the query console on LGTM.com `__. -Notice that we again use ``getOp``, this time to determine whether two binary expressions have the same operator. Running our improved query now finds the Java standard library bug described in the Overview. It also flags up the following suspicious code in `Hadoop HBase `__: +Notice that we again use ``getOp``, this time to determine whether two binary expressions have the same operator. Running our improved query now finds the Java standard library bug described in the Overview. It also flags up the following suspicious code in `Hadoop HBase `__: .. code-block:: java From 85853122711fbe899037e73445ba31c57fe850de Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 6 Jul 2020 10:33:49 +0200 Subject: [PATCH 26/40] fix typo in js/shell-command-constructed-from-input --- .../CWE-078/UnsafeShellCommandConstruction.ql | 2 +- .../UnsafeShellCommandConstruction.expected | 104 +++++++++--------- 2 files changed, 53 insertions(+), 53 deletions(-) diff --git a/javascript/ql/src/Security/CWE-078/UnsafeShellCommandConstruction.ql b/javascript/ql/src/Security/CWE-078/UnsafeShellCommandConstruction.ql index 4663c1c767c..f5d5113634d 100644 --- a/javascript/ql/src/Security/CWE-078/UnsafeShellCommandConstruction.ql +++ b/javascript/ql/src/Security/CWE-078/UnsafeShellCommandConstruction.ql @@ -18,6 +18,6 @@ import DataFlow::PathGraph from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, Sink sinkNode where cfg.hasFlowPath(source, sink) and sinkNode = sink.getNode() -select sinkNode.getAlertLocation(), source, sink, "$@ based on libary input is later used in $@.", +select sinkNode.getAlertLocation(), source, sink, "$@ based on library input is later used in $@.", sinkNode.getAlertLocation(), sinkNode.getSinkType(), sinkNode.getCommandExecution(), "shell command" diff --git a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected index 46221e489e8..b4f1bb5839a 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected @@ -389,55 +389,55 @@ edges | lib/lib.js:314:40:314:43 | name | lib/lib.js:320:23:320:26 | name | | lib/lib.js:314:40:314:43 | name | lib/lib.js:320:23:320:26 | name | #select -| lib/lib2.js:4:10:4:25 | "rm -rf " + name | lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name | $@ based on libary input is later used in $@. | lib/lib2.js:4:10:4:25 | "rm -rf " + name | String concatenation | lib/lib2.js:4:2:4:26 | cp.exec ... + name) | shell command | -| lib/lib2.js:8:10:8:25 | "rm -rf " + name | lib/lib2.js:7:32:7:35 | name | lib/lib2.js:8:22:8:25 | name | $@ based on libary input is later used in $@. | lib/lib2.js:8:10:8:25 | "rm -rf " + name | String concatenation | lib/lib2.js:8:2:8:26 | cp.exec ... + name) | shell command | -| lib/lib.js:4:10:4:25 | "rm -rf " + name | lib/lib.js:3:28:3:31 | name | lib/lib.js:4:22:4:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:4:10:4:25 | "rm -rf " + name | String concatenation | lib/lib.js:4:2:4:26 | cp.exec ... + name) | shell command | -| lib/lib.js:11:10:11:25 | "rm -rf " + name | lib/lib.js:10:32:10:35 | name | lib/lib.js:11:22:11:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:11:10:11:25 | "rm -rf " + name | String concatenation | lib/lib.js:11:2:11:26 | cp.exec ... + name) | shell command | -| lib/lib.js:15:10:15:25 | "rm -rf " + name | lib/lib.js:14:36:14:39 | name | lib/lib.js:15:22:15:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:15:10:15:25 | "rm -rf " + name | String concatenation | lib/lib.js:15:2:15:26 | cp.exec ... + name) | shell command | -| lib/lib.js:20:10:20:25 | "rm -rf " + name | lib/lib.js:19:34:19:37 | name | lib/lib.js:20:22:20:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:20:10:20:25 | "rm -rf " + name | String concatenation | lib/lib.js:20:2:20:26 | cp.exec ... + name) | shell command | -| lib/lib.js:27:10:27:25 | "rm -rf " + name | lib/lib.js:26:35:26:38 | name | lib/lib.js:27:22:27:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:27:10:27:25 | "rm -rf " + name | String concatenation | lib/lib.js:27:2:27:26 | cp.exec ... + name) | shell command | -| lib/lib.js:35:11:35:26 | "rm -rf " + name | lib/lib.js:34:14:34:17 | name | lib/lib.js:35:23:35:26 | name | $@ based on libary input is later used in $@. | lib/lib.js:35:11:35:26 | "rm -rf " + name | String concatenation | lib/lib.js:35:3:35:27 | cp.exec ... + name) | shell command | -| lib/lib.js:38:11:38:26 | "rm -rf " + name | lib/lib.js:37:13:37:16 | name | lib/lib.js:38:23:38:26 | name | $@ based on libary input is later used in $@. | lib/lib.js:38:11:38:26 | "rm -rf " + name | String concatenation | lib/lib.js:38:3:38:27 | cp.exec ... + name) | shell command | -| lib/lib.js:41:11:41:26 | "rm -rf " + name | lib/lib.js:40:6:40:9 | name | lib/lib.js:41:23:41:26 | name | $@ based on libary input is later used in $@. | lib/lib.js:41:11:41:26 | "rm -rf " + name | String concatenation | lib/lib.js:41:3:41:27 | cp.exec ... + name) | shell command | -| lib/lib.js:50:35:50:50 | "rm -rf " + name | lib/lib.js:49:31:49:34 | name | lib/lib.js:50:47:50:50 | name | $@ based on libary input is later used in $@. | lib/lib.js:50:35:50:50 | "rm -rf " + name | String concatenation | lib/lib.js:50:2:50:51 | require ... + name) | shell command | -| lib/lib.js:54:13:54:28 | "rm -rf " + name | lib/lib.js:53:33:53:36 | name | lib/lib.js:54:25:54:28 | name | $@ based on libary input is later used in $@. | lib/lib.js:54:13:54:28 | "rm -rf " + name | String concatenation | lib/lib.js:55:2:55:14 | cp.exec(cmd1) | shell command | -| lib/lib.js:57:13:57:28 | "rm -rf " + name | lib/lib.js:53:33:53:36 | name | lib/lib.js:57:25:57:28 | name | $@ based on libary input is later used in $@. | lib/lib.js:57:13:57:28 | "rm -rf " + name | String concatenation | lib/lib.js:59:3:59:14 | cp.exec(cmd) | shell command | -| lib/lib.js:65:10:65:25 | "rm -rf " + name | lib/lib.js:64:41:64:44 | name | lib/lib.js:65:22:65:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:65:10:65:25 | "rm -rf " + name | String concatenation | lib/lib.js:65:2:65:26 | cp.exec ... + name) | shell command | -| lib/lib.js:71:10:71:31 | "cat /f ... + name | lib/lib.js:64:41:64:44 | name | lib/lib.js:71:28:71:31 | name | $@ based on libary input is later used in $@. | lib/lib.js:71:10:71:31 | "cat /f ... + name | String concatenation | lib/lib.js:71:2:71:32 | cp.exec ... + name) | shell command | -| lib/lib.js:73:10:73:31 | "cat \\" ... + "\\"" | lib/lib.js:64:41:64:44 | name | lib/lib.js:73:21:73:24 | name | $@ based on libary input is later used in $@. | lib/lib.js:73:10:73:31 | "cat \\" ... + "\\"" | String concatenation | lib/lib.js:73:2:73:32 | cp.exec ... + "\\"") | shell command | -| lib/lib.js:75:10:75:29 | "cat '" + name + "'" | lib/lib.js:64:41:64:44 | name | lib/lib.js:75:20:75:23 | name | $@ based on libary input is later used in $@. | lib/lib.js:75:10:75:29 | "cat '" + name + "'" | String concatenation | lib/lib.js:75:2:75:30 | cp.exec ... + "'") | shell command | -| lib/lib.js:77:10:77:37 | "cat '/ ... e + "'" | lib/lib.js:64:41:64:44 | name | lib/lib.js:77:28:77:31 | name | $@ based on libary input is later used in $@. | lib/lib.js:77:10:77:37 | "cat '/ ... e + "'" | String concatenation | lib/lib.js:77:2:77:38 | cp.exec ... + "'") | shell command | -| lib/lib.js:83:10:83:25 | "rm -rf " + name | lib/lib.js:82:35:82:38 | name | lib/lib.js:83:22:83:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:83:10:83:25 | "rm -rf " + name | String concatenation | lib/lib.js:83:2:83:26 | cp.exec ... + name) | shell command | -| lib/lib.js:86:13:86:16 | name | lib/lib.js:82:35:82:38 | name | lib/lib.js:86:13:86:16 | name | $@ based on libary input is later used in $@. | lib/lib.js:86:13:86:16 | name | Array element | lib/lib.js:87:2:87:25 | cp.exec ... n(" ")) | shell command | -| lib/lib.js:89:21:89:24 | name | lib/lib.js:82:35:82:38 | name | lib/lib.js:89:21:89:24 | name | $@ based on libary input is later used in $@. | lib/lib.js:89:21:89:24 | name | Array element | lib/lib.js:89:2:89:36 | cp.exec ... n(" ")) | shell command | -| lib/lib.js:91:21:91:38 | "\\"" + name + "\\"" | lib/lib.js:82:35:82:38 | name | lib/lib.js:91:21:91:38 | "\\"" + name + "\\"" | $@ based on libary input is later used in $@. | lib/lib.js:91:21:91:38 | "\\"" + name + "\\"" | Array element | lib/lib.js:91:2:91:50 | cp.exec ... n(" ")) | shell command | -| lib/lib.js:98:35:98:38 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:98:35:98:38 | name | $@ based on libary input is later used in $@. | lib/lib.js:98:35:98:38 | name | Formatted string | lib/lib.js:98:2:98:40 | cp.exec ... name)) | shell command | -| lib/lib.js:100:37:100:40 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:100:37:100:40 | name | $@ based on libary input is later used in $@. | lib/lib.js:100:37:100:40 | name | Formatted string | lib/lib.js:100:2:100:42 | cp.exec ... name)) | shell command | -| lib/lib.js:102:46:102:49 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:102:46:102:49 | name | $@ based on libary input is later used in $@. | lib/lib.js:102:46:102:49 | name | Formatted string | lib/lib.js:102:2:102:51 | cp.exec ... name)) | shell command | -| lib/lib.js:108:41:108:44 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:108:41:108:44 | name | $@ based on libary input is later used in $@. | lib/lib.js:108:41:108:44 | name | Formatted string | lib/lib.js:108:2:108:46 | cp.exec ... name)) | shell command | -| lib/lib.js:112:10:112:25 | "rm -rf " + name | lib/lib.js:111:34:111:37 | name | lib/lib.js:112:22:112:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:112:10:112:25 | "rm -rf " + name | String concatenation | lib/lib.js:112:2:112:26 | cp.exec ... + name) | shell command | -| lib/lib.js:121:10:121:25 | "rm -rf " + name | lib/lib.js:120:33:120:36 | name | lib/lib.js:121:22:121:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:121:10:121:25 | "rm -rf " + name | String concatenation | lib/lib.js:121:2:121:26 | cp.exec ... + name) | shell command | -| lib/lib.js:131:11:131:26 | "rm -rf " + name | lib/lib.js:130:6:130:9 | name | lib/lib.js:131:23:131:26 | name | $@ based on libary input is later used in $@. | lib/lib.js:131:11:131:26 | "rm -rf " + name | String concatenation | lib/lib.js:131:3:131:27 | cp.exec ... + name) | shell command | -| lib/lib.js:149:12:149:27 | "rm -rf " + name | lib/lib.js:148:37:148:40 | name | lib/lib.js:149:24:149:27 | name | $@ based on libary input is later used in $@. | lib/lib.js:149:12:149:27 | "rm -rf " + name | String concatenation | lib/lib.js:152:2:152:23 | cp.spaw ... gs, cb) | shell command | -| lib/lib.js:161:13:161:28 | "rm -rf " + name | lib/lib.js:155:38:155:41 | name | lib/lib.js:161:25:161:28 | name | $@ based on libary input is later used in $@. | lib/lib.js:161:13:161:28 | "rm -rf " + name | String concatenation | lib/lib.js:163:2:167:2 | cp.spaw ... t' }\\n\\t) | shell command | -| lib/lib.js:173:10:173:23 | "fo \| " + name | lib/lib.js:170:41:170:44 | name | lib/lib.js:173:20:173:23 | name | $@ based on libary input is later used in $@. | lib/lib.js:173:10:173:23 | "fo \| " + name | String concatenation | lib/lib.js:173:2:173:24 | cp.exec ... + name) | shell command | -| lib/lib.js:182:10:182:27 | "rm -rf " + broken | lib/lib.js:177:38:177:41 | name | lib/lib.js:182:22:182:27 | broken | $@ based on libary input is later used in $@. | lib/lib.js:182:10:182:27 | "rm -rf " + broken | String concatenation | lib/lib.js:182:2:182:28 | cp.exec ... broken) | shell command | -| lib/lib.js:187:10:187:25 | "rm -rf " + name | lib/lib.js:186:34:186:37 | name | lib/lib.js:187:22:187:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:187:10:187:25 | "rm -rf " + name | String concatenation | lib/lib.js:187:2:187:26 | cp.exec ... + name) | shell command | -| lib/lib.js:190:11:190:26 | "rm -rf " + name | lib/lib.js:186:34:186:37 | name | lib/lib.js:190:23:190:26 | name | $@ based on libary input is later used in $@. | lib/lib.js:190:11:190:26 | "rm -rf " + name | String concatenation | lib/lib.js:190:3:190:27 | cp.exec ... + name) | shell command | -| lib/lib.js:197:10:197:25 | "rm -rf " + name | lib/lib.js:196:45:196:48 | name | lib/lib.js:197:22:197:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:197:10:197:25 | "rm -rf " + name | String concatenation | lib/lib.js:197:2:197:26 | cp.exec ... + name) | shell command | -| lib/lib.js:200:11:200:26 | "rm -rf " + name | lib/lib.js:196:45:196:48 | name | lib/lib.js:200:23:200:26 | name | $@ based on libary input is later used in $@. | lib/lib.js:200:11:200:26 | "rm -rf " + name | String concatenation | lib/lib.js:200:3:200:27 | cp.exec ... + name) | shell command | -| lib/lib.js:207:10:207:25 | "rm -rf " + name | lib/lib.js:206:45:206:48 | name | lib/lib.js:207:22:207:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:207:10:207:25 | "rm -rf " + name | String concatenation | lib/lib.js:207:2:207:26 | cp.exec ... + name) | shell command | -| lib/lib.js:212:11:212:26 | "rm -rf " + name | lib/lib.js:206:45:206:48 | name | lib/lib.js:212:23:212:26 | name | $@ based on libary input is later used in $@. | lib/lib.js:212:11:212:26 | "rm -rf " + name | String concatenation | lib/lib.js:212:3:212:27 | cp.exec ... + name) | shell command | -| lib/lib.js:217:10:217:25 | "rm -rf " + name | lib/lib.js:216:39:216:42 | name | lib/lib.js:217:22:217:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:217:10:217:25 | "rm -rf " + name | String concatenation | lib/lib.js:217:2:217:26 | cp.exec ... + name) | shell command | -| lib/lib.js:220:11:220:26 | "rm -rf " + name | lib/lib.js:216:39:216:42 | name | lib/lib.js:220:23:220:26 | name | $@ based on libary input is later used in $@. | lib/lib.js:220:11:220:26 | "rm -rf " + name | String concatenation | lib/lib.js:220:3:220:27 | cp.exec ... + name) | shell command | -| lib/lib.js:224:10:224:25 | "rm -rf " + name | lib/lib.js:216:39:216:42 | name | lib/lib.js:224:22:224:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:224:10:224:25 | "rm -rf " + name | String concatenation | lib/lib.js:224:2:224:26 | cp.exec ... + name) | shell command | -| lib/lib.js:228:10:228:25 | "rm -rf " + name | lib/lib.js:227:39:227:42 | name | lib/lib.js:228:22:228:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:228:10:228:25 | "rm -rf " + name | String concatenation | lib/lib.js:228:2:228:26 | cp.exec ... + name) | shell command | -| lib/lib.js:236:10:236:25 | "rm -rf " + name | lib/lib.js:227:39:227:42 | name | lib/lib.js:236:22:236:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:236:10:236:25 | "rm -rf " + name | String concatenation | lib/lib.js:236:2:236:26 | cp.exec ... + name) | shell command | -| lib/lib.js:249:10:249:25 | "rm -rf " + name | lib/lib.js:248:42:248:45 | name | lib/lib.js:249:22:249:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:249:10:249:25 | "rm -rf " + name | String concatenation | lib/lib.js:249:2:249:26 | cp.exec ... + name) | shell command | -| lib/lib.js:258:10:258:25 | "rm -rf " + name | lib/lib.js:257:35:257:38 | name | lib/lib.js:258:22:258:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:258:10:258:25 | "rm -rf " + name | String concatenation | lib/lib.js:258:2:258:26 | cp.exec ... + name) | shell command | -| lib/lib.js:261:11:261:33 | "rm -rf ... + name | lib/lib.js:257:35:257:38 | name | lib/lib.js:261:30:261:33 | name | $@ based on libary input is later used in $@. | lib/lib.js:261:11:261:33 | "rm -rf ... + name | String concatenation | lib/lib.js:261:3:261:34 | cp.exec ... + name) | shell command | -| lib/lib.js:268:10:268:32 | "rm -rf ... version | lib/lib.js:267:46:267:48 | obj | lib/lib.js:268:22:268:32 | obj.version | $@ based on libary input is later used in $@. | lib/lib.js:268:10:268:32 | "rm -rf ... version | String concatenation | lib/lib.js:268:2:268:33 | cp.exec ... ersion) | shell command | -| lib/lib.js:277:11:277:30 | "rm -rf " + opts.bla | lib/lib.js:276:8:276:11 | opts | lib/lib.js:277:23:277:30 | opts.bla | $@ based on libary input is later used in $@. | lib/lib.js:277:11:277:30 | "rm -rf " + opts.bla | String concatenation | lib/lib.js:277:3:277:31 | cp.exec ... ts.bla) | shell command | -| lib/lib.js:308:11:308:26 | "rm -rf " + name | lib/lib.js:307:39:307:42 | name | lib/lib.js:308:23:308:26 | name | $@ based on libary input is later used in $@. | lib/lib.js:308:11:308:26 | "rm -rf " + name | String concatenation | lib/lib.js:308:3:308:27 | cp.exec ... + name) | shell command | -| lib/lib.js:315:10:315:25 | "rm -rf " + name | lib/lib.js:314:40:314:43 | name | lib/lib.js:315:22:315:25 | name | $@ based on libary input is later used in $@. | lib/lib.js:315:10:315:25 | "rm -rf " + name | String concatenation | lib/lib.js:315:2:315:26 | cp.exec ... + name) | shell command | -| lib/lib.js:320:11:320:26 | "rm -rf " + name | lib/lib.js:314:40:314:43 | name | lib/lib.js:320:23:320:26 | name | $@ based on libary input is later used in $@. | lib/lib.js:320:11:320:26 | "rm -rf " + name | String concatenation | lib/lib.js:320:3:320:27 | cp.exec ... + name) | shell command | +| lib/lib2.js:4:10:4:25 | "rm -rf " + name | lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name | $@ based on library input is later used in $@. | lib/lib2.js:4:10:4:25 | "rm -rf " + name | String concatenation | lib/lib2.js:4:2:4:26 | cp.exec ... + name) | shell command | +| lib/lib2.js:8:10:8:25 | "rm -rf " + name | lib/lib2.js:7:32:7:35 | name | lib/lib2.js:8:22:8:25 | name | $@ based on library input is later used in $@. | lib/lib2.js:8:10:8:25 | "rm -rf " + name | String concatenation | lib/lib2.js:8:2:8:26 | cp.exec ... + name) | shell command | +| lib/lib.js:4:10:4:25 | "rm -rf " + name | lib/lib.js:3:28:3:31 | name | lib/lib.js:4:22:4:25 | name | $@ based on library input is later used in $@. | lib/lib.js:4:10:4:25 | "rm -rf " + name | String concatenation | lib/lib.js:4:2:4:26 | cp.exec ... + name) | shell command | +| lib/lib.js:11:10:11:25 | "rm -rf " + name | lib/lib.js:10:32:10:35 | name | lib/lib.js:11:22:11:25 | name | $@ based on library input is later used in $@. | lib/lib.js:11:10:11:25 | "rm -rf " + name | String concatenation | lib/lib.js:11:2:11:26 | cp.exec ... + name) | shell command | +| lib/lib.js:15:10:15:25 | "rm -rf " + name | lib/lib.js:14:36:14:39 | name | lib/lib.js:15:22:15:25 | name | $@ based on library input is later used in $@. | lib/lib.js:15:10:15:25 | "rm -rf " + name | String concatenation | lib/lib.js:15:2:15:26 | cp.exec ... + name) | shell command | +| lib/lib.js:20:10:20:25 | "rm -rf " + name | lib/lib.js:19:34:19:37 | name | lib/lib.js:20:22:20:25 | name | $@ based on library input is later used in $@. | lib/lib.js:20:10:20:25 | "rm -rf " + name | String concatenation | lib/lib.js:20:2:20:26 | cp.exec ... + name) | shell command | +| lib/lib.js:27:10:27:25 | "rm -rf " + name | lib/lib.js:26:35:26:38 | name | lib/lib.js:27:22:27:25 | name | $@ based on library input is later used in $@. | lib/lib.js:27:10:27:25 | "rm -rf " + name | String concatenation | lib/lib.js:27:2:27:26 | cp.exec ... + name) | shell command | +| lib/lib.js:35:11:35:26 | "rm -rf " + name | lib/lib.js:34:14:34:17 | name | lib/lib.js:35:23:35:26 | name | $@ based on library input is later used in $@. | lib/lib.js:35:11:35:26 | "rm -rf " + name | String concatenation | lib/lib.js:35:3:35:27 | cp.exec ... + name) | shell command | +| lib/lib.js:38:11:38:26 | "rm -rf " + name | lib/lib.js:37:13:37:16 | name | lib/lib.js:38:23:38:26 | name | $@ based on library input is later used in $@. | lib/lib.js:38:11:38:26 | "rm -rf " + name | String concatenation | lib/lib.js:38:3:38:27 | cp.exec ... + name) | shell command | +| lib/lib.js:41:11:41:26 | "rm -rf " + name | lib/lib.js:40:6:40:9 | name | lib/lib.js:41:23:41:26 | name | $@ based on library input is later used in $@. | lib/lib.js:41:11:41:26 | "rm -rf " + name | String concatenation | lib/lib.js:41:3:41:27 | cp.exec ... + name) | shell command | +| lib/lib.js:50:35:50:50 | "rm -rf " + name | lib/lib.js:49:31:49:34 | name | lib/lib.js:50:47:50:50 | name | $@ based on library input is later used in $@. | lib/lib.js:50:35:50:50 | "rm -rf " + name | String concatenation | lib/lib.js:50:2:50:51 | require ... + name) | shell command | +| lib/lib.js:54:13:54:28 | "rm -rf " + name | lib/lib.js:53:33:53:36 | name | lib/lib.js:54:25:54:28 | name | $@ based on library input is later used in $@. | lib/lib.js:54:13:54:28 | "rm -rf " + name | String concatenation | lib/lib.js:55:2:55:14 | cp.exec(cmd1) | shell command | +| lib/lib.js:57:13:57:28 | "rm -rf " + name | lib/lib.js:53:33:53:36 | name | lib/lib.js:57:25:57:28 | name | $@ based on library input is later used in $@. | lib/lib.js:57:13:57:28 | "rm -rf " + name | String concatenation | lib/lib.js:59:3:59:14 | cp.exec(cmd) | shell command | +| lib/lib.js:65:10:65:25 | "rm -rf " + name | lib/lib.js:64:41:64:44 | name | lib/lib.js:65:22:65:25 | name | $@ based on library input is later used in $@. | lib/lib.js:65:10:65:25 | "rm -rf " + name | String concatenation | lib/lib.js:65:2:65:26 | cp.exec ... + name) | shell command | +| lib/lib.js:71:10:71:31 | "cat /f ... + name | lib/lib.js:64:41:64:44 | name | lib/lib.js:71:28:71:31 | name | $@ based on library input is later used in $@. | lib/lib.js:71:10:71:31 | "cat /f ... + name | String concatenation | lib/lib.js:71:2:71:32 | cp.exec ... + name) | shell command | +| lib/lib.js:73:10:73:31 | "cat \\" ... + "\\"" | lib/lib.js:64:41:64:44 | name | lib/lib.js:73:21:73:24 | name | $@ based on library input is later used in $@. | lib/lib.js:73:10:73:31 | "cat \\" ... + "\\"" | String concatenation | lib/lib.js:73:2:73:32 | cp.exec ... + "\\"") | shell command | +| lib/lib.js:75:10:75:29 | "cat '" + name + "'" | lib/lib.js:64:41:64:44 | name | lib/lib.js:75:20:75:23 | name | $@ based on library input is later used in $@. | lib/lib.js:75:10:75:29 | "cat '" + name + "'" | String concatenation | lib/lib.js:75:2:75:30 | cp.exec ... + "'") | shell command | +| lib/lib.js:77:10:77:37 | "cat '/ ... e + "'" | lib/lib.js:64:41:64:44 | name | lib/lib.js:77:28:77:31 | name | $@ based on library input is later used in $@. | lib/lib.js:77:10:77:37 | "cat '/ ... e + "'" | String concatenation | lib/lib.js:77:2:77:38 | cp.exec ... + "'") | shell command | +| lib/lib.js:83:10:83:25 | "rm -rf " + name | lib/lib.js:82:35:82:38 | name | lib/lib.js:83:22:83:25 | name | $@ based on library input is later used in $@. | lib/lib.js:83:10:83:25 | "rm -rf " + name | String concatenation | lib/lib.js:83:2:83:26 | cp.exec ... + name) | shell command | +| lib/lib.js:86:13:86:16 | name | lib/lib.js:82:35:82:38 | name | lib/lib.js:86:13:86:16 | name | $@ based on library input is later used in $@. | lib/lib.js:86:13:86:16 | name | Array element | lib/lib.js:87:2:87:25 | cp.exec ... n(" ")) | shell command | +| lib/lib.js:89:21:89:24 | name | lib/lib.js:82:35:82:38 | name | lib/lib.js:89:21:89:24 | name | $@ based on library input is later used in $@. | lib/lib.js:89:21:89:24 | name | Array element | lib/lib.js:89:2:89:36 | cp.exec ... n(" ")) | shell command | +| lib/lib.js:91:21:91:38 | "\\"" + name + "\\"" | lib/lib.js:82:35:82:38 | name | lib/lib.js:91:21:91:38 | "\\"" + name + "\\"" | $@ based on library input is later used in $@. | lib/lib.js:91:21:91:38 | "\\"" + name + "\\"" | Array element | lib/lib.js:91:2:91:50 | cp.exec ... n(" ")) | shell command | +| lib/lib.js:98:35:98:38 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:98:35:98:38 | name | $@ based on library input is later used in $@. | lib/lib.js:98:35:98:38 | name | Formatted string | lib/lib.js:98:2:98:40 | cp.exec ... name)) | shell command | +| lib/lib.js:100:37:100:40 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:100:37:100:40 | name | $@ based on library input is later used in $@. | lib/lib.js:100:37:100:40 | name | Formatted string | lib/lib.js:100:2:100:42 | cp.exec ... name)) | shell command | +| lib/lib.js:102:46:102:49 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:102:46:102:49 | name | $@ based on library input is later used in $@. | lib/lib.js:102:46:102:49 | name | Formatted string | lib/lib.js:102:2:102:51 | cp.exec ... name)) | shell command | +| lib/lib.js:108:41:108:44 | name | lib/lib.js:97:35:97:38 | name | lib/lib.js:108:41:108:44 | name | $@ based on library input is later used in $@. | lib/lib.js:108:41:108:44 | name | Formatted string | lib/lib.js:108:2:108:46 | cp.exec ... name)) | shell command | +| lib/lib.js:112:10:112:25 | "rm -rf " + name | lib/lib.js:111:34:111:37 | name | lib/lib.js:112:22:112:25 | name | $@ based on library input is later used in $@. | lib/lib.js:112:10:112:25 | "rm -rf " + name | String concatenation | lib/lib.js:112:2:112:26 | cp.exec ... + name) | shell command | +| lib/lib.js:121:10:121:25 | "rm -rf " + name | lib/lib.js:120:33:120:36 | name | lib/lib.js:121:22:121:25 | name | $@ based on library input is later used in $@. | lib/lib.js:121:10:121:25 | "rm -rf " + name | String concatenation | lib/lib.js:121:2:121:26 | cp.exec ... + name) | shell command | +| lib/lib.js:131:11:131:26 | "rm -rf " + name | lib/lib.js:130:6:130:9 | name | lib/lib.js:131:23:131:26 | name | $@ based on library input is later used in $@. | lib/lib.js:131:11:131:26 | "rm -rf " + name | String concatenation | lib/lib.js:131:3:131:27 | cp.exec ... + name) | shell command | +| lib/lib.js:149:12:149:27 | "rm -rf " + name | lib/lib.js:148:37:148:40 | name | lib/lib.js:149:24:149:27 | name | $@ based on library input is later used in $@. | lib/lib.js:149:12:149:27 | "rm -rf " + name | String concatenation | lib/lib.js:152:2:152:23 | cp.spaw ... gs, cb) | shell command | +| lib/lib.js:161:13:161:28 | "rm -rf " + name | lib/lib.js:155:38:155:41 | name | lib/lib.js:161:25:161:28 | name | $@ based on library input is later used in $@. | lib/lib.js:161:13:161:28 | "rm -rf " + name | String concatenation | lib/lib.js:163:2:167:2 | cp.spaw ... t' }\\n\\t) | shell command | +| lib/lib.js:173:10:173:23 | "fo \| " + name | lib/lib.js:170:41:170:44 | name | lib/lib.js:173:20:173:23 | name | $@ based on library input is later used in $@. | lib/lib.js:173:10:173:23 | "fo \| " + name | String concatenation | lib/lib.js:173:2:173:24 | cp.exec ... + name) | shell command | +| lib/lib.js:182:10:182:27 | "rm -rf " + broken | lib/lib.js:177:38:177:41 | name | lib/lib.js:182:22:182:27 | broken | $@ based on library input is later used in $@. | lib/lib.js:182:10:182:27 | "rm -rf " + broken | String concatenation | lib/lib.js:182:2:182:28 | cp.exec ... broken) | shell command | +| lib/lib.js:187:10:187:25 | "rm -rf " + name | lib/lib.js:186:34:186:37 | name | lib/lib.js:187:22:187:25 | name | $@ based on library input is later used in $@. | lib/lib.js:187:10:187:25 | "rm -rf " + name | String concatenation | lib/lib.js:187:2:187:26 | cp.exec ... + name) | shell command | +| lib/lib.js:190:11:190:26 | "rm -rf " + name | lib/lib.js:186:34:186:37 | name | lib/lib.js:190:23:190:26 | name | $@ based on library input is later used in $@. | lib/lib.js:190:11:190:26 | "rm -rf " + name | String concatenation | lib/lib.js:190:3:190:27 | cp.exec ... + name) | shell command | +| lib/lib.js:197:10:197:25 | "rm -rf " + name | lib/lib.js:196:45:196:48 | name | lib/lib.js:197:22:197:25 | name | $@ based on library input is later used in $@. | lib/lib.js:197:10:197:25 | "rm -rf " + name | String concatenation | lib/lib.js:197:2:197:26 | cp.exec ... + name) | shell command | +| lib/lib.js:200:11:200:26 | "rm -rf " + name | lib/lib.js:196:45:196:48 | name | lib/lib.js:200:23:200:26 | name | $@ based on library input is later used in $@. | lib/lib.js:200:11:200:26 | "rm -rf " + name | String concatenation | lib/lib.js:200:3:200:27 | cp.exec ... + name) | shell command | +| lib/lib.js:207:10:207:25 | "rm -rf " + name | lib/lib.js:206:45:206:48 | name | lib/lib.js:207:22:207:25 | name | $@ based on library input is later used in $@. | lib/lib.js:207:10:207:25 | "rm -rf " + name | String concatenation | lib/lib.js:207:2:207:26 | cp.exec ... + name) | shell command | +| lib/lib.js:212:11:212:26 | "rm -rf " + name | lib/lib.js:206:45:206:48 | name | lib/lib.js:212:23:212:26 | name | $@ based on library input is later used in $@. | lib/lib.js:212:11:212:26 | "rm -rf " + name | String concatenation | lib/lib.js:212:3:212:27 | cp.exec ... + name) | shell command | +| lib/lib.js:217:10:217:25 | "rm -rf " + name | lib/lib.js:216:39:216:42 | name | lib/lib.js:217:22:217:25 | name | $@ based on library input is later used in $@. | lib/lib.js:217:10:217:25 | "rm -rf " + name | String concatenation | lib/lib.js:217:2:217:26 | cp.exec ... + name) | shell command | +| lib/lib.js:220:11:220:26 | "rm -rf " + name | lib/lib.js:216:39:216:42 | name | lib/lib.js:220:23:220:26 | name | $@ based on library input is later used in $@. | lib/lib.js:220:11:220:26 | "rm -rf " + name | String concatenation | lib/lib.js:220:3:220:27 | cp.exec ... + name) | shell command | +| lib/lib.js:224:10:224:25 | "rm -rf " + name | lib/lib.js:216:39:216:42 | name | lib/lib.js:224:22:224:25 | name | $@ based on library input is later used in $@. | lib/lib.js:224:10:224:25 | "rm -rf " + name | String concatenation | lib/lib.js:224:2:224:26 | cp.exec ... + name) | shell command | +| lib/lib.js:228:10:228:25 | "rm -rf " + name | lib/lib.js:227:39:227:42 | name | lib/lib.js:228:22:228:25 | name | $@ based on library input is later used in $@. | lib/lib.js:228:10:228:25 | "rm -rf " + name | String concatenation | lib/lib.js:228:2:228:26 | cp.exec ... + name) | shell command | +| lib/lib.js:236:10:236:25 | "rm -rf " + name | lib/lib.js:227:39:227:42 | name | lib/lib.js:236:22:236:25 | name | $@ based on library input is later used in $@. | lib/lib.js:236:10:236:25 | "rm -rf " + name | String concatenation | lib/lib.js:236:2:236:26 | cp.exec ... + name) | shell command | +| lib/lib.js:249:10:249:25 | "rm -rf " + name | lib/lib.js:248:42:248:45 | name | lib/lib.js:249:22:249:25 | name | $@ based on library input is later used in $@. | lib/lib.js:249:10:249:25 | "rm -rf " + name | String concatenation | lib/lib.js:249:2:249:26 | cp.exec ... + name) | shell command | +| lib/lib.js:258:10:258:25 | "rm -rf " + name | lib/lib.js:257:35:257:38 | name | lib/lib.js:258:22:258:25 | name | $@ based on library input is later used in $@. | lib/lib.js:258:10:258:25 | "rm -rf " + name | String concatenation | lib/lib.js:258:2:258:26 | cp.exec ... + name) | shell command | +| lib/lib.js:261:11:261:33 | "rm -rf ... + name | lib/lib.js:257:35:257:38 | name | lib/lib.js:261:30:261:33 | name | $@ based on library input is later used in $@. | lib/lib.js:261:11:261:33 | "rm -rf ... + name | String concatenation | lib/lib.js:261:3:261:34 | cp.exec ... + name) | shell command | +| lib/lib.js:268:10:268:32 | "rm -rf ... version | lib/lib.js:267:46:267:48 | obj | lib/lib.js:268:22:268:32 | obj.version | $@ based on library input is later used in $@. | lib/lib.js:268:10:268:32 | "rm -rf ... version | String concatenation | lib/lib.js:268:2:268:33 | cp.exec ... ersion) | shell command | +| lib/lib.js:277:11:277:30 | "rm -rf " + opts.bla | lib/lib.js:276:8:276:11 | opts | lib/lib.js:277:23:277:30 | opts.bla | $@ based on library input is later used in $@. | lib/lib.js:277:11:277:30 | "rm -rf " + opts.bla | String concatenation | lib/lib.js:277:3:277:31 | cp.exec ... ts.bla) | shell command | +| lib/lib.js:308:11:308:26 | "rm -rf " + name | lib/lib.js:307:39:307:42 | name | lib/lib.js:308:23:308:26 | name | $@ based on library input is later used in $@. | lib/lib.js:308:11:308:26 | "rm -rf " + name | String concatenation | lib/lib.js:308:3:308:27 | cp.exec ... + name) | shell command | +| lib/lib.js:315:10:315:25 | "rm -rf " + name | lib/lib.js:314:40:314:43 | name | lib/lib.js:315:22:315:25 | name | $@ based on library input is later used in $@. | lib/lib.js:315:10:315:25 | "rm -rf " + name | String concatenation | lib/lib.js:315:2:315:26 | cp.exec ... + name) | shell command | +| lib/lib.js:320:11:320:26 | "rm -rf " + name | lib/lib.js:314:40:314:43 | name | lib/lib.js:320:23:320:26 | name | $@ based on library input is later used in $@. | lib/lib.js:320:11:320:26 | "rm -rf " + name | String concatenation | lib/lib.js:320:3:320:27 | cp.exec ... + name) | shell command | From 9a944625d167f7c985ef557ea819726ff1310c5b Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 6 Jul 2020 15:17:15 +0200 Subject: [PATCH 27/40] autoformat --- .../semmle/javascript/security/dataflow/UrlConcatenation.qll | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UrlConcatenation.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UrlConcatenation.qll index e32826b79be..b6ac1782852 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UrlConcatenation.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UrlConcatenation.qll @@ -101,9 +101,7 @@ predicate hostnameSanitizingPrefixEdge(DataFlow::Node source, DataFlow::Node sin * A check that sanitizes the hostname of a URL. */ class HostnameSanitizerGuard extends TaintTracking::SanitizerGuardNode, StringOps::StartsWith { - HostnameSanitizerGuard() { - hasHostnameSanitizingSubstring(getSubstring()) - } + HostnameSanitizerGuard() { hasHostnameSanitizingSubstring(getSubstring()) } override predicate sanitizes(boolean outcome, Expr e) { outcome = getPolarity() and From 6ff8508d01355b1df5f24cd305d244a9db91f213 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Mon, 6 Jul 2020 15:46:11 +0200 Subject: [PATCH 28/40] Java: Clarify documentation for Location predicate results --- java/ql/src/semmle/code/Location.qll | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/java/ql/src/semmle/code/Location.qll b/java/ql/src/semmle/code/Location.qll index a34bba9ddc7..5cb97a39979 100755 --- a/java/ql/src/semmle/code/Location.qll +++ b/java/ql/src/semmle/code/Location.qll @@ -90,16 +90,16 @@ class Top extends @top { /** A location maps language elements to positions in source files. */ class Location extends @location { - /** Gets the line number where this location starts. */ + /** Gets the 1-based line number (inclusive) where this location starts. */ int getStartLine() { locations_default(this, _, result, _, _, _) } - /** Gets the column number where this location starts. */ + /** Gets the 1-based column number (inclusive) where this location starts. */ int getStartColumn() { locations_default(this, _, _, result, _, _) } - /** Gets the line number where this location ends. */ + /** Gets the 1-based line number (inclusive) where this location ends. */ int getEndLine() { locations_default(this, _, _, _, result, _) } - /** Gets the column number where this location ends. */ + /** Gets the 1-based column number (inclusive) where this location ends. */ int getEndColumn() { locations_default(this, _, _, _, _, result) } /** From 0a9686709bf5945a16496edccf104fc6f3bf6458 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Sun, 5 Jul 2020 18:43:02 +0200 Subject: [PATCH 29/40] Fix wrong method name --- java/ql/src/semmle/code/java/security/FileReadWrite.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/semmle/code/java/security/FileReadWrite.qll b/java/ql/src/semmle/code/java/security/FileReadWrite.qll index 85020f60fd4..f6aec6e9999 100644 --- a/java/ql/src/semmle/code/java/security/FileReadWrite.qll +++ b/java/ql/src/semmle/code/java/security/FileReadWrite.qll @@ -24,7 +24,7 @@ private predicate fileRead(VarAccess fileAccess, Expr fileReadingExpr) { fileAccess = ma.getArgument(0) and filesMethod .hasName(["readAllBytes", "readAllLines", "readString", "lines", "newBufferedReader", - "newInputReader", "newByteChannel"]) + "newInputStream", "newByteChannel"]) ) ) or From 0d9b18dbd746c424fa6002c505b10acba2eba258 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Thu, 2 Jul 2020 14:15:11 +0100 Subject: [PATCH 30/40] C++: Accept test changes for is_constexpr Generated copy and move constructors may now be marked as constexpr. --- cpp/ql/test/library-tests/specifiers2/specifiers2.expected | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cpp/ql/test/library-tests/specifiers2/specifiers2.expected b/cpp/ql/test/library-tests/specifiers2/specifiers2.expected index 26c72b4e319..4930fdac720 100644 --- a/cpp/ql/test/library-tests/specifiers2/specifiers2.expected +++ b/cpp/ql/test/library-tests/specifiers2/specifiers2.expected @@ -62,6 +62,8 @@ | Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | extern | | Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | inline | | Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | inline | +| Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | is_constexpr | +| Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | is_constexpr | | Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | public | | Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | public | | Function | specifiers2pp.cpp:33:5:33:18 | fun | fun | public | @@ -69,6 +71,8 @@ | Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | extern | | Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | inline | | Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | inline | +| Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | is_constexpr | +| Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | is_constexpr | | Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | public | | Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | public | | Function | specifiers2pp.cpp:40:12:40:18 | someFun | someFun | extern | From 5649254dbdbb80a259b75d36472ed884e88c05dc Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Mon, 6 Jul 2020 20:35:11 +0200 Subject: [PATCH 31/40] Fix broken link formatting in introduce-libraries-java.rst Co-authored-by: Shati Patel <42641846+shati-patel@users.noreply.github.com> --- docs/language/learn-ql/java/introduce-libraries-java.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/language/learn-ql/java/introduce-libraries-java.rst b/docs/language/learn-ql/java/introduce-libraries-java.rst index ae7f97d6bea..3d4f5920e72 100644 --- a/docs/language/learn-ql/java/introduce-libraries-java.rst +++ b/docs/language/learn-ql/java/introduce-libraries-java.rst @@ -290,7 +290,7 @@ These annotations are represented by class ``Annotation``. An annotation is simp anntp.hasQualifiedName("java.lang", "Deprecated") select ann -➤ `See this in the query console on LGTM.com `__. Only constructors with the ``@Deprecated`` annotation are reported this time. For more information on working with annotations, see the :doc:`article on annotations `. From 67db1df00c5b9e2a473776c07015bb7191e00a7f Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Tue, 7 Jul 2020 11:39:27 +0200 Subject: [PATCH 32/40] C++/C#/JavaScript/Python: Port Location qldoc update. --- cpp/ql/src/semmle/code/cpp/Location.qll | 8 ++++---- csharp/ql/src/semmle/code/csharp/Location.qll | 8 ++++---- javascript/ql/src/semmle/javascript/Locations.qll | 8 ++++---- python/ql/src/semmle/python/Files.qll | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/Location.qll b/cpp/ql/src/semmle/code/cpp/Location.qll index 8fff1808c87..74bf0872be2 100644 --- a/cpp/ql/src/semmle/code/cpp/Location.qll +++ b/cpp/ql/src/semmle/code/cpp/Location.qll @@ -15,16 +15,16 @@ class Location extends @location { /** Gets the file corresponding to this location, if any. */ File getFile() { result = this.getContainer() } - /** Gets the start line of this location. */ + /** Gets the 1-based line number (inclusive) where this location starts. */ int getStartLine() { this.fullLocationInfo(_, result, _, _, _) } - /** Gets the start column of this location. */ + /** Gets the 1-based column number (inclusive) where this location starts. */ int getStartColumn() { this.fullLocationInfo(_, _, result, _, _) } - /** Gets the end line of this location. */ + /** Gets the 1-based line number (inclusive) where this location ends. */ int getEndLine() { this.fullLocationInfo(_, _, _, result, _) } - /** Gets the end column of this location. */ + /** Gets the 1-based column number (inclusive) where this location ends. */ int getEndColumn() { this.fullLocationInfo(_, _, _, _, result) } /** diff --git a/csharp/ql/src/semmle/code/csharp/Location.qll b/csharp/ql/src/semmle/code/csharp/Location.qll index 178eddc1d4d..99df294ae63 100644 --- a/csharp/ql/src/semmle/code/csharp/Location.qll +++ b/csharp/ql/src/semmle/code/csharp/Location.qll @@ -38,16 +38,16 @@ class Location extends @location { /** Gets a textual representation of this location. */ string toString() { none() } - /** Gets the start line of this location. */ + /** Gets the 1-based line number (inclusive) where this location starts. */ final int getStartLine() { this.hasLocationInfo(_, result, _, _, _) } - /** Gets the end line of this location. */ + /** Gets the 1-based line number (inclusive) where this location ends. */ final int getEndLine() { this.hasLocationInfo(_, _, _, result, _) } - /** Gets the start column of this location. */ + /** Gets the 1-based column number (inclusive) where this location starts. */ final int getStartColumn() { this.hasLocationInfo(_, _, result, _, _) } - /** Gets the end column of this location. */ + /** Gets the 1-based column number (inclusive) where this location ends. */ final int getEndColumn() { this.hasLocationInfo(_, _, _, _, result) } } diff --git a/javascript/ql/src/semmle/javascript/Locations.qll b/javascript/ql/src/semmle/javascript/Locations.qll index 72939c8b419..5f6a0050359 100644 --- a/javascript/ql/src/semmle/javascript/Locations.qll +++ b/javascript/ql/src/semmle/javascript/Locations.qll @@ -12,16 +12,16 @@ class Location extends @location { /** Gets the file for this location. */ File getFile() { locations_default(this, result, _, _, _, _) } - /** Gets the start line of this location. */ + /** Gets the 1-based line number (inclusive) where this location starts. */ int getStartLine() { locations_default(this, _, result, _, _, _) } - /** Gets the start column of this location. */ + /** Gets the 1-based column number (inclusive) where this location starts. */ int getStartColumn() { locations_default(this, _, _, result, _, _) } - /** Gets the end line of this location. */ + /** Gets the 1-based line number (inclusive) where this location ends. */ int getEndLine() { locations_default(this, _, _, _, result, _) } - /** Gets the end column of this location. */ + /** Gets the 1-based column number (inclusive) where this location ends. */ int getEndColumn() { locations_default(this, _, _, _, _, result) } /** Gets the number of lines covered by this location. */ diff --git a/python/ql/src/semmle/python/Files.qll b/python/ql/src/semmle/python/Files.qll index c4b71372858..f92297b6a9b 100644 --- a/python/ql/src/semmle/python/Files.qll +++ b/python/ql/src/semmle/python/Files.qll @@ -369,25 +369,25 @@ class Location extends @location { exists(Module m | locations_ast(this, m, _, _, _, _) | result = m.getPath()) } - /** Gets the start line of this location */ + /** Gets the 1-based line number (inclusive) where this location starts. */ int getStartLine() { locations_default(this, _, result, _, _, _) or locations_ast(this, _, result, _, _, _) } - /** Gets the start column of this location */ + /** Gets the 1-based column number (inclusive) where this location starts. */ int getStartColumn() { locations_default(this, _, _, result, _, _) or locations_ast(this, _, _, result, _, _) } - /** Gets the end line of this location */ + /** Gets the 1-based line number (inclusive) where this location ends. */ int getEndLine() { locations_default(this, _, _, _, result, _) or locations_ast(this, _, _, _, result, _) } - /** Gets the end column of this location */ + /** Gets the 1-based column number (inclusive) where this location ends. */ int getEndColumn() { locations_default(this, _, _, _, _, result) or locations_ast(this, _, _, _, _, result) From f07a7bf8cff4152845a013fa44001247e796b3a0 Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Tue, 7 Jul 2020 15:43:52 +0200 Subject: [PATCH 33/40] Python: Autoformat everything using `qlformat`. Will need subsequent PRs fixing up test failures (due to deprecated methods moving around), but other than that everything should be straight-forward. --- .../ql/examples/snippets/catch_exception.ql | 4 +- .../snippets/conditional_expression.ql | 6 +- python/ql/examples/snippets/emptythen.ql | 8 +- python/ql/examples/snippets/extend_class.ql | 4 +- python/ql/examples/snippets/method_call.ql | 4 +- python/ql/examples/snippets/new_instance.ql | 4 +- .../ql/examples/snippets/override_method.ql | 4 +- python/ql/examples/snippets/print.ql | 10 +- python/ql/examples/snippets/private_access.ql | 8 +- .../ql/examples/snippets/raise_exception.ql | 4 +- python/ql/examples/snippets/store_none.ql | 4 +- python/ql/examples/snippets/tryfinally.ql | 4 +- python/ql/src/Classes/ClassAttributes.qll | 240 +- .../ConflictingAttributesInBaseClasses.ql | 62 +- .../DefineEqualsWhenAddingAttributes.ql | 48 +- python/ql/src/Classes/Equality.qll | 102 +- python/ql/src/Classes/EqualsOrHash.ql | 54 +- python/ql/src/Classes/EqualsOrNotEquals.ql | 30 +- python/ql/src/Classes/IncompleteOrdering.ql | 72 +- python/ql/src/Classes/InconsistentMRO.ql | 14 +- .../ql/src/Classes/InitCallsSubclassMethod.ql | 24 +- .../Classes/MaybeUndefinedClassAttribute.ql | 38 +- python/ql/src/Classes/MethodCallOrder.qll | 84 +- python/ql/src/Classes/MissingCallToDel.ql | 12 +- python/ql/src/Classes/MissingCallToInit.ql | 20 +- python/ql/src/Classes/MutatingDescriptor.ql | 24 +- .../OverwritingAttributeInSuperClass.ql | 114 +- .../ql/src/Classes/PropertyInOldStyleClass.ql | 4 +- .../ql/src/Classes/ShouldBeContextManager.ql | 4 +- python/ql/src/Classes/SubclassShadowing.ql | 34 +- python/ql/src/Classes/SuperInOldStyleClass.ql | 14 +- .../SuperclassDelCalledMultipleTimes.ql | 20 +- .../SuperclassInitCalledMultipleTimes.ql | 22 +- .../ql/src/Classes/UndefinedClassAttribute.ql | 18 +- python/ql/src/Classes/UselessClass.ql | 106 +- ...rongNameForArgumentInClassInstantiation.ql | 6 +- ...rongNumberArgumentsInClassInstantiation.ql | 22 +- .../src/Exceptions/CatchingBaseException.ql | 10 +- python/ql/src/Exceptions/EmptyExcept.ql | 104 +- .../Exceptions/IllegalExceptionHandlerType.ql | 22 +- python/ql/src/Exceptions/IllegalRaise.ql | 10 +- .../ql/src/Exceptions/IncorrectExceptOrder.ql | 20 +- python/ql/src/Exceptions/NotImplemented.qll | 10 +- python/ql/src/Exceptions/Raising.qll | 14 +- python/ql/src/Exceptions/RaisingTuple.ql | 10 +- .../Exceptions/UnguardedNextInGenerator.ql | 42 +- python/ql/src/Expressions/CallArgs.qll | 292 +- .../src/Expressions/CallToSuperWrongClass.ql | 18 +- python/ql/src/Expressions/CompareConstants.ql | 8 +- .../Comparisons/UselessComparisonTest.ql | 26 +- .../src/Expressions/ContainsNonContainer.ql | 26 +- .../DuplicateKeyInDictionaryLiteral.ql | 42 +- .../ExpectedMappingForFormatString.ql | 16 +- .../ql/src/Expressions/ExplicitCallToDel.ql | 26 +- .../Formatting/AdvancedFormatting.qll | 164 +- .../Formatting/UnusedArgumentIn3101Format.ql | 14 +- .../UnusedNamedArgumentIn3101Format.ql | 26 +- .../WrongNameInArgumentsFor3101Format.ql | 12 +- .../WrongNumberArgumentsFor3101Format.ql | 20 +- python/ql/src/Expressions/HashedButNoHash.ql | 60 +- .../Expressions/IncorrectComparisonUsingIs.ql | 18 +- python/ql/src/Expressions/IsComparisons.qll | 180 +- .../ql/src/Expressions/NonCallableCalled.ql | 16 +- .../NonPortableComparisonUsingIs.ql | 14 +- .../src/Expressions/RedundantComparison.qll | 73 +- .../src/Expressions/Regex/BackspaceEscape.ql | 6 +- .../Regex/DuplicateCharacterInSet.ql | 42 +- .../src/Expressions/Regex/UnmatchableCaret.ql | 10 +- .../Expressions/Regex/UnmatchableDollar.ql | 10 +- .../ql/src/Expressions/TruncatedDivision.ql | 36 +- ...nintentionalImplicitStringConcatenation.ql | 24 +- .../ql/src/Expressions/UnnecessaryLambda.ql | 68 +- python/ql/src/Expressions/UseofInput.ql | 8 +- .../Expressions/WrongNameForArgumentInCall.ql | 12 +- .../WrongNumberArgumentsForFormat.ql | 42 +- .../Expressions/WrongNumberArgumentsInCall.ql | 19 +- python/ql/src/Filters/ClassifyFiles.ql | 6 +- python/ql/src/Functions/ConsistentReturns.ql | 20 +- .../ql/src/Functions/DeprecatedSliceMethod.ql | 10 +- .../ql/src/Functions/ExplicitReturnInInit.ql | 12 +- .../IncorrectRaiseInSpecialMethod.ql | 212 +- .../Functions/IncorrectlyOverriddenMethod.ql | 26 +- .../IncorrectlySpecifiedOverriddenMethod.ql | 36 +- python/ql/src/Functions/InitIsGenerator.ql | 4 +- .../src/Functions/IterReturnsNonIterator.ql | 12 +- python/ql/src/Functions/IterReturnsNonSelf.ql | 12 +- .../ModificationOfParameterWithDefault.ql | 104 +- python/ql/src/Functions/NonCls.ql | 44 +- python/ql/src/Functions/NonSelf.ql | 58 +- .../src/Functions/OverlyComplexDelMethod.ql | 8 +- .../Functions/ReturnConsistentTupleSizes.ql | 20 +- python/ql/src/Functions/ReturnValueIgnored.ql | 92 +- .../Functions/SignatureOverriddenMethod.ql | 32 +- .../src/Functions/SignatureSpecialMethods.ql | 330 +- .../Functions/UseImplicitNoneReturnValue.ql | 36 +- python/ql/src/Imports/Cyclic.qll | 132 +- python/ql/src/Imports/CyclicImport.ql | 14 +- python/ql/src/Imports/DeprecatedModule.ql | 110 +- .../Imports/FromImportOfMutableAttribute.ql | 30 +- .../ql/src/Imports/ImportShadowedByLoopVar.ql | 10 +- python/ql/src/Imports/ImportandImportFrom.ql | 12 +- python/ql/src/Imports/ModuleImportsItself.ql | 16 +- .../ql/src/Imports/ModuleLevelCyclicImport.ql | 8 +- python/ql/src/Imports/MultipleImports.ql | 46 +- python/ql/src/Imports/UnintentionalImport.ql | 16 +- python/ql/src/Imports/UnusedImport.ql | 160 +- python/ql/src/Lexical/CommentedOutCode.qll | 432 +- python/ql/src/Lexical/OldOctalLiteral.ql | 20 +- python/ql/src/Metrics/CommentRatio.ql | 2 +- .../Dependencies/ExternalDependencies.ql | 14 +- .../ExternalDependenciesSourceLinks.ql | 10 +- python/ql/src/Metrics/DocStringRatio.ql | 4 +- python/ql/src/Metrics/FLinesOfComments.ql | 2 +- .../ql/src/Metrics/FLinesOfDuplicatedCode.ql | 14 +- python/ql/src/Metrics/FLinesOfSimilarCode.ql | 14 +- python/ql/src/Metrics/History/HChurn.ql | 14 +- python/ql/src/Metrics/History/HLinesAdded.ql | 14 +- .../ql/src/Metrics/History/HLinesDeleted.ql | 14 +- .../src/Metrics/History/HNumberOfCoCommits.ql | 10 +- .../src/Metrics/History/HNumberOfReCommits.ql | 24 +- .../Metrics/History/HNumberOfRecentAuthors.ql | 14 +- .../History/HNumberOfRecentChangedFiles.ql | 8 +- python/ql/src/Metrics/Internal/Extents.qll | 50 +- python/ql/src/Numerics/Pythagorean.ql | 34 +- .../ql/src/Resources/FileNotAlwaysClosed.ql | 78 +- python/ql/src/Resources/FileOpen.qll | 196 +- .../CVE-2018-1281/BindToAllInterfaces.ql | 22 +- .../CWE-020/IncompleteHostnameRegExp.ql | 26 +- .../IncompleteUrlSubstringSanitization.ql | 44 +- .../ql/src/Security/CWE-022/PathInjection.ql | 26 +- python/ql/src/Security/CWE-022/TarSlip.ql | 200 +- .../src/Security/CWE-078/CommandInjection.ql | 22 +- .../Security/CWE-079/Jinja2WithoutEscaping.ql | 26 +- .../ql/src/Security/CWE-079/ReflectedXss.ql | 22 +- .../ql/src/Security/CWE-089/SqlInjection.ql | 22 +- .../ql/src/Security/CWE-094/CodeInjection.ql | 12 +- .../Security/CWE-209/StackTraceExposure.ql | 8 +- python/ql/src/Security/CWE-215/FlaskDebug.ql | 8 +- .../CWE-295/MissingHostKeyValidation.ql | 20 +- .../CWE-295/RequestWithoutValidation.ql | 8 +- .../src/Security/CWE-312/CleartextLogging.ql | 18 +- .../src/Security/CWE-312/CleartextStorage.ql | 18 +- python/ql/src/Security/CWE-326/WeakCrypto.ql | 94 +- .../Security/CWE-327/BrokenCryptoAlgorithm.ql | 12 +- .../CWE-327/InsecureDefaultProtocol.ql | 20 +- .../src/Security/CWE-327/InsecureProtocol.ql | 94 +- .../Security/CWE-377/InsecureTemporaryFile.ql | 20 +- .../Security/CWE-502/UnsafeDeserialization.ql | 10 +- python/ql/src/Security/CWE-601/UrlRedirect.ql | 20 +- .../Security/CWE-732/WeakFilePermissions.ql | 28 +- .../Security/CWE-798/HardcodedCredentials.ql | 142 +- .../src/Statements/AssertLiteralConstant.ql | 22 +- python/ql/src/Statements/AssertOnTuple.ql | 16 +- .../src/Statements/BreakOrReturnInFinally.ql | 16 +- .../ql/src/Statements/C_StyleParentheses.ql | 34 +- .../src/Statements/ConstantInConditional.ql | 24 +- python/ql/src/Statements/DocStrings.ql | 40 +- python/ql/src/Statements/ExecUsed.ql | 8 +- .../Statements/IterableStringOrSequence.ql | 32 +- .../MismatchInMultipleAssignment.ql | 68 +- .../ql/src/Statements/ModificationOfLocals.ql | 24 +- .../src/Statements/NestedLoopsSameVariable.ql | 14 +- .../NestedLoopsSameVariableWithReuse.ql | 24 +- .../ql/src/Statements/NonIteratorInForLoop.ql | 14 +- .../ql/src/Statements/RedundantAssignment.ql | 82 +- .../ReturnOrYieldOutsideFunction.ql | 16 +- .../src/Statements/ShouldUseWithStatement.ql | 24 +- .../ql/src/Statements/SideEffectInAssert.ql | 50 +- python/ql/src/Statements/StatementNoEffect.ql | 132 +- .../Statements/StringConcatenationInLoop.ql | 16 +- python/ql/src/Statements/TopLevelPrint.ql | 32 +- python/ql/src/Statements/UnnecessaryDelete.ql | 26 +- .../src/Statements/UnnecessaryElseClause.ql | 14 +- python/ql/src/Statements/UnnecessaryPass.ql | 18 +- python/ql/src/Statements/UnreachableCode.ql | 56 +- .../src/Statements/UnusedExceptionObject.ql | 6 +- python/ql/src/Statements/UseOfExit.ql | 4 +- python/ql/src/Testing/ImpreciseAssert.ql | 130 +- python/ql/src/Testing/Mox.qll | 20 +- python/ql/src/Variables/Definition.qll | 218 +- .../src/Variables/LeakingListComprehension.ql | 26 +- python/ql/src/Variables/Loop.qll | 40 +- .../ql/src/Variables/LoopVariableCapture.ql | 42 +- python/ql/src/Variables/MonkeyPatched.qll | 40 +- python/ql/src/Variables/MultiplyDefined.ql | 62 +- python/ql/src/Variables/ShadowBuiltin.ql | 74 +- python/ql/src/Variables/ShadowGlobal.ql | 64 +- python/ql/src/Variables/Shadowing.qll | 8 +- .../SuspiciousUnusedLoopIterationVariable.ql | 126 +- python/ql/src/Variables/Undefined.qll | 154 +- python/ql/src/Variables/UndefinedExport.ql | 95 +- python/ql/src/Variables/UndefinedGlobal.ql | 148 +- .../ql/src/Variables/UndefinedPlaceHolder.ql | 28 +- python/ql/src/Variables/UninitializedLocal.ql | 22 +- .../ql/src/Variables/UnusedLocalVariable.ql | 24 +- .../ql/src/Variables/UnusedModuleVariable.ql | 72 +- python/ql/src/Variables/UnusedParameter.ql | 26 +- python/ql/src/analysis/AlertSuppression.ql | 114 +- python/ql/src/analysis/CallGraphEfficiency.ql | 26 +- .../analysis/CallGraphMarginalEfficiency.ql | 34 +- python/ql/src/analysis/Consistency.ql | 464 +- python/ql/src/analysis/ContextEfficiency.ql | 28 +- .../src/analysis/ContextMarginalEfficiency.ql | 28 +- .../src/analysis/CrossProjectDefinitions.qll | 144 +- python/ql/src/analysis/DefinitionTracking.qll | 545 +- python/ql/src/analysis/Definitions.ql | 2 +- python/ql/src/analysis/Efficiency.ql | 38 +- python/ql/src/analysis/ImportFailure.ql | 98 +- python/ql/src/analysis/KeyPointsToFailure.ql | 14 +- python/ql/src/analysis/LocalDefinitions.ql | 9 +- python/ql/src/analysis/LocalReferences.ql | 7 +- python/ql/src/analysis/RatioOfDefinitions.ql | 26 +- python/ql/src/analysis/Summary.ql | 68 +- .../ql/src/analysis/TypeInferenceFailure.ql | 4 +- .../experimental/dataflow/TaintTracking.qll | 2 +- .../internal/DataFlowImplSpecific.qll | 2 +- .../dataflow/internal/TaintTrackingPublic.qll | 6 +- .../tainttracking1/TaintTrackingParameter.qll | 2 +- python/ql/src/external/CodeDuplication.qll | 288 +- python/ql/src/external/DefectFilter.qll | 94 +- python/ql/src/external/DuplicateBlock.ql | 16 +- python/ql/src/external/DuplicateFunction.ql | 10 +- python/ql/src/external/ExternalArtifact.qll | 94 +- .../ql/src/external/MostlyDuplicateClass.ql | 6 +- python/ql/src/external/SimilarFunction.ql | 12 +- python/ql/src/external/Thrift.qll | 216 +- python/ql/src/external/VCS.qll | 86 +- python/ql/src/semmle/crypto/Crypto.qll | 216 +- python/ql/src/semmle/python/AstExtended.qll | 164 +- python/ql/src/semmle/python/AstGenerated.qll | 1542 ++--- python/ql/src/semmle/python/Class.qll | 244 +- python/ql/src/semmle/python/Comment.qll | 143 +- python/ql/src/semmle/python/Comparisons.qll | 758 +-- .../ql/src/semmle/python/Comprehensions.qll | 136 +- python/ql/src/semmle/python/Constants.qll | 32 +- python/ql/src/semmle/python/Exprs.qll | 866 +-- python/ql/src/semmle/python/Files.qll | 794 +-- python/ql/src/semmle/python/Flow.qll | 1739 +++--- python/ql/src/semmle/python/Function.qll | 544 +- .../src/semmle/python/GuardedControlFlow.qll | 120 +- python/ql/src/semmle/python/Import.qll | 378 +- python/ql/src/semmle/python/Keywords.qll | 56 +- python/ql/src/semmle/python/Metrics.qll | 536 +- python/ql/src/semmle/python/Module.qll | 350 +- python/ql/src/semmle/python/Operations.qll | 152 +- python/ql/src/semmle/python/SSA.qll | 354 +- python/ql/src/semmle/python/Scope.qll | 244 +- python/ql/src/semmle/python/SelfAttribute.qll | 100 +- python/ql/src/semmle/python/Stmts.qll | 554 +- python/ql/src/semmle/python/TestUtils.qll | 22 +- python/ql/src/semmle/python/Variables.qll | 104 +- .../semmle/python/dataflow/Configuration.qll | 246 +- .../ql/src/semmle/python/dataflow/Files.qll | 16 +- .../semmle/python/dataflow/Implementation.qll | 1700 +++--- .../ql/src/semmle/python/dataflow/Legacy.qll | 100 +- .../semmle/python/dataflow/StateTracking.qll | 274 +- .../semmle/python/dataflow/TaintTracking.qll | 874 +-- .../python/dependencies/Dependencies.qll | 222 +- .../python/dependencies/DependencyKind.qll | 20 +- .../python/dependencies/TechInventory.qll | 150 +- .../ql/src/semmle/python/essa/Definitions.qll | 526 +- python/ql/src/semmle/python/essa/Essa.qll | 956 ++-- .../ql/src/semmle/python/essa/SsaCompute.qll | 492 +- .../src/semmle/python/essa/SsaDefinitions.qll | 250 +- .../semmle/python/filters/GeneratedCode.qll | 238 +- python/ql/src/semmle/python/filters/Tests.qll | 46 +- .../ql/src/semmle/python/libraries/Zope.qll | 62 +- .../src/semmle/python/objects/Callables.qll | 652 +-- .../ql/src/semmle/python/objects/Classes.qll | 492 +- .../src/semmle/python/objects/Constants.qll | 364 +- .../src/semmle/python/objects/Descriptors.qll | 498 +- .../src/semmle/python/objects/Instances.qll | 674 +-- .../ql/src/semmle/python/objects/Modules.qll | 528 +- .../src/semmle/python/objects/ObjectAPI.qll | 1598 +++--- .../semmle/python/objects/ObjectInternal.qll | 794 +-- .../src/semmle/python/objects/Sequences.qll | 372 +- .../ql/src/semmle/python/objects/TObject.qll | 805 ++- python/ql/src/semmle/python/pointsto/Base.qll | 406 +- .../src/semmle/python/pointsto/CallGraph.qll | 86 +- .../ql/src/semmle/python/pointsto/Filters.qll | 48 +- python/ql/src/semmle/python/pointsto/MRO.qll | 654 +-- .../src/semmle/python/pointsto/PointsTo.qll | 4936 ++++++++--------- .../python/pointsto/PointsToContext.qll | 306 +- python/ql/src/semmle/python/regex.qll | 1344 +++-- .../src/semmle/python/security/ClearText.qll | 80 +- .../ql/src/semmle/python/security/Crypto.qll | 210 +- .../src/semmle/python/security/Exceptions.qll | 76 +- .../ql/src/semmle/python/security/Paths.qll | 18 +- .../semmle/python/security/SensitiveData.qll | 230 +- .../semmle/python/security/flow/AnyCall.qll | 4 +- .../python/security/injection/Command.qll | 322 +- .../security/injection/Deserialization.qll | 4 +- .../semmle/python/security/injection/Exec.qll | 18 +- .../python/security/injection/Marshal.qll | 16 +- .../semmle/python/security/injection/Path.qll | 84 +- .../python/security/injection/Pickle.qll | 26 +- .../semmle/python/security/injection/Sql.qll | 64 +- .../semmle/python/security/injection/Xml.qll | 42 +- .../semmle/python/security/injection/Yaml.qll | 16 +- .../semmle/python/security/strings/Basic.qll | 150 +- .../semmle/python/security/strings/Common.qll | 16 +- .../python/security/strings/External.qll | 472 +- .../python/security/strings/Untrusted.qll | 2 +- python/ql/src/semmle/python/strings.qll | 38 +- .../src/semmle/python/templates/PyxlTags.qll | 78 +- .../src/semmle/python/templates/Templates.qll | 12 +- .../ql/src/semmle/python/types/Builtins.qll | 200 +- .../src/semmle/python/types/ClassObject.qll | 666 +-- .../src/semmle/python/types/Descriptors.qll | 30 +- .../ql/src/semmle/python/types/Exceptions.qll | 786 +-- .../ql/src/semmle/python/types/Extensions.qll | 220 +- .../semmle/python/types/FunctionObject.qll | 456 +- .../ql/src/semmle/python/types/ImportTime.qll | 42 +- .../ql/src/semmle/python/types/ModuleKind.qll | 52 +- .../src/semmle/python/types/ModuleObject.qll | 314 +- python/ql/src/semmle/python/types/Object.qll | 589 +- .../ql/src/semmle/python/types/Properties.qll | 128 +- python/ql/src/semmle/python/types/Version.qll | 14 +- .../semmle/python/values/StringAttributes.qll | 108 +- python/ql/src/semmle/python/web/Http.qll | 132 +- .../src/semmle/python/web/HttpConstants.qll | 14 +- .../src/semmle/python/web/bottle/General.qll | 48 +- .../src/semmle/python/web/bottle/Redirect.qll | 16 +- .../src/semmle/python/web/bottle/Request.qll | 86 +- .../src/semmle/python/web/bottle/Response.qll | 44 +- .../semmle/python/web/cherrypy/General.qll | 60 +- .../semmle/python/web/cherrypy/Request.qll | 52 +- .../semmle/python/web/cherrypy/Response.qll | 16 +- .../src/semmle/python/web/client/Requests.qll | 18 +- .../src/semmle/python/web/client/StdLib.qll | 88 +- python/ql/src/semmle/python/web/django/Db.qll | 34 +- .../src/semmle/python/web/django/General.qll | 160 +- .../ql/src/semmle/python/web/django/Model.qll | 102 +- .../src/semmle/python/web/django/Redirect.qll | 24 +- .../src/semmle/python/web/django/Request.qll | 90 +- .../src/semmle/python/web/django/Response.qll | 72 +- .../src/semmle/python/web/django/Shared.qll | 112 +- .../src/semmle/python/web/falcon/General.qll | 38 +- .../src/semmle/python/web/falcon/Request.qll | 58 +- .../src/semmle/python/web/falcon/Response.qll | 22 +- .../src/semmle/python/web/flask/General.qll | 98 +- .../src/semmle/python/web/flask/Redirect.qll | 14 +- .../src/semmle/python/web/flask/Request.qll | 86 +- .../src/semmle/python/web/flask/Response.qll | 60 +- .../semmle/python/web/pyramid/Redirect.qll | 26 +- .../src/semmle/python/web/pyramid/Request.qll | 20 +- .../semmle/python/web/pyramid/Response.qll | 34 +- .../ql/src/semmle/python/web/pyramid/View.qll | 2 +- .../src/semmle/python/web/stdlib/Request.qll | 170 +- .../src/semmle/python/web/stdlib/Response.qll | 46 +- .../semmle/python/web/tornado/Redirect.qll | 18 +- .../src/semmle/python/web/tornado/Request.qll | 96 +- .../semmle/python/web/tornado/Response.qll | 48 +- .../src/semmle/python/web/tornado/Tornado.qll | 48 +- .../semmle/python/web/turbogears/Request.qll | 30 +- .../semmle/python/web/turbogears/Response.qll | 32 +- .../python/web/turbogears/TurboGears.qll | 40 +- .../src/semmle/python/web/twisted/Request.qll | 40 +- .../semmle/python/web/twisted/Response.qll | 48 +- .../src/semmle/python/web/twisted/Twisted.qll | 50 +- .../src/semmle/python/web/webob/Request.qll | 52 +- .../ControlFlow/Exceptions/Likely.ql | 4 +- .../PointsTo/class_properties/ClassValues.ql | 30 +- .../PointsTo/import_time/Pruned.ql | 10 +- .../library-tests/PointsTo/imports/Runtime.ql | 10 +- .../PointsTo/origin_uniqueness/Origin.ql | 8 +- .../library-tests/classes/attr/class_attr.ql | 8 +- .../classes/attr/class_has_attr.ql | 8 +- .../2/library-tests/classes/attr/list_attr.ql | 10 +- .../ql/test/2/library-tests/classes/mro/C3.ql | 2 +- .../test/2/library-tests/classes/mro/mro.ql | 4 +- .../comprehensions/ConsistencyCheck.ql | 4 +- .../locations/general/AllLocations.ql | 6 +- .../modules/general/import_test.ql | 8 +- .../test/2/library-tests/objects/Literals.ql | 6 +- .../ql/test/2/library-tests/six/pointsto.ql | 6 +- .../library-tests/types/classes/new_style.ql | 6 +- .../library-tests/types/exceptions/Raises.ql | 14 +- .../types/properties/BuiltinProperties.ql | 6 +- .../ControlFlow/Exceptions/Likely.ql | 4 +- .../PointsTo/attributes/TestWithType.ql | 2 +- .../PointsTo/class_properties/ClassValues.ql | 30 +- .../PointsTo/consts/BooleanConstants.ql | 6 +- .../PointsTo/import_time/Pruned.ql | 6 +- .../subprocess-assert/ClassValue.ql | 10 +- .../PointsTo/typehints/Values.ql | 4 +- .../library-tests/classes/attr/class_attr.ql | 8 +- .../classes/attr/class_has_attr.ql | 8 +- .../test/3/library-tests/classes/mro/mro.ql | 4 +- .../3/library-tests/classes/mro/mro_index.ql | 4 +- .../locations/general/AllLocations.ql | 6 +- .../modules/general/import_test.ql | 8 +- .../3/library-tests/parameters/Special.ql | 10 +- .../ql/test/3/library-tests/six/pointsto.ql | 6 +- .../3/library-tests/taint/unpacking/Taint.qll | 18 +- .../taint/unpacking/TestTaint.ql | 22 +- .../library-tests/types/exceptions/Raises.ql | 14 +- .../library-tests/types/exceptions/Viable.ql | 2 +- .../types/namespaces/NameSpace.ql | 22 +- .../types/properties/BuiltinProperties.ql | 6 +- .../experimental/dataflow/basic/callGraph.ql | 10 +- .../dataflow/basic/callGraphConfig.qll | 2 +- .../dataflow/basic/callGraphSinks.ql | 2 +- .../dataflow/basic/callGraphSources.ql | 2 +- .../experimental/dataflow/basic/global.ql | 9 +- .../experimental/dataflow/basic/globalStep.ql | 10 +- .../test/experimental/dataflow/basic/local.ql | 10 +- .../experimental/dataflow/basic/localStep.ql | 10 +- .../dataflow/basic/maximalFlows.ql | 9 +- .../dataflow/basic/maximalFlowsConfig.qll | 4 +- .../test/experimental/dataflow/basic/sinks.ql | 2 +- .../experimental/dataflow/basic/sources.ql | 2 +- .../dataflow/coverage/dataflow.ql | 10 +- .../dataflow/regression/dataflow.ql | 10 +- .../test/experimental/dataflow/testConfig.qll | 8 +- .../PointsToSupport/UseFromDefinition.ql | 12 +- .../ControlFlow/augassign/AugAssignFlow.ql | 6 +- .../ControlFlow/augassign/Kind.ql | 14 +- .../ControlFlow/comparison/Compare.ql | 12 +- .../library-tests/ControlFlow/delete/test.ql | 2 +- .../dominators/DominatesConsistency.ql | 10 +- .../ControlFlow/dominators/idom.ql | 4 +- .../general/ImmediateDominatorCheck.ql | 20 +- .../ControlFlow/general/Lines.ql | 6 +- .../ControlFlow/general/Reaches.ql | 8 +- .../ControlFlow/raising_stmts/RaisingFlow.ql | 10 +- .../ControlFlow/splitting/NodeCount.ql | 12 +- .../ControlFlow/splitting/SuccessorCount.ql | 6 +- .../ControlFlow/ssa/defns/test.ql | 2 +- .../ControlFlow/ssa/deletions/test.ql | 16 +- .../ssa/phi-nodes/phi_input_test.ql | 2 +- .../ControlFlow/ssa/phi-nodes/test.ql | 2 +- .../ControlFlow/ssa/uses/test.ql | 2 +- .../ControlFlow/successors/Successors.ql | 22 +- .../truefalse/ExceptionalSuccessors.ql | 8 +- .../truefalse/TrueFalseSuccessors.ql | 8 +- .../library-tests/ControlFlow/try/test_ssa.ql | 2 +- .../library-tests/DuplicateCode/Duplicate.ql | 14 +- .../DuplicateCode/DuplicateStatements.ql | 20 +- .../library-tests/DuplicateCode/Similar.ql | 12 +- .../library-tests/PointsTo/api/ClassValue.ql | 18 +- .../library-tests/PointsTo/api/Constants.ql | 20 +- .../PointsTo/api/QualifedNames.ql | 20 +- .../test/library-tests/PointsTo/api/Value.ql | 16 +- .../PointsTo/calls/getArgumentForCall.ql | 2 +- .../PointsTo/calls/getNamedArgumentForCall.ql | 2 +- .../PointsTo/comparisons/PointsTo.ql | 6 +- .../library-tests/PointsTo/customise/test.ql | 40 +- .../library-tests/PointsTo/decorators/Test.ql | 6 +- .../PointsTo/decorators/Values.ql | 4 +- .../PointsTo/extensions/Extend.ql | 70 +- .../library-tests/PointsTo/functions/Calls.ql | 12 +- .../library-tests/PointsTo/functions/test.ql | 4 +- .../PointsTo/general/GlobalPointsTo.ql | 6 +- .../PointsTo/general/LocalPointsTo.ql | 4 +- .../PointsTo/general/LocalPointsToType.ql | 4 +- .../library-tests/PointsTo/general/Util.qll | 12 +- .../PointsTo/general/interesting.qll | 16 +- .../library-tests/PointsTo/global/Global.ql | 4 +- .../PointsTo/guarded/PointsTo.ql | 6 +- .../PointsTo/guarded/PointsToWithType.ql | 6 +- .../library-tests/PointsTo/imports/Runtime.ql | 8 +- .../PointsTo/imports/RuntimeWithType.ql | 10 +- .../library-tests/PointsTo/indexing/Test.ql | 4 +- .../PointsTo/indexing/TestWithType.ql | 6 +- .../PointsTo/inheritance/BaseTypes.ql | 4 +- .../PointsTo/inheritance/MetaClass.ql | 4 +- .../library-tests/PointsTo/inheritance/Mro.ql | 2 +- .../PointsTo/inheritance/SuperTypes.ql | 4 +- .../PointsTo/local/LocalPointsTo.ql | 4 +- .../library-tests/PointsTo/lookup/Lookup.ql | 16 +- .../PointsTo/metaclass/Failed.ql | 4 +- .../library-tests/PointsTo/metaclass/Mro.ql | 2 +- .../library-tests/PointsTo/metaclass/Style.ql | 12 +- .../library-tests/PointsTo/metaclass/test.ql | 2 +- .../library-tests/PointsTo/new/ClassMethod.ql | 2 +- .../library-tests/PointsTo/new/Consistency.ql | 252 +- .../library-tests/PointsTo/new/Dataflow.ql | 2 +- .../test/library-tests/PointsTo/new/Live.ql | 6 +- .../library-tests/PointsTo/new/NameSpace.ql | 22 +- .../PointsTo/new/PointsToMissing.ql | 26 +- .../PointsTo/new/PointsToWithContext.ql | 2 +- .../PointsTo/new/PointsToWithType.ql | 2 +- .../library-tests/PointsTo/new/Precedes.ql | 2 +- .../ql/test/library-tests/PointsTo/new/SSA.ql | 8 +- .../PointsTo/new/SourceNodeDefinitions.ql | 12 +- .../library-tests/PointsTo/new/SsaAttr.ql | 6 +- .../PointsTo/new/TestEvaluate.ql | 22 +- .../test/library-tests/PointsTo/new/Util.qll | 68 +- .../test/library-tests/PointsTo/new/Values.ql | 2 +- .../library-tests/PointsTo/new/VarUses.ql | 4 +- .../PointsTo/properties/Values.ql | 8 +- .../missing/if-urlsplit-access/Test.ql | 10 +- .../regressions/missing/re-compile/Test.ql | 10 +- .../missing/uncalled-function/Test.ql | 10 +- .../regressions/wrong/classmethod/Test.ql | 10 +- .../PointsTo/subclass/TestEvaluate.ql | 8 +- .../PointsTo/super/SuperMethodCall.ql | 6 +- .../library-tests/attributes/SelfAttribute.ql | 6 +- .../classes/abstract/Abstract.ql | 4 +- .../library-tests/classes/attr/class_attr.ql | 8 +- .../classes/attr/class_defined_attr.ql | 8 +- .../classes/attr/class_defines_attr.ql | 8 +- .../classes/attr/class_has_attr.ql | 8 +- .../test/library-tests/classes/attr/hash.ql | 10 +- .../ql/test/library-tests/comments/length.ql | 4 +- .../library-tests/comparisons/Compare2.ql | 12 +- .../comparisons/CompareControls.ql | 2 +- .../dependencies/Dependencies.ql | 2 +- .../library-tests/descriptors/Descriptors.ql | 8 +- .../test/library-tests/descriptors/Methods.ql | 6 +- .../library-tests/descriptors/Properties.ql | 10 +- .../library-tests/encoding/CheckEncoding.ql | 6 +- .../examples/custom-sanitizer/Taint.qll | 80 +- .../examples/custom-sanitizer/TestTaint.ql | 44 +- .../ql/test/library-tests/exceptions/Legal.ql | 10 +- .../test/library-tests/exprs/ast/AstParent.ql | 2 +- .../library-tests/filters/generated/Filter.ql | 6 +- .../formatting/FormatArguments.ql | 6 +- .../library-tests/jump_to_defn/Consistency.ql | 16 +- .../test/library-tests/jump_to_defn/Remote.ql | 6 +- .../test/library-tests/jump_to_defn/test.ql | 4 +- .../implicit_concatenation/part_locations.ql | 4 +- .../locations/implicit_concatenation/parts.ql | 2 +- .../locations/implicit_concatenation/test.ql | 10 +- .../locations/negative_numbers/negative.ql | 4 +- .../modules/usage/ModuleUsage.ql | 22 +- .../ql/test/library-tests/objects/Literals.ql | 6 +- python/ql/test/library-tests/objects/Name.ql | 32 +- .../overrides/FunctionOverrides.ql | 4 +- .../test/library-tests/parameters/Special.ql | 10 +- .../test/library-tests/regex/Alternation.ql | 2 +- .../ql/test/library-tests/regex/FirstLast.ql | 6 +- .../test/library-tests/regex/GroupContents.ql | 2 +- python/ql/test/library-tests/regex/Regex.ql | 30 +- .../security/fabric-v1-execute/Taint.qll | 18 +- .../security/fabric-v1-execute/TestTaint.ql | 45 +- .../test/library-tests/state_tracking/Lib.qll | 10 +- .../test/library-tests/state_tracking/Test.ql | 12 +- .../state_tracking/Violations.ql | 12 +- .../library-tests/stmts/general/AstParent.ql | 2 +- .../stmts/general/SubExpressions.ql | 2 +- .../library-tests/stmts/raise_stmt/AST.ql | 2 +- .../test/library-tests/stmts/try_stmt/AST.ql | 2 +- .../test/library-tests/stmts/with_stmt/AST.ql | 2 +- .../library-tests/taint/collections/Taint.qll | 18 +- .../taint/collections/TestStep.ql | 8 +- .../taint/collections/TestTaint.ql | 22 +- .../taint/config/RockPaperScissors.ql | 2 +- .../test/library-tests/taint/config/Simple.ql | 2 +- .../library-tests/taint/config/TaintLib.qll | 290 +- .../taint/config/TaintedArgument.ql | 8 +- .../library-tests/taint/config/TestNode.ql | 2 +- .../library-tests/taint/config/TestSource.ql | 2 +- .../library-tests/taint/config/TestStep.ql | 4 +- .../library-tests/taint/dataflow/Config.qll | 16 +- .../library-tests/taint/dataflow/TestNode.ql | 2 +- .../taint/example/DilbertConfig.qll | 44 +- .../test/library-tests/taint/example/Edges.ql | 40 +- .../taint/example/ExampleConfig.ql | 2 +- .../test/library-tests/taint/example/Nodes.ql | 2 +- .../taint/exception_traceback/TestSource.ql | 4 +- .../taint/exception_traceback/TestStep.ql | 10 +- .../taint/extensions/ExtensionsLib.qll | 92 +- .../taint/extensions/TestNode.ql | 2 +- .../taint/extensions/TestStep.ql | 4 +- .../taint/flowpath_regression/Config.qll | 44 +- .../library-tests/taint/general/Contexts.ql | 4 +- .../taint/general/ParamSource.ql | 36 +- .../taint/general/TaintConsistency.ql | 40 +- .../library-tests/taint/general/TaintLib.qll | 316 +- .../library-tests/taint/general/TestDefn.ql | 2 +- .../library-tests/taint/general/TestSink.ql | 2 +- .../library-tests/taint/general/TestStep.ql | 2 +- .../library-tests/taint/general/TestTaint.ql | 22 +- .../library-tests/taint/general/TestVar.ql | 2 +- .../library-tests/taint/namedtuple/Taint.qll | 42 +- .../taint/namedtuple/TestTaint.ql | 22 +- .../library-tests/taint/strings/Taint.qll | 30 +- .../library-tests/taint/strings/TestStep.ql | 8 +- .../library-tests/taint/strings/TestTaint.ql | 22 +- .../library-tests/taint/unpacking/Taint.qll | 18 +- .../library-tests/taint/unpacking/TestStep.ql | 8 +- .../taint/unpacking/TestTaint.ql | 22 +- .../ql/test/library-tests/thrift/Function.ql | 10 +- .../library-tests/types/attributes/Test.ql | 2 +- .../types/classattr/ClassAttribute.ql | 14 +- .../types/classattr/ClassMember.ql | 14 +- .../types/classattr/SpecialAttribute.ql | 16 +- .../types/exceptions/Impossible.ql | 28 +- .../types/exceptions/LineRaises.ql | 14 +- .../library-tests/types/exceptions/Raises.ql | 14 +- .../library-tests/types/exceptions/Viable.ql | 2 +- .../library-tests/variables/scopes/free.ql | 6 +- .../library-tests/variables/scopes/locals.ql | 6 +- .../library-tests/variables/scopes/lookup.ql | 26 +- .../library-tests/web/stdlib/HttpSources.ql | 4 +- .../library-tests/web/stdlib/TestTaint.ql | 44 +- python/ql/test/library-tests/web/zope/Test.ql | 8 +- .../query-tests/Metrics/ratios/CodeRatio.ql | 2 +- .../ql/test/query-tests/Resources/Dataflow.ql | 16 +- .../py_exprs.ql | 186 +- 602 files changed, 26777 insertions(+), 26790 deletions(-) diff --git a/python/ql/examples/snippets/catch_exception.ql b/python/ql/examples/snippets/catch_exception.ql index c117267d112..9d67d0056b6 100644 --- a/python/ql/examples/snippets/catch_exception.ql +++ b/python/ql/examples/snippets/catch_exception.ql @@ -11,6 +11,6 @@ import python from ExceptStmt ex, ClassValue cls where - cls.getName() = "MyExceptionClass" and - ex.getType().pointsTo(cls) + cls.getName() = "MyExceptionClass" and + ex.getType().pointsTo(cls) select ex diff --git a/python/ql/examples/snippets/conditional_expression.ql b/python/ql/examples/snippets/conditional_expression.ql index ee519aedb06..8af55ca104f 100644 --- a/python/ql/examples/snippets/conditional_expression.ql +++ b/python/ql/examples/snippets/conditional_expression.ql @@ -12,7 +12,7 @@ import python from IfExp e, ClassObject cls1, ClassObject cls2 where - e.getBody().refersTo(_, cls1, _) and - e.getOrelse().refersTo(_, cls2, _) and - cls1 != cls2 + e.getBody().refersTo(_, cls1, _) and + e.getOrelse().refersTo(_, cls2, _) and + cls1 != cls2 select e diff --git a/python/ql/examples/snippets/emptythen.ql b/python/ql/examples/snippets/emptythen.ql index bc017d4707a..e6c5b0f290e 100644 --- a/python/ql/examples/snippets/emptythen.ql +++ b/python/ql/examples/snippets/emptythen.ql @@ -14,8 +14,8 @@ import python from If i where - not exists(Stmt s | - i.getStmt(_) = s and - not s instanceof Pass - ) + not exists(Stmt s | + i.getStmt(_) = s and + not s instanceof Pass + ) select i diff --git a/python/ql/examples/snippets/extend_class.ql b/python/ql/examples/snippets/extend_class.ql index cc4dd62647d..805929ab709 100644 --- a/python/ql/examples/snippets/extend_class.ql +++ b/python/ql/examples/snippets/extend_class.ql @@ -14,6 +14,6 @@ import python from ClassObject sub, ClassObject base where - base.getName() = "MyClass" and - sub.getABaseType() = base + base.getName() = "MyClass" and + sub.getABaseType() = base select sub diff --git a/python/ql/examples/snippets/method_call.ql b/python/ql/examples/snippets/method_call.ql index 9f78a4bb22f..f78a9e43be0 100644 --- a/python/ql/examples/snippets/method_call.ql +++ b/python/ql/examples/snippets/method_call.ql @@ -10,6 +10,6 @@ import python from AstNode call, PythonFunctionValue method where - method.getQualifiedName() = "MyClass.methodName" and - method.getACall().getNode() = call + method.getQualifiedName() = "MyClass.methodName" and + method.getACall().getNode() = call select call diff --git a/python/ql/examples/snippets/new_instance.ql b/python/ql/examples/snippets/new_instance.ql index c1293d6638c..75a1ea635d5 100644 --- a/python/ql/examples/snippets/new_instance.ql +++ b/python/ql/examples/snippets/new_instance.ql @@ -11,6 +11,6 @@ import python from Call new, ClassValue cls where - cls.getName() = "MyClass" and - new.getFunc().pointsTo(cls) + cls.getName() = "MyClass" and + new.getFunc().pointsTo(cls) select new diff --git a/python/ql/examples/snippets/override_method.ql b/python/ql/examples/snippets/override_method.ql index 75c276df627..cb0c7d57680 100644 --- a/python/ql/examples/snippets/override_method.ql +++ b/python/ql/examples/snippets/override_method.ql @@ -10,6 +10,6 @@ import python from FunctionObject override, FunctionObject base where - base.getQualifiedName() = "MyClass.methodName" and - override.overrides(base) + base.getQualifiedName() = "MyClass.methodName" and + override.overrides(base) select override diff --git a/python/ql/examples/snippets/print.ql b/python/ql/examples/snippets/print.ql index 1a560d48e3d..f0ba47eafeb 100644 --- a/python/ql/examples/snippets/print.ql +++ b/python/ql/examples/snippets/print.ql @@ -9,9 +9,9 @@ import python from AstNode print where - /* Python 2 without `from __future__ import print_function` */ - print instanceof Print - or - /* Python 3 or with `from __future__ import print_function` */ - print.(Call).getFunc().pointsTo(Value::named("print")) + /* Python 2 without `from __future__ import print_function` */ + print instanceof Print + or + /* Python 3 or with `from __future__ import print_function` */ + print.(Call).getFunc().pointsTo(Value::named("print")) select print diff --git a/python/ql/examples/snippets/private_access.ql b/python/ql/examples/snippets/private_access.ql index 14548864579..d9e28db2875 100644 --- a/python/ql/examples/snippets/private_access.ql +++ b/python/ql/examples/snippets/private_access.ql @@ -9,12 +9,12 @@ import python predicate is_private(Attribute a) { - a.getName().matches("\\_%") and - not a.getName().matches("\\_\\_%\\_\\_") + a.getName().matches("\\_%") and + not a.getName().matches("\\_\\_%\\_\\_") } from Attribute access where - is_private(access) and - not access.getObject().(Name).getId() = "self" + is_private(access) and + not access.getObject().(Name).getId() = "self" select access diff --git a/python/ql/examples/snippets/raise_exception.ql b/python/ql/examples/snippets/raise_exception.ql index ce69c353780..12e4f93a349 100644 --- a/python/ql/examples/snippets/raise_exception.ql +++ b/python/ql/examples/snippets/raise_exception.ql @@ -11,6 +11,6 @@ import python from Raise raise, ClassValue ex where - ex.getName() = "AnException" and - raise.getException().pointsTo(ex.getASuperType()) + ex.getName() = "AnException" and + raise.getException().pointsTo(ex.getASuperType()) select raise, "Don't raise instances of 'AnException'" diff --git a/python/ql/examples/snippets/store_none.ql b/python/ql/examples/snippets/store_none.ql index 88aaac47f56..57be82f229d 100644 --- a/python/ql/examples/snippets/store_none.ql +++ b/python/ql/examples/snippets/store_none.ql @@ -13,6 +13,6 @@ import python from SubscriptNode store where - store.isStore() and - store.getIndex().pointsTo(Value::named("None")) + store.isStore() and + store.getIndex().pointsTo(Value::named("None")) select store diff --git a/python/ql/examples/snippets/tryfinally.ql b/python/ql/examples/snippets/tryfinally.ql index bf5ea3c61a6..b95b386f962 100644 --- a/python/ql/examples/snippets/tryfinally.ql +++ b/python/ql/examples/snippets/tryfinally.ql @@ -11,6 +11,6 @@ import python from Try t where - exists(t.getFinalbody()) and - not exists(t.getAHandler()) + exists(t.getFinalbody()) and + not exists(t.getAHandler()) select t diff --git a/python/ql/src/Classes/ClassAttributes.qll b/python/ql/src/Classes/ClassAttributes.qll index 5da7d29ace3..82c573f8f72 100644 --- a/python/ql/src/Classes/ClassAttributes.qll +++ b/python/ql/src/Classes/ClassAttributes.qll @@ -3,140 +3,140 @@ private import semmle.python.pointsto.PointsTo /** Helper class for UndefinedClassAttribute.ql and MaybeUndefinedClassAttribute.ql */ class CheckClass extends ClassObject { - private predicate ofInterest() { - not this.unknowableAttributes() and - not this.getPyClass().isProbableMixin() and - this.getPyClass().isPublic() and - not this.getPyClass().getScope() instanceof Function and - not this.probablyAbstract() and - not this.declaresAttribute("__new__") and - not this.selfDictAssigns() and - not this.lookupAttribute("__getattribute__") != object_getattribute() and - not this.hasAttribute("__getattr__") and - not this.selfSetattr() and - /* If class overrides object.__init__, but we can't resolve it to a Python function then give up */ - forall(ClassObject sup | - sup = this.getAnImproperSuperType() and - sup.declaresAttribute("__init__") and - not sup = theObjectType() - | - sup.declaredAttribute("__init__") instanceof PyFunctionObject - ) - } + private predicate ofInterest() { + not this.unknowableAttributes() and + not this.getPyClass().isProbableMixin() and + this.getPyClass().isPublic() and + not this.getPyClass().getScope() instanceof Function and + not this.probablyAbstract() and + not this.declaresAttribute("__new__") and + not this.selfDictAssigns() and + not this.lookupAttribute("__getattribute__") != object_getattribute() and + not this.hasAttribute("__getattr__") and + not this.selfSetattr() and + /* If class overrides object.__init__, but we can't resolve it to a Python function then give up */ + forall(ClassObject sup | + sup = this.getAnImproperSuperType() and + sup.declaresAttribute("__init__") and + not sup = theObjectType() + | + sup.declaredAttribute("__init__") instanceof PyFunctionObject + ) + } - predicate alwaysDefines(string name) { - auto_name(name) or - this.hasAttribute(name) or - this.getAnImproperSuperType().assignedInInit(name) or - this.getMetaClass().assignedInInit(name) - } + predicate alwaysDefines(string name) { + auto_name(name) or + this.hasAttribute(name) or + this.getAnImproperSuperType().assignedInInit(name) or + this.getMetaClass().assignedInInit(name) + } - predicate sometimesDefines(string name) { - this.alwaysDefines(name) + predicate sometimesDefines(string name) { + this.alwaysDefines(name) + or + exists(SelfAttributeStore sa | + sa.getScope().getScope+() = this.getAnImproperSuperType().getPyClass() + | + name = sa.getName() + ) + } + + private predicate selfDictAssigns() { + exists(Assign a, SelfAttributeRead self_dict, Subscript sub | + self_dict.getName() = "__dict__" and + ( + self_dict = sub.getObject() or - exists(SelfAttributeStore sa | - sa.getScope().getScope+() = this.getAnImproperSuperType().getPyClass() - | - name = sa.getName() + /* Indirect assignment via temporary variable */ + exists(SsaVariable v | + v.getAUse() = sub.getObject().getAFlowNode() and + v.getDefinition().(DefinitionNode).getValue() = self_dict.getAFlowNode() ) - } + ) and + a.getATarget() = sub and + exists(FunctionObject meth | + meth = this.lookupAttribute(_) and a.getScope() = meth.getFunction() + ) + ) + } - private predicate selfDictAssigns() { - exists(Assign a, SelfAttributeRead self_dict, Subscript sub | - self_dict.getName() = "__dict__" and - ( - self_dict = sub.getObject() - or - /* Indirect assignment via temporary variable */ - exists(SsaVariable v | - v.getAUse() = sub.getObject().getAFlowNode() and - v.getDefinition().(DefinitionNode).getValue() = self_dict.getAFlowNode() - ) - ) and - a.getATarget() = sub and - exists(FunctionObject meth | - meth = this.lookupAttribute(_) and a.getScope() = meth.getFunction() - ) - ) - } + pragma[nomagic] + private predicate monkeyPatched(string name) { + exists(Attribute a | + a.getCtx() instanceof Store and + PointsTo::points_to(a.getObject().getAFlowNode(), _, this, _, _) and + a.getName() = name + ) + } - pragma[nomagic] - private predicate monkeyPatched(string name) { - exists(Attribute a | - a.getCtx() instanceof Store and - PointsTo::points_to(a.getObject().getAFlowNode(), _, this, _, _) and - a.getName() = name - ) - } + private predicate selfSetattr() { + exists(Call c, Name setattr, Name self, Function method | + ( + method.getScope() = this.getPyClass() or + method.getScope() = this.getASuperType().getPyClass() + ) and + c.getScope() = method and + c.getFunc() = setattr and + setattr.getId() = "setattr" and + c.getArg(0) = self and + self.getId() = "self" + ) + } - private predicate selfSetattr() { - exists(Call c, Name setattr, Name self, Function method | - ( - method.getScope() = this.getPyClass() or - method.getScope() = this.getASuperType().getPyClass() - ) and - c.getScope() = method and - c.getFunc() = setattr and - setattr.getId() = "setattr" and - c.getArg(0) = self and - self.getId() = "self" - ) - } + predicate interestingUndefined(SelfAttributeRead a) { + exists(string name | name = a.getName() | + interestingContext(a, name) and + not this.definedInBlock(a.getAFlowNode().getBasicBlock(), name) + ) + } - predicate interestingUndefined(SelfAttributeRead a) { - exists(string name | name = a.getName() | - interestingContext(a, name) and - not this.definedInBlock(a.getAFlowNode().getBasicBlock(), name) - ) - } + private predicate interestingContext(SelfAttributeRead a, string name) { + name = a.getName() and + this.ofInterest() and + this.getPyClass() = a.getScope().getScope() and + not a.locallyDefined() and + not a.guardedByHasattr() and + a.getScope().isPublic() and + not this.monkeyPatched(name) and + not attribute_assigned_in_method(lookupAttribute("setUp"), name) + } - private predicate interestingContext(SelfAttributeRead a, string name) { - name = a.getName() and - this.ofInterest() and - this.getPyClass() = a.getScope().getScope() and - not a.locallyDefined() and - not a.guardedByHasattr() and - a.getScope().isPublic() and - not this.monkeyPatched(name) and - not attribute_assigned_in_method(lookupAttribute("setUp"), name) - } + private predicate probablyAbstract() { + this.getName().matches("Abstract%") + or + this.isAbstract() + } - private predicate probablyAbstract() { - this.getName().matches("Abstract%") - or - this.isAbstract() - } + pragma[nomagic] + private predicate definitionInBlock(BasicBlock b, string name) { + exists(SelfAttributeStore sa | + sa.getAFlowNode().getBasicBlock() = b and + sa.getName() = name and + sa.getClass() = this.getPyClass() + ) + or + exists(FunctionObject method | this.lookupAttribute(_) = method | + attribute_assigned_in_method(method, name) and + b = method.getACall().getBasicBlock() + ) + } - pragma[nomagic] - private predicate definitionInBlock(BasicBlock b, string name) { - exists(SelfAttributeStore sa | - sa.getAFlowNode().getBasicBlock() = b and - sa.getName() = name and - sa.getClass() = this.getPyClass() - ) - or - exists(FunctionObject method | this.lookupAttribute(_) = method | - attribute_assigned_in_method(method, name) and - b = method.getACall().getBasicBlock() - ) - } - - pragma[nomagic] - private predicate definedInBlock(BasicBlock b, string name) { - // manual specialisation: this is only called from interestingUndefined, - // so we can push the context in from there, which must apply to a - // SelfAttributeRead in the same scope - exists(SelfAttributeRead a | a.getScope() = b.getScope() and name = a.getName() | - interestingContext(a, name) - ) and - this.definitionInBlock(b, name) - or - exists(BasicBlock prev | this.definedInBlock(prev, name) and prev.getASuccessor() = b) - } + pragma[nomagic] + private predicate definedInBlock(BasicBlock b, string name) { + // manual specialisation: this is only called from interestingUndefined, + // so we can push the context in from there, which must apply to a + // SelfAttributeRead in the same scope + exists(SelfAttributeRead a | a.getScope() = b.getScope() and name = a.getName() | + interestingContext(a, name) + ) and + this.definitionInBlock(b, name) + or + exists(BasicBlock prev | this.definedInBlock(prev, name) and prev.getASuccessor() = b) + } } private Object object_getattribute() { - result.asBuiltin() = theObjectType().asBuiltin().getMember("__getattribute__") + result.asBuiltin() = theObjectType().asBuiltin().getMember("__getattribute__") } private predicate auto_name(string name) { name = "__class__" or name = "__dict__" } diff --git a/python/ql/src/Classes/ConflictingAttributesInBaseClasses.ql b/python/ql/src/Classes/ConflictingAttributesInBaseClasses.ql index e1f95e36eb6..34a9e133075 100644 --- a/python/ql/src/Classes/ConflictingAttributesInBaseClasses.ql +++ b/python/ql/src/Classes/ConflictingAttributesInBaseClasses.ql @@ -14,48 +14,48 @@ import python predicate does_nothing(PyFunctionObject f) { - not exists(Stmt s | s.getScope() = f.getFunction() | - not s instanceof Pass and not s.(ExprStmt).getValue() = f.getFunction().getDocString() - ) + not exists(Stmt s | s.getScope() = f.getFunction() | + not s instanceof Pass and not s.(ExprStmt).getValue() = f.getFunction().getDocString() + ) } /* 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 | - meth.getScope() = f.getFunction() and - meth.getFunc() = attr and - attr.getObject() = sup and - attr.getName() = f.getName() and - sup.getFunc() = v.getAnAccess() and - v.getId() = "super" - ) + exists(Call sup, Call meth, Attribute attr, GlobalVariable v | + meth.getScope() = f.getFunction() and + meth.getFunc() = attr and + attr.getObject() = sup and + attr.getName() = f.getName() and + sup.getFunc() = v.getAnAccess() and + v.getId() = "super" + ) } /** Holds if the given name is allowed for some reason */ predicate allowed(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" + name = "process_request" } from - ClassObject c, ClassObject b1, ClassObject b2, string name, int i1, int i2, Object o1, Object o2 + 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 allowed(name) and - not o1.overrides(o2) and - not o2.overrides(o1) and - not c.declaresAttribute(name) + 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 allowed(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() + o1.toString(), o2, o2.toString() diff --git a/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.ql b/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.ql index a0fff36b344..15d44f9a1eb 100644 --- a/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.ql +++ b/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.ql @@ -15,21 +15,21 @@ import semmle.python.SelfAttribute import Equality predicate class_stores_to_attribute(ClassValue cls, SelfAttributeStore store, string name) { - exists(FunctionValue f | - f = cls.declaredAttribute(_) and store.getScope() = f.getScope() and store.getName() = name - ) and - /* Exclude classes used as metaclasses */ - not cls.getASuperType() = ClassValue::type() + exists(FunctionValue f | + f = cls.declaredAttribute(_) and store.getScope() = f.getScope() and store.getName() = name + ) and + /* Exclude classes used as metaclasses */ + not cls.getASuperType() = ClassValue::type() } predicate should_override_eq(ClassValue cls, Value base_eq) { - not cls.declaresAttribute("__eq__") and - exists(ClassValue sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq | - not exists(GenericEqMethod eq | eq.getScope() = sup.getScope()) and - not exists(IdentityEqMethod eq | eq.getScope() = sup.getScope()) and - not base_eq.(FunctionValue).getScope() instanceof IdentityEqMethod and - not base_eq = ClassValue::object().declaredAttribute("__eq__") - ) + not cls.declaresAttribute("__eq__") and + exists(ClassValue sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq | + not exists(GenericEqMethod eq | eq.getScope() = sup.getScope()) and + not exists(IdentityEqMethod eq | eq.getScope() = sup.getScope()) and + not base_eq.(FunctionValue).getScope() instanceof IdentityEqMethod and + not base_eq = ClassValue::object().declaredAttribute("__eq__") + ) } /** @@ -37,21 +37,21 @@ predicate should_override_eq(ClassValue cls, Value base_eq) { * which implies that the __eq__ method does not need to be overridden. */ predicate superclassEqExpectsAttribute(ClassValue cls, FunctionValue base_eq, string attrname) { - not cls.declaresAttribute("__eq__") and - exists(ClassValue sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq | - exists(SelfAttributeRead store | store.getName() = attrname | - store.getScope() = base_eq.getScope() - ) + not cls.declaresAttribute("__eq__") and + exists(ClassValue sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq | + exists(SelfAttributeRead store | store.getName() = attrname | + store.getScope() = base_eq.getScope() ) + ) } from ClassValue cls, SelfAttributeStore store, Value 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()) + 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() + "The class '" + cls.getName() + "' does not override $@, but adds the new attribute $@.", base_eq, + "'__eq__'", store, store.getName() diff --git a/python/ql/src/Classes/Equality.qll b/python/ql/src/Classes/Equality.qll index 27d17d863a4..347f5057c38 100644 --- a/python/ql/src/Classes/Equality.qll +++ b/python/ql/src/Classes/Equality.qll @@ -1,14 +1,14 @@ import python private Attribute dictAccess(LocalVariable var) { - result.getName() = "__dict__" and - result.getObject() = var.getAnAccess() + result.getName() = "__dict__" and + result.getObject() = var.getAnAccess() } private Call getattr(LocalVariable obj, LocalVariable attr) { - result.getFunc().(Name).getId() = "getattr" and - result.getArg(0) = obj.getAnAccess() and - result.getArg(1) = attr.getAnAccess() + result.getFunc().(Name).getId() = "getattr" and + result.getArg(0) = obj.getAnAccess() and + result.getArg(1) = attr.getAnAccess() } /** @@ -16,59 +16,59 @@ private Call getattr(LocalVariable obj, LocalVariable attr) { * or compares attributes using `getattr`. */ class GenericEqMethod extends Function { - GenericEqMethod() { - this.getName() = "__eq__" and - exists(LocalVariable self, LocalVariable other | - self.getAnAccess() = this.getArg(0) and - self.getId() = "self" and - other.getAnAccess() = this.getArg(1) and - exists(Compare eq | - eq.getOp(0) instanceof Eq or - eq.getOp(0) instanceof NotEq - | - // `self.__dict__ == other.__dict__` - eq.getAChildNode() = dictAccess(self) and - eq.getAChildNode() = dictAccess(other) - or - // `getattr(self, var) == getattr(other, var)` - exists(Variable var | - eq.getAChildNode() = getattr(self, var) and - eq.getAChildNode() = getattr(other, var) - ) - ) + GenericEqMethod() { + this.getName() = "__eq__" and + exists(LocalVariable self, LocalVariable other | + self.getAnAccess() = this.getArg(0) and + self.getId() = "self" and + other.getAnAccess() = this.getArg(1) and + exists(Compare eq | + eq.getOp(0) instanceof Eq or + eq.getOp(0) instanceof NotEq + | + // `self.__dict__ == other.__dict__` + eq.getAChildNode() = dictAccess(self) and + eq.getAChildNode() = dictAccess(other) + or + // `getattr(self, var) == getattr(other, var)` + exists(Variable var | + eq.getAChildNode() = getattr(self, var) and + eq.getAChildNode() = getattr(other, var) ) - } + ) + ) + } } /** An `__eq__` method that just does `self is other` */ class IdentityEqMethod extends Function { - IdentityEqMethod() { - this.getName() = "__eq__" and - exists(LocalVariable self, LocalVariable other | - self.getAnAccess() = this.getArg(0) and - self.getId() = "self" and - other.getAnAccess() = this.getArg(1) and - exists(Compare eq | eq.getOp(0) instanceof Is | - eq.getAChildNode() = self.getAnAccess() and - eq.getAChildNode() = other.getAnAccess() - ) - ) - } + IdentityEqMethod() { + this.getName() = "__eq__" and + exists(LocalVariable self, LocalVariable other | + self.getAnAccess() = this.getArg(0) and + self.getId() = "self" and + other.getAnAccess() = this.getArg(1) and + exists(Compare eq | eq.getOp(0) instanceof Is | + eq.getAChildNode() = self.getAnAccess() and + eq.getAChildNode() = other.getAnAccess() + ) + ) + } } /** An (in)equality method that delegates to its complement */ class DelegatingEqualityMethod extends Function { - DelegatingEqualityMethod() { - exists(Return ret, UnaryExpr not_, Compare comp, Cmpop op, Parameter p0, Parameter p1 | - ret.getScope() = this and - ret.getValue() = not_ and - not_.getOp() instanceof Not and - not_.getOperand() = comp and - comp.compares(p0.getVariable().getAnAccess(), op, p1.getVariable().getAnAccess()) - | - this.getName() = "__eq__" and op instanceof NotEq - or - this.getName() = "__ne__" and op instanceof Eq - ) - } + DelegatingEqualityMethod() { + exists(Return ret, UnaryExpr not_, Compare comp, Cmpop op, Parameter p0, Parameter p1 | + ret.getScope() = this and + ret.getValue() = not_ and + not_.getOp() instanceof Not and + not_.getOperand() = comp and + comp.compares(p0.getVariable().getAnAccess(), op, p1.getVariable().getAnAccess()) + | + this.getName() = "__eq__" and op instanceof NotEq + or + this.getName() = "__ne__" and op instanceof Eq + ) + } } diff --git a/python/ql/src/Classes/EqualsOrHash.ql b/python/ql/src/Classes/EqualsOrHash.ql index 795e7f4c0ff..26be0c2ec43 100644 --- a/python/ql/src/Classes/EqualsOrHash.ql +++ b/python/ql/src/Classes/EqualsOrHash.ql @@ -14,49 +14,49 @@ import python CallableValue defines_equality(ClassValue c, string name) { - ( - name = "__eq__" - or - major_version() = 2 and name = "__cmp__" - ) and - result = c.declaredAttribute(name) + ( + name = "__eq__" + or + major_version() = 2 and name = "__cmp__" + ) and + result = c.declaredAttribute(name) } CallableValue implemented_method(ClassValue c, string name) { - result = defines_equality(c, name) - or - result = c.declaredAttribute("__hash__") and name = "__hash__" + result = defines_equality(c, name) + or + result = c.declaredAttribute("__hash__") and name = "__hash__" } string unimplemented_method(ClassValue c) { - not exists(defines_equality(c, _)) and - ( - result = "__eq__" and major_version() = 3 - or - major_version() = 2 and result = "__eq__ or __cmp__" - ) + not exists(defines_equality(c, _)) and + ( + result = "__eq__" and major_version() = 3 or - /* Python 3 automatically makes classes unhashable if __eq__ is defined, but __hash__ is not */ - not c.declaresAttribute(result) and result = "__hash__" and major_version() = 2 + major_version() = 2 and result = "__eq__ or __cmp__" + ) + or + /* Python 3 automatically makes classes unhashable if __eq__ is defined, but __hash__ is not */ + not c.declaresAttribute(result) and result = "__hash__" and major_version() = 2 } /** Holds if this class is unhashable */ predicate unhashable(ClassValue cls) { - cls.lookup("__hash__") = Value::named("None") - or - cls.lookup("__hash__").(CallableValue).neverReturns() + cls.lookup("__hash__") = Value::named("None") + or + cls.lookup("__hash__").(CallableValue).neverReturns() } predicate violates_hash_contract(ClassValue c, string present, string missing, Value method) { - not unhashable(c) and - missing = unimplemented_method(c) and - method = implemented_method(c, present) and - not c.failedInference(_) + not unhashable(c) and + missing = unimplemented_method(c) and + method = implemented_method(c, present) and + not c.failedInference(_) } from ClassValue c, string present, string missing, CallableValue method where - violates_hash_contract(c, present, missing, method) and - exists(c.getScope()) // Suppress results that aren't from source + violates_hash_contract(c, present, missing, method) and + exists(c.getScope()) // Suppress results that aren't from source select method, "Class $@ implements " + present + " but does not define " + missing + ".", c, - c.getName() + c.getName() diff --git a/python/ql/src/Classes/EqualsOrNotEquals.ql b/python/ql/src/Classes/EqualsOrNotEquals.ql index 7457de441b0..adac5a20e87 100644 --- a/python/ql/src/Classes/EqualsOrNotEquals.ql +++ b/python/ql/src/Classes/EqualsOrNotEquals.ql @@ -16,33 +16,33 @@ import Equality string equals_or_ne() { result = "__eq__" or result = "__ne__" } predicate total_ordering(Class cls) { - exists(Attribute a | a = cls.getADecorator() | a.getName() = "total_ordering") - or - exists(Name n | n = cls.getADecorator() | n.getId() = "total_ordering") + exists(Attribute a | a = cls.getADecorator() | a.getName() = "total_ordering") + or + exists(Name n | n = cls.getADecorator() | n.getId() = "total_ordering") } CallableValue implemented_method(ClassValue c, string name) { - result = c.declaredAttribute(name) and name = equals_or_ne() + result = c.declaredAttribute(name) and name = equals_or_ne() } string unimplemented_method(ClassValue c) { - not c.declaresAttribute(result) and result = equals_or_ne() + not c.declaresAttribute(result) and result = equals_or_ne() } predicate violates_equality_contract( - ClassValue c, string present, string missing, CallableValue method + ClassValue c, string present, string missing, CallableValue method ) { - missing = unimplemented_method(c) and - method = implemented_method(c, present) and - not c.failedInference(_) and - not total_ordering(c.getScope()) and - /* Python 3 automatically implements __ne__ if __eq__ is defined, but not vice-versa */ - not (major_version() = 3 and present = "__eq__" and missing = "__ne__") and - not method.getScope() instanceof DelegatingEqualityMethod and - not c.lookup(missing).(CallableValue).getScope() instanceof DelegatingEqualityMethod + missing = unimplemented_method(c) and + method = implemented_method(c, present) and + not c.failedInference(_) and + not total_ordering(c.getScope()) and + /* Python 3 automatically implements __ne__ if __eq__ is defined, but not vice-versa */ + not (major_version() = 3 and present = "__eq__" and missing = "__ne__") and + not method.getScope() instanceof DelegatingEqualityMethod and + not c.lookup(missing).(CallableValue).getScope() instanceof DelegatingEqualityMethod } from ClassValue c, string present, string missing, CallableValue method where violates_equality_contract(c, present, missing, method) select method, "Class $@ implements " + present + " but does not implement " + missing + ".", c, - c.getName() + c.getName() diff --git a/python/ql/src/Classes/IncompleteOrdering.ql b/python/ql/src/Classes/IncompleteOrdering.ql index 7755696bd45..d6cd1230ece 100644 --- a/python/ql/src/Classes/IncompleteOrdering.ql +++ b/python/ql/src/Classes/IncompleteOrdering.ql @@ -13,61 +13,61 @@ import python predicate total_ordering(Class cls) { - exists(Attribute a | a = cls.getADecorator() | a.getName() = "total_ordering") - or - exists(Name n | n = cls.getADecorator() | n.getId() = "total_ordering") + exists(Attribute a | a = cls.getADecorator() | a.getName() = "total_ordering") + or + exists(Name n | n = cls.getADecorator() | n.getId() = "total_ordering") } string ordering_name(int n) { - result = "__lt__" and n = 1 - or - result = "__le__" and n = 2 - or - result = "__gt__" and n = 3 - or - result = "__ge__" and n = 4 + result = "__lt__" and n = 1 + or + result = "__le__" and n = 2 + or + result = "__gt__" and n = 3 + or + result = "__ge__" and n = 4 } predicate overrides_ordering_method(ClassValue c, string name) { - name = ordering_name(_) and - ( - c.declaresAttribute(name) - or - exists(ClassValue sup | sup = c.getASuperType() and not sup = Value::named("object") | - sup.declaresAttribute(name) - ) + name = ordering_name(_) and + ( + c.declaresAttribute(name) + or + exists(ClassValue sup | sup = c.getASuperType() and not sup = Value::named("object") | + sup.declaresAttribute(name) ) + ) } string unimplemented_ordering(ClassValue c, int n) { - not c = Value::named("object") and - not overrides_ordering_method(c, result) and - result = ordering_name(n) + not c = Value::named("object") and + not overrides_ordering_method(c, result) and + result = ordering_name(n) } string unimplemented_ordering_methods(ClassValue c, int n) { - n = 0 and result = "" and exists(unimplemented_ordering(c, _)) + n = 0 and result = "" and exists(unimplemented_ordering(c, _)) + or + exists(string prefix, int nm1 | n = nm1 + 1 and prefix = unimplemented_ordering_methods(c, nm1) | + prefix = "" and result = unimplemented_ordering(c, n) or - exists(string prefix, int nm1 | n = nm1 + 1 and prefix = unimplemented_ordering_methods(c, nm1) | - prefix = "" and result = unimplemented_ordering(c, n) - or - result = prefix and not exists(unimplemented_ordering(c, n)) and n < 5 - or - prefix != "" and result = prefix + " or " + unimplemented_ordering(c, n) - ) + result = prefix and not exists(unimplemented_ordering(c, n)) and n < 5 + or + prefix != "" and result = prefix + " or " + unimplemented_ordering(c, n) + ) } Value ordering_method(ClassValue c, string name) { - /* If class doesn't declare a method then don't blame this class (the superclass will be blamed). */ - name = ordering_name(_) and result = c.declaredAttribute(name) + /* If class doesn't declare a method then don't blame this class (the superclass will be blamed). */ + name = ordering_name(_) and result = c.declaredAttribute(name) } from ClassValue c, Value ordering, string name where - not c.failedInference(_) and - not total_ordering(c.getScope()) and - ordering = ordering_method(c, name) and - exists(unimplemented_ordering(c, _)) + not c.failedInference(_) and + not total_ordering(c.getScope()) and + ordering = ordering_method(c, name) and + exists(unimplemented_ordering(c, _)) select c, - "Class " + c.getName() + " implements $@, but does not implement " + - unimplemented_ordering_methods(c, 4) + ".", ordering, name + "Class " + c.getName() + " implements $@, but does not implement " + + unimplemented_ordering_methods(c, 4) + ".", ordering, name diff --git a/python/ql/src/Classes/InconsistentMRO.ql b/python/ql/src/Classes/InconsistentMRO.ql index a9541bc9023..90c1d386938 100644 --- a/python/ql/src/Classes/InconsistentMRO.ql +++ b/python/ql/src/Classes/InconsistentMRO.ql @@ -13,18 +13,18 @@ import python ClassObject left_base(ClassObject type, ClassObject base) { - exists(int i | i > 0 and type.getBaseType(i) = base and result = type.getBaseType(i - 1)) + exists(int i | i > 0 and type.getBaseType(i) = base and result = type.getBaseType(i - 1)) } predicate invalid_mro(ClassObject t, ClassObject left, ClassObject right) { - t.isNewStyle() and - left = left_base(t, right) and - left = right.getAnImproperSuperType() + t.isNewStyle() and + left = left_base(t, right) and + left = right.getAnImproperSuperType() } from ClassObject t, ClassObject left, ClassObject right where invalid_mro(t, left, right) select t, - "Construction of class " + t.getName() + - " can fail due to invalid method resolution order(MRO) for bases $@ and $@.", left, - left.getName(), right, right.getName() + "Construction of class " + t.getName() + + " can fail due to invalid method resolution order(MRO) for bases $@ and $@.", left, + left.getName(), right, right.getName() diff --git a/python/ql/src/Classes/InitCallsSubclassMethod.ql b/python/ql/src/Classes/InitCallsSubclassMethod.ql index b0e1cc0d8f0..19865751c53 100644 --- a/python/ql/src/Classes/InitCallsSubclassMethod.ql +++ b/python/ql/src/Classes/InitCallsSubclassMethod.ql @@ -14,17 +14,17 @@ import python from - ClassObject supercls, string method, Call call, FunctionObject overriding, - FunctionObject overridden + ClassObject supercls, string method, Call call, FunctionObject overriding, + FunctionObject overridden where - exists(FunctionObject init, SelfAttribute sa | - supercls.declaredAttribute("__init__") = init and - call.getScope() = init.getFunction() and - call.getFunc() = sa - | - sa.getName() = method and - overridden = supercls.declaredAttribute(method) and - overriding.overrides(overridden) - ) + exists(FunctionObject init, SelfAttribute sa | + supercls.declaredAttribute("__init__") = init and + call.getScope() = init.getFunction() and + call.getFunc() = sa + | + sa.getName() = method and + overridden = supercls.declaredAttribute(method) and + overriding.overrides(overridden) + ) select call, "Call to self.$@ in __init__ method, which is overridden by $@.", overridden, method, - overriding, overriding.descriptiveString() + overriding, overriding.descriptiveString() diff --git a/python/ql/src/Classes/MaybeUndefinedClassAttribute.ql b/python/ql/src/Classes/MaybeUndefinedClassAttribute.ql index ca8a260b863..7f4e27801c2 100644 --- a/python/ql/src/Classes/MaybeUndefinedClassAttribute.ql +++ b/python/ql/src/Classes/MaybeUndefinedClassAttribute.ql @@ -15,30 +15,30 @@ import semmle.python.SelfAttribute import ClassAttributes predicate guarded_by_other_attribute(SelfAttributeRead a, CheckClass c) { - c.sometimesDefines(a.getName()) and - exists(SelfAttributeRead guard, If i | - i.contains(a) and - c.assignedInInit(guard.getName()) - | - i.getTest() = guard - or - i.getTest().contains(guard) - ) + c.sometimesDefines(a.getName()) and + exists(SelfAttributeRead guard, If i | + i.contains(a) and + c.assignedInInit(guard.getName()) + | + i.getTest() = guard + or + i.getTest().contains(guard) + ) } predicate maybe_undefined_class_attribute(SelfAttributeRead a, CheckClass c) { - c.sometimesDefines(a.getName()) and - not c.alwaysDefines(a.getName()) and - c.interestingUndefined(a) and - not guarded_by_other_attribute(a, c) + c.sometimesDefines(a.getName()) and + not c.alwaysDefines(a.getName()) and + c.interestingUndefined(a) and + not guarded_by_other_attribute(a, c) } from Attribute a, ClassObject c, SelfAttributeStore sa where - maybe_undefined_class_attribute(a, c) and - sa.getClass() = c.getPyClass() and - sa.getName() = a.getName() + maybe_undefined_class_attribute(a, c) and + sa.getClass() = c.getPyClass() and + sa.getName() = a.getName() select a, - "Attribute '" + a.getName() + - "' is not defined in the class body nor in the __init__() method, but it is defined $@", sa, - "here" + "Attribute '" + a.getName() + + "' is not defined in the class body nor in the __init__() method, but it is defined $@", sa, + "here" diff --git a/python/ql/src/Classes/MethodCallOrder.qll b/python/ql/src/Classes/MethodCallOrder.qll index 494c234da3c..620cb802878 100644 --- a/python/ql/src/Classes/MethodCallOrder.qll +++ b/python/ql/src/Classes/MethodCallOrder.qll @@ -3,73 +3,73 @@ import python // Helper predicates for multiple call to __init__/__del__ queries. pragma[noinline] private predicate multiple_invocation_paths_helper( - FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2, FunctionObject multi + FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2, FunctionObject multi ) { - i1 != i2 and - i1 = top.getACallee+() and - i2 = top.getACallee+() and - i1.getFunction() = multi + i1 != i2 and + i1 = top.getACallee+() and + i2 = top.getACallee+() and + i1.getFunction() = multi } pragma[noinline] private predicate multiple_invocation_paths( - FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2, FunctionObject multi + FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2, FunctionObject multi ) { - multiple_invocation_paths_helper(top, i1, i2, multi) and - i2.getFunction() = multi + multiple_invocation_paths_helper(top, i1, i2, multi) and + i2.getFunction() = multi } /** Holds if `self.name` calls `multi` by multiple paths, and thus calls it more than once. */ predicate multiple_calls_to_superclass_method(ClassObject self, FunctionObject multi, string name) { - exists(FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2 | - multiple_invocation_paths(top, i1, i2, multi) and - top.runtime(self.declaredAttribute(name)) and - self.getASuperType().declaredAttribute(name) = multi - | - // Only called twice if called from different functions, - // or if one call-site can reach the other. - i1.getCall().getScope() != i2.getCall().getScope() - or - i1.getCall().strictlyReaches(i2.getCall()) - ) + exists(FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2 | + multiple_invocation_paths(top, i1, i2, multi) and + top.runtime(self.declaredAttribute(name)) and + self.getASuperType().declaredAttribute(name) = multi + | + // Only called twice if called from different functions, + // or if one call-site can reach the other. + i1.getCall().getScope() != i2.getCall().getScope() + or + i1.getCall().strictlyReaches(i2.getCall()) + ) } /** Holds if all attributes called `name` can be inferred to be methods. */ private predicate named_attributes_not_method(ClassObject cls, string name) { - cls.declaresAttribute(name) and not cls.declaredAttribute(name) instanceof FunctionObject + cls.declaresAttribute(name) and not cls.declaredAttribute(name) instanceof FunctionObject } /** Holds if `f` actually does something. */ private predicate does_something(FunctionObject f) { - f.isBuiltin() and not f = theObjectType().lookupAttribute("__init__") - or - exists(Stmt s | s = f.getFunction().getAStmt() and not s instanceof Pass) + f.isBuiltin() and not f = theObjectType().lookupAttribute("__init__") + or + exists(Stmt s | s = f.getFunction().getAStmt() and not s instanceof Pass) } /** Holds if `meth` looks like it should have a call to `name`, but does not */ private predicate missing_call(FunctionObject meth, string name) { - exists(CallNode call, AttrNode attr | - call.getScope() = meth.getFunction() and - call.getFunction() = attr and - attr.getName() = name and - not exists(FunctionObject f | f.getACall() = call) - ) + exists(CallNode call, AttrNode attr | + call.getScope() = meth.getFunction() and + call.getFunction() = attr and + attr.getName() = name and + not exists(FunctionObject f | f.getACall() = call) + ) } /** Holds if `self.name` does not call `missing`, even though it is expected to. */ predicate missing_call_to_superclass_method( - ClassObject self, FunctionObject top, FunctionObject missing, string name + ClassObject self, FunctionObject top, FunctionObject missing, string name ) { - missing = self.getASuperType().declaredAttribute(name) and - top = self.lookupAttribute(name) and - /* There is no call to missing originating from top */ - not top.getACallee*() = missing and - /* Make sure that all named 'methods' are objects that we can understand. */ - not exists(ClassObject sup | - sup = self.getAnImproperSuperType() and - named_attributes_not_method(sup, name) - ) and - not self.isAbstract() and - does_something(missing) and - not missing_call(top, name) + missing = self.getASuperType().declaredAttribute(name) and + top = self.lookupAttribute(name) and + /* There is no call to missing originating from top */ + not top.getACallee*() = missing and + /* Make sure that all named 'methods' are objects that we can understand. */ + not exists(ClassObject sup | + sup = self.getAnImproperSuperType() and + named_attributes_not_method(sup, name) + ) and + not self.isAbstract() and + does_something(missing) and + not missing_call(top, name) } diff --git a/python/ql/src/Classes/MissingCallToDel.ql b/python/ql/src/Classes/MissingCallToDel.ql index b54a9b8c782..e658dba1389 100644 --- a/python/ql/src/Classes/MissingCallToDel.ql +++ b/python/ql/src/Classes/MissingCallToDel.ql @@ -15,10 +15,10 @@ import MethodCallOrder from ClassObject self, FunctionObject missing where - missing_call_to_superclass_method(self, _, missing, "__del__") and - not missing.neverReturns() and - not self.failedInference() and - not missing.isBuiltin() + missing_call_to_superclass_method(self, _, missing, "__del__") and + not missing.neverReturns() and + not self.failedInference() and + not missing.isBuiltin() select self, - "Class " + self.getName() + " may not be cleaned up properly as $@ is not called during deletion.", - missing, missing.descriptiveString() + "Class " + self.getName() + " may not be cleaned up properly as $@ is not called during deletion.", + missing, missing.descriptiveString() diff --git a/python/ql/src/Classes/MissingCallToInit.ql b/python/ql/src/Classes/MissingCallToInit.ql index bb6121e33b6..6daa06de79c 100644 --- a/python/ql/src/Classes/MissingCallToInit.ql +++ b/python/ql/src/Classes/MissingCallToInit.ql @@ -15,14 +15,14 @@ import MethodCallOrder from ClassObject self, FunctionObject initializer, FunctionObject missing where - self.lookupAttribute("__init__") = initializer and - missing_call_to_superclass_method(self, initializer, missing, "__init__") and - // If a superclass is incorrect, don't flag this class as well. - not missing_call_to_superclass_method(self.getASuperType(), _, missing, "__init__") and - not missing.neverReturns() and - not self.failedInference() and - not missing.isBuiltin() and - not self.isAbstract() + self.lookupAttribute("__init__") = initializer and + missing_call_to_superclass_method(self, initializer, missing, "__init__") and + // If a superclass is incorrect, don't flag this class as well. + not missing_call_to_superclass_method(self.getASuperType(), _, missing, "__init__") and + not missing.neverReturns() and + not self.failedInference() and + not missing.isBuiltin() and + not self.isAbstract() select self, - "Class " + self.getName() + " may not be initialized properly as $@ is not called from its $@.", - missing, missing.descriptiveString(), initializer, "__init__ method" + "Class " + self.getName() + " may not be initialized properly as $@ is not called from its $@.", + missing, missing.descriptiveString(), initializer, "__init__ method" diff --git a/python/ql/src/Classes/MutatingDescriptor.ql b/python/ql/src/Classes/MutatingDescriptor.ql index 1f1188c2830..a7118883951 100644 --- a/python/ql/src/Classes/MutatingDescriptor.ql +++ b/python/ql/src/Classes/MutatingDescriptor.ql @@ -13,20 +13,20 @@ import python predicate mutates_descriptor(ClassObject cls, SelfAttributeStore s) { - cls.isDescriptorType() and - exists(PyFunctionObject f, PyFunctionObject get_set | - exists(string name | cls.lookupAttribute(name) = get_set | - name = "__get__" or name = "__set__" or name = "__delete__" - ) and - cls.lookupAttribute(_) = f and - get_set.getACallee*() = f and - not f.getName() = "__init__" and - s.getScope() = f.getFunction() - ) + cls.isDescriptorType() and + exists(PyFunctionObject f, PyFunctionObject get_set | + exists(string name | cls.lookupAttribute(name) = get_set | + name = "__get__" or name = "__set__" or name = "__delete__" + ) and + cls.lookupAttribute(_) = f and + get_set.getACallee*() = f and + not f.getName() = "__init__" and + s.getScope() = f.getFunction() + ) } from ClassObject cls, SelfAttributeStore s where mutates_descriptor(cls, s) select s, - "Mutation of descriptor $@ object may lead to action-at-a-distance effects or race conditions for properties.", - cls, cls.getName() + "Mutation of descriptor $@ object may lead to action-at-a-distance effects or race conditions for properties.", + cls, cls.getName() diff --git a/python/ql/src/Classes/OverwritingAttributeInSuperClass.ql b/python/ql/src/Classes/OverwritingAttributeInSuperClass.ql index 168348e7b1c..e77d5ec481f 100644 --- a/python/ql/src/Classes/OverwritingAttributeInSuperClass.ql +++ b/python/ql/src/Classes/OverwritingAttributeInSuperClass.ql @@ -14,79 +14,79 @@ import python class InitCallStmt extends ExprStmt { - InitCallStmt() { - exists(Call call, Attribute attr | call = this.getValue() and attr = call.getFunc() | - attr.getName() = "__init__" - ) - } + InitCallStmt() { + exists(Call call, Attribute attr | call = this.getValue() and attr = call.getFunc() | + attr.getName() = "__init__" + ) + } } predicate overwrites_which(Function subinit, AssignStmt write_attr, string which) { - write_attr.getScope() = subinit and - self_write_stmt(write_attr, _) and - exists(Stmt top | top.contains(write_attr) or top = write_attr | - ( - exists(int i, int j, InitCallStmt call | call.getScope() = subinit | - i > j and top = subinit.getStmt(i) and call = subinit.getStmt(j) and which = "superclass" - ) - or - exists(int i, int j, InitCallStmt call | call.getScope() = subinit | - i < j and top = subinit.getStmt(i) and call = subinit.getStmt(j) and which = "subclass" - ) - ) + write_attr.getScope() = subinit and + self_write_stmt(write_attr, _) and + exists(Stmt top | top.contains(write_attr) or top = write_attr | + ( + exists(int i, int j, InitCallStmt call | call.getScope() = subinit | + i > j and top = subinit.getStmt(i) and call = subinit.getStmt(j) and which = "superclass" + ) + or + exists(int i, int j, InitCallStmt call | call.getScope() = subinit | + i < j and top = subinit.getStmt(i) and call = subinit.getStmt(j) and which = "subclass" + ) ) + ) } predicate self_write_stmt(Stmt s, string attr) { - exists(Attribute a, Name self | - self = a.getObject() and - s.contains(a) and - self.getId() = "self" and - a.getCtx() instanceof Store and - a.getName() = attr - ) + exists(Attribute a, Name self | + self = a.getObject() and + s.contains(a) and + self.getId() = "self" and + a.getCtx() instanceof Store and + a.getName() = attr + ) } predicate both_assign_attribute(Stmt s1, Stmt s2, Function f1, Function f2) { - exists(string name | - s1.getScope() = f1 and - s2.getScope() = f2 and - self_write_stmt(s1, name) and - self_write_stmt(s2, name) - ) + exists(string name | + s1.getScope() = f1 and + s2.getScope() = f2 and + self_write_stmt(s1, name) and + self_write_stmt(s2, name) + ) } predicate attribute_overwritten( - AssignStmt overwrites, AssignStmt overwritten, string name, string classtype, string classname + AssignStmt overwrites, AssignStmt overwritten, string name, string classtype, string classname ) { - exists( - FunctionObject superinit, FunctionObject subinit, ClassObject superclass, ClassObject subclass, - AssignStmt subattr, AssignStmt superattr - | - ( - classtype = "superclass" and - classname = superclass.getName() and - overwrites = subattr and - overwritten = superattr - or - classtype = "subclass" and - classname = subclass.getName() and - overwrites = superattr and - overwritten = subattr - ) and - /* OK if overwritten in subclass and is a class attribute */ - (not exists(superclass.declaredAttribute(name)) or classtype = "subclass") and - superclass.declaredAttribute("__init__") = superinit and - subclass.declaredAttribute("__init__") = subinit and - superclass = subclass.getASuperType() and - overwrites_which(subinit.getFunction(), subattr, classtype) and - both_assign_attribute(subattr, superattr, subinit.getFunction(), superinit.getFunction()) and - self_write_stmt(superattr, name) - ) + exists( + FunctionObject superinit, FunctionObject subinit, ClassObject superclass, ClassObject subclass, + AssignStmt subattr, AssignStmt superattr + | + ( + classtype = "superclass" and + classname = superclass.getName() and + overwrites = subattr and + overwritten = superattr + or + classtype = "subclass" and + classname = subclass.getName() and + overwrites = superattr and + overwritten = subattr + ) and + /* OK if overwritten in subclass and is a class attribute */ + (not exists(superclass.declaredAttribute(name)) or classtype = "subclass") and + superclass.declaredAttribute("__init__") = superinit and + subclass.declaredAttribute("__init__") = subinit and + superclass = subclass.getASuperType() and + overwrites_which(subinit.getFunction(), subattr, classtype) and + both_assign_attribute(subattr, superattr, subinit.getFunction(), superinit.getFunction()) and + self_write_stmt(superattr, name) + ) } from string classtype, AssignStmt overwrites, AssignStmt overwritten, string name, string classname where attribute_overwritten(overwrites, overwritten, name, classtype, classname) select overwrites, - "Assignment overwrites attribute " + name + ", which was previously defined in " + classtype + - " $@.", overwritten, classname + "Assignment overwrites attribute " + name + ", which was previously defined in " + classtype + + " $@.", overwritten, classname diff --git a/python/ql/src/Classes/PropertyInOldStyleClass.ql b/python/ql/src/Classes/PropertyInOldStyleClass.ql index ff2bf13a9f8..6e1b9a063c0 100644 --- a/python/ql/src/Classes/PropertyInOldStyleClass.ql +++ b/python/ql/src/Classes/PropertyInOldStyleClass.ql @@ -15,5 +15,5 @@ import python from PropertyObject prop, ClassObject cls where cls.declaredAttribute(_) = prop and not cls.failedInference() and not cls.isNewStyle() select prop, - "Property " + prop.getName() + " will not work properly, as class " + cls.getName() + - " is an old-style class." + "Property " + prop.getName() + " will not work properly, as class " + cls.getName() + + " is an old-style class." diff --git a/python/ql/src/Classes/ShouldBeContextManager.ql b/python/ql/src/Classes/ShouldBeContextManager.ql index fa7cebf9c77..bdcc6dc2863 100644 --- a/python/ql/src/Classes/ShouldBeContextManager.ql +++ b/python/ql/src/Classes/ShouldBeContextManager.ql @@ -17,5 +17,5 @@ import python from ClassValue c where not c.isBuiltin() and not c.isContextManager() and exists(c.declaredAttribute("__del__")) select c, - "Class " + c.getName() + - " implements __del__ (presumably to release some resource). Consider making it a context manager." + "Class " + c.getName() + + " implements __del__ (presumably to release some resource). Consider making it a context manager." diff --git a/python/ql/src/Classes/SubclassShadowing.ql b/python/ql/src/Classes/SubclassShadowing.ql index ed1a79869b0..6594f5eee12 100644 --- a/python/ql/src/Classes/SubclassShadowing.ql +++ b/python/ql/src/Classes/SubclassShadowing.ql @@ -20,27 +20,27 @@ import python predicate shadowed_by_super_class( - ClassObject c, ClassObject supercls, Assign assign, FunctionObject f + ClassObject c, ClassObject supercls, Assign assign, FunctionObject f ) { - c.getASuperType() = supercls and - c.declaredAttribute(_) = f and - exists(FunctionObject init, Attribute attr | - supercls.declaredAttribute("__init__") = init and - attr = assign.getATarget() and - attr.getObject().(Name).getId() = "self" and - attr.getName() = f.getName() and - assign.getScope() = init.getOrigin().(FunctionExpr).getInnerScope() - ) and - /* - * It's OK if the super class defines the method as well. - * We assume that the original method must have been defined for a reason. - */ + c.getASuperType() = supercls and + c.declaredAttribute(_) = f and + exists(FunctionObject init, Attribute attr | + supercls.declaredAttribute("__init__") = init and + attr = assign.getATarget() and + attr.getObject().(Name).getId() = "self" and + attr.getName() = f.getName() and + assign.getScope() = init.getOrigin().(FunctionExpr).getInnerScope() + ) and + /* + * It's OK if the super class defines the method as well. + * We assume that the original method must have been defined for a reason. + */ - not supercls.hasAttribute(f.getName()) + not supercls.hasAttribute(f.getName()) } from ClassObject c, ClassObject supercls, Assign assign, FunctionObject shadowed where shadowed_by_super_class(c, supercls, assign, shadowed) select shadowed.getOrigin(), - "Method " + shadowed.getName() + " is shadowed by $@ in super class '" + supercls.getName() + "'.", - assign, "an attribute" + "Method " + shadowed.getName() + " is shadowed by $@ in super class '" + supercls.getName() + "'.", + assign, "an attribute" diff --git a/python/ql/src/Classes/SuperInOldStyleClass.ql b/python/ql/src/Classes/SuperInOldStyleClass.ql index aa4c62c6f08..f309c025fec 100644 --- a/python/ql/src/Classes/SuperInOldStyleClass.ql +++ b/python/ql/src/Classes/SuperInOldStyleClass.ql @@ -13,13 +13,13 @@ import python predicate uses_of_super_in_old_style_class(Call s) { - exists(Function f, ClassObject c | - s.getScope() = f and - f.getScope() = c.getPyClass() and - not c.failedInference() and - not c.isNewStyle() and - s.getFunc().(Name).getId() = "super" - ) + exists(Function f, ClassObject c | + s.getScope() = f and + f.getScope() = c.getPyClass() and + not c.failedInference() and + not c.isNewStyle() and + s.getFunc().(Name).getId() = "super" + ) } from Call c diff --git a/python/ql/src/Classes/SuperclassDelCalledMultipleTimes.ql b/python/ql/src/Classes/SuperclassDelCalledMultipleTimes.ql index cd4c74a5e86..f1ef04bb89a 100644 --- a/python/ql/src/Classes/SuperclassDelCalledMultipleTimes.ql +++ b/python/ql/src/Classes/SuperclassDelCalledMultipleTimes.ql @@ -15,14 +15,14 @@ import MethodCallOrder from ClassObject self, FunctionObject multi where - multiple_calls_to_superclass_method(self, multi, "__del__") and - not multiple_calls_to_superclass_method(self.getABaseType(), multi, "__del__") and - not exists(FunctionObject better | - multiple_calls_to_superclass_method(self, better, "__del__") and - better.overrides(multi) - ) and - not self.failedInference() + multiple_calls_to_superclass_method(self, multi, "__del__") and + not multiple_calls_to_superclass_method(self.getABaseType(), multi, "__del__") and + not exists(FunctionObject better | + multiple_calls_to_superclass_method(self, better, "__del__") and + better.overrides(multi) + ) and + not self.failedInference() select self, - "Class " + self.getName() + - " may not be cleaned up properly as $@ may be called multiple times during destruction.", multi, - multi.descriptiveString() + "Class " + self.getName() + + " may not be cleaned up properly as $@ may be called multiple times during destruction.", multi, + multi.descriptiveString() diff --git a/python/ql/src/Classes/SuperclassInitCalledMultipleTimes.ql b/python/ql/src/Classes/SuperclassInitCalledMultipleTimes.ql index 71d05533fde..cb90cb32510 100644 --- a/python/ql/src/Classes/SuperclassInitCalledMultipleTimes.ql +++ b/python/ql/src/Classes/SuperclassInitCalledMultipleTimes.ql @@ -15,15 +15,15 @@ import MethodCallOrder from ClassObject self, FunctionObject multi where - multi != theObjectType().lookupAttribute("__init__") and - multiple_calls_to_superclass_method(self, multi, "__init__") and - not multiple_calls_to_superclass_method(self.getABaseType(), multi, "__init__") and - not exists(FunctionObject better | - multiple_calls_to_superclass_method(self, better, "__init__") and - better.overrides(multi) - ) and - not self.failedInference() + multi != theObjectType().lookupAttribute("__init__") and + multiple_calls_to_superclass_method(self, multi, "__init__") and + not multiple_calls_to_superclass_method(self.getABaseType(), multi, "__init__") and + not exists(FunctionObject better | + multiple_calls_to_superclass_method(self, better, "__init__") and + better.overrides(multi) + ) and + not self.failedInference() select self, - "Class " + self.getName() + - " may not be initialized properly as $@ may be called multiple times during initialization.", - multi, multi.descriptiveString() + "Class " + self.getName() + + " may not be initialized properly as $@ may be called multiple times during initialization.", + multi, multi.descriptiveString() diff --git a/python/ql/src/Classes/UndefinedClassAttribute.ql b/python/ql/src/Classes/UndefinedClassAttribute.ql index bdbbcbf2496..348daa7f9fb 100644 --- a/python/ql/src/Classes/UndefinedClassAttribute.ql +++ b/python/ql/src/Classes/UndefinedClassAttribute.ql @@ -15,18 +15,18 @@ import semmle.python.SelfAttribute import ClassAttributes predicate undefined_class_attribute(SelfAttributeRead a, CheckClass c, int line, string name) { - name = a.getName() and - not c.sometimesDefines(name) and - c.interestingUndefined(a) and - line = a.getLocation().getStartLine() and - not attribute_assigned_in_method(c.getAMethodCalledFromInit(), name) + name = a.getName() and + not c.sometimesDefines(name) and + c.interestingUndefined(a) and + line = a.getLocation().getStartLine() and + not attribute_assigned_in_method(c.getAMethodCalledFromInit(), name) } predicate report_undefined_class_attribute(Attribute a, ClassObject c, string name) { - exists(int line | - undefined_class_attribute(a, c, line, name) and - line = min(int x | undefined_class_attribute(_, c, x, name)) - ) + exists(int line | + undefined_class_attribute(a, c, line, name) and + line = min(int x | undefined_class_attribute(_, c, x, name)) + ) } from Attribute a, ClassObject c, string name diff --git a/python/ql/src/Classes/UselessClass.ql b/python/ql/src/Classes/UselessClass.ql index e4e3f31f3a2..24bf446fc6a 100644 --- a/python/ql/src/Classes/UselessClass.ql +++ b/python/ql/src/Classes/UselessClass.ql @@ -13,76 +13,76 @@ import python predicate fewer_than_two_public_methods(Class cls, int methods) { - (methods = 0 or methods = 1) and - methods = count(Function f | f = cls.getAMethod() and not f = cls.getInitMethod()) + (methods = 0 or methods = 1) and + methods = count(Function f | f = cls.getAMethod() and not f = cls.getInitMethod()) } predicate does_not_define_special_method(Class cls) { - not exists(Function f | f = cls.getAMethod() and f.isSpecialMethod()) + not exists(Function f | f = cls.getAMethod() and f.isSpecialMethod()) } predicate no_inheritance(Class c) { - not exists(ClassValue cls, ClassValue other | - cls.getScope() = c and - other != ClassValue::object() - | - other.getABaseType() = cls or - cls.getABaseType() = other - ) and - not exists(Expr base | base = c.getABase() | - not base instanceof Name or base.(Name).getId() != "object" - ) + not exists(ClassValue cls, ClassValue other | + cls.getScope() = c and + other != ClassValue::object() + | + other.getABaseType() = cls or + cls.getABaseType() = other + ) and + not exists(Expr base | base = c.getABase() | + not base instanceof Name or base.(Name).getId() != "object" + ) } predicate is_decorated(Class c) { exists(c.getADecorator()) } predicate is_stateful(Class c) { - exists(Function method, ExprContext ctx | - method.getScope() = c and - (ctx instanceof Store or ctx instanceof AugStore) - | - exists(Subscript s | s.getScope() = method and s.getCtx() = ctx) - or - exists(Attribute a | a.getScope() = method and a.getCtx() = ctx) - ) + exists(Function method, ExprContext ctx | + method.getScope() = c and + (ctx instanceof Store or ctx instanceof AugStore) + | + exists(Subscript s | s.getScope() = method and s.getCtx() = ctx) or - exists(Function method, Call call, Attribute a, string name | - method.getScope() = c and - call.getScope() = method and - call.getFunc() = a and - a.getName() = name - | - name = "pop" or - name = "remove" or - name = "discard" or - name = "extend" or - name = "append" - ) + exists(Attribute a | a.getScope() = method and a.getCtx() = ctx) + ) + or + exists(Function method, Call call, Attribute a, string name | + method.getScope() = c and + call.getScope() = method and + call.getFunc() = a and + a.getName() = name + | + name = "pop" or + name = "remove" or + name = "discard" or + name = "extend" or + name = "append" + ) } predicate useless_class(Class c, int methods) { - c.isTopLevel() and - c.isPublic() and - no_inheritance(c) and - fewer_than_two_public_methods(c, methods) and - does_not_define_special_method(c) and - not c.isProbableMixin() and - not is_decorated(c) and - not is_stateful(c) + c.isTopLevel() and + c.isPublic() and + no_inheritance(c) and + fewer_than_two_public_methods(c, methods) and + does_not_define_special_method(c) and + not c.isProbableMixin() and + not is_decorated(c) and + not is_stateful(c) } from Class c, int methods, string msg where - useless_class(c, methods) and - ( - methods = 1 and - msg = - "Class " + c.getName() + - " defines only one public method, which should be replaced by a function." - or - methods = 0 and - msg = - "Class " + c.getName() + - " defines no public methods and could be replaced with a namedtuple or dictionary." - ) + useless_class(c, methods) and + ( + methods = 1 and + msg = + "Class " + c.getName() + + " defines only one public method, which should be replaced by a function." + or + methods = 0 and + msg = + "Class " + c.getName() + + " defines no public methods and could be replaced with a namedtuple or dictionary." + ) select c, msg diff --git a/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.ql b/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.ql index f3276ac61ef..73631a134c8 100644 --- a/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.ql +++ b/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.ql @@ -18,7 +18,7 @@ import Expressions.CallArgs from Call call, ClassValue cls, string name, FunctionValue init where - illegally_named_parameter(call, cls, name) and - init = get_function_or_initializer(cls) + illegally_named_parameter(call, cls, name) and + init = get_function_or_initializer(cls) select call, "Keyword argument '" + name + "' is not a supported parameter name of $@.", init, - init.getQualifiedName() + init.getQualifiedName() diff --git a/python/ql/src/Classes/WrongNumberArgumentsInClassInstantiation.ql b/python/ql/src/Classes/WrongNumberArgumentsInClassInstantiation.ql index c8763bd8aa7..7afd344eacb 100644 --- a/python/ql/src/Classes/WrongNumberArgumentsInClassInstantiation.ql +++ b/python/ql/src/Classes/WrongNumberArgumentsInClassInstantiation.ql @@ -17,15 +17,15 @@ import Expressions.CallArgs from Call call, ClassValue cls, string too, string should, int limit, FunctionValue init where - ( - too_many_args(call, cls, limit) and - too = "too many arguments" and - should = "no more than " - or - too_few_args(call, cls, limit) and - too = "too few arguments" and - should = "no fewer than " - ) and - init = get_function_or_initializer(cls) + ( + too_many_args(call, cls, limit) and + too = "too many arguments" and + should = "no more than " + or + too_few_args(call, cls, limit) and + too = "too few arguments" and + should = "no fewer than " + ) and + init = get_function_or_initializer(cls) select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", init, - init.getQualifiedName() + init.getQualifiedName() diff --git a/python/ql/src/Exceptions/CatchingBaseException.ql b/python/ql/src/Exceptions/CatchingBaseException.ql index 04a95a8e827..828cbf46e4b 100644 --- a/python/ql/src/Exceptions/CatchingBaseException.ql +++ b/python/ql/src/Exceptions/CatchingBaseException.ql @@ -17,13 +17,13 @@ import python predicate doesnt_reraise(ExceptStmt ex) { ex.getAFlowNode().getBasicBlock().reachesExit() } predicate catches_base_exception(ExceptStmt ex) { - ex.getType().pointsTo(ClassValue::baseException()) - or - not exists(ex.getType()) + ex.getType().pointsTo(ClassValue::baseException()) + or + not exists(ex.getType()) } from ExceptStmt ex where - catches_base_exception(ex) and - doesnt_reraise(ex) + catches_base_exception(ex) and + doesnt_reraise(ex) select ex, "Except block directly handles BaseException." diff --git a/python/ql/src/Exceptions/EmptyExcept.ql b/python/ql/src/Exceptions/EmptyExcept.ql index fd656755c1c..207ba6dd247 100755 --- a/python/ql/src/Exceptions/EmptyExcept.ql +++ b/python/ql/src/Exceptions/EmptyExcept.ql @@ -14,88 +14,88 @@ import python predicate empty_except(ExceptStmt ex) { - not exists(Stmt s | s = ex.getAStmt() and not s instanceof Pass) + not exists(Stmt s | s = ex.getAStmt() and not s instanceof Pass) } predicate no_else(ExceptStmt ex) { not exists(ex.getTry().getOrelse()) } predicate no_comment(ExceptStmt ex) { - not exists(Comment c | - c.getLocation().getFile() = ex.getLocation().getFile() and - c.getLocation().getStartLine() >= ex.getLocation().getStartLine() and - c.getLocation().getEndLine() <= ex.getBody().getLastItem().getLocation().getEndLine() - ) + not exists(Comment c | + c.getLocation().getFile() = ex.getLocation().getFile() and + c.getLocation().getStartLine() >= ex.getLocation().getStartLine() and + c.getLocation().getEndLine() <= ex.getBody().getLastItem().getLocation().getEndLine() + ) } predicate non_local_control_flow(ExceptStmt ex) { - ex.getType().pointsTo(ClassValue::stopIteration()) + ex.getType().pointsTo(ClassValue::stopIteration()) } predicate try_has_normal_exit(Try try) { - exists(ControlFlowNode pred, ControlFlowNode succ | - /* Exists a non-exception predecessor, successor pair */ - pred.getASuccessor() = succ and - not pred.getAnExceptionalSuccessor() = succ - | - /* Successor is either a normal flow node or a fall-through exit */ - not exists(Scope s | s.getReturnNode() = succ) and - /* Predecessor is in try body and successor is not */ - pred.getNode().getParentNode*() = try.getAStmt() and - not succ.getNode().getParentNode*() = try.getAStmt() - ) + exists(ControlFlowNode pred, ControlFlowNode succ | + /* Exists a non-exception predecessor, successor pair */ + pred.getASuccessor() = succ and + not pred.getAnExceptionalSuccessor() = succ + | + /* Successor is either a normal flow node or a fall-through exit */ + not exists(Scope s | s.getReturnNode() = succ) and + /* Predecessor is in try body and successor is not */ + pred.getNode().getParentNode*() = try.getAStmt() and + not succ.getNode().getParentNode*() = try.getAStmt() + ) } predicate attribute_access(Stmt s) { - s.(ExprStmt).getValue() instanceof Attribute - or - exists(string name | s.(ExprStmt).getValue().(Call).getFunc().(Name).getId() = name | - name = "getattr" or name = "setattr" or name = "delattr" - ) - or - s.(Delete).getATarget() instanceof Attribute + s.(ExprStmt).getValue() instanceof Attribute + or + exists(string name | s.(ExprStmt).getValue().(Call).getFunc().(Name).getId() = name | + name = "getattr" or name = "setattr" or name = "delattr" + ) + or + s.(Delete).getATarget() instanceof Attribute } predicate subscript(Stmt s) { - s.(ExprStmt).getValue() instanceof Subscript - or - s.(Delete).getATarget() instanceof Subscript + s.(ExprStmt).getValue() instanceof Subscript + or + s.(Delete).getATarget() instanceof Subscript } predicate encode_decode(Call ex, ClassValue type) { - exists(string name | ex.getFunc().(Attribute).getName() = name | - name = "encode" and type = ClassValue::unicodeEncodeError() - or - name = "decode" and type = ClassValue::unicodeDecodeError() - ) + exists(string name | ex.getFunc().(Attribute).getName() = name | + name = "encode" and type = ClassValue::unicodeEncodeError() + or + name = "decode" and type = ClassValue::unicodeDecodeError() + ) } predicate small_handler(ExceptStmt ex, Stmt s, ClassValue type) { - not exists(ex.getTry().getStmt(1)) and - s = ex.getTry().getStmt(0) and - ex.getType().pointsTo(type) + not exists(ex.getTry().getStmt(1)) and + s = ex.getTry().getStmt(0) and + ex.getType().pointsTo(type) } predicate focussed_handler(ExceptStmt ex) { - exists(Stmt s, ClassValue type | small_handler(ex, s, type) | - subscript(s) and type.getASuperType() = ClassValue::lookupError() - or - attribute_access(s) and type = ClassValue::attributeError() - or - s.(ExprStmt).getValue() instanceof Name and type = ClassValue::nameError() - or - encode_decode(s.(ExprStmt).getValue(), type) - ) + exists(Stmt s, ClassValue type | small_handler(ex, s, type) | + subscript(s) and type.getASuperType() = ClassValue::lookupError() + or + attribute_access(s) and type = ClassValue::attributeError() + or + s.(ExprStmt).getValue() instanceof Name and type = ClassValue::nameError() + or + encode_decode(s.(ExprStmt).getValue(), type) + ) } Try try_return() { not exists(result.getStmt(1)) and result.getStmt(0) instanceof Return } from ExceptStmt ex where - empty_except(ex) and - no_else(ex) and - no_comment(ex) and - not non_local_control_flow(ex) and - not ex.getTry() = try_return() and - try_has_normal_exit(ex.getTry()) and - not focussed_handler(ex) + empty_except(ex) and + no_else(ex) and + no_comment(ex) and + not non_local_control_flow(ex) and + not ex.getTry() = try_return() and + try_has_normal_exit(ex.getTry()) and + not focussed_handler(ex) select ex, "'except' clause does nothing but pass and there is no explanatory comment." diff --git a/python/ql/src/Exceptions/IllegalExceptionHandlerType.ql b/python/ql/src/Exceptions/IllegalExceptionHandlerType.ql index c7e014eab4f..61850b2c0d1 100644 --- a/python/ql/src/Exceptions/IllegalExceptionHandlerType.ql +++ b/python/ql/src/Exceptions/IllegalExceptionHandlerType.ql @@ -15,16 +15,16 @@ import python from ExceptFlowNode ex, Value t, ClassValue c, ControlFlowNode origin, string what where - ex.handledException(t, c, origin) and - ( - exists(ClassValue x | x = t | - not x.isLegalExceptionType() and - not x.failedInference(_) and - what = "class '" + x.getName() + "'" - ) - or - not t instanceof ClassValue and - what = "instance of '" + c.getName() + "'" + ex.handledException(t, c, origin) and + ( + exists(ClassValue x | x = t | + not x.isLegalExceptionType() and + not x.failedInference(_) and + what = "class '" + x.getName() + "'" ) + or + not t instanceof ClassValue and + what = "instance of '" + c.getName() + "'" + ) select ex.getNode(), - "Non-exception $@ in exception handler which will never match raised exception.", origin, what + "Non-exception $@ in exception handler which will never match raised exception.", origin, what diff --git a/python/ql/src/Exceptions/IllegalRaise.ql b/python/ql/src/Exceptions/IllegalRaise.ql index f05f5437db2..7d8e8987410 100644 --- a/python/ql/src/Exceptions/IllegalRaise.ql +++ b/python/ql/src/Exceptions/IllegalRaise.ql @@ -17,9 +17,9 @@ import Exceptions.NotImplemented from Raise r, ClassValue t where - type_or_typeof(r, t, _) and - not t.isLegalExceptionType() and - not t.failedInference(_) and - not use_of_not_implemented_in_raise(r, _) + type_or_typeof(r, t, _) and + not t.isLegalExceptionType() and + not t.failedInference(_) and + not use_of_not_implemented_in_raise(r, _) select r, - "Illegal class '" + t.getName() + "' raised; will result in a TypeError being raised instead." + "Illegal class '" + t.getName() + "' raised; will result in a TypeError being raised instead." diff --git a/python/ql/src/Exceptions/IncorrectExceptOrder.ql b/python/ql/src/Exceptions/IncorrectExceptOrder.ql index be756c5efef..0b57dd4659d 100644 --- a/python/ql/src/Exceptions/IncorrectExceptOrder.ql +++ b/python/ql/src/Exceptions/IncorrectExceptOrder.ql @@ -15,14 +15,14 @@ import python predicate incorrect_except_order(ExceptStmt ex1, ClassValue cls1, ExceptStmt ex2, ClassValue cls2) { - exists(int i, int j, Try t | - ex1 = t.getHandler(i) and - ex2 = t.getHandler(j) and - i < j and - cls1 = except_class(ex1) and - cls2 = except_class(ex2) and - cls1 = cls2.getASuperType() - ) + exists(int i, int j, Try t | + ex1 = t.getHandler(i) and + ex2 = t.getHandler(j) and + i < j and + cls1 = except_class(ex1) and + cls2 = except_class(ex2) and + cls1 = cls2.getASuperType() + ) } ClassValue except_class(ExceptStmt ex) { ex.getType().pointsTo(result) } @@ -30,5 +30,5 @@ ClassValue except_class(ExceptStmt ex) { ex.getType().pointsTo(result) } from ExceptStmt ex1, ClassValue cls1, ExceptStmt ex2, ClassValue cls2 where incorrect_except_order(ex1, cls1, ex2, cls2) select ex2, - "Except block for $@ is unreachable; the more general $@ for $@ will always be executed in preference.", - cls2, cls2.getName(), ex1, "except block", cls1, cls1.getName() + "Except block for $@ is unreachable; the more general $@ for $@ will always be executed in preference.", + cls2, cls2.getName(), ex1, "except block", cls1, cls1.getName() diff --git a/python/ql/src/Exceptions/NotImplemented.qll b/python/ql/src/Exceptions/NotImplemented.qll index b319311290e..2186a7b5f30 100644 --- a/python/ql/src/Exceptions/NotImplemented.qll +++ b/python/ql/src/Exceptions/NotImplemented.qll @@ -2,9 +2,9 @@ import python /** Holds if `notimpl` refers to `NotImplemented` or `NotImplemented()` in the `raise` statement */ predicate use_of_not_implemented_in_raise(Raise raise, Expr notimpl) { - notimpl.pointsTo(Value::named("NotImplemented")) and - ( - notimpl = raise.getException() or - notimpl = raise.getException().(Call).getFunc() - ) + notimpl.pointsTo(Value::named("NotImplemented")) and + ( + notimpl = raise.getException() or + notimpl = raise.getException().(Call).getFunc() + ) } diff --git a/python/ql/src/Exceptions/Raising.qll b/python/ql/src/Exceptions/Raising.qll index ad98c93d0c4..c8149481187 100644 --- a/python/ql/src/Exceptions/Raising.qll +++ b/python/ql/src/Exceptions/Raising.qll @@ -2,11 +2,11 @@ import python /** Whether the raise statement 'r' raises 'type' from origin 'orig' */ predicate type_or_typeof(Raise r, ClassValue type, AstNode orig) { - exists(Expr exception | exception = r.getRaised() | - exception.pointsTo(type, orig) - or - not exists(ClassValue exc_type | exception.pointsTo(exc_type)) and - not type = ClassValue::type() and // First value is an unknown exception type - exists(Value val | exception.pointsTo(val, orig) | val.getClass() = type) - ) + exists(Expr exception | exception = r.getRaised() | + exception.pointsTo(type, orig) + or + not exists(ClassValue exc_type | exception.pointsTo(exc_type)) and + not type = ClassValue::type() and // First value is an unknown exception type + exists(Value val | exception.pointsTo(val, orig) | val.getClass() = type) + ) } diff --git a/python/ql/src/Exceptions/RaisingTuple.ql b/python/ql/src/Exceptions/RaisingTuple.ql index dc4b295a90d..abfe785e9cb 100644 --- a/python/ql/src/Exceptions/RaisingTuple.ql +++ b/python/ql/src/Exceptions/RaisingTuple.ql @@ -13,10 +13,10 @@ import python from Raise r, Value v, AstNode origin where - r.getException().pointsTo(v, origin) and - v.getClass() = ClassValue::tuple() and - major_version() = 2 + r.getException().pointsTo(v, origin) and + v.getClass() = ClassValue::tuple() and + major_version() = 2 /* Raising a tuple is a type error in Python 3, so is handled by the IllegalRaise query. */ select r, - "Raising $@ will result in the first element (recursively) being raised and all other elements being discarded.", - origin, "a tuple" + "Raising $@ will result in the first element (recursively) being raised and all other elements being discarded.", + origin, "a tuple" diff --git a/python/ql/src/Exceptions/UnguardedNextInGenerator.ql b/python/ql/src/Exceptions/UnguardedNextInGenerator.ql index c2435d41b3e..c4d7cc956a0 100755 --- a/python/ql/src/Exceptions/UnguardedNextInGenerator.ql +++ b/python/ql/src/Exceptions/UnguardedNextInGenerator.ql @@ -17,45 +17,45 @@ FunctionValue iter() { result = Value::named("iter") } BuiltinFunctionValue next() { result = Value::named("next") } predicate call_to_iter(CallNode call, EssaVariable sequence) { - sequence.getAUse() = iter().getArgumentForCall(call, 0) + sequence.getAUse() = iter().getArgumentForCall(call, 0) } predicate call_to_next(CallNode call, ControlFlowNode iter) { - iter = next().getArgumentForCall(call, 0) + iter = next().getArgumentForCall(call, 0) } predicate call_to_next_has_default(CallNode call) { - exists(call.getArg(1)) or exists(call.getArgByName("default")) + exists(call.getArg(1)) or exists(call.getArgByName("default")) } predicate guarded_not_empty_sequence(EssaVariable sequence) { - sequence.getDefinition() instanceof EssaEdgeRefinement + sequence.getDefinition() instanceof EssaEdgeRefinement } /** The pattern `next(iter(x))` is often used where `x` is known not be empty. Check for that. */ predicate iter_not_exhausted(EssaVariable iterator) { - exists(EssaVariable sequence | - call_to_iter(iterator.getDefinition().(AssignmentDefinition).getValue(), sequence) and - guarded_not_empty_sequence(sequence) - ) + exists(EssaVariable sequence | + call_to_iter(iterator.getDefinition().(AssignmentDefinition).getValue(), sequence) and + guarded_not_empty_sequence(sequence) + ) } predicate stop_iteration_handled(CallNode call) { - exists(Try t | - t.containsInScope(call.getNode()) and - t.getAHandler().getType().pointsTo(ClassValue::stopIteration()) - ) + exists(Try t | + t.containsInScope(call.getNode()) and + t.getAHandler().getType().pointsTo(ClassValue::stopIteration()) + ) } from CallNode call where - call_to_next(call, _) and - not call_to_next_has_default(call) and - not exists(EssaVariable iterator | - call_to_next(call, iterator.getAUse()) and - iter_not_exhausted(iterator) - ) and - call.getNode().getScope().(Function).isGenerator() and - not exists(Comp comp | comp.contains(call.getNode())) and - not stop_iteration_handled(call) + call_to_next(call, _) and + not call_to_next_has_default(call) and + not exists(EssaVariable iterator | + call_to_next(call, iterator.getAUse()) and + iter_not_exhausted(iterator) + ) and + call.getNode().getScope().(Function).isGenerator() and + not exists(Comp comp | comp.contains(call.getNode())) and + not stop_iteration_handled(call) select call, "Call to next() in a generator" diff --git a/python/ql/src/Expressions/CallArgs.qll b/python/ql/src/Expressions/CallArgs.qll index d5820fff91d..5e7e56d99d8 100644 --- a/python/ql/src/Expressions/CallArgs.qll +++ b/python/ql/src/Expressions/CallArgs.qll @@ -1,36 +1,36 @@ -/** INTERNAL - Methods used by queries that test whether functions are invoked correctly. */ +/** INTERNAL - Methods used by queries that test whether functions are invoked correctly. */ import python import Testing.Mox private int varargs_length_objectapi(Call call) { - not exists(call.getStarargs()) and result = 0 - or - exists(TupleObject t | call.getStarargs().refersTo(t) | result = t.getLength()) - or - result = count(call.getStarargs().(List).getAnElt()) + not exists(call.getStarargs()) and result = 0 + or + exists(TupleObject t | call.getStarargs().refersTo(t) | result = t.getLength()) + or + result = count(call.getStarargs().(List).getAnElt()) } private int varargs_length(Call call) { - not exists(call.getStarargs()) and result = 0 - or - exists(TupleValue t | call.getStarargs().pointsTo(t) | result = t.length()) - or - result = count(call.getStarargs().(List).getAnElt()) + not exists(call.getStarargs()) and result = 0 + or + exists(TupleValue t | call.getStarargs().pointsTo(t) | result = t.length()) + or + result = count(call.getStarargs().(List).getAnElt()) } /** Gets a keyword argument that is not a keyword-only parameter. */ private Keyword not_keyword_only_arg_objectapi(Call call, FunctionObject func) { - func.getACall().getNode() = call and - result = call.getAKeyword() and - not func.getFunction().getAKeywordOnlyArg().getId() = result.getArg() + func.getACall().getNode() = call and + result = call.getAKeyword() and + not func.getFunction().getAKeywordOnlyArg().getId() = result.getArg() } /** Gets a keyword argument that is not a keyword-only parameter. */ private Keyword not_keyword_only_arg(Call call, FunctionValue func) { - func.getACall().getNode() = call and - result = call.getAKeyword() and - not func.getScope().getAKeywordOnlyArg().getId() = result.getArg() + func.getACall().getNode() = call and + result = call.getAKeyword() and + not func.getScope().getAKeywordOnlyArg().getId() = result.getArg() } /** @@ -40,17 +40,17 @@ private Keyword not_keyword_only_arg(Call call, FunctionValue func) { * plus the number of keyword arguments that do not match keyword-only arguments (if the function does not take **kwargs). */ private int positional_arg_count_for_call_objectapi(Call call, Object callable) { - call = get_a_call_objectapi(callable).getNode() and - exists(int positional_keywords | - exists(FunctionObject func | func = get_function_or_initializer_objectapi(callable) | - not func.getFunction().hasKwArg() and - positional_keywords = count(not_keyword_only_arg_objectapi(call, func)) - or - func.getFunction().hasKwArg() and positional_keywords = 0 - ) - | - result = count(call.getAnArg()) + varargs_length_objectapi(call) + positional_keywords + call = get_a_call_objectapi(callable).getNode() and + exists(int positional_keywords | + exists(FunctionObject func | func = get_function_or_initializer_objectapi(callable) | + not func.getFunction().hasKwArg() and + positional_keywords = count(not_keyword_only_arg_objectapi(call, func)) + or + func.getFunction().hasKwArg() and positional_keywords = 0 ) + | + result = count(call.getAnArg()) + varargs_length_objectapi(call) + positional_keywords + ) } /** @@ -60,174 +60,174 @@ private int positional_arg_count_for_call_objectapi(Call call, Object callable) * plus the number of keyword arguments that do not match keyword-only arguments (if the function does not take **kwargs). */ private int positional_arg_count_for_call(Call call, Value callable) { - call = get_a_call(callable).getNode() and - exists(int positional_keywords | - exists(FunctionValue func | func = get_function_or_initializer(callable) | - not func.getScope().hasKwArg() and - positional_keywords = count(not_keyword_only_arg(call, func)) - or - func.getScope().hasKwArg() and positional_keywords = 0 - ) - | - result = count(call.getAnArg()) + varargs_length_objectapi(call) + positional_keywords + call = get_a_call(callable).getNode() and + exists(int positional_keywords | + exists(FunctionValue func | func = get_function_or_initializer(callable) | + not func.getScope().hasKwArg() and + positional_keywords = count(not_keyword_only_arg(call, func)) + or + func.getScope().hasKwArg() and positional_keywords = 0 ) + | + result = count(call.getAnArg()) + varargs_length_objectapi(call) + positional_keywords + ) } /** Gets the number of arguments in `call`. */ int arg_count_objectapi(Call call) { - result = count(call.getAnArg()) + varargs_length_objectapi(call) + count(call.getAKeyword()) + result = count(call.getAnArg()) + varargs_length_objectapi(call) + count(call.getAKeyword()) } /** Gets the number of arguments in `call`. */ int arg_count(Call call) { - result = count(call.getAnArg()) + varargs_length(call) + count(call.getAKeyword()) + result = count(call.getAnArg()) + varargs_length(call) + count(call.getAKeyword()) } /** Gets a call corresponding to the given class or function. */ private ControlFlowNode get_a_call_objectapi(Object callable) { - result = callable.(ClassObject).getACall() - or - result = callable.(FunctionObject).getACall() + result = callable.(ClassObject).getACall() + or + result = callable.(FunctionObject).getACall() } /** Gets a call corresponding to the given class or function. */ private ControlFlowNode get_a_call(Value callable) { - result = callable.(ClassValue).getACall() - or - result = callable.(FunctionValue).getACall() + result = callable.(ClassValue).getACall() + or + result = callable.(FunctionValue).getACall() } /** Gets the function object corresponding to the given class or function. */ FunctionObject get_function_or_initializer_objectapi(Object func_or_cls) { - result = func_or_cls.(FunctionObject) - or - result = func_or_cls.(ClassObject).declaredAttribute("__init__") + result = func_or_cls.(FunctionObject) + or + result = func_or_cls.(ClassObject).declaredAttribute("__init__") } /** Gets the function object corresponding to the given class or function. */ FunctionValue get_function_or_initializer(Value func_or_cls) { - result = func_or_cls.(FunctionValue) - or - result = func_or_cls.(ClassValue).declaredAttribute("__init__") + result = func_or_cls.(FunctionValue) + or + result = func_or_cls.(ClassValue).declaredAttribute("__init__") } /**Whether there is an illegally named parameter called `name` in the `call` to `func` */ predicate illegally_named_parameter_objectapi(Call call, Object func, string name) { - not func.isC() and - name = call.getANamedArgumentName() and - call.getAFlowNode() = get_a_call_objectapi(func) and - not get_function_or_initializer_objectapi(func).isLegalArgumentName(name) + not func.isC() and + name = call.getANamedArgumentName() and + call.getAFlowNode() = get_a_call_objectapi(func) and + not get_function_or_initializer_objectapi(func).isLegalArgumentName(name) } /**Whether there is an illegally named parameter called `name` in the `call` to `func` */ predicate illegally_named_parameter(Call call, Value func, string name) { - not func.isBuiltin() and - name = call.getANamedArgumentName() and - call.getAFlowNode() = get_a_call(func) and - not get_function_or_initializer(func).isLegalArgumentName(name) + not func.isBuiltin() and + name = call.getANamedArgumentName() and + call.getAFlowNode() = get_a_call(func) and + not get_function_or_initializer(func).isLegalArgumentName(name) } /**Whether there are too few arguments in the `call` to `callable` where `limit` is the lowest number of legal arguments */ predicate too_few_args_objectapi(Call call, Object callable, int limit) { - // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' - not illegally_named_parameter_objectapi(call, callable, _) and - not exists(call.getStarargs()) and - not exists(call.getKwargs()) and - arg_count_objectapi(call) < limit and - exists(FunctionObject func | func = get_function_or_initializer_objectapi(callable) | - call = func.getAFunctionCall().getNode() and - limit = func.minParameters() and - // The combination of misuse of `mox.Mox().StubOutWithMock()` - // and a bug in mox's implementation of methods results in having to - // pass 1 too few arguments to the mocked function. - not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod()) - or - call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1 - or - callable instanceof ClassObject and - call.getAFlowNode() = get_a_call_objectapi(callable) and - limit = func.minParameters() - 1 - ) + // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' + not illegally_named_parameter_objectapi(call, callable, _) and + not exists(call.getStarargs()) and + not exists(call.getKwargs()) and + arg_count_objectapi(call) < limit and + exists(FunctionObject func | func = get_function_or_initializer_objectapi(callable) | + call = func.getAFunctionCall().getNode() and + limit = func.minParameters() and + // The combination of misuse of `mox.Mox().StubOutWithMock()` + // and a bug in mox's implementation of methods results in having to + // pass 1 too few arguments to the mocked function. + not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod()) + or + call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1 + or + callable instanceof ClassObject and + call.getAFlowNode() = get_a_call_objectapi(callable) and + limit = func.minParameters() - 1 + ) } /**Whether there are too few arguments in the `call` to `callable` where `limit` is the lowest number of legal arguments */ predicate too_few_args(Call call, Value callable, int limit) { - // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' - not illegally_named_parameter(call, callable, _) and - not exists(call.getStarargs()) and - not exists(call.getKwargs()) and - arg_count(call) < limit and - exists(FunctionValue func | func = get_function_or_initializer(callable) | - call = func.getAFunctionCall().getNode() and - limit = func.minParameters() and - /* - * The combination of misuse of `mox.Mox().StubOutWithMock()` - * and a bug in mox's implementation of methods results in having to - * pass 1 too few arguments to the mocked function. - */ + // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' + not illegally_named_parameter(call, callable, _) and + not exists(call.getStarargs()) and + not exists(call.getKwargs()) and + arg_count(call) < limit and + exists(FunctionValue func | func = get_function_or_initializer(callable) | + call = func.getAFunctionCall().getNode() and + limit = func.minParameters() and + /* + * The combination of misuse of `mox.Mox().StubOutWithMock()` + * and a bug in mox's implementation of methods results in having to + * pass 1 too few arguments to the mocked function. + */ - not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod()) - or - call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1 - or - callable instanceof ClassValue and - call.getAFlowNode() = get_a_call(callable) and - limit = func.minParameters() - 1 - ) + not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod()) + or + call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1 + or + callable instanceof ClassValue and + call.getAFlowNode() = get_a_call(callable) and + limit = func.minParameters() - 1 + ) } /**Whether there are too many arguments in the `call` to `func` where `limit` is the highest number of legal arguments */ predicate too_many_args_objectapi(Call call, Object callable, int limit) { - // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' - not illegally_named_parameter_objectapi(call, callable, _) and - exists(FunctionObject func | - func = get_function_or_initializer_objectapi(callable) and - not func.getFunction().hasVarArg() and - limit >= 0 - | - call = func.getAFunctionCall().getNode() and limit = func.maxParameters() - or - call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1 - or - callable instanceof ClassObject and - call.getAFlowNode() = get_a_call_objectapi(callable) and - limit = func.maxParameters() - 1 - ) and - positional_arg_count_for_call_objectapi(call, callable) > limit + // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' + not illegally_named_parameter_objectapi(call, callable, _) and + exists(FunctionObject func | + func = get_function_or_initializer_objectapi(callable) and + not func.getFunction().hasVarArg() and + limit >= 0 + | + call = func.getAFunctionCall().getNode() and limit = func.maxParameters() + or + call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1 + or + callable instanceof ClassObject and + call.getAFlowNode() = get_a_call_objectapi(callable) and + limit = func.maxParameters() - 1 + ) and + positional_arg_count_for_call_objectapi(call, callable) > limit } /**Whether there are too many arguments in the `call` to `func` where `limit` is the highest number of legal arguments */ predicate too_many_args(Call call, Value callable, int limit) { - // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' - not illegally_named_parameter(call, callable, _) and - exists(FunctionValue func | - func = get_function_or_initializer(callable) and - not func.getScope().hasVarArg() and - limit >= 0 - | - call = func.getAFunctionCall().getNode() and limit = func.maxParameters() - or - call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1 - or - callable instanceof ClassValue and - call.getAFlowNode() = get_a_call(callable) and - limit = func.maxParameters() - 1 - ) and - positional_arg_count_for_call(call, callable) > limit + // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' + not illegally_named_parameter(call, callable, _) and + exists(FunctionValue func | + func = get_function_or_initializer(callable) and + not func.getScope().hasVarArg() and + limit >= 0 + | + call = func.getAFunctionCall().getNode() and limit = func.maxParameters() + or + call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1 + or + callable instanceof ClassValue and + call.getAFlowNode() = get_a_call(callable) and + limit = func.maxParameters() - 1 + ) and + positional_arg_count_for_call(call, callable) > limit } /** Holds if `call` has too many or too few arguments for `func` */ predicate wrong_args_objectapi(Call call, FunctionObject func, int limit, string too) { - too_few_args_objectapi(call, func, limit) and too = "too few" - or - too_many_args_objectapi(call, func, limit) and too = "too many" + too_few_args_objectapi(call, func, limit) and too = "too few" + or + too_many_args_objectapi(call, func, limit) and too = "too many" } /** Holds if `call` has too many or too few arguments for `func` */ predicate wrong_args(Call call, FunctionValue func, int limit, string too) { - too_few_args(call, func, limit) and too = "too few" - or - too_many_args(call, func, limit) and too = "too many" + too_few_args(call, func, limit) and too = "too few" + or + too_many_args(call, func, limit) and too = "too many" } /** @@ -236,8 +236,8 @@ predicate wrong_args(Call call, FunctionValue func, int limit, string too) { */ bindingset[call, func] predicate correct_args_if_called_as_method_objectapi(Call call, FunctionObject func) { - arg_count_objectapi(call) + 1 >= func.minParameters() and - arg_count_objectapi(call) < func.maxParameters() + arg_count_objectapi(call) + 1 >= func.minParameters() and + arg_count_objectapi(call) < func.maxParameters() } /** @@ -246,23 +246,23 @@ predicate correct_args_if_called_as_method_objectapi(Call call, FunctionObject f */ bindingset[call, func] predicate correct_args_if_called_as_method(Call call, FunctionValue func) { - arg_count(call) + 1 >= func.minParameters() and - arg_count(call) < func.maxParameters() + arg_count(call) + 1 >= func.minParameters() and + arg_count(call) < func.maxParameters() } /** Holds if `call` is a call to `overriding`, which overrides `func`. */ predicate overridden_call_objectapi(FunctionObject func, FunctionObject overriding, Call call) { - overriding.overrides(func) and - overriding.getACall().getNode() = call + overriding.overrides(func) and + overriding.getACall().getNode() = call } /** Holds if `call` is a call to `overriding`, which overrides `func`. */ predicate overridden_call(FunctionValue func, FunctionValue overriding, Call call) { - overriding.overrides(func) and - overriding.getACall().getNode() = call + overriding.overrides(func) and + overriding.getACall().getNode() = call } /** Holds if `func` will raise a `NotImplemented` error. */ predicate isAbstract(FunctionValue func) { - func.getARaisedType() = ClassValue::notImplementedError() + func.getARaisedType() = ClassValue::notImplementedError() } diff --git a/python/ql/src/Expressions/CallToSuperWrongClass.ql b/python/ql/src/Expressions/CallToSuperWrongClass.ql index 4f218ab5a2c..4098653d7f8 100644 --- a/python/ql/src/Expressions/CallToSuperWrongClass.ql +++ b/python/ql/src/Expressions/CallToSuperWrongClass.ql @@ -16,14 +16,14 @@ import python from CallNode call_to_super, string name where - exists(GlobalVariable gv, ControlFlowNode cn | - call_to_super = ClassValue::super_().getACall() and - gv.getId() = "super" and - cn = call_to_super.getArg(0) and - name = call_to_super.getScope().getScope().(Class).getName() and - exists(ClassValue other | - cn.pointsTo(other) and - not other.getScope().getName() = name - ) + exists(GlobalVariable gv, ControlFlowNode cn | + call_to_super = ClassValue::super_().getACall() and + gv.getId() = "super" and + cn = call_to_super.getArg(0) and + name = call_to_super.getScope().getScope().(Class).getName() and + exists(ClassValue other | + cn.pointsTo(other) and + not other.getScope().getName() = name ) + ) select call_to_super.getNode(), "First argument to super() should be " + name + "." diff --git a/python/ql/src/Expressions/CompareConstants.ql b/python/ql/src/Expressions/CompareConstants.ql index 5b04302db31..d2d8f827dc0 100644 --- a/python/ql/src/Expressions/CompareConstants.ql +++ b/python/ql/src/Expressions/CompareConstants.ql @@ -16,8 +16,8 @@ import python from Compare comparison, Expr left, Expr right where - comparison.compares(left, _, right) and - left.isConstant() and - right.isConstant() and - not exists(Assert a | a.getTest() = comparison) + comparison.compares(left, _, right) and + left.isConstant() and + right.isConstant() and + not exists(Assert a | a.getTest() = comparison) select comparison, "Comparison of constants; use 'True' or 'False' instead." diff --git a/python/ql/src/Expressions/Comparisons/UselessComparisonTest.ql b/python/ql/src/Expressions/Comparisons/UselessComparisonTest.ql index 29f21e7beb2..8bab127ec9d 100644 --- a/python/ql/src/Expressions/Comparisons/UselessComparisonTest.ql +++ b/python/ql/src/Expressions/Comparisons/UselessComparisonTest.ql @@ -21,9 +21,9 @@ import semmle.python.Comparisons */ private predicate is_complex(Expr comp) { - exists(comp.(Compare).getOp(1)) - or - is_complex(comp.(UnaryExpr).getOperand()) + exists(comp.(Compare).getOp(1)) + or + is_complex(comp.(UnaryExpr).getOperand()) } /** @@ -31,21 +31,21 @@ private predicate is_complex(Expr comp) { * strict and also controls that block. */ private predicate useless_test(Comparison comp, ComparisonControlBlock controls, boolean isTrue) { - controls.impliesThat(comp.getBasicBlock(), comp, isTrue) and - /* Exclude complex comparisons of form `a < x < y`, as we do not (yet) have perfect flow control for those */ - not is_complex(controls.getTest().getNode()) + controls.impliesThat(comp.getBasicBlock(), comp, isTrue) and + /* Exclude complex comparisons of form `a < x < y`, as we do not (yet) have perfect flow control for those */ + not is_complex(controls.getTest().getNode()) } private predicate useless_test_ast(AstNode comp, AstNode previous, boolean isTrue) { - forex(Comparison compnode, ConditionBlock block | - compnode.getNode() = comp and - block.getLastNode().getNode() = previous - | - useless_test(compnode, block, isTrue) - ) + forex(Comparison compnode, ConditionBlock block | + compnode.getNode() = comp and + block.getLastNode().getNode() = previous + | + useless_test(compnode, block, isTrue) + ) } from Expr test, Expr other, boolean isTrue where - useless_test_ast(test, other, isTrue) and not useless_test_ast(test.getAChildNode+(), other, _) + useless_test_ast(test, other, isTrue) and not useless_test_ast(test.getAChildNode+(), other, _) select test, "Test is always " + isTrue + ", because of $@", other, "this condition" diff --git a/python/ql/src/Expressions/ContainsNonContainer.ql b/python/ql/src/Expressions/ContainsNonContainer.ql index 69f3b1c68b3..87a3866085c 100644 --- a/python/ql/src/Expressions/ContainsNonContainer.ql +++ b/python/ql/src/Expressions/ContainsNonContainer.ql @@ -14,21 +14,21 @@ import python import semmle.python.pointsto.PointsTo predicate rhs_in_expr(ControlFlowNode rhs, Compare cmp) { - exists(Cmpop op, int i | cmp.getOp(i) = op and cmp.getComparator(i) = rhs.getNode() | - op instanceof In or op instanceof NotIn - ) + exists(Cmpop op, int i | cmp.getOp(i) = op and cmp.getComparator(i) = rhs.getNode() | + op instanceof In or op instanceof NotIn + ) } from ControlFlowNode non_seq, Compare cmp, Value v, ClassValue cls, ControlFlowNode origin where - rhs_in_expr(non_seq, cmp) and - non_seq.pointsTo(_, v, origin) and - v.getClass() = cls and - not Types::failedInference(cls, _) and - not cls.hasAttribute("__contains__") and - not cls.hasAttribute("__iter__") and - not cls.hasAttribute("__getitem__") and - not cls = ClassValue::nonetype() and - not cls = Value::named("types.MappingProxyType") + rhs_in_expr(non_seq, cmp) and + non_seq.pointsTo(_, v, origin) and + v.getClass() = cls and + not Types::failedInference(cls, _) and + not cls.hasAttribute("__contains__") and + not cls.hasAttribute("__iter__") and + not cls.hasAttribute("__getitem__") and + not cls = ClassValue::nonetype() and + not cls = Value::named("types.MappingProxyType") select cmp, "This test may raise an Exception as the $@ may be of non-container class $@.", origin, - "target", cls, cls.getName() + "target", cls, cls.getName() diff --git a/python/ql/src/Expressions/DuplicateKeyInDictionaryLiteral.ql b/python/ql/src/Expressions/DuplicateKeyInDictionaryLiteral.ql index 99a1a0e44e1..61046863718 100644 --- a/python/ql/src/Expressions/DuplicateKeyInDictionaryLiteral.ql +++ b/python/ql/src/Expressions/DuplicateKeyInDictionaryLiteral.ql @@ -15,31 +15,31 @@ import python import semmle.python.strings predicate dict_key(Dict d, Expr k, string s) { - k = d.getAKey() and - ( - s = k.(Num).getN() - or - // We use � to mark unrepresentable characters - // so two instances of � may represent different strings in the source code - not "�" = s.charAt(_) and - exists(StrConst c | c = k | - s = "u\"" + c.getText() + "\"" and c.isUnicode() - or - s = "b\"" + c.getText() + "\"" and not c.isUnicode() - ) + k = d.getAKey() and + ( + s = k.(Num).getN() + or + // We use � to mark unrepresentable characters + // so two instances of � may represent different strings in the source code + not "�" = s.charAt(_) and + exists(StrConst c | c = k | + s = "u\"" + c.getText() + "\"" and c.isUnicode() + or + s = "b\"" + c.getText() + "\"" and not c.isUnicode() ) + ) } from Dict d, Expr k1, Expr k2 where - exists(string s | dict_key(d, k1, s) and dict_key(d, k2, s) and k1 != k2) and - ( - exists(BasicBlock b, int i1, int i2 | - k1.getAFlowNode() = b.getNode(i1) and - k2.getAFlowNode() = b.getNode(i2) and - i1 < i2 - ) - or - k1.getAFlowNode().getBasicBlock().strictlyDominates(k2.getAFlowNode().getBasicBlock()) + exists(string s | dict_key(d, k1, s) and dict_key(d, k2, s) and k1 != k2) and + ( + exists(BasicBlock b, int i1, int i2 | + k1.getAFlowNode() = b.getNode(i1) and + k2.getAFlowNode() = b.getNode(i2) and + i1 < i2 ) + or + k1.getAFlowNode().getBasicBlock().strictlyDominates(k2.getAFlowNode().getBasicBlock()) + ) select k1, "Dictionary key " + repr(k1) + " is subsequently $@.", k2, "overwritten" diff --git a/python/ql/src/Expressions/ExpectedMappingForFormatString.ql b/python/ql/src/Expressions/ExpectedMappingForFormatString.ql index 96025cc444a..76d2f874779 100644 --- a/python/ql/src/Expressions/ExpectedMappingForFormatString.ql +++ b/python/ql/src/Expressions/ExpectedMappingForFormatString.ql @@ -15,12 +15,12 @@ import semmle.python.strings from Expr e, ClassValue t where - exists(BinaryExpr b | - b.getOp() instanceof Mod and - format_string(b.getLeft()) and - e = b.getRight() and - mapping_format(b.getLeft()) and - e.pointsTo().getClass() = t and - not t.isMapping() - ) + exists(BinaryExpr b | + b.getOp() instanceof Mod and + format_string(b.getLeft()) and + e = b.getRight() and + mapping_format(b.getLeft()) and + e.pointsTo().getClass() = t and + not t.isMapping() + ) select e, "Right hand side of a % operator must be a mapping, not class $@.", t, t.getName() diff --git a/python/ql/src/Expressions/ExplicitCallToDel.ql b/python/ql/src/Expressions/ExplicitCallToDel.ql index 81e8fc97b43..cb441ce0267 100644 --- a/python/ql/src/Expressions/ExplicitCallToDel.ql +++ b/python/ql/src/Expressions/ExplicitCallToDel.ql @@ -13,20 +13,20 @@ import python class DelCall extends Call { - DelCall() { this.getFunc().(Attribute).getName() = "__del__" } + DelCall() { this.getFunc().(Attribute).getName() = "__del__" } - predicate isSuperCall() { - exists(Function f | f = this.getScope() and f.getName() = "__del__" | - // We pass in `self` as the first argument... - f.getArg(0).asName().getVariable() = this.getArg(0).(Name).getVariable() - or - // ... or the call is of the form `super(Type, self).__del__()`, or the equivalent - // Python 3: `super().__del__()`. - exists(Call superCall | superCall = this.getFunc().(Attribute).getObject() | - superCall.getFunc().(Name).getId() = "super" - ) - ) - } + predicate isSuperCall() { + exists(Function f | f = this.getScope() and f.getName() = "__del__" | + // We pass in `self` as the first argument... + f.getArg(0).asName().getVariable() = this.getArg(0).(Name).getVariable() + or + // ... or the call is of the form `super(Type, self).__del__()`, or the equivalent + // Python 3: `super().__del__()`. + exists(Call superCall | superCall = this.getFunc().(Attribute).getObject() | + superCall.getFunc().(Name).getId() = "super" + ) + ) + } } from DelCall del diff --git a/python/ql/src/Expressions/Formatting/AdvancedFormatting.qll b/python/ql/src/Expressions/Formatting/AdvancedFormatting.qll index 8c3917e15c3..4941a5f4f1f 100644 --- a/python/ql/src/Expressions/Formatting/AdvancedFormatting.qll +++ b/python/ql/src/Expressions/Formatting/AdvancedFormatting.qll @@ -2,125 +2,125 @@ import python /** A string constant that looks like it may be used in string formatting operations. */ library class PossibleAdvancedFormatString extends StrConst { - PossibleAdvancedFormatString() { this.getText().matches("%{%}%") } + PossibleAdvancedFormatString() { this.getText().matches("%{%}%") } - private predicate field(int start, int end) { - brace_pair(this, start, end) and - this.getText().substring(start, end) != "{{}}" - } + private predicate field(int start, int end) { + brace_pair(this, start, end) and + this.getText().substring(start, end) != "{{}}" + } - /** Gets the number of the formatting field at [start, end) */ - int getFieldNumber(int start, int end) { - result = this.fieldId(start, end).toInt() - or - this.implicitlyNumberedField(start, end) and - result = count(int s | this.implicitlyNumberedField(s, _) and s < start) - } + /** Gets the number of the formatting field at [start, end) */ + int getFieldNumber(int start, int end) { + result = this.fieldId(start, end).toInt() + or + this.implicitlyNumberedField(start, end) and + result = count(int s | this.implicitlyNumberedField(s, _) and s < start) + } - /** Gets the text of the formatting field at [start, end) */ - string getField(int start, int end) { - this.field(start, end) and - result = this.getText().substring(start, end) - } + /** Gets the text of the formatting field at [start, end) */ + string getField(int start, int end) { + this.field(start, end) and + result = this.getText().substring(start, end) + } - private string fieldId(int start, int end) { - this.field(start, end) and - ( - result = this.getText().substring(start, end).regexpCapture("\\{([^!:.\\[]+)[!:.\\[].*", 1) - or - result = this.getText().substring(start + 1, end - 1) and result.regexpMatch("[^!:.\\[]+") - ) - } + private string fieldId(int start, int end) { + this.field(start, end) and + ( + result = this.getText().substring(start, end).regexpCapture("\\{([^!:.\\[]+)[!:.\\[].*", 1) + or + result = this.getText().substring(start + 1, end - 1) and result.regexpMatch("[^!:.\\[]+") + ) + } - /** Gets the name of the formatting field at [start, end) */ - string getFieldName(int start, int end) { - result = this.fieldId(start, end) and - not exists(this.getFieldNumber(start, end)) - } + /** Gets the name of the formatting field at [start, end) */ + string getFieldName(int start, int end) { + result = this.fieldId(start, end) and + not exists(this.getFieldNumber(start, end)) + } - private predicate implicitlyNumberedField(int start, int end) { - this.field(start, end) and - exists(string c | start + 1 = this.getText().indexOf(c) | - c = "}" or c = ":" or c = "!" or c = "." - ) - } + private predicate implicitlyNumberedField(int start, int end) { + this.field(start, end) and + exists(string c | start + 1 = this.getText().indexOf(c) | + c = "}" or c = ":" or c = "!" or c = "." + ) + } - /** Whether this format string has implicitly numbered fields */ - predicate isImplicitlyNumbered() { this.implicitlyNumberedField(_, _) } + /** Whether this format string has implicitly numbered fields */ + predicate isImplicitlyNumbered() { this.implicitlyNumberedField(_, _) } - /** Whether this format string has explicitly numbered fields */ - predicate isExplicitlyNumbered() { exists(this.fieldId(_, _).toInt()) } + /** Whether this format string has explicitly numbered fields */ + predicate isExplicitlyNumbered() { exists(this.fieldId(_, _).toInt()) } } /** Holds if the formatting string `fmt` contains a sequence of braces `{` of length `len`, beginning at index `index`. */ predicate brace_sequence(PossibleAdvancedFormatString fmt, int index, int len) { - exists(string text | text = fmt.getText() | - text.charAt(index) = "{" and not text.charAt(index - 1) = "{" and len = 1 - or - text.charAt(index) = "{" and - text.charAt(index - 1) = "{" and - brace_sequence(fmt, index - 1, len - 1) - ) + exists(string text | text = fmt.getText() | + text.charAt(index) = "{" and not text.charAt(index - 1) = "{" and len = 1 + or + text.charAt(index) = "{" and + text.charAt(index - 1) = "{" and + brace_sequence(fmt, index - 1, len - 1) + ) } /** Holds if index `index` in the format string `fmt` contains an escaped brace `{`. */ predicate escaped_brace(PossibleAdvancedFormatString fmt, int index) { - exists(int len | brace_sequence(fmt, index, len) | len % 2 = 0) + exists(int len | brace_sequence(fmt, index, len) | len % 2 = 0) } /** Holds if index `index` in the format string `fmt` contains a left brace `{` that acts as an escape character. */ predicate escaping_brace(PossibleAdvancedFormatString fmt, int index) { - escaped_brace(fmt, index + 1) + escaped_brace(fmt, index + 1) } private predicate inner_brace_pair(PossibleAdvancedFormatString fmt, int start, int end) { - not escaping_brace(fmt, start) and - not escaped_brace(fmt, start) and - fmt.getText().charAt(start) = "{" and - exists(string pair | - pair = fmt.getText().suffix(start).regexpCapture("(?s)(\\{([^{}]|\\{\\{)*+\\}).*", 1) - | - end = start + pair.length() - ) + not escaping_brace(fmt, start) and + not escaped_brace(fmt, start) and + fmt.getText().charAt(start) = "{" and + exists(string pair | + pair = fmt.getText().suffix(start).regexpCapture("(?s)(\\{([^{}]|\\{\\{)*+\\}).*", 1) + | + end = start + pair.length() + ) } private predicate brace_pair(PossibleAdvancedFormatString fmt, int start, int end) { - inner_brace_pair(fmt, start, end) - or - not escaping_brace(fmt, start) and - not escaped_brace(fmt, start) and - exists(string prefix, string postfix, int innerstart, int innerend | - brace_pair(fmt, innerstart, innerend) and - prefix = fmt.getText().regexpFind("\\{([^{}]|\\{\\{)+\\{", _, start) and - innerstart = start + prefix.length() - 1 and - postfix = fmt.getText().regexpFind("\\}([^{}]|\\}\\})*\\}", _, innerend - 1) and - end = innerend + postfix.length() - 1 - ) + inner_brace_pair(fmt, start, end) + or + not escaping_brace(fmt, start) and + not escaped_brace(fmt, start) and + exists(string prefix, string postfix, int innerstart, int innerend | + brace_pair(fmt, innerstart, innerend) and + prefix = fmt.getText().regexpFind("\\{([^{}]|\\{\\{)+\\{", _, start) and + innerstart = start + prefix.length() - 1 and + postfix = fmt.getText().regexpFind("\\}([^{}]|\\}\\})*\\}", _, innerend - 1) and + end = innerend + postfix.length() - 1 + ) } private predicate advanced_format_call(Call format_expr, PossibleAdvancedFormatString fmt, int args) { - exists(CallNode call | call = format_expr.getAFlowNode() | - call.getFunction().pointsTo(Value::named("format")) and - call.getArg(0).pointsTo(_, fmt.getAFlowNode()) and - args = count(format_expr.getAnArg()) - 1 - or - call.getFunction().(AttrNode).getObject("format").pointsTo(_, fmt.getAFlowNode()) and - args = count(format_expr.getAnArg()) - ) + exists(CallNode call | call = format_expr.getAFlowNode() | + call.getFunction().pointsTo(Value::named("format")) and + call.getArg(0).pointsTo(_, fmt.getAFlowNode()) and + args = count(format_expr.getAnArg()) - 1 + or + call.getFunction().(AttrNode).getObject("format").pointsTo(_, fmt.getAFlowNode()) and + args = count(format_expr.getAnArg()) + ) } /** A string constant that has the `format` method applied to it. */ class AdvancedFormatString extends PossibleAdvancedFormatString { - AdvancedFormatString() { advanced_format_call(_, this, _) } + AdvancedFormatString() { advanced_format_call(_, this, _) } } /** A string formatting operation that uses the `format` method. */ class AdvancedFormattingCall extends Call { - AdvancedFormattingCall() { advanced_format_call(this, _, _) } + AdvancedFormattingCall() { advanced_format_call(this, _, _) } - /** Count of the arguments actually provided */ - int providedArgCount() { advanced_format_call(this, _, result) } + /** Count of the arguments actually provided */ + int providedArgCount() { advanced_format_call(this, _, result) } - /** Gets a formatting string for this call. */ - AdvancedFormatString getAFormat() { advanced_format_call(this, result, _) } + /** Gets a formatting string for this call. */ + AdvancedFormatString getAFormat() { advanced_format_call(this, result, _) } } diff --git a/python/ql/src/Expressions/Formatting/UnusedArgumentIn3101Format.ql b/python/ql/src/Expressions/Formatting/UnusedArgumentIn3101Format.ql index 89af180099d..43c0348a8bd 100644 --- a/python/ql/src/Expressions/Formatting/UnusedArgumentIn3101Format.ql +++ b/python/ql/src/Expressions/Formatting/UnusedArgumentIn3101Format.ql @@ -18,11 +18,11 @@ int field_count(AdvancedFormatString fmt) { result = max(fmt.getFieldNumber(_, _ from AdvancedFormattingCall call, AdvancedFormatString fmt, int arg_count, int max_field where - arg_count = call.providedArgCount() and - max_field = field_count(fmt) and - call.getAFormat() = fmt and - not exists(call.getStarargs()) and - forall(AdvancedFormatString other | other = call.getAFormat() | field_count(other) < arg_count) + arg_count = call.providedArgCount() and + max_field = field_count(fmt) and + call.getAFormat() = fmt and + not exists(call.getStarargs()) and + forall(AdvancedFormatString other | other = call.getAFormat() | field_count(other) < arg_count) select call, - "Too many arguments for string format. Format $@ requires only " + max_field + ", but " + - arg_count.toString() + " are provided.", fmt, "\"" + fmt.getText() + "\"" + "Too many arguments for string format. Format $@ requires only " + max_field + ", but " + + arg_count.toString() + " are provided.", fmt, "\"" + fmt.getText() + "\"" diff --git a/python/ql/src/Expressions/Formatting/UnusedNamedArgumentIn3101Format.ql b/python/ql/src/Expressions/Formatting/UnusedNamedArgumentIn3101Format.ql index 62c598a397e..d5aac3aaab2 100644 --- a/python/ql/src/Expressions/Formatting/UnusedNamedArgumentIn3101Format.ql +++ b/python/ql/src/Expressions/Formatting/UnusedNamedArgumentIn3101Format.ql @@ -15,17 +15,17 @@ import AdvancedFormatting from AdvancedFormattingCall call, AdvancedFormatString fmt, string name, string fmt_repr where - call.getAFormat() = fmt and - name = call.getAKeyword().getArg() and - forall(AdvancedFormatString format | format = call.getAFormat() | - not format.getFieldName(_, _) = name - ) and - not exists(call.getKwargs()) and - ( - strictcount(call.getAFormat()) = 1 and fmt_repr = "format \"" + fmt.getText() + "\"" - or - strictcount(call.getAFormat()) != 1 and fmt_repr = "any format used." - ) + call.getAFormat() = fmt and + name = call.getAKeyword().getArg() and + forall(AdvancedFormatString format | format = call.getAFormat() | + not format.getFieldName(_, _) = name + ) and + not exists(call.getKwargs()) and + ( + strictcount(call.getAFormat()) = 1 and fmt_repr = "format \"" + fmt.getText() + "\"" + or + strictcount(call.getAFormat()) != 1 and fmt_repr = "any format used." + ) select call, - "Surplus named argument for string format. An argument named '" + name + - "' is provided, but it is not required by $@.", fmt, fmt_repr + "Surplus named argument for string format. An argument named '" + name + + "' is provided, but it is not required by $@.", fmt, fmt_repr diff --git a/python/ql/src/Expressions/Formatting/WrongNameInArgumentsFor3101Format.ql b/python/ql/src/Expressions/Formatting/WrongNameInArgumentsFor3101Format.ql index 384d9b9d58e..1cc1e4a9455 100644 --- a/python/ql/src/Expressions/Formatting/WrongNameInArgumentsFor3101Format.ql +++ b/python/ql/src/Expressions/Formatting/WrongNameInArgumentsFor3101Format.ql @@ -16,10 +16,10 @@ import AdvancedFormatting from AdvancedFormattingCall call, AdvancedFormatString fmt, string name where - call.getAFormat() = fmt and - not name = call.getAKeyword().getArg() and - fmt.getFieldName(_, _) = name and - not exists(call.getKwargs()) + call.getAFormat() = fmt and + not name = call.getAKeyword().getArg() and + fmt.getFieldName(_, _) = name and + not exists(call.getKwargs()) select call, - "Missing named argument for string format. Format $@ requires '" + name + "', but it is omitted.", - fmt, "\"" + fmt.getText() + "\"" + "Missing named argument for string format. Format $@ requires '" + name + "', but it is omitted.", + fmt, "\"" + fmt.getText() + "\"" diff --git a/python/ql/src/Expressions/Formatting/WrongNumberArgumentsFor3101Format.ql b/python/ql/src/Expressions/Formatting/WrongNumberArgumentsFor3101Format.ql index 8f3479c5be5..e120cd6b5bb 100644 --- a/python/ql/src/Expressions/Formatting/WrongNumberArgumentsFor3101Format.ql +++ b/python/ql/src/Expressions/Formatting/WrongNumberArgumentsFor3101Format.ql @@ -15,15 +15,15 @@ import python import AdvancedFormatting from - AdvancedFormattingCall call, AdvancedFormatString fmt, int arg_count, int max_field, - string provided + AdvancedFormattingCall call, AdvancedFormatString fmt, int arg_count, int max_field, + string provided where - arg_count = call.providedArgCount() and - max_field = max(fmt.getFieldNumber(_, _)) and - call.getAFormat() = fmt and - not exists(call.getStarargs()) and - arg_count <= max_field and - (if arg_count = 1 then provided = " is provided." else provided = " are provided.") + arg_count = call.providedArgCount() and + max_field = max(fmt.getFieldNumber(_, _)) and + call.getAFormat() = fmt and + not exists(call.getStarargs()) and + arg_count <= max_field and + (if arg_count = 1 then provided = " is provided." else provided = " are provided.") select call, - "Too few arguments for string format. Format $@ requires at least " + (max_field + 1) + ", but " + - arg_count.toString() + provided, fmt, "\"" + fmt.getText() + "\"" + "Too few arguments for string format. Format $@ requires at least " + (max_field + 1) + ", but " + + arg_count.toString() + provided, fmt, "\"" + fmt.getText() + "\"" diff --git a/python/ql/src/Expressions/HashedButNoHash.ql b/python/ql/src/Expressions/HashedButNoHash.ql index 7fbb723fc54..336c344fa37 100644 --- a/python/ql/src/Expressions/HashedButNoHash.ql +++ b/python/ql/src/Expressions/HashedButNoHash.ql @@ -19,39 +19,39 @@ import python */ predicate numpy_array_type(ClassValue na) { - exists(ModuleValue np | np.getName() = "numpy" or np.getName() = "numpy.core" | - na.getASuperType() = np.attr("ndarray") - ) + exists(ModuleValue np | np.getName() = "numpy" or np.getName() = "numpy.core" | + na.getASuperType() = np.attr("ndarray") + ) } predicate has_custom_getitem(Value v) { - v.getClass().lookup("__getitem__") instanceof PythonFunctionValue - or - numpy_array_type(v.getClass()) + v.getClass().lookup("__getitem__") instanceof PythonFunctionValue + or + numpy_array_type(v.getClass()) } predicate explicitly_hashed(ControlFlowNode f) { - exists(CallNode c, GlobalVariable hash | - c.getArg(0) = f and c.getFunction().(NameNode).uses(hash) and hash.getId() = "hash" - ) + exists(CallNode c, GlobalVariable hash | + c.getArg(0) = f and c.getFunction().(NameNode).uses(hash) and hash.getId() = "hash" + ) } predicate unhashable_subscript(ControlFlowNode f, ClassValue c, ControlFlowNode origin) { - is_unhashable(f, c, origin) and - exists(SubscriptNode sub | sub.getIndex() = f | - exists(Value custom_getitem | - sub.getObject().pointsTo(custom_getitem) and - not has_custom_getitem(custom_getitem) - ) + is_unhashable(f, c, origin) and + exists(SubscriptNode sub | sub.getIndex() = f | + exists(Value custom_getitem | + sub.getObject().pointsTo(custom_getitem) and + not has_custom_getitem(custom_getitem) ) + ) } predicate is_unhashable(ControlFlowNode f, ClassValue cls, ControlFlowNode origin) { - exists(Value v | f.pointsTo(v, origin) and v.getClass() = cls | - not cls.hasAttribute("__hash__") and not cls.failedInference(_) and cls.isNewStyle() - or - cls.lookup("__hash__") = Value::named("None") - ) + exists(Value v | f.pointsTo(v, origin) and v.getClass() = cls | + not cls.hasAttribute("__hash__") and not cls.failedInference(_) and cls.isNewStyle() + or + cls.lookup("__hash__") = Value::named("None") + ) } /** @@ -68,18 +68,18 @@ predicate is_unhashable(ControlFlowNode f, ClassValue cls, ControlFlowNode origi * it. */ predicate typeerror_is_caught(ControlFlowNode f) { - exists(Try try | - try.getBody().contains(f.getNode()) and - try.getAHandler().getType().pointsTo(ClassValue::typeError()) - ) + exists(Try try | + try.getBody().contains(f.getNode()) and + try.getAHandler().getType().pointsTo(ClassValue::typeError()) + ) } from ControlFlowNode f, ClassValue c, ControlFlowNode origin where - not typeerror_is_caught(f) and - ( - explicitly_hashed(f) and is_unhashable(f, c, origin) - or - unhashable_subscript(f, c, origin) - ) + not typeerror_is_caught(f) and + ( + explicitly_hashed(f) and is_unhashable(f, c, origin) + or + unhashable_subscript(f, c, origin) + ) select f.getNode(), "This $@ of $@ is unhashable.", origin, "instance", c, c.getQualifiedName() diff --git a/python/ql/src/Expressions/IncorrectComparisonUsingIs.ql b/python/ql/src/Expressions/IncorrectComparisonUsingIs.ql index 5dda5b857f9..ab45d6c15d9 100644 --- a/python/ql/src/Expressions/IncorrectComparisonUsingIs.ql +++ b/python/ql/src/Expressions/IncorrectComparisonUsingIs.ql @@ -15,13 +15,13 @@ import IsComparisons from Compare comp, Cmpop op, ClassValue c, string alt where - invalid_portable_is_comparison(comp, op, c) and - not cpython_interned_constant(comp.getASubExpression()) and - ( - op instanceof Is and alt = "==" - or - op instanceof IsNot and alt = "!=" - ) + invalid_portable_is_comparison(comp, op, c) and + not cpython_interned_constant(comp.getASubExpression()) and + ( + op instanceof Is and alt = "==" + or + op instanceof IsNot and alt = "!=" + ) select comp, - "Values compared using '" + op.getSymbol() + - "' when equivalence is not the same as identity. Use '" + alt + "' instead." + "Values compared using '" + op.getSymbol() + + "' when equivalence is not the same as identity. Use '" + alt + "' instead." diff --git a/python/ql/src/Expressions/IsComparisons.qll b/python/ql/src/Expressions/IsComparisons.qll index a8ce9982859..b6f06b30108 100644 --- a/python/ql/src/Expressions/IsComparisons.qll +++ b/python/ql/src/Expressions/IsComparisons.qll @@ -4,60 +4,60 @@ import python /** Holds if the comparison `comp` uses `is` or `is not` (represented as `op`) to compare its `left` and `right` arguments. */ predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, ControlFlowNode right) { - exists(CompareNode fcomp | fcomp = comp.getAFlowNode() | - fcomp.operands(left, op, right) and - (op instanceof Is or op instanceof IsNot) - ) + exists(CompareNode fcomp | fcomp = comp.getAFlowNode() | + fcomp.operands(left, op, right) and + (op instanceof Is or op instanceof IsNot) + ) } /** Holds if the class `c` overrides the default notion of equality or comparison. */ predicate overrides_eq_or_cmp(ClassValue c) { - major_version() = 2 and c.hasAttribute("__eq__") - or - c.declaresAttribute("__eq__") and not c = Value::named("object") - or - exists(ClassValue sup | sup = c.getASuperType() and not sup = Value::named("object") | - sup.declaresAttribute("__eq__") - ) - or - major_version() = 2 and c.hasAttribute("__cmp__") + major_version() = 2 and c.hasAttribute("__eq__") + or + c.declaresAttribute("__eq__") and not c = Value::named("object") + or + exists(ClassValue sup | sup = c.getASuperType() and not sup = Value::named("object") | + sup.declaresAttribute("__eq__") + ) + or + major_version() = 2 and c.hasAttribute("__cmp__") } /** Holds if the class `cls` is likely to only have a single instance throughout the program. */ predicate probablySingleton(ClassValue cls) { - strictcount(Value inst | inst.getClass() = cls) = 1 - or - cls = Value::named("None").getClass() + strictcount(Value inst | inst.getClass() = cls) = 1 + or + cls = Value::named("None").getClass() } /** Holds if using `is` to compare instances of the class `c` is likely to cause unexpected behavior. */ predicate invalid_to_use_is_portably(ClassValue c) { - overrides_eq_or_cmp(c) and - // Exclude type/builtin-function/bool as it is legitimate to compare them using 'is' but they implement __eq__ - not c = Value::named("type") and - not c = ClassValue::builtinFunction() and - not c = Value::named("bool") and - // OK to compare with 'is' if a singleton - not probablySingleton(c) + overrides_eq_or_cmp(c) and + // Exclude type/builtin-function/bool as it is legitimate to compare them using 'is' but they implement __eq__ + not c = Value::named("type") and + not c = ClassValue::builtinFunction() and + not c = Value::named("bool") and + // OK to compare with 'is' if a singleton + not probablySingleton(c) } /** Holds if the control flow node `f` points to either `True`, `False`, or `None`. */ predicate simple_constant(ControlFlowNode f) { - exists(Value val | f.pointsTo(val) | - val = Value::named("True") or val = Value::named("False") or val = Value::named("None") - ) + exists(Value val | f.pointsTo(val) | + val = Value::named("True") or val = Value::named("False") or val = Value::named("None") + ) } private predicate cpython_interned_value(Expr e) { - exists(string text | text = e.(StrConst).getText() | - text.length() = 0 - or - text.length() = 1 and text.regexpMatch("[U+0000-U+00ff]") - ) + exists(string text | text = e.(StrConst).getText() | + text.length() = 0 or - exists(int i | i = e.(IntegerLiteral).getN().toInt() | -5 <= i and i <= 256) - or - exists(Tuple t | t = e and not exists(t.getAnElt())) + text.length() = 1 and text.regexpMatch("[U+0000-U+00ff]") + ) + or + exists(int i | i = e.(IntegerLiteral).getN().toInt() | -5 <= i and i <= 256) + or + exists(Tuple t | t = e and not exists(t.getAnElt())) } /** @@ -66,83 +66,83 @@ private predicate cpython_interned_value(Expr e) { * follow CPython, but it varies, so this is a best guess. */ private predicate universally_interned_value(Expr e) { - e.(IntegerLiteral).getN().toInt() = 0 - or - exists(Tuple t | t = e and not exists(t.getAnElt())) - or - e.(StrConst).getText() = "" + e.(IntegerLiteral).getN().toInt() = 0 + or + exists(Tuple t | t = e and not exists(t.getAnElt())) + or + e.(StrConst).getText() = "" } /** Holds if the expression `e` points to an interned constant in CPython. */ predicate cpython_interned_constant(Expr e) { - exists(Expr const | e.pointsTo(_, const) | cpython_interned_value(const)) + exists(Expr const | e.pointsTo(_, const) | cpython_interned_value(const)) } /** Holds if the expression `e` points to a value that can be reasonably expected to be interned across all implementations of Python. */ predicate universally_interned_constant(Expr e) { - exists(Expr const | e.pointsTo(_, const) | universally_interned_value(const)) + exists(Expr const | e.pointsTo(_, const) | universally_interned_value(const)) } private predicate comparison_both_types(Compare comp, Cmpop op, ClassValue cls1, ClassValue cls2) { - exists(ControlFlowNode op1, ControlFlowNode op2 | - comparison_using_is(comp, op1, op, op2) or comparison_using_is(comp, op2, op, op1) - | - op1.inferredValue().getClass() = cls1 and - op2.inferredValue().getClass() = cls2 - ) + exists(ControlFlowNode op1, ControlFlowNode op2 | + comparison_using_is(comp, op1, op, op2) or comparison_using_is(comp, op2, op, op1) + | + op1.inferredValue().getClass() = cls1 and + op2.inferredValue().getClass() = cls2 + ) } private predicate comparison_one_type(Compare comp, Cmpop op, ClassValue cls) { - not comparison_both_types(comp, _, _, _) and - exists(ControlFlowNode operand | - comparison_using_is(comp, operand, op, _) or comparison_using_is(comp, _, op, operand) - | - operand.inferredValue().getClass() = cls - ) + not comparison_both_types(comp, _, _, _) and + exists(ControlFlowNode operand | + comparison_using_is(comp, operand, op, _) or comparison_using_is(comp, _, op, operand) + | + operand.inferredValue().getClass() = cls + ) } /** -* Holds if using `is` or `is not` as the operator `op` in the comparison `comp` would be invalid when applied to the class `cls`. + * Holds if using `is` or `is not` as the operator `op` in the comparison `comp` would be invalid when applied to the class `cls`. */ predicate invalid_portable_is_comparison(Compare comp, Cmpop op, ClassValue cls) { - // OK to use 'is' when defining '__eq__' - not exists(Function eq | eq.getName() = "__eq__" or eq.getName() = "__ne__" | - eq = comp.getScope().getScope*() - ) and - ( - comparison_one_type(comp, op, cls) and invalid_to_use_is_portably(cls) - or - exists(ClassValue other | comparison_both_types(comp, op, cls, other) | - invalid_to_use_is_portably(cls) and - invalid_to_use_is_portably(other) - ) - ) and - // OK to use 'is' when comparing items from a known set of objects - not exists(Expr left, Expr right, Value val | - comp.compares(left, op, right) and - exists(ImmutableLiteral il | il.getLiteralValue() = val) - | - left.pointsTo(val) and right.pointsTo(val) - or - // Simple constant in module, probably some sort of sentinel - exists(AstNode origin | - not left.pointsTo(_) and - right.pointsTo(val, origin) and - origin.getScope().getEnclosingModule() = comp.getScope().getEnclosingModule() - ) - ) and - // OK to use 'is' when comparing with a member of an enum - not exists(Expr left, Expr right, AstNode origin | - comp.compares(left, op, right) and - enum_member(origin) - | - left.pointsTo(_, origin) or right.pointsTo(_, origin) + // OK to use 'is' when defining '__eq__' + not exists(Function eq | eq.getName() = "__eq__" or eq.getName() = "__ne__" | + eq = comp.getScope().getScope*() + ) and + ( + comparison_one_type(comp, op, cls) and invalid_to_use_is_portably(cls) + or + exists(ClassValue other | comparison_both_types(comp, op, cls, other) | + invalid_to_use_is_portably(cls) and + invalid_to_use_is_portably(other) ) + ) and + // OK to use 'is' when comparing items from a known set of objects + not exists(Expr left, Expr right, Value val | + comp.compares(left, op, right) and + exists(ImmutableLiteral il | il.getLiteralValue() = val) + | + left.pointsTo(val) and right.pointsTo(val) + or + // Simple constant in module, probably some sort of sentinel + exists(AstNode origin | + not left.pointsTo(_) and + right.pointsTo(val, origin) and + origin.getScope().getEnclosingModule() = comp.getScope().getEnclosingModule() + ) + ) and + // OK to use 'is' when comparing with a member of an enum + not exists(Expr left, Expr right, AstNode origin | + comp.compares(left, op, right) and + enum_member(origin) + | + left.pointsTo(_, origin) or right.pointsTo(_, origin) + ) } private predicate enum_member(AstNode obj) { - exists(ClassValue cls, AssignStmt asgn | cls.getASuperType().getName() = "Enum" | - cls.getScope() = asgn.getScope() and - asgn.getValue() = obj - ) + exists(ClassValue cls, AssignStmt asgn | cls.getASuperType().getName() = "Enum" | + cls.getScope() = asgn.getScope() and + asgn.getValue() = obj + ) } diff --git a/python/ql/src/Expressions/NonCallableCalled.ql b/python/ql/src/Expressions/NonCallableCalled.ql index fdd0bbd13c3..aed13af8f63 100644 --- a/python/ql/src/Expressions/NonCallableCalled.ql +++ b/python/ql/src/Expressions/NonCallableCalled.ql @@ -16,12 +16,12 @@ import Exceptions.NotImplemented from Call c, Value v, ClassValue t, Expr f, AstNode origin where - f = c.getFunc() and - f.pointsTo(v, origin) and - t = v.getClass() and - not t.isCallable() and - not t.failedInference(_) and - not t.hasAttribute("__get__") and - not v = Value::named("None") and - not use_of_not_implemented_in_raise(_, f) + f = c.getFunc() and + f.pointsTo(v, origin) and + t = v.getClass() and + not t.isCallable() and + not t.failedInference(_) and + not t.hasAttribute("__get__") and + not v = Value::named("None") and + not use_of_not_implemented_in_raise(_, f) select c, "Call to a $@ of $@.", origin, "non-callable", t, t.toString() diff --git a/python/ql/src/Expressions/NonPortableComparisonUsingIs.ql b/python/ql/src/Expressions/NonPortableComparisonUsingIs.ql index 3e01ccdacf7..db266020aeb 100644 --- a/python/ql/src/Expressions/NonPortableComparisonUsingIs.ql +++ b/python/ql/src/Expressions/NonPortableComparisonUsingIs.ql @@ -15,11 +15,11 @@ import IsComparisons from Compare comp, Cmpop op, ClassValue c where - invalid_portable_is_comparison(comp, op, c) and - exists(Expr sub | sub = comp.getASubExpression() | - cpython_interned_constant(sub) and - not universally_interned_constant(sub) - ) + invalid_portable_is_comparison(comp, op, c) and + exists(Expr sub | sub = comp.getASubExpression() | + cpython_interned_constant(sub) and + not universally_interned_constant(sub) + ) select comp, - "The result of this comparison with '" + op.getSymbol() + - "' may differ between implementations of Python." + "The result of this comparison with '" + op.getSymbol() + + "' may differ between implementations of Python." diff --git a/python/ql/src/Expressions/RedundantComparison.qll b/python/ql/src/Expressions/RedundantComparison.qll index 6157173020b..a0d4f906501 100644 --- a/python/ql/src/Expressions/RedundantComparison.qll +++ b/python/ql/src/Expressions/RedundantComparison.qll @@ -4,53 +4,54 @@ import python /** A comparison where the left and right hand sides appear to be identical. */ class RedundantComparison extends Compare { - RedundantComparison() { - exists(Expr left, Expr right | - this.compares(left, _, right) and - same_variable(left, right) - ) - } + RedundantComparison() { + exists(Expr left, Expr right | + this.compares(left, _, right) and + same_variable(left, right) + ) + } - /** Holds if this comparison could be redundant due to a missing `self.`, for example - * ```python - * foo == foo - * ``` - * instead of - * ```python - * self.foo == foo - * ``` - */ - predicate maybeMissingSelf() { - exists(Name left | - this.compares(left, _, _) and - not this.isConstant() and - exists(Class cls | left.getScope().getScope() = cls | - exists(SelfAttribute sa | sa.getName() = left.getId() | sa.getClass() = cls) - ) - ) - } + /** + * Holds if this comparison could be redundant due to a missing `self.`, for example + * ```python + * foo == foo + * ``` + * instead of + * ```python + * self.foo == foo + * ``` + */ + predicate maybeMissingSelf() { + exists(Name left | + this.compares(left, _, _) and + not this.isConstant() and + exists(Class cls | left.getScope().getScope() = cls | + exists(SelfAttribute sa | sa.getName() = left.getId() | sa.getClass() = cls) + ) + ) + } } private predicate same_variable(Expr left, Expr right) { - same_name(left, right) - or - same_attribute(left, right) + same_name(left, right) + or + same_attribute(left, right) } private predicate name_in_comparison(Compare comp, Name n, Variable v) { - comp.contains(n) and v = n.getVariable() + comp.contains(n) and v = n.getVariable() } private predicate same_name(Name n1, Name n2) { - n1 != n2 and - exists(Compare comp, Variable v | - name_in_comparison(comp, n1, v) and name_in_comparison(comp, n2, v) - ) + n1 != n2 and + exists(Compare comp, Variable v | + name_in_comparison(comp, n1, v) and name_in_comparison(comp, n2, v) + ) } private predicate same_attribute(Attribute a1, Attribute a2) { - a1 != a2 and - exists(Compare comp | comp.contains(a1) and comp.contains(a2)) and - a1.getName() = a2.getName() and - same_name(a1.getObject(), a2.getObject()) + a1 != a2 and + exists(Compare comp | comp.contains(a1) and comp.contains(a2)) and + a1.getName() = a2.getName() and + same_name(a1.getObject(), a2.getObject()) } diff --git a/python/ql/src/Expressions/Regex/BackspaceEscape.ql b/python/ql/src/Expressions/Regex/BackspaceEscape.ql index b18d581257a..ce69dabec44 100644 --- a/python/ql/src/Expressions/Regex/BackspaceEscape.ql +++ b/python/ql/src/Expressions/Regex/BackspaceEscape.ql @@ -15,7 +15,7 @@ import semmle.python.regex from Regex r, int offset where - r.escapingChar(offset) and - r.getChar(offset + 1) = "b" and - exists(int start, int end | start < offset and end > offset | r.charSet(start, end)) + r.escapingChar(offset) and + r.getChar(offset + 1) = "b" and + exists(int start, int end | start < offset and end > offset | r.charSet(start, end)) select r, "Backspace escape in regular expression at offset " + offset + "." diff --git a/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.ql b/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.ql index 42a745affb8..895c8714ddf 100644 --- a/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.ql +++ b/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.ql @@ -14,29 +14,29 @@ import python import semmle.python.regex predicate duplicate_char_in_class(Regex r, string char) { - exists(int i, int j, int x, int y, int start, int end | - i != x and - j != y and - start < i and - j < end and - start < x and - y < end and - r.character(i, j) and - char = r.getText().substring(i, j) and - r.character(x, y) and - char = r.getText().substring(x, y) and - r.charSet(start, end) - ) and - /* Exclude � as we use it for any unencodable character */ - char != "�" and - //Ignore whitespace in verbose mode - not ( - r.getAMode() = "VERBOSE" and - (char = " " or char = "\t" or char = "\r" or char = "\n") - ) + exists(int i, int j, int x, int y, int start, int end | + i != x and + j != y and + start < i and + j < end and + start < x and + y < end and + r.character(i, j) and + char = r.getText().substring(i, j) and + r.character(x, y) and + char = r.getText().substring(x, y) and + r.charSet(start, end) + ) and + /* Exclude � as we use it for any unencodable character */ + char != "�" and + //Ignore whitespace in verbose mode + not ( + r.getAMode() = "VERBOSE" and + (char = " " or char = "\t" or char = "\r" or char = "\n") + ) } from Regex r, string char where duplicate_char_in_class(r, char) select r, - "This regular expression includes duplicate character '" + char + "' in a set of characters." + "This regular expression includes duplicate character '" + char + "' in a set of characters." diff --git a/python/ql/src/Expressions/Regex/UnmatchableCaret.ql b/python/ql/src/Expressions/Regex/UnmatchableCaret.ql index 7a5c087ec02..f954169ae02 100644 --- a/python/ql/src/Expressions/Regex/UnmatchableCaret.ql +++ b/python/ql/src/Expressions/Regex/UnmatchableCaret.ql @@ -14,13 +14,13 @@ import python import semmle.python.regex predicate unmatchable_caret(Regex r, int start) { - not r.getAMode() = "MULTILINE" and - not r.getAMode() = "VERBOSE" and - r.specialCharacter(start, start + 1, "^") and - not r.firstItem(start, start + 1) + not r.getAMode() = "MULTILINE" and + not r.getAMode() = "VERBOSE" and + r.specialCharacter(start, start + 1, "^") and + not r.firstItem(start, start + 1) } from Regex r, int offset where unmatchable_caret(r, offset) select r, - "This regular expression includes an unmatchable caret at offset " + offset.toString() + "." + "This regular expression includes an unmatchable caret at offset " + offset.toString() + "." diff --git a/python/ql/src/Expressions/Regex/UnmatchableDollar.ql b/python/ql/src/Expressions/Regex/UnmatchableDollar.ql index dfd2bfcf893..3f9457f5bd2 100644 --- a/python/ql/src/Expressions/Regex/UnmatchableDollar.ql +++ b/python/ql/src/Expressions/Regex/UnmatchableDollar.ql @@ -14,13 +14,13 @@ import python import semmle.python.regex predicate unmatchable_dollar(Regex r, int start) { - not r.getAMode() = "MULTILINE" and - not r.getAMode() = "VERBOSE" and - r.specialCharacter(start, start + 1, "$") and - not r.lastItem(start, start + 1) + not r.getAMode() = "MULTILINE" and + not r.getAMode() = "VERBOSE" and + r.specialCharacter(start, start + 1, "$") and + not r.lastItem(start, start + 1) } from Regex r, int offset where unmatchable_dollar(r, offset) select r, - "This regular expression includes an unmatchable dollar at offset " + offset.toString() + "." + "This regular expression includes an unmatchable dollar at offset " + offset.toString() + "." diff --git a/python/ql/src/Expressions/TruncatedDivision.ql b/python/ql/src/Expressions/TruncatedDivision.ql index 399435dbabf..0904081f5ff 100644 --- a/python/ql/src/Expressions/TruncatedDivision.ql +++ b/python/ql/src/Expressions/TruncatedDivision.ql @@ -15,23 +15,23 @@ import python from BinaryExpr div, ControlFlowNode left, ControlFlowNode right where - // Only relevant for Python 2, as all later versions implement true division - major_version() = 2 and - exists(BinaryExprNode bin, Value lval, Value rval | - bin = div.getAFlowNode() and - bin.getNode().getOp() instanceof Div and - bin.getLeft().pointsTo(lval, left) and - lval.getClass() = ClassValue::int_() and - bin.getRight().pointsTo(rval, right) and - rval.getClass() = ClassValue::int_() and - // Ignore instances where integer division leaves no remainder - not lval.(NumericValue).getIntValue() % rval.(NumericValue).getIntValue() = 0 and - not bin.getNode().getEnclosingModule().hasFromFuture("division") and - // Filter out results wrapped in `int(...)` - not exists(CallNode c | - c = ClassValue::int_().getACall() and - c.getAnArg() = bin - ) + // Only relevant for Python 2, as all later versions implement true division + major_version() = 2 and + exists(BinaryExprNode bin, Value lval, Value rval | + bin = div.getAFlowNode() and + bin.getNode().getOp() instanceof Div and + bin.getLeft().pointsTo(lval, left) and + lval.getClass() = ClassValue::int_() and + bin.getRight().pointsTo(rval, right) and + rval.getClass() = ClassValue::int_() and + // Ignore instances where integer division leaves no remainder + not lval.(NumericValue).getIntValue() % rval.(NumericValue).getIntValue() = 0 and + not bin.getNode().getEnclosingModule().hasFromFuture("division") and + // Filter out results wrapped in `int(...)` + not exists(CallNode c | + c = ClassValue::int_().getACall() and + c.getAnArg() = bin ) + ) select div, "Result of division may be truncated as its $@ and $@ arguments may both be integers.", - left.getLocation(), "left", right.getLocation(), "right" + left.getLocation(), "left", right.getLocation(), "right" diff --git a/python/ql/src/Expressions/UnintentionalImplicitStringConcatenation.ql b/python/ql/src/Expressions/UnintentionalImplicitStringConcatenation.ql index 8199be8a051..9547d0045ca 100644 --- a/python/ql/src/Expressions/UnintentionalImplicitStringConcatenation.ql +++ b/python/ql/src/Expressions/UnintentionalImplicitStringConcatenation.ql @@ -15,20 +15,20 @@ import python predicate string_const(Expr s) { - s instanceof StrConst - or - string_const(s.(BinaryExpr).getLeft()) and string_const(s.(BinaryExpr).getRight()) + s instanceof StrConst + or + string_const(s.(BinaryExpr).getLeft()) and string_const(s.(BinaryExpr).getRight()) } from StrConst s where - // Implicitly concatenated string is in a list and that list contains at least one other string. - exists(List l, Expr other | - not s = other and - l.getAnElt() = s and - l.getAnElt() = other and - string_const(other) - ) and - exists(s.getAnImplicitlyConcatenatedPart()) and - not s.isParenthesized() + // Implicitly concatenated string is in a list and that list contains at least one other string. + exists(List l, Expr other | + not s = other and + l.getAnElt() = s and + l.getAnElt() = other and + string_const(other) + ) and + exists(s.getAnImplicitlyConcatenatedPart()) and + not s.isParenthesized() select s, "Implicit string concatenation. Maybe missing a comma?" diff --git a/python/ql/src/Expressions/UnnecessaryLambda.ql b/python/ql/src/Expressions/UnnecessaryLambda.ql index 2b927973015..7486e27d695 100644 --- a/python/ql/src/Expressions/UnnecessaryLambda.ql +++ b/python/ql/src/Expressions/UnnecessaryLambda.ql @@ -14,48 +14,48 @@ import python /* f consists of a single return statement, whose value is a call. The arguments of the call are exactly the parameters of f */ predicate simple_wrapper(Lambda l, Expr wrapped) { - exists(Function f, Call c | f = l.getInnerScope() and c = l.getExpression() | - wrapped = c.getFunc() and - count(f.getAnArg()) = count(c.getAnArg()) and - forall(int arg | exists(f.getArg(arg)) | f.getArgName(arg) = c.getArg(arg).(Name).getId()) and - /* Either no **kwargs or they must match */ - ( - not exists(f.getKwarg()) and not exists(c.getKwargs()) - or - f.getKwarg().(Name).getId() = c.getKwargs().(Name).getId() - ) and - /* Either no *args or they must match */ - ( - not exists(f.getVararg()) and not exists(c.getStarargs()) - or - f.getVararg().(Name).getId() = c.getStarargs().(Name).getId() - ) and - /* No named parameters in call */ - not exists(c.getAKeyword()) + exists(Function f, Call c | f = l.getInnerScope() and c = l.getExpression() | + wrapped = c.getFunc() and + count(f.getAnArg()) = count(c.getAnArg()) and + forall(int arg | exists(f.getArg(arg)) | f.getArgName(arg) = c.getArg(arg).(Name).getId()) and + /* Either no **kwargs or they must match */ + ( + not exists(f.getKwarg()) and not exists(c.getKwargs()) + or + f.getKwarg().(Name).getId() = c.getKwargs().(Name).getId() ) and - // f is not necessarily a drop-in replacement for the lambda if there are default argument values - not exists(l.getArgs().getADefault()) + /* Either no *args or they must match */ + ( + not exists(f.getVararg()) and not exists(c.getStarargs()) + or + f.getVararg().(Name).getId() = c.getStarargs().(Name).getId() + ) and + /* No named parameters in call */ + not exists(c.getAKeyword()) + ) and + // f is not necessarily a drop-in replacement for the lambda if there are default argument values + not exists(l.getArgs().getADefault()) } /* The expression called will refer to the same object if evaluated when the lambda is created or when the lambda is executed. */ predicate unnecessary_lambda(Lambda l, Expr e) { - simple_wrapper(l, e) and - ( - /* plain class */ - exists(ClassValue c | e.pointsTo(c)) - or - /* plain function */ - exists(FunctionValue f | e.pointsTo(f)) - or - /* bound-method of enclosing instance */ - exists(ClassValue cls, Attribute a | cls.getScope() = l.getScope().getScope() and a = e | - a.getObject().(Name).getId() = "self" and - cls.hasAttribute(a.getName()) - ) + simple_wrapper(l, e) and + ( + /* plain class */ + exists(ClassValue c | e.pointsTo(c)) + or + /* plain function */ + exists(FunctionValue f | e.pointsTo(f)) + or + /* bound-method of enclosing instance */ + exists(ClassValue cls, Attribute a | cls.getScope() = l.getScope().getScope() and a = e | + a.getObject().(Name).getId() = "self" and + cls.hasAttribute(a.getName()) ) + ) } from Lambda l, Expr e where unnecessary_lambda(l, e) select l, - "This 'lambda' is just a simple wrapper around a callable object. Use that object directly." + "This 'lambda' is just a simple wrapper around a callable object. Use that object directly." diff --git a/python/ql/src/Expressions/UseofInput.ql b/python/ql/src/Expressions/UseofInput.ql index dc67458a083..2b11eecfa2b 100644 --- a/python/ql/src/Expressions/UseofInput.ql +++ b/python/ql/src/Expressions/UseofInput.ql @@ -14,8 +14,8 @@ import python from CallNode call, Context context, ControlFlowNode func where - context.getAVersion().includes(2, _) and - call.getFunction() = func and - func.pointsTo(context, Value::named("input"), _) and - not func.pointsTo(context, Value::named("raw_input"), _) + context.getAVersion().includes(2, _) and + call.getFunction() = func and + func.pointsTo(context, Value::named("input"), _) and + not func.pointsTo(context, Value::named("raw_input"), _) select call, "The unsafe built-in function 'input' is used in Python 2." diff --git a/python/ql/src/Expressions/WrongNameForArgumentInCall.ql b/python/ql/src/Expressions/WrongNameForArgumentInCall.ql index 4800f898c54..053b0ef2ad2 100644 --- a/python/ql/src/Expressions/WrongNameForArgumentInCall.ql +++ b/python/ql/src/Expressions/WrongNameForArgumentInCall.ql @@ -18,10 +18,10 @@ import Expressions.CallArgs from Call call, FunctionObject func, string name where - illegally_named_parameter_objectapi(call, func, name) and - not func.isAbstract() and - not exists(FunctionObject overridden | - func.overrides(overridden) and overridden.getFunction().getAnArg().(Name).getId() = name - ) + illegally_named_parameter_objectapi(call, func, name) and + not func.isAbstract() and + not exists(FunctionObject overridden | + func.overrides(overridden) and overridden.getFunction().getAnArg().(Name).getId() = name + ) select call, "Keyword argument '" + name + "' is not a supported parameter name of $@.", func, - func.descriptiveString() + func.descriptiveString() diff --git a/python/ql/src/Expressions/WrongNumberArgumentsForFormat.ql b/python/ql/src/Expressions/WrongNumberArgumentsForFormat.ql index 39d265fe290..c9e751d58a0 100644 --- a/python/ql/src/Expressions/WrongNumberArgumentsForFormat.ql +++ b/python/ql/src/Expressions/WrongNumberArgumentsForFormat.ql @@ -16,32 +16,32 @@ import python import semmle.python.strings predicate string_format(BinaryExpr operation, StrConst str, Value args, AstNode origin) { - operation.getOp() instanceof Mod and - exists(Value fmt, Context ctx | - operation.getLeft().pointsTo(ctx, fmt, str) and - operation.getRight().pointsTo(ctx, args, origin) - ) + operation.getOp() instanceof Mod and + exists(Value fmt, Context ctx | + operation.getLeft().pointsTo(ctx, fmt, str) and + operation.getRight().pointsTo(ctx, args, origin) + ) } int sequence_length(Value args) { - /* Guess length of sequence */ - exists(Tuple seq, AstNode origin | seq.pointsTo(args, origin) | - result = strictcount(seq.getAnElt()) and - not seq.getAnElt() instanceof Starred - ) - or - exists(ImmutableLiteral i | i.getLiteralValue() = args | result = 1) + /* Guess length of sequence */ + exists(Tuple seq, AstNode origin | seq.pointsTo(args, origin) | + result = strictcount(seq.getAnElt()) and + not seq.getAnElt() instanceof Starred + ) + or + exists(ImmutableLiteral i | i.getLiteralValue() = args | result = 1) } from - BinaryExpr operation, StrConst fmt, Value args, int slen, int alen, AstNode origin, - string provided + BinaryExpr operation, StrConst fmt, Value args, int slen, int alen, AstNode origin, + string provided where - string_format(operation, fmt, args, origin) and - slen = sequence_length(args) and - alen = format_items(fmt) and - slen != alen and - (if slen = 1 then provided = " is provided." else provided = " are provided.") + string_format(operation, fmt, args, origin) and + slen = sequence_length(args) and + alen = format_items(fmt) and + slen != alen and + (if slen = 1 then provided = " is provided." else provided = " are provided.") select operation, - "Wrong number of $@ for string format. Format $@ takes " + alen.toString() + ", but " + - slen.toString() + provided, origin, "arguments", fmt, fmt.getText() + "Wrong number of $@ for string format. Format $@ takes " + alen.toString() + ", but " + + slen.toString() + provided, origin, "arguments", fmt, fmt.getText() diff --git a/python/ql/src/Expressions/WrongNumberArgumentsInCall.ql b/python/ql/src/Expressions/WrongNumberArgumentsInCall.ql index 02bc685c096..ffebb000034 100644 --- a/python/ql/src/Expressions/WrongNumberArgumentsInCall.ql +++ b/python/ql/src/Expressions/WrongNumberArgumentsInCall.ql @@ -16,15 +16,16 @@ import CallArgs from Call call, FunctionValue func, string too, string should, int limit where -( + ( too_many_args(call, func, limit) and too = "too many arguments" and should = "no more than " or too_few_args(call, func, limit) and too = "too few arguments" and should = "no fewer than " -) and -not isAbstract(func) and -not exists(FunctionValue overridden | func.overrides(overridden) and correct_args_if_called_as_method(call, overridden)) -/* The semantics of `__new__` can be a bit subtle, so we simply exclude `__new__` methods */ -and not func.getName() = "__new__" - -select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", func, func.descriptiveString() - + ) and + not isAbstract(func) and + not exists(FunctionValue overridden | + func.overrides(overridden) and correct_args_if_called_as_method(call, overridden) + ) and + /* The semantics of `__new__` can be a bit subtle, so we simply exclude `__new__` methods */ + not func.getName() = "__new__" +select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", func, + func.descriptiveString() diff --git a/python/ql/src/Filters/ClassifyFiles.ql b/python/ql/src/Filters/ClassifyFiles.ql index 20062f0451f..4c9db8a8462 100644 --- a/python/ql/src/Filters/ClassifyFiles.ql +++ b/python/ql/src/Filters/ClassifyFiles.ql @@ -11,9 +11,9 @@ import semmle.python.filters.GeneratedCode import semmle.python.filters.Tests predicate classify(File f, string tag) { - f instanceof GeneratedFile and tag = "generated" - or - exists(TestScope t | t.getLocation().getFile() = f) and tag = "test" + f instanceof GeneratedFile and tag = "generated" + or + exists(TestScope t | t.getLocation().getFile() = f) and tag = "test" } from File f, string tag diff --git a/python/ql/src/Functions/ConsistentReturns.ql b/python/ql/src/Functions/ConsistentReturns.ql index 9e28dee36a3..f9d81c63936 100644 --- a/python/ql/src/Functions/ConsistentReturns.ql +++ b/python/ql/src/Functions/ConsistentReturns.ql @@ -13,21 +13,21 @@ import python predicate explicitly_returns_non_none(Function func) { - exists(Return return | - return.getScope() = func and - exists(Expr val | val = return.getValue() | not val instanceof None) - ) + exists(Return return | + return.getScope() = func and + exists(Expr val | val = return.getValue() | not val instanceof None) + ) } predicate has_implicit_return(Function func) { - exists(ControlFlowNode fallthru | - fallthru = func.getFallthroughNode() and not fallthru.unlikelyReachable() - ) - or - exists(Return return | return.getScope() = func and not exists(return.getValue())) + exists(ControlFlowNode fallthru | + fallthru = func.getFallthroughNode() and not fallthru.unlikelyReachable() + ) + or + exists(Return return | return.getScope() = func and not exists(return.getValue())) } from Function func where explicitly_returns_non_none(func) and has_implicit_return(func) select func, - "Mixing implicit and explicit returns may indicate an error as implicit returns always return None." + "Mixing implicit and explicit returns may indicate an error as implicit returns always return None." diff --git a/python/ql/src/Functions/DeprecatedSliceMethod.ql b/python/ql/src/Functions/DeprecatedSliceMethod.ql index c37f2195b54..2f3e8373b0b 100644 --- a/python/ql/src/Functions/DeprecatedSliceMethod.ql +++ b/python/ql/src/Functions/DeprecatedSliceMethod.ql @@ -12,13 +12,13 @@ import python predicate slice_method_name(string name) { - name = "__getslice__" or name = "__setslice__" or name = "__delslice__" + name = "__getslice__" or name = "__setslice__" or name = "__delslice__" } from PythonFunctionValue f, string meth where - f.getScope().isMethod() and - not f.isOverridingMethod() and - slice_method_name(meth) and - f.getName() = meth + f.getScope().isMethod() and + not f.isOverridingMethod() and + slice_method_name(meth) and + f.getName() = meth select f, meth + " method has been deprecated since Python 2.0" diff --git a/python/ql/src/Functions/ExplicitReturnInInit.ql b/python/ql/src/Functions/ExplicitReturnInInit.ql index b839c3bc9b7..0ce20249119 100644 --- a/python/ql/src/Functions/ExplicitReturnInInit.ql +++ b/python/ql/src/Functions/ExplicitReturnInInit.ql @@ -14,10 +14,10 @@ import python from Return r, Expr rv where - exists(Function init | init.isInitMethod() and r.getScope() = init) and - r.getValue() = rv and - not rv.pointsTo(Value::none_()) and - not exists(FunctionValue f | f.getACall() = rv.getAFlowNode() | f.neverReturns()) and - // to avoid double reporting, don't trigger if returning result from other __init__ function - not exists(Attribute meth | meth = rv.(Call).getFunc() | meth.getName() = "__init__") + exists(Function init | init.isInitMethod() and r.getScope() = init) and + r.getValue() = rv and + not rv.pointsTo(Value::none_()) and + not exists(FunctionValue f | f.getACall() = rv.getAFlowNode() | f.neverReturns()) and + // to avoid double reporting, don't trigger if returning result from other __init__ function + not exists(Attribute meth | meth = rv.(Call).getFunc() | meth.getName() = "__init__") select r, "Explicit return in __init__ method." diff --git a/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.ql b/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.ql index 14af8ad9058..b3c97e967f6 100644 --- a/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.ql +++ b/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.ql @@ -14,142 +14,142 @@ import python private predicate attribute_method(string name) { - name = "__getattribute__" or name = "__getattr__" or name = "__setattr__" + name = "__getattribute__" or name = "__getattr__" or name = "__setattr__" } private predicate indexing_method(string name) { - name = "__getitem__" or name = "__setitem__" or name = "__delitem__" + name = "__getitem__" or name = "__setitem__" or name = "__delitem__" } private predicate arithmetic_method(string name) { - name = "__add__" or - name = "__sub__" or - name = "__div__" or - name = "__pos__" or - name = "__abs__" or - name = "__floordiv__" or - name = "__div__" or - name = "__divmod__" or - name = "__lshift__" or - name = "__and__" or - name = "__or__" or - name = "__xor__" or - name = "__rshift__" or - name = "__pow__" or - name = "__mul__" or - name = "__neg__" or - name = "__radd__" or - name = "__rsub__" or - name = "__rdiv__" or - name = "__rfloordiv__" or - name = "__rdiv__" or - name = "__rlshift__" or - name = "__rand__" or - name = "__ror__" or - name = "__rxor__" or - name = "__rrshift__" or - name = "__rpow__" or - name = "__rmul__" or - name = "__truediv__" or - name = "__rtruediv__" or - name = "__iadd__" or - name = "__isub__" or - name = "__idiv__" or - name = "__ifloordiv__" or - name = "__idiv__" or - name = "__ilshift__" or - name = "__iand__" or - name = "__ior__" or - name = "__ixor__" or - name = "__irshift__" or - name = "__ipow__" or - name = "__imul__" or - name = "__itruediv__" + name = "__add__" or + name = "__sub__" or + name = "__div__" or + name = "__pos__" or + name = "__abs__" or + name = "__floordiv__" or + name = "__div__" or + name = "__divmod__" or + name = "__lshift__" or + name = "__and__" or + name = "__or__" or + name = "__xor__" or + name = "__rshift__" or + name = "__pow__" or + name = "__mul__" or + name = "__neg__" or + name = "__radd__" or + name = "__rsub__" or + name = "__rdiv__" or + name = "__rfloordiv__" or + name = "__rdiv__" or + name = "__rlshift__" or + name = "__rand__" or + name = "__ror__" or + name = "__rxor__" or + name = "__rrshift__" or + name = "__rpow__" or + name = "__rmul__" or + name = "__truediv__" or + name = "__rtruediv__" or + name = "__iadd__" or + name = "__isub__" or + name = "__idiv__" or + name = "__ifloordiv__" or + name = "__idiv__" or + name = "__ilshift__" or + name = "__iand__" or + name = "__ior__" or + name = "__ixor__" or + name = "__irshift__" or + name = "__ipow__" or + name = "__imul__" or + name = "__itruediv__" } private predicate ordering_method(string name) { - name = "__lt__" - or - name = "__le__" - or - name = "__gt__" - or - name = "__ge__" - or - name = "__cmp__" and major_version() = 2 + name = "__lt__" + or + name = "__le__" + or + name = "__gt__" + or + name = "__ge__" + or + name = "__cmp__" and major_version() = 2 } private predicate cast_method(string name) { - name = "__nonzero__" and major_version() = 2 - or - name = "__int__" - or - name = "__float__" - or - name = "__long__" - or - name = "__trunc__" - or - name = "__complex__" + name = "__nonzero__" and major_version() = 2 + or + name = "__int__" + or + name = "__float__" + or + name = "__long__" + or + name = "__trunc__" + or + name = "__complex__" } predicate correct_raise(string name, ClassObject ex) { - ex.getAnImproperSuperType() = theTypeErrorType() and - ( - name = "__copy__" or - name = "__deepcopy__" or - name = "__call__" or - indexing_method(name) or - attribute_method(name) - ) - or - preferred_raise(name, ex) - or - preferred_raise(name, ex.getASuperType()) + ex.getAnImproperSuperType() = theTypeErrorType() and + ( + name = "__copy__" or + name = "__deepcopy__" or + name = "__call__" or + indexing_method(name) or + attribute_method(name) + ) + or + preferred_raise(name, ex) + or + preferred_raise(name, ex.getASuperType()) } predicate preferred_raise(string name, ClassObject ex) { - attribute_method(name) and ex = theAttributeErrorType() - or - indexing_method(name) and ex = Object::builtin("LookupError") - or - ordering_method(name) and ex = theTypeErrorType() - or - arithmetic_method(name) and ex = Object::builtin("ArithmeticError") - or - name = "__bool__" and ex = theTypeErrorType() + attribute_method(name) and ex = theAttributeErrorType() + or + indexing_method(name) and ex = Object::builtin("LookupError") + or + ordering_method(name) and ex = theTypeErrorType() + or + arithmetic_method(name) and ex = Object::builtin("ArithmeticError") + or + name = "__bool__" and ex = theTypeErrorType() } predicate no_need_to_raise(string name, string message) { - name = "__hash__" and message = "use __hash__ = None instead" - or - cast_method(name) and message = "there is no need to implement the method at all." + name = "__hash__" and message = "use __hash__ = None instead" + or + cast_method(name) and message = "there is no need to implement the method at all." } predicate is_abstract(FunctionObject func) { - func.getFunction().getADecorator().(Name).getId().matches("%abstract%") + func.getFunction().getADecorator().(Name).getId().matches("%abstract%") } predicate always_raises(FunctionObject f, ClassObject ex) { - ex = f.getARaisedType() and - strictcount(f.getARaisedType()) = 1 and - not exists(f.getFunction().getANormalExit()) and - /* raising StopIteration is equivalent to a return in a generator */ - not ex = theStopIterationType() + ex = f.getARaisedType() and + strictcount(f.getARaisedType()) = 1 and + not exists(f.getFunction().getANormalExit()) and + /* raising StopIteration is equivalent to a return in a generator */ + not ex = theStopIterationType() } from FunctionObject f, ClassObject cls, string message where - f.getFunction().isSpecialMethod() and - not is_abstract(f) and - always_raises(f, cls) and - ( - no_need_to_raise(f.getName(), message) and not cls.getName() = "NotImplementedError" - or - not correct_raise(f.getName(), cls) and - not cls.getName() = "NotImplementedError" and - exists(ClassObject preferred | preferred_raise(f.getName(), preferred) | - message = "raise " + preferred.getName() + " instead" - ) + f.getFunction().isSpecialMethod() and + not is_abstract(f) and + always_raises(f, cls) and + ( + no_need_to_raise(f.getName(), message) and not cls.getName() = "NotImplementedError" + or + not correct_raise(f.getName(), cls) and + not cls.getName() = "NotImplementedError" and + exists(ClassObject preferred | preferred_raise(f.getName(), preferred) | + message = "raise " + preferred.getName() + " instead" ) + ) select f, "Function always raises $@; " + message, cls, cls.toString() diff --git a/python/ql/src/Functions/IncorrectlyOverriddenMethod.ql b/python/ql/src/Functions/IncorrectlyOverriddenMethod.ql index 953641a6c6a..e607245f97f 100644 --- a/python/ql/src/Functions/IncorrectlyOverriddenMethod.ql +++ b/python/ql/src/Functions/IncorrectlyOverriddenMethod.ql @@ -14,18 +14,18 @@ import Expressions.CallArgs from Call call, FunctionValue func, FunctionValue overridden, string problem where - func.overrides(overridden) and - ( - wrong_args(call, func, _, problem) and - correct_args_if_called_as_method(call, overridden) - or - exists(string name | - illegally_named_parameter(call, func, name) and - problem = "an argument named '" + name + "'" and - overridden.getScope().getAnArg().(Name).getId() = name - ) + func.overrides(overridden) and + ( + wrong_args(call, func, _, problem) and + correct_args_if_called_as_method(call, overridden) + or + exists(string name | + illegally_named_parameter(call, func, name) and + problem = "an argument named '" + name + "'" and + overridden.getScope().getAnArg().(Name).getId() = name ) + ) select func, - "Overriding method signature does not match $@, where it is passed " + problem + - ". Overridden method $@ is correctly specified.", call, "here", overridden, - overridden.descriptiveString() + "Overriding method signature does not match $@, where it is passed " + problem + + ". Overridden method $@ is correctly specified.", call, "here", overridden, + overridden.descriptiveString() diff --git a/python/ql/src/Functions/IncorrectlySpecifiedOverriddenMethod.ql b/python/ql/src/Functions/IncorrectlySpecifiedOverriddenMethod.ql index 56f4abe29e8..0d68d0b506e 100644 --- a/python/ql/src/Functions/IncorrectlySpecifiedOverriddenMethod.ql +++ b/python/ql/src/Functions/IncorrectlySpecifiedOverriddenMethod.ql @@ -15,23 +15,23 @@ import Expressions.CallArgs from Call call, FunctionValue func, FunctionValue overriding, string problem where - not func.getName() = "__init__" and - overriding.overrides(func) and - call = overriding.getAMethodCall().getNode() and - correct_args_if_called_as_method(call, overriding) and - ( - arg_count(call) + 1 < func.minParameters() and problem = "too few arguments" - or - arg_count(call) >= func.maxParameters() and problem = "too many arguments" - or - exists(string name | - call.getAKeyword().getArg() = name and - overriding.getScope().getAnArg().(Name).getId() = name and - not func.getScope().getAnArg().(Name).getId() = name and - problem = "an argument named '" + name + "'" - ) + not func.getName() = "__init__" and + overriding.overrides(func) and + call = overriding.getAMethodCall().getNode() and + correct_args_if_called_as_method(call, overriding) and + ( + arg_count(call) + 1 < func.minParameters() and problem = "too few arguments" + or + arg_count(call) >= func.maxParameters() and problem = "too many arguments" + or + exists(string name | + call.getAKeyword().getArg() = name and + overriding.getScope().getAnArg().(Name).getId() = name and + not func.getScope().getAnArg().(Name).getId() = name and + problem = "an argument named '" + name + "'" ) + ) select func, - "Overridden method signature does not match $@, where it is passed " + problem + - ". Overriding method $@ matches the call.", call, "call", overriding, - overriding.descriptiveString() + "Overridden method signature does not match $@, where it is passed " + problem + + ". Overriding method $@ matches the call.", call, "call", overriding, + overriding.descriptiveString() diff --git a/python/ql/src/Functions/InitIsGenerator.ql b/python/ql/src/Functions/InitIsGenerator.ql index bb02f103ea3..5e3f1ff574b 100644 --- a/python/ql/src/Functions/InitIsGenerator.ql +++ b/python/ql/src/Functions/InitIsGenerator.ql @@ -14,6 +14,6 @@ import python from Function f where - f.isInitMethod() and - (exists(Yield y | y.getScope() = f) or exists(YieldFrom y | y.getScope() = f)) + f.isInitMethod() and + (exists(Yield y | y.getScope() = f) or exists(YieldFrom y | y.getScope() = f)) select f, "__init__ method is a generator." diff --git a/python/ql/src/Functions/IterReturnsNonIterator.ql b/python/ql/src/Functions/IterReturnsNonIterator.ql index 405bff78c04..aba772c9ab7 100644 --- a/python/ql/src/Functions/IterReturnsNonIterator.ql +++ b/python/ql/src/Functions/IterReturnsNonIterator.ql @@ -14,10 +14,10 @@ import python from ClassValue iterable, FunctionValue iter, ClassValue iterator where - iter = iterable.lookup("__iter__") and - iterator = iter.getAnInferredReturnType() and - not iterator.isIterator() + iter = iterable.lookup("__iter__") and + iterator = iter.getAnInferredReturnType() and + not iterator.isIterator() select iterator, - "Class " + iterator.getName() + - " is returned as an iterator (by $@) but does not fully implement the iterator interface.", - iter, iter.getName() + "Class " + iterator.getName() + + " is returned as an iterator (by $@) but does not fully implement the iterator interface.", + iter, iter.getName() diff --git a/python/ql/src/Functions/IterReturnsNonSelf.ql b/python/ql/src/Functions/IterReturnsNonSelf.ql index 095685b749a..3d6c8c7da35 100644 --- a/python/ql/src/Functions/IterReturnsNonSelf.ql +++ b/python/ql/src/Functions/IterReturnsNonSelf.ql @@ -17,14 +17,14 @@ Function iter_method(ClassValue t) { result = t.lookup("__iter__").(FunctionValu predicate is_self(Name value, Function f) { value.getVariable() = f.getArg(0).(Name).getVariable() } predicate returns_non_self(Function f) { - exists(f.getFallthroughNode()) - or - exists(Return r | r.getScope() = f and not is_self(r.getValue(), f)) - or - exists(Return r | r.getScope() = f and not exists(r.getValue())) + exists(f.getFallthroughNode()) + or + exists(Return r | r.getScope() = f and not is_self(r.getValue(), f)) + or + exists(Return r | r.getScope() = f and not exists(r.getValue())) } from ClassValue t, Function iter where t.isIterator() and iter = iter_method(t) and returns_non_self(iter) select t, "Class " + t.getName() + " is an iterator but its $@ method does not return 'self'.", - iter, iter.getName() + iter, iter.getName() diff --git a/python/ql/src/Functions/ModificationOfParameterWithDefault.ql b/python/ql/src/Functions/ModificationOfParameterWithDefault.ql index aa7c90e4a6b..03edc35fa17 100644 --- a/python/ql/src/Functions/ModificationOfParameterWithDefault.ql +++ b/python/ql/src/Functions/ModificationOfParameterWithDefault.ql @@ -15,85 +15,85 @@ import python import semmle.python.security.Paths predicate safe_method(string name) { - name = "count" or - name = "index" or - name = "copy" or - name = "get" or - name = "has_key" or - name = "items" or - name = "keys" or - name = "values" or - name = "iteritems" or - name = "iterkeys" or - name = "itervalues" or - name = "__contains__" or - name = "__getitem__" or - name = "__getattribute__" + name = "count" or + name = "index" or + name = "copy" or + name = "get" or + name = "has_key" or + name = "items" or + name = "keys" or + name = "values" or + name = "iteritems" or + name = "iterkeys" or + name = "itervalues" or + name = "__contains__" or + name = "__getitem__" or + name = "__getattribute__" } /** Gets the truthiness (non emptyness) of the default of `p` if that value is mutable */ private boolean mutableDefaultValue(Parameter p) { - exists(Dict d | p.getDefault() = d | - exists(d.getAKey()) and result = true - or - not exists(d.getAKey()) and result = false - ) + exists(Dict d | p.getDefault() = d | + exists(d.getAKey()) and result = true or - exists(List l | p.getDefault() = l | - exists(l.getAnElt()) and result = true - or - not exists(l.getAnElt()) and result = false - ) + not exists(d.getAKey()) and result = false + ) + or + exists(List l | p.getDefault() = l | + exists(l.getAnElt()) and result = true + or + not exists(l.getAnElt()) and result = false + ) } class NonEmptyMutableValue extends TaintKind { - NonEmptyMutableValue() { this = "non-empty mutable value" } + NonEmptyMutableValue() { this = "non-empty mutable value" } } class EmptyMutableValue extends TaintKind { - EmptyMutableValue() { this = "empty mutable value" } + EmptyMutableValue() { this = "empty mutable value" } - override boolean booleanValue() { result = false } + override boolean booleanValue() { result = false } } class MutableDefaultValue extends TaintSource { - boolean nonEmpty; + boolean nonEmpty; - MutableDefaultValue() { nonEmpty = mutableDefaultValue(this.(NameNode).getNode()) } + MutableDefaultValue() { nonEmpty = mutableDefaultValue(this.(NameNode).getNode()) } - override string toString() { result = "mutable default value" } + override string toString() { result = "mutable default value" } - override predicate isSourceOf(TaintKind kind) { - nonEmpty = false and kind instanceof EmptyMutableValue - or - nonEmpty = true and kind instanceof NonEmptyMutableValue - } + override predicate isSourceOf(TaintKind kind) { + nonEmpty = false and kind instanceof EmptyMutableValue + or + nonEmpty = true and kind instanceof NonEmptyMutableValue + } } private ClassValue mutable_class() { - result = Value::named("list") or - result = Value::named("dict") + result = Value::named("list") or + result = Value::named("dict") } class Mutation extends TaintSink { - Mutation() { - exists(AugAssign a | a.getTarget().getAFlowNode() = this) - or - exists(Call c, Attribute a | c.getFunc() = a | - a.getObject().getAFlowNode() = this and - not safe_method(a.getName()) and - this.(ControlFlowNode).pointsTo().getClass() = mutable_class() - ) - } + Mutation() { + exists(AugAssign a | a.getTarget().getAFlowNode() = this) + or + exists(Call c, Attribute a | c.getFunc() = a | + a.getObject().getAFlowNode() = this and + not safe_method(a.getName()) and + this.(ControlFlowNode).pointsTo().getClass() = mutable_class() + ) + } - override predicate sinks(TaintKind kind) { - kind instanceof EmptyMutableValue - or - kind instanceof NonEmptyMutableValue - } + override predicate sinks(TaintKind kind) { + kind instanceof EmptyMutableValue + or + kind instanceof NonEmptyMutableValue + } } from TaintedPathSource src, TaintedPathSink sink where src.flowsTo(sink) select sink.getSink(), src, sink, "$@ flows to here and is mutated.", src.getSource(), - "Default value" + "Default value" diff --git a/python/ql/src/Functions/NonCls.ql b/python/ql/src/Functions/NonCls.ql index 10ca06af12c..5cb9fafab89 100644 --- a/python/ql/src/Functions/NonCls.ql +++ b/python/ql/src/Functions/NonCls.ql @@ -15,36 +15,36 @@ import python predicate first_arg_cls(Function f) { - exists(string argname | argname = f.getArgName(0) | - argname = "cls" - or - /* Not PEP8, but relatively common */ - argname = "mcls" - ) + exists(string argname | argname = f.getArgName(0) | + argname = "cls" + or + /* Not PEP8, but relatively common */ + argname = "mcls" + ) } predicate is_type_method(Function f) { - exists(ClassValue c | c.getScope() = f.getScope() and c.getASuperType() = ClassValue::type()) + exists(ClassValue c | c.getScope() = f.getScope() and c.getASuperType() = ClassValue::type()) } predicate classmethod_decorators_only(Function f) { - forall(Expr decorator | decorator = f.getADecorator() | decorator.(Name).getId() = "classmethod") + forall(Expr decorator | decorator = f.getADecorator() | decorator.(Name).getId() = "classmethod") } from Function f, string message where - (f.getADecorator().(Name).getId() = "classmethod" or is_type_method(f)) and - not first_arg_cls(f) and - classmethod_decorators_only(f) and - not f.getName() = "__new__" and - ( - if exists(f.getArgName(0)) - then - message = - "Class methods or methods of a type deriving from type should have 'cls', rather than '" + - f.getArgName(0) + "', as their first parameter." - else - message = - "Class methods or methods of a type deriving from type should have 'cls' as their first parameter." - ) + (f.getADecorator().(Name).getId() = "classmethod" or is_type_method(f)) and + not first_arg_cls(f) and + classmethod_decorators_only(f) and + not f.getName() = "__new__" and + ( + if exists(f.getArgName(0)) + then + message = + "Class methods or methods of a type deriving from type should have 'cls', rather than '" + + f.getArgName(0) + "', as their first parameter." + else + message = + "Class methods or methods of a type deriving from type should have 'cls' as their first parameter." + ) select f, message diff --git a/python/ql/src/Functions/NonSelf.ql b/python/ql/src/Functions/NonSelf.ql index a3102eee2aa..cb8924c071a 100644 --- a/python/ql/src/Functions/NonSelf.ql +++ b/python/ql/src/Functions/NonSelf.ql @@ -17,42 +17,42 @@ import python import semmle.python.libraries.Zope predicate is_type_method(FunctionValue fv) { - exists(ClassValue c | c.declaredAttribute(_) = fv and c.getASuperType() = ClassValue::type()) + exists(ClassValue c | c.declaredAttribute(_) = fv and c.getASuperType() = ClassValue::type()) } predicate used_in_defining_scope(FunctionValue fv) { - exists(Call c | c.getScope() = fv.getScope().getScope() and c.getFunc().pointsTo(fv)) + exists(Call c | c.getScope() = fv.getScope().getScope() and c.getFunc().pointsTo(fv)) } from Function f, FunctionValue fv, string message where - exists(ClassValue cls, string name | - cls.declaredAttribute(name) = fv and - cls.isNewStyle() and - not name = "__new__" and - not name = "__metaclass__" and - not name = "__init_subclass__" and - not name = "__class_getitem__" and - /* declared in scope */ - f.getScope() = cls.getScope() - ) and - not f.getArgName(0) = "self" and - not is_type_method(fv) and - fv.getScope() = f and - not f.getName() = "lambda" and - not used_in_defining_scope(fv) and + exists(ClassValue cls, string name | + cls.declaredAttribute(name) = fv and + cls.isNewStyle() and + not name = "__new__" and + not name = "__metaclass__" and + not name = "__init_subclass__" and + not name = "__class_getitem__" and + /* declared in scope */ + f.getScope() = cls.getScope() + ) and + not f.getArgName(0) = "self" and + not is_type_method(fv) and + fv.getScope() = f and + not f.getName() = "lambda" and + not used_in_defining_scope(fv) and + ( ( - ( - if exists(f.getArgName(0)) - then - message = - "Normal methods should have 'self', rather than '" + f.getArgName(0) + - "', as their first parameter." - else - message = - "Normal methods should have at least one parameter (the first of which should be 'self')." - ) and - not f.hasVarArg() + if exists(f.getArgName(0)) + then + message = + "Normal methods should have 'self', rather than '" + f.getArgName(0) + + "', as their first parameter." + else + message = + "Normal methods should have at least one parameter (the first of which should be 'self')." ) and - not fv instanceof ZopeInterfaceMethodValue + not f.hasVarArg() + ) and + not fv instanceof ZopeInterfaceMethodValue select f, message diff --git a/python/ql/src/Functions/OverlyComplexDelMethod.ql b/python/ql/src/Functions/OverlyComplexDelMethod.ql index b709af7fb11..2fc8789da34 100644 --- a/python/ql/src/Functions/OverlyComplexDelMethod.ql +++ b/python/ql/src/Functions/OverlyComplexDelMethod.ql @@ -17,8 +17,8 @@ import python from FunctionValue method where - exists(ClassValue c | - c.declaredAttribute("__del__") = method and - method.getScope().getMetrics().getCyclomaticComplexity() > 3 - ) + exists(ClassValue c | + c.declaredAttribute("__del__") = method and + method.getScope().getMetrics().getCyclomaticComplexity() > 3 + ) select method, "Overly complex '__del__' method." diff --git a/python/ql/src/Functions/ReturnConsistentTupleSizes.ql b/python/ql/src/Functions/ReturnConsistentTupleSizes.ql index 02965c2a3a5..9046f52cecb 100644 --- a/python/ql/src/Functions/ReturnConsistentTupleSizes.ql +++ b/python/ql/src/Functions/ReturnConsistentTupleSizes.ql @@ -13,18 +13,18 @@ import python predicate returns_tuple_of_size(Function func, int size, AstNode origin) { - exists(Return return, TupleValue val | - return.getScope() = func and - return.getValue().pointsTo(val, origin) - | - size = val.length() - ) + exists(Return return, TupleValue val | + return.getScope() = func and + return.getValue().pointsTo(val, origin) + | + size = val.length() + ) } from Function func, int s1, int s2, AstNode t1, AstNode t2 where - returns_tuple_of_size(func, s1, t1) and - returns_tuple_of_size(func, s2, t2) and - s1 < s2 + returns_tuple_of_size(func, s1, t1) and + returns_tuple_of_size(func, s2, t2) and + s1 < s2 select func, func.getQualifiedName() + " returns $@ and $@.", t1, "tuple of size " + s1, t2, - "tuple of size " + s2 + "tuple of size " + s2 diff --git a/python/ql/src/Functions/ReturnValueIgnored.ql b/python/ql/src/Functions/ReturnValueIgnored.ql index e6d962e594f..b7f272dcc2d 100644 --- a/python/ql/src/Functions/ReturnValueIgnored.ql +++ b/python/ql/src/Functions/ReturnValueIgnored.ql @@ -18,66 +18,66 @@ import python import semmle.python.objects.Callables predicate meaningful_return_value(Expr val) { - val instanceof Name - or - val instanceof BooleanLiteral - or - exists(FunctionValue callee | - val = callee.getACall().getNode() and returns_meaningful_value(callee) - ) - or - not exists(FunctionValue callee | val = callee.getACall().getNode()) and not val instanceof Name + val instanceof Name + or + val instanceof BooleanLiteral + or + exists(FunctionValue callee | + val = callee.getACall().getNode() and returns_meaningful_value(callee) + ) + or + not exists(FunctionValue callee | val = callee.getACall().getNode()) and not val instanceof Name } /* Value is used before returning, and thus its value is not lost if ignored */ predicate used_value(Expr val) { - exists(LocalVariable var, Expr other | - var.getAnAccess() = val and other = var.getAnAccess() and not other = val - ) + exists(LocalVariable var, Expr other | + var.getAnAccess() = val and other = var.getAnAccess() and not other = val + ) } predicate returns_meaningful_value(FunctionValue f) { - not exists(f.getScope().getFallthroughNode()) and - ( - exists(Return ret, Expr val | ret.getScope() = f.getScope() and val = ret.getValue() | - meaningful_return_value(val) and - not used_value(val) - ) - or - /* - * Is f a builtin function that returns something other than None? - * Ignore __import__ as it is often called purely for side effects - */ - - f.isBuiltin() and - f.getAnInferredReturnType() != ClassValue::nonetype() and - not f.getName() = "__import__" + not exists(f.getScope().getFallthroughNode()) and + ( + exists(Return ret, Expr val | ret.getScope() = f.getScope() and val = ret.getValue() | + meaningful_return_value(val) and + not used_value(val) ) + or + /* + * Is f a builtin function that returns something other than None? + * Ignore __import__ as it is often called purely for side effects + */ + + f.isBuiltin() and + f.getAnInferredReturnType() != ClassValue::nonetype() and + not f.getName() = "__import__" + ) } /* If a call is wrapped tightly in a try-except then we assume it is being executed for the exception. */ predicate wrapped_in_try_except(ExprStmt call) { - exists(Try t | - exists(t.getAHandler()) and - strictcount(Call c | t.getBody().contains(c)) = 1 and - call = t.getAStmt() - ) + exists(Try t | + exists(t.getAHandler()) and + strictcount(Call c | t.getBody().contains(c)) = 1 and + call = t.getAStmt() + ) } from ExprStmt call, FunctionValue callee, float percentage_used, int total where - call.getValue() = callee.getACall().getNode() and - returns_meaningful_value(callee) and - not wrapped_in_try_except(call) and - exists(int unused | - unused = count(ExprStmt e | e.getValue().getAFlowNode() = callee.getACall()) and - total = count(callee.getACall()) - | - percentage_used = (100.0 * (total - unused) / total).floor() - ) and - /* Report an alert if we see at least 5 calls and the return value is used in at least 3/4 of those calls. */ - percentage_used >= 75 and - total >= 5 + call.getValue() = callee.getACall().getNode() and + returns_meaningful_value(callee) and + not wrapped_in_try_except(call) and + exists(int unused | + unused = count(ExprStmt e | e.getValue().getAFlowNode() = callee.getACall()) and + total = count(callee.getACall()) + | + percentage_used = (100.0 * (total - unused) / total).floor() + ) and + /* Report an alert if we see at least 5 calls and the return value is used in at least 3/4 of those calls. */ + percentage_used >= 75 and + total >= 5 select call, - "Call discards return value of function $@. The result is used in " + percentage_used.toString() + - "% of calls.", callee, callee.getName() + "Call discards return value of function $@. The result is used in " + percentage_used.toString() + + "% of calls.", callee, callee.getName() diff --git a/python/ql/src/Functions/SignatureOverriddenMethod.ql b/python/ql/src/Functions/SignatureOverriddenMethod.ql index f24cea3811e..e695f2385ea 100644 --- a/python/ql/src/Functions/SignatureOverriddenMethod.ql +++ b/python/ql/src/Functions/SignatureOverriddenMethod.ql @@ -16,20 +16,20 @@ import Expressions.CallArgs from FunctionValue base, PythonFunctionValue derived where - not exists(base.getACall()) and - not exists(FunctionValue a_derived | - a_derived.overrides(base) and - exists(a_derived.getACall()) - ) and - not derived.getScope().isSpecialMethod() and - derived.getName() != "__init__" and - derived.isNormalMethod() and - not derived.getScope().isSpecialMethod() and - // call to overrides distributed for efficiency - ( - derived.overrides(base) and derived.minParameters() > base.maxParameters() - or - derived.overrides(base) and derived.maxParameters() < base.minParameters() - ) + not exists(base.getACall()) and + not exists(FunctionValue a_derived | + a_derived.overrides(base) and + exists(a_derived.getACall()) + ) and + not derived.getScope().isSpecialMethod() and + derived.getName() != "__init__" and + derived.isNormalMethod() and + not derived.getScope().isSpecialMethod() and + // call to overrides distributed for efficiency + ( + derived.overrides(base) and derived.minParameters() > base.maxParameters() + or + derived.overrides(base) and derived.maxParameters() < base.minParameters() + ) select derived, "Overriding method '" + derived.getName() + "' has signature mismatch with $@.", - base, "overridden method" + base, "overridden method" diff --git a/python/ql/src/Functions/SignatureSpecialMethods.ql b/python/ql/src/Functions/SignatureSpecialMethods.ql index bd5587ec903..87aeeae51ff 100644 --- a/python/ql/src/Functions/SignatureSpecialMethods.ql +++ b/python/ql/src/Functions/SignatureSpecialMethods.ql @@ -13,200 +13,200 @@ import python predicate is_unary_op(string name) { - name = "__del__" or - name = "__repr__" or - name = "__str__" or - name = "__hash__" or - name = "__bool__" or - name = "__nonzero__" or - name = "__unicode__" or - name = "__len__" or - name = "__iter__" or - name = "__reversed__" or - name = "__neg__" or - name = "__pos__" or - name = "__abs__" or - name = "__invert__" or - name = "__complex__" or - name = "__int__" or - name = "__float__" or - name = "__long__" or - name = "__oct__" or - name = "__hex__" or - name = "__index__" or - name = "__enter__" + name = "__del__" or + name = "__repr__" or + name = "__str__" or + name = "__hash__" or + name = "__bool__" or + name = "__nonzero__" or + name = "__unicode__" or + name = "__len__" or + name = "__iter__" or + name = "__reversed__" or + name = "__neg__" or + name = "__pos__" or + name = "__abs__" or + name = "__invert__" or + name = "__complex__" or + name = "__int__" or + name = "__float__" or + name = "__long__" or + name = "__oct__" or + name = "__hex__" or + name = "__index__" or + name = "__enter__" } predicate is_binary_op(string name) { - name = "__lt__" or - name = "__le__" or - name = "__eq__" or - name = "__ne__" or - name = "__gt__" or - name = "__ge__" or - name = "__cmp__" or - name = "__rcmp__" or - name = "__getattr___" or - name = "__getattribute___" or - name = "__delattr__" or - name = "__delete__" or - name = "__instancecheck__" or - name = "__subclasscheck__" or - name = "__getitem__" or - name = "__delitem__" or - name = "__contains__" or - name = "__add__" or - name = "__sub__" or - name = "__mul__" or - name = "__floordiv__" or - name = "__div__" or - name = "__truediv__" or - name = "__mod__" or - name = "__divmod__" or - name = "__lshift__" or - name = "__rshift__" or - name = "__and__" or - name = "__xor__" or - name = "__or__" or - name = "__radd__" or - name = "__rsub__" or - name = "__rmul__" or - name = "__rfloordiv__" or - name = "__rdiv__" or - name = "__rtruediv__" or - name = "__rmod__" or - name = "__rdivmod__" or - name = "__rpow__" or - name = "__rlshift__" or - name = "__rrshift__" or - name = "__rand__" or - name = "__rxor__" or - name = "__ror__" or - name = "__iadd__" or - name = "__isub__" or - name = "__imul__" or - name = "__ifloordiv__" or - name = "__idiv__" or - name = "__itruediv__" or - name = "__imod__" or - name = "__idivmod__" or - name = "__ipow__" or - name = "__ilshift__" or - name = "__irshift__" or - name = "__iand__" or - name = "__ixor__" or - name = "__ior__" or - name = "__coerce__" + name = "__lt__" or + name = "__le__" or + name = "__eq__" or + name = "__ne__" or + name = "__gt__" or + name = "__ge__" or + name = "__cmp__" or + name = "__rcmp__" or + name = "__getattr___" or + name = "__getattribute___" or + name = "__delattr__" or + name = "__delete__" or + name = "__instancecheck__" or + name = "__subclasscheck__" or + name = "__getitem__" or + name = "__delitem__" or + name = "__contains__" or + name = "__add__" or + name = "__sub__" or + name = "__mul__" or + name = "__floordiv__" or + name = "__div__" or + name = "__truediv__" or + name = "__mod__" or + name = "__divmod__" or + name = "__lshift__" or + name = "__rshift__" or + name = "__and__" or + name = "__xor__" or + name = "__or__" or + name = "__radd__" or + name = "__rsub__" or + name = "__rmul__" or + name = "__rfloordiv__" or + name = "__rdiv__" or + name = "__rtruediv__" or + name = "__rmod__" or + name = "__rdivmod__" or + name = "__rpow__" or + name = "__rlshift__" or + name = "__rrshift__" or + name = "__rand__" or + name = "__rxor__" or + name = "__ror__" or + name = "__iadd__" or + name = "__isub__" or + name = "__imul__" or + name = "__ifloordiv__" or + name = "__idiv__" or + name = "__itruediv__" or + name = "__imod__" or + name = "__idivmod__" or + name = "__ipow__" or + name = "__ilshift__" or + name = "__irshift__" or + name = "__iand__" or + name = "__ixor__" or + name = "__ior__" or + name = "__coerce__" } predicate is_ternary_op(string name) { - name = "__setattr__" or - name = "__set__" or - name = "__setitem__" or - name = "__getslice__" or - name = "__delslice__" + name = "__setattr__" or + name = "__set__" or + name = "__setitem__" or + name = "__getslice__" or + name = "__delslice__" } predicate is_quad_op(string name) { name = "__setslice__" or name = "__exit__" } int argument_count(PythonFunctionValue f, string name, ClassValue cls) { - cls.declaredAttribute(name) = f and - ( - is_unary_op(name) and result = 1 - or - is_binary_op(name) and result = 2 - or - is_ternary_op(name) and result = 3 - or - is_quad_op(name) and result = 4 - ) + cls.declaredAttribute(name) = f and + ( + is_unary_op(name) and result = 1 + or + is_binary_op(name) and result = 2 + or + is_ternary_op(name) and result = 3 + or + is_quad_op(name) and result = 4 + ) } predicate incorrect_special_method_defn( - PythonFunctionValue func, string message, boolean show_counts, string name, ClassValue owner + PythonFunctionValue func, string message, boolean show_counts, string name, ClassValue owner ) { - exists(int required | required = argument_count(func, name, owner) | - /* actual_non_default <= actual */ - if required > func.maxParameters() - then message = "Too few parameters" and show_counts = true - else - if required < func.minParameters() - then message = "Too many parameters" and show_counts = true - else - if func.minParameters() < required and not func.getScope().hasVarArg() - then - message = (required - func.minParameters()) + " default values(s) will never be used" and - show_counts = false - else none() - ) + exists(int required | required = argument_count(func, name, owner) | + /* actual_non_default <= actual */ + if required > func.maxParameters() + then message = "Too few parameters" and show_counts = true + else + if required < func.minParameters() + then message = "Too many parameters" and show_counts = true + else + if func.minParameters() < required and not func.getScope().hasVarArg() + then + message = (required - func.minParameters()) + " default values(s) will never be used" and + show_counts = false + else none() + ) } predicate incorrect_pow(FunctionValue func, string message, boolean show_counts, ClassValue owner) { - owner.declaredAttribute("__pow__") = func and - ( - func.maxParameters() < 2 and message = "Too few parameters" and show_counts = true - or - func.minParameters() > 3 and message = "Too many parameters" and show_counts = true - or - func.minParameters() < 2 and - message = (2 - func.minParameters()) + " default value(s) will never be used" and - show_counts = false - or - func.minParameters() = 3 and - message = "Third parameter to __pow__ should have a default value" and - show_counts = false - ) + owner.declaredAttribute("__pow__") = func and + ( + func.maxParameters() < 2 and message = "Too few parameters" and show_counts = true + or + func.minParameters() > 3 and message = "Too many parameters" and show_counts = true + or + func.minParameters() < 2 and + message = (2 - func.minParameters()) + " default value(s) will never be used" and + show_counts = false + or + func.minParameters() = 3 and + message = "Third parameter to __pow__ should have a default value" and + show_counts = false + ) } predicate incorrect_get(FunctionValue func, string message, boolean show_counts, ClassValue owner) { - owner.declaredAttribute("__get__") = func and - ( - func.maxParameters() < 3 and message = "Too few parameters" and show_counts = true - or - func.minParameters() > 3 and message = "Too many parameters" and show_counts = true - or - func.minParameters() < 2 and - not func.getScope().hasVarArg() and - message = (2 - func.minParameters()) + " default value(s) will never be used" and - show_counts = false - ) + owner.declaredAttribute("__get__") = func and + ( + func.maxParameters() < 3 and message = "Too few parameters" and show_counts = true + or + func.minParameters() > 3 and message = "Too many parameters" and show_counts = true + or + func.minParameters() < 2 and + not func.getScope().hasVarArg() and + message = (2 - func.minParameters()) + " default value(s) will never be used" and + show_counts = false + ) } string should_have_parameters(PythonFunctionValue f, string name, ClassValue owner) { - exists(int i | i = argument_count(f, name, owner) | result = i.toString()) - or - owner.declaredAttribute(name) = f and - (name = "__get__" or name = "__pow__") and - result = "2 or 3" + exists(int i | i = argument_count(f, name, owner) | result = i.toString()) + or + owner.declaredAttribute(name) = f and + (name = "__get__" or name = "__pow__") and + result = "2 or 3" } string has_parameters(PythonFunctionValue f) { - exists(int i | i = f.minParameters() | - i = 0 and result = "no parameters" - or - i = 1 and result = "1 parameter" - or - i > 1 and result = i.toString() + " parameters" - ) + exists(int i | i = f.minParameters() | + i = 0 and result = "no parameters" + or + i = 1 and result = "1 parameter" + or + i > 1 and result = i.toString() + " parameters" + ) } from - PythonFunctionValue f, string message, string sizes, boolean show_counts, string name, - ClassValue owner + PythonFunctionValue f, string message, string sizes, boolean show_counts, string name, + ClassValue owner where - ( - incorrect_special_method_defn(f, message, show_counts, name, owner) - or - incorrect_pow(f, message, show_counts, owner) and name = "__pow__" - or - incorrect_get(f, message, show_counts, owner) and name = "__get__" - ) and - ( - show_counts = false and sizes = "" - or - show_counts = true and - sizes = - ", which has " + has_parameters(f) + ", but should have " + - should_have_parameters(f, name, owner) - ) + ( + incorrect_special_method_defn(f, message, show_counts, name, owner) + or + incorrect_pow(f, message, show_counts, owner) and name = "__pow__" + or + incorrect_get(f, message, show_counts, owner) and name = "__get__" + ) and + ( + show_counts = false and sizes = "" + or + show_counts = true and + sizes = + ", which has " + has_parameters(f) + ", but should have " + + should_have_parameters(f, name, owner) + ) select f, message + " for special method " + name + sizes + ", in class $@.", owner, owner.getName() diff --git a/python/ql/src/Functions/UseImplicitNoneReturnValue.ql b/python/ql/src/Functions/UseImplicitNoneReturnValue.ql index 38632358c08..606f1e6da51 100644 --- a/python/ql/src/Functions/UseImplicitNoneReturnValue.ql +++ b/python/ql/src/Functions/UseImplicitNoneReturnValue.ql @@ -13,26 +13,26 @@ import python import Testing.Mox predicate is_used(Call c) { - exists(Expr outer | outer != c and outer.containsInScope(c) | - outer instanceof Call or outer instanceof Attribute or outer instanceof Subscript - ) - or - exists(Stmt s | - c = s.getASubExpression() and - not s instanceof ExprStmt and - /* Ignore if a single return, as def f(): return g() is quite common. Covers implicit return in a lambda. */ - not (s instanceof Return and strictcount(Return r | r.getScope() = s.getScope()) = 1) - ) + exists(Expr outer | outer != c and outer.containsInScope(c) | + outer instanceof Call or outer instanceof Attribute or outer instanceof Subscript + ) + or + exists(Stmt s | + c = s.getASubExpression() and + not s instanceof ExprStmt and + /* Ignore if a single return, as def f(): return g() is quite common. Covers implicit return in a lambda. */ + not (s instanceof Return and strictcount(Return r | r.getScope() = s.getScope()) = 1) + ) } from Call c, FunctionValue func where - /* Call result is used, but callee is a procedure */ - is_used(c) and - c.getFunc().pointsTo(func) and - func.getScope().isProcedure() and - /* All callees are procedures */ - forall(FunctionValue callee | c.getFunc().pointsTo(callee) | callee.getScope().isProcedure()) and - /* Mox return objects have an `AndReturn` method */ - not useOfMoxInModule(c.getEnclosingModule()) + /* Call result is used, but callee is a procedure */ + is_used(c) and + c.getFunc().pointsTo(func) and + func.getScope().isProcedure() and + /* All callees are procedures */ + forall(FunctionValue callee | c.getFunc().pointsTo(callee) | callee.getScope().isProcedure()) and + /* Mox return objects have an `AndReturn` method */ + not useOfMoxInModule(c.getEnclosingModule()) select c, "The result of '$@' is used even though it is always None.", func, func.getQualifiedName() diff --git a/python/ql/src/Imports/Cyclic.qll b/python/ql/src/Imports/Cyclic.qll index 29c05b08209..dd25f06d0e5 100644 --- a/python/ql/src/Imports/Cyclic.qll +++ b/python/ql/src/Imports/Cyclic.qll @@ -3,84 +3,84 @@ import python predicate is_import_time(Stmt s) { not s.getScope+() instanceof Function } ModuleValue module_imported_by(ModuleValue m) { - exists(Stmt imp | - result = stmt_imports(imp) and - imp.getEnclosingModule() = m.getScope() and - // Import must reach exit to be part of a cycle - imp.getAnEntryNode().getBasicBlock().reachesExit() - ) + exists(Stmt imp | + result = stmt_imports(imp) and + imp.getEnclosingModule() = m.getScope() and + // Import must reach exit to be part of a cycle + imp.getAnEntryNode().getBasicBlock().reachesExit() + ) } /** Is there a circular import of 'm1' beginning with 'm2'? */ predicate circular_import(ModuleValue m1, ModuleValue m2) { - m1 != m2 and - m2 = module_imported_by(m1) and - m1 = module_imported_by+(m2) + m1 != m2 and + m2 = module_imported_by(m1) and + m1 = module_imported_by+(m2) } ModuleValue stmt_imports(ImportingStmt s) { - exists(string name | result.importedAs(name) and not name = "__main__" | - name = s.getAnImportedModuleName() and - s.getASubExpression().pointsTo(result) and - not result.isPackage() - ) + exists(string name | result.importedAs(name) and not name = "__main__" | + name = s.getAnImportedModuleName() and + s.getASubExpression().pointsTo(result) and + not result.isPackage() + ) } predicate import_time_imported_module(ModuleValue m1, ModuleValue m2, Stmt imp) { - imp.getEnclosingModule() = m1.getScope() and - is_import_time(imp) and - m2 = stmt_imports(imp) + imp.getEnclosingModule() = m1.getScope() and + is_import_time(imp) and + m2 = stmt_imports(imp) } /** Is there a cyclic import of 'm1' beginning with an import 'm2' at 'imp' where all the imports are top-level? */ predicate import_time_circular_import(ModuleValue m1, ModuleValue m2, Stmt imp) { - m1 != m2 and - import_time_imported_module(m1, m2, imp) and - import_time_transitive_import(m2, _, m1) + m1 != m2 and + import_time_imported_module(m1, m2, imp) and + import_time_transitive_import(m2, _, m1) } predicate import_time_transitive_import(ModuleValue base, Stmt imp, ModuleValue last) { - last != base and - ( - import_time_imported_module(base, last, imp) - or - exists(ModuleValue mid | - import_time_transitive_import(base, imp, mid) and - import_time_imported_module(mid, last, _) - ) - ) and - // Import must reach exit to be part of a cycle - imp.getAnEntryNode().getBasicBlock().reachesExit() + last != base and + ( + import_time_imported_module(base, last, imp) + or + exists(ModuleValue mid | + import_time_transitive_import(base, imp, mid) and + import_time_imported_module(mid, last, _) + ) + ) and + // Import must reach exit to be part of a cycle + imp.getAnEntryNode().getBasicBlock().reachesExit() } /** * Returns import-time usages of module 'm' in module 'enclosing' */ predicate import_time_module_use(ModuleValue m, ModuleValue enclosing, Expr use, string attr) { - exists(Expr mod | - use.getEnclosingModule() = enclosing.getScope() and - not use.getScope+() instanceof Function and - mod.pointsTo(m) and - not is_annotation_with_from_future_import_annotations(use) - | - // either 'M.foo' - use.(Attribute).getObject() = mod and use.(Attribute).getName() = attr - or - // or 'from M import foo' - use.(ImportMember).getModule() = mod and use.(ImportMember).getName() = attr - ) + exists(Expr mod | + use.getEnclosingModule() = enclosing.getScope() and + not use.getScope+() instanceof Function and + mod.pointsTo(m) and + not is_annotation_with_from_future_import_annotations(use) + | + // either 'M.foo' + use.(Attribute).getObject() = mod and use.(Attribute).getName() = attr + or + // or 'from M import foo' + use.(ImportMember).getModule() = mod and use.(ImportMember).getName() = attr + ) } /** * Holds if `use` appears inside an annotation. */ predicate is_used_in_annotation(Expr use) { - exists(FunctionExpr f | - f.getReturns().getASubExpression*() = use or - f.getArgs().getAnAnnotation().getASubExpression*() = use - ) - or - exists(AnnAssign a | a.getAnnotation().getASubExpression*() = use) + exists(FunctionExpr f | + f.getReturns().getASubExpression*() = use or + f.getArgs().getAnAnnotation().getASubExpression*() = use + ) + or + exists(AnnAssign a | a.getAnnotation().getASubExpression*() = use) } /** @@ -89,10 +89,10 @@ predicate is_used_in_annotation(Expr use) { * See https://www.python.org/dev/peps/pep-0563/ */ predicate is_annotation_with_from_future_import_annotations(Expr use) { - exists(ImportMember i | i.getScope() = use.getEnclosingModule() | - i.getModule().pointsTo().getName() = "__future__" and i.getName() = "annotations" - ) and - is_used_in_annotation(use) + exists(ImportMember i | i.getScope() = use.getEnclosingModule() | + i.getModule().pointsTo().getName() = "__future__" and i.getName() = "annotations" + ) and + is_used_in_annotation(use) } /** @@ -101,18 +101,18 @@ predicate is_annotation_with_from_future_import_annotations(Expr use) { * occur after the import 'other' in 'first'. */ predicate failing_import_due_to_cycle( - ModuleValue first, ModuleValue other, Stmt imp, ControlFlowNode defn, Expr use, string attr + ModuleValue first, ModuleValue other, Stmt imp, ControlFlowNode defn, Expr use, string attr ) { - import_time_imported_module(other, first, _) and - import_time_transitive_import(first, imp, other) and - import_time_module_use(first, other, use, attr) and - exists(ImportTimeScope n, SsaVariable v | - defn = v.getDefinition() and - n = first.getScope() and - v.getVariable().getScope() = n and - v.getId() = attr - | - not defn.strictlyDominates(imp.getAnEntryNode()) - ) and - not exists(If i | i.isNameEqMain() and i.contains(use)) + import_time_imported_module(other, first, _) and + import_time_transitive_import(first, imp, other) and + import_time_module_use(first, other, use, attr) and + exists(ImportTimeScope n, SsaVariable v | + defn = v.getDefinition() and + n = first.getScope() and + v.getVariable().getScope() = n and + v.getId() = attr + | + not defn.strictlyDominates(imp.getAnEntryNode()) + ) and + not exists(If i | i.isNameEqMain() and i.contains(use)) } diff --git a/python/ql/src/Imports/CyclicImport.ql b/python/ql/src/Imports/CyclicImport.ql index e14b41acb8e..9e4a153a110 100644 --- a/python/ql/src/Imports/CyclicImport.ql +++ b/python/ql/src/Imports/CyclicImport.ql @@ -16,11 +16,11 @@ import Cyclic from ModuleValue m1, ModuleValue m2, Stmt imp where - imp.getEnclosingModule() = m1.getScope() and - stmt_imports(imp) = m2 and - circular_import(m1, m2) and - m1 != m2 and - // this query finds all cyclic imports that are *not* flagged by ModuleLevelCyclicImport - not failing_import_due_to_cycle(m2, m1, _, _, _, _) and - not exists(If i | i.isNameEqMain() and i.contains(imp)) + imp.getEnclosingModule() = m1.getScope() and + stmt_imports(imp) = m2 and + circular_import(m1, m2) and + m1 != m2 and + // this query finds all cyclic imports that are *not* flagged by ModuleLevelCyclicImport + not failing_import_due_to_cycle(m2, m1, _, _, _, _) and + not exists(If i | i.isNameEqMain() and i.contains(imp)) select imp, "Import of module $@ begins an import cycle.", m2, m2.getName() diff --git a/python/ql/src/Imports/DeprecatedModule.ql b/python/ql/src/Imports/DeprecatedModule.ql index 359f3dad10d..62d17bd5e22 100644 --- a/python/ql/src/Imports/DeprecatedModule.ql +++ b/python/ql/src/Imports/DeprecatedModule.ql @@ -17,69 +17,69 @@ import python * and module `instead` should be used instead (or `instead = "no replacement"`) */ predicate deprecated_module(string name, string instead, int major, int minor) { - name = "posixfile" and instead = "fcntl" and major = 1 and minor = 5 - or - name = "gopherlib" and instead = "no replacement" and major = 2 and minor = 5 - or - name = "rgbimgmodule" and instead = "no replacement" and major = 2 and minor = 5 - or - name = "pre" and instead = "re" and major = 1 and minor = 5 - or - name = "whrandom" and instead = "random" and major = 2 and minor = 1 - or - name = "rfc822" and instead = "email" and major = 2 and minor = 3 - or - name = "mimetools" and instead = "email" and major = 2 and minor = 3 - or - name = "MimeWriter" and instead = "email" and major = 2 and minor = 3 - or - name = "mimify" and instead = "email" and major = 2 and minor = 3 - or - name = "rotor" and instead = "no replacement" and major = 2 and minor = 4 - or - name = "statcache" and instead = "no replacement" and major = 2 and minor = 2 - or - name = "mpz" and instead = "a third party" and major = 2 and minor = 2 - or - name = "xreadlines" and instead = "no replacement" and major = 2 and minor = 3 - or - name = "multifile" and instead = "email" and major = 2 and minor = 5 - or - name = "sets" and instead = "builtins" and major = 2 and minor = 6 - or - name = "buildtools" and instead = "no replacement" and major = 2 and minor = 3 - or - name = "cfmfile" and instead = "no replacement" and major = 2 and minor = 4 - or - name = "macfs" and instead = "no replacement" and major = 2 and minor = 3 - or - name = "md5" and instead = "hashlib" and major = 2 and minor = 5 - or - name = "sha" and instead = "hashlib" and major = 2 and minor = 5 + name = "posixfile" and instead = "fcntl" and major = 1 and minor = 5 + or + name = "gopherlib" and instead = "no replacement" and major = 2 and minor = 5 + or + name = "rgbimgmodule" and instead = "no replacement" and major = 2 and minor = 5 + or + name = "pre" and instead = "re" and major = 1 and minor = 5 + or + name = "whrandom" and instead = "random" and major = 2 and minor = 1 + or + name = "rfc822" and instead = "email" and major = 2 and minor = 3 + or + name = "mimetools" and instead = "email" and major = 2 and minor = 3 + or + name = "MimeWriter" and instead = "email" and major = 2 and minor = 3 + or + name = "mimify" and instead = "email" and major = 2 and minor = 3 + or + name = "rotor" and instead = "no replacement" and major = 2 and minor = 4 + or + name = "statcache" and instead = "no replacement" and major = 2 and minor = 2 + or + name = "mpz" and instead = "a third party" and major = 2 and minor = 2 + or + name = "xreadlines" and instead = "no replacement" and major = 2 and minor = 3 + or + name = "multifile" and instead = "email" and major = 2 and minor = 5 + or + name = "sets" and instead = "builtins" and major = 2 and minor = 6 + or + name = "buildtools" and instead = "no replacement" and major = 2 and minor = 3 + or + name = "cfmfile" and instead = "no replacement" and major = 2 and minor = 4 + or + name = "macfs" and instead = "no replacement" and major = 2 and minor = 3 + or + name = "md5" and instead = "hashlib" and major = 2 and minor = 5 + or + name = "sha" and instead = "hashlib" and major = 2 and minor = 5 } string deprecation_message(string mod) { - exists(int major, int minor | deprecated_module(mod, _, major, minor) | - result = - "The " + mod + " module was deprecated in version " + major.toString() + "." + - minor.toString() + "." - ) + exists(int major, int minor | deprecated_module(mod, _, major, minor) | + result = + "The " + mod + " module was deprecated in version " + major.toString() + "." + + minor.toString() + "." + ) } string replacement_message(string mod) { - exists(string instead | deprecated_module(mod, instead, _, _) | - result = " Use " + instead + " module instead." and not instead = "no replacement" - or - result = "" and instead = "no replacement" - ) + exists(string instead | deprecated_module(mod, instead, _, _) | + result = " Use " + instead + " module instead." and not instead = "no replacement" + or + result = "" and instead = "no replacement" + ) } from ImportExpr imp, string name, string instead where - name = imp.getName() and - deprecated_module(name, instead, _, _) and - not exists(Try try, ExceptStmt except | except = try.getAHandler() | - except.getType().pointsTo(ClassValue::importError()) and - except.containsInScope(imp) - ) + name = imp.getName() and + deprecated_module(name, instead, _, _) and + not exists(Try try, ExceptStmt except | except = try.getAHandler() | + except.getType().pointsTo(ClassValue::importError()) and + except.containsInScope(imp) + ) select imp, deprecation_message(name) + replacement_message(name) diff --git a/python/ql/src/Imports/FromImportOfMutableAttribute.ql b/python/ql/src/Imports/FromImportOfMutableAttribute.ql index aa66fd6d9b2..cbb74977a03 100644 --- a/python/ql/src/Imports/FromImportOfMutableAttribute.ql +++ b/python/ql/src/Imports/FromImportOfMutableAttribute.ql @@ -16,19 +16,19 @@ import semmle.python.filters.Tests from ImportMember im, ModuleValue m, AttrNode store_attr, string name where - m.importedAs(im.getModule().(ImportExpr).getImportedModuleName()) and - im.getName() = name and - /* Modification must be in a function, so it can occur during lifetime of the import value */ - store_attr.getScope() instanceof Function and - /* variable resulting from import must have a long lifetime */ - not im.getScope() instanceof Function and - store_attr.isStore() and - store_attr.getObject(name).pointsTo(m) and - /* Import not in same module as modification. */ - not im.getEnclosingModule() = store_attr.getScope().getEnclosingModule() and - /* Modification is not in a test */ - not store_attr.getScope().getScope*() instanceof TestScope + m.importedAs(im.getModule().(ImportExpr).getImportedModuleName()) and + im.getName() = name and + /* Modification must be in a function, so it can occur during lifetime of the import value */ + store_attr.getScope() instanceof Function and + /* variable resulting from import must have a long lifetime */ + not im.getScope() instanceof Function and + store_attr.isStore() and + store_attr.getObject(name).pointsTo(m) and + /* Import not in same module as modification. */ + not im.getEnclosingModule() = store_attr.getScope().getEnclosingModule() and + /* Modification is not in a test */ + not store_attr.getScope().getScope*() instanceof TestScope select im, - "Importing the value of '" + name + - "' from $@ means that any change made to $@ will be not be observed locally.", m, - "module " + m.getName(), store_attr, m.getName() + "." + store_attr.getName() + "Importing the value of '" + name + + "' from $@ means that any change made to $@ will be not be observed locally.", m, + "module " + m.getName(), store_attr, m.getName() + "." + store_attr.getName() diff --git a/python/ql/src/Imports/ImportShadowedByLoopVar.ql b/python/ql/src/Imports/ImportShadowedByLoopVar.ql index f3817a1bcde..035f1640d71 100644 --- a/python/ql/src/Imports/ImportShadowedByLoopVar.ql +++ b/python/ql/src/Imports/ImportShadowedByLoopVar.ql @@ -13,11 +13,11 @@ import python predicate shadowsImport(Variable l) { - exists(Import i, Name shadow | - shadow = i.getAName().getAsname() and - shadow.getId() = l.getId() and - i.getScope() = l.getScope().getScope*() - ) + exists(Import i, Name shadow | + shadow = i.getAName().getAsname() and + shadow.getId() = l.getId() and + i.getScope() = l.getScope().getScope*() + ) } from Variable l, Name defn diff --git a/python/ql/src/Imports/ImportandImportFrom.ql b/python/ql/src/Imports/ImportandImportFrom.ql index f04e6d896ba..e57cac8aed4 100644 --- a/python/ql/src/Imports/ImportandImportFrom.ql +++ b/python/ql/src/Imports/ImportandImportFrom.ql @@ -12,12 +12,12 @@ import python predicate import_and_import_from(Import i1, Import i2, Module m) { - i1.getEnclosingModule() = i2.getEnclosingModule() and - exists(ImportExpr e1, ImportExpr e2, ImportMember im | - e1 = i1.getAName().getValue() and im = i2.getAName().getValue() and e2 = im.getModule() - | - e1.getName() = m.getName() and e2.getName() = m.getName() - ) + i1.getEnclosingModule() = i2.getEnclosingModule() and + exists(ImportExpr e1, ImportExpr e2, ImportMember im | + e1 = i1.getAName().getValue() and im = i2.getAName().getValue() and e2 = im.getModule() + | + e1.getName() = m.getName() and e2.getName() = m.getName() + ) } from Stmt i1, Stmt i2, Module m diff --git a/python/ql/src/Imports/ModuleImportsItself.ql b/python/ql/src/Imports/ModuleImportsItself.ql index 3a4ad487687..c876853fff5 100644 --- a/python/ql/src/Imports/ModuleImportsItself.ql +++ b/python/ql/src/Imports/ModuleImportsItself.ql @@ -13,14 +13,14 @@ import python predicate modules_imports_itself(ImportingStmt i, ModuleValue m) { - i.getEnclosingModule() = m.getScope() and - m = - max(string s, ModuleValue m_ | - s = i.getAnImportedModuleName() and - m_.importedAs(s) - | - m_ order by s.length() - ) + i.getEnclosingModule() = m.getScope() and + m = + max(string s, ModuleValue m_ | + s = i.getAnImportedModuleName() and + m_.importedAs(s) + | + m_ order by s.length() + ) } from ImportingStmt i, ModuleValue m diff --git a/python/ql/src/Imports/ModuleLevelCyclicImport.ql b/python/ql/src/Imports/ModuleLevelCyclicImport.ql index 8cdb25c4ca5..7d9b0cc31c7 100644 --- a/python/ql/src/Imports/ModuleLevelCyclicImport.ql +++ b/python/ql/src/Imports/ModuleLevelCyclicImport.ql @@ -24,7 +24,7 @@ import Cyclic from ModuleValue m1, Stmt imp, ModuleValue m2, string attr, Expr use, ControlFlowNode defn where failing_import_due_to_cycle(m1, m2, imp, defn, use, attr) select use, - "'" + attr + "' may not be defined if module $@ is imported before module $@, as the $@ of " + - attr + " occurs after the cyclic $@ of " + m2.getName() + ".", - // Arguments for the placeholders in the above message: - m1, m1.getName(), m2, m2.getName(), defn, "definition", imp, "import" + "'" + attr + "' may not be defined if module $@ is imported before module $@, as the $@ of " + + attr + " occurs after the cyclic $@ of " + m2.getName() + ".", + // Arguments for the placeholders in the above message: + m1, m1.getName(), m2, m2.getName(), defn, "definition", imp, "import" diff --git a/python/ql/src/Imports/MultipleImports.ql b/python/ql/src/Imports/MultipleImports.ql index 09638457423..fdff082e0c5 100644 --- a/python/ql/src/Imports/MultipleImports.ql +++ b/python/ql/src/Imports/MultipleImports.ql @@ -15,32 +15,32 @@ import python predicate is_simple_import(Import imp) { not exists(Attribute a | imp.contains(a)) } predicate double_import(Import original, Import duplicate, Module m) { - original != duplicate and - is_simple_import(original) and - is_simple_import(duplicate) and - /* Imports import the same thing */ - exists(ImportExpr e1, ImportExpr e2 | - e1.getName() = m.getName() and - e2.getName() = m.getName() and - e1 = original.getAName().getValue() and - e2 = duplicate.getAName().getValue() - ) and - original.getAName().getAsname().(Name).getId() = duplicate.getAName().getAsname().(Name).getId() and - exists(Module enclosing | - original.getScope() = enclosing and - duplicate.getEnclosingModule() = enclosing and - ( - /* Duplicate is not at top level scope */ - duplicate.getScope() != enclosing - or - /* Original dominates duplicate */ - original.getAnEntryNode().dominates(duplicate.getAnEntryNode()) - ) + original != duplicate and + is_simple_import(original) and + is_simple_import(duplicate) and + /* Imports import the same thing */ + exists(ImportExpr e1, ImportExpr e2 | + e1.getName() = m.getName() and + e2.getName() = m.getName() and + e1 = original.getAName().getValue() and + e2 = duplicate.getAName().getValue() + ) and + original.getAName().getAsname().(Name).getId() = duplicate.getAName().getAsname().(Name).getId() and + exists(Module enclosing | + original.getScope() = enclosing and + duplicate.getEnclosingModule() = enclosing and + ( + /* Duplicate is not at top level scope */ + duplicate.getScope() != enclosing + or + /* Original dominates duplicate */ + original.getAnEntryNode().dominates(duplicate.getAnEntryNode()) ) + ) } from Import original, Import duplicate, Module m where double_import(original, duplicate, m) select duplicate, - "This import of module " + m.getName() + " is redundant, as it was previously imported $@.", - original, "on line " + original.getLocation().getStartLine().toString() + "This import of module " + m.getName() + " is redundant, as it was previously imported $@.", + original, "on line " + original.getLocation().getStartLine().toString() diff --git a/python/ql/src/Imports/UnintentionalImport.ql b/python/ql/src/Imports/UnintentionalImport.ql index 47ae2c999a5..dfd751fd527 100644 --- a/python/ql/src/Imports/UnintentionalImport.ql +++ b/python/ql/src/Imports/UnintentionalImport.ql @@ -14,19 +14,19 @@ import python predicate import_star(ImportStar imp, ModuleValue exporter) { - exporter.importedAs(imp.getImportedModuleName()) + exporter.importedAs(imp.getImportedModuleName()) } predicate all_defined(ModuleValue exporter) { - exporter.isBuiltin() - or - exporter.getScope().(ImportTimeScope).definesName("__all__") - or - exporter.getScope().getInitModule().(ImportTimeScope).definesName("__all__") + exporter.isBuiltin() + or + exporter.getScope().(ImportTimeScope).definesName("__all__") + or + exporter.getScope().getInitModule().(ImportTimeScope).definesName("__all__") } from ImportStar imp, ModuleValue exporter where import_star(imp, exporter) and not all_defined(exporter) select imp, - "Import pollutes the enclosing namespace, as the imported module $@ does not define '__all__'.", - exporter, exporter.getName() + "Import pollutes the enclosing namespace, as the imported module $@ does not define '__all__'.", + exporter, exporter.getName() diff --git a/python/ql/src/Imports/UnusedImport.ql b/python/ql/src/Imports/UnusedImport.ql index b8e4903b743..e9c2dbe839d 100644 --- a/python/ql/src/Imports/UnusedImport.ql +++ b/python/ql/src/Imports/UnusedImport.ql @@ -14,112 +14,112 @@ import python import Variables.Definition predicate global_name_used(Module m, string name) { - exists(Name u, GlobalVariable v | - u.uses(v) and - v.getId() = name and - u.getEnclosingModule() = m - ) - or - // A use of an undefined class local variable, will use the global variable - exists(Name u, LocalVariable v | - u.uses(v) and - v.getId() = name and - u.getEnclosingModule() = m and - not v.getScope().getEnclosingScope*() instanceof Function - ) + exists(Name u, GlobalVariable v | + u.uses(v) and + v.getId() = name and + u.getEnclosingModule() = m + ) + or + // A use of an undefined class local variable, will use the global variable + exists(Name u, LocalVariable v | + u.uses(v) and + v.getId() = name and + u.getEnclosingModule() = m and + not v.getScope().getEnclosingScope*() instanceof Function + ) } /** Holds if a module has `__all__` but we don't understand it */ predicate all_not_understood(Module m) { - exists(GlobalVariable a | a.getId() = "__all__" and a.getScope() = m | - // `__all__` is not defined as a simple list - not m.declaredInAll(_) - or - // `__all__` is modified - exists(Call c | c.getFunc().(Attribute).getObject() = a.getALoad()) - ) + exists(GlobalVariable a | a.getId() = "__all__" and a.getScope() = m | + // `__all__` is not defined as a simple list + not m.declaredInAll(_) + or + // `__all__` is modified + exists(Call c | c.getFunc().(Attribute).getObject() = a.getALoad()) + ) } predicate imported_module_used_in_doctest(Import imp) { - exists(string modname, string docstring | - imp.getAName().getAsname().(Name).getId() = modname and - // Look for doctests containing the patterns: - // >>> …name… - // ... …name… - docstring = doctest_in_scope(imp.getScope()) and - docstring.regexpMatch("[\\s\\S]*(>>>|\\.\\.\\.).*" + modname + "[\\s\\S]*") - ) + exists(string modname, string docstring | + imp.getAName().getAsname().(Name).getId() = modname and + // Look for doctests containing the patterns: + // >>> …name… + // ... …name… + docstring = doctest_in_scope(imp.getScope()) and + docstring.regexpMatch("[\\s\\S]*(>>>|\\.\\.\\.).*" + modname + "[\\s\\S]*") + ) } pragma[noinline] private string doctest_in_scope(Scope scope) { - exists(StrConst doc | - doc.getEnclosingModule() = scope and - doc.isDocString() and - result = doc.getText() and - result.regexpMatch("[\\s\\S]*(>>>|\\.\\.\\.)[\\s\\S]*") - ) + exists(StrConst doc | + doc.getEnclosingModule() = scope and + doc.isDocString() and + result = doc.getText() and + result.regexpMatch("[\\s\\S]*(>>>|\\.\\.\\.)[\\s\\S]*") + ) } pragma[noinline] private string typehint_annotation_in_module(Module module_scope) { - exists(StrConst annotation | - annotation = any(Arguments a).getAnAnnotation().getASubExpression*() - or - annotation = any(AnnAssign a).getAnnotation().getASubExpression*() - or - annotation = any(FunctionExpr f).getReturns().getASubExpression*() - | - annotation.pointsTo(Value::forString(result)) and - annotation.getEnclosingModule() = module_scope - ) + exists(StrConst annotation | + annotation = any(Arguments a).getAnAnnotation().getASubExpression*() + or + annotation = any(AnnAssign a).getAnnotation().getASubExpression*() + or + annotation = any(FunctionExpr f).getReturns().getASubExpression*() + | + annotation.pointsTo(Value::forString(result)) and + annotation.getEnclosingModule() = module_scope + ) } pragma[noinline] private string typehint_comment_in_file(File file) { - exists(Comment typehint | - file = typehint.getLocation().getFile() and - result = typehint.getText() and - result.matches("# type:%") - ) + exists(Comment typehint | + file = typehint.getLocation().getFile() and + result = typehint.getText() and + result.matches("# type:%") + ) } /** Holds if the imported alias `name` from `imp` is used in a typehint (in the same file as `imp`) */ predicate imported_alias_used_in_typehint(Import imp, Variable name) { - imp.getAName().getAsname().(Name).getVariable() = name and - exists(File file, Module module_scope | - module_scope = imp.getEnclosingModule() and - file = module_scope.getFile() - | - // Look for type hints containing the patterns: - // # type: …name… - typehint_comment_in_file(file).regexpMatch("# type:.*" + name.getId() + ".*") - or - // Type hint is inside a string annotation, as needed for forward references - typehint_annotation_in_module(module_scope).regexpMatch(".*\\b" + name.getId() + "\\b.*") - ) + imp.getAName().getAsname().(Name).getVariable() = name and + exists(File file, Module module_scope | + module_scope = imp.getEnclosingModule() and + file = module_scope.getFile() + | + // Look for type hints containing the patterns: + // # type: …name… + typehint_comment_in_file(file).regexpMatch("# type:.*" + name.getId() + ".*") + or + // Type hint is inside a string annotation, as needed for forward references + typehint_annotation_in_module(module_scope).regexpMatch(".*\\b" + name.getId() + "\\b.*") + ) } predicate unused_import(Import imp, Variable name) { - imp.getAName().getAsname().(Name).getVariable() = name and - not imp.getAnImportedModuleName() = "__future__" and - not imp.getEnclosingModule().declaredInAll(name.getId()) and - imp.getScope() = imp.getEnclosingModule() and - not global_name_used(imp.getScope(), name.getId()) and - // Imports in `__init__.py` are used to force module loading - not imp.getEnclosingModule().isPackageInit() and - // Name may be imported for use in epytext documentation - not exists(Comment cmt | cmt.getText().matches("%L{" + name.getId() + "}%") | - cmt.getLocation().getFile() = imp.getLocation().getFile() - ) and - not name_acceptable_for_unused_variable(name) and - // Assume that opaque `__all__` includes imported module - not all_not_understood(imp.getEnclosingModule()) and - not imported_module_used_in_doctest(imp) and - not imported_alias_used_in_typehint(imp, name) and - // Only consider import statements that actually point-to something (possibly an unknown module). - // If this is not the case, it's likely that the import statement never gets executed. - imp.getAName().getValue().pointsTo(_) + imp.getAName().getAsname().(Name).getVariable() = name and + not imp.getAnImportedModuleName() = "__future__" and + not imp.getEnclosingModule().declaredInAll(name.getId()) and + imp.getScope() = imp.getEnclosingModule() and + not global_name_used(imp.getScope(), name.getId()) and + // Imports in `__init__.py` are used to force module loading + not imp.getEnclosingModule().isPackageInit() and + // Name may be imported for use in epytext documentation + not exists(Comment cmt | cmt.getText().matches("%L{" + name.getId() + "}%") | + cmt.getLocation().getFile() = imp.getLocation().getFile() + ) and + not name_acceptable_for_unused_variable(name) and + // Assume that opaque `__all__` includes imported module + not all_not_understood(imp.getEnclosingModule()) and + not imported_module_used_in_doctest(imp) and + not imported_alias_used_in_typehint(imp, name) and + // Only consider import statements that actually point-to something (possibly an unknown module). + // If this is not the case, it's likely that the import statement never gets executed. + imp.getAName().getValue().pointsTo(_) } from Stmt s, Variable name diff --git a/python/ql/src/Lexical/CommentedOutCode.qll b/python/ql/src/Lexical/CommentedOutCode.qll index f352bcfac17..97315321a79 100644 --- a/python/ql/src/Lexical/CommentedOutCode.qll +++ b/python/ql/src/Lexical/CommentedOutCode.qll @@ -1,234 +1,234 @@ import python private predicate def_statement(Comment c) { - c.getText().regexpMatch("#(\\S*\\s+)?def\\s.*\\(.*\\).*:\\s*(#.*)?") + c.getText().regexpMatch("#(\\S*\\s+)?def\\s.*\\(.*\\).*:\\s*(#.*)?") } private predicate if_statement(Comment c) { - c.getText().regexpMatch("#(\\S*\\s+)?(el)?if\\s.*:\\s*(#.*)?") - or - c.getText().regexpMatch("#(\\S*\\s+)?else:\\s*(#.*)?") + c.getText().regexpMatch("#(\\S*\\s+)?(el)?if\\s.*:\\s*(#.*)?") + or + c.getText().regexpMatch("#(\\S*\\s+)?else:\\s*(#.*)?") } private predicate for_statement(Comment c) { - c.getText().regexpMatch("#(\\S*\\s+)?for\\s.*\\sin\\s.*:\\s*(#.*)?") + c.getText().regexpMatch("#(\\S*\\s+)?for\\s.*\\sin\\s.*:\\s*(#.*)?") } private predicate with_statement(Comment c) { - c.getText().regexpMatch("#(\\S*\\s+)?with\\s+.*:\\s*(#.*)?") + c.getText().regexpMatch("#(\\S*\\s+)?with\\s+.*:\\s*(#.*)?") } private predicate try_statement(Comment c) { - c.getText().regexpMatch("#(\\S*\\s+)?try:\\s*(#.*)?") - or - c.getText().regexpMatch("#(\\S*\\s+)?except\\s*(\\w+\\s*(\\sas\\s+\\w+\\s*)?)?:\\s*(#.*)?") - or - c.getText().regexpMatch("#(\\S*\\s+)?finally:\\s*(#.*)?") + c.getText().regexpMatch("#(\\S*\\s+)?try:\\s*(#.*)?") + or + c.getText().regexpMatch("#(\\S*\\s+)?except\\s*(\\w+\\s*(\\sas\\s+\\w+\\s*)?)?:\\s*(#.*)?") + or + c.getText().regexpMatch("#(\\S*\\s+)?finally:\\s*(#.*)?") } private int indentation(Comment c) { - exists(int offset | - maybe_code(c) and - exists(c.getText().regexpFind("[^\\s#]", 1, offset)) and - result = offset + c.getLocation().getStartColumn() - ) + exists(int offset | + maybe_code(c) and + exists(c.getText().regexpFind("[^\\s#]", 1, offset)) and + result = offset + c.getLocation().getStartColumn() + ) } private predicate class_statement(Comment c) { - c.getText().regexpMatch("#(\\S*\\s+)?class\\s+\\w+.*:\\s*(#.*)?") + c.getText().regexpMatch("#(\\S*\\s+)?class\\s+\\w+.*:\\s*(#.*)?") } private predicate triple_quote(Comment c) { c.getText().regexpMatch("#.*(\"\"\"|''').*") } private predicate triple_quoted_string_part(Comment start, Comment end) { - triple_quote(start) and end = start - or - exists(Comment mid | - triple_quoted_string_part(start, mid) and - end = non_empty_following(mid) and - not triple_quote(end) - ) + triple_quote(start) and end = start + or + exists(Comment mid | + triple_quoted_string_part(start, mid) and + end = non_empty_following(mid) and + not triple_quote(end) + ) } private predicate maybe_code(Comment c) { - not non_code(c) and not filler(c) and not endline_comment(c) and not file_or_url(c) - or - commented_out_comment(c) + not non_code(c) and not filler(c) and not endline_comment(c) and not file_or_url(c) + or + commented_out_comment(c) } private predicate commented_out_comment(Comment c) { c.getText().regexpMatch("#+\\s+#.*") } private int scope_start(Comment start) { - ( - def_statement(start) or - class_statement(start) - ) and - result = indentation(start) and - not non_code(start) + ( + def_statement(start) or + class_statement(start) + ) and + result = indentation(start) and + not non_code(start) } private int block_start(Comment start) { - ( - if_statement(start) or - for_statement(start) or - try_statement(start) or - with_statement(start) - ) and - result = indentation(start) and - not non_code(start) + ( + if_statement(start) or + for_statement(start) or + try_statement(start) or + with_statement(start) + ) and + result = indentation(start) and + not non_code(start) } private int scope_doc_string_part(Comment start, Comment end) { - result = scope_start(start) and - triple_quote(end) and - end = non_empty_following(start) - or - exists(Comment mid | - result = scope_doc_string_part(start, mid) and - end = non_empty_following(mid) - | - not triple_quote(end) - ) + result = scope_start(start) and + triple_quote(end) and + end = non_empty_following(start) + or + exists(Comment mid | + result = scope_doc_string_part(start, mid) and + end = non_empty_following(mid) + | + not triple_quote(end) + ) } private int scope_part(Comment start, Comment end) { - result = scope_start(start) and end = start - or - exists(Comment mid | - result = scope_doc_string_part(start, mid) and - end = non_empty_following(mid) and - triple_quote(end) - ) - or - exists(Comment mid | - result = scope_part(start, mid) and - end = non_empty_following(mid) - | - indentation(end) > result - ) + result = scope_start(start) and end = start + or + exists(Comment mid | + result = scope_doc_string_part(start, mid) and + end = non_empty_following(mid) and + triple_quote(end) + ) + or + exists(Comment mid | + result = scope_part(start, mid) and + end = non_empty_following(mid) + | + indentation(end) > result + ) } private int block_part(Comment start, Comment end) { - result = block_start(start) and - end = non_empty_following(start) and + result = block_start(start) and + end = non_empty_following(start) and + indentation(end) > result + or + exists(Comment mid | + result = block_part(start, mid) and + end = non_empty_following(mid) + | indentation(end) > result or - exists(Comment mid | - result = block_part(start, mid) and - end = non_empty_following(mid) - | - indentation(end) > result - or - result = block_start(end) - ) + result = block_start(end) + ) } private predicate commented_out_scope_part(Comment start, Comment end) { - exists(scope_doc_string_part(start, end)) - or - exists(scope_part(start, end)) + exists(scope_doc_string_part(start, end)) + or + exists(scope_part(start, end)) } private predicate commented_out_code(Comment c) { - commented_out_scope_part(c, _) - or - commented_out_scope_part(_, c) - or - exists(block_part(c, _)) - or - exists(block_part(_, c)) + commented_out_scope_part(c, _) + or + commented_out_scope_part(_, c) + or + exists(block_part(c, _)) + or + exists(block_part(_, c)) } private predicate commented_out_code_part(Comment start, Comment end) { - commented_out_code(start) and - end = start and - not exists(Comment prev | non_empty_following(prev) = start | commented_out_code(prev)) - or - exists(Comment mid | - commented_out_code_part(start, mid) and - non_empty_following(mid) = end and - commented_out_code(end) - ) + commented_out_code(start) and + end = start and + not exists(Comment prev | non_empty_following(prev) = start | commented_out_code(prev)) + or + exists(Comment mid | + commented_out_code_part(start, mid) and + non_empty_following(mid) = end and + commented_out_code(end) + ) } private predicate commented_out_code_block(Comment start, Comment end) { - /* A block must be at least 2 comments long. */ - start != end and - commented_out_code_part(start, end) and - not commented_out_code(non_empty_following(end)) + /* A block must be at least 2 comments long. */ + start != end and + commented_out_code_part(start, end) and + not commented_out_code(non_empty_following(end)) } /* A single line comment that appears to be commented out code */ class CommentedOutCodeLine extends Comment { - CommentedOutCodeLine() { exists(CommentedOutCodeBlock b | b.contains(this)) } + CommentedOutCodeLine() { exists(CommentedOutCodeBlock b | b.contains(this)) } - /* Whether this commented-out code line is likely to be example code embedded in a larger comment. */ - predicate maybeExampleCode() { - exists(CommentedOutCodeBlock block | - block.contains(this) and - block.maybeExampleCode() - ) - } + /* Whether this commented-out code line is likely to be example code embedded in a larger comment. */ + predicate maybeExampleCode() { + exists(CommentedOutCodeBlock block | + block.contains(this) and + block.maybeExampleCode() + ) + } } /** A block of comments that appears to be commented out code */ class CommentedOutCodeBlock extends @py_comment { - CommentedOutCodeBlock() { commented_out_code_block(this, _) } + CommentedOutCodeBlock() { commented_out_code_block(this, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "Commented out code" } + /** Gets a textual representation of this element. */ + string toString() { result = "Commented out code" } - /** Whether this commented-out code block contains the comment c */ - predicate contains(Comment c) { - this = c - or - exists(Comment prev | - non_empty_following(prev) = c and - not commented_out_code_block(this, prev) and - this.contains(prev) - ) - } + /** Whether this commented-out code block contains the comment c */ + predicate contains(Comment c) { + this = c + or + exists(Comment prev | + non_empty_following(prev) = c and + not commented_out_code_block(this, prev) and + this.contains(prev) + ) + } - /** The length of this comment block (in comments) */ - int length() { result = count(Comment c | this.contains(c)) } + /** The length of this comment block (in comments) */ + int length() { result = count(Comment c | this.contains(c)) } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.(Comment).getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and - exists(Comment end | commented_out_code_block(this, end) | - end.getLocation().hasLocationInfo(_, _, _, endline, endcolumn) - ) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.(Comment).getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and + exists(Comment end | commented_out_code_block(this, end) | + end.getLocation().hasLocationInfo(_, _, _, endline, endcolumn) + ) + } - /** Whether this commented-out code block is likely to be example code embedded in a larger comment. */ - predicate maybeExampleCode() { - exists(CommentBlock block | block.contains(this.(Comment)) | - exists(int all_code | - all_code = sum(CommentedOutCodeBlock code | block.contains(code.(Comment)) | code.length()) and - /* This ratio may need fine tuning */ - block.length() > all_code * 2 - ) - ) - } + /** Whether this commented-out code block is likely to be example code embedded in a larger comment. */ + predicate maybeExampleCode() { + exists(CommentBlock block | block.contains(this.(Comment)) | + exists(int all_code | + all_code = sum(CommentedOutCodeBlock code | block.contains(code.(Comment)) | code.length()) and + /* This ratio may need fine tuning */ + block.length() > all_code * 2 + ) + ) + } } /** Does c contain the pair of words "s1 s2" with only whitespace between them */ private predicate word_pair(Comment c, string s1, string s2) { - exists(int i1, int i2, int o1, int o2 | - s1 = c.getText().regexpFind("\\w+", i1, o1) and - s2 = c.getText().regexpFind("\\w+", i2, o2) and - i2 = i1 + 1 and - c.getText().prefix(o1).regexpMatch("[^'\"]*") and - c.getText().substring(o1 + s1.length(), o2).regexpMatch("\\s+") - ) + exists(int i1, int i2, int o1, int o2 | + s1 = c.getText().regexpFind("\\w+", i1, o1) and + s2 = c.getText().regexpFind("\\w+", i2, o2) and + i2 = i1 + 1 and + c.getText().prefix(o1).regexpMatch("[^'\"]*") and + c.getText().substring(o1 + s1.length(), o2).regexpMatch("\\s+") + ) } /** @@ -241,99 +241,99 @@ private predicate word_pair(Comment c, string s1, string s2) { * "with spam" can only be code if the comment contains a colon. */ private predicate non_code(Comment c) { - exists(string word1, string word2 | - word_pair(c, word1, word2) and - not word2 = operator_keyword() - | - not word1 = a_keyword() - or - word1 = keyword_requiring_colon() and not c.getText().matches("%:%") - ) and - /* Except comments of the form: # (maybe code) # some comment */ - not c.getText().regexpMatch("#\\S+\\s.*#.*") + exists(string word1, string word2 | + word_pair(c, word1, word2) and + not word2 = operator_keyword() + | + not word1 = a_keyword() or - /* Don't count doctests as code */ - c.getText().matches("%>>>%") - or - c.getText().matches("%...%") + word1 = keyword_requiring_colon() and not c.getText().matches("%:%") + ) and + /* Except comments of the form: # (maybe code) # some comment */ + not c.getText().regexpMatch("#\\S+\\s.*#.*") + or + /* Don't count doctests as code */ + c.getText().matches("%>>>%") + or + c.getText().matches("%...%") } private predicate filler(Comment c) { c.getText().regexpMatch("#+[\\s*#-_=+]*") } /** Gets the first non empty comment following c */ private Comment non_empty_following(Comment c) { - not empty(result) and - ( - result = empty_following(c).getFollowing() - or - not empty(c) and result = c.getFollowing() - ) + not empty(result) and + ( + result = empty_following(c).getFollowing() + or + not empty(c) and result = c.getFollowing() + ) } /* Helper for non_empty_following() */ private Comment empty_following(Comment c) { - not empty(c) and - empty(result) and - exists(Comment prev | result = prev.getFollowing() | - prev = c - or - prev = empty_following(c) - ) + not empty(c) and + empty(result) and + exists(Comment prev | result = prev.getFollowing() | + prev = c + or + prev = empty_following(c) + ) } private predicate empty(Comment c) { c.getText().regexpMatch("#+\\s*") } /* A comment following code on the same line */ private predicate endline_comment(Comment c) { - exists(Expr e, string f, int line | - e.getLocation().hasLocationInfo(f, line, _, _, _) and - c.getLocation().hasLocationInfo(f, line, _, _, _) - ) + exists(Expr e, string f, int line | + e.getLocation().hasLocationInfo(f, line, _, _, _) and + c.getLocation().hasLocationInfo(f, line, _, _, _) + ) } private predicate file_or_url(Comment c) { - c.getText().regexpMatch("#[^'\"]+(https?|file)://.*") or - c.getText().regexpMatch("#[^'\"]+(/[a-zA-Z]\\w*)+\\.[a-zA-Z]+.*") or - c.getText().regexpMatch("#[^'\"]+(\\[a-zA-Z]\\w*)+\\.[a-zA-Z]+.*") + c.getText().regexpMatch("#[^'\"]+(https?|file)://.*") or + c.getText().regexpMatch("#[^'\"]+(/[a-zA-Z]\\w*)+\\.[a-zA-Z]+.*") or + c.getText().regexpMatch("#[^'\"]+(\\[a-zA-Z]\\w*)+\\.[a-zA-Z]+.*") } private string operator_keyword() { - result = "import" or - result = "and" or - result = "is" or - result = "or" or - result = "in" or - result = "not" or - result = "as" + result = "import" or + result = "and" or + result = "is" or + result = "or" or + result = "in" or + result = "not" or + result = "as" } private string keyword_requiring_colon() { - result = "try" or - result = "while" or - result = "elif" or - result = "else" or - result = "if" or - result = "except" or - result = "def" or - result = "class" + result = "try" or + result = "while" or + result = "elif" or + result = "else" or + result = "if" or + result = "except" or + result = "def" or + result = "class" } private string other_keyword() { - result = "del" or - result = "lambda" or - result = "from" or - result = "global" or - result = "with" or - result = "assert" or - result = "yield" or - result = "finally" or - result = "print" or - result = "exec" or - result = "raise" or - result = "return" or - result = "for" + result = "del" or + result = "lambda" or + result = "from" or + result = "global" or + result = "with" or + result = "assert" or + result = "yield" or + result = "finally" or + result = "print" or + result = "exec" or + result = "raise" or + result = "return" or + result = "for" } private string a_keyword() { - result = keyword_requiring_colon() or result = other_keyword() or result = operator_keyword() + result = keyword_requiring_colon() or result = other_keyword() or result = operator_keyword() } diff --git a/python/ql/src/Lexical/OldOctalLiteral.ql b/python/ql/src/Lexical/OldOctalLiteral.ql index 28791d8903d..81b66375ff8 100644 --- a/python/ql/src/Lexical/OldOctalLiteral.ql +++ b/python/ql/src/Lexical/OldOctalLiteral.ql @@ -12,17 +12,17 @@ import python predicate is_old_octal(IntegerLiteral i) { - exists(string text | text = i.getText() | - text.charAt(0) = "0" and - not text = "00" and - exists(text.charAt(1).toInt()) and - /* Do not flag file permission masks */ - exists(int len | len = text.length() | - len != 4 and - len != 5 and - len != 7 - ) + exists(string text | text = i.getText() | + text.charAt(0) = "0" and + not text = "00" and + exists(text.charAt(1).toInt()) and + /* Do not flag file permission masks */ + exists(int len | len = text.length() | + len != 4 and + len != 5 and + len != 7 ) + ) } from IntegerLiteral i diff --git a/python/ql/src/Metrics/CommentRatio.ql b/python/ql/src/Metrics/CommentRatio.ql index 76a185321ac..8ebb27cf304 100644 --- a/python/ql/src/Metrics/CommentRatio.ql +++ b/python/ql/src/Metrics/CommentRatio.ql @@ -16,4 +16,4 @@ import python from Module m, ModuleMetrics mm where mm = m.getMetrics() and mm.getNumberOfLines() > 0 select m, 100.0 * (mm.getNumberOfLinesOfComments().(float) / mm.getNumberOfLines().(float)) as ratio - order by ratio desc + order by ratio desc diff --git a/python/ql/src/Metrics/Dependencies/ExternalDependencies.ql b/python/ql/src/Metrics/Dependencies/ExternalDependencies.ql index b2c319070ea..6eaf7422b18 100644 --- a/python/ql/src/Metrics/Dependencies/ExternalDependencies.ql +++ b/python/ql/src/Metrics/Dependencies/ExternalDependencies.ql @@ -30,15 +30,15 @@ import semmle.python.dependencies.TechInventory */ predicate src_package_count(File sourceFile, ExternalPackage package, int total) { - total = - strictcount(AstNode src | - dependency(src, package) and - src.getLocation().getFile() = sourceFile - ) + total = + strictcount(AstNode src | + dependency(src, package) and + src.getLocation().getFile() = sourceFile + ) } from File sourceFile, int total, string entity, ExternalPackage package where - src_package_count(sourceFile, package, total) and - entity = munge(sourceFile, package) + src_package_count(sourceFile, package, total) and + entity = munge(sourceFile, package) select entity, total order by total desc diff --git a/python/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql b/python/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql index 2424d82abeb..c752ec8bc5e 100644 --- a/python/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql +++ b/python/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql @@ -19,9 +19,9 @@ import semmle.python.dependencies.TechInventory from File sourceFile, string entity where - exists(PackageObject package, AstNode src | - dependency(src, package) and - src.getLocation().getFile() = sourceFile and - entity = munge(sourceFile, package) - ) + exists(PackageObject package, AstNode src | + dependency(src, package) and + src.getLocation().getFile() = sourceFile and + entity = munge(sourceFile, package) + ) select entity, sourceFile diff --git a/python/ql/src/Metrics/DocStringRatio.ql b/python/ql/src/Metrics/DocStringRatio.ql index 46859560c16..a8cd8b8dc4e 100644 --- a/python/ql/src/Metrics/DocStringRatio.ql +++ b/python/ql/src/Metrics/DocStringRatio.ql @@ -15,5 +15,5 @@ import python from Module m, ModuleMetrics mm where mm = m.getMetrics() and mm.getNumberOfLines() > 0 select m, - 100.0 * (mm.getNumberOfLinesOfDocStrings().(float) / mm.getNumberOfLines().(float)) as ratio - order by ratio desc + 100.0 * (mm.getNumberOfLinesOfDocStrings().(float) / mm.getNumberOfLines().(float)) as ratio + order by ratio desc diff --git a/python/ql/src/Metrics/FLinesOfComments.ql b/python/ql/src/Metrics/FLinesOfComments.ql index bd52f8d5caa..b426a0b25f3 100644 --- a/python/ql/src/Metrics/FLinesOfComments.ql +++ b/python/ql/src/Metrics/FLinesOfComments.ql @@ -14,5 +14,5 @@ import python from Module m, int n where - n = m.getMetrics().getNumberOfLinesOfComments() + m.getMetrics().getNumberOfLinesOfDocStrings() + n = m.getMetrics().getNumberOfLinesOfComments() + m.getMetrics().getNumberOfLinesOfDocStrings() select m, n order by n desc diff --git a/python/ql/src/Metrics/FLinesOfDuplicatedCode.ql b/python/ql/src/Metrics/FLinesOfDuplicatedCode.ql index 28673a258c1..36602310dd5 100644 --- a/python/ql/src/Metrics/FLinesOfDuplicatedCode.ql +++ b/python/ql/src/Metrics/FLinesOfDuplicatedCode.ql @@ -16,11 +16,11 @@ import external.CodeDuplication from File f, int n where - n = - count(int line | - exists(DuplicateBlock d | d.sourceFile() = f | - line in [d.sourceStartLine() .. d.sourceEndLine()] and - not allowlistedLineForDuplication(f, line) - ) - ) + n = + count(int line | + exists(DuplicateBlock d | d.sourceFile() = f | + line in [d.sourceStartLine() .. d.sourceEndLine()] and + not allowlistedLineForDuplication(f, line) + ) + ) select f, n order by n desc diff --git a/python/ql/src/Metrics/FLinesOfSimilarCode.ql b/python/ql/src/Metrics/FLinesOfSimilarCode.ql index 169c4c8f1b5..b9eb3ddfaa1 100644 --- a/python/ql/src/Metrics/FLinesOfSimilarCode.ql +++ b/python/ql/src/Metrics/FLinesOfSimilarCode.ql @@ -16,11 +16,11 @@ import external.CodeDuplication from File f, int n where - n = - count(int line | - exists(SimilarBlock d | d.sourceFile() = f | - line in [d.sourceStartLine() .. d.sourceEndLine()] and - not allowlistedLineForDuplication(f, line) - ) - ) + n = + count(int line | + exists(SimilarBlock d | d.sourceFile() = f | + line in [d.sourceStartLine() .. d.sourceEndLine()] and + not allowlistedLineForDuplication(f, line) + ) + ) select f, n order by n desc diff --git a/python/ql/src/Metrics/History/HChurn.ql b/python/ql/src/Metrics/History/HChurn.ql index e18b8dd528a..d6a8722e6e2 100644 --- a/python/ql/src/Metrics/History/HChurn.ql +++ b/python/ql/src/Metrics/History/HChurn.ql @@ -13,11 +13,11 @@ import external.VCS from Module m, int n where - n = - sum(Commit entry, int churn | - churn = entry.getRecentChurnForFile(m.getFile()) and not artificialChange(entry) - | - churn - ) and - exists(m.getMetrics().getNumberOfLinesOfCode()) + n = + sum(Commit entry, int churn | + churn = entry.getRecentChurnForFile(m.getFile()) and not artificialChange(entry) + | + churn + ) and + exists(m.getMetrics().getNumberOfLinesOfCode()) select m, n order by n desc diff --git a/python/ql/src/Metrics/History/HLinesAdded.ql b/python/ql/src/Metrics/History/HLinesAdded.ql index 239d227f365..3ecec917ab1 100644 --- a/python/ql/src/Metrics/History/HLinesAdded.ql +++ b/python/ql/src/Metrics/History/HLinesAdded.ql @@ -13,11 +13,11 @@ import external.VCS from Module m, int n where - n = - sum(Commit entry, int churn | - churn = entry.getRecentAdditionsForFile(m.getFile()) and not artificialChange(entry) - | - churn - ) and - exists(m.getMetrics().getNumberOfLinesOfCode()) + n = + sum(Commit entry, int churn | + churn = entry.getRecentAdditionsForFile(m.getFile()) and not artificialChange(entry) + | + churn + ) and + exists(m.getMetrics().getNumberOfLinesOfCode()) select m, n order by n desc diff --git a/python/ql/src/Metrics/History/HLinesDeleted.ql b/python/ql/src/Metrics/History/HLinesDeleted.ql index 7f02c17cc2c..ded74756d55 100644 --- a/python/ql/src/Metrics/History/HLinesDeleted.ql +++ b/python/ql/src/Metrics/History/HLinesDeleted.ql @@ -13,11 +13,11 @@ import external.VCS from Module m, int n where - n = - sum(Commit entry, int churn | - churn = entry.getRecentDeletionsForFile(m.getFile()) and not artificialChange(entry) - | - churn - ) and - exists(m.getMetrics().getNumberOfLinesOfCode()) + n = + sum(Commit entry, int churn | + churn = entry.getRecentDeletionsForFile(m.getFile()) and not artificialChange(entry) + | + churn + ) and + exists(m.getMetrics().getNumberOfLinesOfCode()) select m, n order by n desc diff --git a/python/ql/src/Metrics/History/HNumberOfCoCommits.ql b/python/ql/src/Metrics/History/HNumberOfCoCommits.ql index 4f48641e394..afb62353a03 100644 --- a/python/ql/src/Metrics/History/HNumberOfCoCommits.ql +++ b/python/ql/src/Metrics/History/HNumberOfCoCommits.ql @@ -16,8 +16,8 @@ int committedFiles(Commit commit) { result = count(commit.getAnAffectedFile()) } from Module m where exists(m.getMetrics().getNumberOfLinesOfCode()) select m, - avg(Commit commit, int toAvg | - commit.getAnAffectedFile() = m.getFile() and toAvg = committedFiles(commit) - 1 - | - toAvg - ) + avg(Commit commit, int toAvg | + commit.getAnAffectedFile() = m.getFile() and toAvg = committedFiles(commit) - 1 + | + toAvg + ) diff --git a/python/ql/src/Metrics/History/HNumberOfReCommits.ql b/python/ql/src/Metrics/History/HNumberOfReCommits.ql index c1863e934c9..5df9194ee34 100644 --- a/python/ql/src/Metrics/History/HNumberOfReCommits.ql +++ b/python/ql/src/Metrics/History/HNumberOfReCommits.ql @@ -12,21 +12,21 @@ import python import external.VCS predicate inRange(Commit first, Commit second) { - first.getAnAffectedFile() = second.getAnAffectedFile() and - first != second and - exists(int n | - n = first.getDate().daysTo(second.getDate()) and - n >= 0 and - n < 5 - ) + first.getAnAffectedFile() = second.getAnAffectedFile() and + first != second and + exists(int n | + n = first.getDate().daysTo(second.getDate()) and + n >= 0 and + n < 5 + ) } int recommitsForFile(File f) { - result = - count(Commit recommit | - f = recommit.getAnAffectedFile() and - exists(Commit prev | inRange(prev, recommit)) - ) + result = + count(Commit recommit | + f = recommit.getAnAffectedFile() and + exists(Commit prev | inRange(prev, recommit)) + ) } from Module m diff --git a/python/ql/src/Metrics/History/HNumberOfRecentAuthors.ql b/python/ql/src/Metrics/History/HNumberOfRecentAuthors.ql index 75832cc82bd..e04baa491f4 100644 --- a/python/ql/src/Metrics/History/HNumberOfRecentAuthors.ql +++ b/python/ql/src/Metrics/History/HNumberOfRecentAuthors.ql @@ -14,11 +14,11 @@ import external.VCS from Module m where exists(m.getMetrics().getNumberOfLinesOfCode()) select m, - count(Author author | - exists(Commit e | - e = author.getACommit() and - m.getFile() = e.getAnAffectedFile() and - e.daysToNow() <= 180 and - not artificialChange(e) - ) + count(Author author | + exists(Commit e | + e = author.getACommit() and + m.getFile() = e.getAnAffectedFile() and + e.daysToNow() <= 180 and + not artificialChange(e) ) + ) diff --git a/python/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql b/python/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql index 9b90a73294f..f0d8473b302 100644 --- a/python/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql +++ b/python/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql @@ -13,8 +13,8 @@ import external.VCS from Module m where - exists(Commit e | - e.getAnAffectedFile() = m.getFile() and e.daysToNow() <= 180 and not artificialChange(e) - ) and - exists(m.getMetrics().getNumberOfLinesOfCode()) + exists(Commit e | + e.getAnAffectedFile() = m.getFile() and e.daysToNow() <= 180 and not artificialChange(e) + ) and + exists(m.getMetrics().getNumberOfLinesOfCode()) select m, 1 diff --git a/python/ql/src/Metrics/Internal/Extents.qll b/python/ql/src/Metrics/Internal/Extents.qll index 024d0e26f4c..1e38a4d544d 100644 --- a/python/ql/src/Metrics/Internal/Extents.qll +++ b/python/ql/src/Metrics/Internal/Extents.qll @@ -15,18 +15,19 @@ import python * including the body (if any), as opposed to the location of its name only. */ class RangeFunction extends Function { - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { super.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and - this.getBody().getLastItem().getLocation().hasLocationInfo(filepath, _, _, endline, endcolumn) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + super.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and + this.getBody().getLastItem().getLocation().hasLocationInfo(filepath, _, _, endline, endcolumn) + } } /** @@ -34,16 +35,17 @@ class RangeFunction extends Function { * including the body (if any), as opposed to the location of its name only. */ class RangeClass extends Class { - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { super.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and - this.getBody().getLastItem().getLocation().hasLocationInfo(filepath, _, _, endline, endcolumn) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + super.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and + this.getBody().getLastItem().getLocation().hasLocationInfo(filepath, _, _, endline, endcolumn) + } } diff --git a/python/ql/src/Numerics/Pythagorean.ql b/python/ql/src/Numerics/Pythagorean.ql index 6522da8a2b2..bda74405318 100644 --- a/python/ql/src/Numerics/Pythagorean.ql +++ b/python/ql/src/Numerics/Pythagorean.ql @@ -12,34 +12,34 @@ import python predicate squareOp(BinaryExpr e) { - e.getOp() instanceof Pow and e.getRight().(IntegerLiteral).getN() = "2" + e.getOp() instanceof Pow and e.getRight().(IntegerLiteral).getN() = "2" } predicate squareMul(BinaryExpr e) { - e.getOp() instanceof Mult and e.getRight().(Name).getId() = e.getLeft().(Name).getId() + e.getOp() instanceof Mult and e.getRight().(Name).getId() = e.getLeft().(Name).getId() } predicate squareRef(Name e) { - e.isUse() and - exists(SsaVariable v, Expr s | v.getVariable() = e.getVariable() | - s = v.getDefinition().getNode().getParentNode().(AssignStmt).getValue() and - square(s) - ) + e.isUse() and + exists(SsaVariable v, Expr s | v.getVariable() = e.getVariable() | + s = v.getDefinition().getNode().getParentNode().(AssignStmt).getValue() and + square(s) + ) } predicate square(Expr e) { - squareOp(e) - or - squareMul(e) - or - squareRef(e) + squareOp(e) + or + squareMul(e) + or + squareRef(e) } from Call c, BinaryExpr s where - c.getFunc().toString() = "sqrt" and - c.getArg(0) = s and - s.getOp() instanceof Add and - square(s.getLeft()) and - square(s.getRight()) + c.getFunc().toString() = "sqrt" and + c.getArg(0) = s and + s.getOp() instanceof Add and + square(s.getLeft()) and + square(s.getRight()) select c, "Pythagorean calculation with sub-optimal numerics" diff --git a/python/ql/src/Resources/FileNotAlwaysClosed.ql b/python/ql/src/Resources/FileNotAlwaysClosed.ql index 6a2e6201a63..5b5a869e62a 100755 --- a/python/ql/src/Resources/FileNotAlwaysClosed.ql +++ b/python/ql/src/Resources/FileNotAlwaysClosed.ql @@ -20,55 +20,55 @@ import FileOpen * either `__enter__` and `__exit__` or `__init__` and `__del__` */ predicate opened_in_enter_closed_in_exit(ControlFlowNode open) { - file_not_closed_at_scope_exit(open) and - exists(FunctionValue entry, FunctionValue exit | - open.getScope() = entry.getScope() and - exists(ClassValue cls | - cls.declaredAttribute("__enter__") = entry and cls.declaredAttribute("__exit__") = exit - or - cls.declaredAttribute("__init__") = entry and cls.declaredAttribute("__del__") = exit - ) and - exists(AttrNode attr_open, AttrNode attrclose | - attr_open.getScope() = entry.getScope() and - attrclose.getScope() = exit.getScope() and - expr_is_open(attr_open.(DefinitionNode).getValue(), open) and - attr_open.getName() = attrclose.getName() and - close_method_call(_, attrclose) - ) + file_not_closed_at_scope_exit(open) and + exists(FunctionValue entry, FunctionValue exit | + open.getScope() = entry.getScope() and + exists(ClassValue cls | + cls.declaredAttribute("__enter__") = entry and cls.declaredAttribute("__exit__") = exit + or + cls.declaredAttribute("__init__") = entry and cls.declaredAttribute("__del__") = exit + ) and + exists(AttrNode attr_open, AttrNode attrclose | + attr_open.getScope() = entry.getScope() and + attrclose.getScope() = exit.getScope() and + expr_is_open(attr_open.(DefinitionNode).getValue(), open) and + attr_open.getName() = attrclose.getName() and + close_method_call(_, attrclose) ) + ) } predicate file_not_closed_at_scope_exit(ControlFlowNode open) { - exists(EssaVariable v | - BaseFlow::reaches_exit(v) and - var_is_open(v, open) and - not file_is_returned(v, open) - ) - or - call_to_open(open) and - not exists(AssignmentDefinition def | def.getValue() = open) and - not exists(Return r | r.getValue() = open.getNode()) + exists(EssaVariable v | + BaseFlow::reaches_exit(v) and + var_is_open(v, open) and + not file_is_returned(v, open) + ) + or + call_to_open(open) and + not exists(AssignmentDefinition def | def.getValue() = open) and + not exists(Return r | r.getValue() = open.getNode()) } predicate file_not_closed_at_exception_exit(ControlFlowNode open, ControlFlowNode exit) { - exists(EssaVariable v | - exit.(RaisingNode).viableExceptionalExit(_, _) and - not closes_arg(exit, v.getSourceVariable()) and - not close_method_call(exit, v.getAUse().(NameNode)) and - var_is_open(v, open) and - v.getAUse() = exit.getAChild*() - ) + exists(EssaVariable v | + exit.(RaisingNode).viableExceptionalExit(_, _) and + not closes_arg(exit, v.getSourceVariable()) and + not close_method_call(exit, v.getAUse().(NameNode)) and + var_is_open(v, open) and + v.getAUse() = exit.getAChild*() + ) } /* Check to see if a file is opened but not closed or returned */ from ControlFlowNode defn, string message where - not opened_in_enter_closed_in_exit(defn) and - ( - file_not_closed_at_scope_exit(defn) and message = "File is opened but is not closed." - or - not file_not_closed_at_scope_exit(defn) and - file_not_closed_at_exception_exit(defn, _) and - message = "File may not be closed if an exception is raised." - ) + not opened_in_enter_closed_in_exit(defn) and + ( + file_not_closed_at_scope_exit(defn) and message = "File is opened but is not closed." + or + not file_not_closed_at_scope_exit(defn) and + file_not_closed_at_exception_exit(defn, _) and + message = "File may not be closed if an exception is raised." + ) select defn.getNode(), message diff --git a/python/ql/src/Resources/FileOpen.qll b/python/ql/src/Resources/FileOpen.qll index 5a7aae0595b..c2df0d6263a 100644 --- a/python/ql/src/Resources/FileOpen.qll +++ b/python/ql/src/Resources/FileOpen.qll @@ -6,155 +6,155 @@ import semmle.python.pointsto.Filters /** Holds if `open` is a call that returns a newly opened file */ predicate call_to_open(ControlFlowNode open) { - exists(FunctionValue f | - function_opens_file(f) and - f.getACall() = open - ) and - /* If in `with` statement, then it will be automatically closed. So just treat as not opened */ - not exists(With w | w.getContextExpr() = open.getNode()) + exists(FunctionValue f | + function_opens_file(f) and + f.getACall() = open + ) and + /* If in `with` statement, then it will be automatically closed. So just treat as not opened */ + not exists(With w | w.getContextExpr() = open.getNode()) } /** Holds if `n` refers to a file opened at `open` */ predicate expr_is_open(ControlFlowNode n, ControlFlowNode open) { - call_to_open(open) and open = n + call_to_open(open) and open = n + or + exists(EssaVariable v | + n instanceof NameNode and + var_is_open(v, open) + | + n = v.getAUse() or - exists(EssaVariable v | - n instanceof NameNode and - var_is_open(v, open) - | - n = v.getAUse() - or - wraps_file(n, v) - ) + wraps_file(n, v) + ) } /** Holds if `call` wraps the object referred to by `v` and returns it */ private predicate wraps_file(CallNode call, EssaVariable v) { - exists(ClassValue cls | - call = cls.getACall() and - call.getAnArg() = v.getAUse() - ) + exists(ClassValue cls | + call = cls.getACall() and + call.getAnArg() = v.getAUse() + ) } /** Holds if `var` refers to a file opened at `open` */ predicate var_is_open(EssaVariable v, ControlFlowNode open) { - def_is_open(v.getDefinition(), open) and - /* If use in context expression in `with` statement, then it will be automatically closed. */ - not exists(With w | w.getContextExpr() = v.getAUse().getNode()) + def_is_open(v.getDefinition(), open) and + /* If use in context expression in `with` statement, then it will be automatically closed. */ + not exists(With w | w.getContextExpr() = v.getAUse().getNode()) } /** Holds if `test` will pass through an open file in variable `v` for the `sense` successor */ predicate passes_open_files(Variable v, ControlFlowNode test, boolean sense) { - // `if fd.closed:` - exists(AttrNode closed | - closed = test and - closed.getObject("closed") = v.getAUse() - ) and - sense = false - or - // `if fd ==/is ...:` most commonly `if fd is None:` - equality_test(test, v.getAUse(), sense.booleanNot(), _) - or - // `if fd:` - test = v.getAUse() and sense = true - or - exists(UnaryExprNode n | - n = test and - n.getNode().getOp() instanceof Not - | - passes_open_files(v, n.getOperand(), sense.booleanNot()) - ) + // `if fd.closed:` + exists(AttrNode closed | + closed = test and + closed.getObject("closed") = v.getAUse() + ) and + sense = false + or + // `if fd ==/is ...:` most commonly `if fd is None:` + equality_test(test, v.getAUse(), sense.booleanNot(), _) + or + // `if fd:` + test = v.getAUse() and sense = true + or + exists(UnaryExprNode n | + n = test and + n.getNode().getOp() instanceof Not + | + passes_open_files(v, n.getOperand(), sense.booleanNot()) + ) } /* Helper for `def_is_open` to give better join order */ private predicate passes_open_files(PyEdgeRefinement refinement) { - passes_open_files(refinement.getSourceVariable(), refinement.getPredecessor().getLastNode(), - refinement.getSense()) + passes_open_files(refinement.getSourceVariable(), refinement.getPredecessor().getLastNode(), + refinement.getSense()) } /** Holds if `def` refers to a file opened at `open` */ predicate def_is_open(EssaDefinition def, ControlFlowNode open) { - expr_is_open(def.(AssignmentDefinition).getValue(), open) - or - exists(PyEdgeRefinement refinement | refinement = def | - var_is_open(refinement.getInput(), open) and - passes_open_files(refinement) - ) - or - exists(EssaNodeRefinement refinement | refinement = def | - not closes_file(def) and - not wraps_file(refinement.getDefiningNode(), refinement.getInput()) and - var_is_open(refinement.getInput(), open) - ) - or - var_is_open(def.(PhiFunction).getAnInput(), open) + expr_is_open(def.(AssignmentDefinition).getValue(), open) + or + exists(PyEdgeRefinement refinement | refinement = def | + var_is_open(refinement.getInput(), open) and + passes_open_files(refinement) + ) + or + exists(EssaNodeRefinement refinement | refinement = def | + not closes_file(def) and + not wraps_file(refinement.getDefiningNode(), refinement.getInput()) and + var_is_open(refinement.getInput(), open) + ) + or + var_is_open(def.(PhiFunction).getAnInput(), open) } /** Holds if `call` closes a file */ predicate closes_file(EssaNodeRefinement call) { - closes_arg(call.(ArgumentRefinement).getDefiningNode(), call.getSourceVariable()) or - close_method_call(call.(MethodCallsiteRefinement).getCall(), - call.getSourceVariable().(Variable).getAUse()) + closes_arg(call.(ArgumentRefinement).getDefiningNode(), call.getSourceVariable()) or + close_method_call(call.(MethodCallsiteRefinement).getCall(), + call.getSourceVariable().(Variable).getAUse()) } /** Holds if `call` closes its argument, which is an open file referred to by `v` */ predicate closes_arg(CallNode call, Variable v) { - call.getAnArg() = v.getAUse() and - ( - exists(FunctionValue close | call = close.getACall() and function_closes_file(close)) - or - call.getFunction().(NameNode).getId() = "close" - ) + call.getAnArg() = v.getAUse() and + ( + exists(FunctionValue close | call = close.getACall() and function_closes_file(close)) + or + call.getFunction().(NameNode).getId() = "close" + ) } /** Holds if `call` closes its 'self' argument, which is an open file referred to by `v` */ predicate close_method_call(CallNode call, ControlFlowNode self) { - call.getFunction().(AttrNode).getObject() = self and - exists(FunctionValue close | call = close.getACall() and function_closes_file(close)) - or - call.getFunction().(AttrNode).getObject("close") = self + call.getFunction().(AttrNode).getObject() = self and + exists(FunctionValue close | call = close.getACall() and function_closes_file(close)) + or + call.getFunction().(AttrNode).getObject("close") = self } /** Holds if `close` is a function that appears to close files that are passed to it as an argument. */ predicate function_closes_file(FunctionValue close) { - close = Value::named("os.close") - or - function_should_close_parameter(close.getScope()) + close = Value::named("os.close") + or + function_should_close_parameter(close.getScope()) } /** INTERNAL - Helper predicate for `function_closes_file` */ predicate function_should_close_parameter(Function func) { - exists(EssaDefinition def | - closes_file(def) and - def.getSourceVariable().(Variable).getScope() = func - ) + exists(EssaDefinition def | + closes_file(def) and + def.getSourceVariable().(Variable).getScope() = func + ) } /** Holds if the function `f` opens a file, either directly or indirectly. */ predicate function_opens_file(FunctionValue f) { - f = Value::named("open") - or - exists(EssaVariable v, Return ret | ret.getScope() = f.getScope() | - ret.getValue().getAFlowNode() = v.getAUse() and - var_is_open(v, _) - ) - or - exists(Return ret, FunctionValue callee | ret.getScope() = f.getScope() | - ret.getValue().getAFlowNode() = callee.getACall() and - function_opens_file(callee) - ) + f = Value::named("open") + or + exists(EssaVariable v, Return ret | ret.getScope() = f.getScope() | + ret.getValue().getAFlowNode() = v.getAUse() and + var_is_open(v, _) + ) + or + exists(Return ret, FunctionValue callee | ret.getScope() = f.getScope() | + ret.getValue().getAFlowNode() = callee.getACall() and + function_opens_file(callee) + ) } /** Holds if the variable `v` refers to a file opened at `open` which is subsequently returned from a function. */ predicate file_is_returned(EssaVariable v, ControlFlowNode open) { - exists(NameNode n, Return ret | - var_is_open(v, open) and - v.getAUse() = n - | - ret.getValue() = n.getNode() - or - ret.getValue().(Tuple).getAnElt() = n.getNode() - or - ret.getValue().(List).getAnElt() = n.getNode() - ) + exists(NameNode n, Return ret | + var_is_open(v, open) and + v.getAUse() = n + | + ret.getValue() = n.getNode() + or + ret.getValue().(Tuple).getAnElt() = n.getNode() + or + ret.getValue().(List).getAnElt() = n.getNode() + ) } diff --git a/python/ql/src/Security/CVE-2018-1281/BindToAllInterfaces.ql b/python/ql/src/Security/CVE-2018-1281/BindToAllInterfaces.ql index b787c9094d2..0c160111fba 100644 --- a/python/ql/src/Security/CVE-2018-1281/BindToAllInterfaces.ql +++ b/python/ql/src/Security/CVE-2018-1281/BindToAllInterfaces.ql @@ -15,24 +15,24 @@ import python Value aSocket() { result.getClass() = Value::named("socket.socket") } CallNode socketBindCall() { - result = aSocket().attr("bind").(CallableValue).getACall() and major_version() = 3 - or - result.getFunction().(AttrNode).getObject("bind").pointsTo(aSocket()) and - major_version() = 2 + result = aSocket().attr("bind").(CallableValue).getACall() and major_version() = 3 + or + result.getFunction().(AttrNode).getObject("bind").pointsTo(aSocket()) and + major_version() = 2 } string allInterfaces() { result = "0.0.0.0" or result = "" } Value getTextValue(string address) { - result = Value::forUnicode(address) and major_version() = 3 - or - result = Value::forString(address) and major_version() = 2 + result = Value::forUnicode(address) and major_version() = 3 + or + result = Value::forString(address) and major_version() = 2 } from CallNode call, TupleValue args, string address where - call = socketBindCall() and - call.getArg(0).pointsTo(args) and - args.getItem(0) = getTextValue(address) and - address = allInterfaces() + call = socketBindCall() and + call.getArg(0).pointsTo(args) and + args.getItem(0) = getTextValue(address) and + address = allInterfaces() select call.getNode(), "'" + address + "' binds a socket to all interfaces." diff --git a/python/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql b/python/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql index 3fb7046f8cc..cebaa4fdd2e 100644 --- a/python/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql +++ b/python/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql @@ -21,21 +21,21 @@ private string commonTopLevelDomainRegex() { result = "com|org|edu|gov|uk|net|io */ bindingset[pattern] predicate isIncompleteHostNameRegExpPattern(string pattern, string hostPart) { - hostPart = - pattern - .regexpCapture("(?i).*" + - // an unescaped single `.` - "(?:` clears taint on its `false` edge. */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - taint instanceof TarFileInfo and - clears_taint_on_false_edge(test.getTest(), test.getSense()) - } + /** The test `if :` clears taint on its `false` edge. */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + taint instanceof TarFileInfo and + clears_taint_on_false_edge(test.getTest(), test.getSense()) + } - private predicate clears_taint_on_false_edge(ControlFlowNode test, boolean sense) { - path_sanitizing_test(test) and - sense = false - or - // handle `not` (also nested) - test.(UnaryExprNode).getNode().getOp() instanceof Not and - clears_taint_on_false_edge(test.(UnaryExprNode).getOperand(), sense.booleanNot()) - } + private predicate clears_taint_on_false_edge(ControlFlowNode test, boolean sense) { + path_sanitizing_test(test) and + sense = false + or + // handle `not` (also nested) + test.(UnaryExprNode).getNode().getOp() instanceof Not and + clears_taint_on_false_edge(test.(UnaryExprNode).getOperand(), sense.booleanNot()) + } } private predicate path_sanitizing_test(ControlFlowNode test) { - /* Assume that any test with "path" in it is a sanitizer */ - test.getAChild+().(AttrNode).getName().matches("%path") - or - test.getAChild+().(NameNode).getId().matches("%path") + /* Assume that any test with "path" in it is a sanitizer */ + test.getAChild+().(AttrNode).getName().matches("%path") + or + test.getAChild+().(NameNode).getId().matches("%path") } class TarSlipConfiguration extends TaintTracking::Configuration { - TarSlipConfiguration() { this = "TarSlip configuration" } + TarSlipConfiguration() { this = "TarSlip configuration" } - override predicate isSource(TaintTracking::Source source) { source instanceof TarfileOpen } + override predicate isSource(TaintTracking::Source source) { source instanceof TarfileOpen } - override predicate isSink(TaintTracking::Sink sink) { - sink instanceof ExtractSink or - sink instanceof ExtractAllSink or - sink instanceof ExtractMembersSink - } + override predicate isSink(TaintTracking::Sink sink) { + sink instanceof ExtractSink or + sink instanceof ExtractAllSink or + sink instanceof ExtractMembersSink + } - override predicate isSanitizer(Sanitizer sanitizer) { - sanitizer instanceof TarFileInfoSanitizer - or - sanitizer instanceof ExcludeTarFilePy - } + override predicate isSanitizer(Sanitizer sanitizer) { + sanitizer instanceof TarFileInfoSanitizer + or + sanitizer instanceof ExcludeTarFilePy + } - override predicate isBarrier(DataFlow::Node node) { - // Avoid flow into the tarfile module - exists(ParameterDefinition def | - node.asVariable().getDefinition() = def - or - node.asCfgNode() = def.getDefiningNode() - | - def.getScope() = Value::named("tarfile.open").(CallableValue).getScope() - or - def.isSelf() and def.getScope().getEnclosingModule().getName() = "tarfile" - ) - } + override predicate isBarrier(DataFlow::Node node) { + // Avoid flow into the tarfile module + exists(ParameterDefinition def | + node.asVariable().getDefinition() = def + or + node.asCfgNode() = def.getDefiningNode() + | + def.getScope() = Value::named("tarfile.open").(CallableValue).getScope() + or + def.isSelf() and def.getScope().getEnclosingModule().getName() = "tarfile" + ) + } } from TarSlipConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "Extraction of tarfile from $@", src.getSource(), - "a potentially untrusted source" + "a potentially untrusted source" diff --git a/python/ql/src/Security/CWE-078/CommandInjection.ql b/python/ql/src/Security/CWE-078/CommandInjection.ql index 61ae6db00cd..51b46d72e5d 100755 --- a/python/ql/src/Security/CWE-078/CommandInjection.ql +++ b/python/ql/src/Security/CWE-078/CommandInjection.ql @@ -22,22 +22,22 @@ import semmle.python.web.HttpRequest import semmle.python.security.injection.Command class CommandInjectionConfiguration extends TaintTracking::Configuration { - CommandInjectionConfiguration() { this = "Command injection configuration" } + CommandInjectionConfiguration() { this = "Command injection configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof HttpRequestTaintSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof HttpRequestTaintSource + } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof CommandSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof CommandSink } - override predicate isExtension(TaintTracking::Extension extension) { - extension instanceof FirstElementFlow - or - extension instanceof FabricExecuteExtension - } + override predicate isExtension(TaintTracking::Extension extension) { + extension instanceof FirstElementFlow + or + extension instanceof FabricExecuteExtension + } } from CommandInjectionConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "This command depends on $@.", src.getSource(), - "a user-provided value" + "a user-provided value" diff --git a/python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.ql b/python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.ql index f04e442c8ce..ff3870678d8 100644 --- a/python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.ql +++ b/python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.ql @@ -25,24 +25,24 @@ import python */ ClassValue jinja2EnvironmentOrTemplate() { - result = Value::named("jinja2.Environment") - or - result = Value::named("jinja2.Template") + result = Value::named("jinja2.Environment") + or + result = Value::named("jinja2.Template") } ControlFlowNode getAutoEscapeParameter(CallNode call) { result = call.getArgByName("autoescape") } from CallNode call where - call.getFunction().pointsTo(jinja2EnvironmentOrTemplate()) and - not exists(call.getNode().getStarargs()) and - not exists(call.getNode().getKwargs()) and - ( - not exists(getAutoEscapeParameter(call)) - or - exists(Value isFalse | - getAutoEscapeParameter(call).pointsTo(isFalse) and - isFalse.getDefiniteBooleanValue() = false - ) + call.getFunction().pointsTo(jinja2EnvironmentOrTemplate()) and + not exists(call.getNode().getStarargs()) and + not exists(call.getNode().getKwargs()) and + ( + not exists(getAutoEscapeParameter(call)) + or + exists(Value isFalse | + getAutoEscapeParameter(call).pointsTo(isFalse) and + isFalse.getDefiniteBooleanValue() = false ) + ) select call, "Using jinja2 templates with autoescape=False can potentially allow XSS attacks." diff --git a/python/ql/src/Security/CWE-079/ReflectedXss.ql b/python/ql/src/Security/CWE-079/ReflectedXss.ql index cea41442c5b..9ee38e2ed24 100644 --- a/python/ql/src/Security/CWE-079/ReflectedXss.ql +++ b/python/ql/src/Security/CWE-079/ReflectedXss.ql @@ -22,21 +22,21 @@ import semmle.python.web.HttpResponse import semmle.python.security.strings.Untrusted class ReflectedXssConfiguration extends TaintTracking::Configuration { - ReflectedXssConfiguration() { this = "Reflected XSS configuration" } + ReflectedXssConfiguration() { this = "Reflected XSS configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof HttpRequestTaintSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof HttpRequestTaintSource + } - override predicate isSink(TaintTracking::Sink sink) { - sink instanceof HttpResponseTaintSink and - not sink instanceof DjangoResponseContent - or - sink instanceof DjangoResponseContentXSSVulnerable - } + override predicate isSink(TaintTracking::Sink sink) { + sink instanceof HttpResponseTaintSink and + not sink instanceof DjangoResponseContent + or + sink instanceof DjangoResponseContentXSSVulnerable + } } from ReflectedXssConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "Cross-site scripting vulnerability due to $@.", src.getSource(), - "a user-provided value" + "a user-provided value" diff --git a/python/ql/src/Security/CWE-089/SqlInjection.ql b/python/ql/src/Security/CWE-089/SqlInjection.ql index 86695fdf2ca..b4bff49d2cc 100755 --- a/python/ql/src/Security/CWE-089/SqlInjection.ql +++ b/python/ql/src/Security/CWE-089/SqlInjection.ql @@ -21,13 +21,13 @@ import semmle.python.web.django.Db import semmle.python.web.django.Model class SQLInjectionConfiguration extends TaintTracking::Configuration { - SQLInjectionConfiguration() { this = "SQL injection configuration" } + SQLInjectionConfiguration() { this = "SQL injection configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof HttpRequestTaintSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof HttpRequestTaintSource + } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof SqlInjectionSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof SqlInjectionSink } } /* @@ -37,15 +37,15 @@ class SQLInjectionConfiguration extends TaintTracking::Configuration { */ class DbConfiguration extends TaintTracking::Configuration { - DbConfiguration() { this = "DB configuration" } + DbConfiguration() { this = "DB configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof DjangoModelObjects or - source instanceof DbConnectionSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof DjangoModelObjects or + source instanceof DbConnectionSource + } } from SQLInjectionConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "This SQL query depends on $@.", src.getSource(), - "a user-provided value" + "a user-provided value" diff --git a/python/ql/src/Security/CWE-094/CodeInjection.ql b/python/ql/src/Security/CWE-094/CodeInjection.ql index 5aa5aa4c5c4..ed8b5d4e3e3 100644 --- a/python/ql/src/Security/CWE-094/CodeInjection.ql +++ b/python/ql/src/Security/CWE-094/CodeInjection.ql @@ -22,16 +22,16 @@ import semmle.python.web.HttpRequest import semmle.python.security.injection.Exec class CodeInjectionConfiguration extends TaintTracking::Configuration { - CodeInjectionConfiguration() { this = "Code injection configuration" } + CodeInjectionConfiguration() { this = "Code injection configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof HttpRequestTaintSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof HttpRequestTaintSource + } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof StringEvaluationNode } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof StringEvaluationNode } } from CodeInjectionConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "$@ flows to here and is interpreted as code.", src.getSource(), - "A user-provided value" + "A user-provided value" diff --git a/python/ql/src/Security/CWE-209/StackTraceExposure.ql b/python/ql/src/Security/CWE-209/StackTraceExposure.ql index 928cd44600a..27d89607a29 100644 --- a/python/ql/src/Security/CWE-209/StackTraceExposure.ql +++ b/python/ql/src/Security/CWE-209/StackTraceExposure.ql @@ -18,14 +18,14 @@ import semmle.python.security.Exceptions import semmle.python.web.HttpResponse class StackTraceExposureConfiguration extends TaintTracking::Configuration { - StackTraceExposureConfiguration() { this = "Stack trace exposure configuration" } + StackTraceExposureConfiguration() { this = "Stack trace exposure configuration" } - override predicate isSource(TaintTracking::Source source) { source instanceof ErrorInfoSource } + override predicate isSource(TaintTracking::Source source) { source instanceof ErrorInfoSource } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof HttpResponseTaintSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof HttpResponseTaintSink } } from StackTraceExposureConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "$@ may be exposed to an external user", src.getSource(), - "Error information" + "Error information" diff --git a/python/ql/src/Security/CWE-215/FlaskDebug.ql b/python/ql/src/Security/CWE-215/FlaskDebug.ql index f7869a9829d..a33d6fd788f 100644 --- a/python/ql/src/Security/CWE-215/FlaskDebug.ql +++ b/python/ql/src/Security/CWE-215/FlaskDebug.ql @@ -15,8 +15,8 @@ import semmle.python.web.flask.General from CallNode call, Value isTrue where - call = theFlaskClass().declaredAttribute("run").(FunctionValue).getACall() and - call.getArgByName("debug").pointsTo(isTrue) and - isTrue.getDefiniteBooleanValue() = true + call = theFlaskClass().declaredAttribute("run").(FunctionValue).getACall() and + call.getArgByName("debug").pointsTo(isTrue) and + isTrue.getDefiniteBooleanValue() = true select call, - "A Flask app appears to be run in debug mode. This may allow an attacker to run arbitrary code through the debugger." + "A Flask app appears to be run in debug mode. This may allow an attacker to run arbitrary code through the debugger." diff --git a/python/ql/src/Security/CWE-295/MissingHostKeyValidation.ql b/python/ql/src/Security/CWE-295/MissingHostKeyValidation.ql index 751e609b460..2241a212690 100644 --- a/python/ql/src/Security/CWE-295/MissingHostKeyValidation.ql +++ b/python/ql/src/Security/CWE-295/MissingHostKeyValidation.ql @@ -14,21 +14,21 @@ import python private ModuleValue theParamikoClientModule() { result = Value::named("paramiko.client") } private ClassValue theParamikoSSHClientClass() { - result = theParamikoClientModule().attr("SSHClient") + result = theParamikoClientModule().attr("SSHClient") } private ClassValue unsafe_paramiko_policy(string name) { - (name = "AutoAddPolicy" or name = "WarningPolicy") and - result = theParamikoClientModule().attr(name) + (name = "AutoAddPolicy" or name = "WarningPolicy") and + result = theParamikoClientModule().attr(name) } from CallNode call, ControlFlowNode arg, string name where - call = - theParamikoSSHClientClass().lookup("set_missing_host_key_policy").(FunctionValue).getACall() and - arg = call.getAnArg() and - ( - arg.pointsTo(unsafe_paramiko_policy(name)) or - arg.pointsTo().getClass() = unsafe_paramiko_policy(name) - ) + call = + theParamikoSSHClientClass().lookup("set_missing_host_key_policy").(FunctionValue).getACall() and + arg = call.getAnArg() and + ( + arg.pointsTo(unsafe_paramiko_policy(name)) or + arg.pointsTo().getClass() = unsafe_paramiko_policy(name) + ) select call, "Setting missing host key policy to " + name + " may be unsafe." diff --git a/python/ql/src/Security/CWE-295/RequestWithoutValidation.ql b/python/ql/src/Security/CWE-295/RequestWithoutValidation.ql index b6b22a3f255..173ffbe7671 100644 --- a/python/ql/src/Security/CWE-295/RequestWithoutValidation.ql +++ b/python/ql/src/Security/CWE-295/RequestWithoutValidation.ql @@ -19,8 +19,8 @@ predicate falseNotNone(Value v) { v.getDefiniteBooleanValue() = false and not v from CallNode call, FunctionValue func, Value falsey, ControlFlowNode origin where - func = requestFunction() and - func.getACall() = call and - falseNotNone(falsey) and - call.getArgByName("verify").pointsTo(falsey, origin) + func = requestFunction() and + func.getACall() = call and + falseNotNone(falsey) and + call.getArgByName("verify").pointsTo(falsey, origin) select call, "Call to $@ with verify=$@", func, "requests." + func.getName(), origin, "False" diff --git a/python/ql/src/Security/CWE-312/CleartextLogging.ql b/python/ql/src/Security/CWE-312/CleartextLogging.ql index d1c6ac94d4b..071ab9db141 100644 --- a/python/ql/src/Security/CWE-312/CleartextLogging.ql +++ b/python/ql/src/Security/CWE-312/CleartextLogging.ql @@ -19,19 +19,19 @@ import semmle.python.security.SensitiveData import semmle.python.security.ClearText class CleartextLoggingConfiguration extends TaintTracking::Configuration { - CleartextLoggingConfiguration() { this = "ClearTextLogging" } + CleartextLoggingConfiguration() { this = "ClearTextLogging" } - override predicate isSource(DataFlow::Node src, TaintKind kind) { - src.asCfgNode().(SensitiveData::Source).isSourceOf(kind) - } + override predicate isSource(DataFlow::Node src, TaintKind kind) { + src.asCfgNode().(SensitiveData::Source).isSourceOf(kind) + } - override predicate isSink(DataFlow::Node sink, TaintKind kind) { - sink.asCfgNode() instanceof ClearTextLogging::Sink and - kind instanceof SensitiveData - } + override predicate isSink(DataFlow::Node sink, TaintKind kind) { + sink.asCfgNode() instanceof ClearTextLogging::Sink and + kind instanceof SensitiveData + } } from CleartextLoggingConfiguration config, TaintedPathSource source, TaintedPathSink sink where config.hasFlowPath(source, sink) select sink.getSink(), source, sink, "Sensitive data returned by $@ is logged here.", - source.getSource(), source.getCfgNode().(SensitiveData::Source).repr() + source.getSource(), source.getCfgNode().(SensitiveData::Source).repr() diff --git a/python/ql/src/Security/CWE-312/CleartextStorage.ql b/python/ql/src/Security/CWE-312/CleartextStorage.ql index f1f898b00dd..2c33837b464 100644 --- a/python/ql/src/Security/CWE-312/CleartextStorage.ql +++ b/python/ql/src/Security/CWE-312/CleartextStorage.ql @@ -19,19 +19,19 @@ import semmle.python.security.SensitiveData import semmle.python.security.ClearText class CleartextStorageConfiguration extends TaintTracking::Configuration { - CleartextStorageConfiguration() { this = "ClearTextStorage" } + CleartextStorageConfiguration() { this = "ClearTextStorage" } - override predicate isSource(DataFlow::Node src, TaintKind kind) { - src.asCfgNode().(SensitiveData::Source).isSourceOf(kind) - } + override predicate isSource(DataFlow::Node src, TaintKind kind) { + src.asCfgNode().(SensitiveData::Source).isSourceOf(kind) + } - override predicate isSink(DataFlow::Node sink, TaintKind kind) { - sink.asCfgNode() instanceof ClearTextStorage::Sink and - kind instanceof SensitiveData - } + override predicate isSink(DataFlow::Node sink, TaintKind kind) { + sink.asCfgNode() instanceof ClearTextStorage::Sink and + kind instanceof SensitiveData + } } from CleartextStorageConfiguration config, TaintedPathSource source, TaintedPathSink sink where config.hasFlowPath(source, sink) select sink.getSink(), source, sink, "Sensitive data from $@ is stored here.", source.getSource(), - source.getCfgNode().(SensitiveData::Source).repr() + source.getCfgNode().(SensitiveData::Source).repr() diff --git a/python/ql/src/Security/CWE-326/WeakCrypto.ql b/python/ql/src/Security/CWE-326/WeakCrypto.ql index 1d1637f19a5..27c1fcce429 100644 --- a/python/ql/src/Security/CWE-326/WeakCrypto.ql +++ b/python/ql/src/Security/CWE-326/WeakCrypto.ql @@ -12,70 +12,70 @@ import python int minimumSecureKeySize(string algo) { - algo = "DSA" and result = 2048 - or - algo = "RSA" and result = 2048 - or - algo = "ECC" and result = 224 + algo = "DSA" and result = 2048 + or + algo = "RSA" and result = 2048 + or + algo = "ECC" and result = 224 } predicate dsaRsaKeySizeArg(FunctionValue func, string algorithm, string arg) { - exists(ModuleValue mod | func = mod.attr(_) | - algorithm = "DSA" and - ( - mod = Module::named("cryptography.hazmat.primitives.asymmetric.dsa") and arg = "key_size" - or - mod = Module::named("Crypto.PublicKey.DSA") and arg = "bits" - or - mod = Module::named("Cryptodome.PublicKey.DSA") and arg = "bits" - ) - or - algorithm = "RSA" and - ( - mod = Module::named("cryptography.hazmat.primitives.asymmetric.rsa") and arg = "key_size" - or - mod = Module::named("Crypto.PublicKey.RSA") and arg = "bits" - or - mod = Module::named("Cryptodome.PublicKey.RSA") and arg = "bits" - ) + exists(ModuleValue mod | func = mod.attr(_) | + algorithm = "DSA" and + ( + mod = Module::named("cryptography.hazmat.primitives.asymmetric.dsa") and arg = "key_size" + or + mod = Module::named("Crypto.PublicKey.DSA") and arg = "bits" + or + mod = Module::named("Cryptodome.PublicKey.DSA") and arg = "bits" ) + or + algorithm = "RSA" and + ( + mod = Module::named("cryptography.hazmat.primitives.asymmetric.rsa") and arg = "key_size" + or + mod = Module::named("Crypto.PublicKey.RSA") and arg = "bits" + or + mod = Module::named("Cryptodome.PublicKey.RSA") and arg = "bits" + ) + ) } predicate ecKeySizeArg(FunctionValue func, string arg) { - exists(ModuleValue mod | func = mod.attr(_) | - mod = Module::named("cryptography.hazmat.primitives.asymmetric.ec") and arg = "curve" - ) + exists(ModuleValue mod | func = mod.attr(_) | + mod = Module::named("cryptography.hazmat.primitives.asymmetric.ec") and arg = "curve" + ) } int keySizeFromCurve(ClassValue curveClass) { - result = curveClass.declaredAttribute("key_size").(NumericValue).getIntValue() + result = curveClass.declaredAttribute("key_size").(NumericValue).getIntValue() } predicate algorithmAndKeysizeForCall( - CallNode call, string algorithm, int keySize, ControlFlowNode keyOrigin + CallNode call, string algorithm, int keySize, ControlFlowNode keyOrigin ) { - exists(FunctionValue func, string argname, ControlFlowNode arg | - arg = func.getNamedArgumentForCall(call, argname) - | - exists(NumericValue key | - arg.pointsTo(key, keyOrigin) and - dsaRsaKeySizeArg(func, algorithm, argname) and - keySize = key.getIntValue() - ) - or - exists(Value curveClassInstance | - algorithm = "ECC" and - ecKeySizeArg(func, argname) and - arg.pointsTo(_, curveClassInstance, keyOrigin) and - keySize = keySizeFromCurve(curveClassInstance.getClass()) - ) + exists(FunctionValue func, string argname, ControlFlowNode arg | + arg = func.getNamedArgumentForCall(call, argname) + | + exists(NumericValue key | + arg.pointsTo(key, keyOrigin) and + dsaRsaKeySizeArg(func, algorithm, argname) and + keySize = key.getIntValue() ) + or + exists(Value curveClassInstance | + algorithm = "ECC" and + ecKeySizeArg(func, argname) and + arg.pointsTo(_, curveClassInstance, keyOrigin) and + keySize = keySizeFromCurve(curveClassInstance.getClass()) + ) + ) } from CallNode call, string algo, int keySize, ControlFlowNode origin where - algorithmAndKeysizeForCall(call, algo, keySize, origin) and - keySize < minimumSecureKeySize(algo) + algorithmAndKeysizeForCall(call, algo, keySize, origin) and + keySize < minimumSecureKeySize(algo) select call, - "Creation of an " + algo + " key uses $@ bits, which is below " + minimumSecureKeySize(algo) + - " and considered breakable.", origin, keySize.toString() + "Creation of an " + algo + " key uses $@ bits, which is below " + minimumSecureKeySize(algo) + + " and considered breakable.", origin, keySize.toString() diff --git a/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql b/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql index 55d0f79f791..36064dc0386 100644 --- a/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql +++ b/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql @@ -15,16 +15,16 @@ import semmle.python.security.SensitiveData import semmle.python.security.Crypto class BrokenCryptoConfiguration extends TaintTracking::Configuration { - BrokenCryptoConfiguration() { this = "Broken crypto configuration" } + BrokenCryptoConfiguration() { this = "Broken crypto configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof SensitiveDataSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof SensitiveDataSource + } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof WeakCryptoSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof WeakCryptoSink } } from BrokenCryptoConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "$@ is used in a broken or weak cryptographic algorithm.", - src.getSource(), "Sensitive data" + src.getSource(), "Sensitive data" diff --git a/python/ql/src/Security/CWE-327/InsecureDefaultProtocol.ql b/python/ql/src/Security/CWE-327/InsecureDefaultProtocol.ql index 7e6e3194b2c..30e0c6c0e55 100644 --- a/python/ql/src/Security/CWE-327/InsecureDefaultProtocol.ql +++ b/python/ql/src/Security/CWE-327/InsecureDefaultProtocol.ql @@ -17,18 +17,18 @@ FunctionValue ssl_wrap_socket() { result = Value::named("ssl.wrap_socket") } ClassValue ssl_Context_class() { result = Value::named("ssl.SSLContext") } CallNode unsafe_call(string method_name) { - result = ssl_wrap_socket().getACall() and - not exists(result.getArgByName("ssl_version")) and - method_name = "deprecated method ssl.wrap_socket" - or - result = ssl_Context_class().getACall() and - not exists(result.getArgByName("protocol")) and - not exists(result.getArg(0)) and - method_name = "ssl.SSLContext" + result = ssl_wrap_socket().getACall() and + not exists(result.getArgByName("ssl_version")) and + method_name = "deprecated method ssl.wrap_socket" + or + result = ssl_Context_class().getACall() and + not exists(result.getArgByName("protocol")) and + not exists(result.getArg(0)) and + method_name = "ssl.SSLContext" } from CallNode call, string method_name where call = unsafe_call(method_name) select call, - "Call to " + method_name + - " does not specify a protocol, which may result in an insecure default being used." + "Call to " + method_name + + " does not specify a protocol, which may result in an insecure default being used." diff --git a/python/ql/src/Security/CWE-327/InsecureProtocol.ql b/python/ql/src/Security/CWE-327/InsecureProtocol.ql index 21eff3b38f5..d1ae714b6be 100644 --- a/python/ql/src/Security/CWE-327/InsecureProtocol.ql +++ b/python/ql/src/Security/CWE-327/InsecureProtocol.ql @@ -22,17 +22,17 @@ private ModuleValue the_pyOpenSSL_module() { result = Value::named("pyOpenSSL.SS ClassValue the_pyOpenSSL_Context_class() { result = Value::named("pyOpenSSL.SSL.Context") } string insecure_version_name() { - // For `pyOpenSSL.SSL` - result = "SSLv2_METHOD" or - result = "SSLv23_METHOD" or - result = "SSLv3_METHOD" or - result = "TLSv1_METHOD" or - // For the `ssl` module - result = "PROTOCOL_SSLv2" or - result = "PROTOCOL_SSLv3" or - result = "PROTOCOL_SSLv23" or - result = "PROTOCOL_TLS" or - result = "PROTOCOL_TLSv1" + // For `pyOpenSSL.SSL` + result = "SSLv2_METHOD" or + result = "SSLv23_METHOD" or + result = "SSLv3_METHOD" or + result = "TLSv1_METHOD" or + // For the `ssl` module + result = "PROTOCOL_SSLv2" or + result = "PROTOCOL_SSLv3" or + result = "PROTOCOL_SSLv23" or + result = "PROTOCOL_TLS" or + result = "PROTOCOL_TLSv1" } /* @@ -43,53 +43,53 @@ string insecure_version_name() { bindingset[named_argument] predicate probable_insecure_ssl_constant( - CallNode call, string insecure_version, string named_argument + CallNode call, string insecure_version, string named_argument ) { - exists(ControlFlowNode arg | - arg = call.getArgByName(named_argument) or - arg = call.getArg(0) - | - arg.(AttrNode).getObject(insecure_version).pointsTo(the_ssl_module()) - or - arg.(NameNode).getId() = insecure_version and - exists(Import imp | - imp.getAnImportedModuleName() = "ssl" and - imp.getAName().getAsname().(Name).getId() = insecure_version - ) + exists(ControlFlowNode arg | + arg = call.getArgByName(named_argument) or + arg = call.getArg(0) + | + arg.(AttrNode).getObject(insecure_version).pointsTo(the_ssl_module()) + or + arg.(NameNode).getId() = insecure_version and + exists(Import imp | + imp.getAnImportedModuleName() = "ssl" and + imp.getAName().getAsname().(Name).getId() = insecure_version ) + ) } predicate unsafe_ssl_wrap_socket_call( - CallNode call, string method_name, string insecure_version, string named_argument + CallNode call, string method_name, string insecure_version, string named_argument ) { - ( - call = ssl_wrap_socket().getACall() and - method_name = "deprecated method ssl.wrap_socket" and - named_argument = "ssl_version" - or - call = ssl_Context_class().getACall() and - named_argument = "protocol" and - method_name = "ssl.SSLContext" - ) and - insecure_version = insecure_version_name() and - ( - call.getArgByName(named_argument).pointsTo(the_ssl_module().attr(insecure_version)) - or - probable_insecure_ssl_constant(call, insecure_version, named_argument) - ) + ( + call = ssl_wrap_socket().getACall() and + method_name = "deprecated method ssl.wrap_socket" and + named_argument = "ssl_version" + or + call = ssl_Context_class().getACall() and + named_argument = "protocol" and + method_name = "ssl.SSLContext" + ) and + insecure_version = insecure_version_name() and + ( + call.getArgByName(named_argument).pointsTo(the_ssl_module().attr(insecure_version)) + or + probable_insecure_ssl_constant(call, insecure_version, named_argument) + ) } predicate unsafe_pyOpenSSL_Context_call(CallNode call, string insecure_version) { - call = the_pyOpenSSL_Context_class().getACall() and - insecure_version = insecure_version_name() and - call.getArg(0).pointsTo(the_pyOpenSSL_module().attr(insecure_version)) + call = the_pyOpenSSL_Context_class().getACall() and + insecure_version = insecure_version_name() and + call.getArg(0).pointsTo(the_pyOpenSSL_module().attr(insecure_version)) } from CallNode call, string method_name, string insecure_version where - unsafe_ssl_wrap_socket_call(call, method_name, insecure_version, _) - or - unsafe_pyOpenSSL_Context_call(call, insecure_version) and method_name = "pyOpenSSL.SSL.Context" + unsafe_ssl_wrap_socket_call(call, method_name, insecure_version, _) + or + unsafe_pyOpenSSL_Context_call(call, insecure_version) and method_name = "pyOpenSSL.SSL.Context" select call, - "Insecure SSL/TLS protocol version " + insecure_version + " specified in call to " + method_name + - "." + "Insecure SSL/TLS protocol version " + insecure_version + " specified in call to " + method_name + + "." diff --git a/python/ql/src/Security/CWE-377/InsecureTemporaryFile.ql b/python/ql/src/Security/CWE-377/InsecureTemporaryFile.ql index 41e7e5185ad..df694e8303d 100644 --- a/python/ql/src/Security/CWE-377/InsecureTemporaryFile.ql +++ b/python/ql/src/Security/CWE-377/InsecureTemporaryFile.ql @@ -13,17 +13,17 @@ import python FunctionValue temporary_name_function(string mod, string function) { + ( + mod = "tempfile" and function = "mktemp" + or + mod = "os" and ( - mod = "tempfile" and function = "mktemp" - or - mod = "os" and - ( - function = "tmpnam" - or - function = "tempnam" - ) - ) and - result = Module::named(mod).attr(function) + function = "tmpnam" + or + function = "tempnam" + ) + ) and + result = Module::named(mod).attr(function) } from Call c, string mod, string function diff --git a/python/ql/src/Security/CWE-502/UnsafeDeserialization.ql b/python/ql/src/Security/CWE-502/UnsafeDeserialization.ql index 22f803690b5..0f365240cd5 100644 --- a/python/ql/src/Security/CWE-502/UnsafeDeserialization.ql +++ b/python/ql/src/Security/CWE-502/UnsafeDeserialization.ql @@ -23,13 +23,13 @@ import semmle.python.security.injection.Marshal import semmle.python.security.injection.Yaml class UnsafeDeserializationConfiguration extends TaintTracking::Configuration { - UnsafeDeserializationConfiguration() { this = "Unsafe deserialization configuration" } + UnsafeDeserializationConfiguration() { this = "Unsafe deserialization configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof HttpRequestTaintSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof HttpRequestTaintSource + } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof DeserializationSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof DeserializationSink } } from UnsafeDeserializationConfiguration config, TaintedPathSource src, TaintedPathSink sink diff --git a/python/ql/src/Security/CWE-601/UrlRedirect.ql b/python/ql/src/Security/CWE-601/UrlRedirect.ql index 546395477fa..cb517043a36 100644 --- a/python/ql/src/Security/CWE-601/UrlRedirect.ql +++ b/python/ql/src/Security/CWE-601/UrlRedirect.ql @@ -19,23 +19,23 @@ import semmle.python.security.strings.Untrusted /** Url redirection is a problem only if the user controls the prefix of the URL */ class UntrustedPrefixStringKind extends UntrustedStringKind { - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - result = UntrustedStringKind.super.getTaintForFlowStep(fromnode, tonode) and - not tonode.(BinaryExprNode).getRight() = fromnode - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + result = UntrustedStringKind.super.getTaintForFlowStep(fromnode, tonode) and + not tonode.(BinaryExprNode).getRight() = fromnode + } } class UrlRedirectConfiguration extends TaintTracking::Configuration { - UrlRedirectConfiguration() { this = "URL redirect configuration" } + UrlRedirectConfiguration() { this = "URL redirect configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof HttpRequestTaintSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof HttpRequestTaintSource + } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof HttpRedirectTaintSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof HttpRedirectTaintSink } } from UrlRedirectConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "Untrusted URL redirection due to $@.", src.getSource(), - "a user-provided value" + "a user-provided value" diff --git a/python/ql/src/Security/CWE-732/WeakFilePermissions.ql b/python/ql/src/Security/CWE-732/WeakFilePermissions.ql index a3bd852fb31..7163d02b530 100644 --- a/python/ql/src/Security/CWE-732/WeakFilePermissions.ql +++ b/python/ql/src/Security/CWE-732/WeakFilePermissions.ql @@ -20,32 +20,32 @@ int group_permission(int p) { result = (p / 8) % 8 } bindingset[p] string access(int p) { - p % 4 >= 2 and result = "writable" - or - p % 4 < 2 and p != 0 and result = "readable" + p % 4 >= 2 and result = "writable" + or + p % 4 < 2 and p != 0 and result = "readable" } bindingset[p] string permissive_permission(int p) { - result = "world " + access(world_permission(p)) - or - world_permission(p) = 0 and result = "group " + access(group_permission(p)) + result = "world " + access(world_permission(p)) + or + world_permission(p) = 0 and result = "group " + access(group_permission(p)) } predicate chmod_call(CallNode call, FunctionValue chmod, NumericValue num) { - Value::named("os.chmod") = chmod and - chmod.getACall() = call and - call.getArg(1).pointsTo(num) + Value::named("os.chmod") = chmod and + chmod.getACall() = call and + call.getArg(1).pointsTo(num) } predicate open_call(CallNode call, FunctionValue open, NumericValue num) { - Value::named("os.open") = open and - open.getACall() = call and - call.getArg(2).pointsTo(num) + Value::named("os.open") = open and + open.getACall() = call and + call.getArg(2).pointsTo(num) } from CallNode call, FunctionValue func, NumericValue num, string permission where - (chmod_call(call, func, num) or open_call(call, func, num)) and - permission = permissive_permission(num.getIntValue()) + (chmod_call(call, func, num) or open_call(call, func, num)) and + permission = permissive_permission(num.getIntValue()) select call, "Overly permissive mask in " + func.getName() + " sets file to " + permission + "." diff --git a/python/ql/src/Security/CWE-798/HardcodedCredentials.ql b/python/ql/src/Security/CWE-798/HardcodedCredentials.ql index edc03fb5f36..f62e89abcf7 100644 --- a/python/ql/src/Security/CWE-798/HardcodedCredentials.ql +++ b/python/ql/src/Security/CWE-798/HardcodedCredentials.ql @@ -17,31 +17,31 @@ import semmle.python.dataflow.TaintTracking import semmle.python.filters.Tests class HardcodedValue extends TaintKind { - HardcodedValue() { this = "hard coded value" } + HardcodedValue() { this = "hard coded value" } } bindingset[char, fraction] predicate fewer_characters_than(StrConst str, string char, float fraction) { - exists(string text, int chars | - text = str.getText() and - chars = count(int i | text.charAt(i) = char) - | - /* Allow one character */ - chars = 1 or - chars < text.length() * fraction - ) + exists(string text, int chars | + text = str.getText() and + chars = count(int i | text.charAt(i) = char) + | + /* Allow one character */ + chars = 1 or + chars < text.length() * fraction + ) } predicate possible_reflective_name(string name) { - exists(any(ModuleValue m).attr(name)) - or - exists(any(ClassValue c).lookup(name)) - or - any(ClassValue c).getName() = name - or - exists(Module::named(name)) - or - exists(Value::named(name)) + exists(any(ModuleValue m).attr(name)) + or + exists(any(ClassValue c).lookup(name)) + or + any(ClassValue c).getName() = name + or + exists(Module::named(name)) + or + exists(Value::named(name)) } int char_count(StrConst str) { result = count(string c | c = str.getText().charAt(_)) } @@ -51,57 +51,57 @@ predicate capitalized_word(StrConst str) { str.getText().regexpMatch("[A-Z][a-z] predicate format_string(StrConst str) { str.getText().matches("%{%}%") } predicate maybeCredential(ControlFlowNode f) { - /* A string that is not too short and unlikely to be text or an identifier. */ - exists(StrConst str | str = f.getNode() | - /* At least 10 characters */ - str.getText().length() > 9 and - /* Not too much whitespace */ - fewer_characters_than(str, " ", 0.05) and - /* or underscores */ - fewer_characters_than(str, "_", 0.2) and - /* Not too repetitive */ - exists(int chars | chars = char_count(str) | - chars > 15 or - chars * 3 > str.getText().length() * 2 - ) and - not possible_reflective_name(str.getText()) and - not capitalized_word(str) and - not format_string(str) - ) - or - /* Or, an integer with over 32 bits */ - exists(IntegerLiteral lit | f.getNode() = lit | - not exists(lit.getValue()) and - /* Not a set of flags or round number */ - not lit.getN().matches("%00%") - ) + /* A string that is not too short and unlikely to be text or an identifier. */ + exists(StrConst str | str = f.getNode() | + /* At least 10 characters */ + str.getText().length() > 9 and + /* Not too much whitespace */ + fewer_characters_than(str, " ", 0.05) and + /* or underscores */ + fewer_characters_than(str, "_", 0.2) and + /* Not too repetitive */ + exists(int chars | chars = char_count(str) | + chars > 15 or + chars * 3 > str.getText().length() * 2 + ) and + not possible_reflective_name(str.getText()) and + not capitalized_word(str) and + not format_string(str) + ) + or + /* Or, an integer with over 32 bits */ + exists(IntegerLiteral lit | f.getNode() = lit | + not exists(lit.getValue()) and + /* Not a set of flags or round number */ + not lit.getN().matches("%00%") + ) } class HardcodedValueSource extends TaintSource { - HardcodedValueSource() { maybeCredential(this) } + HardcodedValueSource() { maybeCredential(this) } - override predicate isSourceOf(TaintKind kind) { kind instanceof HardcodedValue } + override predicate isSourceOf(TaintKind kind) { kind instanceof HardcodedValue } } class CredentialSink extends TaintSink { - CredentialSink() { - exists(string name | - name.regexpMatch(getACredentialRegex()) and - not name.suffix(name.length() - 4) = "file" - | - any(FunctionValue func).getNamedArgumentForCall(_, name) = this - or - exists(Keyword k | k.getArg() = name and k.getValue().getAFlowNode() = this) - or - exists(CompareNode cmp, NameNode n | n.getId() = name | - cmp.operands(this, any(Eq eq), n) - or - cmp.operands(n, any(Eq eq), this) - ) - ) - } + CredentialSink() { + exists(string name | + name.regexpMatch(getACredentialRegex()) and + not name.suffix(name.length() - 4) = "file" + | + any(FunctionValue func).getNamedArgumentForCall(_, name) = this + or + exists(Keyword k | k.getArg() = name and k.getValue().getAFlowNode() = this) + or + exists(CompareNode cmp, NameNode n | n.getId() = name | + cmp.operands(this, any(Eq eq), n) + or + cmp.operands(n, any(Eq eq), this) + ) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof HardcodedValue } + override predicate sinks(TaintKind kind) { kind instanceof HardcodedValue } } /** @@ -109,23 +109,23 @@ class CredentialSink extends TaintSink { * indicate the value being held is a credential. */ private string getACredentialRegex() { - result = "(?i).*pass(wd|word|code|phrase)(?!.*question).*" or - result = "(?i).*(puid|username|userid).*" or - result = "(?i).*(cert)(?!.*(format|name)).*" + result = "(?i).*pass(wd|word|code|phrase)(?!.*question).*" or + result = "(?i).*(puid|username|userid).*" or + result = "(?i).*(cert)(?!.*(format|name)).*" } class HardcodedCredentialsConfiguration extends TaintTracking::Configuration { - HardcodedCredentialsConfiguration() { this = "Hardcoded coredentials configuration" } + HardcodedCredentialsConfiguration() { this = "Hardcoded coredentials configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof HardcodedValueSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof HardcodedValueSource + } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof CredentialSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof CredentialSink } } from HardcodedCredentialsConfiguration config, TaintedPathSource src, TaintedPathSink sink where - config.hasFlowPath(src, sink) and - not any(TestScope test).contains(src.getAstNode()) + config.hasFlowPath(src, sink) and + not any(TestScope test).contains(src.getAstNode()) select sink.getSink(), src, sink, "Use of $@.", src.getSource(), "hardcoded credentials" diff --git a/python/ql/src/Statements/AssertLiteralConstant.ql b/python/ql/src/Statements/AssertLiteralConstant.ql index cea2d7302f2..372b25fd10d 100644 --- a/python/ql/src/Statements/AssertLiteralConstant.ql +++ b/python/ql/src/Statements/AssertLiteralConstant.ql @@ -16,15 +16,15 @@ import semmle.python.filters.Tests from Assert a, string value where - /* Exclude asserts inside test cases */ - not a.getScope().getScope*() instanceof TestScope and - exists(Expr test | test = a.getTest() | - value = test.(IntegerLiteral).getN() - or - value = "\"" + test.(StrConst).getS() + "\"" - or - value = test.(NameConstant).toString() - ) and - /* Exclude asserts appearing at the end of a chain of `elif`s */ - not exists(If i | i.getElif().getAnOrelse() = a) + /* Exclude asserts inside test cases */ + not a.getScope().getScope*() instanceof TestScope and + exists(Expr test | test = a.getTest() | + value = test.(IntegerLiteral).getN() + or + value = "\"" + test.(StrConst).getS() + "\"" + or + value = test.(NameConstant).toString() + ) and + /* Exclude asserts appearing at the end of a chain of `elif`s */ + not exists(If i | i.getElif().getAnOrelse() = a) select a, "Assert of literal constant " + value + "." diff --git a/python/ql/src/Statements/AssertOnTuple.ql b/python/ql/src/Statements/AssertOnTuple.ql index 0a2c83f986c..e86e05483c3 100644 --- a/python/ql/src/Statements/AssertOnTuple.ql +++ b/python/ql/src/Statements/AssertOnTuple.ql @@ -15,13 +15,13 @@ import python from Assert a, string b, string non where - a.getTest() instanceof Tuple and - ( - if exists(a.getTest().(Tuple).getAnElt()) - then ( - b = "True" and non = "non-" - ) else ( - b = "False" and non = "" - ) + a.getTest() instanceof Tuple and + ( + if exists(a.getTest().(Tuple).getAnElt()) + then ( + b = "True" and non = "non-" + ) else ( + b = "False" and non = "" ) + ) select a, "Assertion of " + non + "empty tuple is always " + b + "." diff --git a/python/ql/src/Statements/BreakOrReturnInFinally.ql b/python/ql/src/Statements/BreakOrReturnInFinally.ql index 7dbc2fa8edd..02f501e0bfd 100644 --- a/python/ql/src/Statements/BreakOrReturnInFinally.ql +++ b/python/ql/src/Statements/BreakOrReturnInFinally.ql @@ -16,12 +16,12 @@ import python from Stmt s, string kind where - s instanceof Return and kind = "return" and exists(Try t | t.getFinalbody().contains(s)) - or - s instanceof Break and - kind = "break" and - exists(Try t | t.getFinalbody().contains(s) | - not exists(For loop | loop.contains(s) and t.getFinalbody().contains(loop)) and - not exists(While loop | loop.contains(s) and t.getFinalbody().contains(loop)) - ) + s instanceof Return and kind = "return" and exists(Try t | t.getFinalbody().contains(s)) + or + s instanceof Break and + kind = "break" and + exists(Try t | t.getFinalbody().contains(s) | + not exists(For loop | loop.contains(s) and t.getFinalbody().contains(loop)) and + not exists(While loop | loop.contains(s) and t.getFinalbody().contains(loop)) + ) select s, "'" + kind + "' in a finally block will swallow any exceptions raised." diff --git a/python/ql/src/Statements/C_StyleParentheses.ql b/python/ql/src/Statements/C_StyleParentheses.ql index 1c0f27bf8b6..d428f78f1b6 100644 --- a/python/ql/src/Statements/C_StyleParentheses.ql +++ b/python/ql/src/Statements/C_StyleParentheses.ql @@ -15,21 +15,21 @@ import python from Expr e, Location l, string kind, string what where - e.isParenthesized() and - not e instanceof Tuple and - ( - exists(If i | i.getTest() = e) and kind = "if" and what = "condition" - or - exists(While w | w.getTest() = e) and kind = "while" and what = "condition" - or - exists(Return r | r.getValue() = e) and kind = "return" and what = "value" - or - exists(Assert a | a.getTest() = e and not exists(a.getMsg())) and - kind = "assert" and - what = "test" - ) and - // These require parentheses - (not e instanceof Yield and not e instanceof YieldFrom and not e instanceof GeneratorExp) and - l = e.getLocation() and - l.getStartLine() = l.getEndLine() + e.isParenthesized() and + not e instanceof Tuple and + ( + exists(If i | i.getTest() = e) and kind = "if" and what = "condition" + or + exists(While w | w.getTest() = e) and kind = "while" and what = "condition" + or + exists(Return r | r.getValue() = e) and kind = "return" and what = "value" + or + exists(Assert a | a.getTest() = e and not exists(a.getMsg())) and + kind = "assert" and + what = "test" + ) and + // These require parentheses + (not e instanceof Yield and not e instanceof YieldFrom and not e instanceof GeneratorExp) and + l = e.getLocation() and + l.getStartLine() = l.getEndLine() select e, "Parenthesized " + what + " in '" + kind + "' statement." diff --git a/python/ql/src/Statements/ConstantInConditional.ql b/python/ql/src/Statements/ConstantInConditional.ql index e01e693467a..0b12d6efd98 100644 --- a/python/ql/src/Statements/ConstantInConditional.ql +++ b/python/ql/src/Statements/ConstantInConditional.ql @@ -16,27 +16,27 @@ import python predicate is_condition(Expr cond) { - exists(If i | i.getTest() = cond) or - exists(IfExp ie | ie.getTest() = cond) + exists(If i | i.getTest() = cond) or + exists(IfExp ie | ie.getTest() = cond) } /* Treat certain unmodified builtins as constants as well. */ predicate effective_constant(Name cond) { - exists(GlobalVariable var | var = cond.getVariable() and not exists(NameNode f | f.defines(var)) | - var.getId() = "True" or var.getId() = "False" or var.getId() = "NotImplemented" - ) + exists(GlobalVariable var | var = cond.getVariable() and not exists(NameNode f | f.defines(var)) | + var.getId() = "True" or var.getId() = "False" or var.getId() = "NotImplemented" + ) } predicate test_makes_code_unreachable(Expr cond) { - exists(If i | i.getTest() = cond | i.getStmt(0).isUnreachable() or i.getOrelse(0).isUnreachable()) - or - exists(While w | w.getTest() = cond and w.getStmt(0).isUnreachable()) + exists(If i | i.getTest() = cond | i.getStmt(0).isUnreachable() or i.getOrelse(0).isUnreachable()) + or + exists(While w | w.getTest() = cond and w.getStmt(0).isUnreachable()) } from Expr cond where - is_condition(cond) and - (cond.isConstant() or effective_constant(cond)) and - /* Ignore cases where test makes code unreachable, as that is handled in different query */ - not test_makes_code_unreachable(cond) + is_condition(cond) and + (cond.isConstant() or effective_constant(cond)) and + /* Ignore cases where test makes code unreachable, as that is handled in different query */ + not test_makes_code_unreachable(cond) select cond, "Testing a constant will always give the same result." diff --git a/python/ql/src/Statements/DocStrings.ql b/python/ql/src/Statements/DocStrings.ql index d6a1d812300..b20731f723b 100644 --- a/python/ql/src/Statements/DocStrings.ql +++ b/python/ql/src/Statements/DocStrings.ql @@ -18,32 +18,32 @@ import python predicate needs_docstring(Scope s) { - s.isPublic() and - ( - not s instanceof Function - or - function_needs_docstring(s) - ) + s.isPublic() and + ( + not s instanceof Function + or + function_needs_docstring(s) + ) } predicate function_needs_docstring(Function f) { - not exists(FunctionValue fo, FunctionValue base | fo.overrides(base) and fo.getScope() = f | - not function_needs_docstring(base.getScope()) - ) and - f.getName() != "lambda" and - (f.getMetrics().getNumberOfLinesOfCode() - count(f.getADecorator())) > 2 and - not exists(PythonPropertyObject p | - p.getGetter().getFunction() = f or - p.getSetter().getFunction() = f - ) + not exists(FunctionValue fo, FunctionValue base | fo.overrides(base) and fo.getScope() = f | + not function_needs_docstring(base.getScope()) + ) and + f.getName() != "lambda" and + (f.getMetrics().getNumberOfLinesOfCode() - count(f.getADecorator())) > 2 and + not exists(PythonPropertyObject p | + p.getGetter().getFunction() = f or + p.getSetter().getFunction() = f + ) } string scope_type(Scope s) { - result = "Module" and s instanceof Module and not s.(Module).isPackage() - or - result = "Class" and s instanceof Class - or - result = "Function" and s instanceof Function + result = "Module" and s instanceof Module and not s.(Module).isPackage() + or + result = "Class" and s instanceof Class + or + result = "Function" and s instanceof Function } from Scope s diff --git a/python/ql/src/Statements/ExecUsed.ql b/python/ql/src/Statements/ExecUsed.ql index 44d4da02e32..e1e4bb45c6b 100644 --- a/python/ql/src/Statements/ExecUsed.ql +++ b/python/ql/src/Statements/ExecUsed.ql @@ -13,13 +13,13 @@ import python string message() { - result = "The 'exec' statement is used." and major_version() = 2 - or - result = "The 'exec' function is used." and major_version() = 3 + result = "The 'exec' statement is used." and major_version() = 2 + or + result = "The 'exec' function is used." and major_version() = 3 } predicate exec_function_call(Call c) { - exists(GlobalVariable exec | exec = c.getFunc().(Name).getVariable() and exec.getId() = "exec") + exists(GlobalVariable exec | exec = c.getFunc().(Name).getVariable() and exec.getId() = "exec") } from AstNode exec diff --git a/python/ql/src/Statements/IterableStringOrSequence.ql b/python/ql/src/Statements/IterableStringOrSequence.ql index 7ab43f33b76..a92a1d79d5f 100644 --- a/python/ql/src/Statements/IterableStringOrSequence.ql +++ b/python/ql/src/Statements/IterableStringOrSequence.ql @@ -15,24 +15,24 @@ import python import semmle.python.filters.Tests predicate has_string_type(Value v) { - v.getClass() = ClassValue::str() - or - v.getClass() = ClassValue::unicode() and major_version() = 2 + v.getClass() = ClassValue::str() + or + v.getClass() = ClassValue::unicode() and major_version() = 2 } from - For loop, ControlFlowNode iter, Value str, Value seq, ControlFlowNode seq_origin, - ControlFlowNode str_origin + For loop, ControlFlowNode iter, Value str, Value seq, ControlFlowNode seq_origin, + ControlFlowNode str_origin where - loop.getIter().getAFlowNode() = iter and - iter.pointsTo(str, str_origin) and - iter.pointsTo(seq, seq_origin) and - has_string_type(str) and - seq.getClass().isIterable() and - not has_string_type(seq) and - // suppress occurrences from tests - not seq_origin.getScope().getScope*() instanceof TestScope and - not str_origin.getScope().getScope*() instanceof TestScope + loop.getIter().getAFlowNode() = iter and + iter.pointsTo(str, str_origin) and + iter.pointsTo(seq, seq_origin) and + has_string_type(str) and + seq.getClass().isIterable() and + not has_string_type(seq) and + // suppress occurrences from tests + not seq_origin.getScope().getScope*() instanceof TestScope and + not str_origin.getScope().getScope*() instanceof TestScope select loop, - "Iteration over $@, of class " + seq.getClass().getName() + ", may also iterate over $@.", - seq_origin, "sequence", str_origin, "string" + "Iteration over $@, of class " + seq.getClass().getName() + ", may also iterate over $@.", + seq_origin, "sequence", str_origin, "string" diff --git a/python/ql/src/Statements/MismatchInMultipleAssignment.ql b/python/ql/src/Statements/MismatchInMultipleAssignment.ql index 157ddf1270b..f4ba916cb50 100644 --- a/python/ql/src/Statements/MismatchInMultipleAssignment.ql +++ b/python/ql/src/Statements/MismatchInMultipleAssignment.ql @@ -17,45 +17,45 @@ import python private int len(ExprList el) { result = count(el.getAnItem()) } predicate mismatched(Assign a, int lcount, int rcount, Location loc, string sequenceType) { - exists(ExprList l, ExprList r | - ( - a.getATarget().(Tuple).getElts() = l or - a.getATarget().(List).getElts() = l - ) and - ( - a.getValue().(Tuple).getElts() = r and sequenceType = "tuple" - or - a.getValue().(List).getElts() = r and sequenceType = "list" - ) and - loc = a.getValue().getLocation() and - lcount = len(l) and - rcount = len(r) and - lcount != rcount and - not exists(Starred s | l.getAnItem() = s or r.getAnItem() = s) - ) + exists(ExprList l, ExprList r | + ( + a.getATarget().(Tuple).getElts() = l or + a.getATarget().(List).getElts() = l + ) and + ( + a.getValue().(Tuple).getElts() = r and sequenceType = "tuple" + or + a.getValue().(List).getElts() = r and sequenceType = "list" + ) and + loc = a.getValue().getLocation() and + lcount = len(l) and + rcount = len(r) and + lcount != rcount and + not exists(Starred s | l.getAnItem() = s or r.getAnItem() = s) + ) } predicate mismatched_tuple_rhs(Assign a, int lcount, int rcount, Location loc) { - exists(ExprList l, TupleValue r, AstNode origin | - ( - a.getATarget().(Tuple).getElts() = l or - a.getATarget().(List).getElts() = l - ) and - a.getValue().pointsTo(r, origin) and - loc = origin.getLocation() and - lcount = len(l) and - rcount = r.length() and - lcount != rcount and - not exists(Starred s | l.getAnItem() = s) - ) + exists(ExprList l, TupleValue r, AstNode origin | + ( + a.getATarget().(Tuple).getElts() = l or + a.getATarget().(List).getElts() = l + ) and + a.getValue().pointsTo(r, origin) and + loc = origin.getLocation() and + lcount = len(l) and + rcount = r.length() and + lcount != rcount and + not exists(Starred s | l.getAnItem() = s) + ) } from Assign a, int lcount, int rcount, Location loc, string sequenceType where - mismatched(a, lcount, rcount, loc, sequenceType) - or - mismatched_tuple_rhs(a, lcount, rcount, loc) and - sequenceType = "tuple" + mismatched(a, lcount, rcount, loc, sequenceType) + or + mismatched_tuple_rhs(a, lcount, rcount, loc) and + sequenceType = "tuple" select a, - "Left hand side of assignment contains " + lcount + - " variables, but right hand side is a $@ of length " + rcount + ".", loc, sequenceType + "Left hand side of assignment contains " + lcount + + " variables, but right hand side is a $@ of length " + rcount + ".", loc, sequenceType diff --git a/python/ql/src/Statements/ModificationOfLocals.ql b/python/ql/src/Statements/ModificationOfLocals.ql index 8253d51426a..1a76c38c52e 100644 --- a/python/ql/src/Statements/ModificationOfLocals.ql +++ b/python/ql/src/Statements/ModificationOfLocals.ql @@ -15,18 +15,18 @@ import python predicate originIsLocals(ControlFlowNode n) { n.pointsTo(_, _, Value::named("locals").getACall()) } predicate modification_of_locals(ControlFlowNode f) { - originIsLocals(f.(SubscriptNode).getObject()) and - (f.isStore() or f.isDelete()) - or - exists(string mname, AttrNode attr | - attr = f.(CallNode).getFunction() and - originIsLocals(attr.getObject(mname)) - | - mname = "pop" or - mname = "popitem" or - mname = "update" or - mname = "clear" - ) + originIsLocals(f.(SubscriptNode).getObject()) and + (f.isStore() or f.isDelete()) + or + exists(string mname, AttrNode attr | + attr = f.(CallNode).getFunction() and + originIsLocals(attr.getObject(mname)) + | + mname = "pop" or + mname = "popitem" or + mname = "update" or + mname = "clear" + ) } from AstNode a, ControlFlowNode f diff --git a/python/ql/src/Statements/NestedLoopsSameVariable.ql b/python/ql/src/Statements/NestedLoopsSameVariable.ql index 8e966077c16..f57fa9b361a 100644 --- a/python/ql/src/Statements/NestedLoopsSameVariable.ql +++ b/python/ql/src/Statements/NestedLoopsSameVariable.ql @@ -16,15 +16,15 @@ import python predicate loop_variable(For f, Variable v) { f.getTarget().defines(v) } predicate variableUsedInNestedLoops(For inner, For outer, Variable v) { - /* Only treat loops in body as inner loops. Loops in the else clause are ignored. */ - outer.getBody().contains(inner) and - loop_variable(inner, v) and - loop_variable(outer, v) and - /* Ignore cases where there is no use of the variable or the only use is in the inner loop */ - exists(Name n | n.uses(v) and outer.contains(n) and not inner.contains(n)) + /* Only treat loops in body as inner loops. Loops in the else clause are ignored. */ + outer.getBody().contains(inner) and + loop_variable(inner, v) and + loop_variable(outer, v) and + /* Ignore cases where there is no use of the variable or the only use is in the inner loop */ + exists(Name n | n.uses(v) and outer.contains(n) and not inner.contains(n)) } from For inner, For outer, Variable v where variableUsedInNestedLoops(inner, outer, v) select inner, "Nested for statement uses loop variable '" + v.getId() + "' of enclosing $@.", outer, - "for statement" + "for statement" diff --git a/python/ql/src/Statements/NestedLoopsSameVariableWithReuse.ql b/python/ql/src/Statements/NestedLoopsSameVariableWithReuse.ql index 400c43d1d94..de293a7aeea 100644 --- a/python/ql/src/Statements/NestedLoopsSameVariableWithReuse.ql +++ b/python/ql/src/Statements/NestedLoopsSameVariableWithReuse.ql @@ -14,23 +14,23 @@ import python predicate loop_variable_ssa(For f, Variable v, SsaVariable s) { - f.getTarget().getAFlowNode() = s.getDefinition() and v = s.getVariable() + f.getTarget().getAFlowNode() = s.getDefinition() and v = s.getVariable() } predicate variableUsedInNestedLoops(For inner, For outer, Variable v, Name n) { - /* Ignore cases where there is no use of the variable or the only use is in the inner loop. */ - outer.contains(n) and - not inner.contains(n) and - /* Only treat loops in body as inner loops. Loops in the else clause are ignored. */ - outer.getBody().contains(inner) and - exists(SsaVariable s | - loop_variable_ssa(inner, v, s.getAnUltimateDefinition()) and - loop_variable_ssa(outer, v, _) and - s.getAUse().getNode() = n - ) + /* Ignore cases where there is no use of the variable or the only use is in the inner loop. */ + outer.contains(n) and + not inner.contains(n) and + /* Only treat loops in body as inner loops. Loops in the else clause are ignored. */ + outer.getBody().contains(inner) and + exists(SsaVariable s | + loop_variable_ssa(inner, v, s.getAnUltimateDefinition()) and + loop_variable_ssa(outer, v, _) and + s.getAUse().getNode() = n + ) } from For inner, For outer, Variable v, Name n where variableUsedInNestedLoops(inner, outer, v, n) select inner, "Nested for statement $@ loop variable '" + v.getId() + "' of enclosing $@.", n, - "uses", outer, "for statement" + "uses", outer, "for statement" diff --git a/python/ql/src/Statements/NonIteratorInForLoop.ql b/python/ql/src/Statements/NonIteratorInForLoop.ql index 85982ccc030..0df5c30a77d 100644 --- a/python/ql/src/Statements/NonIteratorInForLoop.ql +++ b/python/ql/src/Statements/NonIteratorInForLoop.ql @@ -15,11 +15,11 @@ import python from For loop, ControlFlowNode iter, Value v, ClassValue t, ControlFlowNode origin where - loop.getIter().getAFlowNode() = iter and - iter.pointsTo(_, v, origin) and - v.getClass() = t and - not t.isIterable() and - not t.failedInference(_) and - not v = Value::named("None") and - not t.isDescriptorType() + loop.getIter().getAFlowNode() = iter and + iter.pointsTo(_, v, origin) and + v.getClass() = t and + not t.isIterable() and + not t.failedInference(_) and + not v = Value::named("None") and + not t.isDescriptorType() select loop, "$@ of class '$@' may be used in for-loop.", origin, "Non-iterable", t, t.getName() diff --git a/python/ql/src/Statements/RedundantAssignment.ql b/python/ql/src/Statements/RedundantAssignment.ql index 87b03cd5989..b56dea3eecb 100644 --- a/python/ql/src/Statements/RedundantAssignment.ql +++ b/python/ql/src/Statements/RedundantAssignment.ql @@ -14,27 +14,27 @@ import python predicate assignment(AssignStmt a, Expr left, Expr right) { - a.getATarget() = left and a.getValue() = right + a.getATarget() = left and a.getValue() = right } predicate corresponding(Expr left, Expr right) { - assignment(_, left, right) - or - exists(Attribute la, Attribute ra | - corresponding(la, ra) and - left = la.getObject() and - right = ra.getObject() - ) + assignment(_, left, right) + or + exists(Attribute la, Attribute ra | + corresponding(la, ra) and + left = la.getObject() and + right = ra.getObject() + ) } predicate same_value(Expr left, Expr right) { - same_name(left, right) - or - same_attribute(left, right) + same_name(left, right) + or + same_attribute(left, right) } predicate maybe_defined_in_outer_scope(Name n) { - exists(SsaVariable v | v.getAUse().getNode() = n | v.maybeUndefined()) + exists(SsaVariable v | v.getAUse().getNode() = n | v.maybeUndefined()) } /* @@ -50,54 +50,54 @@ predicate maybe_defined_in_outer_scope(Name n) { predicate isBuiltin(string name) { exists(Value v | v = Value::named(name) and v.isBuiltin()) } predicate same_name(Name n1, Name n2) { - corresponding(n1, n2) and - n1.getVariable() = n2.getVariable() and - not isBuiltin(n1.getId()) and - not maybe_defined_in_outer_scope(n2) + corresponding(n1, n2) and + n1.getVariable() = n2.getVariable() and + not isBuiltin(n1.getId()) and + not maybe_defined_in_outer_scope(n2) } ClassValue value_type(Attribute a) { a.getObject().pointsTo().getClass() = result } predicate is_property_access(Attribute a) { - value_type(a).lookup(a.getName()) instanceof PropertyValue + value_type(a).lookup(a.getName()) instanceof PropertyValue } predicate same_attribute(Attribute a1, Attribute a2) { - corresponding(a1, a2) and - a1.getName() = a2.getName() and - same_value(a1.getObject(), a2.getObject()) and - exists(value_type(a1)) and - not is_property_access(a1) + corresponding(a1, a2) and + a1.getName() = a2.getName() and + same_value(a1.getObject(), a2.getObject()) and + exists(value_type(a1)) and + not is_property_access(a1) } int pyflakes_commented_line(File file) { - exists(Comment c | c.getText().toLowerCase().matches("%pyflakes%") | - c.getLocation().hasLocationInfo(file.getAbsolutePath(), result, _, _, _) - ) + exists(Comment c | c.getText().toLowerCase().matches("%pyflakes%") | + c.getLocation().hasLocationInfo(file.getAbsolutePath(), result, _, _, _) + ) } predicate pyflakes_commented(AssignStmt assignment) { - exists(Location loc | - assignment.getLocation() = loc and - loc.getStartLine() = pyflakes_commented_line(loc.getFile()) - ) + exists(Location loc | + assignment.getLocation() = loc and + loc.getStartLine() = pyflakes_commented_line(loc.getFile()) + ) } predicate side_effecting_lhs(Attribute lhs) { - exists(ClassValue cls, ClassValue decl | - lhs.getObject().pointsTo().getClass() = cls and - decl = cls.getASuperType() and - not decl.isBuiltin() - | - decl.declaresAttribute("__setattr__") - ) + exists(ClassValue cls, ClassValue decl | + lhs.getObject().pointsTo().getClass() = cls and + decl = cls.getASuperType() and + not decl.isBuiltin() + | + decl.declaresAttribute("__setattr__") + ) } from AssignStmt a, Expr left, Expr right where - assignment(a, left, right) and - same_value(left, right) and - // some people use self-assignment to shut Pyflakes up, such as `ok = ok # Pyflakes` - not pyflakes_commented(a) and - not side_effecting_lhs(left) + assignment(a, left, right) and + same_value(left, right) and + // some people use self-assignment to shut Pyflakes up, such as `ok = ok # Pyflakes` + not pyflakes_commented(a) and + not side_effecting_lhs(left) select a, "This assignment assigns a variable to itself." diff --git a/python/ql/src/Statements/ReturnOrYieldOutsideFunction.ql b/python/ql/src/Statements/ReturnOrYieldOutsideFunction.ql index ff6b7f379d3..a940dc60123 100644 --- a/python/ql/src/Statements/ReturnOrYieldOutsideFunction.ql +++ b/python/ql/src/Statements/ReturnOrYieldOutsideFunction.ql @@ -14,12 +14,12 @@ import python from AstNode node, string kind where - not node.getScope() instanceof Function and - ( - node instanceof Return and kind = "return" - or - node instanceof Yield and kind = "yield" - or - node instanceof YieldFrom and kind = "yield from" - ) + not node.getScope() instanceof Function and + ( + node instanceof Return and kind = "return" + or + node instanceof Yield and kind = "yield" + or + node instanceof YieldFrom and kind = "yield from" + ) select node, "'" + kind + "' is used outside a function." diff --git a/python/ql/src/Statements/ShouldUseWithStatement.ql b/python/ql/src/Statements/ShouldUseWithStatement.ql index 9be28d5ceec..b453f971e86 100644 --- a/python/ql/src/Statements/ShouldUseWithStatement.ql +++ b/python/ql/src/Statements/ShouldUseWithStatement.ql @@ -17,23 +17,23 @@ import python predicate calls_close(Call c) { exists(Attribute a | c.getFunc() = a and a.getName() = "close") } predicate only_stmt_in_finally(Try t, Call c) { - exists(ExprStmt s | - t.getAFinalstmt() = s and s.getValue() = c and strictcount(t.getAFinalstmt()) = 1 - ) + exists(ExprStmt s | + t.getAFinalstmt() = s and s.getValue() = c and strictcount(t.getAFinalstmt()) = 1 + ) } predicate points_to_context_manager(ControlFlowNode f, ClassValue cls) { - forex(Value v | f.pointsTo(v) | v.getClass() = cls) and - cls.isContextManager() + forex(Value v | f.pointsTo(v) | v.getClass() = cls) and + cls.isContextManager() } from Call close, Try t, ClassValue cls where - only_stmt_in_finally(t, close) and - calls_close(close) and - exists(ControlFlowNode f | f = close.getFunc().getAFlowNode().(AttrNode).getObject() | - points_to_context_manager(f, cls) - ) + only_stmt_in_finally(t, close) and + calls_close(close) and + exists(ControlFlowNode f | f = close.getFunc().getAFlowNode().(AttrNode).getObject() | + points_to_context_manager(f, cls) + ) select close, - "Instance of context-manager class $@ is closed in a finally block. Consider using 'with' statement.", - cls, cls.getName() + "Instance of context-manager class $@ is closed in a finally block. Consider using 'with' statement.", + cls, cls.getName() diff --git a/python/ql/src/Statements/SideEffectInAssert.ql b/python/ql/src/Statements/SideEffectInAssert.ql index a8ed146b16e..f96e04243af 100644 --- a/python/ql/src/Statements/SideEffectInAssert.ql +++ b/python/ql/src/Statements/SideEffectInAssert.ql @@ -14,37 +14,37 @@ import python predicate func_with_side_effects(Expr e) { - exists(string name | name = e.(Attribute).getName() or name = e.(Name).getId() | - name = "print" or - name = "write" or - name = "append" or - name = "pop" or - name = "remove" or - name = "discard" or - name = "delete" or - name = "close" or - name = "open" or - name = "exit" - ) + exists(string name | name = e.(Attribute).getName() or name = e.(Name).getId() | + name = "print" or + name = "write" or + name = "append" or + name = "pop" or + name = "remove" or + name = "discard" or + name = "delete" or + name = "close" or + name = "open" or + name = "exit" + ) } predicate call_with_side_effect(Call e) { - e.getAFlowNode() = Value::named("subprocess.call").getACall() - or - e.getAFlowNode() = Value::named("subprocess.check_call").getACall() - or - e.getAFlowNode() = Value::named("subprocess.check_output").getACall() + e.getAFlowNode() = Value::named("subprocess.call").getACall() + or + e.getAFlowNode() = Value::named("subprocess.check_call").getACall() + or + e.getAFlowNode() = Value::named("subprocess.check_output").getACall() } predicate probable_side_effect(Expr e) { - // Only consider explicit yields, not artificial ones in comprehensions - e instanceof Yield and not exists(Comp c | c.contains(e)) - or - e instanceof YieldFrom - or - e instanceof Call and func_with_side_effects(e.(Call).getFunc()) - or - e instanceof Call and call_with_side_effect(e) + // Only consider explicit yields, not artificial ones in comprehensions + e instanceof Yield and not exists(Comp c | c.contains(e)) + or + e instanceof YieldFrom + or + e instanceof Call and func_with_side_effects(e.(Call).getFunc()) + or + e instanceof Call and call_with_side_effect(e) } from Assert a, Expr e diff --git a/python/ql/src/Statements/StatementNoEffect.ql b/python/ql/src/Statements/StatementNoEffect.ql index dde0b5d7cde..48129bc38d5 100644 --- a/python/ql/src/Statements/StatementNoEffect.ql +++ b/python/ql/src/Statements/StatementNoEffect.ql @@ -14,34 +14,34 @@ import python predicate understood_attribute(Attribute attr, ClassValue cls, ClassValue attr_cls) { - exists(string name | attr.getName() = name | - attr.getObject().pointsTo().getClass() = cls and - cls.attr(name).getClass() = attr_cls - ) + exists(string name | attr.getName() = name | + attr.getObject().pointsTo().getClass() = cls and + cls.attr(name).getClass() = attr_cls + ) } /* Conservative estimate of whether attribute lookup has a side effect */ predicate side_effecting_attribute(Attribute attr) { - exists(ClassValue cls, ClassValue attr_cls | - understood_attribute(attr, cls, attr_cls) and - side_effecting_descriptor_type(attr_cls) - ) + exists(ClassValue cls, ClassValue attr_cls | + understood_attribute(attr, cls, attr_cls) and + side_effecting_descriptor_type(attr_cls) + ) } predicate maybe_side_effecting_attribute(Attribute attr) { - not understood_attribute(attr, _, _) and not attr.pointsTo(_) - or - side_effecting_attribute(attr) + not understood_attribute(attr, _, _) and not attr.pointsTo(_) + or + side_effecting_attribute(attr) } predicate side_effecting_descriptor_type(ClassValue descriptor) { - descriptor.isDescriptorType() and - // Technically all descriptor gets have side effects, - // but some are indicative of a missing call and - // we want to treat them as having no effect. - not descriptor = ClassValue::functionType() and - not descriptor = ClassValue::staticmethod() and - not descriptor = ClassValue::classmethod() + descriptor.isDescriptorType() and + // Technically all descriptor gets have side effects, + // but some are indicative of a missing call and + // we want to treat them as having no effect. + not descriptor = ClassValue::functionType() and + not descriptor = ClassValue::staticmethod() and + not descriptor = ClassValue::classmethod() } /** @@ -49,87 +49,87 @@ predicate side_effecting_descriptor_type(ClassValue descriptor) { * side-effecting unless we know otherwise. */ predicate side_effecting_binary(Expr b) { - exists(Expr sub, ClassValue cls, string method_name | - binary_operator_special_method(b, sub, cls, method_name) - or - comparison_special_method(b, sub, cls, method_name) - | - method_name = special_method() and - cls.hasAttribute(method_name) and - not exists(ClassValue declaring | - declaring.declaresAttribute(method_name) and - declaring = cls.getASuperType() and - declaring.isBuiltin() and - not declaring = ClassValue::object() - ) + exists(Expr sub, ClassValue cls, string method_name | + binary_operator_special_method(b, sub, cls, method_name) + or + comparison_special_method(b, sub, cls, method_name) + | + method_name = special_method() and + cls.hasAttribute(method_name) and + not exists(ClassValue declaring | + declaring.declaresAttribute(method_name) and + declaring = cls.getASuperType() and + declaring.isBuiltin() and + not declaring = ClassValue::object() ) + ) } pragma[nomagic] private predicate binary_operator_special_method( - BinaryExpr b, Expr sub, ClassValue cls, string method_name + BinaryExpr b, Expr sub, ClassValue cls, string method_name ) { - method_name = special_method() and - sub = b.getLeft() and - method_name = b.getOp().getSpecialMethodName() and - sub.pointsTo().getClass() = cls + method_name = special_method() and + sub = b.getLeft() and + method_name = b.getOp().getSpecialMethodName() and + sub.pointsTo().getClass() = cls } pragma[nomagic] private predicate comparison_special_method(Compare b, Expr sub, ClassValue cls, string method_name) { - exists(Cmpop op | - b.compares(sub, op, _) and - method_name = op.getSpecialMethodName() - ) and - sub.pointsTo().getClass() = cls + exists(Cmpop op | + b.compares(sub, op, _) and + method_name = op.getSpecialMethodName() + ) and + sub.pointsTo().getClass() = cls } private string special_method() { - result = any(Cmpop c).getSpecialMethodName() - or - result = any(BinaryExpr b).getOp().getSpecialMethodName() + result = any(Cmpop c).getSpecialMethodName() + or + result = any(BinaryExpr b).getOp().getSpecialMethodName() } predicate is_notebook(File f) { - exists(Comment c | c.getLocation().getFile() = f | - c.getText().regexpMatch("#\\s*.+\\s*") - ) + exists(Comment c | c.getLocation().getFile() = f | + c.getText().regexpMatch("#\\s*.+\\s*") + ) } /** Expression (statement) in a jupyter/ipython notebook */ predicate in_notebook(Expr e) { is_notebook(e.getScope().(Module).getFile()) } FunctionValue assertRaises() { - result = Value::named("unittest.TestCase").(ClassValue).lookup("assertRaises") + result = Value::named("unittest.TestCase").(ClassValue).lookup("assertRaises") } /** Holds if expression `e` is in a `with` block that tests for exceptions being raised. */ predicate in_raises_test(Expr e) { - exists(With w | - w.contains(e) and - w.getContextExpr() = assertRaises().getACall().getNode() - ) + exists(With w | + w.contains(e) and + w.getContextExpr() = assertRaises().getACall().getNode() + ) } /** Holds if expression has the form of a Python 2 `print >> out, ...` statement */ predicate python2_print(Expr e) { - e.(BinaryExpr).getLeft().(Name).getId() = "print" and - e.(BinaryExpr).getOp() instanceof RShift - or - python2_print(e.(Tuple).getElt(0)) + e.(BinaryExpr).getLeft().(Name).getId() = "print" and + e.(BinaryExpr).getOp() instanceof RShift + or + python2_print(e.(Tuple).getElt(0)) } predicate no_effect(Expr e) { - // strings can be used as comments - not e instanceof StrConst and - not e.hasSideEffects() and - forall(Expr sub | sub = e.getASubExpression*() | - not side_effecting_binary(sub) and - not maybe_side_effecting_attribute(sub) - ) and - not in_notebook(e) and - not in_raises_test(e) and - not python2_print(e) + // strings can be used as comments + not e instanceof StrConst and + not e.hasSideEffects() and + forall(Expr sub | sub = e.getASubExpression*() | + not side_effecting_binary(sub) and + not maybe_side_effecting_attribute(sub) + ) and + not in_notebook(e) and + not in_raises_test(e) and + not python2_print(e) } from ExprStmt stmt diff --git a/python/ql/src/Statements/StringConcatenationInLoop.ql b/python/ql/src/Statements/StringConcatenationInLoop.ql index f225e27cdcd..563a42e5462 100644 --- a/python/ql/src/Statements/StringConcatenationInLoop.ql +++ b/python/ql/src/Statements/StringConcatenationInLoop.ql @@ -13,14 +13,14 @@ import python predicate string_concat_in_loop(BinaryExpr b) { - b.getOp() instanceof Add and - exists(SsaVariable d, SsaVariable u, BinaryExprNode add | - add.getNode() = b and d = u.getAnUltimateDefinition() - | - d.getDefinition().(DefinitionNode).getValue() = add and - u.getAUse() = add.getAnOperand() and - add.getAnOperand().pointsTo().getClass() = ClassValue::str() - ) + b.getOp() instanceof Add and + exists(SsaVariable d, SsaVariable u, BinaryExprNode add | + add.getNode() = b and d = u.getAnUltimateDefinition() + | + d.getDefinition().(DefinitionNode).getValue() = add and + u.getAUse() = add.getAnOperand() and + add.getAnOperand().pointsTo().getClass() = ClassValue::str() + ) } from BinaryExpr b, Stmt s diff --git a/python/ql/src/Statements/TopLevelPrint.ql b/python/ql/src/Statements/TopLevelPrint.ql index d818d80a251..b2d111cce1f 100644 --- a/python/ql/src/Statements/TopLevelPrint.ql +++ b/python/ql/src/Statements/TopLevelPrint.ql @@ -14,27 +14,27 @@ import python predicate main_eq_name(If i) { - exists(Name n, StrConst m, Compare c | - i.getTest() = c and - c.getLeft() = n and - c.getAComparator() = m and - n.getId() = "__name__" and - m.getText() = "__main__" - ) + exists(Name n, StrConst m, Compare c | + i.getTest() = c and + c.getLeft() = n and + c.getAComparator() = m and + n.getId() = "__name__" and + m.getText() = "__main__" + ) } predicate is_print_stmt(Stmt s) { - s instanceof Print - or - exists(ExprStmt e, Call c, Name n | - e = s and c = e.getValue() and n = c.getFunc() and n.getId() = "print" - ) + s instanceof Print + or + exists(ExprStmt e, Call c, Name n | + e = s and c = e.getValue() and n = c.getFunc() and n.getId() = "print" + ) } from Stmt p where - is_print_stmt(p) and - // TODO: Need to discuss how we would like to handle ModuleObject.getKind in the glorious future - exists(ModuleValue m | m.getScope() = p.getScope() and m.isUsedAsModule()) and - not exists(If i | main_eq_name(i) and i.getASubStatement().getASubStatement*() = p) + is_print_stmt(p) and + // TODO: Need to discuss how we would like to handle ModuleObject.getKind in the glorious future + exists(ModuleValue m | m.getScope() = p.getScope() and m.isUsedAsModule()) and + not exists(If i | main_eq_name(i) and i.getASubStatement().getASubStatement*() = p) select p, "Print statement may execute during import." diff --git a/python/ql/src/Statements/UnnecessaryDelete.ql b/python/ql/src/Statements/UnnecessaryDelete.ql index d10bcc2ed20..c9e047c307d 100644 --- a/python/ql/src/Statements/UnnecessaryDelete.ql +++ b/python/ql/src/Statements/UnnecessaryDelete.ql @@ -16,17 +16,17 @@ import python from Delete del, Expr e, Function f where - f.getLastStatement() = del and - e = del.getATarget() and - f.containsInScope(e) and - not e instanceof Subscript and - not e instanceof Attribute and - not exists(Stmt s | s.(While).contains(del) or s.(For).contains(del)) and - // False positive: calling `sys.exc_info` within a function results in a - // reference cycle, and an explicit call to `del` helps break this cycle. - not exists(FunctionValue ex | - ex = Value::named("sys.exc_info") and - ex.getACall().getScope() = f - ) + f.getLastStatement() = del and + e = del.getATarget() and + f.containsInScope(e) and + not e instanceof Subscript and + not e instanceof Attribute and + not exists(Stmt s | s.(While).contains(del) or s.(For).contains(del)) and + // False positive: calling `sys.exc_info` within a function results in a + // reference cycle, and an explicit call to `del` helps break this cycle. + not exists(FunctionValue ex | + ex = Value::named("sys.exc_info") and + ex.getACall().getScope() = f + ) select del, "Unnecessary deletion of local variable $@ in function $@.", e.getLocation(), - e.toString(), f.getLocation(), f.getName() + e.toString(), f.getLocation(), f.getName() diff --git a/python/ql/src/Statements/UnnecessaryElseClause.ql b/python/ql/src/Statements/UnnecessaryElseClause.ql index 8884b06e740..35ac254b276 100644 --- a/python/ql/src/Statements/UnnecessaryElseClause.ql +++ b/python/ql/src/Statements/UnnecessaryElseClause.ql @@ -14,11 +14,11 @@ import python from Stmt loop, StmtList body, StmtList clause, string kind where - ( - exists(For f | f = loop | clause = f.getOrelse() and body = f.getBody() and kind = "for") - or - exists(While w | w = loop | clause = w.getOrelse() and body = w.getBody() and kind = "while") - ) and - not exists(Break b | body.contains(b)) + ( + exists(For f | f = loop | clause = f.getOrelse() and body = f.getBody() and kind = "for") + or + exists(While w | w = loop | clause = w.getOrelse() and body = w.getBody() and kind = "while") + ) and + not exists(Break b | body.contains(b)) select loop, - "This '" + kind + "' statement has a redundant 'else' as no 'break' is present in the body." + "This '" + kind + "' statement has a redundant 'else' as no 'break' is present in the body." diff --git a/python/ql/src/Statements/UnnecessaryPass.ql b/python/ql/src/Statements/UnnecessaryPass.ql index fe0e0171930..215fac5192e 100644 --- a/python/ql/src/Statements/UnnecessaryPass.ql +++ b/python/ql/src/Statements/UnnecessaryPass.ql @@ -13,20 +13,20 @@ import python predicate is_doc_string(ExprStmt s) { - s.getValue() instanceof Unicode or s.getValue() instanceof Bytes + s.getValue() instanceof Unicode or s.getValue() instanceof Bytes } predicate has_doc_string(StmtList stmts) { - stmts.getParent() instanceof Scope and - is_doc_string(stmts.getItem(0)) + stmts.getParent() instanceof Scope and + is_doc_string(stmts.getItem(0)) } from Pass p, StmtList list where - list.getAnItem() = p and - ( - strictcount(list.getAnItem()) = 2 and not has_doc_string(list) - or - strictcount(list.getAnItem()) > 2 - ) + list.getAnItem() = p and + ( + strictcount(list.getAnItem()) = 2 and not has_doc_string(list) + or + strictcount(list.getAnItem()) > 2 + ) select p, "Unnecessary 'pass' statement." diff --git a/python/ql/src/Statements/UnreachableCode.ql b/python/ql/src/Statements/UnreachableCode.ql index 88f8b9427a2..04e9f79c415 100644 --- a/python/ql/src/Statements/UnreachableCode.ql +++ b/python/ql/src/Statements/UnreachableCode.ql @@ -14,49 +14,49 @@ import python predicate typing_import(ImportingStmt is) { - exists(Module m | - is.getScope() = m and - exists(TypeHintComment tc | tc.getLocation().getFile() = m.getFile()) - ) + exists(Module m | + is.getScope() = m and + exists(TypeHintComment tc | tc.getLocation().getFile() = m.getFile()) + ) } /** Holds if `s` contains the only `yield` in scope */ predicate unique_yield(Stmt s) { - exists(Yield y | s.contains(y)) and - exists(Function f | - f = s.getScope() and - strictcount(Yield y | f.containsInScope(y)) = 1 - ) + exists(Yield y | s.contains(y)) and + exists(Function f | + f = s.getScope() and + strictcount(Yield y | f.containsInScope(y)) = 1 + ) } /** Holds if `contextlib.suppress` may be used in the same scope as `s` */ predicate suppression_in_scope(Stmt s) { - exists(With w | - w.getContextExpr().(Call).getFunc().pointsTo(Value::named("contextlib.suppress")) and - w.getScope() = s.getScope() - ) + exists(With w | + w.getContextExpr().(Call).getFunc().pointsTo(Value::named("contextlib.suppress")) and + w.getScope() = s.getScope() + ) } /** Holds if `s` is a statement that raises an exception at the end of an if-elif-else chain. */ predicate marks_an_impossible_else_branch(Stmt s) { - exists(If i | i.getOrelse().getItem(0) = s | - s.(Assert).getTest() instanceof False - or - s instanceof Raise - ) + exists(If i | i.getOrelse().getItem(0) = s | + s.(Assert).getTest() instanceof False + or + s instanceof Raise + ) } predicate reportable_unreachable(Stmt s) { - s.isUnreachable() and - not typing_import(s) and - not suppression_in_scope(s) and - not exists(Stmt other | other.isUnreachable() | - other.contains(s) - or - exists(StmtList l, int i, int j | l.getItem(i) = other and l.getItem(j) = s and i < j) - ) and - not unique_yield(s) and - not marks_an_impossible_else_branch(s) + s.isUnreachable() and + not typing_import(s) and + not suppression_in_scope(s) and + not exists(Stmt other | other.isUnreachable() | + other.contains(s) + or + exists(StmtList l, int i, int j | l.getItem(i) = other and l.getItem(j) = s and i < j) + ) and + not unique_yield(s) and + not marks_an_impossible_else_branch(s) } from Stmt s diff --git a/python/ql/src/Statements/UnusedExceptionObject.ql b/python/ql/src/Statements/UnusedExceptionObject.ql index 32b59113c5b..6c19f82d60f 100644 --- a/python/ql/src/Statements/UnusedExceptionObject.ql +++ b/python/ql/src/Statements/UnusedExceptionObject.ql @@ -14,7 +14,7 @@ import python from Call call, ClassValue ex where - call.getFunc().pointsTo(ex) and - ex.getASuperType() = ClassValue::exception() and - exists(ExprStmt s | s.getValue() = call) + call.getFunc().pointsTo(ex) and + ex.getASuperType() = ClassValue::exception() and + exists(ExprStmt s | s.getValue() = call) select call, "Instantiating an exception, but not raising it, has no effect" diff --git a/python/ql/src/Statements/UseOfExit.ql b/python/ql/src/Statements/UseOfExit.ql index 31e0e51ab39..4a2730b7753 100644 --- a/python/ql/src/Statements/UseOfExit.ql +++ b/python/ql/src/Statements/UseOfExit.ql @@ -14,5 +14,5 @@ import python from CallNode call, string name where call.getFunction().pointsTo(Value::siteQuitter(name)) select call, - "The '" + name + - "' site.Quitter object may not exist if the 'site' module is not loaded or is modified." + "The '" + name + + "' site.Quitter object may not exist if the 'site' module is not loaded or is modified." diff --git a/python/ql/src/Testing/ImpreciseAssert.ql b/python/ql/src/Testing/ImpreciseAssert.ql index a3d7e1ae7c3..121ec6024e8 100644 --- a/python/ql/src/Testing/ImpreciseAssert.ql +++ b/python/ql/src/Testing/ImpreciseAssert.ql @@ -14,79 +14,79 @@ import python /* Helper predicate for CallToAssertOnComparison class */ predicate callToAssertOnComparison(Call call, string assertName, Cmpop op) { - call.getFunc().(Attribute).getName() = assertName and - (assertName = "assertTrue" or assertName = "assertFalse") and - exists(Compare cmp | - cmp = call.getArg(0) and - /* Exclude complex comparisons like: a < b < c */ - not exists(cmp.getOp(1)) and - op = cmp.getOp(0) - ) + call.getFunc().(Attribute).getName() = assertName and + (assertName = "assertTrue" or assertName = "assertFalse") and + exists(Compare cmp | + cmp = call.getArg(0) and + /* Exclude complex comparisons like: a < b < c */ + not exists(cmp.getOp(1)) and + op = cmp.getOp(0) + ) } class CallToAssertOnComparison extends Call { - CallToAssertOnComparison() { callToAssertOnComparison(this, _, _) } + CallToAssertOnComparison() { callToAssertOnComparison(this, _, _) } - Cmpop getOperator() { callToAssertOnComparison(this, _, result) } + Cmpop getOperator() { callToAssertOnComparison(this, _, result) } - string getMethodName() { callToAssertOnComparison(this, result, _) } + string getMethodName() { callToAssertOnComparison(this, result, _) } - string getBetterName() { - exists(Cmpop op | - callToAssertOnComparison(this, "assertTrue", op) and - ( - op instanceof Eq and result = "assertEqual" - or - op instanceof NotEq and result = "assertNotEqual" - or - op instanceof Lt and result = "assertLess" - or - op instanceof LtE and result = "assertLessEqual" - or - op instanceof Gt and result = "assertGreater" - or - op instanceof GtE and result = "assertGreaterEqual" - or - op instanceof In and result = "assertIn" - or - op instanceof NotIn and result = "assertNotIn" - or - op instanceof Is and result = "assertIs" - or - op instanceof IsNot and result = "assertIsNot" - ) - or - callToAssertOnComparison(this, "assertFalse", op) and - ( - op instanceof NotEq and result = "assertEqual" - or - op instanceof Eq and result = "assertNotEqual" - or - op instanceof GtE and result = "assertLess" - or - op instanceof Gt and result = "assertLessEqual" - or - op instanceof LtE and result = "assertGreater" - or - op instanceof Lt and result = "assertGreaterEqual" - or - op instanceof NotIn and result = "assertIn" - or - op instanceof In and result = "assertNotIn" - or - op instanceof IsNot and result = "assertIs" - or - op instanceof Is and result = "assertIsNot" - ) - ) - } + string getBetterName() { + exists(Cmpop op | + callToAssertOnComparison(this, "assertTrue", op) and + ( + op instanceof Eq and result = "assertEqual" + or + op instanceof NotEq and result = "assertNotEqual" + or + op instanceof Lt and result = "assertLess" + or + op instanceof LtE and result = "assertLessEqual" + or + op instanceof Gt and result = "assertGreater" + or + op instanceof GtE and result = "assertGreaterEqual" + or + op instanceof In and result = "assertIn" + or + op instanceof NotIn and result = "assertNotIn" + or + op instanceof Is and result = "assertIs" + or + op instanceof IsNot and result = "assertIsNot" + ) + or + callToAssertOnComparison(this, "assertFalse", op) and + ( + op instanceof NotEq and result = "assertEqual" + or + op instanceof Eq and result = "assertNotEqual" + or + op instanceof GtE and result = "assertLess" + or + op instanceof Gt and result = "assertLessEqual" + or + op instanceof LtE and result = "assertGreater" + or + op instanceof Lt and result = "assertGreaterEqual" + or + op instanceof NotIn and result = "assertIn" + or + op instanceof In and result = "assertNotIn" + or + op instanceof IsNot and result = "assertIs" + or + op instanceof Is and result = "assertIsNot" + ) + ) + } } from CallToAssertOnComparison call where - /* Exclude cases where an explicit message is provided*/ - not exists(call.getArg(1)) + /* Exclude cases where an explicit message is provided*/ + not exists(call.getArg(1)) select call, - call.getMethodName() + "(a " + call.getOperator().getSymbol() + " b) " + - "cannot provide an informative message. Using " + call.getBetterName() + - "(a, b) instead will give more informative messages." + call.getMethodName() + "(a " + call.getOperator().getSymbol() + " b) " + + "cannot provide an informative message. Using " + call.getBetterName() + + "(a, b) instead will give more informative messages." diff --git a/python/ql/src/Testing/Mox.qll b/python/ql/src/Testing/Mox.qll index 0c26d2ef899..a131ca7eeca 100644 --- a/python/ql/src/Testing/Mox.qll +++ b/python/ql/src/Testing/Mox.qll @@ -2,15 +2,15 @@ import python /** Whether `mox` or `.StubOutWithMock()` is used in thin module `m`. */ predicate useOfMoxInModule(Module m) { - exists(ModuleObject mox | mox.getName() = "mox" or mox.getName() = "mox3.mox" | - exists(ControlFlowNode use | - use.refersTo(mox) and - use.getScope().getEnclosingModule() = m - ) - ) - or - exists(Call call | - call.getFunc().(Attribute).getName() = "StubOutWithMock" and - call.getEnclosingModule() = m + exists(ModuleObject mox | mox.getName() = "mox" or mox.getName() = "mox3.mox" | + exists(ControlFlowNode use | + use.refersTo(mox) and + use.getScope().getEnclosingModule() = m ) + ) + or + exists(Call call | + call.getFunc().(Attribute).getName() = "StubOutWithMock" and + call.getEnclosingModule() = m + ) } diff --git a/python/ql/src/Variables/Definition.qll b/python/ql/src/Variables/Definition.qll index e8bc95ef79c..76f3986d605 100644 --- a/python/ql/src/Variables/Definition.qll +++ b/python/ql/src/Variables/Definition.qll @@ -4,100 +4,100 @@ import python * A control-flow node that defines a variable */ class Definition extends NameNode, DefinitionNode { - /** - * The variable defined by this control-flow node. - */ - Variable getVariable() { this.defines(result) } + /** + * The variable defined by this control-flow node. + */ + Variable getVariable() { this.defines(result) } - /** - * The SSA variable corresponding to the current definition. Since SSA variables - * are only generated for definitions with at least one use, not all definitions - * will have an SSA variable. - */ - SsaVariable getSsaVariable() { result.getDefinition() = this } + /** + * The SSA variable corresponding to the current definition. Since SSA variables + * are only generated for definitions with at least one use, not all definitions + * will have an SSA variable. + */ + SsaVariable getSsaVariable() { result.getDefinition() = this } - /** - * The index of this definition in its basic block. - */ - private int indexInBB(BasicBlock bb, Variable v) { - v = this.getVariable() and - this = bb.getNode(result) - } + /** + * The index of this definition in its basic block. + */ + private int indexInBB(BasicBlock bb, Variable v) { + v = this.getVariable() and + this = bb.getNode(result) + } - /** - * The rank of this definition among other definitions of the same variable - * in its basic block. The first definition will have rank 1, and subsequent - * definitions will have sequentially increasing ranks. - */ - private int rankInBB(BasicBlock bb, Variable v) { - exists(int defIdx | defIdx = this.indexInBB(bb, v) | - defIdx = rank[result](int idx, Definition def | idx = def.indexInBB(bb, v) | idx) - ) - } + /** + * The rank of this definition among other definitions of the same variable + * in its basic block. The first definition will have rank 1, and subsequent + * definitions will have sequentially increasing ranks. + */ + private int rankInBB(BasicBlock bb, Variable v) { + exists(int defIdx | defIdx = this.indexInBB(bb, v) | + defIdx = rank[result](int idx, Definition def | idx = def.indexInBB(bb, v) | idx) + ) + } - /** Is this definition the first in its basic block for its variable? */ - predicate isFirst() { this.rankInBB(_, _) = 1 } + /** Is this definition the first in its basic block for its variable? */ + predicate isFirst() { this.rankInBB(_, _) = 1 } - /** Is this definition the last in its basic block for its variable? */ - predicate isLast() { - exists(BasicBlock b, Variable v | - this.rankInBB(b, v) = max(Definition other | any() | other.rankInBB(b, v)) - ) - } + /** Is this definition the last in its basic block for its variable? */ + predicate isLast() { + exists(BasicBlock b, Variable v | + this.rankInBB(b, v) = max(Definition other | any() | other.rankInBB(b, v)) + ) + } - /** - * Is this definition unused? A definition is unused if the value it provides - * is not read anywhere. - */ - predicate isUnused() { - // SSA variables only exist for definitions that have at least one use. - not exists(this.getSsaVariable()) and - // If a variable is used in a foreign scope, all bets are off. - not this.getVariable().escapes() and - // Global variables don't have SSA variables unless the scope is global. - this.getVariable().getScope() = this.getScope() and - // A call to locals() or vars() in the variable scope counts as a use - not exists(Function f, Call c, string locals_or_vars | - c.getScope() = f and - this.getScope() = f and - c.getFunc().(Name).getId() = locals_or_vars - | - locals_or_vars = "locals" or locals_or_vars = "vars" - ) - } + /** + * Is this definition unused? A definition is unused if the value it provides + * is not read anywhere. + */ + predicate isUnused() { + // SSA variables only exist for definitions that have at least one use. + not exists(this.getSsaVariable()) and + // If a variable is used in a foreign scope, all bets are off. + not this.getVariable().escapes() and + // Global variables don't have SSA variables unless the scope is global. + this.getVariable().getScope() = this.getScope() and + // A call to locals() or vars() in the variable scope counts as a use + not exists(Function f, Call c, string locals_or_vars | + c.getScope() = f and + this.getScope() = f and + c.getFunc().(Name).getId() = locals_or_vars + | + locals_or_vars = "locals" or locals_or_vars = "vars" + ) + } - /** - * An immediate re-definition of this definition's variable. - */ - Definition getARedef() { - result != this and - exists(Variable var | var = this.getVariable() and var = result.getVariable() | - // Definitions in different basic blocks. - this.isLast() and - reaches_without_redef(var, this.getBasicBlock(), result.getBasicBlock()) and - result.isFirst() - ) - or - // Definitions in the same basic block. - exists(BasicBlock common, Variable var | - this.rankInBB(common, var) + 1 = result.rankInBB(common, var) - ) - } + /** + * An immediate re-definition of this definition's variable. + */ + Definition getARedef() { + result != this and + exists(Variable var | var = this.getVariable() and var = result.getVariable() | + // Definitions in different basic blocks. + this.isLast() and + reaches_without_redef(var, this.getBasicBlock(), result.getBasicBlock()) and + result.isFirst() + ) + or + // Definitions in the same basic block. + exists(BasicBlock common, Variable var | + this.rankInBB(common, var) + 1 = result.rankInBB(common, var) + ) + } - /** - * We only consider assignments as potential alert targets, not parameters - * and imports and other name-defining constructs. - * We also ignore anything named "_", "empty", "unused" or "dummy" - */ - predicate isRelevant() { - exists(AstNode p | p = this.getNode().getParentNode() | - p instanceof Assign or p instanceof AugAssign or p instanceof Tuple - ) and - not name_acceptable_for_unused_variable(this.getVariable()) and - /* Decorated classes and functions are used */ - not exists(this.getNode().getParentNode().(FunctionDef).getDefinedFunction().getADecorator()) and - not exists(this.getNode().getParentNode().(ClassDef).getDefinedClass().getADecorator()) - } + /** + * We only consider assignments as potential alert targets, not parameters + * and imports and other name-defining constructs. + * We also ignore anything named "_", "empty", "unused" or "dummy" + */ + predicate isRelevant() { + exists(AstNode p | p = this.getNode().getParentNode() | + p instanceof Assign or p instanceof AugAssign or p instanceof Tuple + ) and + not name_acceptable_for_unused_variable(this.getVariable()) and + /* Decorated classes and functions are used */ + not exists(this.getNode().getParentNode().(FunctionDef).getDefinedFunction().getADecorator()) and + not exists(this.getNode().getParentNode().(ClassDef).getDefinedClass().getADecorator()) + } } /** @@ -106,36 +106,36 @@ class Definition extends NameNode, DefinitionNode { * observed transitivity will be caused by loops in the control-flow graph. */ private predicate reaches_without_redef(Variable v, BasicBlock a, BasicBlock b) { - exists(Definition def | a.getASuccessor() = b | - def.getBasicBlock() = a and def.getVariable() = v and maybe_redefined(v) - ) - or - exists(BasicBlock mid | reaches_without_redef(v, a, mid) | - not exists(NameNode cfn | cfn.defines(v) | cfn.getBasicBlock() = mid) and - mid.getASuccessor() = b - ) + exists(Definition def | a.getASuccessor() = b | + def.getBasicBlock() = a and def.getVariable() = v and maybe_redefined(v) + ) + or + exists(BasicBlock mid | reaches_without_redef(v, a, mid) | + not exists(NameNode cfn | cfn.defines(v) | cfn.getBasicBlock() = mid) and + mid.getASuccessor() = b + ) } private predicate maybe_redefined(Variable v) { strictcount(Definition d | d.defines(v)) > 1 } predicate name_acceptable_for_unused_variable(Variable var) { - exists(string name | var.getId() = name | - name.regexpMatch("_+") or - name = "empty" or - name.matches("%unused%") or - name = "dummy" or - name.regexpMatch("__.*") - ) + exists(string name | var.getId() = name | + name.regexpMatch("_+") or + name = "empty" or + name.matches("%unused%") or + name = "dummy" or + name.regexpMatch("__.*") + ) } class ListComprehensionDeclaration extends ListComp { - Name getALeakedVariableUse() { - major_version() = 2 and - this.getIterationVariable(_).getId() = result.getId() and - result.getScope() = this.getScope() and - this.getAFlowNode().strictlyReaches(result.getAFlowNode()) and - result.isUse() - } + Name getALeakedVariableUse() { + major_version() = 2 and + this.getIterationVariable(_).getId() = result.getId() and + result.getScope() = this.getScope() and + this.getAFlowNode().strictlyReaches(result.getAFlowNode()) and + result.isUse() + } - Name getDefinition() { result = this.getIterationVariable(0).getAStore() } + Name getDefinition() { result = this.getIterationVariable(0).getAStore() } } diff --git a/python/ql/src/Variables/LeakingListComprehension.ql b/python/ql/src/Variables/LeakingListComprehension.ql index 32a338d31c1..9b98fb43a31 100644 --- a/python/ql/src/Variables/LeakingListComprehension.ql +++ b/python/ql/src/Variables/LeakingListComprehension.ql @@ -15,17 +15,17 @@ import Definition from ListComprehensionDeclaration l, Name use, Name defn where - use = l.getALeakedVariableUse() and - defn = l.getDefinition() and - l.getAFlowNode().strictlyReaches(use.getAFlowNode()) and - /* Make sure we aren't in a loop, as the variable may be redefined */ - not use.getAFlowNode().strictlyReaches(l.getAFlowNode()) and - not l.contains(use) and - not use.deletes(_) and - not exists(SsaVariable v | - v.getAUse() = use.getAFlowNode() and - not v.getDefinition().strictlyDominates(l.getAFlowNode()) - ) + use = l.getALeakedVariableUse() and + defn = l.getDefinition() and + l.getAFlowNode().strictlyReaches(use.getAFlowNode()) and + /* Make sure we aren't in a loop, as the variable may be redefined */ + not use.getAFlowNode().strictlyReaches(l.getAFlowNode()) and + not l.contains(use) and + not use.deletes(_) and + not exists(SsaVariable v | + v.getAUse() = use.getAFlowNode() and + not v.getDefinition().strictlyDominates(l.getAFlowNode()) + ) select use, - use.getId() + " may have a different value in Python 3, as the $@ will not be in scope.", defn, - "list comprehension variable" + use.getId() + " may have a different value in Python 3, as the $@ will not be in scope.", defn, + "list comprehension variable" diff --git a/python/ql/src/Variables/Loop.qll b/python/ql/src/Variables/Loop.qll index cc998f70e94..be19d4077c4 100644 --- a/python/ql/src/Variables/Loop.qll +++ b/python/ql/src/Variables/Loop.qll @@ -1,15 +1,15 @@ import python private predicate empty_sequence(Expr e) { - exists(SsaVariable var | var.getAUse().getNode() = e | - empty_sequence(var.getDefinition().getNode()) - ) - or - e instanceof List and not exists(e.(List).getAnElt()) - or - e instanceof Tuple and not exists(e.(Tuple).getAnElt()) - or - e.(StrConst).getText().length() = 0 + exists(SsaVariable var | var.getAUse().getNode() = e | + empty_sequence(var.getDefinition().getNode()) + ) + or + e instanceof List and not exists(e.(List).getAnElt()) + or + e instanceof Tuple and not exists(e.(Tuple).getAnElt()) + or + e.(StrConst).getText().length() = 0 } /* This has the potential for refinement, but we err on the side of fewer false positives for now. */ @@ -17,23 +17,23 @@ private predicate probably_non_empty_sequence(Expr e) { not empty_sequence(e) } /** A loop which probably defines v */ private Stmt loop_probably_defines(Variable v) { - exists(Name defn | defn.defines(v) and result.contains(defn) | - probably_non_empty_sequence(result.(For).getIter()) - or - probably_non_empty_sequence(result.(While).getTest()) - ) + exists(Name defn | defn.defines(v) and result.contains(defn) | + probably_non_empty_sequence(result.(For).getIter()) + or + probably_non_empty_sequence(result.(While).getTest()) + ) } /** Holds if the variable used by `use` is probably defined in a loop */ predicate probably_defined_in_loop(Name use) { - exists(Stmt loop | loop = loop_probably_defines(use.getVariable()) | - loop.getAFlowNode().strictlyReaches(use.getAFlowNode()) - ) + exists(Stmt loop | loop = loop_probably_defines(use.getVariable()) | + loop.getAFlowNode().strictlyReaches(use.getAFlowNode()) + ) } /** Holds if `s` is a loop that probably executes at least once */ predicate loop_probably_executes_at_least_once(Stmt s) { - probably_non_empty_sequence(s.(For).getIter()) - or - probably_non_empty_sequence(s.(While).getTest()) + probably_non_empty_sequence(s.(For).getIter()) + or + probably_non_empty_sequence(s.(While).getTest()) } diff --git a/python/ql/src/Variables/LoopVariableCapture.ql b/python/ql/src/Variables/LoopVariableCapture.ql index a88abb8b5f5..74cd20b1d6c 100644 --- a/python/ql/src/Variables/LoopVariableCapture.ql +++ b/python/ql/src/Variables/LoopVariableCapture.ql @@ -13,33 +13,33 @@ import python // Gets the scope of the iteration variable of the looping scope Scope iteration_variable_scope(AstNode loop) { - result = loop.(For).getScope() - or - result = loop.(Comp).getFunction() + result = loop.(For).getScope() + or + result = loop.(Comp).getFunction() } predicate capturing_looping_construct(CallableExpr capturing, AstNode loop, Variable var) { - var.getScope() = iteration_variable_scope(loop) and - var.getAnAccess().getScope() = capturing.getInnerScope() and - capturing.getParentNode+() = loop and - ( - loop.(For).getTarget() = var.getAnAccess() - or - var = loop.(Comp).getAnIterationVariable() - ) + var.getScope() = iteration_variable_scope(loop) and + var.getAnAccess().getScope() = capturing.getInnerScope() and + capturing.getParentNode+() = loop and + ( + loop.(For).getTarget() = var.getAnAccess() + or + var = loop.(Comp).getAnIterationVariable() + ) } predicate escaping_capturing_looping_construct(CallableExpr capturing, AstNode loop, Variable var) { - capturing_looping_construct(capturing, loop, var) and - // Escapes if used out side of for loop or is a lambda in a comprehension - ( - loop instanceof For and - exists(Expr e | e.pointsTo(_, _, capturing) | not loop.contains(e)) - or - loop.(Comp).getElt() = capturing - or - loop.(Comp).getElt().(Tuple).getAnElt() = capturing - ) + capturing_looping_construct(capturing, loop, var) and + // Escapes if used out side of for loop or is a lambda in a comprehension + ( + loop instanceof For and + exists(Expr e | e.pointsTo(_, _, capturing) | not loop.contains(e)) + or + loop.(Comp).getElt() = capturing + or + loop.(Comp).getElt().(Tuple).getAnElt() = capturing + ) } from CallableExpr capturing, AstNode loop, Variable var diff --git a/python/ql/src/Variables/MonkeyPatched.qll b/python/ql/src/Variables/MonkeyPatched.qll index 2a846e3deb1..d06731f5223 100644 --- a/python/ql/src/Variables/MonkeyPatched.qll +++ b/python/ql/src/Variables/MonkeyPatched.qll @@ -1,24 +1,24 @@ import python predicate monkey_patched_builtin(string name) { - exists(AttrNode attr, SubscriptNode subscr, StrConst s | - subscr.isStore() and - subscr.getIndex().getNode() = s and - s.getText() = name and - subscr.getObject() = attr and - attr.getObject("__dict__").pointsTo(Module::builtinModule()) - ) - or - exists(CallNode call, ControlFlowNode bltn, StrConst s | - call.getArg(0) = bltn and - bltn.pointsTo(Module::builtinModule()) and - call.getArg(1).getNode() = s and - s.getText() = name and - call.getFunction().pointsTo(Value::named("setattr")) - ) - or - exists(AttrNode attr | - attr.isStore() and - attr.getObject(name).pointsTo(Module::builtinModule()) - ) + exists(AttrNode attr, SubscriptNode subscr, StrConst s | + subscr.isStore() and + subscr.getIndex().getNode() = s and + s.getText() = name and + subscr.getObject() = attr and + attr.getObject("__dict__").pointsTo(Module::builtinModule()) + ) + or + exists(CallNode call, ControlFlowNode bltn, StrConst s | + call.getArg(0) = bltn and + bltn.pointsTo(Module::builtinModule()) and + call.getArg(1).getNode() = s and + s.getText() = name and + call.getFunction().pointsTo(Value::named("setattr")) + ) + or + exists(AttrNode attr | + attr.isStore() and + attr.getObject(name).pointsTo(Module::builtinModule()) + ) } diff --git a/python/ql/src/Variables/MultiplyDefined.ql b/python/ql/src/Variables/MultiplyDefined.ql index cae39729b9b..c7f09987ff1 100644 --- a/python/ql/src/Variables/MultiplyDefined.ql +++ b/python/ql/src/Variables/MultiplyDefined.ql @@ -15,35 +15,35 @@ import python import Definition predicate multiply_defined(AstNode asgn1, AstNode asgn2, Variable v) { - /* - * Must be redefined on all possible paths in the CFG corresponding to the original source. - * For example, splitting may create a path where `def` is unconditionally redefined, even though - * it is not in the original source. - */ + /* + * Must be redefined on all possible paths in the CFG corresponding to the original source. + * For example, splitting may create a path where `def` is unconditionally redefined, even though + * it is not in the original source. + */ - forex(Definition def, Definition redef | - def.getVariable() = v and - def = asgn1.getAFlowNode() and - redef = asgn2.getAFlowNode() - | - def.isUnused() and - def.getARedef() = redef and - def.isRelevant() - ) + forex(Definition def, Definition redef | + def.getVariable() = v and + def = asgn1.getAFlowNode() and + redef = asgn2.getAFlowNode() + | + def.isUnused() and + def.getARedef() = redef and + def.isRelevant() + ) } predicate simple_literal(Expr e) { - e.(Num).getN() = "0" - or - e instanceof NameConstant - or - e instanceof List and not exists(e.(List).getAnElt()) - or - e instanceof Tuple and not exists(e.(Tuple).getAnElt()) - or - e instanceof Dict and not exists(e.(Dict).getAKey()) - or - e.(StrConst).getText() = "" + e.(Num).getN() = "0" + or + e instanceof NameConstant + or + e instanceof List and not exists(e.(List).getAnElt()) + or + e instanceof Tuple and not exists(e.(Tuple).getAnElt()) + or + e instanceof Dict and not exists(e.(Dict).getAKey()) + or + e.(StrConst).getText() = "" } /** @@ -56,14 +56,14 @@ predicate simple_literal(Expr e) { * x = value2 */ predicate uninteresting_definition(AstNode asgn1) { - exists(AssignStmt a | a.getATarget() = asgn1 | simple_literal(a.getValue())) + exists(AssignStmt a | a.getATarget() = asgn1 | simple_literal(a.getValue())) } from AstNode asgn1, AstNode asgn2, Variable v where - multiply_defined(asgn1, asgn2, v) and - forall(Name el | el = asgn1.getParentNode().(Tuple).getAnElt() | multiply_defined(el, _, _)) and - not uninteresting_definition(asgn1) + multiply_defined(asgn1, asgn2, v) and + forall(Name el | el = asgn1.getParentNode().(Tuple).getAnElt() | multiply_defined(el, _, _)) and + not uninteresting_definition(asgn1) select asgn1, - "This assignment to '" + v.getId() + - "' is unnecessary as it is redefined $@ before this value is used.", asgn2 as t, "here" + "This assignment to '" + v.getId() + + "' is unnecessary as it is redefined $@ before this value is used.", asgn2 as t, "here" diff --git a/python/ql/src/Variables/ShadowBuiltin.ql b/python/ql/src/Variables/ShadowBuiltin.ql index 7073c429ec2..7e3b305380d 100644 --- a/python/ql/src/Variables/ShadowBuiltin.ql +++ b/python/ql/src/Variables/ShadowBuiltin.ql @@ -17,49 +17,49 @@ import Shadowing import semmle.python.types.Builtins predicate allow_list(string name) { - /* These are rarely used and thus unlikely to be confusing */ - name = "iter" or - name = "next" or - name = "input" or - name = "file" or - name = "apply" or - name = "slice" or - name = "buffer" or - name = "coerce" or - name = "intern" or - name = "exit" or - name = "quit" or - name = "license" or - /* These are short and/or hard to avoid */ - name = "dir" or - name = "id" or - name = "max" or - name = "min" or - name = "sum" or - name = "cmp" or - name = "chr" or - name = "ord" or - name = "bytes" or - name = "_" + /* These are rarely used and thus unlikely to be confusing */ + name = "iter" or + name = "next" or + name = "input" or + name = "file" or + name = "apply" or + name = "slice" or + name = "buffer" or + name = "coerce" or + name = "intern" or + name = "exit" or + name = "quit" or + name = "license" or + /* These are short and/or hard to avoid */ + name = "dir" or + name = "id" or + name = "max" or + name = "min" or + name = "sum" or + name = "cmp" or + name = "chr" or + name = "ord" or + name = "bytes" or + name = "_" } predicate shadows(Name d, string name, Function scope, int line) { - exists(LocalVariable l | - d.defines(l) and - l.getId() = name and - exists(Builtin::builtin(l.getId())) - ) and - d.getScope() = scope and - d.getLocation().getStartLine() = line and - not allow_list(name) and - not optimizing_parameter(d) + exists(LocalVariable l | + d.defines(l) and + l.getId() = name and + exists(Builtin::builtin(l.getId())) + ) and + d.getScope() = scope and + d.getLocation().getStartLine() = line and + not allow_list(name) and + not optimizing_parameter(d) } predicate first_shadowing_definition(Name d, string name) { - exists(int first, Scope scope | - shadows(d, name, scope, first) and - first = min(int line | shadows(_, name, scope, line)) - ) + exists(int first, Scope scope | + shadows(d, name, scope, first) and + first = min(int line | shadows(_, name, scope, line)) + ) } from Name d, string name diff --git a/python/ql/src/Variables/ShadowGlobal.ql b/python/ql/src/Variables/ShadowGlobal.ql index c7140c75d83..065abf42fe4 100644 --- a/python/ql/src/Variables/ShadowGlobal.ql +++ b/python/ql/src/Variables/ShadowGlobal.ql @@ -17,54 +17,54 @@ import Shadowing import semmle.python.types.Builtins predicate shadows(Name d, GlobalVariable g, Function scope, int line) { - g.getScope() = scope.getScope() and - d.getScope() = scope and - exists(LocalVariable l | - d.defines(l) and - l.getId() = g.getId() - ) and - not exists(Import il, Import ig, Name gd | il.contains(d) and gd.defines(g) and ig.contains(gd)) and - not exists(Assign a | a.getATarget() = d and a.getValue() = g.getAnAccess()) and - not exists(Builtin::builtin(g.getId())) and - d.getLocation().getStartLine() = line and - exists(Name defn | defn.defines(g) | not exists(If i | i.isNameEqMain() | i.contains(defn))) and - not optimizing_parameter(d) + g.getScope() = scope.getScope() and + d.getScope() = scope and + exists(LocalVariable l | + d.defines(l) and + l.getId() = g.getId() + ) and + not exists(Import il, Import ig, Name gd | il.contains(d) and gd.defines(g) and ig.contains(gd)) and + not exists(Assign a | a.getATarget() = d and a.getValue() = g.getAnAccess()) and + not exists(Builtin::builtin(g.getId())) and + d.getLocation().getStartLine() = line and + exists(Name defn | defn.defines(g) | not exists(If i | i.isNameEqMain() | i.contains(defn))) and + not optimizing_parameter(d) } /* pytest dynamically populates its namespace so, we cannot look directly for the pytest.fixture function */ AttrNode pytest_fixture_attr() { - exists(ModuleValue pytest | result.getObject("fixture").pointsTo(pytest)) + exists(ModuleValue pytest | result.getObject("fixture").pointsTo(pytest)) } Value pytest_fixture() { - exists(CallNode call | - call.getFunction() = pytest_fixture_attr() - or - call.getFunction().(CallNode).getFunction() = pytest_fixture_attr() - | - call.pointsTo(result) - ) + exists(CallNode call | + call.getFunction() = pytest_fixture_attr() + or + call.getFunction().(CallNode).getFunction() = pytest_fixture_attr() + | + call.pointsTo(result) + ) } /* pytest fixtures require that the parameter name is also a global */ predicate assigned_pytest_fixture(GlobalVariable v) { - exists(NameNode def | - def.defines(v) and def.(DefinitionNode).getValue().pointsTo(pytest_fixture()) - ) + exists(NameNode def | + def.defines(v) and def.(DefinitionNode).getValue().pointsTo(pytest_fixture()) + ) } predicate first_shadowing_definition(Name d, GlobalVariable g) { - exists(int first, Scope scope | - shadows(d, g, scope, first) and - first = min(int line | shadows(_, g, scope, line)) - ) + exists(int first, Scope scope | + shadows(d, g, scope, first) and + first = min(int line | shadows(_, g, scope, line)) + ) } from Name d, GlobalVariable g, Name def where - first_shadowing_definition(d, g) and - not exists(Name n | n.deletes(g)) and - def.defines(g) and - not assigned_pytest_fixture(g) and - not g.getId() = "_" + first_shadowing_definition(d, g) and + not exists(Name n | n.deletes(g)) and + def.defines(g) and + not assigned_pytest_fixture(g) and + not g.getId() = "_" select d, "Local variable '" + g.getId() + "' shadows a global variable defined $@.", def, "here" diff --git a/python/ql/src/Variables/Shadowing.qll b/python/ql/src/Variables/Shadowing.qll index a3dabb0a4e6..e8e47943a64 100644 --- a/python/ql/src/Variables/Shadowing.qll +++ b/python/ql/src/Variables/Shadowing.qll @@ -7,8 +7,8 @@ import python */ predicate optimizing_parameter(Parameter p) { - exists(string name, Name glob | p.getDefault() = glob | - glob.getId() = name and - p.asName().getId() = name - ) + exists(string name, Name glob | p.getDefault() = glob | + glob.getId() = name and + p.asName().getId() = name + ) } diff --git a/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariable.ql b/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariable.ql index 77d1e5ca67c..169b686a22a 100644 --- a/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariable.ql +++ b/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariable.ql @@ -14,16 +14,16 @@ import python import Definition predicate is_increment(Stmt s) { - /* x += n */ - s.(AugAssign).getValue() instanceof IntegerLiteral - or - /* x = x + n */ - exists(Name t, BinaryExpr add | - t = s.(AssignStmt).getTarget(0) and - add = s.(AssignStmt).getValue() and - add.getLeft().(Name).getVariable() = t.getVariable() and - add.getRight() instanceof IntegerLiteral - ) + /* x += n */ + s.(AugAssign).getValue() instanceof IntegerLiteral + or + /* x = x + n */ + exists(Name t, BinaryExpr add | + t = s.(AssignStmt).getTarget(0) and + add = s.(AssignStmt).getValue() and + add.getLeft().(Name).getVariable() = t.getVariable() and + add.getRight() instanceof IntegerLiteral + ) } predicate counting_loop(For f) { is_increment(f.getAStmt()) } @@ -31,49 +31,49 @@ predicate counting_loop(For f) { is_increment(f.getAStmt()) } predicate empty_loop(For f) { not exists(f.getStmt(1)) and f.getStmt(0) instanceof Pass } predicate one_item_only(For f) { - not exists(Continue c | f.contains(c)) and - exists(Stmt s | s = f.getBody().getLastItem() | - s instanceof Return - or - s instanceof Break - ) + not exists(Continue c | f.contains(c)) and + exists(Stmt s | s = f.getBody().getLastItem() | + s instanceof Return + or + s instanceof Break + ) } predicate points_to_call_to_range(ControlFlowNode f) { - /* (x)range is a function in Py2 and a class in Py3, so we must treat it as a plain object */ - exists(Value range | - range = Value::named("range") or - range = Value::named("xrange") - | - f = range.getACall() - ) - or - /* In case points-to fails due to 'from six.moves import range' or similar. */ - exists(string range | f.getNode().(Call).getFunc().(Name).getId() = range | - range = "range" or range = "xrange" - ) - or - /* Handle list(range(...)) and list(list(range(...))) */ - f.(CallNode).pointsTo().getClass() = ClassValue::list() and - points_to_call_to_range(f.(CallNode).getArg(0)) + /* (x)range is a function in Py2 and a class in Py3, so we must treat it as a plain object */ + exists(Value range | + range = Value::named("range") or + range = Value::named("xrange") + | + f = range.getACall() + ) + or + /* In case points-to fails due to 'from six.moves import range' or similar. */ + exists(string range | f.getNode().(Call).getFunc().(Name).getId() = range | + range = "range" or range = "xrange" + ) + or + /* Handle list(range(...)) and list(list(range(...))) */ + f.(CallNode).pointsTo().getClass() = ClassValue::list() and + points_to_call_to_range(f.(CallNode).getArg(0)) } /** Whether n is a use of a variable that is a not effectively a constant. */ predicate use_of_non_constant(Name n) { - exists(Variable var | - n.uses(var) and - /* use is local */ - not n.getScope() instanceof Module and - /* variable is not global */ - not var.getScope() instanceof Module - | - /* Defined more than once (dynamically) */ - strictcount(Name def | def.defines(var)) > 1 - or - exists(For f, Name def | f.contains(def) and def.defines(var)) - or - exists(While w, Name def | w.contains(def) and def.defines(var)) - ) + exists(Variable var | + n.uses(var) and + /* use is local */ + not n.getScope() instanceof Module and + /* variable is not global */ + not var.getScope() instanceof Module + | + /* Defined more than once (dynamically) */ + strictcount(Name def | def.defines(var)) > 1 + or + exists(For f, Name def | f.contains(def) and def.defines(var)) + or + exists(While w, Name def | w.contains(def) and def.defines(var)) + ) } /** @@ -81,9 +81,9 @@ predicate use_of_non_constant(Name n) { * E.g. queue.add(None) */ predicate implicit_repeat(For f) { - not exists(f.getStmt(1)) and - exists(ImmutableLiteral imm | f.getStmt(0).contains(imm)) and - not exists(Name n | f.getBody().contains(n) and use_of_non_constant(n)) + not exists(f.getStmt(1)) and + exists(ImmutableLiteral imm | f.getStmt(0).contains(imm)) and + not exists(Name n | f.getBody().contains(n) and use_of_non_constant(n)) } /** @@ -93,22 +93,22 @@ predicate implicit_repeat(For f) { * E.g. gets `x` from `{ y for y in x }`. */ ControlFlowNode get_comp_iterable(For f) { - exists(Comp c | c.getFunction().getStmt(0) = f | c.getAFlowNode().getAPredecessor() = result) + exists(Comp c | c.getFunction().getStmt(0) = f | c.getAFlowNode().getAPredecessor() = result) } from For f, Variable v, string msg where - f.getTarget() = v.getAnAccess() and - not f.getAStmt().contains(v.getAnAccess()) and - not points_to_call_to_range(f.getIter().getAFlowNode()) and - not points_to_call_to_range(get_comp_iterable(f)) and - not name_acceptable_for_unused_variable(v) and - not f.getScope().getName() = "genexpr" and - not empty_loop(f) and - not one_item_only(f) and - not counting_loop(f) and - not implicit_repeat(f) and - if exists(Name del | del.deletes(v) and f.getAStmt().contains(del)) - then msg = "' is deleted, but not used, in the loop body." - else msg = "' is not used in the loop body." + f.getTarget() = v.getAnAccess() and + not f.getAStmt().contains(v.getAnAccess()) and + not points_to_call_to_range(f.getIter().getAFlowNode()) and + not points_to_call_to_range(get_comp_iterable(f)) and + not name_acceptable_for_unused_variable(v) and + not f.getScope().getName() = "genexpr" and + not empty_loop(f) and + not one_item_only(f) and + not counting_loop(f) and + not implicit_repeat(f) and + if exists(Name del | del.deletes(v) and f.getAStmt().contains(del)) + then msg = "' is deleted, but not used, in the loop body." + else msg = "' is not used in the loop body." select f, "For loop variable '" + v.getId() + msg diff --git a/python/ql/src/Variables/Undefined.qll b/python/ql/src/Variables/Undefined.qll index 2c757733af4..ae401a83aaf 100644 --- a/python/ql/src/Variables/Undefined.qll +++ b/python/ql/src/Variables/Undefined.qll @@ -4,24 +4,24 @@ import semmle.python.dataflow.TaintTracking /** Marker for "uninitialized". */ class Uninitialized extends TaintKind { - Uninitialized() { this = "undefined" } + Uninitialized() { this = "undefined" } } private predicate loop_entry_variables(EssaVariable pred, EssaVariable succ) { - exists(PhiFunction phi, BasicBlock pb | - loop_entry_edge(pb, phi.getBasicBlock()) and - succ = phi.getVariable() and - pred = phi.getInput(pb) - ) + exists(PhiFunction phi, BasicBlock pb | + loop_entry_edge(pb, phi.getBasicBlock()) and + succ = phi.getVariable() and + pred = phi.getInput(pb) + ) } private predicate loop_entry_edge(BasicBlock pred, BasicBlock loop) { - pred = loop.getAPredecessor() and - pred = loop.getImmediateDominator() and - exists(Stmt s | - loop_probably_executes_at_least_once(s) and - s.getAFlowNode().getBasicBlock() = loop - ) + pred = loop.getAPredecessor() and + pred = loop.getImmediateDominator() and + exists(Stmt s | + loop_probably_executes_at_least_once(s) and + s.getAFlowNode().getBasicBlock() = loop + ) } /** @@ -29,11 +29,11 @@ private predicate loop_entry_edge(BasicBlock pred, BasicBlock loop) { * any use dominated by another use of the same variable must be defined, or is unreachable. */ private predicate first_use(NameNode u, EssaVariable v) { - v.getASourceUse() = u and - not exists(NameNode other | - v.getASourceUse() = other and - other.strictlyDominates(u) - ) + v.getASourceUse() = u and + not exists(NameNode other | + v.getASourceUse() = other and + other.strictlyDominates(u) + ) } /** @@ -41,77 +41,77 @@ private predicate first_use(NameNode u, EssaVariable v) { * there is a function called `method_name` that can exit the program. */ private predicate maybe_call_to_exiting_function(CallNode call) { - exists(FunctionValue exits, string name | exits.neverReturns() and exits.getName() = name | - call.getFunction().(NameNode).getId() = name or - call.getFunction().(AttrNode).getName() = name - ) + exists(FunctionValue exits, string name | exits.neverReturns() and exits.getName() = name | + call.getFunction().(NameNode).getId() = name or + call.getFunction().(AttrNode).getName() = name + ) } predicate exitFunctionGuardedEdge(EssaVariable pred, EssaVariable succ) { - exists(CallNode exit_call | - succ.(PhiFunction).getInput(exit_call.getBasicBlock()) = pred and - maybe_call_to_exiting_function(exit_call) - ) + exists(CallNode exit_call | + succ.(PhiFunction).getInput(exit_call.getBasicBlock()) = pred and + maybe_call_to_exiting_function(exit_call) + ) } class UninitializedConfig extends TaintTracking::Configuration { - UninitializedConfig() { this = "Unitialized local config" } + UninitializedConfig() { this = "Unitialized local config" } - override predicate isSource(DataFlow::Node source, TaintKind kind) { - kind instanceof Uninitialized and - exists(EssaVariable var | - source.asVariable() = var and - var.getSourceVariable() instanceof FastLocalVariable and - not var.getSourceVariable().(Variable).escapes() - | - var instanceof ScopeEntryDefinition - or - var instanceof DeletionDefinition - ) - } + override predicate isSource(DataFlow::Node source, TaintKind kind) { + kind instanceof Uninitialized and + exists(EssaVariable var | + source.asVariable() = var and + var.getSourceVariable() instanceof FastLocalVariable and + not var.getSourceVariable().(Variable).escapes() + | + var instanceof ScopeEntryDefinition + or + var instanceof DeletionDefinition + ) + } - override predicate isBarrier(DataFlow::Node node, TaintKind kind) { - kind instanceof Uninitialized and - ( - definition(node.asVariable()) - or - use(node.asVariable()) - or - sanitizingNode(node.asCfgNode()) - ) - } + override predicate isBarrier(DataFlow::Node node, TaintKind kind) { + kind instanceof Uninitialized and + ( + definition(node.asVariable()) + or + use(node.asVariable()) + or + sanitizingNode(node.asCfgNode()) + ) + } - private predicate definition(EssaDefinition def) { - def instanceof AssignmentDefinition - or - def instanceof ExceptionCapture - or - def instanceof ParameterDefinition - } + private predicate definition(EssaDefinition def) { + def instanceof AssignmentDefinition + or + def instanceof ExceptionCapture + or + def instanceof ParameterDefinition + } - private predicate use(EssaDefinition def) { - exists(def.(EssaNodeRefinement).getInput().getASourceUse()) - or - exists(def.(PhiFunction).getAnInput().getASourceUse()) - or - exists(def.(EssaEdgeRefinement).getInput().getASourceUse()) - } + private predicate use(EssaDefinition def) { + exists(def.(EssaNodeRefinement).getInput().getASourceUse()) + or + exists(def.(PhiFunction).getAnInput().getASourceUse()) + or + exists(def.(EssaEdgeRefinement).getInput().getASourceUse()) + } - private predicate sanitizingNode(ControlFlowNode node) { - exists(EssaVariable v | - v.getASourceUse() = node and - not first_use(node, v) - ) - } + private predicate sanitizingNode(ControlFlowNode node) { + exists(EssaVariable v | + v.getASourceUse() = node and + not first_use(node, v) + ) + } - override predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node dest) { - /* - * If we are guaranteed to iterate over a loop at least once, then we can prune any edges that - * don't pass through the body. - */ + override predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node dest) { + /* + * If we are guaranteed to iterate over a loop at least once, then we can prune any edges that + * don't pass through the body. + */ - loop_entry_variables(src.asVariable(), dest.asVariable()) - or - exitFunctionGuardedEdge(src.asVariable(), dest.asVariable()) - } + loop_entry_variables(src.asVariable(), dest.asVariable()) + or + exitFunctionGuardedEdge(src.asVariable(), dest.asVariable()) + } } diff --git a/python/ql/src/Variables/UndefinedExport.ql b/python/ql/src/Variables/UndefinedExport.ql index 61e7baab2e2..52d51ce4f72 100644 --- a/python/ql/src/Variables/UndefinedExport.ql +++ b/python/ql/src/Variables/UndefinedExport.ql @@ -15,68 +15,67 @@ import python /** Whether name is declared in the __all__ list of this module */ predicate declaredInAll(Module m, StrConst name) { - exists(Assign a, GlobalVariable all | - a.defines(all) and - a.getScope() = m and - all.getId() = "__all__" and - a.getValue().(List).getAnElt() = name - ) + exists(Assign a, GlobalVariable all | + a.defines(all) and + a.getScope() = m and + all.getId() = "__all__" and + a.getValue().(List).getAnElt() = name + ) } predicate mutates_globals(ModuleValue m) { - exists(CallNode globals | - globals = Value::named("globals").(FunctionValue).getACall() and - globals.getScope() = m.getScope() - | - exists(AttrNode attr | attr.getObject() = globals) - or - exists(SubscriptNode sub | sub.getObject() = globals and sub.isStore()) - ) + exists(CallNode globals | + globals = Value::named("globals").(FunctionValue).getACall() and + globals.getScope() = m.getScope() + | + exists(AttrNode attr | attr.getObject() = globals) or - // Enum (added in 3.4) has method `_convert_` that alters globals - // This was called `_convert` until 3.8, but that name will be removed in 3.9 - exists(ClassValue enum_class | - enum_class.getASuperType() = Value::named("enum.Enum") and - ( - // In Python < 3.8, Enum._convert can be found with points-to - exists(Value enum_convert | - enum_convert = enum_class.attr("_convert") and - exists(CallNode call | call.getScope() = m.getScope() | - enum_convert.getACall() = call or - call.getFunction().pointsTo(enum_convert) - ) - ) - or - // In Python 3.8, Enum._convert_ is implemented using a metaclass, and our points-to - // analysis doesn't handle that well enough. So we need a special case for this - not exists(Value enum_convert | enum_convert = enum_class.attr("_convert")) and - exists(CallNode call | call.getScope() = m.getScope() | - call.getFunction().(AttrNode).getObject(["_convert", "_convert_"]).pointsTo() = - enum_class - ) + exists(SubscriptNode sub | sub.getObject() = globals and sub.isStore()) + ) + or + // Enum (added in 3.4) has method `_convert_` that alters globals + // This was called `_convert` until 3.8, but that name will be removed in 3.9 + exists(ClassValue enum_class | + enum_class.getASuperType() = Value::named("enum.Enum") and + ( + // In Python < 3.8, Enum._convert can be found with points-to + exists(Value enum_convert | + enum_convert = enum_class.attr("_convert") and + exists(CallNode call | call.getScope() = m.getScope() | + enum_convert.getACall() = call or + call.getFunction().pointsTo(enum_convert) ) + ) + or + // In Python 3.8, Enum._convert_ is implemented using a metaclass, and our points-to + // analysis doesn't handle that well enough. So we need a special case for this + not exists(Value enum_convert | enum_convert = enum_class.attr("_convert")) and + exists(CallNode call | call.getScope() = m.getScope() | + call.getFunction().(AttrNode).getObject(["_convert", "_convert_"]).pointsTo() = enum_class + ) ) + ) } predicate is_exported_submodule_name(ModuleValue m, string exported_name) { - m.getScope().getShortName() = "__init__" and - exists(m.getScope().getPackage().getSubModule(exported_name)) + m.getScope().getShortName() = "__init__" and + exists(m.getScope().getPackage().getSubModule(exported_name)) } predicate contains_unknown_import_star(ModuleValue m) { - exists(ImportStarNode imp | imp.getEnclosingModule() = m.getScope() | - imp.getModule().pointsTo().isAbsent() - or - not exists(imp.getModule().pointsTo()) - ) + exists(ImportStarNode imp | imp.getEnclosingModule() = m.getScope() | + imp.getModule().pointsTo().isAbsent() + or + not exists(imp.getModule().pointsTo()) + ) } from ModuleValue m, StrConst name, string exported_name where - declaredInAll(m.getScope(), name) and - exported_name = name.getText() and - not m.hasAttribute(exported_name) and - not is_exported_submodule_name(m, exported_name) and - not contains_unknown_import_star(m) and - not mutates_globals(m) + declaredInAll(m.getScope(), name) and + exported_name = name.getText() and + not m.hasAttribute(exported_name) and + not is_exported_submodule_name(m, exported_name) and + not contains_unknown_import_star(m) and + not mutates_globals(m) select name, "The name '" + exported_name + "' is exported by __all__ but is not defined." diff --git a/python/ql/src/Variables/UndefinedGlobal.ql b/python/ql/src/Variables/UndefinedGlobal.ql index c21c048a3e1..bbb48db8fb1 100644 --- a/python/ql/src/Variables/UndefinedGlobal.ql +++ b/python/ql/src/Variables/UndefinedGlobal.ql @@ -16,104 +16,104 @@ import Loop import semmle.python.pointsto.PointsTo predicate guarded_against_name_error(Name u) { - exists(Try t | t.getBody().getAnItem().contains(u) | - t.getAHandler().getType().(Name).getId() = "NameError" - ) - or - exists(ConditionBlock guard, BasicBlock controlled, Call globals | - guard.getLastNode().getNode().contains(globals) or - guard.getLastNode().getNode() = globals - | - globals.getFunc().(Name).getId() = "globals" and - guard.controls(controlled, _) and - controlled.contains(u.getAFlowNode()) - ) + exists(Try t | t.getBody().getAnItem().contains(u) | + t.getAHandler().getType().(Name).getId() = "NameError" + ) + or + exists(ConditionBlock guard, BasicBlock controlled, Call globals | + guard.getLastNode().getNode().contains(globals) or + guard.getLastNode().getNode() = globals + | + globals.getFunc().(Name).getId() = "globals" and + guard.controls(controlled, _) and + controlled.contains(u.getAFlowNode()) + ) } predicate contains_unknown_import_star(Module m) { - exists(ImportStar imp | imp.getScope() = m | - exists(ModuleValue imported | imported.importedAs(imp.getImportedModuleName()) | - not imported.hasCompleteExportInfo() - ) + exists(ImportStar imp | imp.getScope() = m | + exists(ModuleValue imported | imported.importedAs(imp.getImportedModuleName()) | + not imported.hasCompleteExportInfo() ) + ) } predicate undefined_use_in_function(Name u) { - exists(Function f | - u.getScope().getScope*() = f and - // Either function is a method or inner function or it is live at the end of the module scope - ( - not f.getScope() = u.getEnclosingModule() or - u.getEnclosingModule().(ImportTimeScope).definesName(f.getName()) - ) and - // There is a use, but not a definition of this global variable in the function or enclosing scope - exists(GlobalVariable v | u.uses(v) | - not exists(Assign a, Scope defnScope | - a.getATarget() = v.getAnAccess() and a.getScope() = defnScope - | - defnScope = f - or - // Exclude modules as that case is handled more precisely below. - defnScope = f.getScope().getScope*() and not defnScope instanceof Module - ) - ) + exists(Function f | + u.getScope().getScope*() = f and + // Either function is a method or inner function or it is live at the end of the module scope + ( + not f.getScope() = u.getEnclosingModule() or + u.getEnclosingModule().(ImportTimeScope).definesName(f.getName()) ) and - not u.getEnclosingModule().(ImportTimeScope).definesName(u.getId()) and - not exists(ModuleValue m | m.getScope() = u.getEnclosingModule() | m.hasAttribute(u.getId())) and - not globallyDefinedName(u.getId()) and - not exists(SsaVariable var | var.getAUse().getNode() = u and not var.maybeUndefined()) and - not guarded_against_name_error(u) and - not (u.getEnclosingModule().isPackageInit() and u.getId() = "__path__") + // There is a use, but not a definition of this global variable in the function or enclosing scope + exists(GlobalVariable v | u.uses(v) | + not exists(Assign a, Scope defnScope | + a.getATarget() = v.getAnAccess() and a.getScope() = defnScope + | + defnScope = f + or + // Exclude modules as that case is handled more precisely below. + defnScope = f.getScope().getScope*() and not defnScope instanceof Module + ) + ) + ) and + not u.getEnclosingModule().(ImportTimeScope).definesName(u.getId()) and + not exists(ModuleValue m | m.getScope() = u.getEnclosingModule() | m.hasAttribute(u.getId())) and + not globallyDefinedName(u.getId()) and + not exists(SsaVariable var | var.getAUse().getNode() = u and not var.maybeUndefined()) and + not guarded_against_name_error(u) and + not (u.getEnclosingModule().isPackageInit() and u.getId() = "__path__") } predicate undefined_use_in_class_or_module(Name u) { - exists(GlobalVariable v | u.uses(v)) and - not exists(Function f | u.getScope().getScope*() = f) and - exists(SsaVariable var | var.getAUse().getNode() = u | var.maybeUndefined()) and - not guarded_against_name_error(u) and - not exists(ModuleValue m | m.getScope() = u.getEnclosingModule() | m.hasAttribute(u.getId())) and - not (u.getEnclosingModule().isPackageInit() and u.getId() = "__path__") and - not globallyDefinedName(u.getId()) + exists(GlobalVariable v | u.uses(v)) and + not exists(Function f | u.getScope().getScope*() = f) and + exists(SsaVariable var | var.getAUse().getNode() = u | var.maybeUndefined()) and + not guarded_against_name_error(u) and + not exists(ModuleValue m | m.getScope() = u.getEnclosingModule() | m.hasAttribute(u.getId())) and + not (u.getEnclosingModule().isPackageInit() and u.getId() = "__path__") and + not globallyDefinedName(u.getId()) } predicate use_of_exec(Module m) { - exists(Exec exec | exec.getScope() = m) - or - exists(CallNode call, FunctionValue exec | exec.getACall() = call and call.getScope() = m | - exec = Value::named("exec") or - exec = Value::named("execfile") - ) + exists(Exec exec | exec.getScope() = m) + or + exists(CallNode call, FunctionValue exec | exec.getACall() = call and call.getScope() = m | + exec = Value::named("exec") or + exec = Value::named("execfile") + ) } predicate undefined_use(Name u) { - ( - undefined_use_in_class_or_module(u) - or - undefined_use_in_function(u) - ) and - not monkey_patched_builtin(u.getId()) and - not contains_unknown_import_star(u.getEnclosingModule()) and - not use_of_exec(u.getEnclosingModule()) and - not exists(u.getVariable().getAStore()) and - not u.pointsTo(_) and - not probably_defined_in_loop(u) + ( + undefined_use_in_class_or_module(u) + or + undefined_use_in_function(u) + ) and + not monkey_patched_builtin(u.getId()) and + not contains_unknown_import_star(u.getEnclosingModule()) and + not use_of_exec(u.getEnclosingModule()) and + not exists(u.getVariable().getAStore()) and + not u.pointsTo(_) and + not probably_defined_in_loop(u) } private predicate first_use_in_a_block(Name use) { - exists(GlobalVariable v, BasicBlock b, int i | - i = min(int j | b.getNode(j).getNode() = v.getALoad()) and b.getNode(i) = use.getAFlowNode() - ) + exists(GlobalVariable v, BasicBlock b, int i | + i = min(int j | b.getNode(j).getNode() = v.getALoad()) and b.getNode(i) = use.getAFlowNode() + ) } predicate first_undefined_use(Name use) { - undefined_use(use) and - exists(GlobalVariable v | v.getALoad() = use | - first_use_in_a_block(use) and - not exists(ControlFlowNode other | - other.getNode() = v.getALoad() and - other.getBasicBlock().strictlyDominates(use.getAFlowNode().getBasicBlock()) - ) + undefined_use(use) and + exists(GlobalVariable v | v.getALoad() = use | + first_use_in_a_block(use) and + not exists(ControlFlowNode other | + other.getNode() = v.getALoad() and + other.getBasicBlock().strictlyDominates(use.getAFlowNode().getBasicBlock()) ) + ) } from Name u diff --git a/python/ql/src/Variables/UndefinedPlaceHolder.ql b/python/ql/src/Variables/UndefinedPlaceHolder.ql index 1ec4f85749f..29004a6123f 100644 --- a/python/ql/src/Variables/UndefinedPlaceHolder.ql +++ b/python/ql/src/Variables/UndefinedPlaceHolder.ql @@ -15,32 +15,32 @@ import Variables.MonkeyPatched /* Local variable part */ predicate initialized_as_local(PlaceHolder use) { - exists(SsaVariable l, Function f | f = use.getScope() and l.getAUse() = use.getAFlowNode() | - l.getVariable() instanceof LocalVariable and - not l.maybeUndefined() - ) + exists(SsaVariable l, Function f | f = use.getScope() and l.getAUse() = use.getAFlowNode() | + l.getVariable() instanceof LocalVariable and + not l.maybeUndefined() + ) } /* Not a template member */ Class enclosing_class(PlaceHolder use) { result.getAMethod() = use.getScope() } predicate template_attribute(PlaceHolder use) { - exists(ImportTimeScope cls | cls = enclosing_class(use) | cls.definesName(use.getId())) + exists(ImportTimeScope cls | cls = enclosing_class(use) | cls.definesName(use.getId())) } /* Global Stuff */ predicate not_a_global(PlaceHolder use) { - not exists(PythonModuleObject mo | - mo.hasAttribute(use.getId()) and mo.getModule() = use.getEnclosingModule() - ) and - not globallyDefinedName(use.getId()) and - not monkey_patched_builtin(use.getId()) and - not globallyDefinedName(use.getId()) + not exists(PythonModuleObject mo | + mo.hasAttribute(use.getId()) and mo.getModule() = use.getEnclosingModule() + ) and + not globallyDefinedName(use.getId()) and + not monkey_patched_builtin(use.getId()) and + not globallyDefinedName(use.getId()) } from PlaceHolder p where - not initialized_as_local(p) and - not template_attribute(p) and - not_a_global(p) + not initialized_as_local(p) and + not template_attribute(p) and + not_a_global(p) select p, "This use of place-holder variable '" + p.getId() + "' may be undefined" diff --git a/python/ql/src/Variables/UninitializedLocal.ql b/python/ql/src/Variables/UninitializedLocal.ql index 343036be152..23a063be5ab 100644 --- a/python/ql/src/Variables/UninitializedLocal.ql +++ b/python/ql/src/Variables/UninitializedLocal.ql @@ -15,20 +15,20 @@ import Undefined import semmle.python.pointsto.PointsTo predicate uninitialized_local(NameNode use) { - exists(FastLocalVariable local | use.uses(local) or use.deletes(local) | not local.escapes()) and - ( - any(Uninitialized uninit).taints(use) and - PointsToInternal::reachableBlock(use.getBasicBlock(), _) - or - not exists(EssaVariable var | var.getASourceUse() = use) - ) + exists(FastLocalVariable local | use.uses(local) or use.deletes(local) | not local.escapes()) and + ( + any(Uninitialized uninit).taints(use) and + PointsToInternal::reachableBlock(use.getBasicBlock(), _) + or + not exists(EssaVariable var | var.getASourceUse() = use) + ) } predicate explicitly_guarded(NameNode u) { - exists(Try t | - t.getBody().contains(u.getNode()) and - t.getAHandler().getType().pointsTo(ClassValue::nameError()) - ) + exists(Try t | + t.getBody().contains(u.getNode()) and + t.getAHandler().getType().pointsTo(ClassValue::nameError()) + ) } from NameNode u diff --git a/python/ql/src/Variables/UnusedLocalVariable.ql b/python/ql/src/Variables/UnusedLocalVariable.ql index ab280151bf3..de83345f62d 100644 --- a/python/ql/src/Variables/UnusedLocalVariable.ql +++ b/python/ql/src/Variables/UnusedLocalVariable.ql @@ -15,20 +15,20 @@ import python import Definition predicate unused_local(Name unused, LocalVariable v) { - forex(Definition def | def.getNode() = unused | - def.getVariable() = v and - def.isUnused() and - not exists(def.getARedef()) and - def.isRelevant() and - not v = any(Nonlocal n).getAVariable() and - not exists(def.getNode().getParentNode().(FunctionDef).getDefinedFunction().getADecorator()) and - not exists(def.getNode().getParentNode().(ClassDef).getDefinedClass().getADecorator()) - ) + forex(Definition def | def.getNode() = unused | + def.getVariable() = v and + def.isUnused() and + not exists(def.getARedef()) and + def.isRelevant() and + not v = any(Nonlocal n).getAVariable() and + not exists(def.getNode().getParentNode().(FunctionDef).getDefinedFunction().getADecorator()) and + not exists(def.getNode().getParentNode().(ClassDef).getDefinedClass().getADecorator()) + ) } from Name unused, LocalVariable v where - unused_local(unused, v) and - // If unused is part of a tuple, count it as unused if all elements of that tuple are unused. - forall(Name el | el = unused.getParentNode().(Tuple).getAnElt() | unused_local(el, _)) + unused_local(unused, v) and + // If unused is part of a tuple, count it as unused if all elements of that tuple are unused. + forall(Name el | el = unused.getParentNode().(Tuple).getAnElt() | unused_local(el, _)) select unused, "The value assigned to local variable '" + v.getId() + "' is never used." diff --git a/python/ql/src/Variables/UnusedModuleVariable.ql b/python/ql/src/Variables/UnusedModuleVariable.ql index 2d873908ba5..543f17f6f35 100644 --- a/python/ql/src/Variables/UnusedModuleVariable.ql +++ b/python/ql/src/Variables/UnusedModuleVariable.ql @@ -19,48 +19,48 @@ import Definition * but it is more complex than a simple list of strings */ predicate complex_all(Module m) { - exists(Assign a, GlobalVariable all | - a.defines(all) and a.getScope() = m and all.getId() = "__all__" - | - not a.getValue() instanceof List - or - exists(Expr e | e = a.getValue().(List).getAnElt() | not e instanceof StrConst) - ) + exists(Assign a, GlobalVariable all | + a.defines(all) and a.getScope() = m and all.getId() = "__all__" + | + not a.getValue() instanceof List or - exists(Call c, GlobalVariable all | - c.getFunc().(Attribute).getObject() = all.getALoad() and - c.getScope() = m and - all.getId() = "__all__" - ) + exists(Expr e | e = a.getValue().(List).getAnElt() | not e instanceof StrConst) + ) + or + exists(Call c, GlobalVariable all | + c.getFunc().(Attribute).getObject() = all.getALoad() and + c.getScope() = m and + all.getId() = "__all__" + ) } predicate unused_global(Name unused, GlobalVariable v) { - not exists(ImportingStmt is | is.contains(unused)) and - forex(DefinitionNode defn | defn.getNode() = unused | - not defn.getValue().getNode() instanceof FunctionExpr and - not defn.getValue().getNode() instanceof ClassExpr and - not exists(Name u | - // A use of the variable - u.uses(v) - | - // That is reachable from this definition, directly - defn.strictlyReaches(u.getAFlowNode()) - or - // indirectly - defn.getBasicBlock().reachesExit() and u.getScope() != unused.getScope() - ) and - not unused.getEnclosingModule().getAnExport() = v.getId() and - not exists(unused.getParentNode().(ClassDef).getDefinedClass().getADecorator()) and - not exists(unused.getParentNode().(FunctionDef).getDefinedFunction().getADecorator()) and - unused.defines(v) and - not name_acceptable_for_unused_variable(v) and - not complex_all(unused.getEnclosingModule()) - ) + not exists(ImportingStmt is | is.contains(unused)) and + forex(DefinitionNode defn | defn.getNode() = unused | + not defn.getValue().getNode() instanceof FunctionExpr and + not defn.getValue().getNode() instanceof ClassExpr and + not exists(Name u | + // A use of the variable + u.uses(v) + | + // That is reachable from this definition, directly + defn.strictlyReaches(u.getAFlowNode()) + or + // indirectly + defn.getBasicBlock().reachesExit() and u.getScope() != unused.getScope() + ) and + not unused.getEnclosingModule().getAnExport() = v.getId() and + not exists(unused.getParentNode().(ClassDef).getDefinedClass().getADecorator()) and + not exists(unused.getParentNode().(FunctionDef).getDefinedFunction().getADecorator()) and + unused.defines(v) and + not name_acceptable_for_unused_variable(v) and + not complex_all(unused.getEnclosingModule()) + ) } from Name unused, GlobalVariable v where - unused_global(unused, v) and - // If unused is part of a tuple, count it as unused if all elements of that tuple are unused. - forall(Name el | el = unused.getParentNode().(Tuple).getAnElt() | unused_global(el, _)) + unused_global(unused, v) and + // If unused is part of a tuple, count it as unused if all elements of that tuple are unused. + forall(Name el | el = unused.getParentNode().(Tuple).getAnElt() | unused_global(el, _)) select unused, "The global variable '" + v.getId() + "' is not used." diff --git a/python/ql/src/Variables/UnusedParameter.ql b/python/ql/src/Variables/UnusedParameter.ql index af3b6c6b3cd..74e1c2ac536 100644 --- a/python/ql/src/Variables/UnusedParameter.ql +++ b/python/ql/src/Variables/UnusedParameter.ql @@ -13,24 +13,24 @@ import python import Definition predicate unused_parameter(FunctionValue f, LocalVariable v) { - v.isParameter() and - v.getScope() = f.getScope() and - not name_acceptable_for_unused_variable(v) and - not exists(NameNode u | u.uses(v)) and - not exists(Name inner, LocalVariable iv | - inner.uses(iv) and iv.getId() = v.getId() and inner.getScope().getScope() = v.getScope() - ) + v.isParameter() and + v.getScope() = f.getScope() and + not name_acceptable_for_unused_variable(v) and + not exists(NameNode u | u.uses(v)) and + not exists(Name inner, LocalVariable iv | + inner.uses(iv) and iv.getId() = v.getId() and inner.getScope().getScope() = v.getScope() + ) } predicate is_abstract(FunctionValue func) { - func.getScope().getADecorator().(Name).getId().matches("%abstract%") + func.getScope().getADecorator().(Name).getId().matches("%abstract%") } from PythonFunctionValue f, LocalVariable v where - v.getId() != "self" and - unused_parameter(f, v) and - not f.isOverridingMethod() and - not f.isOverriddenMethod() and - not is_abstract(f) + v.getId() != "self" and + unused_parameter(f, v) and + not f.isOverridingMethod() and + not f.isOverriddenMethod() and + not is_abstract(f) select f, "The parameter '" + v.getId() + "' is never used." diff --git a/python/ql/src/analysis/AlertSuppression.ql b/python/ql/src/analysis/AlertSuppression.ql index 2cd92b99a30..c8fefc92cc1 100644 --- a/python/ql/src/analysis/AlertSuppression.ql +++ b/python/ql/src/analysis/AlertSuppression.ql @@ -11,97 +11,97 @@ import python * An alert suppression comment. */ abstract class SuppressionComment extends Comment { - /** Gets the scope of this suppression. */ - abstract SuppressionScope getScope(); + /** Gets the scope of this suppression. */ + abstract SuppressionScope getScope(); - /** Gets the suppression annotation in this comment. */ - abstract string getAnnotation(); + /** Gets the suppression annotation in this comment. */ + abstract string getAnnotation(); - /** - * Holds if this comment applies to the range from column `startcolumn` of line `startline` - * to column `endcolumn` of line `endline` in file `filepath`. - */ - abstract predicate covers( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ); + /** + * Holds if this comment applies to the range from column `startcolumn` of line `startline` + * to column `endcolumn` of line `endline` in file `filepath`. + */ + abstract predicate covers( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ); } /** * An alert comment that applies to a single line */ abstract class LineSuppressionComment extends SuppressionComment { - LineSuppressionComment() { - exists(string filepath, int l | - this.getLocation().hasLocationInfo(filepath, l, _, _, _) and - any(AstNode a).getLocation().hasLocationInfo(filepath, l, _, _, _) - ) - } + LineSuppressionComment() { + exists(string filepath, int l | + this.getLocation().hasLocationInfo(filepath, l, _, _, _) and + any(AstNode a).getLocation().hasLocationInfo(filepath, l, _, _, _) + ) + } - /** Gets the scope of this suppression. */ - override SuppressionScope getScope() { result = this } + /** Gets the scope of this suppression. */ + override SuppressionScope getScope() { result = this } - override predicate covers( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getLocation().hasLocationInfo(filepath, startline, _, endline, endcolumn) and - startcolumn = 1 - } + override predicate covers( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getLocation().hasLocationInfo(filepath, startline, _, endline, endcolumn) and + startcolumn = 1 + } } /** * An lgtm suppression comment. */ class LgtmSuppressionComment extends LineSuppressionComment { - string annotation; + string annotation; - LgtmSuppressionComment() { - exists(string all | all = this.getContents() | - // match `lgtm[...]` anywhere in the comment - annotation = all.regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _) - or - // match `lgtm` at the start of the comment and after semicolon - annotation = all.regexpFind("(?i)(?<=^|;)\\s*lgtm(?!\\B|\\s*\\[)", _, _).trim() - ) - } + LgtmSuppressionComment() { + exists(string all | all = this.getContents() | + // match `lgtm[...]` anywhere in the comment + annotation = all.regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _) + or + // match `lgtm` at the start of the comment and after semicolon + annotation = all.regexpFind("(?i)(?<=^|;)\\s*lgtm(?!\\B|\\s*\\[)", _, _).trim() + ) + } - /** Gets the suppression annotation in this comment. */ - override string getAnnotation() { result = annotation } + /** Gets the suppression annotation in this comment. */ + override string getAnnotation() { result = annotation } } /** * A noqa suppression comment. Both pylint and pyflakes respect this, so lgtm ought to too. */ class NoqaSuppressionComment extends LineSuppressionComment { - NoqaSuppressionComment() { this.getContents().toLowerCase().regexpMatch("\\s*noqa\\s*") } + NoqaSuppressionComment() { this.getContents().toLowerCase().regexpMatch("\\s*noqa\\s*") } - override string getAnnotation() { result = "lgtm" } + override string getAnnotation() { result = "lgtm" } } /** * The scope of an alert suppression comment. */ class SuppressionScope extends @py_comment { - SuppressionScope() { this instanceof SuppressionComment } + SuppressionScope() { this instanceof SuppressionComment } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.(SuppressionComment).covers(filepath, startline, startcolumn, endline, endcolumn) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.(SuppressionComment).covers(filepath, startline, startcolumn, endline, endcolumn) + } - /** Gets a textual representation of this element. */ - string toString() { result = "suppression range" } + /** Gets a textual representation of this element. */ + string toString() { result = "suppression range" } } from SuppressionComment c select c, // suppression comment - c.getContents(), // text of suppression comment (excluding delimiters) - c.getAnnotation(), // text of suppression annotation - c.getScope() // scope of suppression + c.getContents(), // text of suppression comment (excluding delimiters) + c.getAnnotation(), // text of suppression annotation + c.getScope() // scope of suppression diff --git a/python/ql/src/analysis/CallGraphEfficiency.ql b/python/ql/src/analysis/CallGraphEfficiency.ql index a35e9282b10..5e36823b8ac 100644 --- a/python/ql/src/analysis/CallGraphEfficiency.ql +++ b/python/ql/src/analysis/CallGraphEfficiency.ql @@ -9,17 +9,17 @@ import semmle.python.pointsto.PointsToContext from int total_facts, int total_size, int depth, float efficiency where - total_facts = - strictcount(ControlFlowNode call, CallableValue func | - exists(PointsToContext ctx | - call = func.getACall(ctx) and - depth = ctx.getDepth() - ) - ) and - total_size = - strictcount(ControlFlowNode call, CallableValue func, PointsToContext ctx | - call = func.getACall(ctx) and - depth = ctx.getDepth() - ) and - efficiency = 100.0 * total_facts / total_size + total_facts = + strictcount(ControlFlowNode call, CallableValue func | + exists(PointsToContext ctx | + call = func.getACall(ctx) and + depth = ctx.getDepth() + ) + ) and + total_size = + strictcount(ControlFlowNode call, CallableValue func, PointsToContext ctx | + call = func.getACall(ctx) and + depth = ctx.getDepth() + ) and + efficiency = 100.0 * total_facts / total_size select depth, total_facts, total_size, efficiency diff --git a/python/ql/src/analysis/CallGraphMarginalEfficiency.ql b/python/ql/src/analysis/CallGraphMarginalEfficiency.ql index 7e93e30ea0b..394bf379eeb 100644 --- a/python/ql/src/analysis/CallGraphMarginalEfficiency.ql +++ b/python/ql/src/analysis/CallGraphMarginalEfficiency.ql @@ -9,21 +9,21 @@ import semmle.python.pointsto.PointsToContext from int total_facts, int total_size, int depth, float efficiency where - total_facts = - strictcount(ControlFlowNode call, CallableValue func | - exists(PointsToContext ctx | - call = func.getACall(ctx) and - depth = ctx.getDepth() and - not exists(PointsToContext shallower | - call = func.getACall(shallower) and - shallower.getDepth() < depth - ) - ) - ) and - total_size = - strictcount(ControlFlowNode call, CallableValue func, PointsToContext ctx | - call = func.getACall(ctx) and - depth = ctx.getDepth() - ) and - efficiency = 100.0 * total_facts / total_size + total_facts = + strictcount(ControlFlowNode call, CallableValue func | + exists(PointsToContext ctx | + call = func.getACall(ctx) and + depth = ctx.getDepth() and + not exists(PointsToContext shallower | + call = func.getACall(shallower) and + shallower.getDepth() < depth + ) + ) + ) and + total_size = + strictcount(ControlFlowNode call, CallableValue func, PointsToContext ctx | + call = func.getACall(ctx) and + depth = ctx.getDepth() + ) and + efficiency = 100.0 * total_facts / total_size select depth, total_facts, total_size, efficiency diff --git a/python/ql/src/analysis/Consistency.ql b/python/ql/src/analysis/Consistency.ql index 9b49ed4ff90..cf84c36d0cd 100644 --- a/python/ql/src/analysis/Consistency.ql +++ b/python/ql/src/analysis/Consistency.ql @@ -8,291 +8,291 @@ import python import DefinitionTracking predicate uniqueness_error(int number, string what, string problem) { - ( - what = "toString" or - what = "getLocation" or - what = "getNode" or - what = "getDefinition" or - what = "getEntryNode" or - what = "getOrigin" or - what = "getAnInferredType" - ) and - ( - number = 0 and problem = "no results for " + what + "()" - or - number in [2 .. 10] and problem = number.toString() + " results for " + what + "()" - ) + ( + what = "toString" or + what = "getLocation" or + what = "getNode" or + what = "getDefinition" or + what = "getEntryNode" or + what = "getOrigin" or + what = "getAnInferredType" + ) and + ( + number = 0 and problem = "no results for " + what + "()" + or + number in [2 .. 10] and problem = number.toString() + " results for " + what + "()" + ) } predicate ast_consistency(string clsname, string problem, string what) { - exists(AstNode a | clsname = a.getAQlClass() | - uniqueness_error(count(a.toString()), "toString", problem) and - what = "at " + a.getLocation().toString() - or - uniqueness_error(strictcount(a.getLocation()), "getLocation", problem) and - what = a.getLocation().toString() - or - not exists(a.getLocation()) and - not a.(Module).isPackage() and - problem = "no location" and - what = a.toString() - ) + exists(AstNode a | clsname = a.getAQlClass() | + uniqueness_error(count(a.toString()), "toString", problem) and + what = "at " + a.getLocation().toString() + or + uniqueness_error(strictcount(a.getLocation()), "getLocation", problem) and + what = a.getLocation().toString() + or + not exists(a.getLocation()) and + not a.(Module).isPackage() and + problem = "no location" and + what = a.toString() + ) } predicate location_consistency(string clsname, string problem, string what) { - exists(Location l | clsname = l.getAQlClass() | - uniqueness_error(count(l.toString()), "toString", problem) and what = "at " + l.toString() - or - not exists(l.toString()) and - problem = "no toString" and - ( - exists(AstNode thing | thing.getLocation() = l | - what = "a location of a " + thing.getAQlClass() - ) - or - not exists(AstNode thing | thing.getLocation() = l) and - what = "a location" - ) - or - l.getEndLine() < l.getStartLine() and - problem = "end line before start line" and - what = "at " + l.toString() - or - l.getEndLine() = l.getStartLine() and - l.getEndColumn() < l.getStartColumn() and - problem = "end column before start column" and - what = "at " + l.toString() + exists(Location l | clsname = l.getAQlClass() | + uniqueness_error(count(l.toString()), "toString", problem) and what = "at " + l.toString() + or + not exists(l.toString()) and + problem = "no toString" and + ( + exists(AstNode thing | thing.getLocation() = l | + what = "a location of a " + thing.getAQlClass() + ) + or + not exists(AstNode thing | thing.getLocation() = l) and + what = "a location" ) + or + l.getEndLine() < l.getStartLine() and + problem = "end line before start line" and + what = "at " + l.toString() + or + l.getEndLine() = l.getStartLine() and + l.getEndColumn() < l.getStartColumn() and + problem = "end column before start column" and + what = "at " + l.toString() + ) } predicate cfg_consistency(string clsname, string problem, string what) { - exists(ControlFlowNode f | clsname = f.getAQlClass() | - uniqueness_error(count(f.getNode()), "getNode", problem) and - what = "at " + f.getLocation().toString() - or - not exists(f.getLocation()) and - not exists(Module p | p.isPackage() | p.getEntryNode() = f or p.getAnExitNode() = f) and - problem = "no location" and - what = f.toString() - or - uniqueness_error(count(f.(AttrNode).getObject()), "getValue", problem) and - what = "at " + f.getLocation().toString() - ) + exists(ControlFlowNode f | clsname = f.getAQlClass() | + uniqueness_error(count(f.getNode()), "getNode", problem) and + what = "at " + f.getLocation().toString() + or + not exists(f.getLocation()) and + not exists(Module p | p.isPackage() | p.getEntryNode() = f or p.getAnExitNode() = f) and + problem = "no location" and + what = f.toString() + or + uniqueness_error(count(f.(AttrNode).getObject()), "getValue", problem) and + what = "at " + f.getLocation().toString() + ) } predicate scope_consistency(string clsname, string problem, string what) { - exists(Scope s | clsname = s.getAQlClass() | - uniqueness_error(count(s.getEntryNode()), "getEntryNode", problem) and - what = "at " + s.getLocation().toString() - or - uniqueness_error(count(s.toString()), "toString", problem) and - what = "at " + s.getLocation().toString() - or - uniqueness_error(strictcount(s.getLocation()), "getLocation", problem) and - what = "at " + s.getLocation().toString() - or - not exists(s.getLocation()) and - problem = "no location" and - what = s.toString() and - not s.(Module).isPackage() - ) + exists(Scope s | clsname = s.getAQlClass() | + uniqueness_error(count(s.getEntryNode()), "getEntryNode", problem) and + what = "at " + s.getLocation().toString() + or + uniqueness_error(count(s.toString()), "toString", problem) and + what = "at " + s.getLocation().toString() + or + uniqueness_error(strictcount(s.getLocation()), "getLocation", problem) and + what = "at " + s.getLocation().toString() + or + not exists(s.getLocation()) and + problem = "no location" and + what = s.toString() and + not s.(Module).isPackage() + ) } string best_description_builtin_object(Object o) { - o.isBuiltin() and - ( - result = o.toString() - or - not exists(o.toString()) and py_cobjectnames(o, result) - or - not exists(o.toString()) and - not py_cobjectnames(o, _) and - result = "builtin object of type " + o.getAnInferredType().toString() - or - not exists(o.toString()) and - not py_cobjectnames(o, _) and - not exists(o.getAnInferredType().toString()) and - result = "builtin object" - ) + o.isBuiltin() and + ( + result = o.toString() + or + not exists(o.toString()) and py_cobjectnames(o, result) + or + not exists(o.toString()) and + not py_cobjectnames(o, _) and + result = "builtin object of type " + o.getAnInferredType().toString() + or + not exists(o.toString()) and + not py_cobjectnames(o, _) and + not exists(o.getAnInferredType().toString()) and + result = "builtin object" + ) } private predicate introspected_builtin_object(Object o) { - /* - * Only check objects from the extractor, missing data for objects generated from C source code analysis is OK. - * as it will be ignored if it doesn't match up with the introspected form. - */ + /* + * Only check objects from the extractor, missing data for objects generated from C source code analysis is OK. + * as it will be ignored if it doesn't match up with the introspected form. + */ - py_cobject_sources(o, 0) + py_cobject_sources(o, 0) } predicate builtin_object_consistency(string clsname, string problem, string what) { - exists(Object o | - clsname = o.getAQlClass() and - what = best_description_builtin_object(o) and - introspected_builtin_object(o) - | - not exists(o.getAnInferredType()) and - not py_cobjectnames(o, _) and - problem = "neither name nor type" - or - uniqueness_error(count(string name | py_cobjectnames(o, name)), "name", problem) - or - not exists(o.getAnInferredType()) and problem = "no results for getAnInferredType" - or - not exists(o.toString()) and - problem = "no toString" and - not exists(string name | name.prefix(7) = "_semmle" | py_special_objects(o, name)) and - not o = unknownValue() - ) + exists(Object o | + clsname = o.getAQlClass() and + what = best_description_builtin_object(o) and + introspected_builtin_object(o) + | + not exists(o.getAnInferredType()) and + not py_cobjectnames(o, _) and + problem = "neither name nor type" + or + uniqueness_error(count(string name | py_cobjectnames(o, name)), "name", problem) + or + not exists(o.getAnInferredType()) and problem = "no results for getAnInferredType" + or + not exists(o.toString()) and + problem = "no toString" and + not exists(string name | name.prefix(7) = "_semmle" | py_special_objects(o, name)) and + not o = unknownValue() + ) } predicate source_object_consistency(string clsname, string problem, string what) { - exists(Object o | clsname = o.getAQlClass() and not o.isBuiltin() | - uniqueness_error(count(o.getOrigin()), "getOrigin", problem) and - what = "at " + o.getOrigin().getLocation().toString() - or - not exists(o.getOrigin().getLocation()) and problem = "no location" and what = "??" - or - not exists(o.toString()) and - problem = "no toString" and - what = "at " + o.getOrigin().getLocation().toString() - or - strictcount(o.toString()) > 1 and problem = "multiple toStrings()" and what = o.toString() - ) + exists(Object o | clsname = o.getAQlClass() and not o.isBuiltin() | + uniqueness_error(count(o.getOrigin()), "getOrigin", problem) and + what = "at " + o.getOrigin().getLocation().toString() + or + not exists(o.getOrigin().getLocation()) and problem = "no location" and what = "??" + or + not exists(o.toString()) and + problem = "no toString" and + what = "at " + o.getOrigin().getLocation().toString() + or + strictcount(o.toString()) > 1 and problem = "multiple toStrings()" and what = o.toString() + ) } predicate ssa_consistency(string clsname, string problem, string what) { - /* Zero or one definitions of each SSA variable */ - exists(SsaVariable var | clsname = var.getAQlClass() | - uniqueness_error(strictcount(var.getDefinition()), "getDefinition", problem) and - what = var.getId() - ) - or - /* Dominance criterion: Definition *must* dominate *all* uses. */ - exists(SsaVariable var, ControlFlowNode defn, ControlFlowNode use | - defn = var.getDefinition() and use = var.getAUse() - | - not defn.strictlyDominates(use) and - not defn = use and - /* Phi nodes which share a flow node with a use come *before* the use */ - not (exists(var.getAPhiInput()) and defn = use) and - clsname = var.getAQlClass() and - problem = "a definition which does not dominate a use at " + use.getLocation() and - what = var.getId() + " at " + var.getLocation() - ) - or - /* Minimality of phi nodes */ - exists(SsaVariable var | - strictcount(var.getAPhiInput()) = 1 and - var - .getAPhiInput() - .getDefinition() - .getBasicBlock() - .strictlyDominates(var.getDefinition().getBasicBlock()) - | - clsname = var.getAQlClass() and - problem = " a definition which is dominated by the definition of an incoming phi edge." and - what = var.getId() + " at " + var.getLocation() - ) + /* Zero or one definitions of each SSA variable */ + exists(SsaVariable var | clsname = var.getAQlClass() | + uniqueness_error(strictcount(var.getDefinition()), "getDefinition", problem) and + what = var.getId() + ) + or + /* Dominance criterion: Definition *must* dominate *all* uses. */ + exists(SsaVariable var, ControlFlowNode defn, ControlFlowNode use | + defn = var.getDefinition() and use = var.getAUse() + | + not defn.strictlyDominates(use) and + not defn = use and + /* Phi nodes which share a flow node with a use come *before* the use */ + not (exists(var.getAPhiInput()) and defn = use) and + clsname = var.getAQlClass() and + problem = "a definition which does not dominate a use at " + use.getLocation() and + what = var.getId() + " at " + var.getLocation() + ) + or + /* Minimality of phi nodes */ + exists(SsaVariable var | + strictcount(var.getAPhiInput()) = 1 and + var + .getAPhiInput() + .getDefinition() + .getBasicBlock() + .strictlyDominates(var.getDefinition().getBasicBlock()) + | + clsname = var.getAQlClass() and + problem = " a definition which is dominated by the definition of an incoming phi edge." and + what = var.getId() + " at " + var.getLocation() + ) } predicate function_object_consistency(string clsname, string problem, string what) { - exists(FunctionObject func | clsname = func.getAQlClass() | - what = func.getName() and - ( - count(func.descriptiveString()) = 0 and problem = "no descriptiveString()" - or - exists(int c | c = strictcount(func.descriptiveString()) and c > 1 | - problem = c + "descriptiveString()s" - ) - ) - or - not exists(func.getName()) and what = "?" and problem = "no name" + exists(FunctionObject func | clsname = func.getAQlClass() | + what = func.getName() and + ( + count(func.descriptiveString()) = 0 and problem = "no descriptiveString()" + or + exists(int c | c = strictcount(func.descriptiveString()) and c > 1 | + problem = c + "descriptiveString()s" + ) ) + or + not exists(func.getName()) and what = "?" and problem = "no name" + ) } predicate multiple_origins_per_object(Object obj) { - not obj.isC() and - not obj instanceof ModuleObject and - exists(ControlFlowNode use, Context ctx | - strictcount(ControlFlowNode orig | use.refersTo(ctx, obj, _, orig)) > 1 - ) + not obj.isC() and + not obj instanceof ModuleObject and + exists(ControlFlowNode use, Context ctx | + strictcount(ControlFlowNode orig | use.refersTo(ctx, obj, _, orig)) > 1 + ) } predicate intermediate_origins(ControlFlowNode use, ControlFlowNode inter, Object obj) { - exists(ControlFlowNode orig, Context ctx | not inter = orig | - use.refersTo(ctx, obj, _, inter) and - inter.refersTo(ctx, obj, _, orig) and - // It can sometimes happen that two different modules (e.g. cPickle and Pickle) - // have the same attribute, but different origins. - not strictcount(Object val | inter.(AttrNode).getObject().refersTo(val)) > 1 - ) + exists(ControlFlowNode orig, Context ctx | not inter = orig | + use.refersTo(ctx, obj, _, inter) and + inter.refersTo(ctx, obj, _, orig) and + // It can sometimes happen that two different modules (e.g. cPickle and Pickle) + // have the same attribute, but different origins. + not strictcount(Object val | inter.(AttrNode).getObject().refersTo(val)) > 1 + ) } predicate points_to_consistency(string clsname, string problem, string what) { - exists(Object obj | - multiple_origins_per_object(obj) and - clsname = obj.getAQlClass() and - problem = "multiple origins for an object" and - what = obj.toString() - ) - or - exists(ControlFlowNode use, ControlFlowNode inter, Object obj | - intermediate_origins(use, inter, obj) and - clsname = use.getAQlClass() and - problem = "has intermediate origin " + inter and - what = use.toString() - ) + exists(Object obj | + multiple_origins_per_object(obj) and + clsname = obj.getAQlClass() and + problem = "multiple origins for an object" and + what = obj.toString() + ) + or + exists(ControlFlowNode use, ControlFlowNode inter, Object obj | + intermediate_origins(use, inter, obj) and + clsname = use.getAQlClass() and + problem = "has intermediate origin " + inter and + what = use.toString() + ) } predicate jump_to_definition_consistency(string clsname, string problem, string what) { - problem = "multiple (jump-to) definitions" and - exists(Expr use | - strictcount(getUniqueDefinition(use)) > 1 and - clsname = use.getAQlClass() and - what = use.toString() - ) + problem = "multiple (jump-to) definitions" and + exists(Expr use | + strictcount(getUniqueDefinition(use)) > 1 and + clsname = use.getAQlClass() and + what = use.toString() + ) } predicate file_consistency(string clsname, string problem, string what) { - exists(File file, Folder folder | - clsname = file.getAQlClass() and - problem = "has same name as a folder" and - what = file.getAbsolutePath() and - what = folder.getAbsolutePath() - ) - or - exists(Container f | - clsname = f.getAQlClass() and - uniqueness_error(count(f.toString()), "toString", problem) and - what = "file " + f.getName() - ) + exists(File file, Folder folder | + clsname = file.getAQlClass() and + problem = "has same name as a folder" and + what = file.getAbsolutePath() and + what = folder.getAbsolutePath() + ) + or + exists(Container f | + clsname = f.getAQlClass() and + uniqueness_error(count(f.toString()), "toString", problem) and + what = "file " + f.getName() + ) } predicate class_value_consistency(string clsname, string problem, string what) { - exists(ClassValue value, ClassValue sup, string attr | - what = value.getName() and - sup = value.getASuperType() and - exists(sup.lookup(attr)) and - not value.failedInference(_) and - not exists(value.lookup(attr)) and - clsname = value.getAQlClass() and - problem = "no attribute '" + attr + "', but super type '" + sup.getName() + "' does." - ) + exists(ClassValue value, ClassValue sup, string attr | + what = value.getName() and + sup = value.getASuperType() and + exists(sup.lookup(attr)) and + not value.failedInference(_) and + not exists(value.lookup(attr)) and + clsname = value.getAQlClass() and + problem = "no attribute '" + attr + "', but super type '" + sup.getName() + "' does." + ) } from string clsname, string problem, string what where - ast_consistency(clsname, problem, what) or - location_consistency(clsname, problem, what) or - scope_consistency(clsname, problem, what) or - cfg_consistency(clsname, problem, what) or - ssa_consistency(clsname, problem, what) or - builtin_object_consistency(clsname, problem, what) or - source_object_consistency(clsname, problem, what) or - function_object_consistency(clsname, problem, what) or - points_to_consistency(clsname, problem, what) or - jump_to_definition_consistency(clsname, problem, what) or - file_consistency(clsname, problem, what) or - class_value_consistency(clsname, problem, what) + ast_consistency(clsname, problem, what) or + location_consistency(clsname, problem, what) or + scope_consistency(clsname, problem, what) or + cfg_consistency(clsname, problem, what) or + ssa_consistency(clsname, problem, what) or + builtin_object_consistency(clsname, problem, what) or + source_object_consistency(clsname, problem, what) or + function_object_consistency(clsname, problem, what) or + points_to_consistency(clsname, problem, what) or + jump_to_definition_consistency(clsname, problem, what) or + file_consistency(clsname, problem, what) or + class_value_consistency(clsname, problem, what) select clsname + " " + what + " has " + problem diff --git a/python/ql/src/analysis/ContextEfficiency.ql b/python/ql/src/analysis/ContextEfficiency.ql index 9c4a6355585..205091ba1e3 100644 --- a/python/ql/src/analysis/ContextEfficiency.ql +++ b/python/ql/src/analysis/ContextEfficiency.ql @@ -9,18 +9,18 @@ import semmle.python.pointsto.PointsToContext from int total_facts, int total_size, int depth, float efficiency where - total_facts = - strictcount(ControlFlowNode f, Object value, ClassObject cls | - exists(PointsToContext ctx | - PointsTo::points_to(f, ctx, value, cls, _) and - depth = ctx.getDepth() - ) - ) and - total_size = - strictcount(ControlFlowNode f, Object value, ClassObject cls, PointsToContext ctx, - ControlFlowNode orig | - PointsTo::points_to(f, ctx, value, cls, orig) and - depth = ctx.getDepth() - ) and - efficiency = 100.0 * total_facts / total_size + total_facts = + strictcount(ControlFlowNode f, Object value, ClassObject cls | + exists(PointsToContext ctx | + PointsTo::points_to(f, ctx, value, cls, _) and + depth = ctx.getDepth() + ) + ) and + total_size = + strictcount(ControlFlowNode f, Object value, ClassObject cls, PointsToContext ctx, + ControlFlowNode orig | + PointsTo::points_to(f, ctx, value, cls, orig) and + depth = ctx.getDepth() + ) and + efficiency = 100.0 * total_facts / total_size select depth, total_facts, total_size, efficiency diff --git a/python/ql/src/analysis/ContextMarginalEfficiency.ql b/python/ql/src/analysis/ContextMarginalEfficiency.ql index 68229363761..755ccad683c 100644 --- a/python/ql/src/analysis/ContextMarginalEfficiency.ql +++ b/python/ql/src/analysis/ContextMarginalEfficiency.ql @@ -8,25 +8,25 @@ import semmle.python.pointsto.PointsTo import semmle.python.pointsto.PointsToContext int depth(ControlFlowNode f, Object value, ClassObject cls) { - exists(PointsToContext ctx | - PointsTo::points_to(f, ctx, value, cls, _) and - result = ctx.getDepth() - ) + exists(PointsToContext ctx | + PointsTo::points_to(f, ctx, value, cls, _) and + result = ctx.getDepth() + ) } int shallowest(ControlFlowNode f, Object value, ClassObject cls) { - result = min(int x | x = depth(f, value, cls)) + result = min(int x | x = depth(f, value, cls)) } from int total_facts, int total_size, int depth, float efficiency where - total_facts = - strictcount(ControlFlowNode f, Object value, ClassObject cls | depth = shallowest(f, value, cls)) and - total_size = - strictcount(ControlFlowNode f, Object value, ClassObject cls, PointsToContext ctx, - ControlFlowNode orig | - PointsTo::points_to(f, ctx, value, cls, orig) and - depth = ctx.getDepth() - ) and - efficiency = 100.0 * total_facts / total_size + total_facts = + strictcount(ControlFlowNode f, Object value, ClassObject cls | depth = shallowest(f, value, cls)) and + total_size = + strictcount(ControlFlowNode f, Object value, ClassObject cls, PointsToContext ctx, + ControlFlowNode orig | + PointsTo::points_to(f, ctx, value, cls, orig) and + depth = ctx.getDepth() + ) and + efficiency = 100.0 * total_facts / total_size select depth, total_facts, total_size, efficiency diff --git a/python/ql/src/analysis/CrossProjectDefinitions.qll b/python/ql/src/analysis/CrossProjectDefinitions.qll index 5b9a904e794..c3d2735d9c6 100644 --- a/python/ql/src/analysis/CrossProjectDefinitions.qll +++ b/python/ql/src/analysis/CrossProjectDefinitions.qll @@ -6,14 +6,14 @@ import python import semmle.python.pointsto.PointsTo private newtype TSymbol = - TModule(Module m) or - TMember(Symbol outer, string part) { - exists(Object o | outer.resolvesTo() = o | - o.(ModuleObject).hasAttribute(part) - or - o.(ClassObject).hasAttribute(part) - ) - } + TModule(Module m) or + TMember(Symbol outer, string part) { + exists(Object o | outer.resolvesTo() = o | + o.(ModuleObject).hasAttribute(part) + or + o.(ClassObject).hasAttribute(part) + ) + } /** * A "symbol" referencing an object in another module @@ -28,76 +28,76 @@ private newtype TSymbol = * then symbol for the method `m` would be "mod/C.m" */ class Symbol extends TSymbol { - string toString() { - exists(Module m | this = TModule(m) and result = m.getName()) - or - exists(TModule outer, string part | - this = TMember(outer, part) and - outer = TModule(_) and - result = outer.(Symbol).toString() + "/" + part - ) - or - exists(TMember outer, string part | - this = TMember(outer, part) and - outer = TMember(_, _) and - result = outer.(Symbol).toString() + "." + part - ) - } + string toString() { + exists(Module m | this = TModule(m) and result = m.getName()) + or + exists(TModule outer, string part | + this = TMember(outer, part) and + outer = TModule(_) and + result = outer.(Symbol).toString() + "/" + part + ) + or + exists(TMember outer, string part | + this = TMember(outer, part) and + outer = TMember(_, _) and + result = outer.(Symbol).toString() + "." + part + ) + } - /** Finds the `AstNode` that this `Symbol` refers to. */ - AstNode find() { - this = TModule(result) - or - exists(Symbol s, string name | this = TMember(s, name) | - exists(ClassObject cls | - s.resolvesTo() = cls and - cls.attributeRefersTo(name, _, result.getAFlowNode()) - ) - or - exists(ModuleObject m | - s.resolvesTo() = m and - m.attributeRefersTo(name, _, result.getAFlowNode()) - ) - ) - } + /** Finds the `AstNode` that this `Symbol` refers to. */ + AstNode find() { + this = TModule(result) + or + exists(Symbol s, string name | this = TMember(s, name) | + exists(ClassObject cls | + s.resolvesTo() = cls and + cls.attributeRefersTo(name, _, result.getAFlowNode()) + ) + or + exists(ModuleObject m | + s.resolvesTo() = m and + m.attributeRefersTo(name, _, result.getAFlowNode()) + ) + ) + } - /** - * Find the class or module `Object` that this `Symbol` refers to, if - * this `Symbol` refers to a class or module. - */ - Object resolvesTo() { - this = TModule(result.(ModuleObject).getModule()) - or - exists(Symbol s, string name, Object o | - this = TMember(s, name) and - o = s.resolvesTo() and - result = attribute_in_scope(o, name) - ) - } + /** + * Find the class or module `Object` that this `Symbol` refers to, if + * this `Symbol` refers to a class or module. + */ + Object resolvesTo() { + this = TModule(result.(ModuleObject).getModule()) + or + exists(Symbol s, string name, Object o | + this = TMember(s, name) and + o = s.resolvesTo() and + result = attribute_in_scope(o, name) + ) + } - /** - * Gets the `Module` for the module part of this `Symbol`. - * For example, this would return the `os` module for the `Symbol` "os/environ". - */ - Module getModule() { - this = TModule(result) - or - exists(Symbol outer | this = TMember(outer, _) and result = outer.getModule()) - } + /** + * Gets the `Module` for the module part of this `Symbol`. + * For example, this would return the `os` module for the `Symbol` "os/environ". + */ + Module getModule() { + this = TModule(result) + or + exists(Symbol outer | this = TMember(outer, _) and result = outer.getModule()) + } - /** Gets the `Symbol` that is the named member of this `Symbol`. */ - Symbol getMember(string name) { result = TMember(this, name) } + /** Gets the `Symbol` that is the named member of this `Symbol`. */ + Symbol getMember(string name) { result = TMember(this, name) } } /* Helper for `Symbol`.resolvesTo() */ private Object attribute_in_scope(Object obj, string name) { - exists(ClassObject cls | cls = obj | - cls.lookupAttribute(name) = result and result.(ControlFlowNode).getScope() = cls.getPyClass() - ) - or - exists(ModuleObject mod | mod = obj | - mod.attr(name) = result and - result.(ControlFlowNode).getScope() = mod.getModule() and - not result.(ControlFlowNode).isEntryNode() - ) + exists(ClassObject cls | cls = obj | + cls.lookupAttribute(name) = result and result.(ControlFlowNode).getScope() = cls.getPyClass() + ) + or + exists(ModuleObject mod | mod = obj | + mod.attr(name) = result and + result.(ControlFlowNode).getScope() = mod.getModule() and + not result.(ControlFlowNode).isEntryNode() + ) } diff --git a/python/ql/src/analysis/DefinitionTracking.qll b/python/ql/src/analysis/DefinitionTracking.qll index 2d3b0138c21..f9c4f962af9 100644 --- a/python/ql/src/analysis/DefinitionTracking.qll +++ b/python/ql/src/analysis/DefinitionTracking.qll @@ -6,138 +6,138 @@ import python import semmle.python.pointsto.PointsTo private newtype TDefinition = - TLocalDefinition(AstNode a) { a instanceof Expr or a instanceof Stmt or a instanceof Module } + TLocalDefinition(AstNode a) { a instanceof Expr or a instanceof Stmt or a instanceof Module } /** A definition for the purposes of jump-to-definition. */ class Definition extends TLocalDefinition { - /** Gets a textual representation of this element. */ - string toString() { result = "Definition " + this.getAstNode().getLocation().toString() } + /** Gets a textual representation of this element. */ + string toString() { result = "Definition " + this.getAstNode().getLocation().toString() } - AstNode getAstNode() { this = TLocalDefinition(result) } + AstNode getAstNode() { this = TLocalDefinition(result) } - Module getModule() { result = this.getAstNode().getScope().getEnclosingModule() } + Module getModule() { result = this.getAstNode().getScope().getEnclosingModule() } - Location getLocation() { result = this.getAstNode().getLocation() } + Location getLocation() { result = this.getAstNode().getLocation() } } private predicate jump_to_defn(ControlFlowNode use, Definition defn) { - exists(EssaVariable var | - use = var.getASourceUse() and - ssa_variable_defn(var, defn) - ) - or - exists(string name | - use.isLoad() and - jump_to_defn_attribute(use.(AttrNode).getObject(name), name, defn) - ) - or - exists(PythonModuleObject mod | - use.(ImportExprNode).refersTo(mod) and - defn.getAstNode() = mod.getModule() - ) - or - exists(PythonModuleObject mod, string name | - use.(ImportMemberNode).getModule(name).refersTo(mod) and - scope_jump_to_defn_attribute(mod.getModule(), name, defn) - ) - or - exists(PackageObject package | - use.(ImportExprNode).refersTo(package) and - defn.getAstNode() = package.getInitModule().getModule() - ) - or - exists(PackageObject package, string name | - use.(ImportMemberNode).getModule(name).refersTo(package) and - scope_jump_to_defn_attribute(package.getInitModule().getModule(), name, defn) - ) - or - (use instanceof PyFunctionObject or use instanceof ClassObject) and - defn.getAstNode() = use.getNode() + exists(EssaVariable var | + use = var.getASourceUse() and + ssa_variable_defn(var, defn) + ) + or + exists(string name | + use.isLoad() and + jump_to_defn_attribute(use.(AttrNode).getObject(name), name, defn) + ) + or + exists(PythonModuleObject mod | + use.(ImportExprNode).refersTo(mod) and + defn.getAstNode() = mod.getModule() + ) + or + exists(PythonModuleObject mod, string name | + use.(ImportMemberNode).getModule(name).refersTo(mod) and + scope_jump_to_defn_attribute(mod.getModule(), name, defn) + ) + or + exists(PackageObject package | + use.(ImportExprNode).refersTo(package) and + defn.getAstNode() = package.getInitModule().getModule() + ) + or + exists(PackageObject package, string name | + use.(ImportMemberNode).getModule(name).refersTo(package) and + scope_jump_to_defn_attribute(package.getInitModule().getModule(), name, defn) + ) + or + (use instanceof PyFunctionObject or use instanceof ClassObject) and + defn.getAstNode() = use.getNode() } /* Prefer class and functions to class-expressions and function-expressions. */ private predicate preferred_jump_to_defn(Expr use, Definition def) { - not use instanceof ClassExpr and - not use instanceof FunctionExpr and - jump_to_defn(use.getAFlowNode(), def) + not use instanceof ClassExpr and + not use instanceof FunctionExpr and + jump_to_defn(use.getAFlowNode(), def) } private predicate unique_jump_to_defn(Expr use, Definition def) { - preferred_jump_to_defn(use, def) and - not exists(Definition other | - other != def and - preferred_jump_to_defn(use, other) - ) + preferred_jump_to_defn(use, def) and + not exists(Definition other | + other != def and + preferred_jump_to_defn(use, other) + ) } private predicate ssa_variable_defn(EssaVariable var, Definition defn) { - ssa_defn_defn(var.getDefinition(), defn) + ssa_defn_defn(var.getDefinition(), defn) } /** Holds if the phi-function `phi` refers to (`value`, `cls`, `origin`) given the context `context`. */ private predicate ssa_phi_defn(PhiFunction phi, Definition defn) { - ssa_variable_defn(phi.getAnInput(), defn) + ssa_variable_defn(phi.getAnInput(), defn) } /** Holds if the ESSA defn `def` refers to (`value`, `cls`, `origin`) given the context `context`. */ private predicate ssa_defn_defn(EssaDefinition def, Definition defn) { - ssa_phi_defn(def, defn) - or - ssa_node_defn(def, defn) - or - ssa_filter_defn(def, defn) - or - ssa_node_refinement_defn(def, defn) + ssa_phi_defn(def, defn) + or + ssa_node_defn(def, defn) + or + ssa_filter_defn(def, defn) + or + ssa_node_refinement_defn(def, defn) } /** Holds if ESSA edge refinement, `def`, is defined by `defn` */ predicate ssa_filter_defn(PyEdgeRefinement def, Definition defn) { - ssa_variable_defn(def.getInput(), defn) + ssa_variable_defn(def.getInput(), defn) } /** Holds if ESSA defn, `uniphi`,is defined by `defn` */ predicate uni_edged_phi_defn(SingleSuccessorGuard uniphi, Definition defn) { - ssa_variable_defn(uniphi.getInput(), defn) + ssa_variable_defn(uniphi.getInput(), defn) } pragma[noinline] private predicate ssa_node_defn(EssaNodeDefinition def, Definition defn) { - assignment_jump_to_defn(def, defn) - or - parameter_defn(def, defn) - or - delete_defn(def, defn) - or - scope_entry_defn(def, defn) - or - implicit_submodule_defn(def, defn) + assignment_jump_to_defn(def, defn) + or + parameter_defn(def, defn) + or + delete_defn(def, defn) + or + scope_entry_defn(def, defn) + or + implicit_submodule_defn(def, defn) } /* Definition for normal assignments `def = ...` */ private predicate assignment_jump_to_defn(AssignmentDefinition def, Definition defn) { - defn = TLocalDefinition(def.getValue().getNode()) + defn = TLocalDefinition(def.getValue().getNode()) } pragma[noinline] private predicate ssa_node_refinement_defn(EssaNodeRefinement def, Definition defn) { - method_callsite_defn(def, defn) - or - import_star_defn(def, defn) - or - attribute_assignment_defn(def, defn) - or - callsite_defn(def, defn) - or - argument_defn(def, defn) - or - attribute_delete_defn(def, defn) - or - uni_edged_phi_defn(def, defn) + method_callsite_defn(def, defn) + or + import_star_defn(def, defn) + or + attribute_assignment_defn(def, defn) + or + callsite_defn(def, defn) + or + argument_defn(def, defn) + or + attribute_delete_defn(def, defn) + or + uni_edged_phi_defn(def, defn) } /* Definition for parameter. `def foo(param): ...` */ private predicate parameter_defn(ParameterDefinition def, Definition defn) { - defn.getAstNode() = def.getDefiningNode().getNode() + defn.getAstNode() = def.getDefiningNode().getNode() } /* Definition for deletion: `del name` */ @@ -145,11 +145,11 @@ private predicate delete_defn(DeletionDefinition def, Definition defn) { none() /* Implicit "defn" of the names of submodules at the start of an `__init__.py` file. */ private predicate implicit_submodule_defn(ImplicitSubModuleDefinition def, Definition defn) { - exists(PackageObject package, ModuleObject mod | - package.getInitModule().getModule() = def.getDefiningNode().getScope() and - mod = package.submodule(def.getSourceVariable().getName()) and - defn.getAstNode() = mod.getModule() - ) + exists(PackageObject package, ModuleObject mod | + package.getInitModule().getModule() = def.getDefiningNode().getScope() and + mod = package.submodule(def.getSourceVariable().getName()) and + defn.getAstNode() = mod.getModule() + ) } /* @@ -158,42 +158,42 @@ private predicate implicit_submodule_defn(ImplicitSubModuleDefinition def, Defin */ private predicate scope_entry_value_transfer_at_callsite( - EssaVariable pred_var, ScopeEntryDefinition succ_def + EssaVariable pred_var, ScopeEntryDefinition succ_def ) { - exists(CallNode callsite, FunctionObject f | - f.getACall() = callsite and - pred_var.getSourceVariable() = succ_def.getSourceVariable() and - pred_var.getAUse() = callsite and - succ_def.getDefiningNode() = f.getFunction().getEntryNode() - ) + exists(CallNode callsite, FunctionObject f | + f.getACall() = callsite and + pred_var.getSourceVariable() = succ_def.getSourceVariable() and + pred_var.getAUse() = callsite and + succ_def.getDefiningNode() = f.getFunction().getEntryNode() + ) } /* Model the transfer of values at scope-entry points. Transfer from `pred_var, pred_context` to `succ_def, succ_context` */ private predicate scope_entry_value_transfer(EssaVariable pred_var, ScopeEntryDefinition succ_def) { - BaseFlow::scope_entry_value_transfer_from_earlier(pred_var, _, succ_def, _) - or - scope_entry_value_transfer_at_callsite(pred_var, succ_def) - or - class_entry_value_transfer(pred_var, succ_def) + BaseFlow::scope_entry_value_transfer_from_earlier(pred_var, _, succ_def, _) + or + scope_entry_value_transfer_at_callsite(pred_var, succ_def) + or + class_entry_value_transfer(pred_var, succ_def) } /* Helper for scope_entry_value_transfer */ private predicate class_entry_value_transfer(EssaVariable pred_var, ScopeEntryDefinition succ_def) { - exists(ImportTimeScope scope, ControlFlowNode class_def | - class_def = pred_var.getAUse() and - scope.entryEdge(class_def, succ_def.getDefiningNode()) and - pred_var.getSourceVariable() = succ_def.getSourceVariable() - ) + exists(ImportTimeScope scope, ControlFlowNode class_def | + class_def = pred_var.getAUse() and + scope.entryEdge(class_def, succ_def.getDefiningNode()) and + pred_var.getSourceVariable() = succ_def.getSourceVariable() + ) } /* Definition for implicit variable declarations at scope-entry. */ pragma[noinline] private predicate scope_entry_defn(ScopeEntryDefinition def, Definition defn) { - /* Transfer from another scope */ - exists(EssaVariable var | - scope_entry_value_transfer(var, def) and - ssa_variable_defn(var, defn) - ) + /* Transfer from another scope */ + exists(EssaVariable var | + scope_entry_value_transfer(var, def) and + ssa_variable_defn(var, defn) + ) } /* @@ -203,73 +203,73 @@ private predicate scope_entry_defn(ScopeEntryDefinition def, Definition defn) { pragma[noinline] private predicate callsite_defn(CallsiteRefinement def, Definition defn) { - ssa_variable_defn(def.getInput(), defn) + ssa_variable_defn(def.getInput(), defn) } /* Pass through for `self` for the implicit re-defn of `self` in `self.foo()` */ private predicate method_callsite_defn(MethodCallsiteRefinement def, Definition defn) { - /* The value of self remains the same, only the attributes may change */ - ssa_variable_defn(def.getInput(), defn) + /* The value of self remains the same, only the attributes may change */ + ssa_variable_defn(def.getInput(), defn) } /** Helpers for import_star_defn */ pragma[noinline] private predicate module_and_name_for_import_star( - ModuleObject mod, string name, ImportStarRefinement def + ModuleObject mod, string name, ImportStarRefinement def ) { - exists(ImportStarNode im_star | - module_and_name_for_import_star_helper(mod, name, im_star, def) and - mod.exports(name) - ) + exists(ImportStarNode im_star | + module_and_name_for_import_star_helper(mod, name, im_star, def) and + mod.exports(name) + ) } pragma[noinline] private predicate module_and_name_for_import_star_helper( - ModuleObject mod, string name, ImportStarNode im_star, ImportStarRefinement def + ModuleObject mod, string name, ImportStarNode im_star, ImportStarRefinement def ) { - im_star = def.getDefiningNode() and - im_star.getModule().refersTo(mod) and - name = def.getSourceVariable().getName() + im_star = def.getDefiningNode() and + im_star.getModule().refersTo(mod) and + name = def.getSourceVariable().getName() } /** Holds if `def` is technically a defn of `var`, but the `from ... import *` does not in fact define `var` */ pragma[noinline] private predicate variable_not_redefined_by_import_star(EssaVariable var, ImportStarRefinement def) { - var = def.getInput() and - exists(ModuleObject mod | - def.getDefiningNode().(ImportStarNode).getModule().refersTo(mod) and - not mod.exports(var.getSourceVariable().getName()) - ) + var = def.getInput() and + exists(ModuleObject mod | + def.getDefiningNode().(ImportStarNode).getModule().refersTo(mod) and + not mod.exports(var.getSourceVariable().getName()) + ) } /* Definition for `from ... import *` */ private predicate import_star_defn(ImportStarRefinement def, Definition defn) { - exists(ModuleObject mod, string name | module_and_name_for_import_star(mod, name, def) | - /* Attribute from imported module */ - scope_jump_to_defn_attribute(mod.getModule(), name, defn) - ) - or - exists(EssaVariable var | - /* Retain value held before import */ - variable_not_redefined_by_import_star(var, def) and - ssa_variable_defn(var, defn) - ) + exists(ModuleObject mod, string name | module_and_name_for_import_star(mod, name, def) | + /* Attribute from imported module */ + scope_jump_to_defn_attribute(mod.getModule(), name, defn) + ) + or + exists(EssaVariable var | + /* Retain value held before import */ + variable_not_redefined_by_import_star(var, def) and + ssa_variable_defn(var, defn) + ) } /** Attribute assignments have no effect as far as defn tracking is concerned */ private predicate attribute_assignment_defn(AttributeAssignment def, Definition defn) { - ssa_variable_defn(def.getInput(), defn) + ssa_variable_defn(def.getInput(), defn) } /** Ignore the effects of calls on their arguments. This is an approximation, but attempting to improve accuracy would be very expensive for very little gain. */ private predicate argument_defn(ArgumentRefinement def, Definition defn) { - ssa_variable_defn(def.getInput(), defn) + ssa_variable_defn(def.getInput(), defn) } /** Attribute deletions have no effect as far as value tracking is concerned. */ pragma[noinline] private predicate attribute_delete_defn(EssaAttributeDeletion def, Definition defn) { - ssa_variable_defn(def.getInput(), defn) + ssa_variable_defn(def.getInput(), defn) } /* @@ -284,119 +284,119 @@ private predicate attribute_delete_defn(EssaAttributeDeletion def, Definition de * Holds if the attribute `name` of the ssa variable `var` refers to (`value`, `cls`, `origin`) */ predicate ssa_variable_jump_to_defn_attribute(EssaVariable var, string name, Definition defn) { - ssa_defn_jump_to_defn_attribute(var.getDefinition(), name, defn) + ssa_defn_jump_to_defn_attribute(var.getDefinition(), name, defn) } /** Helper for ssa_variable_jump_to_defn_attribute */ private predicate ssa_defn_jump_to_defn_attribute(EssaDefinition def, string name, Definition defn) { - ssa_phi_jump_to_defn_attribute(def, name, defn) - or - ssa_node_jump_to_defn_attribute(def, name, defn) - or - ssa_node_refinement_jump_to_defn_attribute(def, name, defn) - or - ssa_filter_jump_to_defn_attribute(def, name, defn) + ssa_phi_jump_to_defn_attribute(def, name, defn) + or + ssa_node_jump_to_defn_attribute(def, name, defn) + or + ssa_node_refinement_jump_to_defn_attribute(def, name, defn) + or + ssa_filter_jump_to_defn_attribute(def, name, defn) } /** Holds if ESSA edge refinement, `def`, is defined by `defn` of `priority` */ predicate ssa_filter_jump_to_defn_attribute(PyEdgeRefinement def, string name, Definition defn) { - ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn) + ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn) } /** Holds if the attribute `name` of the ssa phi-function defn `phi` refers to (`value`, `cls`, `origin`) */ private predicate ssa_phi_jump_to_defn_attribute(PhiFunction phi, string name, Definition defn) { - ssa_variable_jump_to_defn_attribute(phi.getAnInput(), name, defn) + ssa_variable_jump_to_defn_attribute(phi.getAnInput(), name, defn) } /** Helper for ssa_defn_jump_to_defn_attribute */ pragma[noinline] private predicate ssa_node_jump_to_defn_attribute( - EssaNodeDefinition def, string name, Definition defn + EssaNodeDefinition def, string name, Definition defn ) { - assignment_jump_to_defn_attribute(def, name, defn) - or - self_parameter_jump_to_defn_attribute(def, name, defn) - or - scope_entry_jump_to_defn_attribute(def, name, defn) + assignment_jump_to_defn_attribute(def, name, defn) + or + self_parameter_jump_to_defn_attribute(def, name, defn) + or + scope_entry_jump_to_defn_attribute(def, name, defn) } /** Helper for ssa_defn_jump_to_defn_attribute */ pragma[noinline] private predicate ssa_node_refinement_jump_to_defn_attribute( - EssaNodeRefinement def, string name, Definition defn + EssaNodeRefinement def, string name, Definition defn ) { - attribute_assignment_jump_to_defn_attribute(def, name, defn) - or - argument_jump_to_defn_attribute(def, name, defn) + attribute_assignment_jump_to_defn_attribute(def, name, defn) + or + argument_jump_to_defn_attribute(def, name, defn) } pragma[noinline] private predicate scope_entry_jump_to_defn_attribute( - ScopeEntryDefinition def, string name, Definition defn + ScopeEntryDefinition def, string name, Definition defn ) { - exists(EssaVariable var | - scope_entry_value_transfer(var, def) and - ssa_variable_jump_to_defn_attribute(var, name, defn) - ) + exists(EssaVariable var | + scope_entry_value_transfer(var, def) and + ssa_variable_jump_to_defn_attribute(var, name, defn) + ) } private predicate scope_jump_to_defn_attribute(ImportTimeScope s, string name, Definition defn) { - exists(EssaVariable var | - BaseFlow::reaches_exit(var) and - var.getScope() = s and - var.getName() = name - | - ssa_variable_defn(var, defn) - ) + exists(EssaVariable var | + BaseFlow::reaches_exit(var) and + var.getScope() = s and + var.getName() = name + | + ssa_variable_defn(var, defn) + ) } private predicate jump_to_defn_attribute(ControlFlowNode use, string name, Definition defn) { - /* Local attribute */ - exists(EssaVariable var | - use = var.getASourceUse() and - ssa_variable_jump_to_defn_attribute(var, name, defn) - ) + /* Local attribute */ + exists(EssaVariable var | + use = var.getASourceUse() and + ssa_variable_jump_to_defn_attribute(var, name, defn) + ) + or + /* Instance attributes */ + exists(ClassObject cls | use.refersTo(_, cls, _) | + scope_jump_to_defn_attribute(cls.getPyClass(), name, defn) + ) + or + /* Super attributes */ + exists(AttrNode f, SuperBoundMethod sbm, Object function | + use = f.getObject(name) and + f.refersTo(sbm) and + function = sbm.getFunction(_) and + function.getOrigin() = defn.getAstNode() + ) + or + /* Class or module attribute */ + exists(Object obj, Scope scope | + use.refersTo(obj) and + scope_jump_to_defn_attribute(scope, name, defn) + | + obj.(ClassObject).getPyClass() = scope or - /* Instance attributes */ - exists(ClassObject cls | use.refersTo(_, cls, _) | - scope_jump_to_defn_attribute(cls.getPyClass(), name, defn) - ) + obj.(PythonModuleObject).getModule() = scope or - /* Super attributes */ - exists(AttrNode f, SuperBoundMethod sbm, Object function | - use = f.getObject(name) and - f.refersTo(sbm) and - function = sbm.getFunction(_) and - function.getOrigin() = defn.getAstNode() - ) - or - /* Class or module attribute */ - exists(Object obj, Scope scope | - use.refersTo(obj) and - scope_jump_to_defn_attribute(scope, name, defn) - | - obj.(ClassObject).getPyClass() = scope - or - obj.(PythonModuleObject).getModule() = scope - or - obj.(PackageObject).getInitModule().getModule() = scope - ) + obj.(PackageObject).getInitModule().getModule() = scope + ) } pragma[noinline] private predicate assignment_jump_to_defn_attribute( - AssignmentDefinition def, string name, Definition defn + AssignmentDefinition def, string name, Definition defn ) { - jump_to_defn_attribute(def.getValue(), name, defn) + jump_to_defn_attribute(def.getValue(), name, defn) } pragma[noinline] private predicate attribute_assignment_jump_to_defn_attribute( - AttributeAssignment def, string name, Definition defn + AttributeAssignment def, string name, Definition defn ) { - defn.getAstNode() = def.getDefiningNode().getNode() and name = def.getName() - or - ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn) and not name = def.getName() + defn.getAstNode() = def.getDefiningNode().getNode() and name = def.getName() + or + ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn) and not name = def.getName() } /** @@ -404,42 +404,42 @@ private predicate attribute_assignment_jump_to_defn_attribute( * `def` takes the form `setattr(use, "name")` where `use` is the input to the defn. */ private predicate sets_attribute(ArgumentRefinement def, string name) { - exists(CallNode call | - call = def.getDefiningNode() and - call.getFunction().refersTo(Object::builtin("setattr")) and - def.getInput().getAUse() = call.getArg(0) and - call.getArg(1).getNode().(StrConst).getText() = name - ) + exists(CallNode call | + call = def.getDefiningNode() and + call.getFunction().refersTo(Object::builtin("setattr")) and + def.getInput().getAUse() = call.getArg(0) and + call.getArg(1).getNode().(StrConst).getText() = name + ) } pragma[noinline] private predicate argument_jump_to_defn_attribute( - ArgumentRefinement def, string name, Definition defn + ArgumentRefinement def, string name, Definition defn ) { - if sets_attribute(def, name) - then jump_to_defn(def.getDefiningNode().(CallNode).getArg(2), defn) - else ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn) + if sets_attribute(def, name) + then jump_to_defn(def.getDefiningNode().(CallNode).getArg(2), defn) + else ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn) } /** Gets the (temporally) preceding variable for "self", e.g. `def` is in method foo() and `result` is in `__init__()`. */ private EssaVariable preceding_self_variable(ParameterDefinition def) { - def.isSelf() and - exists(Function preceding, Function method | - method = def.getScope() and - // Only methods - preceding.isMethod() and - preceding.precedes(method) and - BaseFlow::reaches_exit(result) and - result.getSourceVariable().(Variable).isSelf() and - result.getScope() = preceding - ) + def.isSelf() and + exists(Function preceding, Function method | + method = def.getScope() and + // Only methods + preceding.isMethod() and + preceding.precedes(method) and + BaseFlow::reaches_exit(result) and + result.getSourceVariable().(Variable).isSelf() and + result.getScope() = preceding + ) } pragma[noinline] private predicate self_parameter_jump_to_defn_attribute( - ParameterDefinition def, string name, Definition defn + ParameterDefinition def, string name, Definition defn ) { - ssa_variable_jump_to_defn_attribute(preceding_self_variable(def), name, defn) + ssa_variable_jump_to_defn_attribute(preceding_self_variable(def), name, defn) } /** @@ -447,11 +447,11 @@ private predicate self_parameter_jump_to_defn_attribute( * This exists primarily for testing use `getPreferredDefinition()` instead. */ Definition getADefinition(Expr use) { - jump_to_defn(use.getAFlowNode(), result) and - not use instanceof Call and - not use.isArtificial() and - // Not the use itself - not result = TLocalDefinition(use) + jump_to_defn(use.getAFlowNode(), result) and + not use instanceof Call and + not use.isArtificial() and + // Not the use itself + not result = TLocalDefinition(use) } /** @@ -459,44 +459,45 @@ Definition getADefinition(Expr use) { * Helper for the jump-to-definition query. */ Definition getUniqueDefinition(Expr use) { - unique_jump_to_defn(use, result) and - not use instanceof Call and - not use.isArtificial() and - // Not the use itself - not result = TLocalDefinition(use) + unique_jump_to_defn(use, result) and + not use instanceof Call and + not use.isArtificial() and + // Not the use itself + not result = TLocalDefinition(use) } /** Helper class to get suitable locations for attributes */ class NiceLocationExpr extends @py_expr { - /** Gets a textual representation of this element. */ - string toString() { result = this.(Expr).toString() } - /** - * Holds if this element is at the specified location. - * The location spans column `bc` of line `bl` to - * column `ec` of line `el` in file `f`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo(string f, int bl, int bc, int el, int ec) { - /* Attribute location for x.y is that of 'y' so that url does not overlap with that of 'x' */ - exists(int abl, int abc | this.(Attribute).getLocation().hasLocationInfo(f, abl, abc, el, ec) | - bl = el and bc = ec - this.(Attribute).getName().length() + 1 - ) - or - this.(Name).getLocation().hasLocationInfo(f, bl, bc, el, ec) - or - // Show xxx for `xxx` in `from xxx import y` or - // for `import xxx` or for `import xxx as yyy`. - this.(ImportExpr).getLocation().hasLocationInfo(f, bl, bc, el, ec) - or - /* Show y for `y` in `from xxx import y` */ - exists(string name | - name = this.(ImportMember).getName() and - this.(ImportMember).getLocation().hasLocationInfo(f, _, _, el, ec) and - bl = el and - bc = ec - name.length() + 1 - ) - } + /** Gets a textual representation of this element. */ + string toString() { result = this.(Expr).toString() } + + /** + * Holds if this element is at the specified location. + * The location spans column `bc` of line `bl` to + * column `ec` of line `el` in file `f`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo(string f, int bl, int bc, int el, int ec) { + /* Attribute location for x.y is that of 'y' so that url does not overlap with that of 'x' */ + exists(int abl, int abc | this.(Attribute).getLocation().hasLocationInfo(f, abl, abc, el, ec) | + bl = el and bc = ec - this.(Attribute).getName().length() + 1 + ) + or + this.(Name).getLocation().hasLocationInfo(f, bl, bc, el, ec) + or + // Show xxx for `xxx` in `from xxx import y` or + // for `import xxx` or for `import xxx as yyy`. + this.(ImportExpr).getLocation().hasLocationInfo(f, bl, bc, el, ec) + or + /* Show y for `y` in `from xxx import y` */ + exists(string name | + name = this.(ImportMember).getName() and + this.(ImportMember).getLocation().hasLocationInfo(f, _, _, el, ec) and + bl = el and + bc = ec - name.length() + 1 + ) + } } /** @@ -504,13 +505,13 @@ class NiceLocationExpr extends @py_expr { */ cached Definition definitionOf(NiceLocationExpr use, string kind) { - exists(string f, int l | - result = getUniqueDefinition(use) and - kind = "Definition" and - use.hasLocationInfo(f, l, _, _, _) and - // Ignore if the definition is on the same line as the use - not result.getLocation().hasLocationInfo(f, l, _, _, _) - ) + exists(string f, int l | + result = getUniqueDefinition(use) and + kind = "Definition" and + use.hasLocationInfo(f, l, _, _, _) and + // Ignore if the definition is on the same line as the use + not result.getLocation().hasLocationInfo(f, l, _, _, _) + ) } /** diff --git a/python/ql/src/analysis/Definitions.ql b/python/ql/src/analysis/Definitions.ql index b6b38708943..a2ed4f36bf6 100644 --- a/python/ql/src/analysis/Definitions.ql +++ b/python/ql/src/analysis/Definitions.ql @@ -10,4 +10,4 @@ import DefinitionTracking from NiceLocationExpr use, Definition defn, string kind where defn = definitionOf(use, kind) -select use, defn, kind \ No newline at end of file +select use, defn, kind diff --git a/python/ql/src/analysis/Efficiency.ql b/python/ql/src/analysis/Efficiency.ql index 634d670dc77..7f08e30502e 100644 --- a/python/ql/src/analysis/Efficiency.ql +++ b/python/ql/src/analysis/Efficiency.ql @@ -8,27 +8,27 @@ import semmle.python.pointsto.PointsTo import semmle.python.pointsto.PointsToContext predicate trivial(ControlFlowNode f) { - exists(Parameter p | p = f.getNode()) - or - f instanceof NameConstantNode - or - f.getNode() instanceof ImmutableLiteral + exists(Parameter p | p = f.getNode()) + or + f instanceof NameConstantNode + or + f.getNode() instanceof ImmutableLiteral } from int interesting_facts, int interesting_facts_in_source, int total_size, float efficiency where - interesting_facts = - strictcount(ControlFlowNode f, Object value, ClassObject cls | - f.refersTo(value, cls, _) and not trivial(f) - ) and - interesting_facts_in_source = - strictcount(ControlFlowNode f, Object value, ClassObject cls | - f.refersTo(value, cls, _) and - not trivial(f) and - exists(f.getScope().getEnclosingModule().getFile().getRelativePath()) - ) and - total_size = - strictcount(ControlFlowNode f, PointsToContext ctx, Object value, ClassObject cls, - ControlFlowNode orig | PointsTo::points_to(f, ctx, value, cls, orig)) and - efficiency = 100.0 * interesting_facts_in_source / total_size + interesting_facts = + strictcount(ControlFlowNode f, Object value, ClassObject cls | + f.refersTo(value, cls, _) and not trivial(f) + ) and + interesting_facts_in_source = + strictcount(ControlFlowNode f, Object value, ClassObject cls | + f.refersTo(value, cls, _) and + not trivial(f) and + exists(f.getScope().getEnclosingModule().getFile().getRelativePath()) + ) and + total_size = + strictcount(ControlFlowNode f, PointsToContext ctx, Object value, ClassObject cls, + ControlFlowNode orig | PointsTo::points_to(f, ctx, value, cls, orig)) and + efficiency = 100.0 * interesting_facts_in_source / total_size select interesting_facts, interesting_facts_in_source, total_size, efficiency diff --git a/python/ql/src/analysis/ImportFailure.ql b/python/ql/src/analysis/ImportFailure.ql index 9c944f9ae83..9ed1fd001c8 100644 --- a/python/ql/src/analysis/ImportFailure.ql +++ b/python/ql/src/analysis/ImportFailure.ql @@ -9,75 +9,75 @@ import python ImportExpr alternative_import(ImportExpr ie) { - exists(Alias thisalias, Alias otheralias | - (thisalias.getValue() = ie or thisalias.getValue().(ImportMember).getModule() = ie) and - (otheralias.getValue() = result or otheralias.getValue().(ImportMember).getModule() = result) and - ( - exists(If i | i.getBody().contains(ie) and i.getOrelse().contains(result)) - or - exists(If i | i.getBody().contains(result) and i.getOrelse().contains(ie)) - or - exists(Try t | t.getBody().contains(ie) and t.getAHandler().contains(result)) - or - exists(Try t | t.getBody().contains(result) and t.getAHandler().contains(ie)) - ) + exists(Alias thisalias, Alias otheralias | + (thisalias.getValue() = ie or thisalias.getValue().(ImportMember).getModule() = ie) and + (otheralias.getValue() = result or otheralias.getValue().(ImportMember).getModule() = result) and + ( + exists(If i | i.getBody().contains(ie) and i.getOrelse().contains(result)) + or + exists(If i | i.getBody().contains(result) and i.getOrelse().contains(ie)) + or + exists(Try t | t.getBody().contains(ie) and t.getAHandler().contains(result)) + or + exists(Try t | t.getBody().contains(result) and t.getAHandler().contains(ie)) ) + ) } string os_specific_import(ImportExpr ie) { - exists(string name | name = ie.getImportedModuleName() | - name.matches("org.python.%") and result = "java" - or - name.matches("java.%") and result = "java" - or - name.matches("Carbon.%") and result = "darwin" - or - result = "win32" and - ( - name = "_winapi" or - name = "_win32api" or - name = "_winreg" or - name = "nt" or - name.matches("win32%") or - name = "ntpath" - ) - or - result = "linux2" and - (name = "posix" or name = "posixpath") - or - result = "unsupported" and - (name = "__pypy__" or name = "ce" or name.matches("riscos%")) + exists(string name | name = ie.getImportedModuleName() | + name.matches("org.python.%") and result = "java" + or + name.matches("java.%") and result = "java" + or + name.matches("Carbon.%") and result = "darwin" + or + result = "win32" and + ( + name = "_winapi" or + name = "_win32api" or + name = "_winreg" or + name = "nt" or + name.matches("win32%") or + name = "ntpath" ) + or + result = "linux2" and + (name = "posix" or name = "posixpath") + or + result = "unsupported" and + (name = "__pypy__" or name = "ce" or name.matches("riscos%")) + ) } string get_os() { py_flags_versioned("sys.platform", result, major_version().toString()) } predicate ok_to_fail(ImportExpr ie) { - alternative_import(ie).refersTo(_) - or - os_specific_import(ie) != get_os() + alternative_import(ie).refersTo(_) + or + os_specific_import(ie) != get_os() } class VersionTest extends @py_flow_node { - VersionTest() { - exists(string name | - name.matches("%version%") and - this.(CompareNode).getAChild+().pointsTo(Module::named("sys").attr(name)) - ) - } + VersionTest() { + exists(string name | + name.matches("%version%") and + this.(CompareNode).getAChild+().pointsTo(Module::named("sys").attr(name)) + ) + } - string toString() { result = "VersionTest" } + string toString() { result = "VersionTest" } } /** A guard on the version of the Python interpreter */ class VersionGuard extends ConditionBlock { - VersionGuard() { this.getLastNode() instanceof VersionTest } + VersionGuard() { this.getLastNode() instanceof VersionTest } } from ImportExpr ie where - not ie.refersTo(_) and - exists(Context c | c.appliesTo(ie.getAFlowNode())) and - not ok_to_fail(ie) and - not exists(VersionGuard guard | guard.controls(ie.getAFlowNode().getBasicBlock(), _)) + not ie.refersTo(_) and + exists(Context c | c.appliesTo(ie.getAFlowNode())) and + not ok_to_fail(ie) and + not exists(VersionGuard guard | guard.controls(ie.getAFlowNode().getBasicBlock(), _)) select ie, "Unable to resolve import of '" + ie.getImportedModuleName() + "'." diff --git a/python/ql/src/analysis/KeyPointsToFailure.ql b/python/ql/src/analysis/KeyPointsToFailure.ql index 6cff87dbe85..d869d547c75 100644 --- a/python/ql/src/analysis/KeyPointsToFailure.ql +++ b/python/ql/src/analysis/KeyPointsToFailure.ql @@ -11,16 +11,16 @@ import python import semmle.python.pointsto.PointsTo predicate points_to_failure(Expr e) { - exists(ControlFlowNode f | f = e.getAFlowNode() | not PointsTo::pointsTo(f, _, _, _)) + exists(ControlFlowNode f | f = e.getAFlowNode() | not PointsTo::pointsTo(f, _, _, _)) } predicate key_points_to_failure(Expr e) { - points_to_failure(e) and - not points_to_failure(e.getASubExpression()) and - not exists(SsaVariable ssa | ssa.getAUse() = e.getAFlowNode() | - points_to_failure(ssa.getAnUltimateDefinition().getDefinition().getNode()) - ) and - not exists(Assign a | a.getATarget() = e) + points_to_failure(e) and + not points_to_failure(e.getASubExpression()) and + not exists(SsaVariable ssa | ssa.getAUse() = e.getAFlowNode() | + points_to_failure(ssa.getAnUltimateDefinition().getDefinition().getNode()) + ) and + not exists(Assign a | a.getATarget() = e) } from Attribute e diff --git a/python/ql/src/analysis/LocalDefinitions.ql b/python/ql/src/analysis/LocalDefinitions.ql index 89d0b95e0a3..f9f3da0cdee 100644 --- a/python/ql/src/analysis/LocalDefinitions.ql +++ b/python/ql/src/analysis/LocalDefinitions.ql @@ -13,7 +13,8 @@ import DefinitionTracking external string selectedSourceFile(); from NiceLocationExpr use, Definition defn, string kind, string f -where defn = definitionOf(use, kind) -and use.hasLocationInfo(f, _, _, _, _) -and getEncodedFile(selectedSourceFile()).getAbsolutePath() = f -select use, defn, kind \ No newline at end of file +where + defn = definitionOf(use, kind) and + use.hasLocationInfo(f, _, _, _, _) and + getEncodedFile(selectedSourceFile()).getAbsolutePath() = f +select use, defn, kind diff --git a/python/ql/src/analysis/LocalReferences.ql b/python/ql/src/analysis/LocalReferences.ql index 9cb812903d6..21e600b4ac1 100644 --- a/python/ql/src/analysis/LocalReferences.ql +++ b/python/ql/src/analysis/LocalReferences.ql @@ -13,6 +13,7 @@ import DefinitionTracking external string selectedSourceFile(); from NiceLocationExpr use, Definition defn, string kind -where defn = definitionOf(use, kind) -and defn.getLocation().getFile() = getEncodedFile(selectedSourceFile()) -select use, defn, kind \ No newline at end of file +where + defn = definitionOf(use, kind) and + defn.getLocation().getFile() = getEncodedFile(selectedSourceFile()) +select use, defn, kind diff --git a/python/ql/src/analysis/RatioOfDefinitions.ql b/python/ql/src/analysis/RatioOfDefinitions.ql index 72a3fed0e69..f7ef4741ee6 100644 --- a/python/ql/src/analysis/RatioOfDefinitions.ql +++ b/python/ql/src/analysis/RatioOfDefinitions.ql @@ -6,21 +6,21 @@ import python import DefinitionTracking predicate want_to_have_definition(Expr e) { - /* not builtin object like len, tuple, etc. */ - not exists(Value builtin | e.pointsTo(builtin) and builtin.isBuiltin()) and - ( - e instanceof Name and e.(Name).getCtx() instanceof Load - or - e instanceof Attribute and e.(Attribute).getCtx() instanceof Load - or - e instanceof ImportMember - or - e instanceof ImportExpr - ) + /* not builtin object like len, tuple, etc. */ + not exists(Value builtin | e.pointsTo(builtin) and builtin.isBuiltin()) and + ( + e instanceof Name and e.(Name).getCtx() instanceof Load + or + e instanceof Attribute and e.(Attribute).getCtx() instanceof Load + or + e instanceof ImportMember + or + e instanceof ImportExpr + ) } from int yes, int no where - yes = count(Expr e | want_to_have_definition(e) and exists(getUniqueDefinition(e))) and - no = count(Expr e | want_to_have_definition(e) and not exists(getUniqueDefinition(e))) + yes = count(Expr e | want_to_have_definition(e) and exists(getUniqueDefinition(e))) and + no = count(Expr e | want_to_have_definition(e) and not exists(getUniqueDefinition(e))) select yes, no, yes * 100 / (yes + no) + "%" diff --git a/python/ql/src/analysis/Summary.ql b/python/ql/src/analysis/Summary.ql index 55564edb16e..6eb92dd935d 100644 --- a/python/ql/src/analysis/Summary.ql +++ b/python/ql/src/analysis/Summary.ql @@ -6,38 +6,38 @@ import python from string key, string value where - key = "Extractor version" and py_flags_versioned("extractor.version", value, _) - or - key = "Snapshot build time" and - exists(date d | snapshotDate(d) and value = d.toString()) - or - key = "Interpreter version" and - exists(string major, string minor | - py_flags_versioned("version.major", major, _) and - py_flags_versioned("version.minor", minor, _) and - value = major + "." + minor - ) - or - key = "Build platform" and - exists(string raw | py_flags_versioned("sys.platform", raw, _) | - if raw = "win32" - then value = "Windows" - else - if raw = "linux2" - then value = "Linux" - else - if raw = "darwin" - then value = "OSX" - else value = raw - ) - or - key = "Source location" and sourceLocationPrefix(value) - or - key = "Lines of code (source)" and - value = - sum(ModuleMetrics m | exists(m.getFile().getRelativePath()) | m.getNumberOfLinesOfCode()) - .toString() - or - key = "Lines of code (total)" and - value = sum(ModuleMetrics m | any() | m.getNumberOfLinesOfCode()).toString() + key = "Extractor version" and py_flags_versioned("extractor.version", value, _) + or + key = "Snapshot build time" and + exists(date d | snapshotDate(d) and value = d.toString()) + or + key = "Interpreter version" and + exists(string major, string minor | + py_flags_versioned("version.major", major, _) and + py_flags_versioned("version.minor", minor, _) and + value = major + "." + minor + ) + or + key = "Build platform" and + exists(string raw | py_flags_versioned("sys.platform", raw, _) | + if raw = "win32" + then value = "Windows" + else + if raw = "linux2" + then value = "Linux" + else + if raw = "darwin" + then value = "OSX" + else value = raw + ) + or + key = "Source location" and sourceLocationPrefix(value) + or + key = "Lines of code (source)" and + value = + sum(ModuleMetrics m | exists(m.getFile().getRelativePath()) | m.getNumberOfLinesOfCode()) + .toString() + or + key = "Lines of code (total)" and + value = sum(ModuleMetrics m | any() | m.getNumberOfLinesOfCode()).toString() select key, value diff --git a/python/ql/src/analysis/TypeInferenceFailure.ql b/python/ql/src/analysis/TypeInferenceFailure.ql index 0e6e42e8385..d863d6bb2cc 100644 --- a/python/ql/src/analysis/TypeInferenceFailure.ql +++ b/python/ql/src/analysis/TypeInferenceFailure.ql @@ -11,6 +11,6 @@ import python from ControlFlowNode f, Object o where - f.refersTo(o) and - not exists(ClassObject c | f.refersTo(o, c, _)) + f.refersTo(o) and + not exists(ClassObject c | f.refersTo(o, c, _)) select o, "Type inference fails for 'object'." diff --git a/python/ql/src/experimental/dataflow/TaintTracking.qll b/python/ql/src/experimental/dataflow/TaintTracking.qll index b6c14f2d776..c74684803ad 100644 --- a/python/ql/src/experimental/dataflow/TaintTracking.qll +++ b/python/ql/src/experimental/dataflow/TaintTracking.qll @@ -16,4 +16,4 @@ import python */ module TaintTracking { import experimental.dataflow.internal.tainttracking1.TaintTrackingImpl -} \ No newline at end of file +} diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImplSpecific.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImplSpecific.qll index 8e18162df56..e88726b158b 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImplSpecific.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImplSpecific.qll @@ -3,7 +3,7 @@ */ module Private { import DataFlowPrivate -// import DataFlowDispatch + // import DataFlowDispatch } module Public { diff --git a/python/ql/src/experimental/dataflow/internal/TaintTrackingPublic.qll b/python/ql/src/experimental/dataflow/internal/TaintTrackingPublic.qll index 28e254613ca..d929d6ce014 100644 --- a/python/ql/src/experimental/dataflow/internal/TaintTrackingPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/TaintTrackingPublic.qll @@ -6,13 +6,11 @@ private import python private import TaintTrackingPrivate private import experimental.dataflow.DataFlow - // /** // * Holds if taint propagates from `source` to `sink` in zero or more local // * (intra-procedural) steps. // */ // predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) } - // // /** // // * Holds if taint can flow from `e1` to `e2` in zero or more // // * local (intra-procedural) steps. @@ -20,10 +18,8 @@ private import experimental.dataflow.DataFlow // // predicate localExprTaint(Expr e1, Expr e2) { // // localTaint(DataFlow::exprNode(e1), DataFlow::exprNode(e2)) // // } - // // /** A member (property or field) that is tainted if its containing object is tainted. */ // // abstract class TaintedMember extends AssignableMember { } - // /** // * Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local // * (intra-procedural) step. @@ -33,4 +29,4 @@ private import experimental.dataflow.DataFlow // DataFlow::localFlowStep(nodeFrom, nodeTo) // or // localAdditionalTaintStep(nodeFrom, nodeTo) -// } \ No newline at end of file +// } diff --git a/python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingParameter.qll b/python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingParameter.qll index 7f9fb029103..f1b2b94d3aa 100644 --- a/python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingParameter.qll +++ b/python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingParameter.qll @@ -3,4 +3,4 @@ import experimental.dataflow.internal.TaintTrackingPublic as Public module Private { import experimental.dataflow.DataFlow::DataFlow as DataFlow import experimental.dataflow.internal.TaintTrackingPrivate -} \ No newline at end of file +} diff --git a/python/ql/src/external/CodeDuplication.qll b/python/ql/src/external/CodeDuplication.qll index 85b5fda8fbb..01ab28d955b 100644 --- a/python/ql/src/external/CodeDuplication.qll +++ b/python/ql/src/external/CodeDuplication.qll @@ -13,80 +13,80 @@ private string relativePath(File file) { result = file.getRelativePath().replace */ pragma[noinline, nomagic] private predicate tokenLocation(File file, int sl, int sc, int ec, int el, Copy copy, int index) { - file = copy.sourceFile() and - tokens(copy, index, sl, sc, ec, el) + file = copy.sourceFile() and + tokens(copy, index, sl, sc, ec, el) } /** A token block used for detection of duplicate and similar code. */ class Copy extends @duplication_or_similarity { - private int lastToken() { result = max(int i | tokens(this, i, _, _, _, _) | i) } + private int lastToken() { result = max(int i | tokens(this, i, _, _, _, _) | i) } - /** Gets the index of the token in this block starting at the location `loc`, if any. */ - int tokenStartingAt(Location loc) { - tokenLocation(loc.getFile(), loc.getStartLine(), loc.getStartColumn(), _, _, this, result) - } + /** Gets the index of the token in this block starting at the location `loc`, if any. */ + int tokenStartingAt(Location loc) { + tokenLocation(loc.getFile(), loc.getStartLine(), loc.getStartColumn(), _, _, this, result) + } - /** Gets the index of the token in this block ending at the location `loc`, if any. */ - int tokenEndingAt(Location loc) { - tokenLocation(loc.getFile(), _, _, loc.getEndLine(), loc.getEndColumn(), this, result) - } + /** Gets the index of the token in this block ending at the location `loc`, if any. */ + int tokenEndingAt(Location loc) { + tokenLocation(loc.getFile(), _, _, loc.getEndLine(), loc.getEndColumn(), this, result) + } - /** Gets the line on which the first token in this block starts. */ - int sourceStartLine() { tokens(this, 0, result, _, _, _) } + /** Gets the line on which the first token in this block starts. */ + int sourceStartLine() { tokens(this, 0, result, _, _, _) } - /** Gets the column on which the first token in this block starts. */ - int sourceStartColumn() { tokens(this, 0, _, result, _, _) } + /** Gets the column on which the first token in this block starts. */ + int sourceStartColumn() { tokens(this, 0, _, result, _, _) } - /** Gets the line on which the last token in this block ends. */ - int sourceEndLine() { tokens(this, this.lastToken(), _, _, result, _) } + /** Gets the line on which the last token in this block ends. */ + int sourceEndLine() { tokens(this, this.lastToken(), _, _, result, _) } - /** Gets the column on which the last token in this block ends. */ - int sourceEndColumn() { tokens(this, this.lastToken(), _, _, _, result) } + /** Gets the column on which the last token in this block ends. */ + int sourceEndColumn() { tokens(this, this.lastToken(), _, _, _, result) } - /** Gets the number of lines containing at least (part of) one token in this block. */ - int sourceLines() { result = this.sourceEndLine() + 1 - this.sourceStartLine() } + /** Gets the number of lines containing at least (part of) one token in this block. */ + int sourceLines() { result = this.sourceEndLine() + 1 - this.sourceStartLine() } - /** Gets an opaque identifier for the equivalence class of this block. */ - int getEquivalenceClass() { duplicateCode(this, _, result) or similarCode(this, _, result) } + /** Gets an opaque identifier for the equivalence class of this block. */ + int getEquivalenceClass() { duplicateCode(this, _, result) or similarCode(this, _, result) } - /** Gets the source file in which this block appears. */ - File sourceFile() { - exists(string name | duplicateCode(this, name, _) or similarCode(this, name, _) | - name.replaceAll("\\", "/") = relativePath(result) - ) - } + /** Gets the source file in which this block appears. */ + File sourceFile() { + exists(string name | duplicateCode(this, name, _) or similarCode(this, name, _) | + name.replaceAll("\\", "/") = relativePath(result) + ) + } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - sourceFile().getAbsolutePath() = filepath and - startline = sourceStartLine() and - startcolumn = sourceStartColumn() and - endline = sourceEndLine() and - endcolumn = sourceEndColumn() - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + sourceFile().getAbsolutePath() = filepath and + startline = sourceStartLine() and + startcolumn = sourceStartColumn() and + endline = sourceEndLine() and + endcolumn = sourceEndColumn() + } - /** Gets a textual representation of this element. */ - string toString() { result = "Copy" } + /** Gets a textual representation of this element. */ + string toString() { result = "Copy" } - /** - * Gets a block that extends this one, that is, its first token is also - * covered by this block, but they are not the same block. - */ - Copy extendingBlock() { - exists(File file, int sl, int sc, int ec, int el | - tokenLocation(file, sl, sc, ec, el, this, _) and - tokenLocation(file, sl, sc, ec, el, result, 0) - ) and - this != result - } + /** + * Gets a block that extends this one, that is, its first token is also + * covered by this block, but they are not the same block. + */ + Copy extendingBlock() { + exists(File file, int sl, int sc, int ec, int el | + tokenLocation(file, sl, sc, ec, el, this, _) and + tokenLocation(file, sl, sc, ec, el, result, 0) + ) and + this != result + } } /** @@ -96,18 +96,18 @@ class Copy extends @duplication_or_similarity { * `start2`, and `end` the equivalence class of `end1` and `end2`. */ predicate similar_extension( - SimilarBlock start1, SimilarBlock start2, SimilarBlock ext1, SimilarBlock ext2, int start, int ext + SimilarBlock start1, SimilarBlock start2, SimilarBlock ext1, SimilarBlock ext2, int start, int ext ) { - start1.getEquivalenceClass() = start and - start2.getEquivalenceClass() = start and - ext1.getEquivalenceClass() = ext and - ext2.getEquivalenceClass() = ext and - start1 != start2 and - ( - ext1 = start1 and ext2 = start2 - or - similar_extension(start1.extendingBlock(), start2.extendingBlock(), ext1, ext2, _, ext) - ) + start1.getEquivalenceClass() = start and + start2.getEquivalenceClass() = start and + ext1.getEquivalenceClass() = ext and + ext2.getEquivalenceClass() = ext and + start1 != start2 and + ( + ext1 = start1 and ext2 = start2 + or + similar_extension(start1.extendingBlock(), start2.extendingBlock(), ext1, ext2, _, ext) + ) } /** @@ -117,31 +117,31 @@ predicate similar_extension( * `start2`, and `end` the equivalence class of `end1` and `end2`. */ predicate duplicate_extension( - DuplicateBlock start1, DuplicateBlock start2, DuplicateBlock ext1, DuplicateBlock ext2, int start, - int ext + DuplicateBlock start1, DuplicateBlock start2, DuplicateBlock ext1, DuplicateBlock ext2, int start, + int ext ) { - start1.getEquivalenceClass() = start and - start2.getEquivalenceClass() = start and - ext1.getEquivalenceClass() = ext and - ext2.getEquivalenceClass() = ext and - start1 != start2 and - ( - ext1 = start1 and ext2 = start2 - or - duplicate_extension(start1.extendingBlock(), start2.extendingBlock(), ext1, ext2, _, ext) - ) + start1.getEquivalenceClass() = start and + start2.getEquivalenceClass() = start and + ext1.getEquivalenceClass() = ext and + ext2.getEquivalenceClass() = ext and + start1 != start2 and + ( + ext1 = start1 and ext2 = start2 + or + duplicate_extension(start1.extendingBlock(), start2.extendingBlock(), ext1, ext2, _, ext) + ) } /** A block of duplicated code. */ class DuplicateBlock extends Copy, @duplication { - override string toString() { result = "Duplicate code: " + sourceLines() + " duplicated lines." } + override string toString() { result = "Duplicate code: " + sourceLines() + " duplicated lines." } } /** A block of similar code. */ class SimilarBlock extends Copy, @similarity { - override string toString() { - result = "Similar code: " + sourceLines() + " almost duplicated lines." - } + override string toString() { + result = "Similar code: " + sourceLines() + " almost duplicated lines." + } } /** @@ -149,14 +149,14 @@ class SimilarBlock extends Copy, @similarity { * respectively, where `scope1` and `scope2` are not the same. */ predicate duplicateStatement(Scope scope1, Scope scope2, Stmt stmt1, Stmt stmt2) { - exists(int equivstart, int equivend, int first, int last | - scope1.contains(stmt1) and - scope2.contains(stmt2) and - duplicateCoversStatement(equivstart, equivend, first, last, stmt1) and - duplicateCoversStatement(equivstart, equivend, first, last, stmt2) and - stmt1 != stmt2 and - scope1 != scope2 - ) + exists(int equivstart, int equivend, int first, int last | + scope1.contains(stmt1) and + scope2.contains(stmt2) and + duplicateCoversStatement(equivstart, equivend, first, last, stmt1) and + duplicateCoversStatement(equivstart, equivend, first, last, stmt2) and + stmt1 != stmt2 and + scope1 != scope2 + ) } /** @@ -167,17 +167,17 @@ predicate duplicateStatement(Scope scope1, Scope scope2, Stmt stmt1, Stmt stmt2) * block, respectively. */ private predicate duplicateCoversStatement( - int equivstart, int equivend, int first, int last, Stmt stmt + int equivstart, int equivend, int first, int last, Stmt stmt ) { - exists(DuplicateBlock b1, DuplicateBlock b2, Location startloc, Location endloc | - stmt.getLocation() = startloc and - stmt.getLastStatement().getLocation() = endloc and - first = b1.tokenStartingAt(startloc) and - last = b2.tokenEndingAt(endloc) and - b1.getEquivalenceClass() = equivstart and - b2.getEquivalenceClass() = equivend and - duplicate_extension(b1, _, b2, _, equivstart, equivend) - ) + exists(DuplicateBlock b1, DuplicateBlock b2, Location startloc, Location endloc | + stmt.getLocation() = startloc and + stmt.getLastStatement().getLocation() = endloc and + first = b1.tokenStartingAt(startloc) and + last = b2.tokenEndingAt(endloc) and + b1.getEquivalenceClass() = equivstart and + b2.getEquivalenceClass() = equivend and + duplicate_extension(b1, _, b2, _, equivstart, equivend) + ) } /** @@ -185,23 +185,23 @@ private predicate duplicateCoversStatement( * toplevel that has `duplicate` lines in common with `scope1`. */ predicate duplicateStatements(Scope scope1, Scope scope2, int duplicate, int total) { - duplicate = strictcount(Stmt stmt | duplicateStatement(scope1, scope2, stmt, _)) and - total = strictcount(Stmt stmt | scope1.contains(stmt)) + duplicate = strictcount(Stmt stmt | duplicateStatement(scope1, scope2, stmt, _)) and + total = strictcount(Stmt stmt | scope1.contains(stmt)) } /** * Find pairs of scopes that are identical or almost identical */ predicate duplicateScopes(Scope s, Scope other, float percent, string message) { - exists(int total, int duplicate | duplicateStatements(s, other, duplicate, total) | - percent = 100.0 * duplicate / total and - percent >= 80.0 and - if duplicate = total - then message = "All " + total + " statements in " + s.getName() + " are identical in $@." - else - message = - duplicate + " out of " + total + " statements in " + s.getName() + " are duplicated in $@." - ) + exists(int total, int duplicate | duplicateStatements(s, other, duplicate, total) | + percent = 100.0 * duplicate / total and + percent >= 80.0 and + if duplicate = total + then message = "All " + total + " statements in " + s.getName() + " are identical in $@." + else + message = + duplicate + " out of " + total + " statements in " + s.getName() + " are duplicated in $@." + ) } /** @@ -209,14 +209,14 @@ predicate duplicateScopes(Scope s, Scope other, float percent, string message) { * respectively, where `scope1` and `scope2` are not the same. */ private predicate similarStatement(Scope scope1, Scope scope2, Stmt stmt1, Stmt stmt2) { - exists(int start, int end, int first, int last | - scope1.contains(stmt1) and - scope2.contains(stmt2) and - similarCoversStatement(start, end, first, last, stmt1) and - similarCoversStatement(start, end, first, last, stmt2) and - stmt1 != stmt2 and - scope1 != scope2 - ) + exists(int start, int end, int first, int last | + scope1.contains(stmt1) and + scope2.contains(stmt2) and + similarCoversStatement(start, end, first, last, stmt1) and + similarCoversStatement(start, end, first, last, stmt2) and + stmt1 != stmt2 and + scope1 != scope2 + ) } /** @@ -227,17 +227,17 @@ private predicate similarStatement(Scope scope1, Scope scope2, Stmt stmt1, Stmt * block, respectively. */ private predicate similarCoversStatement( - int equivstart, int equivend, int first, int last, Stmt stmt + int equivstart, int equivend, int first, int last, Stmt stmt ) { - exists(SimilarBlock b1, SimilarBlock b2, Location startloc, Location endloc | - stmt.getLocation() = startloc and - stmt.getLastStatement().getLocation() = endloc and - first = b1.tokenStartingAt(startloc) and - last = b2.tokenEndingAt(endloc) and - b1.getEquivalenceClass() = equivstart and - b2.getEquivalenceClass() = equivend and - similar_extension(b1, _, b2, _, equivstart, equivend) - ) + exists(SimilarBlock b1, SimilarBlock b2, Location startloc, Location endloc | + stmt.getLocation() = startloc and + stmt.getLastStatement().getLocation() = endloc and + first = b1.tokenStartingAt(startloc) and + last = b2.tokenEndingAt(endloc) and + b1.getEquivalenceClass() = equivstart and + b2.getEquivalenceClass() = equivend and + similar_extension(b1, _, b2, _, equivstart, equivend) + ) } /** @@ -245,23 +245,23 @@ private predicate similarCoversStatement( * toplevel that has `similar` similar lines to `scope1`. */ private predicate similarStatements(Scope scope1, Scope scope2, int similar, int total) { - similar = strictcount(Stmt stmt | similarStatement(scope1, scope2, stmt, _)) and - total = strictcount(Stmt stmt | scope1.contains(stmt)) + similar = strictcount(Stmt stmt | similarStatement(scope1, scope2, stmt, _)) and + total = strictcount(Stmt stmt | scope1.contains(stmt)) } /** * Find pairs of scopes that are similar */ predicate similarScopes(Scope s, Scope other, float percent, string message) { - exists(int total, int similar | similarStatements(s, other, similar, total) | - percent = 100.0 * similar / total and - percent >= 80.0 and - if similar = total - then message = "All statements in " + s.getName() + " are similar in $@." - else - message = - similar + " out of " + total + " statements in " + s.getName() + " are similar in $@." - ) + exists(int total, int similar | similarStatements(s, other, similar, total) | + percent = 100.0 * similar / total and + percent >= 80.0 and + if similar = total + then message = "All statements in " + s.getName() + " are similar in $@." + else + message = + similar + " out of " + total + " statements in " + s.getName() + " are similar in $@." + ) } /** @@ -269,5 +269,5 @@ predicate similarScopes(Scope s, Scope other, float percent, string message) { * This is true for blocks of import statements. */ predicate allowlistedLineForDuplication(File f, int line) { - exists(ImportingStmt i | i.getLocation().getFile() = f and i.getLocation().getStartLine() = line) + exists(ImportingStmt i | i.getLocation().getFile() = f and i.getLocation().getStartLine() = line) } diff --git a/python/ql/src/external/DefectFilter.qll b/python/ql/src/external/DefectFilter.qll index 4c4bdcf779a..62704b9fd0e 100644 --- a/python/ql/src/external/DefectFilter.qll +++ b/python/ql/src/external/DefectFilter.qll @@ -11,71 +11,71 @@ import semmle.python.Files * For more information, see [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). */ external predicate defectResults( - int id, string queryPath, string filepath, int startline, int startcol, int endline, int endcol, - string message + int id, string queryPath, string filepath, int startline, int startcol, int endline, int endcol, + string message ); /** * A defect query result stored in a dashboard database. */ class DefectResult extends int { - DefectResult() { defectResults(this, _, _, _, _, _, _, _) } + DefectResult() { defectResults(this, _, _, _, _, _, _, _) } - /** Gets the path of the query that reported the result. */ - string getQueryPath() { defectResults(this, result, _, _, _, _, _, _) } + /** Gets the path of the query that reported the result. */ + string getQueryPath() { defectResults(this, result, _, _, _, _, _, _) } - /** Gets the file in which this query result was reported. */ - File getFile() { - exists(string path | - defectResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path - ) - } + /** Gets the file in which this query result was reported. */ + File getFile() { + exists(string path | + defectResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path + ) + } - /** Gets the file path in which this query result was reported. */ - string getFilePath() { defectResults(this, _, result, _, _, _, _, _) } + /** Gets the file path in which this query result was reported. */ + string getFilePath() { defectResults(this, _, result, _, _, _, _, _) } - /** Gets the line on which the location of this query result starts. */ - int getStartLine() { defectResults(this, _, _, result, _, _, _, _) } + /** Gets the line on which the location of this query result starts. */ + int getStartLine() { defectResults(this, _, _, result, _, _, _, _) } - /** Gets the column on which the location of this query result starts. */ - int getStartColumn() { defectResults(this, _, _, _, result, _, _, _) } + /** Gets the column on which the location of this query result starts. */ + int getStartColumn() { defectResults(this, _, _, _, result, _, _, _) } - /** Gets the line on which the location of this query result ends. */ - int getEndLine() { defectResults(this, _, _, _, _, result, _, _) } + /** Gets the line on which the location of this query result ends. */ + int getEndLine() { defectResults(this, _, _, _, _, result, _, _) } - /** Gets the column on which the location of this query result ends. */ - int getEndColumn() { defectResults(this, _, _, _, _, _, result, _) } + /** Gets the column on which the location of this query result ends. */ + int getEndColumn() { defectResults(this, _, _, _, _, _, result, _) } - /** Gets the message associated with this query result. */ - string getMessage() { defectResults(this, _, _, _, _, _, _, result) } + /** Gets the message associated with this query result. */ + string getMessage() { defectResults(this, _, _, _, _, _, _, result) } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - defectResults(this, _, filepath, startline, startcolumn, endline, endcolumn, _) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + defectResults(this, _, filepath, startline, startcolumn, endline, endcolumn, _) + } - /** Gets the URL corresponding to the location of this query result. */ - string getURL() { - result = - "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" + - getEndLine() + ":" + getEndColumn() - } + /** Gets the URL corresponding to the location of this query result. */ + string getURL() { + result = + "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" + + getEndLine() + ":" + getEndColumn() + } } // crude containment by line number only predicate contains(Location l, DefectResult res) { - exists(string path, int bl1, int el1, int bl2, int el2 | - l.hasLocationInfo(path, bl1, _, el1, _) and - res.hasLocationInfo(path, bl2, _, el2, _) and - bl1 <= bl2 and - el1 >= el2 - ) + exists(string path, int bl1, int el1, int bl2, int el2 | + l.hasLocationInfo(path, bl1, _, el1, _) and + res.hasLocationInfo(path, bl2, _, el2, _) and + bl1 <= bl2 and + el1 >= el2 + ) } diff --git a/python/ql/src/external/DuplicateBlock.ql b/python/ql/src/external/DuplicateBlock.ql index 38aed20739f..90067fa834c 100644 --- a/python/ql/src/external/DuplicateBlock.ql +++ b/python/ql/src/external/DuplicateBlock.ql @@ -19,16 +19,16 @@ import python import CodeDuplication predicate sorted_by_location(DuplicateBlock x, DuplicateBlock y) { - if x.sourceFile() = y.sourceFile() - then x.sourceStartLine() < y.sourceStartLine() - else x.sourceFile().getAbsolutePath() < y.sourceFile().getAbsolutePath() + if x.sourceFile() = y.sourceFile() + then x.sourceStartLine() < y.sourceStartLine() + else x.sourceFile().getAbsolutePath() < y.sourceFile().getAbsolutePath() } from DuplicateBlock d, DuplicateBlock other where - d.sourceLines() > 10 and - other.getEquivalenceClass() = d.getEquivalenceClass() and - sorted_by_location(other, d) + d.sourceLines() > 10 and + other.getEquivalenceClass() = d.getEquivalenceClass() and + sorted_by_location(other, d) select d, - "Duplicate code: " + d.sourceLines() + " lines are duplicated at " + - other.sourceFile().getShortName() + ":" + other.sourceStartLine().toString() + "Duplicate code: " + d.sourceLines() + " lines are duplicated at " + + other.sourceFile().getShortName() + ":" + other.sourceStartLine().toString() diff --git a/python/ql/src/external/DuplicateFunction.ql b/python/ql/src/external/DuplicateFunction.ql index b638f6fb5b2..57a566449d3 100644 --- a/python/ql/src/external/DuplicateFunction.ql +++ b/python/ql/src/external/DuplicateFunction.ql @@ -21,9 +21,9 @@ predicate relevant(Function m) { m.getMetrics().getNumberOfLinesOfCode() > 5 } from Function m, Function other, string message, int percent where - duplicateScopes(m, other, percent, message) and - relevant(m) and - percent > 95.0 and - not duplicateScopes(m.getEnclosingModule(), other.getEnclosingModule(), _, _) and - not duplicateScopes(m.getScope(), other.getScope(), _, _) + duplicateScopes(m, other, percent, message) and + relevant(m) and + percent > 95.0 and + not duplicateScopes(m.getEnclosingModule(), other.getEnclosingModule(), _, _) and + not duplicateScopes(m.getScope(), other.getScope(), _, _) select m, message, other, other.getName() diff --git a/python/ql/src/external/ExternalArtifact.qll b/python/ql/src/external/ExternalArtifact.qll index 210152b7687..3aa4095a823 100644 --- a/python/ql/src/external/ExternalArtifact.qll +++ b/python/ql/src/external/ExternalArtifact.qll @@ -5,83 +5,83 @@ import python class ExternalDefect extends @externalDefect { - string getQueryPath() { - exists(string path | - externalDefects(this, path, _, _, _) and - result = path.replaceAll("\\", "/") - ) - } + string getQueryPath() { + exists(string path | + externalDefects(this, path, _, _, _) and + result = path.replaceAll("\\", "/") + ) + } - string getMessage() { externalDefects(this, _, _, result, _) } + string getMessage() { externalDefects(this, _, _, result, _) } - float getSeverity() { externalDefects(this, _, _, _, result) } + float getSeverity() { externalDefects(this, _, _, _, result) } - Location getLocation() { externalDefects(this, _, result, _, _) } + Location getLocation() { externalDefects(this, _, result, _, _) } - /** Gets a textual representation of this element. */ - string toString() { result = getQueryPath() + ": " + getLocation() + " - " + getMessage() } + /** Gets a textual representation of this element. */ + string toString() { result = getQueryPath() + ": " + getLocation() + " - " + getMessage() } } class ExternalMetric extends @externalMetric { - string getQueryPath() { externalMetrics(this, result, _, _) } + string getQueryPath() { externalMetrics(this, result, _, _) } - float getValue() { externalMetrics(this, _, _, result) } + float getValue() { externalMetrics(this, _, _, result) } - Location getLocation() { externalMetrics(this, _, result, _) } + Location getLocation() { externalMetrics(this, _, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = getQueryPath() + ": " + getLocation() + " - " + getValue() } + /** Gets a textual representation of this element. */ + string toString() { result = getQueryPath() + ": " + getLocation() + " - " + getValue() } } /** * An external data item. */ class ExternalData extends @externalDataElement { - /** Gets the path of the file this data was loaded from. */ - string getDataPath() { externalData(this, result, _, _) } + /** Gets the path of the file this data was loaded from. */ + string getDataPath() { externalData(this, result, _, _) } - /** - * Gets the path of the file this data was loaded from, with its - * extension replaced by `.ql`. - */ - string getQueryPath() { result = getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") } + /** + * Gets the path of the file this data was loaded from, with its + * extension replaced by `.ql`. + */ + string getQueryPath() { result = getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") } - /** Gets the number of fields in this data item. */ - int getNumFields() { result = 1 + max(int i | externalData(this, _, i, _) | i) } + /** Gets the number of fields in this data item. */ + int getNumFields() { result = 1 + max(int i | externalData(this, _, i, _) | i) } - /** Gets the value of the field at position `index` of this data item. */ - string getField(int index) { externalData(this, _, index, result) } + /** Gets the value of the field at position `index` of this data item. */ + string getField(int index) { externalData(this, _, index, result) } - /** Gets the integer value of the field at position `index` of this data item. */ - int getFieldAsInt(int index) { result = getField(index).toInt() } + /** Gets the integer value of the field at position `index` of this data item. */ + int getFieldAsInt(int index) { result = getField(index).toInt() } - /** Gets the floating-point value of the field at position `index` of this data item. */ - float getFieldAsFloat(int index) { result = getField(index).toFloat() } + /** Gets the floating-point value of the field at position `index` of this data item. */ + float getFieldAsFloat(int index) { result = getField(index).toFloat() } - /** Gets the value of the field at position `index` of this data item, interpreted as a date. */ - date getFieldAsDate(int index) { result = getField(index).toDate() } + /** Gets the value of the field at position `index` of this data item, interpreted as a date. */ + date getFieldAsDate(int index) { result = getField(index).toDate() } - /** Gets a textual representation of this data item. */ - string toString() { result = getQueryPath() + ": " + buildTupleString(0) } + /** Gets a textual representation of this data item. */ + string toString() { result = getQueryPath() + ": " + buildTupleString(0) } - /** Gets a textual representation of this data item, starting with the field at position `start`. */ - private string buildTupleString(int start) { - start = getNumFields() - 1 and result = getField(start) - or - start < getNumFields() - 1 and result = getField(start) + "," + buildTupleString(start + 1) - } + /** Gets a textual representation of this data item, starting with the field at position `start`. */ + private string buildTupleString(int start) { + start = getNumFields() - 1 and result = getField(start) + or + start < getNumFields() - 1 and result = getField(start) + "," + buildTupleString(start + 1) + } } /** * External data with a location, and a message, as produced by tools that used to produce QLDs. */ class DefectExternalData extends ExternalData { - DefectExternalData() { - this.getField(0).regexpMatch("\\w+://.*:[0-9]+:[0-9]+:[0-9]+:[0-9]+$") and - this.getNumFields() = 2 - } + DefectExternalData() { + this.getField(0).regexpMatch("\\w+://.*:[0-9]+:[0-9]+:[0-9]+:[0-9]+$") and + this.getNumFields() = 2 + } - string getURL() { result = getField(0) } + string getURL() { result = getField(0) } - string getMessage() { result = getField(1) } + string getMessage() { result = getField(1) } } diff --git a/python/ql/src/external/MostlyDuplicateClass.ql b/python/ql/src/external/MostlyDuplicateClass.ql index 88169ab897f..9cdcd4502f2 100644 --- a/python/ql/src/external/MostlyDuplicateClass.ql +++ b/python/ql/src/external/MostlyDuplicateClass.ql @@ -19,7 +19,7 @@ import CodeDuplication from Class c, Class other, string message where - duplicateScopes(c, other, _, message) and - count(c.getAStmt()) > 3 and - not duplicateScopes(c.getEnclosingModule(), _, _, _) + duplicateScopes(c, other, _, message) and + count(c.getAStmt()) > 3 and + not duplicateScopes(c.getEnclosingModule(), _, _, _) select c, message, other, other.getName() diff --git a/python/ql/src/external/SimilarFunction.ql b/python/ql/src/external/SimilarFunction.ql index bcd63a41dcf..9e8db82dcd4 100644 --- a/python/ql/src/external/SimilarFunction.ql +++ b/python/ql/src/external/SimilarFunction.ql @@ -21,10 +21,10 @@ predicate relevant(Function m) { m.getMetrics().getNumberOfLinesOfCode() > 10 } from Function m, Function other, string message, int percent where - similarScopes(m, other, percent, message) and - relevant(m) and - percent > 95.0 and - not duplicateScopes(m, other, _, _) and - not duplicateScopes(m.getEnclosingModule(), other.getEnclosingModule(), _, _) and - not duplicateScopes(m.getScope(), other.getScope(), _, _) + similarScopes(m, other, percent, message) and + relevant(m) and + percent > 95.0 and + not duplicateScopes(m, other, _, _) and + not duplicateScopes(m.getEnclosingModule(), other.getEnclosingModule(), _, _) and + not duplicateScopes(m.getScope(), other.getScope(), _, _) select m, message, other, other.getName() diff --git a/python/ql/src/external/Thrift.qll b/python/ql/src/external/Thrift.qll index efb9ff9f33e..f9f8d67701d 100644 --- a/python/ql/src/external/Thrift.qll +++ b/python/ql/src/external/Thrift.qll @@ -7,197 +7,201 @@ import external.ExternalArtifact /** An item in the parse tree of the IDL file */ class ThriftElement extends ExternalData { - string kind; + string kind; - ThriftElement() { this.getDataPath() = "thrift-" + kind } + ThriftElement() { this.getDataPath() = "thrift-" + kind } - string getKind() { result = kind } + string getKind() { result = kind } - string getId() { result = getField(0) } + string getId() { result = getField(0) } - int getIndex() { result = getFieldAsInt(1) } + int getIndex() { result = getFieldAsInt(1) } - ThriftElement getParent() { result.getId() = this.getField(2) } + ThriftElement getParent() { result.getId() = this.getField(2) } - string getValue() { result = this.getField(3) } + string getValue() { result = this.getField(3) } - ThriftElement getChild(int n) { result.getIndex() = n and result.getParent() = this } + ThriftElement getChild(int n) { result.getIndex() = n and result.getParent() = this } - ThriftElement getAChild() { result = this.getChild(_) } + ThriftElement getAChild() { result = this.getChild(_) } - override string toString() { result = this.getKind() } + override string toString() { result = this.getKind() } - string getPath() { result = this.getField(4) } + string getPath() { result = this.getField(4) } - private int line() { result = this.getFieldAsInt(5) } + private int line() { result = this.getFieldAsInt(5) } - private int column() { result = this.getFieldAsInt(6) } + private int column() { result = this.getFieldAsInt(6) } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - filepath = this.getPath() and - startline = this.line() and - startcolumn = this.column() and - endline = this.line() and - endcolumn = this.column() + this.getValue().length() - 1 - or - exists(ThriftElement first, ThriftElement last | - first = this.getChild(min(int l | exists(this.getChild(l)))) and - last = this.getChild(max(int l | exists(this.getChild(l)))) and - first.hasLocationInfo(filepath, startline, startcolumn, _, _) and - last.hasLocationInfo(filepath, _, _, endline, endcolumn) - ) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = this.getPath() and + startline = this.line() and + startcolumn = this.column() and + endline = this.line() and + endcolumn = this.column() + this.getValue().length() - 1 + or + exists(ThriftElement first, ThriftElement last | + first = this.getChild(min(int l | exists(this.getChild(l)))) and + last = this.getChild(max(int l | exists(this.getChild(l)))) and + first.hasLocationInfo(filepath, startline, startcolumn, _, _) and + last.hasLocationInfo(filepath, _, _, endline, endcolumn) + ) + } - File getFile() { this.hasLocationInfo(result.getAbsolutePath(), _, _, _, _) } + File getFile() { this.hasLocationInfo(result.getAbsolutePath(), _, _, _, _) } } abstract class ThriftNamedElement extends ThriftElement { - abstract ThriftElement getNameElement(); + abstract ThriftElement getNameElement(); - final string getName() { result = this.getNameElement().getValue() } + final string getName() { result = this.getNameElement().getValue() } - override string toString() { - result = this.getKind() + " " + this.getName() - or - not exists(this.getName()) and result = this.getKind() + " ???" - } + override string toString() { + result = this.getKind() + " " + this.getName() + or + not exists(this.getName()) and result = this.getKind() + " ???" + } - override predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) { - exists(ThriftElement first | - first = this.getChild(min(int l | exists(this.getChild(l)))) and - first.hasLocationInfo(filepath, startline, startcolumn, _, _) and - this.getNameElement().hasLocationInfo(filepath, _, _, endline, endcolumn) - ) - } + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + exists(ThriftElement first | + first = this.getChild(min(int l | exists(this.getChild(l)))) and + first.hasLocationInfo(filepath, startline, startcolumn, _, _) and + this.getNameElement().hasLocationInfo(filepath, _, _, endline, endcolumn) + ) + } } class ThriftType extends ThriftNamedElement { - ThriftType() { kind.matches("%type") } + ThriftType() { kind.matches("%type") } - override ThriftElement getNameElement() { - result = this.getChild(0) - or - result = this.getChild(0).(ThriftType).getNameElement() - } + override ThriftElement getNameElement() { + result = this.getChild(0) + or + result = this.getChild(0).(ThriftType).getNameElement() + } - override string toString() { result = "type " + this.getName() } + override string toString() { result = "type " + this.getName() } - predicate references(ThriftStruct struct) { - this.getName() = struct.getName() and - exists(string path | - this.hasLocationInfo(path, _, _, _, _) and - struct.hasLocationInfo(path, _, _, _, _) - ) - } + predicate references(ThriftStruct struct) { + this.getName() = struct.getName() and + exists(string path | + this.hasLocationInfo(path, _, _, _, _) and + struct.hasLocationInfo(path, _, _, _, _) + ) + } } /** A thrift typedef */ class ThriftTypeDef extends ThriftNamedElement { - ThriftTypeDef() { kind.matches("typedef") } + ThriftTypeDef() { kind.matches("typedef") } - override ThriftElement getNameElement() { result = this.getChild(2).getChild(0) } + override ThriftElement getNameElement() { result = this.getChild(2).getChild(0) } } /** A thrift enum declaration */ class ThriftEnum extends ThriftNamedElement { - ThriftEnum() { kind.matches("enum") } + ThriftEnum() { kind.matches("enum") } - override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } + override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } } /** A thrift enum field */ class ThriftEnumField extends ThriftNamedElement { - ThriftEnumField() { kind.matches("enumfield") } + ThriftEnumField() { kind.matches("enumfield") } - override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } + override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } } /** A thrift service declaration */ class ThriftService extends ThriftNamedElement { - ThriftService() { kind.matches("service") } + ThriftService() { kind.matches("service") } - override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } + override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } - ThriftFunction getAFunction() { result = this.getChild(_) } + ThriftFunction getAFunction() { result = this.getChild(_) } - ThriftFunction getFunction(string name) { - result.getName() = name and - result = this.getAFunction() - } + ThriftFunction getFunction(string name) { + result.getName() = name and + result = this.getAFunction() + } } /** A thrift function declaration */ class ThriftFunction extends ThriftNamedElement { - ThriftFunction() { kind.matches("function") } + ThriftFunction() { kind.matches("function") } - override ThriftElement getNameElement() { result = this.getChild(2).getChild(0) } + override ThriftElement getNameElement() { result = this.getChild(2).getChild(0) } - ThriftField getArgument(int n) { result = this.getChild(n + 3) } + ThriftField getArgument(int n) { result = this.getChild(n + 3) } - ThriftField getAnArgument() { result = this.getArgument(_) } + ThriftField getAnArgument() { result = this.getArgument(_) } - private ThriftThrows getAllThrows() { result = this.getChild(_) } + private ThriftThrows getAllThrows() { result = this.getChild(_) } - ThriftField getAThrows() { result = this.getAllThrows().getAChild() } + ThriftField getAThrows() { result = this.getAllThrows().getAChild() } - ThriftType getReturnType() { result = this.getChild(1).getChild(0) } + ThriftType getReturnType() { result = this.getChild(1).getChild(0) } - override predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) { - this.getChild(1).hasLocationInfo(filepath, startline, startcolumn, _, _) and - this.getChild(2).hasLocationInfo(filepath, _, _, endline, endcolumn) - } + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getChild(1).hasLocationInfo(filepath, startline, startcolumn, _, _) and + this.getChild(2).hasLocationInfo(filepath, _, _, endline, endcolumn) + } - ThriftService getService() { result.getAFunction() = this } + ThriftService getService() { result.getAFunction() = this } - string getQualifiedName() { result = this.getService().getName() + "." + this.getName() } + string getQualifiedName() { result = this.getService().getName() + "." + this.getName() } } class ThriftField extends ThriftNamedElement { - ThriftField() { kind.matches("field") } + ThriftField() { kind.matches("field") } - override ThriftElement getNameElement() { result = this.getChild(4) } + override ThriftElement getNameElement() { result = this.getChild(4) } - ThriftType getType() { result = this.getChild(2) } + ThriftType getType() { result = this.getChild(2) } } class ThriftStruct extends ThriftNamedElement { - ThriftStruct() { kind.matches("struct") } + ThriftStruct() { kind.matches("struct") } - override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } + override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } - ThriftField getMember(int n) { result = this.getChild(n + 1) } + ThriftField getMember(int n) { result = this.getChild(n + 1) } - ThriftField getAMember() { result = this.getMember(_) } + ThriftField getAMember() { result = this.getMember(_) } } class ThriftException extends ThriftNamedElement { - ThriftException() { kind.matches("exception") } + ThriftException() { kind.matches("exception") } - override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } + override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } - ThriftField getMember(int n) { result = this.getChild(n + 1) } + ThriftField getMember(int n) { result = this.getChild(n + 1) } - ThriftField getAMember() { result = this.getMember(_) } + ThriftField getAMember() { result = this.getMember(_) } } class ThriftThrows extends ThriftElement { - ThriftThrows() { kind.matches("throws") } + ThriftThrows() { kind.matches("throws") } - ThriftField getAThrows() { result = this.getChild(_) } + ThriftField getAThrows() { result = this.getChild(_) } } /** A parse tree element that holds a primitive value */ class ThriftValue extends ThriftElement { - ThriftValue() { exists(this.getValue()) } + ThriftValue() { exists(this.getValue()) } - override string toString() { result = this.getKind() + " " + this.getValue() } + override string toString() { result = this.getKind() + " " + this.getValue() } } diff --git a/python/ql/src/external/VCS.qll b/python/ql/src/external/VCS.qll index c7d7af334c9..068ef881e4a 100644 --- a/python/ql/src/external/VCS.qll +++ b/python/ql/src/external/VCS.qll @@ -1,77 +1,79 @@ import python class Commit extends @svnentry { - Commit() { - svnaffectedfiles(this, _, _) and - exists(date svnDate, date snapshotDate | - svnentries(this, _, _, svnDate, _) and - snapshotDate(snapshotDate) and - svnDate <= snapshotDate - ) - } + Commit() { + svnaffectedfiles(this, _, _) and + exists(date svnDate, date snapshotDate | + svnentries(this, _, _, svnDate, _) and + snapshotDate(snapshotDate) and + svnDate <= snapshotDate + ) + } - /** Gets a textual representation of this element. */ - string toString() { result = this.getRevisionName() } + /** Gets a textual representation of this element. */ + string toString() { result = this.getRevisionName() } - string getRevisionName() { svnentries(this, result, _, _, _) } + string getRevisionName() { svnentries(this, result, _, _, _) } - string getAuthor() { svnentries(this, _, result, _, _) } + string getAuthor() { svnentries(this, _, result, _, _) } - date getDate() { svnentries(this, _, _, result, _) } + date getDate() { svnentries(this, _, _, result, _) } - int getChangeSize() { svnentries(this, _, _, _, result) } + int getChangeSize() { svnentries(this, _, _, _, result) } - string getMessage() { svnentrymsg(this, result) } + string getMessage() { svnentrymsg(this, result) } - string getAnAffectedFilePath(string action) { - exists(File rawFile | svnaffectedfiles(this, rawFile, action) | result = rawFile.getAbsolutePath()) - } + string getAnAffectedFilePath(string action) { + exists(File rawFile | svnaffectedfiles(this, rawFile, action) | + result = rawFile.getAbsolutePath() + ) + } - string getAnAffectedFilePath() { result = getAnAffectedFilePath(_) } + string getAnAffectedFilePath() { result = getAnAffectedFilePath(_) } - File getAnAffectedFile(string action) { svnaffectedfiles(this, result, action) } + File getAnAffectedFile(string action) { svnaffectedfiles(this, result, action) } - File getAnAffectedFile() { exists(string action | result = this.getAnAffectedFile(action)) } + File getAnAffectedFile() { exists(string action | result = this.getAnAffectedFile(action)) } - predicate isRecent() { recentCommit(this) } + predicate isRecent() { recentCommit(this) } - int daysToNow() { - exists(date now | snapshotDate(now) | result = getDate().daysTo(now) and result >= 0) - } + int daysToNow() { + exists(date now | snapshotDate(now) | result = getDate().daysTo(now) and result >= 0) + } - int getRecentAdditionsForFile(File f) { svnchurn(this, f, result, _) } + int getRecentAdditionsForFile(File f) { svnchurn(this, f, result, _) } - int getRecentDeletionsForFile(File f) { svnchurn(this, f, _, result) } + int getRecentDeletionsForFile(File f) { svnchurn(this, f, _, result) } - int getRecentChurnForFile(File f) { - result = getRecentAdditionsForFile(f) + getRecentDeletionsForFile(f) - } + int getRecentChurnForFile(File f) { + result = getRecentAdditionsForFile(f) + getRecentDeletionsForFile(f) + } } class Author extends string { - Author() { exists(Commit e | this = e.getAuthor()) } + Author() { exists(Commit e | this = e.getAuthor()) } - Commit getACommit() { result.getAuthor() = this } + Commit getACommit() { result.getAuthor() = this } - File getAnEditedFile() { result = this.getACommit().getAnAffectedFile() } + File getAnEditedFile() { result = this.getACommit().getAnAffectedFile() } } predicate recentCommit(Commit e) { - exists(date snapshotDate, date commitDate, int days | - snapshotDate(snapshotDate) and - e.getDate() = commitDate and - days = commitDate.daysTo(snapshotDate) and - days >= 0 and - days <= 60 - ) + exists(date snapshotDate, date commitDate, int days | + snapshotDate(snapshotDate) and + e.getDate() = commitDate and + days = commitDate.daysTo(snapshotDate) and + days >= 0 and + days <= 60 + ) } date firstChange(File f) { - result = min(Commit e, date toMin | f = e.getAnAffectedFile() and toMin = e.getDate() | toMin) + result = min(Commit e, date toMin | f = e.getAnAffectedFile() and toMin = e.getDate() | toMin) } predicate firstCommit(Commit e) { - not exists(File f | f = e.getAnAffectedFile() | firstChange(f) < e.getDate()) + not exists(File f | f = e.getAnAffectedFile() | firstChange(f) < e.getDate()) } predicate artificialChange(Commit e) { firstCommit(e) or e.getChangeSize() >= 50000 } diff --git a/python/ql/src/semmle/crypto/Crypto.qll b/python/ql/src/semmle/crypto/Crypto.qll index 4eaa3eb8dbd..ff13b559198 100644 --- a/python/ql/src/semmle/crypto/Crypto.qll +++ b/python/ql/src/semmle/crypto/Crypto.qll @@ -15,80 +15,80 @@ * The names are inspired by the names used in real world crypto libraries. */ private module AlgorithmNames { - predicate isStrongHashingAlgorithm(string name) { - name = "DSA" or - name = "ED25519" or - name = "ES256" or - name = "ECDSA256" or - name = "ES384" or - name = "ECDSA384" or - name = "ES512" or - name = "ECDSA512" or - name = "SHA2" or - name = "SHA224" or - name = "SHA256" or - name = "SHA384" or - name = "SHA512" or - name = "SHA3" - } + predicate isStrongHashingAlgorithm(string name) { + name = "DSA" or + name = "ED25519" or + name = "ES256" or + name = "ECDSA256" or + name = "ES384" or + name = "ECDSA384" or + name = "ES512" or + name = "ECDSA512" or + name = "SHA2" or + name = "SHA224" or + name = "SHA256" or + name = "SHA384" or + name = "SHA512" or + name = "SHA3" + } - predicate isWeakHashingAlgorithm(string name) { - name = "HAVEL128" or - name = "MD2" or - name = "MD4" or - name = "MD5" or - name = "PANAMA" or - name = "RIPEMD" or - name = "RIPEMD128" or - name = "RIPEMD256" or - name = "RIPEMD160" or - name = "RIPEMD320" or - name = "SHA0" or - name = "SHA1" - } + predicate isWeakHashingAlgorithm(string name) { + name = "HAVEL128" or + name = "MD2" or + name = "MD4" or + name = "MD5" or + name = "PANAMA" or + name = "RIPEMD" or + name = "RIPEMD128" or + name = "RIPEMD256" or + name = "RIPEMD160" or + name = "RIPEMD320" or + name = "SHA0" or + name = "SHA1" + } - predicate isStrongEncryptionAlgorithm(string name) { - name = "AES" or - name = "AES128" or - name = "AES192" or - name = "AES256" or - name = "AES512" or - name = "RSA" or - name = "RABBIT" or - name = "BLOWFISH" - } + predicate isStrongEncryptionAlgorithm(string name) { + name = "AES" or + name = "AES128" or + name = "AES192" or + name = "AES256" or + name = "AES512" or + name = "RSA" or + name = "RABBIT" or + name = "BLOWFISH" + } - predicate isWeakEncryptionAlgorithm(string name) { - name = "DES" or - name = "3DES" or - name = "TRIPLEDES" or - name = "TDEA" or - name = "TRIPLEDEA" or - name = "ARC2" or - name = "RC2" or - name = "ARC4" or - name = "RC4" or - name = "ARCFOUR" or - name = "ARC5" or - name = "RC5" - } + predicate isWeakEncryptionAlgorithm(string name) { + name = "DES" or + name = "3DES" or + name = "TRIPLEDES" or + name = "TDEA" or + name = "TRIPLEDEA" or + name = "ARC2" or + name = "RC2" or + name = "ARC4" or + name = "RC4" or + name = "ARCFOUR" or + name = "ARC5" or + name = "RC5" + } - predicate isStrongPasswordHashingAlgorithm(string name) { - name = "ARGON2" or - name = "PBKDF2" or - name = "BCRYPT" or - name = "SCRYPT" - } + predicate isStrongPasswordHashingAlgorithm(string name) { + name = "ARGON2" or + name = "PBKDF2" or + name = "BCRYPT" or + name = "SCRYPT" + } - predicate isWeakPasswordHashingAlgorithm(string name) { none() } + predicate isWeakPasswordHashingAlgorithm(string name) { none() } - /** - * Normalizes `name`: upper-case, no spaces, dashes or underscores. - * - * All names of this module are in this normalized form. - */ - bindingset[name] - string normalizeName(string name) { result = name.toUpperCase().regexpReplaceAll("[-_ ]", "") } + /** + * Normalizes `name`: upper-case, no spaces, dashes or underscores. + * + * All names of this module are in this normalized form. + */ + bindingset[name] + string normalizeName(string name) { result = name.toUpperCase().regexpReplaceAll("[-_ ]", "") } } private import AlgorithmNames @@ -97,78 +97,78 @@ private import AlgorithmNames * A cryptographic algorithm. */ private newtype TCryptographicAlgorithm = - MkHashingAlgorithm(string name, boolean isWeak) { - isStrongHashingAlgorithm(name) and isWeak = false - or - isWeakHashingAlgorithm(name) and isWeak = true - } or - MkEncryptionAlgorithm(string name, boolean isWeak) { - isStrongEncryptionAlgorithm(name) and isWeak = false - or - isWeakEncryptionAlgorithm(name) and isWeak = true - } or - MkPasswordHashingAlgorithm(string name, boolean isWeak) { - isStrongPasswordHashingAlgorithm(name) and isWeak = false - or - isWeakPasswordHashingAlgorithm(name) and isWeak = true - } + MkHashingAlgorithm(string name, boolean isWeak) { + isStrongHashingAlgorithm(name) and isWeak = false + or + isWeakHashingAlgorithm(name) and isWeak = true + } or + MkEncryptionAlgorithm(string name, boolean isWeak) { + isStrongEncryptionAlgorithm(name) and isWeak = false + or + isWeakEncryptionAlgorithm(name) and isWeak = true + } or + MkPasswordHashingAlgorithm(string name, boolean isWeak) { + isStrongPasswordHashingAlgorithm(name) and isWeak = false + or + isWeakPasswordHashingAlgorithm(name) and isWeak = true + } /** * A cryptographic algorithm. */ abstract class CryptographicAlgorithm extends TCryptographicAlgorithm { - /** Gets a textual representation of this element. */ - string toString() { result = getName() } + /** Gets a textual representation of this element. */ + string toString() { result = getName() } - /** - * Gets the name of the algorithm. - */ - abstract string getName(); + /** + * Gets the name of the algorithm. + */ + abstract string getName(); - /** - * Holds if this algorithm is weak. - */ - abstract predicate isWeak(); + /** + * Holds if this algorithm is weak. + */ + abstract predicate isWeak(); } /** * A hashing algorithm such as `MD5` or `SHA512`. */ class HashingAlgorithm extends MkHashingAlgorithm, CryptographicAlgorithm { - string name; - boolean isWeak; + string name; + boolean isWeak; - HashingAlgorithm() { this = MkHashingAlgorithm(name, isWeak) } + HashingAlgorithm() { this = MkHashingAlgorithm(name, isWeak) } - override string getName() { result = name } + override string getName() { result = name } - override predicate isWeak() { isWeak = true } + override predicate isWeak() { isWeak = true } } /** * An encryption algorithm such as `DES` or `AES512`. */ class EncryptionAlgorithm extends MkEncryptionAlgorithm, CryptographicAlgorithm { - string name; - boolean isWeak; + string name; + boolean isWeak; - EncryptionAlgorithm() { this = MkEncryptionAlgorithm(name, isWeak) } + EncryptionAlgorithm() { this = MkEncryptionAlgorithm(name, isWeak) } - override string getName() { result = name } + override string getName() { result = name } - override predicate isWeak() { isWeak = true } + override predicate isWeak() { isWeak = true } } /** * A password hashing algorithm such as `PBKDF2` or `SCRYPT`. */ class PasswordHashingAlgorithm extends MkPasswordHashingAlgorithm, CryptographicAlgorithm { - string name; - boolean isWeak; + string name; + boolean isWeak; - PasswordHashingAlgorithm() { this = MkPasswordHashingAlgorithm(name, isWeak) } + PasswordHashingAlgorithm() { this = MkPasswordHashingAlgorithm(name, isWeak) } - override string getName() { result = name } + override string getName() { result = name } - override predicate isWeak() { isWeak = true } + override predicate isWeak() { isWeak = true } } diff --git a/python/ql/src/semmle/python/AstExtended.qll b/python/ql/src/semmle/python/AstExtended.qll index 8a858c5fefc..7767050f40f 100644 --- a/python/ql/src/semmle/python/AstExtended.qll +++ b/python/ql/src/semmle/python/AstExtended.qll @@ -2,59 +2,59 @@ import python /** Syntactic node (Class, Function, Module, Expr, Stmt or Comprehension) corresponding to a flow node */ abstract class AstNode extends AstNode_ { - /* - * Special comment for documentation generation. - * All subclasses of `AstNode` that represent concrete syntax should have - * a comment of the form: - */ + /* + * Special comment for documentation generation. + * All subclasses of `AstNode` that represent concrete syntax should have + * a comment of the form: + */ - /* syntax: ... */ - /** Gets the scope that this node occurs in */ - abstract Scope getScope(); + /* syntax: ... */ + /** Gets the scope that this node occurs in */ + abstract Scope getScope(); - /** - * Gets a flow node corresponding directly to this node. - * NOTE: For some statements and other purely syntactic elements, - * there may not be a `ControlFlowNode` - */ - ControlFlowNode getAFlowNode() { py_flow_bb_node(result, this, _, _) } + /** + * Gets a flow node corresponding directly to this node. + * NOTE: For some statements and other purely syntactic elements, + * there may not be a `ControlFlowNode` + */ + ControlFlowNode getAFlowNode() { py_flow_bb_node(result, this, _, _) } - /** Gets the location for this AST node */ - Location getLocation() { none() } + /** Gets the location for this AST node */ + Location getLocation() { none() } - /** - * Whether this syntactic element is artificial, that is it is generated - * by the compiler and is not present in the source - */ - predicate isArtificial() { none() } + /** + * Whether this syntactic element is artificial, that is it is generated + * by the compiler and is not present in the source + */ + predicate isArtificial() { none() } - /** - * Gets a child node of this node in the AST. This predicate exists to aid exploration of the AST - * and other experiments. The child-parent relation may not be meaningful. - * For a more meaningful relation in terms of dependency use - * Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or - * Scope.getAStmt(). - */ - abstract AstNode getAChildNode(); + /** + * Gets a child node of this node in the AST. This predicate exists to aid exploration of the AST + * and other experiments. The child-parent relation may not be meaningful. + * For a more meaningful relation in terms of dependency use + * Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or + * Scope.getAStmt(). + */ + abstract AstNode getAChildNode(); - /** - * Gets the parent node of this node in the AST. This predicate exists to aid exploration of the AST - * and other experiments. The child-parent relation may not be meaningful. - * For a more meaningful relation in terms of dependency use - * Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or - * Scope.getAStmt() applied to the parent. - */ - AstNode getParentNode() { result.getAChildNode() = this } + /** + * Gets the parent node of this node in the AST. This predicate exists to aid exploration of the AST + * and other experiments. The child-parent relation may not be meaningful. + * For a more meaningful relation in terms of dependency use + * Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or + * Scope.getAStmt() applied to the parent. + */ + AstNode getParentNode() { result.getAChildNode() = this } - /** Whether this contains `inner` syntactically */ - predicate contains(AstNode inner) { this.getAChildNode+() = inner } + /** Whether this contains `inner` syntactically */ + predicate contains(AstNode inner) { this.getAChildNode+() = inner } - /** Whether this contains `inner` syntactically and `inner` has the same scope as `this` */ - predicate containsInScope(AstNode inner) { - this.contains(inner) and - this.getScope() = inner.getScope() and - not inner instanceof Scope - } + /** Whether this contains `inner` syntactically and `inner` has the same scope as `this` */ + predicate containsInScope(AstNode inner) { + this.contains(inner) and + this.getScope() = inner.getScope() and + not inner instanceof Scope + } } /* Parents */ @@ -80,32 +80,32 @@ library class StrListParent extends StrListParent_ { } library class ExprParent extends ExprParent_ { } library class DictItem extends DictItem_, AstNode { - override string toString() { result = DictItem_.super.toString() } + override string toString() { result = DictItem_.super.toString() } - override AstNode getAChildNode() { none() } + override AstNode getAChildNode() { none() } - override Scope getScope() { none() } + override Scope getScope() { none() } } /** A comprehension part, the 'for a in seq' part of [ a * a for a in seq ] */ class Comprehension extends Comprehension_, AstNode { - /** Gets the scope of this comprehension */ - override Scope getScope() { - /* Comprehensions exists only in Python 2 list comprehensions, so their scope is that of the list comp. */ - exists(ListComp l | this = l.getAGenerator() | result = l.getScope()) - } + /** Gets the scope of this comprehension */ + override Scope getScope() { + /* Comprehensions exists only in Python 2 list comprehensions, so their scope is that of the list comp. */ + exists(ListComp l | this = l.getAGenerator() | result = l.getScope()) + } - override string toString() { result = "Comprehension" } + override string toString() { result = "Comprehension" } - override Location getLocation() { result = Comprehension_.super.getLocation() } + override Location getLocation() { result = Comprehension_.super.getLocation() } - override AstNode getAChildNode() { result = this.getASubExpression() } + override AstNode getAChildNode() { result = this.getASubExpression() } - Expr getASubExpression() { - result = this.getIter() or - result = this.getAnIf() or - result = this.getTarget() - } + Expr getASubExpression() { + result = this.getIter() or + result = this.getAnIf() or + result = this.getTarget() + } } class BytesOrStr extends BytesOrStr_ { } @@ -116,17 +116,17 @@ class BytesOrStr extends BytesOrStr_ { } * would be composed of three `StringPart`s. */ class StringPart extends StringPart_, AstNode { - override Scope getScope() { - exists(Bytes b | this = b.getAnImplicitlyConcatenatedPart() | result = b.getScope()) - or - exists(Unicode u | this = u.getAnImplicitlyConcatenatedPart() | result = u.getScope()) - } + override Scope getScope() { + exists(Bytes b | this = b.getAnImplicitlyConcatenatedPart() | result = b.getScope()) + or + exists(Unicode u | this = u.getAnImplicitlyConcatenatedPart() | result = u.getScope()) + } - override AstNode getAChildNode() { none() } + override AstNode getAChildNode() { none() } - override string toString() { result = StringPart_.super.toString() } + override string toString() { result = StringPart_.super.toString() } - override Location getLocation() { result = StringPart_.super.getLocation() } + override Location getLocation() { result = StringPart_.super.getLocation() } } class StringPartList extends StringPartList_ { } @@ -134,21 +134,21 @@ class StringPartList extends StringPartList_ { } /* **** Lists ***/ /** A parameter list */ class ParameterList extends @py_parameter_list { - Function getParent() { py_parameter_lists(this, result) } + Function getParent() { py_parameter_lists(this, result) } - /** Gets a parameter */ - Parameter getAnItem() { - /* Item can be a Name or a Tuple, both of which are expressions */ - py_exprs(result, _, this, _) - } + /** Gets a parameter */ + Parameter getAnItem() { + /* Item can be a Name or a Tuple, both of which are expressions */ + py_exprs(result, _, this, _) + } - /** Gets the nth parameter */ - Parameter getItem(int index) { - /* Item can be a Name or a Tuple, both of which are expressions */ - py_exprs(result, _, this, index) - } + /** Gets the nth parameter */ + Parameter getItem(int index) { + /* Item can be a Name or a Tuple, both of which are expressions */ + py_exprs(result, _, this, index) + } - string toString() { result = "ParameterList" } + string toString() { result = "ParameterList" } } /** A list of Comprehensions (for generating parts of a set, list or dictionary comprehension) */ @@ -156,7 +156,7 @@ class ComprehensionList extends ComprehensionList_ { } /** A list of expressions */ class ExprList extends ExprList_ { - /* syntax: Expr, ... */ + /* syntax: Expr, ... */ } library class DictItemList extends DictItemList_ { } diff --git a/python/ql/src/semmle/python/AstGenerated.qll b/python/ql/src/semmle/python/AstGenerated.qll index 6c1802f58c2..146fea4d18e 100644 --- a/python/ql/src/semmle/python/AstGenerated.qll +++ b/python/ql/src/semmle/python/AstGenerated.qll @@ -8,1639 +8,1639 @@ import python /** INTERNAL: See the class `Add` for further information. */ library class Add_ extends @py_Add, Operator { - override string toString() { result = "Add" } + override string toString() { result = "Add" } } /** INTERNAL: See the class `And` for further information. */ library class And_ extends @py_And, Boolop { - override string toString() { result = "And" } + override string toString() { result = "And" } } /** INTERNAL: See the class `AnnAssign` for further information. */ library class AnnAssign_ extends @py_AnnAssign, Stmt { - /** Gets the value of this annotated assignment. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this annotated assignment. */ + Expr getValue() { py_exprs(result, _, this, 1) } - /** Gets the annotation of this annotated assignment. */ - Expr getAnnotation() { py_exprs(result, _, this, 2) } + /** Gets the annotation of this annotated assignment. */ + Expr getAnnotation() { py_exprs(result, _, this, 2) } - /** Gets the target of this annotated assignment. */ - Expr getTarget() { py_exprs(result, _, this, 3) } + /** Gets the target of this annotated assignment. */ + Expr getTarget() { py_exprs(result, _, this, 3) } - override string toString() { result = "AnnAssign" } + override string toString() { result = "AnnAssign" } } /** INTERNAL: See the class `Assert` for further information. */ library class Assert_ extends @py_Assert, Stmt { - /** Gets the value being tested of this assert statement. */ - Expr getTest() { py_exprs(result, _, this, 1) } + /** Gets the value being tested of this assert statement. */ + Expr getTest() { py_exprs(result, _, this, 1) } - /** Gets the failure message of this assert statement. */ - Expr getMsg() { py_exprs(result, _, this, 2) } + /** Gets the failure message of this assert statement. */ + Expr getMsg() { py_exprs(result, _, this, 2) } - override string toString() { result = "Assert" } + override string toString() { result = "Assert" } } /** INTERNAL: See the class `Assign` for further information. */ library class Assign_ extends @py_Assign, Stmt { - /** Gets the value of this assignment statement. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this assignment statement. */ + Expr getValue() { py_exprs(result, _, this, 1) } - /** Gets the targets of this assignment statement. */ - ExprList getTargets() { py_expr_lists(result, this, 2) } + /** Gets the targets of this assignment statement. */ + ExprList getTargets() { py_expr_lists(result, this, 2) } - /** Gets the nth target of this assignment statement. */ - Expr getTarget(int index) { result = this.getTargets().getItem(index) } + /** Gets the nth target of this assignment statement. */ + Expr getTarget(int index) { result = this.getTargets().getItem(index) } - /** Gets a target of this assignment statement. */ - Expr getATarget() { result = this.getTargets().getAnItem() } + /** Gets a target of this assignment statement. */ + Expr getATarget() { result = this.getTargets().getAnItem() } - override string toString() { result = "Assign" } + override string toString() { result = "Assign" } } /** INTERNAL: See the class `AssignExpr` for further information. */ library class AssignExpr_ extends @py_AssignExpr, Expr { - /** Gets the value of this assignment expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the value of this assignment expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - /** Gets the target of this assignment expression. */ - Expr getTarget() { py_exprs(result, _, this, 3) } + /** Gets the target of this assignment expression. */ + Expr getTarget() { py_exprs(result, _, this, 3) } - override string toString() { result = "AssignExpr" } + override string toString() { result = "AssignExpr" } } /** INTERNAL: See the class `Attribute` for further information. */ library class Attribute_ extends @py_Attribute, Expr { - /** Gets the object of this attribute expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the object of this attribute expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - /** Gets the attribute name of this attribute expression. */ - string getAttr() { py_strs(result, this, 3) } + /** Gets the attribute name of this attribute expression. */ + string getAttr() { py_strs(result, this, 3) } - /** Gets the context of this attribute expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this attribute expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override string toString() { result = "Attribute" } + override string toString() { result = "Attribute" } } /** INTERNAL: See the class `AugAssign` for further information. */ library class AugAssign_ extends @py_AugAssign, Stmt { - /** Gets the operation of this augmented assignment statement. */ - BinaryExpr getOperation() { py_exprs(result, _, this, 1) } + /** Gets the operation of this augmented assignment statement. */ + BinaryExpr getOperation() { py_exprs(result, _, this, 1) } - override string toString() { result = "AugAssign" } + override string toString() { result = "AugAssign" } } /** INTERNAL: See the class `AugLoad` for further information. */ library class AugLoad_ extends @py_AugLoad, ExprContext { - override string toString() { result = "AugLoad" } + override string toString() { result = "AugLoad" } } /** INTERNAL: See the class `AugStore` for further information. */ library class AugStore_ extends @py_AugStore, ExprContext { - override string toString() { result = "AugStore" } + override string toString() { result = "AugStore" } } /** INTERNAL: See the class `Await` for further information. */ library class Await_ extends @py_Await, Expr { - /** Gets the expression waited upon of this await expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the expression waited upon of this await expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - override string toString() { result = "Await" } + override string toString() { result = "Await" } } /** INTERNAL: See the class `BinaryExpr` for further information. */ library class BinaryExpr_ extends @py_BinaryExpr, Expr { - /** Gets the left sub-expression of this binary expression. */ - Expr getLeft() { py_exprs(result, _, this, 2) } + /** Gets the left sub-expression of this binary expression. */ + Expr getLeft() { py_exprs(result, _, this, 2) } - /** Gets the operator of this binary expression. */ - Operator getOp() { py_operators(result, _, this) } + /** Gets the operator of this binary expression. */ + Operator getOp() { py_operators(result, _, this) } - /** Gets the right sub-expression of this binary expression. */ - Expr getRight() { py_exprs(result, _, this, 4) } + /** Gets the right sub-expression of this binary expression. */ + Expr getRight() { py_exprs(result, _, this, 4) } - override ExprParent getParent() { py_exprs(this, _, result, _) } + override ExprParent getParent() { py_exprs(this, _, result, _) } - override string toString() { result = "BinaryExpr" } + override string toString() { result = "BinaryExpr" } } /** INTERNAL: See the class `BitAnd` for further information. */ library class BitAnd_ extends @py_BitAnd, Operator { - override string toString() { result = "BitAnd" } + override string toString() { result = "BitAnd" } } /** INTERNAL: See the class `BitOr` for further information. */ library class BitOr_ extends @py_BitOr, Operator { - override string toString() { result = "BitOr" } + override string toString() { result = "BitOr" } } /** INTERNAL: See the class `BitXor` for further information. */ library class BitXor_ extends @py_BitXor, Operator { - override string toString() { result = "BitXor" } + override string toString() { result = "BitXor" } } /** INTERNAL: See the class `BoolExpr` for further information. */ library class BoolExpr_ extends @py_BoolExpr, Expr { - /** Gets the operator of this boolean expression. */ - Boolop getOp() { py_boolops(result, _, this) } + /** Gets the operator of this boolean expression. */ + Boolop getOp() { py_boolops(result, _, this) } - /** Gets the sub-expressions of this boolean expression. */ - ExprList getValues() { py_expr_lists(result, this, 3) } + /** Gets the sub-expressions of this boolean expression. */ + ExprList getValues() { py_expr_lists(result, this, 3) } - /** Gets the nth sub-expression of this boolean expression. */ - Expr getValue(int index) { result = this.getValues().getItem(index) } + /** Gets the nth sub-expression of this boolean expression. */ + Expr getValue(int index) { result = this.getValues().getItem(index) } - /** Gets a sub-expression of this boolean expression. */ - Expr getAValue() { result = this.getValues().getAnItem() } + /** Gets a sub-expression of this boolean expression. */ + Expr getAValue() { result = this.getValues().getAnItem() } - override string toString() { result = "BoolExpr" } + override string toString() { result = "BoolExpr" } } /** INTERNAL: See the class `Break` for further information. */ library class Break_ extends @py_Break, Stmt { - override string toString() { result = "Break" } + override string toString() { result = "Break" } } /** INTERNAL: See the class `Bytes` for further information. */ library class Bytes_ extends @py_Bytes, Expr { - /** Gets the value of this bytes expression. */ - string getS() { py_bytes(result, this, 2) } + /** Gets the value of this bytes expression. */ + string getS() { py_bytes(result, this, 2) } - /** Gets the prefix of this bytes expression. */ - string getPrefix() { py_bytes(result, this, 3) } + /** Gets the prefix of this bytes expression. */ + string getPrefix() { py_bytes(result, this, 3) } - /** Gets the implicitly_concatenated_parts of this bytes expression. */ - StringPartList getImplicitlyConcatenatedParts() { py_StringPart_lists(result, this) } + /** Gets the implicitly_concatenated_parts of this bytes expression. */ + StringPartList getImplicitlyConcatenatedParts() { py_StringPart_lists(result, this) } - /** Gets the nth implicitly_concatenated_part of this bytes expression. */ - StringPart getImplicitlyConcatenatedPart(int index) { - result = this.getImplicitlyConcatenatedParts().getItem(index) - } + /** Gets the nth implicitly_concatenated_part of this bytes expression. */ + StringPart getImplicitlyConcatenatedPart(int index) { + result = this.getImplicitlyConcatenatedParts().getItem(index) + } - /** Gets an implicitly_concatenated_part of this bytes expression. */ - StringPart getAnImplicitlyConcatenatedPart() { - result = this.getImplicitlyConcatenatedParts().getAnItem() - } + /** Gets an implicitly_concatenated_part of this bytes expression. */ + StringPart getAnImplicitlyConcatenatedPart() { + result = this.getImplicitlyConcatenatedParts().getAnItem() + } - override string toString() { result = "Bytes" } + override string toString() { result = "Bytes" } } /** INTERNAL: See the class `BytesOrStr` for further information. */ library class BytesOrStr_ extends @py_Bytes_or_Str { - /** Gets a textual representation of this element. */ - string toString() { result = "BytesOrStr" } + /** Gets a textual representation of this element. */ + string toString() { result = "BytesOrStr" } } /** INTERNAL: See the class `Call` for further information. */ library class Call_ extends @py_Call, Expr { - /** Gets the callable of this call expression. */ - Expr getFunc() { py_exprs(result, _, this, 2) } + /** Gets the callable of this call expression. */ + Expr getFunc() { py_exprs(result, _, this, 2) } - /** Gets the positional arguments of this call expression. */ - ExprList getPositionalArgs() { py_expr_lists(result, this, 3) } + /** Gets the positional arguments of this call expression. */ + ExprList getPositionalArgs() { py_expr_lists(result, this, 3) } - /** Gets the nth positional argument of this call expression. */ - Expr getPositionalArg(int index) { result = this.getPositionalArgs().getItem(index) } + /** Gets the nth positional argument of this call expression. */ + Expr getPositionalArg(int index) { result = this.getPositionalArgs().getItem(index) } - /** Gets a positional argument of this call expression. */ - Expr getAPositionalArg() { result = this.getPositionalArgs().getAnItem() } + /** Gets a positional argument of this call expression. */ + Expr getAPositionalArg() { result = this.getPositionalArgs().getAnItem() } - /** Gets the named arguments of this call expression. */ - DictItemList getNamedArgs() { py_dict_item_lists(result, this) } + /** Gets the named arguments of this call expression. */ + DictItemList getNamedArgs() { py_dict_item_lists(result, this) } - /** Gets the nth named argument of this call expression. */ - DictItem getNamedArg(int index) { result = this.getNamedArgs().getItem(index) } + /** Gets the nth named argument of this call expression. */ + DictItem getNamedArg(int index) { result = this.getNamedArgs().getItem(index) } - /** Gets a named argument of this call expression. */ - DictItem getANamedArg() { result = this.getNamedArgs().getAnItem() } + /** Gets a named argument of this call expression. */ + DictItem getANamedArg() { result = this.getNamedArgs().getAnItem() } - override string toString() { result = "Call" } + override string toString() { result = "Call" } } /** INTERNAL: See the class `Class` for further information. */ library class Class_ extends @py_Class { - /** Gets the name of this class. */ - string getName() { py_strs(result, this, 0) } + /** Gets the name of this class. */ + string getName() { py_strs(result, this, 0) } - /** Gets the body of this class. */ - StmtList getBody() { py_stmt_lists(result, this, 1) } + /** Gets the body of this class. */ + StmtList getBody() { py_stmt_lists(result, this, 1) } - /** Gets the nth statement of this class. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this class. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this class. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this class. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - ClassExpr getParent() { py_Classes(this, result) } + ClassExpr getParent() { py_Classes(this, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Class" } + /** Gets a textual representation of this element. */ + string toString() { result = "Class" } } /** INTERNAL: See the class `ClassExpr` for further information. */ library class ClassExpr_ extends @py_ClassExpr, Expr { - /** Gets the name of this class definition. */ - string getName() { py_strs(result, this, 2) } + /** Gets the name of this class definition. */ + string getName() { py_strs(result, this, 2) } - /** Gets the bases of this class definition. */ - ExprList getBases() { py_expr_lists(result, this, 3) } + /** Gets the bases of this class definition. */ + ExprList getBases() { py_expr_lists(result, this, 3) } - /** Gets the nth base of this class definition. */ - Expr getBase(int index) { result = this.getBases().getItem(index) } + /** Gets the nth base of this class definition. */ + Expr getBase(int index) { result = this.getBases().getItem(index) } - /** Gets a base of this class definition. */ - Expr getABase() { result = this.getBases().getAnItem() } + /** Gets a base of this class definition. */ + Expr getABase() { result = this.getBases().getAnItem() } - /** Gets the keyword arguments of this class definition. */ - DictItemList getKeywords() { py_dict_item_lists(result, this) } + /** Gets the keyword arguments of this class definition. */ + DictItemList getKeywords() { py_dict_item_lists(result, this) } - /** Gets the nth keyword argument of this class definition. */ - DictItem getKeyword(int index) { result = this.getKeywords().getItem(index) } + /** Gets the nth keyword argument of this class definition. */ + DictItem getKeyword(int index) { result = this.getKeywords().getItem(index) } - /** Gets a keyword argument of this class definition. */ - DictItem getAKeyword() { result = this.getKeywords().getAnItem() } + /** Gets a keyword argument of this class definition. */ + DictItem getAKeyword() { result = this.getKeywords().getAnItem() } - /** Gets the class scope of this class definition. */ - Class getInnerScope() { py_Classes(result, this) } + /** Gets the class scope of this class definition. */ + Class getInnerScope() { py_Classes(result, this) } - override string toString() { result = "ClassExpr" } + override string toString() { result = "ClassExpr" } } /** INTERNAL: See the class `Compare` for further information. */ library class Compare_ extends @py_Compare, Expr { - /** Gets the left sub-expression of this compare expression. */ - Expr getLeft() { py_exprs(result, _, this, 2) } + /** Gets the left sub-expression of this compare expression. */ + Expr getLeft() { py_exprs(result, _, this, 2) } - /** Gets the comparison operators of this compare expression. */ - CmpopList getOps() { py_cmpop_lists(result, this) } + /** Gets the comparison operators of this compare expression. */ + CmpopList getOps() { py_cmpop_lists(result, this) } - /** Gets the nth comparison operator of this compare expression. */ - Cmpop getOp(int index) { result = this.getOps().getItem(index) } + /** Gets the nth comparison operator of this compare expression. */ + Cmpop getOp(int index) { result = this.getOps().getItem(index) } - /** Gets a comparison operator of this compare expression. */ - Cmpop getAnOp() { result = this.getOps().getAnItem() } + /** Gets a comparison operator of this compare expression. */ + Cmpop getAnOp() { result = this.getOps().getAnItem() } - /** Gets the right sub-expressions of this compare expression. */ - ExprList getComparators() { py_expr_lists(result, this, 4) } + /** Gets the right sub-expressions of this compare expression. */ + ExprList getComparators() { py_expr_lists(result, this, 4) } - /** Gets the nth right sub-expression of this compare expression. */ - Expr getComparator(int index) { result = this.getComparators().getItem(index) } + /** Gets the nth right sub-expression of this compare expression. */ + Expr getComparator(int index) { result = this.getComparators().getItem(index) } - /** Gets a right sub-expression of this compare expression. */ - Expr getAComparator() { result = this.getComparators().getAnItem() } + /** Gets a right sub-expression of this compare expression. */ + Expr getAComparator() { result = this.getComparators().getAnItem() } - override string toString() { result = "Compare" } + override string toString() { result = "Compare" } } /** INTERNAL: See the class `Continue` for further information. */ library class Continue_ extends @py_Continue, Stmt { - override string toString() { result = "Continue" } + override string toString() { result = "Continue" } } /** INTERNAL: See the class `Del` for further information. */ library class Del_ extends @py_Del, ExprContext { - override string toString() { result = "Del" } + override string toString() { result = "Del" } } /** INTERNAL: See the class `Delete` for further information. */ library class Delete_ extends @py_Delete, Stmt { - /** Gets the targets of this delete statement. */ - ExprList getTargets() { py_expr_lists(result, this, 1) } + /** Gets the targets of this delete statement. */ + ExprList getTargets() { py_expr_lists(result, this, 1) } - /** Gets the nth target of this delete statement. */ - Expr getTarget(int index) { result = this.getTargets().getItem(index) } + /** Gets the nth target of this delete statement. */ + Expr getTarget(int index) { result = this.getTargets().getItem(index) } - /** Gets a target of this delete statement. */ - Expr getATarget() { result = this.getTargets().getAnItem() } + /** Gets a target of this delete statement. */ + Expr getATarget() { result = this.getTargets().getAnItem() } - override string toString() { result = "Delete" } + override string toString() { result = "Delete" } } /** INTERNAL: See the class `Dict` for further information. */ library class Dict_ extends @py_Dict, Expr { - /** Gets the items of this dictionary expression. */ - DictItemList getItems() { py_dict_item_lists(result, this) } + /** Gets the items of this dictionary expression. */ + DictItemList getItems() { py_dict_item_lists(result, this) } - /** Gets the nth item of this dictionary expression. */ - DictItem getItem(int index) { result = this.getItems().getItem(index) } + /** Gets the nth item of this dictionary expression. */ + DictItem getItem(int index) { result = this.getItems().getItem(index) } - /** Gets an item of this dictionary expression. */ - DictItem getAnItem() { result = this.getItems().getAnItem() } + /** Gets an item of this dictionary expression. */ + DictItem getAnItem() { result = this.getItems().getAnItem() } - override string toString() { result = "Dict" } + override string toString() { result = "Dict" } } /** INTERNAL: See the class `DictComp` for further information. */ library class DictComp_ extends @py_DictComp, Expr { - /** Gets the implementation of this dictionary comprehension. */ - Function getFunction() { py_Functions(result, this) } + /** Gets the implementation of this dictionary comprehension. */ + Function getFunction() { py_Functions(result, this) } - /** Gets the iterable of this dictionary comprehension. */ - Expr getIterable() { py_exprs(result, _, this, 3) } + /** Gets the iterable of this dictionary comprehension. */ + Expr getIterable() { py_exprs(result, _, this, 3) } - override string toString() { result = "DictComp" } + override string toString() { result = "DictComp" } } /** INTERNAL: See the class `DictUnpacking` for further information. */ library class DictUnpacking_ extends @py_DictUnpacking, DictItem { - /** Gets the location of this dictionary unpacking. */ - override Location getLocation() { py_locations(result, this) } + /** Gets the location of this dictionary unpacking. */ + override Location getLocation() { py_locations(result, this) } - /** Gets the value of this dictionary unpacking. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this dictionary unpacking. */ + Expr getValue() { py_exprs(result, _, this, 1) } - override string toString() { result = "DictUnpacking" } + override string toString() { result = "DictUnpacking" } } /** INTERNAL: See the class `Div` for further information. */ library class Div_ extends @py_Div, Operator { - override string toString() { result = "Div" } + override string toString() { result = "Div" } } /** INTERNAL: See the class `Ellipsis` for further information. */ library class Ellipsis_ extends @py_Ellipsis, Expr { - override string toString() { result = "Ellipsis" } + override string toString() { result = "Ellipsis" } } /** INTERNAL: See the class `Eq` for further information. */ library class Eq_ extends @py_Eq, Cmpop { - override string toString() { result = "Eq" } + override string toString() { result = "Eq" } } /** INTERNAL: See the class `ExceptStmt` for further information. */ library class ExceptStmt_ extends @py_ExceptStmt, Stmt { - /** Gets the type of this except block. */ - Expr getType() { py_exprs(result, _, this, 1) } + /** Gets the type of this except block. */ + Expr getType() { py_exprs(result, _, this, 1) } - /** Gets the name of this except block. */ - Expr getName() { py_exprs(result, _, this, 2) } + /** Gets the name of this except block. */ + Expr getName() { py_exprs(result, _, this, 2) } - /** Gets the body of this except block. */ - StmtList getBody() { py_stmt_lists(result, this, 3) } + /** Gets the body of this except block. */ + StmtList getBody() { py_stmt_lists(result, this, 3) } - /** Gets the nth statement of this except block. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this except block. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this except block. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this except block. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - override string toString() { result = "ExceptStmt" } + override string toString() { result = "ExceptStmt" } } /** INTERNAL: See the class `Exec` for further information. */ library class Exec_ extends @py_Exec, Stmt { - /** Gets the body of this exec statement. */ - Expr getBody() { py_exprs(result, _, this, 1) } + /** Gets the body of this exec statement. */ + Expr getBody() { py_exprs(result, _, this, 1) } - /** Gets the globals of this exec statement. */ - Expr getGlobals() { py_exprs(result, _, this, 2) } + /** Gets the globals of this exec statement. */ + Expr getGlobals() { py_exprs(result, _, this, 2) } - /** Gets the locals of this exec statement. */ - Expr getLocals() { py_exprs(result, _, this, 3) } + /** Gets the locals of this exec statement. */ + Expr getLocals() { py_exprs(result, _, this, 3) } - override string toString() { result = "Exec" } + override string toString() { result = "Exec" } } /** INTERNAL: See the class `ExprStmt` for further information. */ library class ExprStmt_ extends @py_Expr_stmt, Stmt { - /** Gets the value of this expr statement. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this expr statement. */ + Expr getValue() { py_exprs(result, _, this, 1) } - override string toString() { result = "ExprStmt" } + override string toString() { result = "ExprStmt" } } /** INTERNAL: See the class `Filter` for further information. */ library class Filter_ extends @py_Filter, Expr { - /** Gets the filtered value of this template filter expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the filtered value of this template filter expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - /** Gets the filter of this template filter expression. */ - Expr getFilter() { py_exprs(result, _, this, 3) } + /** Gets the filter of this template filter expression. */ + Expr getFilter() { py_exprs(result, _, this, 3) } - override string toString() { result = "Filter" } + override string toString() { result = "Filter" } } /** INTERNAL: See the class `FloorDiv` for further information. */ library class FloorDiv_ extends @py_FloorDiv, Operator { - override string toString() { result = "FloorDiv" } + override string toString() { result = "FloorDiv" } } /** INTERNAL: See the class `For` for further information. */ library class For_ extends @py_For, Stmt { - /** Gets the target of this for statement. */ - Expr getTarget() { py_exprs(result, _, this, 1) } + /** Gets the target of this for statement. */ + Expr getTarget() { py_exprs(result, _, this, 1) } - /** Gets the iterable of this for statement. */ - Expr getIter() { py_exprs(result, _, this, 2) } + /** Gets the iterable of this for statement. */ + Expr getIter() { py_exprs(result, _, this, 2) } - /** Gets the body of this for statement. */ - StmtList getBody() { py_stmt_lists(result, this, 3) } + /** Gets the body of this for statement. */ + StmtList getBody() { py_stmt_lists(result, this, 3) } - /** Gets the nth statement of this for statement. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this for statement. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this for statement. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this for statement. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - /** Gets the else block of this for statement. */ - StmtList getOrelse() { py_stmt_lists(result, this, 4) } + /** Gets the else block of this for statement. */ + StmtList getOrelse() { py_stmt_lists(result, this, 4) } - /** Gets the nth else statement of this for statement. */ - Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } + /** Gets the nth else statement of this for statement. */ + Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } - /** Gets an else statement of this for statement. */ - Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } + /** Gets an else statement of this for statement. */ + Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } - /** Whether the async property of this for statement is true. */ - predicate isAsync() { py_bools(this, 5) } + /** Whether the async property of this for statement is true. */ + predicate isAsync() { py_bools(this, 5) } - override string toString() { result = "For" } + override string toString() { result = "For" } } /** INTERNAL: See the class `FormattedValue` for further information. */ library class FormattedValue_ extends @py_FormattedValue, Expr { - /** Gets the expression to be formatted of this formatted value. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the expression to be formatted of this formatted value. */ + Expr getValue() { py_exprs(result, _, this, 2) } - /** Gets the type conversion of this formatted value. */ - string getConversion() { py_strs(result, this, 3) } + /** Gets the type conversion of this formatted value. */ + string getConversion() { py_strs(result, this, 3) } - /** Gets the format specifier of this formatted value. */ - Fstring getFormatSpec() { py_exprs(result, _, this, 4) } + /** Gets the format specifier of this formatted value. */ + Fstring getFormatSpec() { py_exprs(result, _, this, 4) } - override string toString() { result = "FormattedValue" } + override string toString() { result = "FormattedValue" } } /** INTERNAL: See the class `Function` for further information. */ library class Function_ extends @py_Function { - /** Gets the name of this function. */ - string getName() { py_strs(result, this, 0) } + /** Gets the name of this function. */ + string getName() { py_strs(result, this, 0) } - /** Gets the positional parameter list of this function. */ - ParameterList getArgs() { py_parameter_lists(result, this) } + /** Gets the positional parameter list of this function. */ + ParameterList getArgs() { py_parameter_lists(result, this) } - /** Gets the nth positional parameter of this function. */ - Parameter getArg(int index) { result = this.getArgs().getItem(index) } + /** Gets the nth positional parameter of this function. */ + Parameter getArg(int index) { result = this.getArgs().getItem(index) } - /** Gets a positional parameter of this function. */ - Parameter getAnArg() { result = this.getArgs().getAnItem() } + /** Gets a positional parameter of this function. */ + Parameter getAnArg() { result = this.getArgs().getAnItem() } - /** Gets the tuple (*) parameter of this function. */ - Expr getVararg() { py_exprs(result, _, this, 2) } + /** Gets the tuple (*) parameter of this function. */ + Expr getVararg() { py_exprs(result, _, this, 2) } - /** Gets the keyword-only parameter list of this function. */ - ExprList getKwonlyargs() { py_expr_lists(result, this, 3) } + /** Gets the keyword-only parameter list of this function. */ + ExprList getKwonlyargs() { py_expr_lists(result, this, 3) } - /** Gets the nth keyword-only parameter of this function. */ - Expr getKwonlyarg(int index) { result = this.getKwonlyargs().getItem(index) } + /** Gets the nth keyword-only parameter of this function. */ + Expr getKwonlyarg(int index) { result = this.getKwonlyargs().getItem(index) } - /** Gets a keyword-only parameter of this function. */ - Expr getAKwonlyarg() { result = this.getKwonlyargs().getAnItem() } + /** Gets a keyword-only parameter of this function. */ + Expr getAKwonlyarg() { result = this.getKwonlyargs().getAnItem() } - /** Gets the dictionary (**) parameter of this function. */ - Expr getKwarg() { py_exprs(result, _, this, 4) } + /** Gets the dictionary (**) parameter of this function. */ + Expr getKwarg() { py_exprs(result, _, this, 4) } - /** Gets the body of this function. */ - StmtList getBody() { py_stmt_lists(result, this, 5) } + /** Gets the body of this function. */ + StmtList getBody() { py_stmt_lists(result, this, 5) } - /** Gets the nth statement of this function. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this function. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this function. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this function. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - /** Whether the async property of this function is true. */ - predicate isAsync() { py_bools(this, 6) } + /** Whether the async property of this function is true. */ + predicate isAsync() { py_bools(this, 6) } - FunctionParent getParent() { py_Functions(this, result) } + FunctionParent getParent() { py_Functions(this, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Function" } + /** Gets a textual representation of this element. */ + string toString() { result = "Function" } } /** INTERNAL: See the class `FunctionExpr` for further information. */ library class FunctionExpr_ extends @py_FunctionExpr, Expr { - /** Gets the name of this function definition. */ - string getName() { py_strs(result, this, 2) } + /** Gets the name of this function definition. */ + string getName() { py_strs(result, this, 2) } - /** Gets the parameters of this function definition. */ - Arguments getArgs() { py_arguments(result, this) } + /** Gets the parameters of this function definition. */ + Arguments getArgs() { py_arguments(result, this) } - /** Gets the return annotation of this function definition. */ - Expr getReturns() { py_exprs(result, _, this, 4) } + /** Gets the return annotation of this function definition. */ + Expr getReturns() { py_exprs(result, _, this, 4) } - /** Gets the function scope of this function definition. */ - Function getInnerScope() { py_Functions(result, this) } + /** Gets the function scope of this function definition. */ + Function getInnerScope() { py_Functions(result, this) } - override string toString() { result = "FunctionExpr" } + override string toString() { result = "FunctionExpr" } } /** INTERNAL: See the class `FunctionParent` for further information. */ library class FunctionParent_ extends @py_Function_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "FunctionParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "FunctionParent" } } /** INTERNAL: See the class `GeneratorExp` for further information. */ library class GeneratorExp_ extends @py_GeneratorExp, Expr { - /** Gets the implementation of this generator expression. */ - Function getFunction() { py_Functions(result, this) } + /** Gets the implementation of this generator expression. */ + Function getFunction() { py_Functions(result, this) } - /** Gets the iterable of this generator expression. */ - Expr getIterable() { py_exprs(result, _, this, 3) } + /** Gets the iterable of this generator expression. */ + Expr getIterable() { py_exprs(result, _, this, 3) } - override string toString() { result = "GeneratorExp" } + override string toString() { result = "GeneratorExp" } } /** INTERNAL: See the class `Global` for further information. */ library class Global_ extends @py_Global, Stmt { - /** Gets the names of this global statement. */ - StringList getNames() { py_str_lists(result, this) } + /** Gets the names of this global statement. */ + StringList getNames() { py_str_lists(result, this) } - /** Gets the nth name of this global statement. */ - string getName(int index) { result = this.getNames().getItem(index) } + /** Gets the nth name of this global statement. */ + string getName(int index) { result = this.getNames().getItem(index) } - /** Gets a name of this global statement. */ - string getAName() { result = this.getNames().getAnItem() } + /** Gets a name of this global statement. */ + string getAName() { result = this.getNames().getAnItem() } - override string toString() { result = "Global" } + override string toString() { result = "Global" } } /** INTERNAL: See the class `Gt` for further information. */ library class Gt_ extends @py_Gt, Cmpop { - override string toString() { result = "Gt" } + override string toString() { result = "Gt" } } /** INTERNAL: See the class `GtE` for further information. */ library class GtE_ extends @py_GtE, Cmpop { - override string toString() { result = "GtE" } + override string toString() { result = "GtE" } } /** INTERNAL: See the class `If` for further information. */ library class If_ extends @py_If, Stmt { - /** Gets the test of this if statement. */ - Expr getTest() { py_exprs(result, _, this, 1) } + /** Gets the test of this if statement. */ + Expr getTest() { py_exprs(result, _, this, 1) } - /** Gets the if-true block of this if statement. */ - StmtList getBody() { py_stmt_lists(result, this, 2) } + /** Gets the if-true block of this if statement. */ + StmtList getBody() { py_stmt_lists(result, this, 2) } - /** Gets the nth if-true statement of this if statement. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth if-true statement of this if statement. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets an if-true statement of this if statement. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets an if-true statement of this if statement. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - /** Gets the if-false block of this if statement. */ - StmtList getOrelse() { py_stmt_lists(result, this, 3) } + /** Gets the if-false block of this if statement. */ + StmtList getOrelse() { py_stmt_lists(result, this, 3) } - /** Gets the nth if-false statement of this if statement. */ - Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } + /** Gets the nth if-false statement of this if statement. */ + Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } - /** Gets an if-false statement of this if statement. */ - Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } + /** Gets an if-false statement of this if statement. */ + Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } - override string toString() { result = "If" } + override string toString() { result = "If" } } /** INTERNAL: See the class `IfExp` for further information. */ library class IfExp_ extends @py_IfExp, Expr { - /** Gets the test of this if expression. */ - Expr getTest() { py_exprs(result, _, this, 2) } + /** Gets the test of this if expression. */ + Expr getTest() { py_exprs(result, _, this, 2) } - /** Gets the if-true expression of this if expression. */ - Expr getBody() { py_exprs(result, _, this, 3) } + /** Gets the if-true expression of this if expression. */ + Expr getBody() { py_exprs(result, _, this, 3) } - /** Gets the if-false expression of this if expression. */ - Expr getOrelse() { py_exprs(result, _, this, 4) } + /** Gets the if-false expression of this if expression. */ + Expr getOrelse() { py_exprs(result, _, this, 4) } - override string toString() { result = "IfExp" } + override string toString() { result = "IfExp" } } /** INTERNAL: See the class `Import` for further information. */ library class Import_ extends @py_Import, Stmt { - /** Gets the alias list of this import statement. */ - AliasList getNames() { py_alias_lists(result, this) } + /** Gets the alias list of this import statement. */ + AliasList getNames() { py_alias_lists(result, this) } - /** Gets the nth alias of this import statement. */ - Alias getName(int index) { result = this.getNames().getItem(index) } + /** Gets the nth alias of this import statement. */ + Alias getName(int index) { result = this.getNames().getItem(index) } - /** Gets an alias of this import statement. */ - Alias getAName() { result = this.getNames().getAnItem() } + /** Gets an alias of this import statement. */ + Alias getAName() { result = this.getNames().getAnItem() } - override string toString() { result = "Import" } + override string toString() { result = "Import" } } /** INTERNAL: See the class `ImportExpr` for further information. */ library class ImportExpr_ extends @py_ImportExpr, Expr { - /** Gets the level of this import expression. */ - int getLevel() { py_ints(result, this) } + /** Gets the level of this import expression. */ + int getLevel() { py_ints(result, this) } - /** Gets the name of this import expression. */ - string getName() { py_strs(result, this, 3) } + /** Gets the name of this import expression. */ + string getName() { py_strs(result, this, 3) } - /** Whether the top level property of this import expression is true. */ - predicate isTop() { py_bools(this, 4) } + /** Whether the top level property of this import expression is true. */ + predicate isTop() { py_bools(this, 4) } - override string toString() { result = "ImportExpr" } + override string toString() { result = "ImportExpr" } } /** INTERNAL: See the class `ImportStar` for further information. */ library class ImportStar_ extends @py_ImportStar, Stmt { - /** Gets the module of this import * statement. */ - Expr getModule() { py_exprs(result, _, this, 1) } + /** Gets the module of this import * statement. */ + Expr getModule() { py_exprs(result, _, this, 1) } - override string toString() { result = "ImportStar" } + override string toString() { result = "ImportStar" } } /** INTERNAL: See the class `ImportMember` for further information. */ library class ImportMember_ extends @py_ImportMember, Expr { - /** Gets the module of this from import. */ - Expr getModule() { py_exprs(result, _, this, 2) } + /** Gets the module of this from import. */ + Expr getModule() { py_exprs(result, _, this, 2) } - /** Gets the name of this from import. */ - string getName() { py_strs(result, this, 3) } + /** Gets the name of this from import. */ + string getName() { py_strs(result, this, 3) } - override string toString() { result = "ImportMember" } + override string toString() { result = "ImportMember" } } /** INTERNAL: See the class `In` for further information. */ library class In_ extends @py_In, Cmpop { - override string toString() { result = "In" } + override string toString() { result = "In" } } /** INTERNAL: See the class `Invert` for further information. */ library class Invert_ extends @py_Invert, Unaryop { - override string toString() { result = "Invert" } + override string toString() { result = "Invert" } } /** INTERNAL: See the class `Is` for further information. */ library class Is_ extends @py_Is, Cmpop { - override string toString() { result = "Is" } + override string toString() { result = "Is" } } /** INTERNAL: See the class `IsNot` for further information. */ library class IsNot_ extends @py_IsNot, Cmpop { - override string toString() { result = "IsNot" } + override string toString() { result = "IsNot" } } /** INTERNAL: See the class `Fstring` for further information. */ library class Fstring_ extends @py_Fstring, Expr { - /** Gets the values of this formatted string literal. */ - ExprList getValues() { py_expr_lists(result, this, 2) } + /** Gets the values of this formatted string literal. */ + ExprList getValues() { py_expr_lists(result, this, 2) } - /** Gets the nth value of this formatted string literal. */ - Expr getValue(int index) { result = this.getValues().getItem(index) } + /** Gets the nth value of this formatted string literal. */ + Expr getValue(int index) { result = this.getValues().getItem(index) } - /** Gets a value of this formatted string literal. */ - Expr getAValue() { result = this.getValues().getAnItem() } + /** Gets a value of this formatted string literal. */ + Expr getAValue() { result = this.getValues().getAnItem() } - override ExprParent getParent() { py_exprs(this, _, result, _) } + override ExprParent getParent() { py_exprs(this, _, result, _) } - override string toString() { result = "Fstring" } + override string toString() { result = "Fstring" } } /** INTERNAL: See the class `KeyValuePair` for further information. */ library class KeyValuePair_ extends @py_KeyValuePair, DictItem { - /** Gets the location of this key-value pair. */ - override Location getLocation() { py_locations(result, this) } + /** Gets the location of this key-value pair. */ + override Location getLocation() { py_locations(result, this) } - /** Gets the value of this key-value pair. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this key-value pair. */ + Expr getValue() { py_exprs(result, _, this, 1) } - /** Gets the key of this key-value pair. */ - Expr getKey() { py_exprs(result, _, this, 2) } + /** Gets the key of this key-value pair. */ + Expr getKey() { py_exprs(result, _, this, 2) } - override string toString() { result = "KeyValuePair" } + override string toString() { result = "KeyValuePair" } } /** INTERNAL: See the class `LShift` for further information. */ library class LShift_ extends @py_LShift, Operator { - override string toString() { result = "LShift" } + override string toString() { result = "LShift" } } /** INTERNAL: See the class `Lambda` for further information. */ library class Lambda_ extends @py_Lambda, Expr { - /** Gets the arguments of this lambda expression. */ - Arguments getArgs() { py_arguments(result, this) } + /** Gets the arguments of this lambda expression. */ + Arguments getArgs() { py_arguments(result, this) } - /** Gets the function scope of this lambda expression. */ - Function getInnerScope() { py_Functions(result, this) } + /** Gets the function scope of this lambda expression. */ + Function getInnerScope() { py_Functions(result, this) } - override string toString() { result = "Lambda" } + override string toString() { result = "Lambda" } } /** INTERNAL: See the class `List` for further information. */ library class List_ extends @py_List, Expr { - /** Gets the element list of this list expression. */ - ExprList getElts() { py_expr_lists(result, this, 2) } + /** Gets the element list of this list expression. */ + ExprList getElts() { py_expr_lists(result, this, 2) } - /** Gets the nth element of this list expression. */ - Expr getElt(int index) { result = this.getElts().getItem(index) } + /** Gets the nth element of this list expression. */ + Expr getElt(int index) { result = this.getElts().getItem(index) } - /** Gets an element of this list expression. */ - Expr getAnElt() { result = this.getElts().getAnItem() } + /** Gets an element of this list expression. */ + Expr getAnElt() { result = this.getElts().getAnItem() } - /** Gets the context of this list expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this list expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override string toString() { result = "List" } + override string toString() { result = "List" } } /** INTERNAL: See the class `ListComp` for further information. */ library class ListComp_ extends @py_ListComp, Expr { - /** Gets the implementation of this list comprehension. */ - Function getFunction() { py_Functions(result, this) } + /** Gets the implementation of this list comprehension. */ + Function getFunction() { py_Functions(result, this) } - /** Gets the iterable of this list comprehension. */ - Expr getIterable() { py_exprs(result, _, this, 3) } + /** Gets the iterable of this list comprehension. */ + Expr getIterable() { py_exprs(result, _, this, 3) } - /** Gets the generators of this list comprehension. */ - ComprehensionList getGenerators() { py_comprehension_lists(result, this) } + /** Gets the generators of this list comprehension. */ + ComprehensionList getGenerators() { py_comprehension_lists(result, this) } - /** Gets the nth generator of this list comprehension. */ - Comprehension getGenerator(int index) { result = this.getGenerators().getItem(index) } + /** Gets the nth generator of this list comprehension. */ + Comprehension getGenerator(int index) { result = this.getGenerators().getItem(index) } - /** Gets a generator of this list comprehension. */ - Comprehension getAGenerator() { result = this.getGenerators().getAnItem() } + /** Gets a generator of this list comprehension. */ + Comprehension getAGenerator() { result = this.getGenerators().getAnItem() } - /** Gets the elements of this list comprehension. */ - Expr getElt() { py_exprs(result, _, this, 5) } + /** Gets the elements of this list comprehension. */ + Expr getElt() { py_exprs(result, _, this, 5) } - override string toString() { result = "ListComp" } + override string toString() { result = "ListComp" } } /** INTERNAL: See the class `Load` for further information. */ library class Load_ extends @py_Load, ExprContext { - override string toString() { result = "Load" } + override string toString() { result = "Load" } } /** INTERNAL: See the class `Lt` for further information. */ library class Lt_ extends @py_Lt, Cmpop { - override string toString() { result = "Lt" } + override string toString() { result = "Lt" } } /** INTERNAL: See the class `LtE` for further information. */ library class LtE_ extends @py_LtE, Cmpop { - override string toString() { result = "LtE" } + override string toString() { result = "LtE" } } /** INTERNAL: See the class `MatMult` for further information. */ library class MatMult_ extends @py_MatMult, Operator { - override string toString() { result = "MatMult" } + override string toString() { result = "MatMult" } } /** INTERNAL: See the class `Mod` for further information. */ library class Mod_ extends @py_Mod, Operator { - override string toString() { result = "Mod" } + override string toString() { result = "Mod" } } /** INTERNAL: See the class `Module` for further information. */ library class Module_ extends @py_Module { - /** Gets the name of this module. */ - string getName() { py_strs(result, this, 0) } + /** Gets the name of this module. */ + string getName() { py_strs(result, this, 0) } - /** Gets the hash (not populated) of this module. */ - string getHash() { py_strs(result, this, 1) } + /** Gets the hash (not populated) of this module. */ + string getHash() { py_strs(result, this, 1) } - /** Gets the body of this module. */ - StmtList getBody() { py_stmt_lists(result, this, 2) } + /** Gets the body of this module. */ + StmtList getBody() { py_stmt_lists(result, this, 2) } - /** Gets the nth statement of this module. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this module. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this module. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this module. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - /** Gets the kind of this module. */ - string getKind() { py_strs(result, this, 3) } + /** Gets the kind of this module. */ + string getKind() { py_strs(result, this, 3) } - /** Gets a textual representation of this element. */ - string toString() { result = "Module" } + /** Gets a textual representation of this element. */ + string toString() { result = "Module" } } /** INTERNAL: See the class `Mult` for further information. */ library class Mult_ extends @py_Mult, Operator { - override string toString() { result = "Mult" } + override string toString() { result = "Mult" } } /** INTERNAL: See the class `Name` for further information. */ library class Name_ extends @py_Name, Expr { - /** Gets the variable of this name expression. */ - Variable getVariable() { py_variables(result, this) } + /** Gets the variable of this name expression. */ + Variable getVariable() { py_variables(result, this) } - /** Gets the context of this name expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this name expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override ExprParent getParent() { py_exprs(this, _, result, _) } + override ExprParent getParent() { py_exprs(this, _, result, _) } - override string toString() { result = "Name" } + override string toString() { result = "Name" } } /** INTERNAL: See the class `Nonlocal` for further information. */ library class Nonlocal_ extends @py_Nonlocal, Stmt { - /** Gets the names of this nonlocal statement. */ - StringList getNames() { py_str_lists(result, this) } + /** Gets the names of this nonlocal statement. */ + StringList getNames() { py_str_lists(result, this) } - /** Gets the nth name of this nonlocal statement. */ - string getName(int index) { result = this.getNames().getItem(index) } + /** Gets the nth name of this nonlocal statement. */ + string getName(int index) { result = this.getNames().getItem(index) } - /** Gets a name of this nonlocal statement. */ - string getAName() { result = this.getNames().getAnItem() } + /** Gets a name of this nonlocal statement. */ + string getAName() { result = this.getNames().getAnItem() } - override string toString() { result = "Nonlocal" } + override string toString() { result = "Nonlocal" } } /** INTERNAL: See the class `Not` for further information. */ library class Not_ extends @py_Not, Unaryop { - override string toString() { result = "Not" } + override string toString() { result = "Not" } } /** INTERNAL: See the class `NotEq` for further information. */ library class NotEq_ extends @py_NotEq, Cmpop { - override string toString() { result = "NotEq" } + override string toString() { result = "NotEq" } } /** INTERNAL: See the class `NotIn` for further information. */ library class NotIn_ extends @py_NotIn, Cmpop { - override string toString() { result = "NotIn" } + override string toString() { result = "NotIn" } } /** INTERNAL: See the class `Num` for further information. */ library class Num_ extends @py_Num, Expr { - /** Gets the value of this numeric literal. */ - string getN() { py_numbers(result, this, 2) } + /** Gets the value of this numeric literal. */ + string getN() { py_numbers(result, this, 2) } - /** Gets the text of this numeric literal. */ - string getText() { py_numbers(result, this, 3) } + /** Gets the text of this numeric literal. */ + string getText() { py_numbers(result, this, 3) } - override string toString() { result = "Num" } + override string toString() { result = "Num" } } /** INTERNAL: See the class `Or` for further information. */ library class Or_ extends @py_Or, Boolop { - override string toString() { result = "Or" } + override string toString() { result = "Or" } } /** INTERNAL: See the class `Param` for further information. */ library class Param_ extends @py_Param, ExprContext { - override string toString() { result = "Param" } + override string toString() { result = "Param" } } /** INTERNAL: See the class `Pass` for further information. */ library class Pass_ extends @py_Pass, Stmt { - override string toString() { result = "Pass" } + override string toString() { result = "Pass" } } /** INTERNAL: See the class `PlaceHolder` for further information. */ library class PlaceHolder_ extends @py_PlaceHolder, Expr { - /** Gets the variable of this template place-holder expression. */ - Variable getVariable() { py_variables(result, this) } + /** Gets the variable of this template place-holder expression. */ + Variable getVariable() { py_variables(result, this) } - /** Gets the context of this template place-holder expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this template place-holder expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override string toString() { result = "PlaceHolder" } + override string toString() { result = "PlaceHolder" } } /** INTERNAL: See the class `Pow` for further information. */ library class Pow_ extends @py_Pow, Operator { - override string toString() { result = "Pow" } + override string toString() { result = "Pow" } } /** INTERNAL: See the class `Print` for further information. */ library class Print_ extends @py_Print, Stmt { - /** Gets the destination of this print statement. */ - Expr getDest() { py_exprs(result, _, this, 1) } + /** Gets the destination of this print statement. */ + Expr getDest() { py_exprs(result, _, this, 1) } - /** Gets the values of this print statement. */ - ExprList getValues() { py_expr_lists(result, this, 2) } + /** Gets the values of this print statement. */ + ExprList getValues() { py_expr_lists(result, this, 2) } - /** Gets the nth value of this print statement. */ - Expr getValue(int index) { result = this.getValues().getItem(index) } + /** Gets the nth value of this print statement. */ + Expr getValue(int index) { result = this.getValues().getItem(index) } - /** Gets a value of this print statement. */ - Expr getAValue() { result = this.getValues().getAnItem() } + /** Gets a value of this print statement. */ + Expr getAValue() { result = this.getValues().getAnItem() } - /** Whether the new line property of this print statement is true. */ - predicate isNl() { py_bools(this, 3) } + /** Whether the new line property of this print statement is true. */ + predicate isNl() { py_bools(this, 3) } - override string toString() { result = "Print" } + override string toString() { result = "Print" } } /** INTERNAL: See the class `RShift` for further information. */ library class RShift_ extends @py_RShift, Operator { - override string toString() { result = "RShift" } + override string toString() { result = "RShift" } } /** INTERNAL: See the class `Raise` for further information. */ library class Raise_ extends @py_Raise, Stmt { - /** Gets the exception of this raise statement. */ - Expr getExc() { py_exprs(result, _, this, 1) } + /** Gets the exception of this raise statement. */ + Expr getExc() { py_exprs(result, _, this, 1) } - /** Gets the cause of this raise statement. */ - Expr getCause() { py_exprs(result, _, this, 2) } + /** Gets the cause of this raise statement. */ + Expr getCause() { py_exprs(result, _, this, 2) } - /** Gets the type of this raise statement. */ - Expr getType() { py_exprs(result, _, this, 3) } + /** Gets the type of this raise statement. */ + Expr getType() { py_exprs(result, _, this, 3) } - /** Gets the instance of this raise statement. */ - Expr getInst() { py_exprs(result, _, this, 4) } + /** Gets the instance of this raise statement. */ + Expr getInst() { py_exprs(result, _, this, 4) } - /** Gets the traceback of this raise statement. */ - Expr getTback() { py_exprs(result, _, this, 5) } + /** Gets the traceback of this raise statement. */ + Expr getTback() { py_exprs(result, _, this, 5) } - override string toString() { result = "Raise" } + override string toString() { result = "Raise" } } /** INTERNAL: See the class `Repr` for further information. */ library class Repr_ extends @py_Repr, Expr { - /** Gets the value of this backtick expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the value of this backtick expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - override string toString() { result = "Repr" } + override string toString() { result = "Repr" } } /** INTERNAL: See the class `Return` for further information. */ library class Return_ extends @py_Return, Stmt { - /** Gets the value of this return statement. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this return statement. */ + Expr getValue() { py_exprs(result, _, this, 1) } - override string toString() { result = "Return" } + override string toString() { result = "Return" } } /** INTERNAL: See the class `Set` for further information. */ library class Set_ extends @py_Set, Expr { - /** Gets the elements of this set expression. */ - ExprList getElts() { py_expr_lists(result, this, 2) } + /** Gets the elements of this set expression. */ + ExprList getElts() { py_expr_lists(result, this, 2) } - /** Gets the nth element of this set expression. */ - Expr getElt(int index) { result = this.getElts().getItem(index) } + /** Gets the nth element of this set expression. */ + Expr getElt(int index) { result = this.getElts().getItem(index) } - /** Gets an element of this set expression. */ - Expr getAnElt() { result = this.getElts().getAnItem() } + /** Gets an element of this set expression. */ + Expr getAnElt() { result = this.getElts().getAnItem() } - override string toString() { result = "Set" } + override string toString() { result = "Set" } } /** INTERNAL: See the class `SetComp` for further information. */ library class SetComp_ extends @py_SetComp, Expr { - /** Gets the implementation of this set comprehension. */ - Function getFunction() { py_Functions(result, this) } + /** Gets the implementation of this set comprehension. */ + Function getFunction() { py_Functions(result, this) } - /** Gets the iterable of this set comprehension. */ - Expr getIterable() { py_exprs(result, _, this, 3) } + /** Gets the iterable of this set comprehension. */ + Expr getIterable() { py_exprs(result, _, this, 3) } - override string toString() { result = "SetComp" } + override string toString() { result = "SetComp" } } /** INTERNAL: See the class `Slice` for further information. */ library class Slice_ extends @py_Slice, Expr { - /** Gets the start of this slice. */ - Expr getStart() { py_exprs(result, _, this, 2) } + /** Gets the start of this slice. */ + Expr getStart() { py_exprs(result, _, this, 2) } - /** Gets the stop of this slice. */ - Expr getStop() { py_exprs(result, _, this, 3) } + /** Gets the stop of this slice. */ + Expr getStop() { py_exprs(result, _, this, 3) } - /** Gets the step of this slice. */ - Expr getStep() { py_exprs(result, _, this, 4) } + /** Gets the step of this slice. */ + Expr getStep() { py_exprs(result, _, this, 4) } - override string toString() { result = "Slice" } + override string toString() { result = "Slice" } } /** INTERNAL: See the class `SpecialOperation` for further information. */ library class SpecialOperation_ extends @py_SpecialOperation, Expr { - /** Gets the name of this special operation. */ - string getName() { py_strs(result, this, 2) } + /** Gets the name of this special operation. */ + string getName() { py_strs(result, this, 2) } - /** Gets the arguments of this special operation. */ - ExprList getArguments() { py_expr_lists(result, this, 3) } + /** Gets the arguments of this special operation. */ + ExprList getArguments() { py_expr_lists(result, this, 3) } - /** Gets the nth argument of this special operation. */ - Expr getArgument(int index) { result = this.getArguments().getItem(index) } + /** Gets the nth argument of this special operation. */ + Expr getArgument(int index) { result = this.getArguments().getItem(index) } - /** Gets an argument of this special operation. */ - Expr getAnArgument() { result = this.getArguments().getAnItem() } + /** Gets an argument of this special operation. */ + Expr getAnArgument() { result = this.getArguments().getAnItem() } - override string toString() { result = "SpecialOperation" } + override string toString() { result = "SpecialOperation" } } /** INTERNAL: See the class `Starred` for further information. */ library class Starred_ extends @py_Starred, Expr { - /** Gets the value of this starred expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the value of this starred expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - /** Gets the context of this starred expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this starred expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override string toString() { result = "Starred" } + override string toString() { result = "Starred" } } /** INTERNAL: See the class `Store` for further information. */ library class Store_ extends @py_Store, ExprContext { - override string toString() { result = "Store" } + override string toString() { result = "Store" } } /** INTERNAL: See the class `Str` for further information. */ library class Str_ extends @py_Str, Expr { - /** Gets the text of this string literal. */ - string getS() { py_strs(result, this, 2) } + /** Gets the text of this string literal. */ + string getS() { py_strs(result, this, 2) } - /** Gets the prefix of this string literal. */ - string getPrefix() { py_strs(result, this, 3) } + /** Gets the prefix of this string literal. */ + string getPrefix() { py_strs(result, this, 3) } - /** Gets the implicitly_concatenated_parts of this string literal. */ - StringPartList getImplicitlyConcatenatedParts() { py_StringPart_lists(result, this) } + /** Gets the implicitly_concatenated_parts of this string literal. */ + StringPartList getImplicitlyConcatenatedParts() { py_StringPart_lists(result, this) } - /** Gets the nth implicitly_concatenated_part of this string literal. */ - StringPart getImplicitlyConcatenatedPart(int index) { - result = this.getImplicitlyConcatenatedParts().getItem(index) - } + /** Gets the nth implicitly_concatenated_part of this string literal. */ + StringPart getImplicitlyConcatenatedPart(int index) { + result = this.getImplicitlyConcatenatedParts().getItem(index) + } - /** Gets an implicitly_concatenated_part of this string literal. */ - StringPart getAnImplicitlyConcatenatedPart() { - result = this.getImplicitlyConcatenatedParts().getAnItem() - } + /** Gets an implicitly_concatenated_part of this string literal. */ + StringPart getAnImplicitlyConcatenatedPart() { + result = this.getImplicitlyConcatenatedParts().getAnItem() + } - override string toString() { result = "Str" } + override string toString() { result = "Str" } } /** INTERNAL: See the class `StringPart` for further information. */ library class StringPart_ extends @py_StringPart { - /** Gets the text of this implicitly concatenated part. */ - string getText() { py_strs(result, this, 0) } + /** Gets the text of this implicitly concatenated part. */ + string getText() { py_strs(result, this, 0) } - /** Gets the location of this implicitly concatenated part. */ - Location getLocation() { py_locations(result, this) } + /** Gets the location of this implicitly concatenated part. */ + Location getLocation() { py_locations(result, this) } - StringPartList getParent() { py_StringParts(this, result, _) } + StringPartList getParent() { py_StringParts(this, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "StringPart" } + /** Gets a textual representation of this element. */ + string toString() { result = "StringPart" } } /** INTERNAL: See the class `StringPartList` for further information. */ library class StringPartList_ extends @py_StringPart_list { - BytesOrStr getParent() { py_StringPart_lists(this, result) } + BytesOrStr getParent() { py_StringPart_lists(this, result) } - /** Gets an item of this implicitly concatenated part list */ - StringPart getAnItem() { py_StringParts(result, this, _) } + /** Gets an item of this implicitly concatenated part list */ + StringPart getAnItem() { py_StringParts(result, this, _) } - /** Gets the nth item of this implicitly concatenated part list */ - StringPart getItem(int index) { py_StringParts(result, this, index) } + /** Gets the nth item of this implicitly concatenated part list */ + StringPart getItem(int index) { py_StringParts(result, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "StringPartList" } + /** Gets a textual representation of this element. */ + string toString() { result = "StringPartList" } } /** INTERNAL: See the class `Sub` for further information. */ library class Sub_ extends @py_Sub, Operator { - override string toString() { result = "Sub" } + override string toString() { result = "Sub" } } /** INTERNAL: See the class `Subscript` for further information. */ library class Subscript_ extends @py_Subscript, Expr { - /** Gets the value of this subscript expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the value of this subscript expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - /** Gets the index of this subscript expression. */ - Expr getIndex() { py_exprs(result, _, this, 3) } + /** Gets the index of this subscript expression. */ + Expr getIndex() { py_exprs(result, _, this, 3) } - /** Gets the context of this subscript expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this subscript expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override string toString() { result = "Subscript" } + override string toString() { result = "Subscript" } } /** INTERNAL: See the class `TemplateDottedNotation` for further information. */ library class TemplateDottedNotation_ extends @py_TemplateDottedNotation, Expr { - /** Gets the object of this template dotted notation expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the object of this template dotted notation expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - /** Gets the attribute name of this template dotted notation expression. */ - string getAttr() { py_strs(result, this, 3) } + /** Gets the attribute name of this template dotted notation expression. */ + string getAttr() { py_strs(result, this, 3) } - /** Gets the context of this template dotted notation expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this template dotted notation expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override string toString() { result = "TemplateDottedNotation" } + override string toString() { result = "TemplateDottedNotation" } } /** INTERNAL: See the class `TemplateWrite` for further information. */ library class TemplateWrite_ extends @py_TemplateWrite, Stmt { - /** Gets the value of this template write statement. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this template write statement. */ + Expr getValue() { py_exprs(result, _, this, 1) } - override string toString() { result = "TemplateWrite" } + override string toString() { result = "TemplateWrite" } } /** INTERNAL: See the class `Try` for further information. */ library class Try_ extends @py_Try, Stmt { - /** Gets the body of this try statement. */ - StmtList getBody() { py_stmt_lists(result, this, 1) } + /** Gets the body of this try statement. */ + StmtList getBody() { py_stmt_lists(result, this, 1) } - /** Gets the nth statement of this try statement. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this try statement. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this try statement. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this try statement. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - /** Gets the else block of this try statement. */ - StmtList getOrelse() { py_stmt_lists(result, this, 2) } + /** Gets the else block of this try statement. */ + StmtList getOrelse() { py_stmt_lists(result, this, 2) } - /** Gets the nth else statement of this try statement. */ - Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } + /** Gets the nth else statement of this try statement. */ + Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } - /** Gets an else statement of this try statement. */ - Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } + /** Gets an else statement of this try statement. */ + Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } - /** Gets the exception handlers of this try statement. */ - StmtList getHandlers() { py_stmt_lists(result, this, 3) } + /** Gets the exception handlers of this try statement. */ + StmtList getHandlers() { py_stmt_lists(result, this, 3) } - /** Gets the nth exception handler of this try statement. */ - Stmt getHandler(int index) { result = this.getHandlers().getItem(index) } + /** Gets the nth exception handler of this try statement. */ + Stmt getHandler(int index) { result = this.getHandlers().getItem(index) } - /** Gets an exception handler of this try statement. */ - Stmt getAHandler() { result = this.getHandlers().getAnItem() } + /** Gets an exception handler of this try statement. */ + Stmt getAHandler() { result = this.getHandlers().getAnItem() } - /** Gets the finally block of this try statement. */ - StmtList getFinalbody() { py_stmt_lists(result, this, 4) } + /** Gets the finally block of this try statement. */ + StmtList getFinalbody() { py_stmt_lists(result, this, 4) } - /** Gets the nth finally statement of this try statement. */ - Stmt getFinalstmt(int index) { result = this.getFinalbody().getItem(index) } + /** Gets the nth finally statement of this try statement. */ + Stmt getFinalstmt(int index) { result = this.getFinalbody().getItem(index) } - /** Gets a finally statement of this try statement. */ - Stmt getAFinalstmt() { result = this.getFinalbody().getAnItem() } + /** Gets a finally statement of this try statement. */ + Stmt getAFinalstmt() { result = this.getFinalbody().getAnItem() } - override string toString() { result = "Try" } + override string toString() { result = "Try" } } /** INTERNAL: See the class `Tuple` for further information. */ library class Tuple_ extends @py_Tuple, Expr { - /** Gets the elements of this tuple expression. */ - ExprList getElts() { py_expr_lists(result, this, 2) } + /** Gets the elements of this tuple expression. */ + ExprList getElts() { py_expr_lists(result, this, 2) } - /** Gets the nth element of this tuple expression. */ - Expr getElt(int index) { result = this.getElts().getItem(index) } + /** Gets the nth element of this tuple expression. */ + Expr getElt(int index) { result = this.getElts().getItem(index) } - /** Gets an element of this tuple expression. */ - Expr getAnElt() { result = this.getElts().getAnItem() } + /** Gets an element of this tuple expression. */ + Expr getAnElt() { result = this.getElts().getAnItem() } - /** Gets the context of this tuple expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this tuple expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override ExprParent getParent() { py_exprs(this, _, result, _) } + override ExprParent getParent() { py_exprs(this, _, result, _) } - override string toString() { result = "Tuple" } + override string toString() { result = "Tuple" } } /** INTERNAL: See the class `UAdd` for further information. */ library class UAdd_ extends @py_UAdd, Unaryop { - override string toString() { result = "UAdd" } + override string toString() { result = "UAdd" } } /** INTERNAL: See the class `USub` for further information. */ library class USub_ extends @py_USub, Unaryop { - override string toString() { result = "USub" } + override string toString() { result = "USub" } } /** INTERNAL: See the class `UnaryExpr` for further information. */ library class UnaryExpr_ extends @py_UnaryExpr, Expr { - /** Gets the operator of this unary expression. */ - Unaryop getOp() { py_unaryops(result, _, this) } + /** Gets the operator of this unary expression. */ + Unaryop getOp() { py_unaryops(result, _, this) } - /** Gets the operand of this unary expression. */ - Expr getOperand() { py_exprs(result, _, this, 3) } + /** Gets the operand of this unary expression. */ + Expr getOperand() { py_exprs(result, _, this, 3) } - override string toString() { result = "UnaryExpr" } + override string toString() { result = "UnaryExpr" } } /** INTERNAL: See the class `While` for further information. */ library class While_ extends @py_While, Stmt { - /** Gets the test of this while statement. */ - Expr getTest() { py_exprs(result, _, this, 1) } + /** Gets the test of this while statement. */ + Expr getTest() { py_exprs(result, _, this, 1) } - /** Gets the body of this while statement. */ - StmtList getBody() { py_stmt_lists(result, this, 2) } + /** Gets the body of this while statement. */ + StmtList getBody() { py_stmt_lists(result, this, 2) } - /** Gets the nth statement of this while statement. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this while statement. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this while statement. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this while statement. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - /** Gets the else block of this while statement. */ - StmtList getOrelse() { py_stmt_lists(result, this, 3) } + /** Gets the else block of this while statement. */ + StmtList getOrelse() { py_stmt_lists(result, this, 3) } - /** Gets the nth else statement of this while statement. */ - Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } + /** Gets the nth else statement of this while statement. */ + Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } - /** Gets an else statement of this while statement. */ - Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } + /** Gets an else statement of this while statement. */ + Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } - override string toString() { result = "While" } + override string toString() { result = "While" } } /** INTERNAL: See the class `With` for further information. */ library class With_ extends @py_With, Stmt { - /** Gets the context manager of this with statement. */ - Expr getContextExpr() { py_exprs(result, _, this, 1) } + /** Gets the context manager of this with statement. */ + Expr getContextExpr() { py_exprs(result, _, this, 1) } - /** Gets the optional variable of this with statement. */ - Expr getOptionalVars() { py_exprs(result, _, this, 2) } + /** Gets the optional variable of this with statement. */ + Expr getOptionalVars() { py_exprs(result, _, this, 2) } - /** Gets the body of this with statement. */ - StmtList getBody() { py_stmt_lists(result, this, 3) } + /** Gets the body of this with statement. */ + StmtList getBody() { py_stmt_lists(result, this, 3) } - /** Gets the nth statement of this with statement. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this with statement. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this with statement. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this with statement. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - /** Whether the async property of this with statement is true. */ - predicate isAsync() { py_bools(this, 4) } + /** Whether the async property of this with statement is true. */ + predicate isAsync() { py_bools(this, 4) } - override string toString() { result = "With" } + override string toString() { result = "With" } } /** INTERNAL: See the class `Yield` for further information. */ library class Yield_ extends @py_Yield, Expr { - /** Gets the value of this yield expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the value of this yield expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - override string toString() { result = "Yield" } + override string toString() { result = "Yield" } } /** INTERNAL: See the class `YieldFrom` for further information. */ library class YieldFrom_ extends @py_YieldFrom, Expr { - /** Gets the value of this yield-from expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the value of this yield-from expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - override string toString() { result = "YieldFrom" } + override string toString() { result = "YieldFrom" } } /** INTERNAL: See the class `Alias` for further information. */ library class Alias_ extends @py_alias { - /** Gets the value of this alias. */ - Expr getValue() { py_exprs(result, _, this, 0) } + /** Gets the value of this alias. */ + Expr getValue() { py_exprs(result, _, this, 0) } - /** Gets the name of this alias. */ - Expr getAsname() { py_exprs(result, _, this, 1) } + /** Gets the name of this alias. */ + Expr getAsname() { py_exprs(result, _, this, 1) } - AliasList getParent() { py_aliases(this, result, _) } + AliasList getParent() { py_aliases(this, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "Alias" } + /** Gets a textual representation of this element. */ + string toString() { result = "Alias" } } /** INTERNAL: See the class `AliasList` for further information. */ library class AliasList_ extends @py_alias_list { - Import getParent() { py_alias_lists(this, result) } + Import getParent() { py_alias_lists(this, result) } - /** Gets an item of this alias list */ - Alias getAnItem() { py_aliases(result, this, _) } + /** Gets an item of this alias list */ + Alias getAnItem() { py_aliases(result, this, _) } - /** Gets the nth item of this alias list */ - Alias getItem(int index) { py_aliases(result, this, index) } + /** Gets the nth item of this alias list */ + Alias getItem(int index) { py_aliases(result, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "AliasList" } + /** Gets a textual representation of this element. */ + string toString() { result = "AliasList" } } /** INTERNAL: See the class `Arguments` for further information. */ library class Arguments_ extends @py_arguments { - /** Gets the keyword-only default values of this parameters definition. */ - ExprList getKwDefaults() { py_expr_lists(result, this, 0) } + /** Gets the keyword-only default values of this parameters definition. */ + ExprList getKwDefaults() { py_expr_lists(result, this, 0) } - /** Gets the nth keyword-only default value of this parameters definition. */ - Expr getKwDefault(int index) { result = this.getKwDefaults().getItem(index) } + /** Gets the nth keyword-only default value of this parameters definition. */ + Expr getKwDefault(int index) { result = this.getKwDefaults().getItem(index) } - /** Gets a keyword-only default value of this parameters definition. */ - Expr getAKwDefault() { result = this.getKwDefaults().getAnItem() } + /** Gets a keyword-only default value of this parameters definition. */ + Expr getAKwDefault() { result = this.getKwDefaults().getAnItem() } - /** Gets the default values of this parameters definition. */ - ExprList getDefaults() { py_expr_lists(result, this, 1) } + /** Gets the default values of this parameters definition. */ + ExprList getDefaults() { py_expr_lists(result, this, 1) } - /** Gets the nth default value of this parameters definition. */ - Expr getDefault(int index) { result = this.getDefaults().getItem(index) } + /** Gets the nth default value of this parameters definition. */ + Expr getDefault(int index) { result = this.getDefaults().getItem(index) } - /** Gets a default value of this parameters definition. */ - Expr getADefault() { result = this.getDefaults().getAnItem() } + /** Gets a default value of this parameters definition. */ + Expr getADefault() { result = this.getDefaults().getAnItem() } - /** Gets the annotations of this parameters definition. */ - ExprList getAnnotations() { py_expr_lists(result, this, 2) } + /** Gets the annotations of this parameters definition. */ + ExprList getAnnotations() { py_expr_lists(result, this, 2) } - /** Gets the nth annotation of this parameters definition. */ - Expr getAnnotation(int index) { result = this.getAnnotations().getItem(index) } + /** Gets the nth annotation of this parameters definition. */ + Expr getAnnotation(int index) { result = this.getAnnotations().getItem(index) } - /** Gets an annotation of this parameters definition. */ - Expr getAnAnnotation() { result = this.getAnnotations().getAnItem() } + /** Gets an annotation of this parameters definition. */ + Expr getAnAnnotation() { result = this.getAnnotations().getAnItem() } - /** Gets the *arg annotation of this parameters definition. */ - Expr getVarargannotation() { py_exprs(result, _, this, 3) } + /** Gets the *arg annotation of this parameters definition. */ + Expr getVarargannotation() { py_exprs(result, _, this, 3) } - /** Gets the **kwarg annotation of this parameters definition. */ - Expr getKwargannotation() { py_exprs(result, _, this, 4) } + /** Gets the **kwarg annotation of this parameters definition. */ + Expr getKwargannotation() { py_exprs(result, _, this, 4) } - /** Gets the keyword-only annotations of this parameters definition. */ - ExprList getKwAnnotations() { py_expr_lists(result, this, 5) } + /** Gets the keyword-only annotations of this parameters definition. */ + ExprList getKwAnnotations() { py_expr_lists(result, this, 5) } - /** Gets the nth keyword-only annotation of this parameters definition. */ - Expr getKwAnnotation(int index) { result = this.getKwAnnotations().getItem(index) } + /** Gets the nth keyword-only annotation of this parameters definition. */ + Expr getKwAnnotation(int index) { result = this.getKwAnnotations().getItem(index) } - /** Gets a keyword-only annotation of this parameters definition. */ - Expr getAKwAnnotation() { result = this.getKwAnnotations().getAnItem() } + /** Gets a keyword-only annotation of this parameters definition. */ + Expr getAKwAnnotation() { result = this.getKwAnnotations().getAnItem() } - ArgumentsParent getParent() { py_arguments(this, result) } + ArgumentsParent getParent() { py_arguments(this, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Arguments" } + /** Gets a textual representation of this element. */ + string toString() { result = "Arguments" } } /** INTERNAL: See the class `ArgumentsParent` for further information. */ library class ArgumentsParent_ extends @py_arguments_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "ArgumentsParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "ArgumentsParent" } } /** INTERNAL: See the class `AstNode` for further information. */ library class AstNode_ extends @py_ast_node { - /** Gets a textual representation of this element. */ - string toString() { result = "AstNode" } + /** Gets a textual representation of this element. */ + string toString() { result = "AstNode" } } /** INTERNAL: See the class `BoolParent` for further information. */ library class BoolParent_ extends @py_bool_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "BoolParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "BoolParent" } } /** INTERNAL: See the class `Boolop` for further information. */ library class Boolop_ extends @py_boolop { - BoolExpr getParent() { py_boolops(this, _, result) } + BoolExpr getParent() { py_boolops(this, _, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Boolop" } + /** Gets a textual representation of this element. */ + string toString() { result = "Boolop" } } /** INTERNAL: See the class `Cmpop` for further information. */ library class Cmpop_ extends @py_cmpop { - CmpopList getParent() { py_cmpops(this, _, result, _) } + CmpopList getParent() { py_cmpops(this, _, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "Cmpop" } + /** Gets a textual representation of this element. */ + string toString() { result = "Cmpop" } } /** INTERNAL: See the class `CmpopList` for further information. */ library class CmpopList_ extends @py_cmpop_list { - Compare getParent() { py_cmpop_lists(this, result) } + Compare getParent() { py_cmpop_lists(this, result) } - /** Gets an item of this comparison operator list */ - Cmpop getAnItem() { py_cmpops(result, _, this, _) } + /** Gets an item of this comparison operator list */ + Cmpop getAnItem() { py_cmpops(result, _, this, _) } - /** Gets the nth item of this comparison operator list */ - Cmpop getItem(int index) { py_cmpops(result, _, this, index) } + /** Gets the nth item of this comparison operator list */ + Cmpop getItem(int index) { py_cmpops(result, _, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "CmpopList" } + /** Gets a textual representation of this element. */ + string toString() { result = "CmpopList" } } /** INTERNAL: See the class `Comprehension` for further information. */ library class Comprehension_ extends @py_comprehension { - /** Gets the location of this comprehension. */ - Location getLocation() { py_locations(result, this) } + /** Gets the location of this comprehension. */ + Location getLocation() { py_locations(result, this) } - /** Gets the iterable of this comprehension. */ - Expr getIter() { py_exprs(result, _, this, 1) } + /** Gets the iterable of this comprehension. */ + Expr getIter() { py_exprs(result, _, this, 1) } - /** Gets the target of this comprehension. */ - Expr getTarget() { py_exprs(result, _, this, 2) } + /** Gets the target of this comprehension. */ + Expr getTarget() { py_exprs(result, _, this, 2) } - /** Gets the conditions of this comprehension. */ - ExprList getIfs() { py_expr_lists(result, this, 3) } + /** Gets the conditions of this comprehension. */ + ExprList getIfs() { py_expr_lists(result, this, 3) } - /** Gets the nth condition of this comprehension. */ - Expr getIf(int index) { result = this.getIfs().getItem(index) } + /** Gets the nth condition of this comprehension. */ + Expr getIf(int index) { result = this.getIfs().getItem(index) } - /** Gets a condition of this comprehension. */ - Expr getAnIf() { result = this.getIfs().getAnItem() } + /** Gets a condition of this comprehension. */ + Expr getAnIf() { result = this.getIfs().getAnItem() } - ComprehensionList getParent() { py_comprehensions(this, result, _) } + ComprehensionList getParent() { py_comprehensions(this, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "Comprehension" } + /** Gets a textual representation of this element. */ + string toString() { result = "Comprehension" } } /** INTERNAL: See the class `ComprehensionList` for further information. */ library class ComprehensionList_ extends @py_comprehension_list { - ListComp getParent() { py_comprehension_lists(this, result) } + ListComp getParent() { py_comprehension_lists(this, result) } - /** Gets an item of this comprehension list */ - Comprehension getAnItem() { py_comprehensions(result, this, _) } + /** Gets an item of this comprehension list */ + Comprehension getAnItem() { py_comprehensions(result, this, _) } - /** Gets the nth item of this comprehension list */ - Comprehension getItem(int index) { py_comprehensions(result, this, index) } + /** Gets the nth item of this comprehension list */ + Comprehension getItem(int index) { py_comprehensions(result, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "ComprehensionList" } + /** Gets a textual representation of this element. */ + string toString() { result = "ComprehensionList" } } /** INTERNAL: See the class `DictItem` for further information. */ library class DictItem_ extends @py_dict_item { - DictItemList getParent() { py_dict_items(this, _, result, _) } + DictItemList getParent() { py_dict_items(this, _, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "DictItem" } + /** Gets a textual representation of this element. */ + string toString() { result = "DictItem" } } /** INTERNAL: See the class `DictItemList` for further information. */ library class DictItemList_ extends @py_dict_item_list { - DictItemListParent getParent() { py_dict_item_lists(this, result) } + DictItemListParent getParent() { py_dict_item_lists(this, result) } - /** Gets an item of this dict_item list */ - DictItem getAnItem() { py_dict_items(result, _, this, _) } + /** Gets an item of this dict_item list */ + DictItem getAnItem() { py_dict_items(result, _, this, _) } - /** Gets the nth item of this dict_item list */ - DictItem getItem(int index) { py_dict_items(result, _, this, index) } + /** Gets the nth item of this dict_item list */ + DictItem getItem(int index) { py_dict_items(result, _, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "DictItemList" } + /** Gets a textual representation of this element. */ + string toString() { result = "DictItemList" } } /** INTERNAL: See the class `DictItemListParent` for further information. */ library class DictItemListParent_ extends @py_dict_item_list_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "DictItemListParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "DictItemListParent" } } /** INTERNAL: See the class `Expr` for further information. */ library class Expr_ extends @py_expr { - /** Gets the location of this expression. */ - Location getLocation() { py_locations(result, this) } + /** Gets the location of this expression. */ + Location getLocation() { py_locations(result, this) } - /** Whether the parenthesised property of this expression is true. */ - predicate isParenthesised() { py_bools(this, 1) } + /** Whether the parenthesised property of this expression is true. */ + predicate isParenthesised() { py_bools(this, 1) } - ExprParent getParent() { py_exprs(this, _, result, _) } + ExprParent getParent() { py_exprs(this, _, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "Expr" } + /** Gets a textual representation of this element. */ + string toString() { result = "Expr" } } /** INTERNAL: See the class `ExprContext` for further information. */ library class ExprContext_ extends @py_expr_context { - ExprContextParent getParent() { py_expr_contexts(this, _, result) } + ExprContextParent getParent() { py_expr_contexts(this, _, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "ExprContext" } + /** Gets a textual representation of this element. */ + string toString() { result = "ExprContext" } } /** INTERNAL: See the class `ExprContextParent` for further information. */ library class ExprContextParent_ extends @py_expr_context_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "ExprContextParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "ExprContextParent" } } /** INTERNAL: See the class `ExprList` for further information. */ library class ExprList_ extends @py_expr_list { - ExprListParent getParent() { py_expr_lists(this, result, _) } + ExprListParent getParent() { py_expr_lists(this, result, _) } - /** Gets an item of this expression list */ - Expr getAnItem() { py_exprs(result, _, this, _) } + /** Gets an item of this expression list */ + Expr getAnItem() { py_exprs(result, _, this, _) } - /** Gets the nth item of this expression list */ - Expr getItem(int index) { py_exprs(result, _, this, index) } + /** Gets the nth item of this expression list */ + Expr getItem(int index) { py_exprs(result, _, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "ExprList" } + /** Gets a textual representation of this element. */ + string toString() { result = "ExprList" } } /** INTERNAL: See the class `ExprListParent` for further information. */ library class ExprListParent_ extends @py_expr_list_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "ExprListParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "ExprListParent" } } /** INTERNAL: See the class `ExprOrStmt` for further information. */ library class ExprOrStmt_ extends @py_expr_or_stmt { - /** Gets a textual representation of this element. */ - string toString() { result = "ExprOrStmt" } + /** Gets a textual representation of this element. */ + string toString() { result = "ExprOrStmt" } } /** INTERNAL: See the class `ExprParent` for further information. */ library class ExprParent_ extends @py_expr_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "ExprParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "ExprParent" } } /** INTERNAL: See the class `Keyword` for further information. */ library class Keyword_ extends @py_keyword, DictItem { - /** Gets the location of this keyword argument. */ - override Location getLocation() { py_locations(result, this) } + /** Gets the location of this keyword argument. */ + override Location getLocation() { py_locations(result, this) } - /** Gets the value of this keyword argument. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this keyword argument. */ + Expr getValue() { py_exprs(result, _, this, 1) } - /** Gets the arg of this keyword argument. */ - string getArg() { py_strs(result, this, 2) } + /** Gets the arg of this keyword argument. */ + string getArg() { py_strs(result, this, 2) } - override string toString() { result = "Keyword" } + override string toString() { result = "Keyword" } } /** INTERNAL: See the class `LocationParent` for further information. */ library class LocationParent_ extends @py_location_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "LocationParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "LocationParent" } } /** INTERNAL: See the class `Operator` for further information. */ library class Operator_ extends @py_operator { - BinaryExpr getParent() { py_operators(this, _, result) } + BinaryExpr getParent() { py_operators(this, _, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Operator" } + /** Gets a textual representation of this element. */ + string toString() { result = "Operator" } } /** INTERNAL: See the class `Parameter` for further information. */ library class Parameter_ extends @py_parameter { - /** Gets a textual representation of this element. */ - string toString() { result = "Parameter" } + /** Gets a textual representation of this element. */ + string toString() { result = "Parameter" } } /** INTERNAL: See the class `Scope` for further information. */ library class Scope_ extends @py_scope { - /** Gets a textual representation of this element. */ - string toString() { result = "Scope" } + /** Gets a textual representation of this element. */ + string toString() { result = "Scope" } } /** INTERNAL: See the class `Stmt` for further information. */ library class Stmt_ extends @py_stmt { - /** Gets the location of this statement. */ - Location getLocation() { py_locations(result, this) } + /** Gets the location of this statement. */ + Location getLocation() { py_locations(result, this) } - StmtList getParent() { py_stmts(this, _, result, _) } + StmtList getParent() { py_stmts(this, _, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "Stmt" } + /** Gets a textual representation of this element. */ + string toString() { result = "Stmt" } } /** INTERNAL: See the class `StmtList` for further information. */ library class StmtList_ extends @py_stmt_list { - StmtListParent getParent() { py_stmt_lists(this, result, _) } + StmtListParent getParent() { py_stmt_lists(this, result, _) } - /** Gets an item of this statement list */ - Stmt getAnItem() { py_stmts(result, _, this, _) } + /** Gets an item of this statement list */ + Stmt getAnItem() { py_stmts(result, _, this, _) } - /** Gets the nth item of this statement list */ - Stmt getItem(int index) { py_stmts(result, _, this, index) } + /** Gets the nth item of this statement list */ + Stmt getItem(int index) { py_stmts(result, _, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "StmtList" } + /** Gets a textual representation of this element. */ + string toString() { result = "StmtList" } } /** INTERNAL: See the class `StmtListParent` for further information. */ library class StmtListParent_ extends @py_stmt_list_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "StmtListParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "StmtListParent" } } /** INTERNAL: See the class `StringList` for further information. */ library class StringList_ extends @py_str_list { - StrListParent getParent() { py_str_lists(this, result) } + StrListParent getParent() { py_str_lists(this, result) } - /** Gets an item of this string list */ - string getAnItem() { py_strs(result, this, _) } + /** Gets an item of this string list */ + string getAnItem() { py_strs(result, this, _) } - /** Gets the nth item of this string list */ - string getItem(int index) { py_strs(result, this, index) } + /** Gets the nth item of this string list */ + string getItem(int index) { py_strs(result, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "StringList" } + /** Gets a textual representation of this element. */ + string toString() { result = "StringList" } } /** INTERNAL: See the class `StrListParent` for further information. */ library class StrListParent_ extends @py_str_list_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "StrListParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "StrListParent" } } /** INTERNAL: See the class `StrParent` for further information. */ library class StrParent_ extends @py_str_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "StrParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "StrParent" } } /** INTERNAL: See the class `Unaryop` for further information. */ library class Unaryop_ extends @py_unaryop { - UnaryExpr getParent() { py_unaryops(this, _, result) } + UnaryExpr getParent() { py_unaryops(this, _, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Unaryop" } + /** Gets a textual representation of this element. */ + string toString() { result = "Unaryop" } } /** INTERNAL: See the class `VariableParent` for further information. */ library class VariableParent_ extends @py_variable_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "VariableParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "VariableParent" } } diff --git a/python/ql/src/semmle/python/Class.qll b/python/ql/src/semmle/python/Class.qll index 3c7c74fa194..a2a4c88a53e 100644 --- a/python/ql/src/semmle/python/Class.qll +++ b/python/ql/src/semmle/python/Class.qll @@ -9,167 +9,167 @@ import python * It is recommended to use `ClassDef` instead. */ class ClassExpr extends ClassExpr_ { - /** Gets the metaclass expression */ - Expr getMetaClass() { - if major_version() = 3 - then - exists(Keyword metacls | - this.getAKeyword() = metacls and - metacls.getArg() = "metaclass" and - result = metacls.getValue() - ) - else - exists(Assign a | - a = this.getInnerScope().getAStmt() and - a.getATarget().(Name).getId() = "__metaclass__" and - result = a.getValue() - ) - } + /** Gets the metaclass expression */ + Expr getMetaClass() { + if major_version() = 3 + then + exists(Keyword metacls | + this.getAKeyword() = metacls and + metacls.getArg() = "metaclass" and + result = metacls.getValue() + ) + else + exists(Assign a | + a = this.getInnerScope().getAStmt() and + a.getATarget().(Name).getId() = "__metaclass__" and + result = a.getValue() + ) + } - /** Gets the nth keyword argument of this class definition. */ - override DictUnpackingOrKeyword getKeyword(int index) { - result = this.getKeywords().getItem(index) - } + /** Gets the nth keyword argument of this class definition. */ + override DictUnpackingOrKeyword getKeyword(int index) { + result = this.getKeywords().getItem(index) + } - /** Gets a keyword argument of this class definition. */ - override DictUnpackingOrKeyword getAKeyword() { result = this.getKeywords().getAnItem() } + /** Gets a keyword argument of this class definition. */ + override DictUnpackingOrKeyword getAKeyword() { result = this.getKeywords().getAnItem() } - override Expr getASubExpression() { - result = this.getABase() or - result = this.getAKeyword().getValue() or - result = this.getKwargs() or - result = this.getStarargs() - } + override Expr getASubExpression() { + result = this.getABase() or + result = this.getAKeyword().getValue() or + result = this.getKwargs() or + result = this.getStarargs() + } - /** Gets a call corresponding to a decorator of this class definition. */ - Call getADecoratorCall() { - result.getArg(0) = this or - result.getArg(0) = this.getADecoratorCall() - } + /** Gets a call corresponding to a decorator of this class definition. */ + Call getADecoratorCall() { + result.getArg(0) = this or + result.getArg(0) = this.getADecoratorCall() + } - /** Gets a decorator of this function expression */ - Expr getADecorator() { result = this.getADecoratorCall().getFunc() } + /** Gets a decorator of this function expression */ + Expr getADecorator() { result = this.getADecoratorCall().getFunc() } - override AstNode getAChildNode() { - result = this.getASubExpression() - or - result = this.getInnerScope() - } + override AstNode getAChildNode() { + result = this.getASubExpression() + or + result = this.getInnerScope() + } - /** Gets a tuple (*) argument of this class definition. */ - Expr getStarargs() { result = this.getABase().(Starred).getValue() } + /** Gets a tuple (*) argument of this class definition. */ + Expr getStarargs() { result = this.getABase().(Starred).getValue() } - /** Gets a dictionary (**) argument of this class definition. */ - Expr getKwargs() { result = this.getAKeyword().(DictUnpacking).getValue() } + /** Gets a dictionary (**) argument of this class definition. */ + Expr getKwargs() { result = this.getAKeyword().(DictUnpacking).getValue() } } /** A class statement. Note that ClassDef extends Assign as a class definition binds the newly created class */ class ClassDef extends Assign { - /* syntax: class name(...): ... */ - ClassDef() { - /* This is an artificial assignment the rhs of which is a (possibly decorated) ClassExpr */ - exists(ClassExpr c | this.getValue() = c or this.getValue() = c.getADecoratorCall()) - } + /* syntax: class name(...): ... */ + ClassDef() { + /* This is an artificial assignment the rhs of which is a (possibly decorated) ClassExpr */ + exists(ClassExpr c | this.getValue() = c or this.getValue() = c.getADecoratorCall()) + } - override string toString() { result = "ClassDef" } + override string toString() { result = "ClassDef" } - /** Gets the class for this statement */ - Class getDefinedClass() { - exists(ClassExpr c | this.getValue() = c or this.getValue() = c.getADecoratorCall() | - result = c.getInnerScope() - ) - } + /** Gets the class for this statement */ + Class getDefinedClass() { + exists(ClassExpr c | this.getValue() = c or this.getValue() = c.getADecoratorCall() | + result = c.getInnerScope() + ) + } - override Stmt getLastStatement() { result = this.getDefinedClass().getLastStatement() } + override Stmt getLastStatement() { result = this.getDefinedClass().getLastStatement() } } /** The scope of a class. This is the scope of all the statements within the class definition */ class Class extends Class_, Scope, AstNode { - /** - * Use getADecorator() instead of getDefinition().getADecorator() - * Use getMetaClass() instead of getDefinition().getMetaClass() - */ - deprecated ClassExpr getDefinition() { result = this.getParent() } + /** + * Use getADecorator() instead of getDefinition().getADecorator() + * Use getMetaClass() instead of getDefinition().getMetaClass() + */ + deprecated ClassExpr getDefinition() { result = this.getParent() } - /** Gets a defined init method of this class */ - Function getInitMethod() { result.getScope() = this and result.isInitMethod() } + /** Gets a defined init method of this class */ + Function getInitMethod() { result.getScope() = this and result.isInitMethod() } - /** Gets a method defined in this class */ - Function getAMethod() { result.getScope() = this } + /** Gets a method defined in this class */ + Function getAMethod() { result.getScope() = this } - override Location getLocation() { py_scope_location(result, this) } + override Location getLocation() { py_scope_location(result, this) } - /** Gets the scope (module, class or function) in which this class is defined */ - override Scope getEnclosingScope() { result = this.getParent().getScope() } + /** Gets the scope (module, class or function) in which this class is defined */ + override Scope getEnclosingScope() { result = this.getParent().getScope() } - /** Use getEnclosingScope() instead */ - override Scope getScope() { result = this.getParent().getScope() } + /** Use getEnclosingScope() instead */ + override Scope getScope() { result = this.getParent().getScope() } - override string toString() { result = "Class " + this.getName() } + override string toString() { result = "Class " + this.getName() } - /** Gets the statements forming the body of this class */ - override StmtList getBody() { result = Class_.super.getBody() } + /** Gets the statements forming the body of this class */ + override StmtList getBody() { result = Class_.super.getBody() } - /** Gets the nth statement in the class */ - override Stmt getStmt(int index) { result = Class_.super.getStmt(index) } + /** Gets the nth statement in the class */ + override Stmt getStmt(int index) { result = Class_.super.getStmt(index) } - /** Gets a statement in the class */ - override Stmt getAStmt() { result = Class_.super.getAStmt() } + /** Gets a statement in the class */ + override Stmt getAStmt() { result = Class_.super.getAStmt() } - /** Gets the name used to define this class */ - override string getName() { result = Class_.super.getName() } + /** Gets the name used to define this class */ + override string getName() { result = Class_.super.getName() } - /** Holds if this expression may have a side effect (as determined purely from its syntax). */ - predicate hasSideEffects() { any() } + /** Holds if this expression may have a side effect (as determined purely from its syntax). */ + predicate hasSideEffects() { any() } - /** Holds if this is probably a mixin (has 'mixin' or similar in name or docstring) */ - predicate isProbableMixin() { - ( - this.getName().toLowerCase().matches("%mixin%") - or - this.getDocString().getText().toLowerCase().matches("%mixin%") - or - this.getDocString().getText().toLowerCase().matches("%mix-in%") - ) - } + /** Holds if this is probably a mixin (has 'mixin' or similar in name or docstring) */ + predicate isProbableMixin() { + ( + this.getName().toLowerCase().matches("%mixin%") + or + this.getDocString().getText().toLowerCase().matches("%mixin%") + or + this.getDocString().getText().toLowerCase().matches("%mix-in%") + ) + } - override AstNode getAChildNode() { result = this.getAStmt() } + override AstNode getAChildNode() { result = this.getAStmt() } - /** Gets a decorator of this class. */ - Expr getADecorator() { result = this.getParent().getADecorator() } + /** Gets a decorator of this class. */ + Expr getADecorator() { result = this.getParent().getADecorator() } - /** Gets the metaclass expression */ - Expr getMetaClass() { result = this.getParent().getMetaClass() } + /** Gets the metaclass expression */ + Expr getMetaClass() { result = this.getParent().getMetaClass() } - /** Gets the ClassObject corresponding to this class */ - ClassObject getClassObject() { result.getOrigin() = this.getParent() } + /** Gets the ClassObject corresponding to this class */ + ClassObject getClassObject() { result.getOrigin() = this.getParent() } - /** Gets the nth base of this class definition. */ - Expr getBase(int index) { result = this.getParent().getBase(index) } + /** Gets the nth base of this class definition. */ + Expr getBase(int index) { result = this.getParent().getBase(index) } - /** Gets a base of this class definition. */ - Expr getABase() { result = this.getParent().getABase() } + /** Gets a base of this class definition. */ + Expr getABase() { result = this.getParent().getABase() } - /** Gets the metrics for this class */ - ClassMetrics getMetrics() { result = this } + /** Gets the metrics for this class */ + ClassMetrics getMetrics() { result = this } - /** - * Gets the qualified name for this class. - * Should return the same name as the `__qualname__` attribute on classes in Python 3. - */ - string getQualifiedName() { - this.getScope() instanceof Module and result = this.getName() - or - exists(string enclosing_name | - enclosing_name = this.getScope().(Function).getQualifiedName() - or - enclosing_name = this.getScope().(Class).getQualifiedName() - | - result = enclosing_name + "." + this.getName() - ) - } + /** + * Gets the qualified name for this class. + * Should return the same name as the `__qualname__` attribute on classes in Python 3. + */ + string getQualifiedName() { + this.getScope() instanceof Module and result = this.getName() + or + exists(string enclosing_name | + enclosing_name = this.getScope().(Function).getQualifiedName() + or + enclosing_name = this.getScope().(Class).getQualifiedName() + | + result = enclosing_name + "." + this.getName() + ) + } - override predicate containsInScope(AstNode inner) { Scope.super.containsInScope(inner) } + override predicate containsInScope(AstNode inner) { Scope.super.containsInScope(inner) } - override predicate contains(AstNode inner) { Scope.super.contains(inner) } + override predicate contains(AstNode inner) { Scope.super.contains(inner) } } diff --git a/python/ql/src/semmle/python/Comment.qll b/python/ql/src/semmle/python/Comment.qll index ce90b631308..94dd429e404 100644 --- a/python/ql/src/semmle/python/Comment.qll +++ b/python/ql/src/semmle/python/Comment.qll @@ -6,96 +6,99 @@ import python /** A source code comment */ class Comment extends @py_comment { - /** Gets the full text of the comment including the leading '#' */ - string getText() { py_comments(this, result, _) } + /** Gets the full text of the comment including the leading '#' */ + string getText() { py_comments(this, result, _) } - /** Gets the contents of the comment excluding the leading '#' */ - string getContents() { result = this.getText().suffix(1) } + /** Gets the contents of the comment excluding the leading '#' */ + string getContents() { result = this.getText().suffix(1) } - Location getLocation() { py_comments(this, _, result) } + Location getLocation() { py_comments(this, _, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Comment " + this.getText() } + /** Gets a textual representation of this element. */ + string toString() { result = "Comment " + this.getText() } - /** - * Gets this immediately following comment. - * Blanks line are allowed between this comment and the following comment, - * but code or other comments are not. - */ - Comment getFollowing() { - exists(File f, int n | this.file_line(f, n) | - result.file_line(f, n + 1) - or - result.file_line(f, n + 2) and f.emptyLine(n + 1) - or - result.file_line(f, n + 3) and f.emptyLine(n + 2) and f.emptyLine(n + 1) - ) - } + /** + * Gets this immediately following comment. + * Blanks line are allowed between this comment and the following comment, + * but code or other comments are not. + */ + Comment getFollowing() { + exists(File f, int n | this.file_line(f, n) | + result.file_line(f, n + 1) + or + result.file_line(f, n + 2) and f.emptyLine(n + 1) + or + result.file_line(f, n + 3) and f.emptyLine(n + 2) and f.emptyLine(n + 1) + ) + } - private predicate file_line(File f, int n) { - this.getLocation().getFile() = f and - this.getLocation().getStartLine() = n - } + private predicate file_line(File f, int n) { + this.getLocation().getFile() = f and + this.getLocation().getStartLine() = n + } } private predicate comment_block_part(Comment start, Comment part, int i) { - not exists(Comment prev | prev.getFollowing() = part) and - exists(Comment following | part.getFollowing() = following) and - start = part and - i = 1 - or - exists(Comment prev | - comment_block_part(start, prev, i - 1) and - part = prev.getFollowing() - ) + not exists(Comment prev | prev.getFollowing() = part) and + exists(Comment following | part.getFollowing() = following) and + start = part and + i = 1 + or + exists(Comment prev | + comment_block_part(start, prev, i - 1) and + part = prev.getFollowing() + ) } /** A block of consecutive comments */ class CommentBlock extends @py_comment { - CommentBlock() { comment_block_part(this, _, _) } + CommentBlock() { comment_block_part(this, _, _) } - private Comment last() { comment_block_part(this, result, this.length()) } + private Comment last() { comment_block_part(this, result, this.length()) } - /** Gets a textual representation of this element. */ - string toString() { result = "Comment block" } + /** Gets a textual representation of this element. */ + string toString() { result = "Comment block" } - /** The length of this comment block (in comments) */ - int length() { result = max(int i | comment_block_part(this, _, i)) } + /** The length of this comment block (in comments) */ + int length() { result = max(int i | comment_block_part(this, _, i)) } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { this.(Comment).getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and - exists(Comment end | end = this.last() | end.getLocation().hasLocationInfo(_, _, _, endline, endcolumn)) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.(Comment).getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and + exists(Comment end | end = this.last() | + end.getLocation().hasLocationInfo(_, _, _, endline, endcolumn) + ) + } - /** Holds if this comment block contains `c`. */ - predicate contains(Comment c) { - comment_block_part(this, c, _) + /** Holds if this comment block contains `c`. */ + predicate contains(Comment c) { + comment_block_part(this, c, _) + or + this = c + } + + /** Gets a string representation of this comment block. */ + string getContents() { + result = + concat(Comment c, int i | + comment_block_part(this, c, i) or - this = c - } - - /** Gets a string representation of this comment block. */ - string getContents() { - result = - concat(Comment c, int i | - comment_block_part(this, c, i) - or - this = c and i = 0 - | - c.getContents() order by i - ) - } + this = c and i = 0 + | + c.getContents() order by i + ) + } } /** A type-hint comment. Any comment that starts with `# type:` */ class TypeHintComment extends Comment { - TypeHintComment() { this.getText().regexpMatch("# +type:.*") } + TypeHintComment() { this.getText().regexpMatch("# +type:.*") } } diff --git a/python/ql/src/semmle/python/Comparisons.qll b/python/ql/src/semmle/python/Comparisons.qll index dd0a1773791..a82d7a8a9a2 100644 --- a/python/ql/src/semmle/python/Comparisons.qll +++ b/python/ql/src/semmle/python/Comparisons.qll @@ -6,74 +6,74 @@ import python /** A class representing the six comparison operators, ==, !=, <, <=, > and >=. */ class CompareOp extends int { - CompareOp() { this in [1 .. 6] } + CompareOp() { this in [1 .. 6] } - /** Gets the logical inverse operator */ - CompareOp invert() { - this = eq() and result = ne() - or - this = ne() and result = eq() - or - this = lt() and result = ge() - or - this = gt() and result = le() - or - this = le() and result = gt() - or - this = ge() and result = lt() - } + /** Gets the logical inverse operator */ + CompareOp invert() { + this = eq() and result = ne() + or + this = ne() and result = eq() + or + this = lt() and result = ge() + or + this = gt() and result = le() + or + this = le() and result = gt() + or + this = ge() and result = lt() + } - /** Gets the reverse operator (swapping the operands) */ - CompareOp reverse() { - this = eq() and result = eq() - or - this = ne() and result = ne() - or - this = lt() and result = gt() - or - this = gt() and result = lt() - or - this = le() and result = ge() - or - this = ge() and result = le() - } + /** Gets the reverse operator (swapping the operands) */ + CompareOp reverse() { + this = eq() and result = eq() + or + this = ne() and result = ne() + or + this = lt() and result = gt() + or + this = gt() and result = lt() + or + this = le() and result = ge() + or + this = ge() and result = le() + } - /** Gets the textual representation of `this`. */ - string repr() { - this = eq() and result = "==" - or - this = ne() and result = "!=" - or - this = lt() and result = "<" - or - this = gt() and result = ">" - or - this = le() and result = "<=" - or - this = ge() and result = ">=" - } + /** Gets the textual representation of `this`. */ + string repr() { + this = eq() and result = "==" + or + this = ne() and result = "!=" + or + this = lt() and result = "<" + or + this = gt() and result = ">" + or + this = le() and result = "<=" + or + this = ge() and result = ">=" + } - /** Holds if `op` is the `Cmpop` corresponding to `this`. */ - predicate forOp(Cmpop op) { - op instanceof Eq and this = eq() - or - op instanceof NotEq and this = ne() - or - op instanceof Lt and this = lt() - or - op instanceof LtE and this = le() - or - op instanceof Gt and this = gt() - or - op instanceof GtE and this = ge() - } + /** Holds if `op` is the `Cmpop` corresponding to `this`. */ + predicate forOp(Cmpop op) { + op instanceof Eq and this = eq() + or + op instanceof NotEq and this = ne() + or + op instanceof Lt and this = lt() + or + op instanceof LtE and this = le() + or + op instanceof Gt and this = gt() + or + op instanceof GtE and this = ge() + } - /** Return this if isTrue is true, otherwise returns the inverse */ - CompareOp conditional(boolean isTrue) { - result = this and isTrue = true - or - result = this.invert() and isTrue = false - } + /** Return this if isTrue is true, otherwise returns the inverse */ + CompareOp conditional(boolean isTrue) { + result = this and isTrue = true + or + result = this.invert() and isTrue = false + } } /** The `CompareOp` for "equals". */ @@ -97,74 +97,74 @@ CompareOp ge() { result = 6 } /* Workaround precision limits in floating point numbers */ bindingset[x] private predicate ok_magnitude(float x) { - x > -9007199254740992.0 and // -2**53 - x < 9007199254740992.0 // 2**53 + x > -9007199254740992.0 and // -2**53 + x < 9007199254740992.0 // 2**53 } bindingset[x, y] private float add(float x, float y) { - ok_magnitude(x) and - ok_magnitude(y) and - ok_magnitude(result) and - result = x + y + ok_magnitude(x) and + ok_magnitude(y) and + ok_magnitude(result) and + result = x + y } bindingset[x, y] private float sub(float x, float y) { - ok_magnitude(x) and - ok_magnitude(y) and - ok_magnitude(result) and - result = x - y + ok_magnitude(x) and + ok_magnitude(y) and + ok_magnitude(result) and + result = x - y } /** Normalise equality cmp into the form `left op right + k`. */ private predicate test( - ControlFlowNode cmp, ControlFlowNode left, CompareOp op, ControlFlowNode right, float k + ControlFlowNode cmp, ControlFlowNode left, CompareOp op, ControlFlowNode right, float k ) { - simple_test(cmp, left, op, right) and k = 0 - or - add_test(cmp, left, op, right, k) - or - not_test(cmp, left, op, right, k) - or - subtract_test(cmp, left, op, right, k) - or - exists(float c | test(cmp, right, op.reverse(), left, c) and k = -c) + simple_test(cmp, left, op, right) and k = 0 + or + add_test(cmp, left, op, right, k) + or + not_test(cmp, left, op, right, k) + or + subtract_test(cmp, left, op, right, k) + or + exists(float c | test(cmp, right, op.reverse(), left, c) and k = -c) } /** Various simple tests in left op right + k form. */ private predicate simple_test(CompareNode cmp, ControlFlowNode l, CompareOp cmpop, ControlFlowNode r) { - exists(Cmpop op | cmp.operands(l, op, r) and cmpop.forOp(op)) + exists(Cmpop op | cmp.operands(l, op, r) and cmpop.forOp(op)) } private predicate add_test_left( - CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k + CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k ) { - exists(BinaryExprNode lhs, float c, float x, Num n | - lhs.getNode().getOp() instanceof Add and - test(cmp, lhs, op, r, c) and - x = n.getN().toFloat() and - k = sub(c, x) - | - l = lhs.getLeft() and n = lhs.getRight().getNode() - or - l = lhs.getRight() and n = lhs.getLeft().getNode() - ) + exists(BinaryExprNode lhs, float c, float x, Num n | + lhs.getNode().getOp() instanceof Add and + test(cmp, lhs, op, r, c) and + x = n.getN().toFloat() and + k = sub(c, x) + | + l = lhs.getLeft() and n = lhs.getRight().getNode() + or + l = lhs.getRight() and n = lhs.getLeft().getNode() + ) } private predicate add_test_right( - CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k + CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k ) { - exists(BinaryExprNode rhs, float c, float x, Num n | - rhs.getNode().getOp() instanceof Add and - test(cmp, l, op, rhs, c) and - x = n.getN().toFloat() and - k = add(c, x) - | - r = rhs.getLeft() and n = rhs.getRight().getNode() - or - r = rhs.getRight() and n = rhs.getLeft().getNode() - ) + exists(BinaryExprNode rhs, float c, float x, Num n | + rhs.getNode().getOp() instanceof Add and + test(cmp, l, op, rhs, c) and + x = n.getN().toFloat() and + k = add(c, x) + | + r = rhs.getLeft() and n = rhs.getRight().getNode() + or + r = rhs.getRight() and n = rhs.getLeft().getNode() + ) } /* @@ -173,39 +173,39 @@ private predicate add_test_right( */ private predicate add_test( - CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k + CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k ) { - add_test_left(cmp, l, op, r, k) - or - add_test_right(cmp, l, op, r, k) + add_test_left(cmp, l, op, r, k) + or + add_test_right(cmp, l, op, r, k) } private predicate subtract_test_left( - CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k + CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k ) { - exists(BinaryExprNode lhs, float c, float x, Num n | - lhs.getNode().getOp() instanceof Sub and - test(cmp, lhs, op, r, c) and - l = lhs.getLeft() and - n = lhs.getRight().getNode() and - x = n.getN().toFloat() - | - k = add(c, x) - ) + exists(BinaryExprNode lhs, float c, float x, Num n | + lhs.getNode().getOp() instanceof Sub and + test(cmp, lhs, op, r, c) and + l = lhs.getLeft() and + n = lhs.getRight().getNode() and + x = n.getN().toFloat() + | + k = add(c, x) + ) } private predicate subtract_test_right( - CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k + CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k ) { - exists(BinaryExprNode rhs, float c, float x, Num n | - rhs.getNode().getOp() instanceof Sub and - test(cmp, l, op, rhs, c) and - r = rhs.getRight() and - n = rhs.getLeft().getNode() and - x = n.getN().toFloat() - | - k = sub(c, x) - ) + exists(BinaryExprNode rhs, float c, float x, Num n | + rhs.getNode().getOp() instanceof Sub and + test(cmp, l, op, rhs, c) and + r = rhs.getRight() and + n = rhs.getLeft().getNode() and + x = n.getN().toFloat() + | + k = sub(c, x) + ) } /* @@ -214,18 +214,18 @@ private predicate subtract_test_right( */ private predicate subtract_test( - CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k + CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k ) { - subtract_test_left(cmp, l, op, r, k) - or - subtract_test_right(cmp, l, op, r, k) + subtract_test_left(cmp, l, op, r, k) + or + subtract_test_right(cmp, l, op, r, k) } private predicate not_test( - UnaryExprNode u, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k + UnaryExprNode u, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k ) { - u.getNode().getOp() instanceof Not and - test(u.getOperand(), l, op.invert(), r, k) + u.getNode().getOp() instanceof Not and + test(u.getOperand(), l, op.invert(), r, k) } /** @@ -233,243 +233,243 @@ private predicate not_test( * `k` is a floating point constant and `OP` is one of `<=`, `>`, `==` or `!=`. */ class Comparison extends ControlFlowNode { - Comparison() { test(this, _, _, _, _) } + Comparison() { test(this, _, _, _, _) } - /** Whether this condition tests `l op r + k` */ - predicate tests(ControlFlowNode l, CompareOp op, ControlFlowNode r, float k) { - test(this, l, op, r, k) - } + /** Whether this condition tests `l op r + k` */ + predicate tests(ControlFlowNode l, CompareOp op, ControlFlowNode r, float k) { + test(this, l, op, r, k) + } - /** Whether this condition tests `l op k` */ - predicate tests(ControlFlowNode l, CompareOp op, float k) { - exists(ControlFlowNode r, float x, float c | test(this, l, op, r, c) | - x = r.getNode().(Num).getN().toFloat() and - k = add(c, x) - ) - } + /** Whether this condition tests `l op k` */ + predicate tests(ControlFlowNode l, CompareOp op, float k) { + exists(ControlFlowNode r, float x, float c | test(this, l, op, r, c) | + x = r.getNode().(Num).getN().toFloat() and + k = add(c, x) + ) + } - /* - * The following predicates determine whether this test, when its result is `thisIsTrue`, - * is equivalent to the predicate `v OP k` or `v1 OP v2 + k`. - * For example, the test `x <= y` being false, is equivalent to the predicate `x > y`. - */ + /* + * The following predicates determine whether this test, when its result is `thisIsTrue`, + * is equivalent to the predicate `v OP k` or `v1 OP v2 + k`. + * For example, the test `x <= y` being false, is equivalent to the predicate `x > y`. + */ - private predicate equivalentToEq(boolean thisIsTrue, SsaVariable v, float k) { - this.tests(v.getAUse(), eq().conditional(thisIsTrue), k) - } + private predicate equivalentToEq(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), eq().conditional(thisIsTrue), k) + } - private predicate equivalentToNotEq(boolean thisIsTrue, SsaVariable v, float k) { - this.tests(v.getAUse(), ne().conditional(thisIsTrue), k) - } + private predicate equivalentToNotEq(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), ne().conditional(thisIsTrue), k) + } - private predicate equivalentToLt(boolean thisIsTrue, SsaVariable v, float k) { - this.tests(v.getAUse(), lt().conditional(thisIsTrue), k) - } + private predicate equivalentToLt(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), lt().conditional(thisIsTrue), k) + } - private predicate equivalentToLtEq(boolean thisIsTrue, SsaVariable v, float k) { - this.tests(v.getAUse(), le().conditional(thisIsTrue), k) - } + private predicate equivalentToLtEq(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), le().conditional(thisIsTrue), k) + } - private predicate equivalentToGt(boolean thisIsTrue, SsaVariable v, float k) { - this.tests(v.getAUse(), gt().conditional(thisIsTrue), k) - } + private predicate equivalentToGt(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), gt().conditional(thisIsTrue), k) + } - private predicate equivalentToGtEq(boolean thisIsTrue, SsaVariable v, float k) { - this.tests(v.getAUse(), ge().conditional(thisIsTrue), k) - } + private predicate equivalentToGtEq(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), ge().conditional(thisIsTrue), k) + } - private predicate equivalentToEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { - this.tests(v1.getAUse(), eq().conditional(thisIsTrue), v2.getAUse(), k) - } + private predicate equivalentToEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), eq().conditional(thisIsTrue), v2.getAUse(), k) + } - private predicate equivalentToNotEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { - this.tests(v1.getAUse(), ne().conditional(thisIsTrue), v2.getAUse(), k) - } + private predicate equivalentToNotEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), ne().conditional(thisIsTrue), v2.getAUse(), k) + } - private predicate equivalentToLt(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { - this.tests(v1.getAUse(), lt().conditional(thisIsTrue), v2.getAUse(), k) - } + private predicate equivalentToLt(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), lt().conditional(thisIsTrue), v2.getAUse(), k) + } - private predicate equivalentToLtEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { - this.tests(v1.getAUse(), le().conditional(thisIsTrue), v2.getAUse(), k) - } + private predicate equivalentToLtEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), le().conditional(thisIsTrue), v2.getAUse(), k) + } - private predicate equivalentToGt(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { - this.tests(v1.getAUse(), gt().conditional(thisIsTrue), v2.getAUse(), k) - } + private predicate equivalentToGt(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), gt().conditional(thisIsTrue), v2.getAUse(), k) + } - private predicate equivalentToGtEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { - this.tests(v1.getAUse(), ge().conditional(thisIsTrue), v2.getAUse(), k) - } + private predicate equivalentToGtEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), ge().conditional(thisIsTrue), v2.getAUse(), k) + } - /** - * Whether the result of this comparison being `thisIsTrue` implies that the result of `that` is `isThatTrue`. - * In other words, does the predicate that is equivalent to the result of `this` being `thisIsTrue` - * imply the predicate that is equivalent to the result of `that` being `thatIsTrue`. - * For example, assume that there are two tests, which when normalised have the form `x < y` and `x > y + 1`. - * Then the test `x < y` having a true result, implies that the test `x > y + 1` will have a false result. - * (`x < y` having a false result implies nothing about `x > y + 1`) - */ - predicate impliesThat(boolean thisIsTrue, Comparison that, boolean thatIsTrue) { - /* `v == k` => `v == k` */ - exists(SsaVariable v, float k1, float k2 | - this.equivalentToEq(thisIsTrue, v, k1) and - that.equivalentToEq(thatIsTrue, v, k2) and - eq(k1, k2) - or - this.equivalentToNotEq(thisIsTrue, v, k1) and - that.equivalentToNotEq(thatIsTrue, v, k2) and - eq(k1, k2) - ) - or - exists(SsaVariable v, float k1, float k2 | - /* `v < k1` => `v != k2` iff k1 <= k2 */ - this.equivalentToLt(thisIsTrue, v, k1) and - that.equivalentToNotEq(thatIsTrue, v, k2) and - le(k1, k2) - or - /* `v <= k1` => `v != k2` iff k1 < k2 */ - this.equivalentToLtEq(thisIsTrue, v, k1) and - that.equivalentToNotEq(thatIsTrue, v, k2) and - lt(k1, k2) - or - /* `v > k1` => `v != k2` iff k1 >= k2 */ - this.equivalentToGt(thisIsTrue, v, k1) and - that.equivalentToNotEq(thatIsTrue, v, k2) and - ge(k1, k2) - or - /* `v >= k1` => `v != k2` iff k1 > k2 */ - this.equivalentToGtEq(thisIsTrue, v, k1) and - that.equivalentToNotEq(thatIsTrue, v, k2) and - gt(k1, k2) - ) - or - exists(SsaVariable v, float k1, float k2 | - /* `v < k1` => `v < k2` iff k1 <= k2 */ - this.equivalentToLt(thisIsTrue, v, k1) and - that.equivalentToLt(thatIsTrue, v, k2) and - le(k1, k2) - or - /* `v < k1` => `v <= k2` iff k1 <= k2 */ - this.equivalentToLt(thisIsTrue, v, k1) and - that.equivalentToLtEq(thatIsTrue, v, k2) and - le(k1, k2) - or - /* `v <= k1` => `v < k2` iff k1 < k2 */ - this.equivalentToLtEq(thisIsTrue, v, k1) and - that.equivalentToLt(thatIsTrue, v, k2) and - lt(k1, k2) - or - /* `v <= k1` => `v <= k2` iff k1 <= k2 */ - this.equivalentToLtEq(thisIsTrue, v, k1) and - that.equivalentToLtEq(thatIsTrue, v, k2) and - le(k1, k2) - ) - or - exists(SsaVariable v, float k1, float k2 | - /* `v > k1` => `v >= k2` iff k1 >= k2 */ - this.equivalentToGt(thisIsTrue, v, k1) and - that.equivalentToGt(thatIsTrue, v, k2) and - ge(k1, k2) - or - /* `v > k1` => `v >= k2` iff k1 >= k2 */ - this.equivalentToGt(thisIsTrue, v, k1) and - that.equivalentToGtEq(thatIsTrue, v, k2) and - ge(k1, k2) - or - /* `v >= k1` => `v > k2` iff k1 > k2 */ - this.equivalentToGtEq(thisIsTrue, v, k1) and - that.equivalentToGt(thatIsTrue, v, k2) and - gt(k1, k2) - or - /* `v >= k1` => `v >= k2` iff k1 >= k2 */ - this.equivalentToGtEq(thisIsTrue, v, k1) and - that.equivalentToGtEq(thatIsTrue, v, k2) and - ge(k1, k2) - ) - or - exists(SsaVariable v1, SsaVariable v2, float k | - /* `v1 == v2 + k` => `v1 == v2 + k` */ - this.equivalentToEq(thisIsTrue, v1, v2, k) and - that.equivalentToEq(thatIsTrue, v1, v2, k) - or - this.equivalentToNotEq(thisIsTrue, v1, v2, k) and - that.equivalentToNotEq(thatIsTrue, v1, v2, k) - ) - or - exists(SsaVariable v1, SsaVariable v2, float k1, float k2 | - /* `v1 < v2 + k1` => `v1 != v2 + k2` iff k1 <= k2 */ - this.equivalentToLt(thisIsTrue, v1, v2, k1) and - that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and - le(k1, k2) - or - /* `v1 <= v2 + k1` => `v1 != v2 + k2` iff k1 < k2 */ - this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and - that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and - lt(k1, k2) - or - /* `v1 > v2 + k1` => `v1 != v2 + k2` iff k1 >= k2 */ - this.equivalentToGt(thisIsTrue, v1, v2, k1) and - that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and - ge(k1, k2) - or - /* `v1 >= v2 + k1` => `v1 != v2 + k2` iff k1 > k2 */ - this.equivalentToGtEq(thisIsTrue, v1, v2, k1) and - that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and - gt(k1, k2) - ) - or - exists(SsaVariable v1, SsaVariable v2, float k1, float k2 | - /* `v1 <= v2 + k1` => `v1 <= v2 + k2` iff k1 <= k2 */ - this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and - that.equivalentToLtEq(thatIsTrue, v1, v2, k2) and - le(k1, k2) - or - /* `v1 < v2 + k1` => `v1 <= v2 + k2` iff k1 <= k2 */ - this.equivalentToLt(thisIsTrue, v1, v2, k1) and - that.equivalentToLtEq(thatIsTrue, v1, v2, k2) and - le(k1, k2) - or - /* `v1 <= v2 + k1` => `v1 < v2 + k2` iff k1 < k2 */ - this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and - that.equivalentToLt(thatIsTrue, v1, v2, k2) and - lt(k1, k2) - or - /* `v1 <= v2 + k1` => `v1 <= v2 + k2` iff k1 <= k2 */ - this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and - that.equivalentToLtEq(thatIsTrue, v1, v2, k2) and - le(k1, k2) - ) - or - exists(SsaVariable v1, SsaVariable v2, float k1, float k2 | - /* `v1 > v2 + k1` => `v1 > v2 + k2` iff k1 >= k2 */ - this.equivalentToGt(thisIsTrue, v1, v2, k1) and - that.equivalentToGt(thatIsTrue, v1, v2, k2) and - ge(k1, k2) - or - /* `v1 > v2 + k1` => `v2 >= v2 + k2` iff k1 >= k2 */ - this.equivalentToGt(thisIsTrue, v1, v2, k1) and - that.equivalentToGtEq(thatIsTrue, v1, v2, k2) and - ge(k1, k2) - or - /* `v1 >= v2 + k1` => `v2 > v2 + k2` iff k1 > k2 */ - this.equivalentToGtEq(thisIsTrue, v1, v2, k1) and - that.equivalentToGt(thatIsTrue, v1, v2, k2) and - gt(k1, k2) - or - /* `v1 >= v2 + k1` => `v2 >= v2 + k2` iff k1 >= k2 */ - this.equivalentToGtEq(thisIsTrue, v1, v2, k1) and - that.equivalentToGtEq(thatIsTrue, v1, v2, k2) and - ge(k1, k2) - ) - } + /** + * Whether the result of this comparison being `thisIsTrue` implies that the result of `that` is `isThatTrue`. + * In other words, does the predicate that is equivalent to the result of `this` being `thisIsTrue` + * imply the predicate that is equivalent to the result of `that` being `thatIsTrue`. + * For example, assume that there are two tests, which when normalised have the form `x < y` and `x > y + 1`. + * Then the test `x < y` having a true result, implies that the test `x > y + 1` will have a false result. + * (`x < y` having a false result implies nothing about `x > y + 1`) + */ + predicate impliesThat(boolean thisIsTrue, Comparison that, boolean thatIsTrue) { + /* `v == k` => `v == k` */ + exists(SsaVariable v, float k1, float k2 | + this.equivalentToEq(thisIsTrue, v, k1) and + that.equivalentToEq(thatIsTrue, v, k2) and + eq(k1, k2) + or + this.equivalentToNotEq(thisIsTrue, v, k1) and + that.equivalentToNotEq(thatIsTrue, v, k2) and + eq(k1, k2) + ) + or + exists(SsaVariable v, float k1, float k2 | + /* `v < k1` => `v != k2` iff k1 <= k2 */ + this.equivalentToLt(thisIsTrue, v, k1) and + that.equivalentToNotEq(thatIsTrue, v, k2) and + le(k1, k2) + or + /* `v <= k1` => `v != k2` iff k1 < k2 */ + this.equivalentToLtEq(thisIsTrue, v, k1) and + that.equivalentToNotEq(thatIsTrue, v, k2) and + lt(k1, k2) + or + /* `v > k1` => `v != k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v, k1) and + that.equivalentToNotEq(thatIsTrue, v, k2) and + ge(k1, k2) + or + /* `v >= k1` => `v != k2` iff k1 > k2 */ + this.equivalentToGtEq(thisIsTrue, v, k1) and + that.equivalentToNotEq(thatIsTrue, v, k2) and + gt(k1, k2) + ) + or + exists(SsaVariable v, float k1, float k2 | + /* `v < k1` => `v < k2` iff k1 <= k2 */ + this.equivalentToLt(thisIsTrue, v, k1) and + that.equivalentToLt(thatIsTrue, v, k2) and + le(k1, k2) + or + /* `v < k1` => `v <= k2` iff k1 <= k2 */ + this.equivalentToLt(thisIsTrue, v, k1) and + that.equivalentToLtEq(thatIsTrue, v, k2) and + le(k1, k2) + or + /* `v <= k1` => `v < k2` iff k1 < k2 */ + this.equivalentToLtEq(thisIsTrue, v, k1) and + that.equivalentToLt(thatIsTrue, v, k2) and + lt(k1, k2) + or + /* `v <= k1` => `v <= k2` iff k1 <= k2 */ + this.equivalentToLtEq(thisIsTrue, v, k1) and + that.equivalentToLtEq(thatIsTrue, v, k2) and + le(k1, k2) + ) + or + exists(SsaVariable v, float k1, float k2 | + /* `v > k1` => `v >= k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v, k1) and + that.equivalentToGt(thatIsTrue, v, k2) and + ge(k1, k2) + or + /* `v > k1` => `v >= k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v, k1) and + that.equivalentToGtEq(thatIsTrue, v, k2) and + ge(k1, k2) + or + /* `v >= k1` => `v > k2` iff k1 > k2 */ + this.equivalentToGtEq(thisIsTrue, v, k1) and + that.equivalentToGt(thatIsTrue, v, k2) and + gt(k1, k2) + or + /* `v >= k1` => `v >= k2` iff k1 >= k2 */ + this.equivalentToGtEq(thisIsTrue, v, k1) and + that.equivalentToGtEq(thatIsTrue, v, k2) and + ge(k1, k2) + ) + or + exists(SsaVariable v1, SsaVariable v2, float k | + /* `v1 == v2 + k` => `v1 == v2 + k` */ + this.equivalentToEq(thisIsTrue, v1, v2, k) and + that.equivalentToEq(thatIsTrue, v1, v2, k) + or + this.equivalentToNotEq(thisIsTrue, v1, v2, k) and + that.equivalentToNotEq(thatIsTrue, v1, v2, k) + ) + or + exists(SsaVariable v1, SsaVariable v2, float k1, float k2 | + /* `v1 < v2 + k1` => `v1 != v2 + k2` iff k1 <= k2 */ + this.equivalentToLt(thisIsTrue, v1, v2, k1) and + that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and + le(k1, k2) + or + /* `v1 <= v2 + k1` => `v1 != v2 + k2` iff k1 < k2 */ + this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and + lt(k1, k2) + or + /* `v1 > v2 + k1` => `v1 != v2 + k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v1, v2, k1) and + that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and + ge(k1, k2) + or + /* `v1 >= v2 + k1` => `v1 != v2 + k2` iff k1 > k2 */ + this.equivalentToGtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and + gt(k1, k2) + ) + or + exists(SsaVariable v1, SsaVariable v2, float k1, float k2 | + /* `v1 <= v2 + k1` => `v1 <= v2 + k2` iff k1 <= k2 */ + this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToLtEq(thatIsTrue, v1, v2, k2) and + le(k1, k2) + or + /* `v1 < v2 + k1` => `v1 <= v2 + k2` iff k1 <= k2 */ + this.equivalentToLt(thisIsTrue, v1, v2, k1) and + that.equivalentToLtEq(thatIsTrue, v1, v2, k2) and + le(k1, k2) + or + /* `v1 <= v2 + k1` => `v1 < v2 + k2` iff k1 < k2 */ + this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToLt(thatIsTrue, v1, v2, k2) and + lt(k1, k2) + or + /* `v1 <= v2 + k1` => `v1 <= v2 + k2` iff k1 <= k2 */ + this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToLtEq(thatIsTrue, v1, v2, k2) and + le(k1, k2) + ) + or + exists(SsaVariable v1, SsaVariable v2, float k1, float k2 | + /* `v1 > v2 + k1` => `v1 > v2 + k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v1, v2, k1) and + that.equivalentToGt(thatIsTrue, v1, v2, k2) and + ge(k1, k2) + or + /* `v1 > v2 + k1` => `v2 >= v2 + k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v1, v2, k1) and + that.equivalentToGtEq(thatIsTrue, v1, v2, k2) and + ge(k1, k2) + or + /* `v1 >= v2 + k1` => `v2 > v2 + k2` iff k1 > k2 */ + this.equivalentToGtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToGt(thatIsTrue, v1, v2, k2) and + gt(k1, k2) + or + /* `v1 >= v2 + k1` => `v2 >= v2 + k2` iff k1 >= k2 */ + this.equivalentToGtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToGtEq(thatIsTrue, v1, v2, k2) and + ge(k1, k2) + ) + } } /* Work around differences in floating-point comparisons between Python and QL */ private predicate is_zero(float x) { - x = 0.0 - or - x = -0.0 + x = 0.0 + or + x = -0.0 } bindingset[x, y] @@ -492,33 +492,33 @@ private predicate ge(float x, float y) { lt(y, x) or eq(x, y) } * in which the condition is an instance of `Comparison` */ class ComparisonControlBlock extends ConditionBlock { - ComparisonControlBlock() { this.getLastNode() instanceof Comparison } + ComparisonControlBlock() { this.getLastNode() instanceof Comparison } - /** Whether this conditional guard determines that, in block `b`, `l == r + k` if `eq` is true, or `l != r + k` if `eq` is false, */ - predicate controls(ControlFlowNode l, CompareOp op, ControlFlowNode r, float k, BasicBlock b) { - exists(boolean control | - this.controls(b, control) and this.getTest().tests(l, op, r, k) and control = true - or - this.controls(b, control) and this.getTest().tests(l, op.invert(), r, k) and control = false - ) - } + /** Whether this conditional guard determines that, in block `b`, `l == r + k` if `eq` is true, or `l != r + k` if `eq` is false, */ + predicate controls(ControlFlowNode l, CompareOp op, ControlFlowNode r, float k, BasicBlock b) { + exists(boolean control | + this.controls(b, control) and this.getTest().tests(l, op, r, k) and control = true + or + this.controls(b, control) and this.getTest().tests(l, op.invert(), r, k) and control = false + ) + } - /** Whether this conditional guard determines that, in block `b`, `l == r + k` if `eq` is true, or `l != r + k` if `eq` is false, */ - predicate controls(ControlFlowNode l, CompareOp op, float k, BasicBlock b) { - exists(boolean control | - this.controls(b, control) and this.getTest().tests(l, op, k) and control = true - or - this.controls(b, control) and this.getTest().tests(l, op.invert(), k) and control = false - ) - } + /** Whether this conditional guard determines that, in block `b`, `l == r + k` if `eq` is true, or `l != r + k` if `eq` is false, */ + predicate controls(ControlFlowNode l, CompareOp op, float k, BasicBlock b) { + exists(boolean control | + this.controls(b, control) and this.getTest().tests(l, op, k) and control = true + or + this.controls(b, control) and this.getTest().tests(l, op.invert(), k) and control = false + ) + } - Comparison getTest() { this.getLastNode() = result } + Comparison getTest() { this.getLastNode() = result } - /** Whether this conditional guard implies that, in block `b`, the result of `that` is `thatIsTrue` */ - predicate impliesThat(BasicBlock b, Comparison that, boolean thatIsTrue) { - exists(boolean controlSense | - this.controls(b, controlSense) and - this.getTest().impliesThat(controlSense, that, thatIsTrue) - ) - } + /** Whether this conditional guard implies that, in block `b`, the result of `that` is `thatIsTrue` */ + predicate impliesThat(BasicBlock b, Comparison that, boolean thatIsTrue) { + exists(boolean controlSense | + this.controls(b, controlSense) and + this.getTest().impliesThat(controlSense, that, thatIsTrue) + ) + } } diff --git a/python/ql/src/semmle/python/Comprehensions.qll b/python/ql/src/semmle/python/Comprehensions.qll index d3cd82d4fd6..1e8d3cc109b 100644 --- a/python/ql/src/semmle/python/Comprehensions.qll +++ b/python/ql/src/semmle/python/Comprehensions.qll @@ -2,109 +2,109 @@ import python /** Base class for list, set and dictionary comprehensions, and generator expressions. */ abstract class Comp extends Expr { - abstract Function getFunction(); + abstract Function getFunction(); - /** Gets the iteration variable for the nth innermost generator of this list comprehension */ - Variable getIterationVariable(int n) { - result.getAnAccess() = this.getNthInnerLoop(n).getTarget() - } + /** Gets the iteration variable for the nth innermost generator of this list comprehension */ + Variable getIterationVariable(int n) { + result.getAnAccess() = this.getNthInnerLoop(n).getTarget() + } - private For getNthInnerLoop(int n) { - n = 0 and result = this.getFunction().getStmt(0) - or - result = this.getNthInnerLoop(n - 1).getStmt(0) - } + private For getNthInnerLoop(int n) { + n = 0 and result = this.getFunction().getStmt(0) + or + result = this.getNthInnerLoop(n - 1).getStmt(0) + } - /** Gets the iteration variable for a generator of this list comprehension */ - Variable getAnIterationVariable() { result = this.getIterationVariable(_) } + /** Gets the iteration variable for a generator of this list comprehension */ + Variable getAnIterationVariable() { result = this.getIterationVariable(_) } - /** Gets the scope in which the body of this list comprehension evaluates. */ - Scope getEvaluatingScope() { result = this.getFunction() } + /** Gets the scope in which the body of this list comprehension evaluates. */ + Scope getEvaluatingScope() { result = this.getFunction() } - /** Gets the expression for elements of this comprehension. */ - Expr getElt() { - exists(Yield yield, Stmt body | - result = yield.getValue() and - body = this.getNthInnerLoop(_).getAStmt() - | - yield = body.(ExprStmt).getValue() - or - yield = body.(If).getStmt(0).(ExprStmt).getValue() - ) - } + /** Gets the expression for elements of this comprehension. */ + Expr getElt() { + exists(Yield yield, Stmt body | + result = yield.getValue() and + body = this.getNthInnerLoop(_).getAStmt() + | + yield = body.(ExprStmt).getValue() + or + yield = body.(If).getStmt(0).(ExprStmt).getValue() + ) + } } /** A list comprehension, such as `[ chr(x) for x in range(ord('A'), ord('Z')+1) ]` */ class ListComp extends ListComp_, Comp { - override Expr getASubExpression() { - result = this.getAGenerator().getASubExpression() or - result = this.getElt() or - result = this.getIterable() - } + override Expr getASubExpression() { + result = this.getAGenerator().getASubExpression() or + result = this.getElt() or + result = this.getIterable() + } - override AstNode getAChildNode() { - result = this.getAGenerator() or - result = this.getIterable() or - result = this.getFunction() - } + override AstNode getAChildNode() { + result = this.getAGenerator() or + result = this.getIterable() or + result = this.getFunction() + } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } - /** Gets the scope in which the body of this list comprehension evaluates. */ - override Scope getEvaluatingScope() { - major_version() = 2 and result = this.getScope() - or - major_version() = 3 and result = this.getFunction() - } + /** Gets the scope in which the body of this list comprehension evaluates. */ + override Scope getEvaluatingScope() { + major_version() = 2 and result = this.getScope() + or + major_version() = 3 and result = this.getFunction() + } - /** Gets the iteration variable for the nth innermost generator of this list comprehension */ - override Variable getIterationVariable(int n) { result = Comp.super.getIterationVariable(n) } + /** Gets the iteration variable for the nth innermost generator of this list comprehension */ + override Variable getIterationVariable(int n) { result = Comp.super.getIterationVariable(n) } - override Function getFunction() { result = ListComp_.super.getFunction() } + override Function getFunction() { result = ListComp_.super.getFunction() } - override string toString() { result = ListComp_.super.toString() } + override string toString() { result = ListComp_.super.toString() } - override Expr getElt() { result = Comp.super.getElt() } + override Expr getElt() { result = Comp.super.getElt() } } /** A set comprehension such as `{ v for v in "0123456789" }` */ class SetComp extends SetComp_, Comp { - override Expr getASubExpression() { result = this.getIterable() } + override Expr getASubExpression() { result = this.getIterable() } - override AstNode getAChildNode() { - result = this.getASubExpression() or - result = this.getFunction() - } + override AstNode getAChildNode() { + result = this.getASubExpression() or + result = this.getFunction() + } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } - override Function getFunction() { result = SetComp_.super.getFunction() } + override Function getFunction() { result = SetComp_.super.getFunction() } } /** A dictionary comprehension, such as `{ k:v for k, v in enumerate("0123456789") }` */ class DictComp extends DictComp_, Comp { - override Expr getASubExpression() { result = this.getIterable() } + override Expr getASubExpression() { result = this.getIterable() } - override AstNode getAChildNode() { - result = this.getASubExpression() or - result = this.getFunction() - } + override AstNode getAChildNode() { + result = this.getASubExpression() or + result = this.getFunction() + } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } - override Function getFunction() { result = DictComp_.super.getFunction() } + override Function getFunction() { result = DictComp_.super.getFunction() } } /** A generator expression, such as `(var for var in iterable)` */ class GeneratorExp extends GeneratorExp_, Comp { - override Expr getASubExpression() { result = this.getIterable() } + override Expr getASubExpression() { result = this.getIterable() } - override AstNode getAChildNode() { - result = this.getASubExpression() or - result = this.getFunction() - } + override AstNode getAChildNode() { + result = this.getASubExpression() or + result = this.getFunction() + } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } - override Function getFunction() { result = GeneratorExp_.super.getFunction() } + override Function getFunction() { result = GeneratorExp_.super.getFunction() } } diff --git a/python/ql/src/semmle/python/Constants.qll b/python/ql/src/semmle/python/Constants.qll index 6ca694a3ad1..3faa6072acc 100644 --- a/python/ql/src/semmle/python/Constants.qll +++ b/python/ql/src/semmle/python/Constants.qll @@ -4,31 +4,31 @@ import python /** the Python major version number */ int major_version() { - explicit_major_version(result) - or - not explicit_major_version(_) and - /* If there is more than one version, prefer 2 for backwards compatibilty */ - (if py_flags_versioned("version.major", "2", "2") then result = 2 else result = 3) + explicit_major_version(result) + or + not explicit_major_version(_) and + /* If there is more than one version, prefer 2 for backwards compatibilty */ + (if py_flags_versioned("version.major", "2", "2") then result = 2 else result = 3) } /** the Python minor version number */ int minor_version() { - exists(string v | py_flags_versioned("version.minor", v, major_version().toString()) | - result = v.toInt() - ) + exists(string v | py_flags_versioned("version.minor", v, major_version().toString()) | + result = v.toInt() + ) } /** the Python micro version number */ int micro_version() { - exists(string v | py_flags_versioned("version.micro", v, major_version().toString()) | - result = v.toInt() - ) + exists(string v | py_flags_versioned("version.micro", v, major_version().toString()) | + result = v.toInt() + ) } private predicate explicit_major_version(int v) { - exists(string version | py_flags_versioned("language.version", version, _) | - version.charAt(0) = "2" and v = 2 - or - version.charAt(0) = "3" and v = 3 - ) + exists(string version | py_flags_versioned("language.version", version, _) | + version.charAt(0) = "2" and v = 2 + or + version.charAt(0) = "3" and v = 3 + ) } diff --git a/python/ql/src/semmle/python/Exprs.qll b/python/ql/src/semmle/python/Exprs.qll index e2dc663bd1b..553e12103ad 100644 --- a/python/ql/src/semmle/python/Exprs.qll +++ b/python/ql/src/semmle/python/Exprs.qll @@ -4,284 +4,284 @@ private import semmle.python.objects.ObjectInternal /** An expression */ class Expr extends Expr_, AstNode { - /** Gets the scope of this expression */ - override Scope getScope() { py_scopes(this, result) } + /** Gets the scope of this expression */ + override Scope getScope() { py_scopes(this, result) } - /** Gets a textual representation of this element. */ - override string toString() { result = "Expression" } + /** Gets a textual representation of this element. */ + override string toString() { result = "Expression" } - /** Gets the module in which this expression occurs */ - Module getEnclosingModule() { result = this.getScope().getEnclosingModule() } + /** Gets the module in which this expression occurs */ + Module getEnclosingModule() { result = this.getScope().getEnclosingModule() } - /** - * Whether this expression defines variable `v` - * If doing dataflow, then consider using SsaVariable.getDefinition() for more precision. - */ - predicate defines(Variable v) { this.getASubExpression+().defines(v) } + /** + * Whether this expression defines variable `v` + * If doing dataflow, then consider using SsaVariable.getDefinition() for more precision. + */ + predicate defines(Variable v) { this.getASubExpression+().defines(v) } - /** Whether this expression may have a side effect (as determined purely from its syntax) */ - predicate hasSideEffects() { - /* If an exception raised by this expression handled, count that as a side effect */ - this.getAFlowNode().getASuccessor().getNode() instanceof ExceptStmt - or - this.getASubExpression().hasSideEffects() - } + /** Whether this expression may have a side effect (as determined purely from its syntax) */ + predicate hasSideEffects() { + /* If an exception raised by this expression handled, count that as a side effect */ + this.getAFlowNode().getASuccessor().getNode() instanceof ExceptStmt + or + this.getASubExpression().hasSideEffects() + } - /** Whether this expression is a constant */ - predicate isConstant() { not this.isVariable() } + /** Whether this expression is a constant */ + predicate isConstant() { not this.isVariable() } - /** Use isParenthesized instead. */ - deprecated override predicate isParenthesised() { this.isParenthesized() } + /** Use isParenthesized instead. */ + deprecated override predicate isParenthesised() { this.isParenthesized() } - /** Whether the parenthesized property of this expression is true. */ - predicate isParenthesized() { Expr_.super.isParenthesised() } + /** Whether the parenthesized property of this expression is true. */ + predicate isParenthesized() { Expr_.super.isParenthesised() } - private predicate isVariable() { - this.hasSideEffects() - or - this instanceof Name - or - exists(Expr e | e = this.getASubExpression() and e.isVariable()) - } + private predicate isVariable() { + this.hasSideEffects() + or + this instanceof Name + or + exists(Expr e | e = this.getASubExpression() and e.isVariable()) + } - override Location getLocation() { result = Expr_.super.getLocation() } + override Location getLocation() { result = Expr_.super.getLocation() } - /** Gets an immediate (non-nested) sub-expression of this expression */ - Expr getASubExpression() { none() } + /** Gets an immediate (non-nested) sub-expression of this expression */ + Expr getASubExpression() { none() } - /** Use StrConst.getText() instead */ - deprecated string strValue() { none() } + /** Use StrConst.getText() instead */ + deprecated string strValue() { none() } - override AstNode getAChildNode() { result = this.getASubExpression() } + override AstNode getAChildNode() { result = this.getASubExpression() } - /** - * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. - * Gets what this expression might "refer-to". Performs a combination of localized (intra-procedural) points-to - * analysis and global module-level analysis. This points-to analysis favours precision over recall. It is highly - * precise, but may not provide information for a significant number of flow-nodes. - * If the class is unimportant then use `refersTo(value)` or `refersTo(value, origin)` instead. - * NOTE: For complex dataflow, involving multiple stages of points-to analysis, it may be more precise to use - * `ControlFlowNode.refersTo(...)` instead. - */ - predicate refersTo(Object obj, ClassObject cls, AstNode origin) { - this.refersTo(_, obj, cls, origin) - } + /** + * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. + * Gets what this expression might "refer-to". Performs a combination of localized (intra-procedural) points-to + * analysis and global module-level analysis. This points-to analysis favours precision over recall. It is highly + * precise, but may not provide information for a significant number of flow-nodes. + * If the class is unimportant then use `refersTo(value)` or `refersTo(value, origin)` instead. + * NOTE: For complex dataflow, involving multiple stages of points-to analysis, it may be more precise to use + * `ControlFlowNode.refersTo(...)` instead. + */ + predicate refersTo(Object obj, ClassObject cls, AstNode origin) { + this.refersTo(_, obj, cls, origin) + } - /** - * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. - * Gets what this expression might "refer-to" in the given `context`. - */ - predicate refersTo(Context context, Object obj, ClassObject cls, AstNode origin) { - this.getAFlowNode().refersTo(context, obj, cls, origin.getAFlowNode()) - } + /** + * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. + * Gets what this expression might "refer-to" in the given `context`. + */ + predicate refersTo(Context context, Object obj, ClassObject cls, AstNode origin) { + this.getAFlowNode().refersTo(context, obj, cls, origin.getAFlowNode()) + } - /** - * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. - * Holds if this expression might "refer-to" to `value` which is from `origin` - * Unlike `this.refersTo(value, _, origin)`, this predicate includes results - * where the class cannot be inferred. - */ - pragma[nomagic] - predicate refersTo(Object obj, AstNode origin) { - this.getAFlowNode().refersTo(obj, origin.getAFlowNode()) - } + /** + * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. + * Holds if this expression might "refer-to" to `value` which is from `origin` + * Unlike `this.refersTo(value, _, origin)`, this predicate includes results + * where the class cannot be inferred. + */ + pragma[nomagic] + predicate refersTo(Object obj, AstNode origin) { + this.getAFlowNode().refersTo(obj, origin.getAFlowNode()) + } - /** - * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. - * Equivalent to `this.refersTo(value, _)` - */ - predicate refersTo(Object obj) { this.refersTo(obj, _) } + /** + * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. + * Equivalent to `this.refersTo(value, _)` + */ + predicate refersTo(Object obj) { this.refersTo(obj, _) } - /** - * Holds if this expression might "point-to" to `value` which is from `origin` - * in the given `context`. - */ - predicate pointsTo(Context context, Value value, AstNode origin) { - this.getAFlowNode().pointsTo(context, value, origin.getAFlowNode()) - } + /** + * Holds if this expression might "point-to" to `value` which is from `origin` + * in the given `context`. + */ + predicate pointsTo(Context context, Value value, AstNode origin) { + this.getAFlowNode().pointsTo(context, value, origin.getAFlowNode()) + } - /** - * Holds if this expression might "point-to" to `value` which is from `origin`. - */ - predicate pointsTo(Value value, AstNode origin) { - this.getAFlowNode().pointsTo(value, origin.getAFlowNode()) - } + /** + * Holds if this expression might "point-to" to `value` which is from `origin`. + */ + predicate pointsTo(Value value, AstNode origin) { + this.getAFlowNode().pointsTo(value, origin.getAFlowNode()) + } - /** - * Holds if this expression might "point-to" to `value`. - */ - predicate pointsTo(Value value) { this.pointsTo(value, _) } + /** + * Holds if this expression might "point-to" to `value`. + */ + predicate pointsTo(Value value) { this.pointsTo(value, _) } - /** Gets a value that this expression might "point-to". */ - Value pointsTo() { this.pointsTo(result) } + /** Gets a value that this expression might "point-to". */ + Value pointsTo() { this.pointsTo(result) } } /** An assignment expression, such as `x := y` */ class AssignExpr extends AssignExpr_ { - override Expr getASubExpression() { - result = this.getValue() or - result = this.getTarget() - } + override Expr getASubExpression() { + result = this.getValue() or + result = this.getTarget() + } } /** An attribute expression, such as `value.attr` */ class Attribute extends Attribute_ { - /* syntax: Expr.name */ - override Expr getASubExpression() { result = this.getObject() } + /* syntax: Expr.name */ + override Expr getASubExpression() { result = this.getObject() } - override AttrNode getAFlowNode() { result = super.getAFlowNode() } + override AttrNode getAFlowNode() { result = super.getAFlowNode() } - /** Gets the name of this attribute. That is the `name` in `obj.name` */ - string getName() { result = Attribute_.super.getAttr() } + /** Gets the name of this attribute. That is the `name` in `obj.name` */ + string getName() { result = Attribute_.super.getAttr() } - /** Gets the object of this attribute. That is the `obj` in `obj.name` */ - Expr getObject() { result = Attribute_.super.getValue() } + /** Gets the object of this attribute. That is the `obj` in `obj.name` */ + Expr getObject() { result = Attribute_.super.getValue() } - /** - * Gets the expression corresponding to the object of the attribute, if the name of the attribute is `name`. - * Equivalent to `this.getObject() and this.getName() = name`. - */ - Expr getObject(string name) { - result = Attribute_.super.getValue() and - name = Attribute_.super.getAttr() - } + /** + * Gets the expression corresponding to the object of the attribute, if the name of the attribute is `name`. + * Equivalent to `this.getObject() and this.getName() = name`. + */ + Expr getObject(string name) { + result = Attribute_.super.getValue() and + name = Attribute_.super.getAttr() + } } /** A subscript expression, such as `value[slice]` */ class Subscript extends Subscript_ { - /* syntax: Expr[Expr] */ - override Expr getASubExpression() { - result = this.getIndex() - or - result = this.getObject() - } + /* syntax: Expr[Expr] */ + override Expr getASubExpression() { + result = this.getIndex() + or + result = this.getObject() + } - Expr getObject() { result = Subscript_.super.getValue() } + Expr getObject() { result = Subscript_.super.getValue() } - override SubscriptNode getAFlowNode() { result = super.getAFlowNode() } + override SubscriptNode getAFlowNode() { result = super.getAFlowNode() } } /** A call expression, such as `func(...)` */ class Call extends Call_ { - /* syntax: Expr(...) */ - override Expr getASubExpression() { - result = this.getAPositionalArg() or - result = this.getAKeyword().getValue() or - result = this.getFunc() - } + /* syntax: Expr(...) */ + override Expr getASubExpression() { + result = this.getAPositionalArg() or + result = this.getAKeyword().getValue() or + result = this.getFunc() + } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } - override string toString() { result = this.getFunc().toString() + "()" } + override string toString() { result = this.getFunc().toString() + "()" } - override CallNode getAFlowNode() { result = super.getAFlowNode() } + override CallNode getAFlowNode() { result = super.getAFlowNode() } - /** Gets a tuple (*) argument of this call. */ - Expr getStarargs() { result = this.getAPositionalArg().(Starred).getValue() } + /** Gets a tuple (*) argument of this call. */ + Expr getStarargs() { result = this.getAPositionalArg().(Starred).getValue() } - /** Gets a dictionary (**) argument of this call. */ - Expr getKwargs() { result = this.getANamedArg().(DictUnpacking).getValue() } + /** Gets a dictionary (**) argument of this call. */ + Expr getKwargs() { result = this.getANamedArg().(DictUnpacking).getValue() } - /* Backwards compatibility */ - /** - * Gets the nth keyword argument of this call expression, provided it is not preceded by a double-starred argument. - * This exists primarily for backwards compatibility. You are recommended to use - * Call.getNamedArg(index) instead. - */ - Keyword getKeyword(int index) { - result = this.getNamedArg(index) and - not exists(DictUnpacking d, int lower | d = this.getNamedArg(lower) and lower < index) - } + /* Backwards compatibility */ + /** + * Gets the nth keyword argument of this call expression, provided it is not preceded by a double-starred argument. + * This exists primarily for backwards compatibility. You are recommended to use + * Call.getNamedArg(index) instead. + */ + Keyword getKeyword(int index) { + result = this.getNamedArg(index) and + not exists(DictUnpacking d, int lower | d = this.getNamedArg(lower) and lower < index) + } - /** - * Gets a keyword argument of this call expression, provided it is not preceded by a double-starred argument. - * This exists primarily for backwards compatibility. You are recommended to use - * Call.getANamedArg() instead. - */ - Keyword getAKeyword() { result = this.getKeyword(_) } + /** + * Gets a keyword argument of this call expression, provided it is not preceded by a double-starred argument. + * This exists primarily for backwards compatibility. You are recommended to use + * Call.getANamedArg() instead. + */ + Keyword getAKeyword() { result = this.getKeyword(_) } - /** - * Gets the positional argument at `index`, provided it is not preceded by a starred argument. - * This exists primarily for backwards compatibility. You are recommended to use - * Call.getPositionalArg(index) instead. - */ - Expr getArg(int index) { - result = this.getPositionalArg(index) and - not result instanceof Starred and - not exists(Starred s, int lower | s = this.getPositionalArg(lower) and lower < index) - } + /** + * Gets the positional argument at `index`, provided it is not preceded by a starred argument. + * This exists primarily for backwards compatibility. You are recommended to use + * Call.getPositionalArg(index) instead. + */ + Expr getArg(int index) { + result = this.getPositionalArg(index) and + not result instanceof Starred and + not exists(Starred s, int lower | s = this.getPositionalArg(lower) and lower < index) + } - /** - * Gets a positional argument, provided it is not preceded by a starred argument. - * This exists primarily for backwards compatibility. You are recommended to use - * Call.getAPositionalArg() instead. - */ - Expr getAnArg() { result = this.getArg(_) } + /** + * Gets a positional argument, provided it is not preceded by a starred argument. + * This exists primarily for backwards compatibility. You are recommended to use + * Call.getAPositionalArg() instead. + */ + Expr getAnArg() { result = this.getArg(_) } - override AstNode getAChildNode() { - result = this.getAPositionalArg() or - result = this.getANamedArg() or - result = this.getFunc() - } + override AstNode getAChildNode() { + result = this.getAPositionalArg() or + result = this.getANamedArg() or + result = this.getFunc() + } - /** Gets the name of a named argument, including those passed in dict literals. */ - string getANamedArgumentName() { - result = this.getAKeyword().getArg() - or - result = this.getKwargs().(Dict).getAKey().(StrConst).getText() - } + /** Gets the name of a named argument, including those passed in dict literals. */ + string getANamedArgumentName() { + result = this.getAKeyword().getArg() + or + result = this.getKwargs().(Dict).getAKey().(StrConst).getText() + } - /** Gets the positional argument count of this call, provided there is no more than one tuple (*) argument. */ - int getPositionalArgumentCount() { - count(this.getStarargs()) < 2 and - result = count(Expr arg | arg = this.getAPositionalArg() and not arg instanceof Starred) - } + /** Gets the positional argument count of this call, provided there is no more than one tuple (*) argument. */ + int getPositionalArgumentCount() { + count(this.getStarargs()) < 2 and + result = count(Expr arg | arg = this.getAPositionalArg() and not arg instanceof Starred) + } - /** Gets the tuple (*) argument of this call, provided there is exactly one. */ - Expr getStarArg() { - count(this.getStarargs()) < 2 and - result = getStarargs() - } + /** Gets the tuple (*) argument of this call, provided there is exactly one. */ + Expr getStarArg() { + count(this.getStarargs()) < 2 and + result = getStarargs() + } } /** A conditional expression such as, `body if test else orelse` */ class IfExp extends IfExp_ { - /* syntax: Expr if Expr else Expr */ - override Expr getASubExpression() { - result = this.getTest() or result = this.getBody() or result = this.getOrelse() - } + /* syntax: Expr if Expr else Expr */ + override Expr getASubExpression() { + result = this.getTest() or result = this.getBody() or result = this.getOrelse() + } - override IfExprNode getAFlowNode() { result = super.getAFlowNode() } + override IfExprNode getAFlowNode() { result = super.getAFlowNode() } } /** A starred expression, such as the `*rest` in the assignment `first, *rest = seq` */ class Starred extends Starred_ { - /* syntax: *Expr */ - override Expr getASubExpression() { result = this.getValue() } + /* syntax: *Expr */ + override Expr getASubExpression() { result = this.getValue() } } /** A yield expression, such as `yield value` */ class Yield extends Yield_ { - /* syntax: yield Expr */ - override Expr getASubExpression() { result = this.getValue() } + /* syntax: yield Expr */ + override Expr getASubExpression() { result = this.getValue() } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } } /** A yield expression, such as `yield from value` */ class YieldFrom extends YieldFrom_ { - /* syntax: yield from Expr */ - override Expr getASubExpression() { result = this.getValue() } + /* syntax: yield from Expr */ + override Expr getASubExpression() { result = this.getValue() } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } } /** A repr (backticks) expression, such as `` `value` `` */ class Repr extends Repr_ { - /* syntax: `Expr` */ - override Expr getASubExpression() { result = this.getValue() } + /* syntax: `Expr` */ + override Expr getASubExpression() { result = this.getValue() } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } } /* Constants */ @@ -290,28 +290,28 @@ class Repr extends Repr_ { * `"hello"` are treated as Bytes for Python2, but Unicode for Python3. */ class Bytes extends StrConst { - /* syntax: b"hello" */ - Bytes() { not this.isUnicode() } + /* syntax: b"hello" */ + Bytes() { not this.isUnicode() } - override Object getLiteralObject() { - py_cobjecttypes(result, theBytesType()) and - py_cobjectnames(result, this.quotedString()) - } + override Object getLiteralObject() { + py_cobjecttypes(result, theBytesType()) and + py_cobjectnames(result, this.quotedString()) + } - /** - * The extractor puts quotes into the name of each string (to prevent "0" clashing with 0). - * The following predicate help us match up a string/byte literals in the source - * which the equivalent object. - */ - private string quotedString() { - exists(string b_unquoted | b_unquoted = this.getS() | result = "b'" + b_unquoted + "'") - } + /** + * The extractor puts quotes into the name of each string (to prevent "0" clashing with 0). + * The following predicate help us match up a string/byte literals in the source + * which the equivalent object. + */ + private string quotedString() { + exists(string b_unquoted | b_unquoted = this.getS() | result = "b'" + b_unquoted + "'") + } } /** An ellipsis expression, such as `...` */ class Ellipsis extends Ellipsis_ { - /* syntax: ... */ - override Expr getASubExpression() { none() } + /* syntax: ... */ + override Expr getASubExpression() { none() } } /** @@ -319,117 +319,117 @@ class Ellipsis extends Ellipsis_ { * Consists of string (both unicode and byte) literals and numeric literals. */ abstract class ImmutableLiteral extends Expr { - abstract Object getLiteralObject(); + abstract Object getLiteralObject(); - abstract boolean booleanValue(); + abstract boolean booleanValue(); - final Value getLiteralValue() { result.(ConstantObjectInternal).getLiteral() = this } + final Value getLiteralValue() { result.(ConstantObjectInternal).getLiteral() = this } } /** A numerical constant expression, such as `7` or `4.2` */ abstract class Num extends Num_, ImmutableLiteral { - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } - /* We want to declare this abstract, but currently we cannot. */ - override string toString() { result = "Num with missing toString" } + /* We want to declare this abstract, but currently we cannot. */ + override string toString() { result = "Num with missing toString" } } /** An integer numeric constant, such as `7` or `0x9` */ class IntegerLiteral extends Num { - /* syntax: 4 */ - IntegerLiteral() { not this instanceof FloatLiteral and not this instanceof ImaginaryLiteral } + /* syntax: 4 */ + IntegerLiteral() { not this instanceof FloatLiteral and not this instanceof ImaginaryLiteral } - /** - * Gets the (integer) value of this constant. Will not return a result if the value does not fit into - * a 32 bit signed value - */ - int getValue() { result = this.getN().toInt() } + /** + * Gets the (integer) value of this constant. Will not return a result if the value does not fit into + * a 32 bit signed value + */ + int getValue() { result = this.getN().toInt() } - override string toString() { result = "IntegerLiteral" } + override string toString() { result = "IntegerLiteral" } - override Object getLiteralObject() { - py_cobjecttypes(result, theIntType()) and py_cobjectnames(result, this.getN()) - or - py_cobjecttypes(result, theLongType()) and py_cobjectnames(result, this.getN()) - } + override Object getLiteralObject() { + py_cobjecttypes(result, theIntType()) and py_cobjectnames(result, this.getN()) + or + py_cobjecttypes(result, theLongType()) and py_cobjectnames(result, this.getN()) + } - override boolean booleanValue() { - this.getValue() = 0 and result = false - or - this.getValue() != 0 and result = true - } + override boolean booleanValue() { + this.getValue() = 0 and result = false + or + this.getValue() != 0 and result = true + } } /** A floating point numeric constant, such as `0.4` or `4e3` */ class FloatLiteral extends Num { - /* syntax: 4.2 */ - FloatLiteral() { - not this instanceof ImaginaryLiteral and - this.getN().regexpMatch(".*[.eE].*") - } + /* syntax: 4.2 */ + FloatLiteral() { + not this instanceof ImaginaryLiteral and + this.getN().regexpMatch(".*[.eE].*") + } - float getValue() { result = this.getN().toFloat() } + float getValue() { result = this.getN().toFloat() } - override string toString() { result = "FloatLiteral" } + override string toString() { result = "FloatLiteral" } - override Object getLiteralObject() { - py_cobjecttypes(result, theFloatType()) and py_cobjectnames(result, this.getN()) - } + override Object getLiteralObject() { + py_cobjecttypes(result, theFloatType()) and py_cobjectnames(result, this.getN()) + } - override boolean booleanValue() { - this.getValue() = 0.0 and result = false - or - // In QL 0.0 != -0.0 - this.getValue() = -0.0 and result = false - or - this.getValue() != 0.0 and this.getValue() != -0.0 and result = true - } + override boolean booleanValue() { + this.getValue() = 0.0 and result = false + or + // In QL 0.0 != -0.0 + this.getValue() = -0.0 and result = false + or + this.getValue() != 0.0 and this.getValue() != -0.0 and result = true + } } /** An imaginary numeric constant, such as `3j` */ class ImaginaryLiteral extends Num { - private float value; + private float value; - /* syntax: 1.0j */ - ImaginaryLiteral() { value = this.getN().regexpCapture("(.+)j.*", 1).toFloat() } + /* syntax: 1.0j */ + ImaginaryLiteral() { value = this.getN().regexpCapture("(.+)j.*", 1).toFloat() } - /** Gets the value of this constant as a floating point value */ - float getValue() { result = value } + /** Gets the value of this constant as a floating point value */ + float getValue() { result = value } - override string toString() { result = "ImaginaryLiteral" } + override string toString() { result = "ImaginaryLiteral" } - override Object getLiteralObject() { - py_cobjecttypes(result, theComplexType()) and py_cobjectnames(result, this.getN()) - } + override Object getLiteralObject() { + py_cobjecttypes(result, theComplexType()) and py_cobjectnames(result, this.getN()) + } - override boolean booleanValue() { - this.getValue() = 0.0 and result = false - or - // In QL 0.0 != -0.0 - this.getValue() = -0.0 and result = false - or - this.getValue() != 0.0 and this.getValue() != -0.0 and result = true - } + override boolean booleanValue() { + this.getValue() = 0.0 and result = false + or + // In QL 0.0 != -0.0 + this.getValue() = -0.0 and result = false + or + this.getValue() != 0.0 and this.getValue() != -0.0 and result = true + } } class NegativeIntegerLiteral extends ImmutableLiteral, UnaryExpr { - NegativeIntegerLiteral() { - this.getOp() instanceof USub and - this.getOperand() instanceof IntegerLiteral - } + NegativeIntegerLiteral() { + this.getOp() instanceof USub and + this.getOperand() instanceof IntegerLiteral + } - override boolean booleanValue() { result = this.getOperand().(IntegerLiteral).booleanValue() } + override boolean booleanValue() { result = this.getOperand().(IntegerLiteral).booleanValue() } - override Object getLiteralObject() { - (py_cobjecttypes(result, theIntType()) or py_cobjecttypes(result, theLongType())) and - py_cobjectnames(result, "-" + this.getOperand().(IntegerLiteral).getN()) - } + override Object getLiteralObject() { + (py_cobjecttypes(result, theIntType()) or py_cobjecttypes(result, theLongType())) and + py_cobjectnames(result, "-" + this.getOperand().(IntegerLiteral).getN()) + } - /** - * Gets the (integer) value of this constant. Will not return a result if the value does not fit into - * a 32 bit signed value - */ - int getValue() { result = -this.getOperand().(IntegerLiteral).getValue() } + /** + * Gets the (integer) value of this constant. Will not return a result if the value does not fit into + * a 32 bit signed value + */ + int getValue() { result = -this.getOperand().(IntegerLiteral).getValue() } } /** @@ -437,68 +437,68 @@ class NegativeIntegerLiteral extends ImmutableLiteral, UnaryExpr { * "hello" are treated as Bytes for Python2, but Unicode for Python3. */ class Unicode extends StrConst { - /* syntax: "hello" */ - Unicode() { this.isUnicode() } + /* syntax: "hello" */ + Unicode() { this.isUnicode() } - override Object getLiteralObject() { - py_cobjecttypes(result, theUnicodeType()) and - py_cobjectnames(result, this.quotedString()) - } + override Object getLiteralObject() { + py_cobjecttypes(result, theUnicodeType()) and + py_cobjectnames(result, this.quotedString()) + } - /** - * The extractor puts quotes into the name of each string (to prevent "0" clashing with 0). - * The following predicate help us match up a string/byte literals in the source - * which the equivalent object. - */ - string quotedString() { - exists(string u_unquoted | u_unquoted = this.getS() | result = "u'" + u_unquoted + "'") - } + /** + * The extractor puts quotes into the name of each string (to prevent "0" clashing with 0). + * The following predicate help us match up a string/byte literals in the source + * which the equivalent object. + */ + string quotedString() { + exists(string u_unquoted | u_unquoted = this.getS() | result = "u'" + u_unquoted + "'") + } } /* Compound Values */ /** A dictionary expression, such as `{'key':'value'}` */ class Dict extends Dict_ { - /* syntax: {Expr: Expr, ...} */ - /** Gets the value of an item of this dict display */ - Expr getAValue() { result = this.getAnItem().(DictDisplayItem).getValue() } + /* syntax: {Expr: Expr, ...} */ + /** Gets the value of an item of this dict display */ + Expr getAValue() { result = this.getAnItem().(DictDisplayItem).getValue() } - /** - * Gets the key of an item of this dict display, for those items that have keys - * E.g, in {'a':1, **b} this returns only 'a' - */ - Expr getAKey() { result = this.getAnItem().(KeyValuePair).getKey() } + /** + * Gets the key of an item of this dict display, for those items that have keys + * E.g, in {'a':1, **b} this returns only 'a' + */ + Expr getAKey() { result = this.getAnItem().(KeyValuePair).getKey() } - override Expr getASubExpression() { result = this.getAValue() or result = this.getAKey() } + override Expr getASubExpression() { result = this.getAValue() or result = this.getAKey() } - override AstNode getAChildNode() { result = this.getAnItem() } + override AstNode getAChildNode() { result = this.getAnItem() } } /** A list expression, such as `[ 1, 3, 5, 7, 9 ]` */ class List extends List_ { - /* syntax: [Expr, ...] */ - override Expr getASubExpression() { result = this.getAnElt() } + /* syntax: [Expr, ...] */ + override Expr getASubExpression() { result = this.getAnElt() } } /** A set expression such as `{ 1, 3, 5, 7, 9 }` */ class Set extends Set_ { - /* syntax: {Expr, ...} */ - override Expr getASubExpression() { result = this.getAnElt() } + /* syntax: {Expr, ...} */ + override Expr getASubExpression() { result = this.getAnElt() } } class PlaceHolder extends PlaceHolder_ { - string getId() { result = this.getVariable().getId() } + string getId() { result = this.getVariable().getId() } - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } - override string toString() { result = "$" + this.getId() } + override string toString() { result = "$" + this.getId() } - override NameNode getAFlowNode() { result = super.getAFlowNode() } + override NameNode getAFlowNode() { result = super.getAFlowNode() } } /** A tuple expression such as `( 1, 3, 5, 7, 9 )` */ class Tuple extends Tuple_ { - /* syntax: (Expr, ...) */ - override Expr getASubExpression() { result = this.getAnElt() } + /* syntax: (Expr, ...) */ + override Expr getASubExpression() { result = this.getAnElt() } } /** @@ -506,138 +506,138 @@ class Tuple extends Tuple_ { * `None`, `True` and `False` are excluded. */ class Name extends Name_ { - /* syntax: name */ - string getId() { result = this.getVariable().getId() } + /* syntax: name */ + string getId() { result = this.getVariable().getId() } - /** Whether this expression is a definition */ - predicate isDefinition() { - py_expr_contexts(_, 5, this) - or - /* Treat Param as a definition (which it is) */ - py_expr_contexts(_, 4, this) - or - /* The target in an augmented assignment is also a definition (and a use) */ - exists(AugAssign aa | aa.getTarget() = this) - } + /** Whether this expression is a definition */ + predicate isDefinition() { + py_expr_contexts(_, 5, this) + or + /* Treat Param as a definition (which it is) */ + py_expr_contexts(_, 4, this) + or + /* The target in an augmented assignment is also a definition (and a use) */ + exists(AugAssign aa | aa.getTarget() = this) + } - /** - * Whether this expression defines variable `v` - * If doing dataflow, then consider using SsaVariable.getDefinition() for more precision. - */ - override predicate defines(Variable v) { - this.isDefinition() and - v = this.getVariable() - } + /** + * Whether this expression defines variable `v` + * If doing dataflow, then consider using SsaVariable.getDefinition() for more precision. + */ + override predicate defines(Variable v) { + this.isDefinition() and + v = this.getVariable() + } - /** Whether this expression is a deletion */ - predicate isDeletion() { py_expr_contexts(_, 2, this) } + /** Whether this expression is a deletion */ + predicate isDeletion() { py_expr_contexts(_, 2, this) } - /** - * Whether this expression deletes variable `v`. - * If doing dataflow, then consider using SsaVariable.getDefinition() for more precision. - */ - predicate deletes(Variable v) { - this.isDeletion() and - v = this.getVariable() - } + /** + * Whether this expression deletes variable `v`. + * If doing dataflow, then consider using SsaVariable.getDefinition() for more precision. + */ + predicate deletes(Variable v) { + this.isDeletion() and + v = this.getVariable() + } - /** Whether this expression is a use */ - predicate isUse() { py_expr_contexts(_, 3, this) } + /** Whether this expression is a use */ + predicate isUse() { py_expr_contexts(_, 3, this) } - /** - * Whether this expression is a use of variable `v` - * If doing dataflow, then consider using SsaVariable.getAUse() for more precision. - */ - predicate uses(Variable v) { - this.isUse() and - v = this.getVariable() - } + /** + * Whether this expression is a use of variable `v` + * If doing dataflow, then consider using SsaVariable.getAUse() for more precision. + */ + predicate uses(Variable v) { + this.isUse() and + v = this.getVariable() + } - override predicate isConstant() { none() } + override predicate isConstant() { none() } - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } - override string toString() { result = this.getId() } + override string toString() { result = this.getId() } - override NameNode getAFlowNode() { result = super.getAFlowNode() } + override NameNode getAFlowNode() { result = super.getAFlowNode() } - override predicate isArtificial() { - /* Artificial variable names in comprehensions all start with "." */ - this.getId().charAt(0) = "." - } + override predicate isArtificial() { + /* Artificial variable names in comprehensions all start with "." */ + this.getId().charAt(0) = "." + } } class Filter extends Filter_ { - override Expr getASubExpression() { - result = this.getFilter() - or - result = this.getValue() - } + override Expr getASubExpression() { + result = this.getFilter() + or + result = this.getValue() + } } /** A slice. E.g `0:1` in the expression `x[0:1]` */ class Slice extends Slice_ { - override Expr getASubExpression() { - result = this.getStart() or - result = this.getStop() or - result = this.getStep() - } + override Expr getASubExpression() { + result = this.getStart() or + result = this.getStop() or + result = this.getStep() + } } /** A string constant. */ class StrConst extends Str_, ImmutableLiteral { - /* syntax: "hello" */ - predicate isUnicode() { - this.getPrefix().charAt(_) = "u" - or - this.getPrefix().charAt(_) = "U" - or - not this.getPrefix().charAt(_) = "b" and major_version() = 3 - or - not this.getPrefix().charAt(_) = "b" and - this.getEnclosingModule().hasFromFuture("unicode_literals") - } + /* syntax: "hello" */ + predicate isUnicode() { + this.getPrefix().charAt(_) = "u" + or + this.getPrefix().charAt(_) = "U" + or + not this.getPrefix().charAt(_) = "b" and major_version() = 3 + or + not this.getPrefix().charAt(_) = "b" and + this.getEnclosingModule().hasFromFuture("unicode_literals") + } - deprecated override string strValue() { result = this.getS() } + deprecated override string strValue() { result = this.getS() } - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } - override AstNode getAChildNode() { result = this.getAnImplicitlyConcatenatedPart() } + override AstNode getAChildNode() { result = this.getAnImplicitlyConcatenatedPart() } - /** Gets the text of this str constant */ - string getText() { result = this.getS() } + /** Gets the text of this str constant */ + string getText() { result = this.getS() } - /** Whether this is a docstring */ - predicate isDocString() { exists(Scope s | s.getDocString() = this) } + /** Whether this is a docstring */ + predicate isDocString() { exists(Scope s | s.getDocString() = this) } - override boolean booleanValue() { - this.getText() = "" and result = false - or - this.getText() != "" and result = true - } + override boolean booleanValue() { + this.getText() = "" and result = false + or + this.getText() != "" and result = true + } - override Object getLiteralObject() { none() } + override Object getLiteralObject() { none() } } private predicate name_consts(Name_ n, string id) { - exists(Variable v | py_variables(v, n) and id = v.getId() | - id = "True" or id = "False" or id = "None" - ) + exists(Variable v | py_variables(v, n) and id = v.getId() | + id = "True" or id = "False" or id = "None" + ) } /** A named constant, one of `None`, `True` or `False` */ abstract class NameConstant extends Name, ImmutableLiteral { - NameConstant() { name_consts(this, _) } + NameConstant() { name_consts(this, _) } - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } - override string toString() { name_consts(this, result) } + override string toString() { name_consts(this, result) } - override predicate isConstant() { any() } + override predicate isConstant() { any() } - override NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() } + override NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() } - override predicate isArtificial() { none() } + override predicate isArtificial() { none() } } /** A boolean named constant, either `True` or `False` */ @@ -645,44 +645,44 @@ abstract class BooleanLiteral extends NameConstant { } /** The boolean named constant `True` */ class True extends BooleanLiteral { - /* syntax: True */ - True() { name_consts(this, "True") } + /* syntax: True */ + True() { name_consts(this, "True") } - override Object getLiteralObject() { name_consts(this, "True") and result = theTrueObject() } + override Object getLiteralObject() { name_consts(this, "True") and result = theTrueObject() } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } } /** The boolean named constant `False` */ class False extends BooleanLiteral { - /* syntax: False */ - False() { name_consts(this, "False") } + /* syntax: False */ + False() { name_consts(this, "False") } - override Object getLiteralObject() { name_consts(this, "False") and result = theFalseObject() } + override Object getLiteralObject() { name_consts(this, "False") and result = theFalseObject() } - override boolean booleanValue() { result = false } + override boolean booleanValue() { result = false } } /** `None` */ class None extends NameConstant { - /* syntax: None */ - None() { name_consts(this, "None") } + /* syntax: None */ + None() { name_consts(this, "None") } - override Object getLiteralObject() { name_consts(this, "None") and result = theNoneObject() } + override Object getLiteralObject() { name_consts(this, "None") and result = theNoneObject() } - override boolean booleanValue() { result = false } + override boolean booleanValue() { result = false } } /** An await expression such as `await coro`. */ class Await extends Await_ { - /* syntax: await Expr */ - override Expr getASubExpression() { result = this.getValue() } + /* syntax: await Expr */ + override Expr getASubExpression() { result = this.getValue() } } /** A formatted string literal expression, such as `f'hello {world!s}'` */ class Fstring extends Fstring_ { - /* syntax: f"Yes!" */ - override Expr getASubExpression() { result = this.getAValue() } + /* syntax: f"Yes!" */ + override Expr getASubExpression() { result = this.getAValue() } } /** @@ -690,10 +690,10 @@ class Fstring extends Fstring_ { * For example, in the string `f'hello {world!s}'` the formatted value is `world!s`. */ class FormattedValue extends FormattedValue_ { - override Expr getASubExpression() { - result = this.getValue() or - result = this.getFormatSpec() - } + override Expr getASubExpression() { + result = this.getValue() or + result = this.getFormatSpec() + } } /* Expression Contexts */ diff --git a/python/ql/src/semmle/python/Files.qll b/python/ql/src/semmle/python/Files.qll index c4b71372858..f7cf07caa56 100644 --- a/python/ql/src/semmle/python/Files.qll +++ b/python/ql/src/semmle/python/Files.qll @@ -2,125 +2,125 @@ import python /** A file */ class File extends Container { - File() { files(this, _, _, _, _) } + File() { files(this, _, _, _, _) } - /** DEPRECATED: Use `getAbsolutePath` instead. */ - deprecated override string getName() { result = this.getAbsolutePath() } + /** DEPRECATED: Use `getAbsolutePath` instead. */ + deprecated override string getName() { result = this.getAbsolutePath() } - /** DEPRECATED: Use `getAbsolutePath` instead. */ - deprecated string getFullName() { result = this.getAbsolutePath() } + /** DEPRECATED: Use `getAbsolutePath` instead. */ + deprecated string getFullName() { result = this.getAbsolutePath() } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getAbsolutePath() = filepath and - startline = 0 and - startcolumn = 0 and - endline = 0 and - endcolumn = 0 - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getAbsolutePath() = filepath and + startline = 0 and + startcolumn = 0 and + endline = 0 and + endcolumn = 0 + } - /** Whether this file is a source code file. */ - predicate fromSource() { - /* If we start to analyse .pyc files, then this will have to change. */ - any() - } + /** Whether this file is a source code file. */ + predicate fromSource() { + /* If we start to analyse .pyc files, then this will have to change. */ + any() + } - /** Gets a short name for this file (just the file name) */ - string getShortName() { - exists(string simple, string ext | files(this, _, simple, ext, _) | result = simple + ext) - } + /** Gets a short name for this file (just the file name) */ + string getShortName() { + exists(string simple, string ext | files(this, _, simple, ext, _) | result = simple + ext) + } - private int lastLine() { - result = max(int i | exists(Location l | l.getFile() = this and l.getEndLine() = i)) - } + private int lastLine() { + result = max(int i | exists(Location l | l.getFile() = this and l.getEndLine() = i)) + } - /** Whether line n is empty (it contains neither code nor comment). */ - predicate emptyLine(int n) { - n in [0 .. this.lastLine()] and - not occupied_line(this, n) - } + /** Whether line n is empty (it contains neither code nor comment). */ + predicate emptyLine(int n) { + n in [0 .. this.lastLine()] and + not occupied_line(this, n) + } - string getSpecifiedEncoding() { - exists(Comment c, Location l | l = c.getLocation() and l.getFile() = this | - l.getStartLine() < 3 and - result = c.getText().regexpCapture(".*coding[:=]\\s*([-\\w.]+).*", 1) - ) - } + string getSpecifiedEncoding() { + exists(Comment c, Location l | l = c.getLocation() and l.getFile() = this | + l.getStartLine() < 3 and + result = c.getText().regexpCapture(".*coding[:=]\\s*([-\\w.]+).*", 1) + ) + } - override string getAbsolutePath() { files(this, result, _, _, _) } + override string getAbsolutePath() { files(this, result, _, _, _) } - /** Gets the URL of this file. */ - override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" } + /** Gets the URL of this file. */ + override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" } - override Container getImportRoot(int n) { - /* File stem must be a legal Python identifier */ - this.getStem().regexpMatch("[^\\d\\W]\\w*") and - result = this.getParent().getImportRoot(n) - } + override Container getImportRoot(int n) { + /* File stem must be a legal Python identifier */ + this.getStem().regexpMatch("[^\\d\\W]\\w*") and + result = this.getParent().getImportRoot(n) + } - /** - * Gets the contents of this file as a string. - * This will only work for those non-python files that - * are specified to be extracted. - */ - string getContents() { file_contents(this, result) } + /** + * Gets the contents of this file as a string. + * This will only work for those non-python files that + * are specified to be extracted. + */ + string getContents() { file_contents(this, result) } } private predicate occupied_line(File f, int n) { - exists(Location l | l.getFile() = f | - l.getStartLine() = n - or - exists(StrConst s | s.getLocation() = l | n in [l.getStartLine() .. l.getEndLine()]) - ) + exists(Location l | l.getFile() = f | + l.getStartLine() = n + or + exists(StrConst s | s.getLocation() = l | n in [l.getStartLine() .. l.getEndLine()]) + ) } /** A folder (directory) */ class Folder extends Container { - Folder() { folders(this, _, _) } + Folder() { folders(this, _, _) } - /** DEPRECATED: Use `getAbsolutePath` instead. */ - deprecated override string getName() { result = this.getAbsolutePath() } + /** DEPRECATED: Use `getAbsolutePath` instead. */ + deprecated override string getName() { result = this.getAbsolutePath() } - /** DEPRECATED: Use `getBaseName` instead. */ - deprecated string getSimple() { folders(this, _, result) } + /** DEPRECATED: Use `getBaseName` instead. */ + deprecated string getSimple() { folders(this, _, result) } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getAbsolutePath() = filepath and - startline = 0 and - startcolumn = 0 and - endline = 0 and - endcolumn = 0 - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getAbsolutePath() = filepath and + startline = 0 and + startcolumn = 0 and + endline = 0 and + endcolumn = 0 + } - override string getAbsolutePath() { folders(this, result, _) } + override string getAbsolutePath() { folders(this, result, _) } - /** Gets the URL of this folder. */ - override string getURL() { result = "folder://" + this.getAbsolutePath() } + /** Gets the URL of this folder. */ + override string getURL() { result = "folder://" + this.getAbsolutePath() } - override Container getImportRoot(int n) { - this.isImportRoot(n) and result = this - or - /* Folder must be a legal Python identifier */ - this.getBaseName().regexpMatch("[^\\d\\W]\\w*") and - result = this.getParent().getImportRoot(n) - } + override Container getImportRoot(int n) { + this.isImportRoot(n) and result = this + or + /* Folder must be a legal Python identifier */ + this.getBaseName().regexpMatch("[^\\d\\W]\\w*") and + result = this.getParent().getImportRoot(n) + } } /** @@ -128,327 +128,331 @@ class Folder extends Container { * hold elements of interest. */ abstract class Container extends @container { - Container getParent() { containerparent(result, this) } + Container getParent() { containerparent(result, this) } - /** Gets a child of this container */ - deprecated Container getChild() { containerparent(this, result) } + /** Gets a child of this container */ + deprecated Container getChild() { containerparent(this, result) } - /** - * Gets a textual representation of the path of this container. - * - * This is the absolute path of the container. + /** + * Gets a textual representation of the path of this container. + * + * This is the absolute path of the container. + */ + string toString() { result = this.getAbsolutePath() } + + /** Gets the name of this container */ + abstract string getName(); + + /** + * Gets the relative path of this file or folder from the root folder of the + * analyzed source location. The relative path of the root folder itself is + * the empty string. + * + * This has no result if the container is outside the source root, that is, + * if the root folder is not a reflexive, transitive parent of this container. + */ + string getRelativePath() { + exists(string absPath, string pref | + absPath = this.getAbsolutePath() and sourceLocationPrefix(pref) + | + absPath = pref and result = "" + or + absPath = pref.regexpReplaceAll("/$", "") + "/" + result and + not result.matches("/%") + ) + } + + /** Whether this file or folder is part of the standard library */ + predicate inStdlib() { this.inStdlib(_, _) } + + /** + * Whether this file or folder is part of the standard library + * for version `major.minor` + */ + predicate inStdlib(int major, int minor) { + exists(Module m | + m.getPath() = this and + m.inStdLib(major, minor) + ) + } + + /* Standard cross-language API */ + /** Gets a file or sub-folder in this container. */ + Container getAChildContainer() { containerparent(this, result) } + + /** Gets a file in this container. */ + File getAFile() { result = this.getAChildContainer() } + + /** Gets a sub-folder in this container. */ + Folder getAFolder() { result = this.getAChildContainer() } + + /** + * Gets the absolute, canonical path of this container, using forward slashes + * as path separator. + * + * The path starts with a _root prefix_ followed by zero or more _path + * segments_ separated by forward slashes. + * + * The root prefix is of one of the following forms: + * + * 1. A single forward slash `/` (Unix-style) + * 2. An upper-case drive letter followed by a colon and a forward slash, + * such as `C:/` (Windows-style) + * 3. Two forward slashes, a computer name, and then another forward slash, + * such as `//FileServer/` (UNC-style) + * + * Path segments are never empty (that is, absolute paths never contain two + * contiguous slashes, except as part of a UNC-style root prefix). Also, path + * segments never contain forward slashes, and no path segment is of the + * form `.` (one dot) or `..` (two dots). + * + * Note that an absolute path never ends with a forward slash, except if it is + * a bare root prefix, that is, the path has no path segments. A container + * whose absolute path has no segments is always a `Folder`, not a `File`. + */ + abstract string getAbsolutePath(); + + /** + * Gets the base name of this container including extension, that is, the last + * segment of its absolute path, or the empty string if it has no segments. + * + * Here are some examples of absolute paths and the corresponding base names + * (surrounded with quotes to avoid ambiguity): + * + * + * + * + * + * + * + * + * + *
Absolute pathBase name
"/tmp/tst.py""tst.py"
"C:/Program Files (x86)""Program Files (x86)"
"/"""
"C:/"""
"D:/"""
"//FileServer/"""
+ */ + string getBaseName() { + result = getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1) + } + + /** + * Gets the extension of this container, that is, the suffix of its base name + * after the last dot character, if any. + * + * In particular, + * + * - if the name does not include a dot, there is no extension, so this + * predicate has no result; + * - if the name ends in a dot, the extension is the empty string; + * - if the name contains multiple dots, the extension follows the last dot. + * + * Here are some examples of absolute paths and the corresponding extensions + * (surrounded with quotes to avoid ambiguity): + * + * + * + * + * + * + * + * + *
Absolute pathExtension
"/tmp/tst.py""py"
"/tmp/.gitignore""gitignore"
"/bin/bash"not defined
"/tmp/tst2."""
"/tmp/x.tar.gz""gz"
+ */ + string getExtension() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) } + + /** + * Gets the stem of this container, that is, the prefix of its base name up to + * (but not including) the last dot character if there is one, or the entire + * base name if there is not. + * + * Here are some examples of absolute paths and the corresponding stems + * (surrounded with quotes to avoid ambiguity): + * + * + * + * + * + * + * + * + *
Absolute pathStem
"/tmp/tst.py""tst"
"/tmp/.gitignore"""
"/bin/bash""bash"
"/tmp/tst2.""tst2"
"/tmp/x.tar.gz""x.tar"
+ */ + string getStem() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) } + + File getFile(string baseName) { + result = this.getAFile() and + result.getBaseName() = baseName + } + + Folder getFolder(string baseName) { + result = this.getAFolder() and + result.getBaseName() = baseName + } + + Container getParentContainer() { this = result.getAChildContainer() } + + Container getChildContainer(string baseName) { + result = this.getAChildContainer() and + result.getBaseName() = baseName + } + + /** + * Gets a URL representing the location of this container. + * + * For more information see [Providing URLs](https://help.semmle.com/QL/learn-ql/ql/locations.html#providing-urls). + */ + abstract string getURL(); + + /** Holds if this folder is on the import path. */ + predicate isImportRoot() { this.isImportRoot(_) } + + /** + * Holds if this folder is on the import path, at index `n` in the list of + * paths. The list of paths is composed of the paths passed to the extractor and + * `sys.path`. + */ + predicate isImportRoot(int n) { this.getName() = import_path_element(n) } + + /** Holds if this folder is the root folder for the standard library. */ + predicate isStdLibRoot(int major, int minor) { + major = major_version() and + minor = minor_version() and + this.isStdLibRoot() + } + + /** Holds if this folder is the root folder for the standard library. */ + predicate isStdLibRoot() { + /* + * Look for a standard lib module and find its import path + * We use `os` as it is the most likely to be imported and + * `tty` because it is small for testing. */ - string toString() { result = this.getAbsolutePath() } - /** Gets the name of this container */ - abstract string getName(); + exists(Module m | m.getName() = "os" or m.getName() = "tty" | + m.getFile().getImportRoot() = this + ) + } - /** - * Gets the relative path of this file or folder from the root folder of the - * analyzed source location. The relative path of the root folder itself is - * the empty string. - * - * This has no result if the container is outside the source root, that is, - * if the root folder is not a reflexive, transitive parent of this container. - */ - string getRelativePath() { - exists(string absPath, string pref | - absPath = this.getAbsolutePath() and sourceLocationPrefix(pref) - | - absPath = pref and result = "" - or - absPath = pref.regexpReplaceAll("/$", "") + "/" + result and - not result.matches("/%") - ) - } + /** Gets the path element from which this container would be loaded. */ + Container getImportRoot() { + exists(int n | + result = this.getImportRoot(n) and + not exists(int m | + exists(this.getImportRoot(m)) and + m < n + ) + ) + } - /** Whether this file or folder is part of the standard library */ - predicate inStdlib() { this.inStdlib(_, _) } - - /** - * Whether this file or folder is part of the standard library - * for version `major.minor` - */ - predicate inStdlib(int major, int minor) { - exists(Module m | - m.getPath() = this and - m.inStdLib(major, minor) - ) - } - - /* Standard cross-language API */ - /** Gets a file or sub-folder in this container. */ - Container getAChildContainer() { containerparent(this, result) } - - /** Gets a file in this container. */ - File getAFile() { result = this.getAChildContainer() } - - /** Gets a sub-folder in this container. */ - Folder getAFolder() { result = this.getAChildContainer() } - - /** - * Gets the absolute, canonical path of this container, using forward slashes - * as path separator. - * - * The path starts with a _root prefix_ followed by zero or more _path - * segments_ separated by forward slashes. - * - * The root prefix is of one of the following forms: - * - * 1. A single forward slash `/` (Unix-style) - * 2. An upper-case drive letter followed by a colon and a forward slash, - * such as `C:/` (Windows-style) - * 3. Two forward slashes, a computer name, and then another forward slash, - * such as `//FileServer/` (UNC-style) - * - * Path segments are never empty (that is, absolute paths never contain two - * contiguous slashes, except as part of a UNC-style root prefix). Also, path - * segments never contain forward slashes, and no path segment is of the - * form `.` (one dot) or `..` (two dots). - * - * Note that an absolute path never ends with a forward slash, except if it is - * a bare root prefix, that is, the path has no path segments. A container - * whose absolute path has no segments is always a `Folder`, not a `File`. - */ - abstract string getAbsolutePath(); - - /** - * Gets the base name of this container including extension, that is, the last - * segment of its absolute path, or the empty string if it has no segments. - * - * Here are some examples of absolute paths and the corresponding base names - * (surrounded with quotes to avoid ambiguity): - * - * - * - * - * - * - * - * - * - *
Absolute pathBase name
"/tmp/tst.py""tst.py"
"C:/Program Files (x86)""Program Files (x86)"
"/"""
"C:/"""
"D:/"""
"//FileServer/"""
- */ - string getBaseName() { - result = getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1) - } - - /** - * Gets the extension of this container, that is, the suffix of its base name - * after the last dot character, if any. - * - * In particular, - * - * - if the name does not include a dot, there is no extension, so this - * predicate has no result; - * - if the name ends in a dot, the extension is the empty string; - * - if the name contains multiple dots, the extension follows the last dot. - * - * Here are some examples of absolute paths and the corresponding extensions - * (surrounded with quotes to avoid ambiguity): - * - * - * - * - * - * - * - * - *
Absolute pathExtension
"/tmp/tst.py""py"
"/tmp/.gitignore""gitignore"
"/bin/bash"not defined
"/tmp/tst2."""
"/tmp/x.tar.gz""gz"
- */ - string getExtension() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) } - - /** - * Gets the stem of this container, that is, the prefix of its base name up to - * (but not including) the last dot character if there is one, or the entire - * base name if there is not. - * - * Here are some examples of absolute paths and the corresponding stems - * (surrounded with quotes to avoid ambiguity): - * - * - * - * - * - * - * - * - *
Absolute pathStem
"/tmp/tst.py""tst"
"/tmp/.gitignore"""
"/bin/bash""bash"
"/tmp/tst2.""tst2"
"/tmp/x.tar.gz""x.tar"
- */ - string getStem() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) } - - File getFile(string baseName) { - result = this.getAFile() and - result.getBaseName() = baseName - } - - Folder getFolder(string baseName) { - result = this.getAFolder() and - result.getBaseName() = baseName - } - - Container getParentContainer() { this = result.getAChildContainer() } - - Container getChildContainer(string baseName) { - result = this.getAChildContainer() and - result.getBaseName() = baseName - } - - /** - * Gets a URL representing the location of this container. - * - * For more information see [Providing URLs](https://help.semmle.com/QL/learn-ql/ql/locations.html#providing-urls). - */ - abstract string getURL(); - - /** Holds if this folder is on the import path. */ - predicate isImportRoot() { this.isImportRoot(_) } - - /** - * Holds if this folder is on the import path, at index `n` in the list of - * paths. The list of paths is composed of the paths passed to the extractor and - * `sys.path`. - */ - predicate isImportRoot(int n) { this.getName() = import_path_element(n) } - - /** Holds if this folder is the root folder for the standard library. */ - predicate isStdLibRoot(int major, int minor) { - major = major_version() and - minor = minor_version() and - this.isStdLibRoot() - } - - /** Holds if this folder is the root folder for the standard library. */ - predicate isStdLibRoot() { - /* - * Look for a standard lib module and find its import path - * We use `os` as it is the most likely to be imported and - * `tty` because it is small for testing. - */ - - exists(Module m | m.getName() = "os" or m.getName() = "tty" | - m.getFile().getImportRoot() = this - ) - } - - /** Gets the path element from which this container would be loaded. */ - Container getImportRoot() { - exists(int n | - result = this.getImportRoot(n) and - not exists(int m | - exists(this.getImportRoot(m)) and - m < n - ) - ) - } - - /** Gets the path element from which this container would be loaded, given the index into the list of possible paths `n`. */ - abstract Container getImportRoot(int n); + /** Gets the path element from which this container would be loaded, given the index into the list of possible paths `n`. */ + abstract Container getImportRoot(int n); } private string import_path_element(int n) { - exists(string path, string pathsep, int k | - path = get_path("extractor.path") and k = 0 - or - path = get_path("sys.path") and k = count(get_path("extractor.path").splitAt(pathsep)) - | - py_flags_versioned("os.pathsep", pathsep, _) and - result = path.splitAt(pathsep, n - k).replaceAll("\\", "/") - ) + exists(string path, string pathsep, int k | + path = get_path("extractor.path") and k = 0 + or + path = get_path("sys.path") and k = count(get_path("extractor.path").splitAt(pathsep)) + | + py_flags_versioned("os.pathsep", pathsep, _) and + result = path.splitAt(pathsep, n - k).replaceAll("\\", "/") + ) } private string get_path(string name) { py_flags_versioned(name, result, _) } class Location extends @location { - /** Gets the file for this location */ - File getFile() { result = this.getPath() } + /** Gets the file for this location */ + File getFile() { result = this.getPath() } - private Container getPath() { - locations_default(this, result, _, _, _, _) - or - exists(Module m | locations_ast(this, m, _, _, _, _) | result = m.getPath()) - } + private Container getPath() { + locations_default(this, result, _, _, _, _) + or + exists(Module m | locations_ast(this, m, _, _, _, _) | result = m.getPath()) + } - /** Gets the start line of this location */ - int getStartLine() { - locations_default(this, _, result, _, _, _) or - locations_ast(this, _, result, _, _, _) - } + /** Gets the start line of this location */ + int getStartLine() { + locations_default(this, _, result, _, _, _) or + locations_ast(this, _, result, _, _, _) + } - /** Gets the start column of this location */ - int getStartColumn() { - locations_default(this, _, _, result, _, _) or - locations_ast(this, _, _, result, _, _) - } + /** Gets the start column of this location */ + int getStartColumn() { + locations_default(this, _, _, result, _, _) or + locations_ast(this, _, _, result, _, _) + } - /** Gets the end line of this location */ - int getEndLine() { - locations_default(this, _, _, _, result, _) or - locations_ast(this, _, _, _, result, _) - } + /** Gets the end line of this location */ + int getEndLine() { + locations_default(this, _, _, _, result, _) or + locations_ast(this, _, _, _, result, _) + } - /** Gets the end column of this location */ - int getEndColumn() { - locations_default(this, _, _, _, _, result) or - locations_ast(this, _, _, _, _, result) - } + /** Gets the end column of this location */ + int getEndColumn() { + locations_default(this, _, _, _, _, result) or + locations_ast(this, _, _, _, _, result) + } - /** Gets a textual representation of this element. */ - string toString() { - result = this.getPath().getAbsolutePath() + ":" + this.getStartLine().toString() - } + /** Gets a textual representation of this element. */ + string toString() { + result = this.getPath().getAbsolutePath() + ":" + this.getStartLine().toString() + } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { exists(File f | f.getAbsolutePath() = filepath | - locations_default(this, f, startline, startcolumn, endline, endcolumn) - or - exists(Module m | m.getFile() = f | locations_ast(this, m, startline, startcolumn, endline, endcolumn)) - ) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + exists(File f | f.getAbsolutePath() = filepath | + locations_default(this, f, startline, startcolumn, endline, endcolumn) + or + exists(Module m | m.getFile() = f | + locations_ast(this, m, startline, startcolumn, endline, endcolumn) + ) + ) + } } /** A non-empty line in the source code */ class Line extends @py_line { - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { exists(Module m | - m.getFile().getAbsolutePath() = filepath and - endline = startline and - startcolumn = 1 and - py_line_lengths(this, m, startline, endcolumn) - ) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + exists(Module m | + m.getFile().getAbsolutePath() = filepath and + endline = startline and + startcolumn = 1 and + py_line_lengths(this, m, startline, endcolumn) + ) + } - /** Gets a textual representation of this element. */ - string toString() { - exists(Module m | py_line_lengths(this, m, _, _) | - result = m.getFile().getShortName() + ":" + this.getLineNumber().toString() - ) - } + /** Gets a textual representation of this element. */ + string toString() { + exists(Module m | py_line_lengths(this, m, _, _) | + result = m.getFile().getShortName() + ":" + this.getLineNumber().toString() + ) + } - /** Gets the line number of this line */ - int getLineNumber() { py_line_lengths(this, _, result, _) } + /** Gets the line number of this line */ + int getLineNumber() { py_line_lengths(this, _, result, _) } - /** Gets the length of this line */ - int getLength() { py_line_lengths(this, _, _, result) } + /** Gets the length of this line */ + int getLength() { py_line_lengths(this, _, _, result) } - /** Gets the file for this line */ - Module getModule() { py_line_lengths(this, result, _, _) } + /** Gets the file for this line */ + Module getModule() { py_line_lengths(this, result, _, _) } } /** @@ -456,12 +460,12 @@ class Line extends @py_line { * much information about that module will be lost */ class SyntaxError extends Location { - SyntaxError() { py_syntax_error_versioned(this, _, major_version().toString()) } + SyntaxError() { py_syntax_error_versioned(this, _, major_version().toString()) } - override string toString() { result = "Syntax Error" } + override string toString() { result = "Syntax Error" } - /** Gets the message corresponding to this syntax error */ - string getMessage() { py_syntax_error_versioned(this, result, major_version().toString()) } + /** Gets the message corresponding to this syntax error */ + string getMessage() { py_syntax_error_versioned(this, result, major_version().toString()) } } /** @@ -469,10 +473,10 @@ class SyntaxError extends Location { * much information about that module will be lost */ class EncodingError extends SyntaxError { - EncodingError() { - /* Leave spaces around 'decode' in unlikely event it occurs as a name in a syntax error */ - this.getMessage().toLowerCase().matches("% decode %") - } + EncodingError() { + /* Leave spaces around 'decode' in unlikely event it occurs as a name in a syntax error */ + this.getMessage().toLowerCase().matches("% decode %") + } - override string toString() { result = "Encoding Error" } + override string toString() { result = "Encoding Error" } } diff --git a/python/ql/src/semmle/python/Flow.qll b/python/ql/src/semmle/python/Flow.qll index 08d0504f398..e68800022ed 100755 --- a/python/ql/src/semmle/python/Flow.qll +++ b/python/ql/src/semmle/python/Flow.qll @@ -13,11 +13,11 @@ private import semmle.python.pointsto.PointsTo */ private predicate augstore(ControlFlowNode load, ControlFlowNode store) { - exists(Expr load_store | exists(AugAssign aa | aa.getTarget() = load_store) | - toAst(load) = load_store and - toAst(store) = load_store and - load.strictlyDominates(store) - ) + exists(Expr load_store | exists(AugAssign aa | aa.getTarget() = load_store) | + toAst(load) = load_store and + toAst(store) = load_store and + load.strictlyDominates(store) + ) } /** A non-dispatched getNode() to avoid negative recursion issues */ @@ -29,306 +29,306 @@ private AstNode toAst(ControlFlowNode n) { py_flow_bb_node(n, result, _, _) } * Edges between control flow nodes include exceptional as well as normal control flow. */ class ControlFlowNode extends @py_flow_node { - /** Whether this control flow node is a load (including those in augmented assignments) */ - predicate isLoad() { - exists(Expr e | e = toAst(this) | py_expr_contexts(_, 3, e) and not augstore(_, this)) - } + /** Whether this control flow node is a load (including those in augmented assignments) */ + predicate isLoad() { + exists(Expr e | e = toAst(this) | py_expr_contexts(_, 3, e) and not augstore(_, this)) + } - /** Whether this control flow node is a store (including those in augmented assignments) */ - predicate isStore() { - exists(Expr e | e = toAst(this) | py_expr_contexts(_, 5, e) or augstore(_, this)) - } + /** Whether this control flow node is a store (including those in augmented assignments) */ + predicate isStore() { + exists(Expr e | e = toAst(this) | py_expr_contexts(_, 5, e) or augstore(_, this)) + } - /** Whether this control flow node is a delete */ - predicate isDelete() { exists(Expr e | e = toAst(this) | py_expr_contexts(_, 2, e)) } + /** Whether this control flow node is a delete */ + predicate isDelete() { exists(Expr e | e = toAst(this) | py_expr_contexts(_, 2, e)) } - /** Whether this control flow node is a parameter */ - predicate isParameter() { exists(Expr e | e = toAst(this) | py_expr_contexts(_, 4, e)) } + /** Whether this control flow node is a parameter */ + predicate isParameter() { exists(Expr e | e = toAst(this) | py_expr_contexts(_, 4, e)) } - /** Whether this control flow node is a store in an augmented assignment */ - predicate isAugStore() { augstore(_, this) } + /** Whether this control flow node is a store in an augmented assignment */ + predicate isAugStore() { augstore(_, this) } - /** Whether this control flow node is a load in an augmented assignment */ - predicate isAugLoad() { augstore(this, _) } + /** Whether this control flow node is a load in an augmented assignment */ + predicate isAugLoad() { augstore(this, _) } - /** Whether this flow node corresponds to a literal */ - predicate isLiteral() { - toAst(this) instanceof Bytes - or - toAst(this) instanceof Dict - or - toAst(this) instanceof DictComp - or - toAst(this) instanceof Set - or - toAst(this) instanceof SetComp - or - toAst(this) instanceof Ellipsis - or - toAst(this) instanceof GeneratorExp - or - toAst(this) instanceof Lambda - or - toAst(this) instanceof ListComp - or - toAst(this) instanceof List - or - toAst(this) instanceof Num - or - toAst(this) instanceof Tuple - or - toAst(this) instanceof Unicode - or - toAst(this) instanceof NameConstant - } + /** Whether this flow node corresponds to a literal */ + predicate isLiteral() { + toAst(this) instanceof Bytes + or + toAst(this) instanceof Dict + or + toAst(this) instanceof DictComp + or + toAst(this) instanceof Set + or + toAst(this) instanceof SetComp + or + toAst(this) instanceof Ellipsis + or + toAst(this) instanceof GeneratorExp + or + toAst(this) instanceof Lambda + or + toAst(this) instanceof ListComp + or + toAst(this) instanceof List + or + toAst(this) instanceof Num + or + toAst(this) instanceof Tuple + or + toAst(this) instanceof Unicode + or + toAst(this) instanceof NameConstant + } - /** Use NameNode.isLoad() instead */ - deprecated predicate isUse() { toAst(this) instanceof Name and this.isLoad() } + /** Use NameNode.isLoad() instead */ + deprecated predicate isUse() { toAst(this) instanceof Name and this.isLoad() } - /** Use NameNode.isStore() */ - deprecated predicate isDefinition() { toAst(this) instanceof Name and this.isStore() } + /** Use NameNode.isStore() */ + deprecated predicate isDefinition() { toAst(this) instanceof Name and this.isStore() } - /** Whether this flow node corresponds to an attribute expression */ - predicate isAttribute() { toAst(this) instanceof Attribute } + /** Whether this flow node corresponds to an attribute expression */ + predicate isAttribute() { toAst(this) instanceof Attribute } - /** Use AttrNode.isLoad() instead */ - deprecated predicate isAttributeLoad() { toAst(this) instanceof Attribute and this.isLoad() } + /** Use AttrNode.isLoad() instead */ + deprecated predicate isAttributeLoad() { toAst(this) instanceof Attribute and this.isLoad() } - /** Use AttrNode.isStore() instead */ - deprecated predicate isAttributeStore() { toAst(this) instanceof Attribute and this.isStore() } + /** Use AttrNode.isStore() instead */ + deprecated predicate isAttributeStore() { toAst(this) instanceof Attribute and this.isStore() } - /** Whether this flow node corresponds to an subscript expression */ - predicate isSubscript() { toAst(this) instanceof Subscript } + /** Whether this flow node corresponds to an subscript expression */ + predicate isSubscript() { toAst(this) instanceof Subscript } - /** Use SubscriptNode.isLoad() instead */ - deprecated predicate isSubscriptLoad() { toAst(this) instanceof Subscript and this.isLoad() } + /** Use SubscriptNode.isLoad() instead */ + deprecated predicate isSubscriptLoad() { toAst(this) instanceof Subscript and this.isLoad() } - /** Use SubscriptNode.isStore() instead */ - deprecated predicate isSubscriptStore() { toAst(this) instanceof Subscript and this.isStore() } + /** Use SubscriptNode.isStore() instead */ + deprecated predicate isSubscriptStore() { toAst(this) instanceof Subscript and this.isStore() } - /** Whether this flow node corresponds to an import member */ - predicate isImportMember() { toAst(this) instanceof ImportMember } + /** Whether this flow node corresponds to an import member */ + predicate isImportMember() { toAst(this) instanceof ImportMember } - /** Whether this flow node corresponds to a call */ - predicate isCall() { toAst(this) instanceof Call } + /** Whether this flow node corresponds to a call */ + predicate isCall() { toAst(this) instanceof Call } - /** Whether this flow node is the first in a module */ - predicate isModuleEntry() { this.isEntryNode() and toAst(this) instanceof Module } + /** Whether this flow node is the first in a module */ + predicate isModuleEntry() { this.isEntryNode() and toAst(this) instanceof Module } - /** Whether this flow node corresponds to an import */ - predicate isImport() { toAst(this) instanceof ImportExpr } + /** Whether this flow node corresponds to an import */ + predicate isImport() { toAst(this) instanceof ImportExpr } - /** Whether this flow node corresponds to a conditional expression */ - predicate isIfExp() { toAst(this) instanceof IfExp } + /** Whether this flow node corresponds to a conditional expression */ + predicate isIfExp() { toAst(this) instanceof IfExp } - /** Whether this flow node corresponds to a function definition expression */ - predicate isFunction() { toAst(this) instanceof FunctionExpr } + /** Whether this flow node corresponds to a function definition expression */ + predicate isFunction() { toAst(this) instanceof FunctionExpr } - /** Whether this flow node corresponds to a class definition expression */ - predicate isClass() { toAst(this) instanceof ClassExpr } + /** Whether this flow node corresponds to a class definition expression */ + predicate isClass() { toAst(this) instanceof ClassExpr } - /** Gets a predecessor of this flow node */ - ControlFlowNode getAPredecessor() { this = result.getASuccessor() } + /** Gets a predecessor of this flow node */ + ControlFlowNode getAPredecessor() { this = result.getASuccessor() } - /** Gets a successor of this flow node */ - ControlFlowNode getASuccessor() { py_successors(this, result) } + /** Gets a successor of this flow node */ + ControlFlowNode getASuccessor() { py_successors(this, result) } - /** Gets the immediate dominator of this flow node */ - ControlFlowNode getImmediateDominator() { py_idoms(this, result) } + /** Gets the immediate dominator of this flow node */ + ControlFlowNode getImmediateDominator() { py_idoms(this, result) } - /** Gets the syntactic element corresponding to this flow node */ - AstNode getNode() { py_flow_bb_node(this, result, _, _) } + /** Gets the syntactic element corresponding to this flow node */ + AstNode getNode() { py_flow_bb_node(this, result, _, _) } - /** Gets a textual representation of this element. */ - string toString() { - exists(Scope s | s.getEntryNode() = this | result = "Entry node for " + s.toString()) - or - exists(Scope s | s.getANormalExit() = this | result = "Exit node for " + s.toString()) - or - not exists(Scope s | s.getEntryNode() = this or s.getANormalExit() = this) and - result = "ControlFlowNode for " + this.getNode().toString() - } + /** Gets a textual representation of this element. */ + string toString() { + exists(Scope s | s.getEntryNode() = this | result = "Entry node for " + s.toString()) + or + exists(Scope s | s.getANormalExit() = this | result = "Exit node for " + s.toString()) + or + not exists(Scope s | s.getEntryNode() = this or s.getANormalExit() = this) and + result = "ControlFlowNode for " + this.getNode().toString() + } - /** Gets the location of this ControlFlowNode */ - Location getLocation() { result = this.getNode().getLocation() } + /** Gets the location of this ControlFlowNode */ + Location getLocation() { result = this.getNode().getLocation() } - /** Whether this flow node is the first in its scope */ - predicate isEntryNode() { py_scope_flow(this, _, -1) } + /** Whether this flow node is the first in its scope */ + predicate isEntryNode() { py_scope_flow(this, _, -1) } - /** The value that this ControlFlowNode points-to. */ - predicate pointsTo(Value value) { this.pointsTo(_, value, _) } + /** The value that this ControlFlowNode points-to. */ + predicate pointsTo(Value value) { this.pointsTo(_, value, _) } - /** Gets the value that this ControlFlowNode points-to. */ - Value pointsTo() { this.pointsTo(_, result, _) } + /** Gets the value that this ControlFlowNode points-to. */ + Value pointsTo() { this.pointsTo(_, result, _) } - /** Gets a value that this ControlFlowNode may points-to. */ - Value inferredValue() { this.pointsTo(_, result, _) } + /** Gets a value that this ControlFlowNode may points-to. */ + Value inferredValue() { this.pointsTo(_, result, _) } - /** The value and origin that this ControlFlowNode points-to. */ - predicate pointsTo(Value value, ControlFlowNode origin) { this.pointsTo(_, value, origin) } + /** The value and origin that this ControlFlowNode points-to. */ + predicate pointsTo(Value value, ControlFlowNode origin) { this.pointsTo(_, value, origin) } - /** The value and origin that this ControlFlowNode points-to, given the context. */ - predicate pointsTo(Context context, Value value, ControlFlowNode origin) { - PointsTo::pointsTo(this, context, value, origin) - } + /** The value and origin that this ControlFlowNode points-to, given the context. */ + predicate pointsTo(Context context, Value value, ControlFlowNode origin) { + PointsTo::pointsTo(this, context, value, origin) + } - /** - * Gets what this flow node might "refer-to". Performs a combination of localized (intra-procedural) points-to - * analysis and global module-level analysis. This points-to analysis favours precision over recall. It is highly - * precise, but may not provide information for a significant number of flow-nodes. - * If the class is unimportant then use `refersTo(value)` or `refersTo(value, origin)` instead. - */ - pragma[nomagic] - predicate refersTo(Object obj, ClassObject cls, ControlFlowNode origin) { - this.refersTo(_, obj, cls, origin) - } + /** + * Gets what this flow node might "refer-to". Performs a combination of localized (intra-procedural) points-to + * analysis and global module-level analysis. This points-to analysis favours precision over recall. It is highly + * precise, but may not provide information for a significant number of flow-nodes. + * If the class is unimportant then use `refersTo(value)` or `refersTo(value, origin)` instead. + */ + pragma[nomagic] + predicate refersTo(Object obj, ClassObject cls, ControlFlowNode origin) { + this.refersTo(_, obj, cls, origin) + } - /** Gets what this expression might "refer-to" in the given `context`. */ - pragma[nomagic] - predicate refersTo(Context context, Object obj, ClassObject cls, ControlFlowNode origin) { - not obj = unknownValue() and - not cls = theUnknownType() and - PointsTo::points_to(this, context, obj, cls, origin) - } + /** Gets what this expression might "refer-to" in the given `context`. */ + pragma[nomagic] + predicate refersTo(Context context, Object obj, ClassObject cls, ControlFlowNode origin) { + not obj = unknownValue() and + not cls = theUnknownType() and + PointsTo::points_to(this, context, obj, cls, origin) + } - /** - * Whether this flow node might "refer-to" to `value` which is from `origin` - * Unlike `this.refersTo(value, _, origin)` this predicate includes results - * where the class cannot be inferred. - */ - pragma[nomagic] - predicate refersTo(Object obj, ControlFlowNode origin) { - not obj = unknownValue() and - PointsTo::points_to(this, _, obj, _, origin) - } + /** + * Whether this flow node might "refer-to" to `value` which is from `origin` + * Unlike `this.refersTo(value, _, origin)` this predicate includes results + * where the class cannot be inferred. + */ + pragma[nomagic] + predicate refersTo(Object obj, ControlFlowNode origin) { + not obj = unknownValue() and + PointsTo::points_to(this, _, obj, _, origin) + } - /** Equivalent to `this.refersTo(value, _)` */ - predicate refersTo(Object obj) { this.refersTo(obj, _) } + /** Equivalent to `this.refersTo(value, _)` */ + predicate refersTo(Object obj) { this.refersTo(obj, _) } - /** Gets the basic block containing this flow node */ - BasicBlock getBasicBlock() { result.contains(this) } + /** Gets the basic block containing this flow node */ + BasicBlock getBasicBlock() { result.contains(this) } - /** Gets the scope containing this flow node */ - Scope getScope() { - if this.getNode() instanceof Scope - then - /* Entry or exit node */ - result = this.getNode() - else result = this.getNode().getScope() - } + /** Gets the scope containing this flow node */ + Scope getScope() { + if this.getNode() instanceof Scope + then + /* Entry or exit node */ + result = this.getNode() + else result = this.getNode().getScope() + } - /** Gets the enclosing module */ - Module getEnclosingModule() { result = this.getScope().getEnclosingModule() } + /** Gets the enclosing module */ + Module getEnclosingModule() { result = this.getScope().getEnclosingModule() } - /** Gets a successor for this node if the relevant condition is True. */ - ControlFlowNode getATrueSuccessor() { - result = this.getASuccessor() and - py_true_successors(this, result) - } + /** Gets a successor for this node if the relevant condition is True. */ + ControlFlowNode getATrueSuccessor() { + result = this.getASuccessor() and + py_true_successors(this, result) + } - /** Gets a successor for this node if the relevant condition is False. */ - ControlFlowNode getAFalseSuccessor() { - result = this.getASuccessor() and - py_false_successors(this, result) - } + /** Gets a successor for this node if the relevant condition is False. */ + ControlFlowNode getAFalseSuccessor() { + result = this.getASuccessor() and + py_false_successors(this, result) + } - /** Gets a successor for this node if an exception is raised. */ - ControlFlowNode getAnExceptionalSuccessor() { - result = this.getASuccessor() and - py_exception_successors(this, result) - } + /** Gets a successor for this node if an exception is raised. */ + ControlFlowNode getAnExceptionalSuccessor() { + result = this.getASuccessor() and + py_exception_successors(this, result) + } - /** Gets a successor for this node if no exception is raised. */ - ControlFlowNode getANormalSuccessor() { - result = this.getASuccessor() and - not py_exception_successors(this, result) - } + /** Gets a successor for this node if no exception is raised. */ + ControlFlowNode getANormalSuccessor() { + result = this.getASuccessor() and + not py_exception_successors(this, result) + } - /** Whether the scope may be exited as a result of this node raising an exception */ - predicate isExceptionalExit(Scope s) { py_scope_flow(this, s, 1) } + /** Whether the scope may be exited as a result of this node raising an exception */ + predicate isExceptionalExit(Scope s) { py_scope_flow(this, s, 1) } - /** Whether this node is a normal (non-exceptional) exit */ - predicate isNormalExit() { py_scope_flow(this, _, 0) or py_scope_flow(this, _, 2) } + /** Whether this node is a normal (non-exceptional) exit */ + predicate isNormalExit() { py_scope_flow(this, _, 0) or py_scope_flow(this, _, 2) } - /** Whether it is unlikely that this ControlFlowNode can be reached */ - predicate unlikelyReachable() { - not start_bb_likely_reachable(this.getBasicBlock()) - or - exists(BasicBlock b | - start_bb_likely_reachable(b) and - not end_bb_likely_reachable(b) and - // If there is an unlikely successor edge earlier in the BB - // than this node, then this node must be unreachable. - exists(ControlFlowNode p, int i, int j | - p.(RaisingNode).unlikelySuccessor(_) and - p = b.getNode(i) and - this = b.getNode(j) and - i < j - ) - ) - } + /** Whether it is unlikely that this ControlFlowNode can be reached */ + predicate unlikelyReachable() { + not start_bb_likely_reachable(this.getBasicBlock()) + or + exists(BasicBlock b | + start_bb_likely_reachable(b) and + not end_bb_likely_reachable(b) and + // If there is an unlikely successor edge earlier in the BB + // than this node, then this node must be unreachable. + exists(ControlFlowNode p, int i, int j | + p.(RaisingNode).unlikelySuccessor(_) and + p = b.getNode(i) and + this = b.getNode(j) and + i < j + ) + ) + } - /** - * Check whether this control-flow node has complete points-to information. - * This would mean that the analysis managed to infer an over approximation - * of possible values at runtime. - */ - predicate hasCompletePointsToSet() { - // If the tracking failed, then `this` will be its own "origin". In that - // case, we want to exclude nodes for which there is also a different - // origin, as that would indicate that some paths failed and some did not. - this.refersTo(_, _, this) and - not exists(ControlFlowNode other | other != this and this.refersTo(_, _, other)) - or - // If `this` is a use of a variable, then we must have complete points-to - // for that variable. - exists(SsaVariable v | v.getAUse() = this | varHasCompletePointsToSet(v)) - } + /** + * Check whether this control-flow node has complete points-to information. + * This would mean that the analysis managed to infer an over approximation + * of possible values at runtime. + */ + predicate hasCompletePointsToSet() { + // If the tracking failed, then `this` will be its own "origin". In that + // case, we want to exclude nodes for which there is also a different + // origin, as that would indicate that some paths failed and some did not. + this.refersTo(_, _, this) and + not exists(ControlFlowNode other | other != this and this.refersTo(_, _, other)) + or + // If `this` is a use of a variable, then we must have complete points-to + // for that variable. + exists(SsaVariable v | v.getAUse() = this | varHasCompletePointsToSet(v)) + } - /** Whether this strictly dominates other. */ - pragma[inline] - predicate strictlyDominates(ControlFlowNode other) { - // This predicate is gigantic, so it must be inlined. - // About 1.4 billion tuples for OpenStack Cinder. - this.getBasicBlock().strictlyDominates(other.getBasicBlock()) - or - exists(BasicBlock b, int i, int j | this = b.getNode(i) and other = b.getNode(j) and i < j) - } + /** Whether this strictly dominates other. */ + pragma[inline] + predicate strictlyDominates(ControlFlowNode other) { + // This predicate is gigantic, so it must be inlined. + // About 1.4 billion tuples for OpenStack Cinder. + this.getBasicBlock().strictlyDominates(other.getBasicBlock()) + or + exists(BasicBlock b, int i, int j | this = b.getNode(i) and other = b.getNode(j) and i < j) + } - /** - * Whether this dominates other. - * Note that all nodes dominate themselves. - */ - pragma[inline] - predicate dominates(ControlFlowNode other) { - // This predicate is gigantic, so it must be inlined. - this.getBasicBlock().strictlyDominates(other.getBasicBlock()) - or - exists(BasicBlock b, int i, int j | this = b.getNode(i) and other = b.getNode(j) and i <= j) - } + /** + * Whether this dominates other. + * Note that all nodes dominate themselves. + */ + pragma[inline] + predicate dominates(ControlFlowNode other) { + // This predicate is gigantic, so it must be inlined. + this.getBasicBlock().strictlyDominates(other.getBasicBlock()) + or + exists(BasicBlock b, int i, int j | this = b.getNode(i) and other = b.getNode(j) and i <= j) + } - /** Whether this strictly reaches other. */ - pragma[inline] - predicate strictlyReaches(ControlFlowNode other) { - // This predicate is gigantic, even larger than strictlyDominates, - // so it must be inlined. - this.getBasicBlock().strictlyReaches(other.getBasicBlock()) - or - exists(BasicBlock b, int i, int j | this = b.getNode(i) and other = b.getNode(j) and i < j) - } + /** Whether this strictly reaches other. */ + pragma[inline] + predicate strictlyReaches(ControlFlowNode other) { + // This predicate is gigantic, even larger than strictlyDominates, + // so it must be inlined. + this.getBasicBlock().strictlyReaches(other.getBasicBlock()) + or + exists(BasicBlock b, int i, int j | this = b.getNode(i) and other = b.getNode(j) and i < j) + } - /* Holds if this CFG node is a branch */ - predicate isBranch() { py_true_successors(this, _) or py_false_successors(this, _) } + /* Holds if this CFG node is a branch */ + predicate isBranch() { py_true_successors(this, _) or py_false_successors(this, _) } - ControlFlowNode getAChild() { result = this.getExprChild(this.getBasicBlock()) } + ControlFlowNode getAChild() { result = this.getExprChild(this.getBasicBlock()) } - /* join-ordering helper for `getAChild() */ - pragma[noinline] - private ControlFlowNode getExprChild(BasicBlock dom) { - this.getNode().(Expr).getAChildNode() = result.getNode() and - result.getBasicBlock().dominates(dom) and - not this instanceof UnaryExprNode - } + /* join-ordering helper for `getAChild() */ + pragma[noinline] + private ControlFlowNode getExprChild(BasicBlock dom) { + this.getNode().(Expr).getAChildNode() = result.getNode() and + result.getBasicBlock().dominates(dom) and + not this instanceof UnaryExprNode + } } /* @@ -338,7 +338,7 @@ class ControlFlowNode extends @py_flow_node { */ private class AnyNode extends ControlFlowNode { - override AstNode getNode() { result = super.getNode() } + override AstNode getNode() { result = super.getNode() } } /** @@ -347,300 +347,300 @@ private class AnyNode extends ControlFlowNode { * of possible values at runtime. */ private predicate varHasCompletePointsToSet(SsaVariable var) { - // Global variables may be modified non-locally or concurrently. - not var.getVariable() instanceof GlobalVariable and - ( - // If we have complete points-to information on the definition of - // this variable, then the variable has complete information. - var.getDefinition().(DefinitionNode).getValue().hasCompletePointsToSet() - or - // If this variable is a phi output, then we have complete - // points-to information about it if all phi inputs had complete - // information. - forex(SsaVariable phiInput | phiInput = var.getAPhiInput() | - varHasCompletePointsToSet(phiInput) - ) + // Global variables may be modified non-locally or concurrently. + not var.getVariable() instanceof GlobalVariable and + ( + // If we have complete points-to information on the definition of + // this variable, then the variable has complete information. + var.getDefinition().(DefinitionNode).getValue().hasCompletePointsToSet() + or + // If this variable is a phi output, then we have complete + // points-to information about it if all phi inputs had complete + // information. + forex(SsaVariable phiInput | phiInput = var.getAPhiInput() | + varHasCompletePointsToSet(phiInput) ) + ) } /** A control flow node corresponding to a call expression, such as `func(...)` */ class CallNode extends ControlFlowNode { - CallNode() { toAst(this) instanceof Call } + CallNode() { toAst(this) instanceof Call } - /** Gets the flow node corresponding to the function expression for the call corresponding to this flow node */ - ControlFlowNode getFunction() { - exists(Call c | - this.getNode() = c and - c.getFunc() = result.getNode() and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the flow node corresponding to the function expression for the call corresponding to this flow node */ + ControlFlowNode getFunction() { + exists(Call c | + this.getNode() = c and + c.getFunc() = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** Gets the flow node corresponding to the nth argument of the call corresponding to this flow node */ - ControlFlowNode getArg(int n) { - exists(Call c | - this.getNode() = c and - c.getArg(n) = result.getNode() and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the flow node corresponding to the nth argument of the call corresponding to this flow node */ + ControlFlowNode getArg(int n) { + exists(Call c | + this.getNode() = c and + c.getArg(n) = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** Gets the flow node corresponding to the named argument of the call corresponding to this flow node */ - ControlFlowNode getArgByName(string name) { - exists(Call c, Keyword k | - this.getNode() = c and - k = c.getAKeyword() and - k.getValue() = result.getNode() and - k.getArg() = name and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the flow node corresponding to the named argument of the call corresponding to this flow node */ + ControlFlowNode getArgByName(string name) { + exists(Call c, Keyword k | + this.getNode() = c and + k = c.getAKeyword() and + k.getValue() = result.getNode() and + k.getArg() = name and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** Gets the flow node corresponding to an argument of the call corresponding to this flow node */ - ControlFlowNode getAnArg() { - exists(int n | result = this.getArg(n)) - or - exists(string name | result = this.getArgByName(name)) - } + /** Gets the flow node corresponding to an argument of the call corresponding to this flow node */ + ControlFlowNode getAnArg() { + exists(int n | result = this.getArg(n)) + or + exists(string name | result = this.getArgByName(name)) + } - override Call getNode() { result = super.getNode() } + override Call getNode() { result = super.getNode() } - predicate isDecoratorCall() { - this.isClassDecoratorCall() - or - this.isFunctionDecoratorCall() - } + predicate isDecoratorCall() { + this.isClassDecoratorCall() + or + this.isFunctionDecoratorCall() + } - predicate isClassDecoratorCall() { - exists(ClassExpr cls | this.getNode() = cls.getADecoratorCall()) - } + predicate isClassDecoratorCall() { + exists(ClassExpr cls | this.getNode() = cls.getADecoratorCall()) + } - predicate isFunctionDecoratorCall() { - exists(FunctionExpr func | this.getNode() = func.getADecoratorCall()) - } + predicate isFunctionDecoratorCall() { + exists(FunctionExpr func | this.getNode() = func.getADecoratorCall()) + } - /** Gets the tuple (*) argument of this call, provided there is exactly one. */ - ControlFlowNode getStarArg() { - result.getNode() = this.getNode().getStarArg() and - result.getBasicBlock().dominates(this.getBasicBlock()) - } + /** Gets the tuple (*) argument of this call, provided there is exactly one. */ + ControlFlowNode getStarArg() { + result.getNode() = this.getNode().getStarArg() and + result.getBasicBlock().dominates(this.getBasicBlock()) + } } /** A control flow corresponding to an attribute expression, such as `value.attr` */ class AttrNode extends ControlFlowNode { - AttrNode() { toAst(this) instanceof Attribute } + AttrNode() { toAst(this) instanceof Attribute } - /** Gets the flow node corresponding to the object of the attribute expression corresponding to this flow node */ - ControlFlowNode getObject() { - exists(Attribute a | - this.getNode() = a and - a.getObject() = result.getNode() and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the flow node corresponding to the object of the attribute expression corresponding to this flow node */ + ControlFlowNode getObject() { + exists(Attribute a | + this.getNode() = a and + a.getObject() = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** Use getObject() instead */ - deprecated ControlFlowNode getValue() { result = this.getObject() } + /** Use getObject() instead */ + deprecated ControlFlowNode getValue() { result = this.getObject() } - /** Use getObject(name) instead */ - deprecated ControlFlowNode getValue(string name) { result = this.getObject(name) } + /** Use getObject(name) instead */ + deprecated ControlFlowNode getValue(string name) { result = this.getObject(name) } - /** - * Gets the flow node corresponding to the object of the attribute expression corresponding to this flow node, - * with the matching name - */ - ControlFlowNode getObject(string name) { - exists(Attribute a | - this.getNode() = a and - a.getObject() = result.getNode() and - a.getName() = name and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** + * Gets the flow node corresponding to the object of the attribute expression corresponding to this flow node, + * with the matching name + */ + ControlFlowNode getObject(string name) { + exists(Attribute a | + this.getNode() = a and + a.getObject() = result.getNode() and + a.getName() = name and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** Gets the attribute name of the attribute expression corresponding to this flow node */ - string getName() { exists(Attribute a | this.getNode() = a and a.getName() = result) } + /** Gets the attribute name of the attribute expression corresponding to this flow node */ + string getName() { exists(Attribute a | this.getNode() = a and a.getName() = result) } - override Attribute getNode() { result = super.getNode() } + override Attribute getNode() { result = super.getNode() } } /** A control flow node corresponding to a `from ... import ...` expression */ class ImportMemberNode extends ControlFlowNode { - ImportMemberNode() { toAst(this) instanceof ImportMember } + ImportMemberNode() { toAst(this) instanceof ImportMember } - /** - * Gets the flow node corresponding to the module in the import-member expression corresponding to this flow node, - * with the matching name - */ - ControlFlowNode getModule(string name) { - exists(ImportMember i | this.getNode() = i and i.getModule() = result.getNode() | - i.getName() = name and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** + * Gets the flow node corresponding to the module in the import-member expression corresponding to this flow node, + * with the matching name + */ + ControlFlowNode getModule(string name) { + exists(ImportMember i | this.getNode() = i and i.getModule() = result.getNode() | + i.getName() = name and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - override ImportMember getNode() { result = super.getNode() } + override ImportMember getNode() { result = super.getNode() } } /** A control flow node corresponding to an artificial expression representing an import */ class ImportExprNode extends ControlFlowNode { - ImportExprNode() { toAst(this) instanceof ImportExpr } + ImportExprNode() { toAst(this) instanceof ImportExpr } - override ImportExpr getNode() { result = super.getNode() } + override ImportExpr getNode() { result = super.getNode() } } /** A control flow node corresponding to a `from ... import *` statement */ class ImportStarNode extends ControlFlowNode { - ImportStarNode() { toAst(this) instanceof ImportStar } + ImportStarNode() { toAst(this) instanceof ImportStar } - /** Gets the flow node corresponding to the module in the import-star corresponding to this flow node */ - ControlFlowNode getModule() { - exists(ImportStar i | this.getNode() = i and i.getModuleExpr() = result.getNode() | - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the flow node corresponding to the module in the import-star corresponding to this flow node */ + ControlFlowNode getModule() { + exists(ImportStar i | this.getNode() = i and i.getModuleExpr() = result.getNode() | + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - override ImportStar getNode() { result = super.getNode() } + override ImportStar getNode() { result = super.getNode() } } /** A control flow node corresponding to a subscript expression, such as `value[slice]` */ class SubscriptNode extends ControlFlowNode { - SubscriptNode() { toAst(this) instanceof Subscript } + SubscriptNode() { toAst(this) instanceof Subscript } - /** - * DEPRECATED: Use `getObject()` instead. - * This will be formally deprecated before the end 2018 and removed in 2019. - */ - deprecated ControlFlowNode getValue() { - exists(Subscript s | - this.getNode() = s and - s.getObject() = result.getNode() and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** + * DEPRECATED: Use `getObject()` instead. + * This will be formally deprecated before the end 2018 and removed in 2019. + */ + deprecated ControlFlowNode getValue() { + exists(Subscript s | + this.getNode() = s and + s.getObject() = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** flow node corresponding to the value of the sequence in a subscript operation */ - ControlFlowNode getObject() { - exists(Subscript s | - this.getNode() = s and - s.getObject() = result.getNode() and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** flow node corresponding to the value of the sequence in a subscript operation */ + ControlFlowNode getObject() { + exists(Subscript s | + this.getNode() = s and + s.getObject() = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** flow node corresponding to the index in a subscript operation */ - ControlFlowNode getIndex() { - exists(Subscript s | - this.getNode() = s and - s.getIndex() = result.getNode() and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** flow node corresponding to the index in a subscript operation */ + ControlFlowNode getIndex() { + exists(Subscript s | + this.getNode() = s and + s.getIndex() = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - override Subscript getNode() { result = super.getNode() } + override Subscript getNode() { result = super.getNode() } } /** A control flow node corresponding to a comparison operation, such as `x DeletionNode -> NameNode('b') -> AttrNode('y') -> DeletionNode`. */ class DeletionNode extends ControlFlowNode { - DeletionNode() { toAst(this) instanceof Delete } + DeletionNode() { toAst(this) instanceof Delete } - /** Gets the unique target of this deletion node. */ - ControlFlowNode getTarget() { result.getASuccessor() = this } + /** Gets the unique target of this deletion node. */ + ControlFlowNode getTarget() { result.getASuccessor() = this } } /** A control flow node corresponding to a sequence (tuple or list) literal */ abstract class SequenceNode extends ControlFlowNode { - SequenceNode() { - toAst(this) instanceof Tuple - or - toAst(this) instanceof List - } + SequenceNode() { + toAst(this) instanceof Tuple + or + toAst(this) instanceof List + } - /** Gets the control flow node for an element of this sequence */ - ControlFlowNode getAnElement() { result = this.getElement(_) } + /** Gets the control flow node for an element of this sequence */ + ControlFlowNode getAnElement() { result = this.getElement(_) } - /** Gets the control flow node for the nth element of this sequence */ - abstract ControlFlowNode getElement(int n); + /** Gets the control flow node for the nth element of this sequence */ + abstract ControlFlowNode getElement(int n); } /** A control flow node corresponding to a tuple expression such as `( 1, 3, 5, 7, 9 )` */ class TupleNode extends SequenceNode { - TupleNode() { toAst(this) instanceof Tuple } + TupleNode() { toAst(this) instanceof Tuple } - override ControlFlowNode getElement(int n) { - exists(Tuple t | this.getNode() = t and result.getNode() = t.getElt(n)) and - ( - result.getBasicBlock().dominates(this.getBasicBlock()) - or - this.getBasicBlock().dominates(result.getBasicBlock()) - ) - } + override ControlFlowNode getElement(int n) { + exists(Tuple t | this.getNode() = t and result.getNode() = t.getElt(n)) and + ( + result.getBasicBlock().dominates(this.getBasicBlock()) + or + this.getBasicBlock().dominates(result.getBasicBlock()) + ) + } } /** A control flow node corresponding to a list expression, such as `[ 1, 3, 5, 7, 9 ]` */ class ListNode extends SequenceNode { - ListNode() { toAst(this) instanceof List } + ListNode() { toAst(this) instanceof List } - override ControlFlowNode getElement(int n) { - exists(List l | this.getNode() = l and result.getNode() = l.getElt(n)) and - ( - result.getBasicBlock().dominates(this.getBasicBlock()) - or - this.getBasicBlock().dominates(result.getBasicBlock()) - ) - } + override ControlFlowNode getElement(int n) { + exists(List l | this.getNode() = l and result.getNode() = l.getElt(n)) and + ( + result.getBasicBlock().dominates(this.getBasicBlock()) + or + this.getBasicBlock().dominates(result.getBasicBlock()) + ) + } } class SetNode extends ControlFlowNode { - SetNode() { toAst(this) instanceof Set } + SetNode() { toAst(this) instanceof Set } - ControlFlowNode getAnElement() { - exists(Set s | this.getNode() = s and result.getNode() = s.getElt(_)) and - ( - result.getBasicBlock().dominates(this.getBasicBlock()) - or - this.getBasicBlock().dominates(result.getBasicBlock()) - ) - } + ControlFlowNode getAnElement() { + exists(Set s | this.getNode() = s and result.getNode() = s.getElt(_)) and + ( + result.getBasicBlock().dominates(this.getBasicBlock()) + or + this.getBasicBlock().dominates(result.getBasicBlock()) + ) + } } /** A control flow node corresponding to a dictionary literal, such as `{ 'a': 1, 'b': 2 }` */ class DictNode extends ControlFlowNode { - DictNode() { toAst(this) instanceof Dict } + DictNode() { toAst(this) instanceof Dict } - /** - * Gets a key of this dictionary literal node, for those items that have keys - * E.g, in {'a':1, **b} this returns only 'a' - */ - ControlFlowNode getAKey() { - exists(Dict d | this.getNode() = d and result.getNode() = d.getAKey()) and - result.getBasicBlock().dominates(this.getBasicBlock()) - } + /** + * Gets a key of this dictionary literal node, for those items that have keys + * E.g, in {'a':1, **b} this returns only 'a' + */ + ControlFlowNode getAKey() { + exists(Dict d | this.getNode() = d and result.getNode() = d.getAKey()) and + result.getBasicBlock().dominates(this.getBasicBlock()) + } - /** Gets a value of this dictionary literal node */ - ControlFlowNode getAValue() { - exists(Dict d | this.getNode() = d and result.getNode() = d.getAValue()) and - result.getBasicBlock().dominates(this.getBasicBlock()) - } + /** Gets a value of this dictionary literal node */ + ControlFlowNode getAValue() { + exists(Dict d | this.getNode() = d and result.getNode() = d.getAValue()) and + result.getBasicBlock().dominates(this.getBasicBlock()) + } } private AstNode assigned_value(Expr lhs) { - /* lhs = result */ - exists(Assign a | a.getATarget() = lhs and result = a.getValue()) - or - /* import result as lhs */ - exists(Alias a | a.getAsname() = lhs and result = a.getValue()) - or - /* lhs += x => result = (lhs + x) */ - exists(AugAssign a, BinaryExpr b | b = a.getOperation() and result = b and lhs = b.getLeft()) - or - /* - * ..., lhs, ... = ..., result, ... - * or - * ..., (..., lhs, ...), ... = ..., (..., result, ...), ... - */ + /* lhs = result */ + exists(Assign a | a.getATarget() = lhs and result = a.getValue()) + or + /* import result as lhs */ + exists(Alias a | a.getAsname() = lhs and result = a.getValue()) + or + /* lhs += x => result = (lhs + x) */ + exists(AugAssign a, BinaryExpr b | b = a.getOperation() and result = b and lhs = b.getLeft()) + or + /* + * ..., lhs, ... = ..., result, ... + * or + * ..., (..., lhs, ...), ... = ..., (..., result, ...), ... + */ - exists(Assign a | nested_sequence_assign(a.getATarget(), a.getValue(), lhs, result)) - or - /* for lhs in seq: => `result` is the `for` node, representing the `iter(next(seq))` operation. */ - result.(For).getTarget() = lhs + exists(Assign a | nested_sequence_assign(a.getATarget(), a.getValue(), lhs, result)) + or + /* for lhs in seq: => `result` is the `for` node, representing the `iter(next(seq))` operation. */ + result.(For).getTarget() = lhs } predicate nested_sequence_assign( - Expr left_parent, Expr right_parent, Expr left_result, Expr right_result + Expr left_parent, Expr right_parent, Expr left_result, Expr right_result ) { - exists(Assign a | - a.getATarget().getASubExpression*() = left_parent and - a.getValue().getASubExpression*() = right_parent + exists(Assign a | + a.getATarget().getASubExpression*() = left_parent and + a.getValue().getASubExpression*() = right_parent + ) and + exists(int i, Expr left_elem, Expr right_elem | + ( + left_elem = left_parent.(Tuple).getElt(i) + or + left_elem = left_parent.(List).getElt(i) ) and - exists(int i, Expr left_elem, Expr right_elem | - ( - left_elem = left_parent.(Tuple).getElt(i) - or - left_elem = left_parent.(List).getElt(i) - ) and - ( - right_elem = right_parent.(Tuple).getElt(i) - or - right_elem = right_parent.(List).getElt(i) - ) - | - left_result = left_elem and right_result = right_elem - or - nested_sequence_assign(left_elem, right_elem, left_result, right_result) + ( + right_elem = right_parent.(Tuple).getElt(i) + or + right_elem = right_parent.(List).getElt(i) ) + | + left_result = left_elem and right_result = right_elem + or + nested_sequence_assign(left_elem, right_elem, left_result, right_result) + ) } /** A flow node for a `for` statement. */ class ForNode extends ControlFlowNode { - ForNode() { toAst(this) instanceof For } + ForNode() { toAst(this) instanceof For } - override For getNode() { result = super.getNode() } + override For getNode() { result = super.getNode() } - /** Holds if this `for` statement causes iteration over `sequence` storing each step of the iteration in `target` */ - predicate iterates(ControlFlowNode target, ControlFlowNode sequence) { - sequence = getSequence() and - target = possibleTarget() and - not target = unrolledSuffix().possibleTarget() - } + /** Holds if this `for` statement causes iteration over `sequence` storing each step of the iteration in `target` */ + predicate iterates(ControlFlowNode target, ControlFlowNode sequence) { + sequence = getSequence() and + target = possibleTarget() and + not target = unrolledSuffix().possibleTarget() + } - /** Gets the sequence node for this `for` statement. */ - ControlFlowNode getSequence() { - exists(For for | - toAst(this) = for and - for.getIter() = result.getNode() - | - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the sequence node for this `for` statement. */ + ControlFlowNode getSequence() { + exists(For for | + toAst(this) = for and + for.getIter() = result.getNode() + | + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** A possible `target` for this `for` statement, not accounting for loop unrolling */ - private ControlFlowNode possibleTarget() { - exists(For for | - toAst(this) = for and - for.getTarget() = result.getNode() and - this.getBasicBlock().dominates(result.getBasicBlock()) - ) - } + /** A possible `target` for this `for` statement, not accounting for loop unrolling */ + private ControlFlowNode possibleTarget() { + exists(For for | + toAst(this) = for and + for.getTarget() = result.getNode() and + this.getBasicBlock().dominates(result.getBasicBlock()) + ) + } - /** The unrolled `for` statement node matching this one */ - private ForNode unrolledSuffix() { - not this = result and - toAst(this) = toAst(result) and - this.getBasicBlock().dominates(result.getBasicBlock()) - } + /** The unrolled `for` statement node matching this one */ + private ForNode unrolledSuffix() { + not this = result and + toAst(this) = toAst(result) and + this.getBasicBlock().dominates(result.getBasicBlock()) + } } /** A flow node for a `raise` statement */ class RaiseStmtNode extends ControlFlowNode { - RaiseStmtNode() { toAst(this) instanceof Raise } + RaiseStmtNode() { toAst(this) instanceof Raise } - /** Gets the control flow node for the exception raised by this raise statement */ - ControlFlowNode getException() { - exists(Raise r | - r = toAst(this) and - r.getException() = toAst(result) and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the control flow node for the exception raised by this raise statement */ + ControlFlowNode getException() { + exists(Raise r | + r = toAst(this) and + r.getException() = toAst(result) and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } } /** @@ -877,320 +877,321 @@ class RaiseStmtNode extends ControlFlowNode { * `None`, `True` and `False` are excluded. */ class NameNode extends ControlFlowNode { - NameNode() { - exists(Name n | py_flow_bb_node(this, n, _, _)) - or - exists(PlaceHolder p | py_flow_bb_node(this, p, _, _)) - } + NameNode() { + exists(Name n | py_flow_bb_node(this, n, _, _)) + or + exists(PlaceHolder p | py_flow_bb_node(this, p, _, _)) + } - /** Whether this flow node defines the variable `v`. */ - predicate defines(Variable v) { - exists(Name d | this.getNode() = d and d.defines(v)) and - not this.isLoad() - } + /** Whether this flow node defines the variable `v`. */ + predicate defines(Variable v) { + exists(Name d | this.getNode() = d and d.defines(v)) and + not this.isLoad() + } - /** Whether this flow node deletes the variable `v`. */ - predicate deletes(Variable v) { exists(Name d | this.getNode() = d and d.deletes(v)) } + /** Whether this flow node deletes the variable `v`. */ + predicate deletes(Variable v) { exists(Name d | this.getNode() = d and d.deletes(v)) } - /** Whether this flow node uses the variable `v`. */ - predicate uses(Variable v) { - this.isLoad() and - exists(Name u | this.getNode() = u and u.uses(v)) - or - exists(PlaceHolder u | - this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Load - ) - or - Scopes::use_of_global_variable(this, v.getScope(), v.getId()) - } + /** Whether this flow node uses the variable `v`. */ + predicate uses(Variable v) { + this.isLoad() and + exists(Name u | this.getNode() = u and u.uses(v)) + or + exists(PlaceHolder u | + this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Load + ) + or + Scopes::use_of_global_variable(this, v.getScope(), v.getId()) + } - string getId() { - result = this.getNode().(Name).getId() - or - result = this.getNode().(PlaceHolder).getId() - } + string getId() { + result = this.getNode().(Name).getId() + or + result = this.getNode().(PlaceHolder).getId() + } - /** Whether this is a use of a local variable. */ - predicate isLocal() { Scopes::local(this) } + /** Whether this is a use of a local variable. */ + predicate isLocal() { Scopes::local(this) } - /** Whether this is a use of a non-local variable. */ - predicate isNonLocal() { Scopes::non_local(this) } + /** Whether this is a use of a non-local variable. */ + predicate isNonLocal() { Scopes::non_local(this) } - /** Whether this is a use of a global (including builtin) variable. */ - predicate isGlobal() { Scopes::use_of_global_variable(this, _, _) } + /** Whether this is a use of a global (including builtin) variable. */ + predicate isGlobal() { Scopes::use_of_global_variable(this, _, _) } - predicate isSelf() { exists(SsaVariable selfvar | selfvar.isSelf() and selfvar.getAUse() = this) } + predicate isSelf() { exists(SsaVariable selfvar | selfvar.isSelf() and selfvar.getAUse() = this) } } /** A control flow node corresponding to a named constant, one of `None`, `True` or `False`. */ class NameConstantNode extends NameNode { - NameConstantNode() { exists(NameConstant n | py_flow_bb_node(this, n, _, _)) } + NameConstantNode() { exists(NameConstant n | py_flow_bb_node(this, n, _, _)) } - deprecated override predicate defines(Variable v) { none() } + deprecated override predicate defines(Variable v) { none() } - deprecated override predicate deletes(Variable v) { none() } - /* - * We ought to override uses as well, but that has - * a serious performance impact. - * deprecated predicate uses(Variable v) { none() } - */ + deprecated override predicate deletes(Variable v) { none() } + /* + * We ought to override uses as well, but that has + * a serious performance impact. + * deprecated predicate uses(Variable v) { none() } + */ - } + } /** A control flow node correspoinding to a starred expression, `*a`. */ class StarredNode extends ControlFlowNode { - StarredNode() { toAst(this) instanceof Starred } + StarredNode() { toAst(this) instanceof Starred } - ControlFlowNode getValue() { toAst(result) = toAst(this).(Starred).getValue() } + ControlFlowNode getValue() { toAst(result) = toAst(this).(Starred).getValue() } } private module Scopes { - private predicate fast_local(NameNode n) { - exists(FastLocalVariable v | - n.uses(v) and - v.getScope() = n.getScope() - ) - } + private predicate fast_local(NameNode n) { + exists(FastLocalVariable v | + n.uses(v) and + v.getScope() = n.getScope() + ) + } - predicate local(NameNode n) { - fast_local(n) - or - exists(SsaVariable var | - var.getAUse() = n and - n.getScope() instanceof Class and - exists(var.getDefinition()) - ) - } + predicate local(NameNode n) { + fast_local(n) + or + exists(SsaVariable var | + var.getAUse() = n and + n.getScope() instanceof Class and + exists(var.getDefinition()) + ) + } - predicate non_local(NameNode n) { - exists(FastLocalVariable flv | - flv.getALoad() = n.getNode() and - not flv.getScope() = n.getScope() - ) - } + predicate non_local(NameNode n) { + exists(FastLocalVariable flv | + flv.getALoad() = n.getNode() and + not flv.getScope() = n.getScope() + ) + } - // magic is fine, but we get questionable join-ordering of it - pragma[nomagic] - predicate use_of_global_variable(NameNode n, Module scope, string name) { - n.isLoad() and - not non_local(n) and - not exists(SsaVariable var | var.getAUse() = n | - var.getVariable() instanceof FastLocalVariable - or - n.getScope() instanceof Class and - not maybe_undefined(var) - ) and - name = n.getId() and - scope = n.getEnclosingModule() - } + // magic is fine, but we get questionable join-ordering of it + pragma[nomagic] + predicate use_of_global_variable(NameNode n, Module scope, string name) { + n.isLoad() and + not non_local(n) and + not exists(SsaVariable var | var.getAUse() = n | + var.getVariable() instanceof FastLocalVariable + or + n.getScope() instanceof Class and + not maybe_undefined(var) + ) and + name = n.getId() and + scope = n.getEnclosingModule() + } - private predicate maybe_defined(SsaVariable var) { - exists(var.getDefinition()) and not py_ssa_phi(var, _) and not var.getDefinition().isDelete() - or - exists(SsaVariable input | input = var.getAPhiInput() | maybe_defined(input)) - } + private predicate maybe_defined(SsaVariable var) { + exists(var.getDefinition()) and not py_ssa_phi(var, _) and not var.getDefinition().isDelete() + or + exists(SsaVariable input | input = var.getAPhiInput() | maybe_defined(input)) + } - private predicate maybe_undefined(SsaVariable var) { - not exists(var.getDefinition()) and not py_ssa_phi(var, _) - or - var.getDefinition().isDelete() - or - maybe_undefined(var.getAPhiInput()) - or - exists(BasicBlock incoming | - exists(var.getAPhiInput()) and - incoming.getASuccessor() = var.getDefinition().getBasicBlock() and - not var.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming) - ) - } + private predicate maybe_undefined(SsaVariable var) { + not exists(var.getDefinition()) and not py_ssa_phi(var, _) + or + var.getDefinition().isDelete() + or + maybe_undefined(var.getAPhiInput()) + or + exists(BasicBlock incoming | + exists(var.getAPhiInput()) and + incoming.getASuccessor() = var.getDefinition().getBasicBlock() and + not var.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming) + ) + } } /** A basic block (ignoring exceptional flow edges to scope exit) */ class BasicBlock extends @py_flow_node { - BasicBlock() { py_flow_bb_node(_, _, this, _) } + BasicBlock() { py_flow_bb_node(_, _, this, _) } - /** Whether this basic block contains the specified node */ - predicate contains(ControlFlowNode node) { py_flow_bb_node(node, _, this, _) } + /** Whether this basic block contains the specified node */ + predicate contains(ControlFlowNode node) { py_flow_bb_node(node, _, this, _) } - /** Gets the nth node in this basic block */ - ControlFlowNode getNode(int n) { py_flow_bb_node(result, _, this, n) } + /** Gets the nth node in this basic block */ + ControlFlowNode getNode(int n) { py_flow_bb_node(result, _, this, n) } - /** Gets a textual representation of this element. */ - string toString() { result = "BasicBlock" } + /** Gets a textual representation of this element. */ + string toString() { result = "BasicBlock" } - /** Whether this basic block strictly dominates the other */ - pragma[nomagic] - predicate strictlyDominates(BasicBlock other) { other.getImmediateDominator+() = this } + /** Whether this basic block strictly dominates the other */ + pragma[nomagic] + predicate strictlyDominates(BasicBlock other) { other.getImmediateDominator+() = this } - /** Whether this basic block dominates the other */ - pragma[nomagic] - predicate dominates(BasicBlock other) { - this = other - or - this.strictlyDominates(other) - } + /** Whether this basic block dominates the other */ + pragma[nomagic] + predicate dominates(BasicBlock other) { + this = other + or + this.strictlyDominates(other) + } - cached - BasicBlock getImmediateDominator() { - this.firstNode().getImmediateDominator().getBasicBlock() = result - } + cached + BasicBlock getImmediateDominator() { + this.firstNode().getImmediateDominator().getBasicBlock() = result + } - /** - * Dominance frontier of a node x is the set of all nodes `other` such that `this` dominates a predecessor - * of `other` but does not strictly dominate `other` - */ - pragma[noinline] - predicate dominanceFrontier(BasicBlock other) { - this.dominates(other.getAPredecessor()) and not this.strictlyDominates(other) - } + /** + * Dominance frontier of a node x is the set of all nodes `other` such that `this` dominates a predecessor + * of `other` but does not strictly dominate `other` + */ + pragma[noinline] + predicate dominanceFrontier(BasicBlock other) { + this.dominates(other.getAPredecessor()) and not this.strictlyDominates(other) + } - private ControlFlowNode firstNode() { result = this } + private ControlFlowNode firstNode() { result = this } - /** Gets the last node in this basic block */ - ControlFlowNode getLastNode() { - exists(int i | - this.getNode(i) = result and - i = max(int j | py_flow_bb_node(_, _, this, j)) - ) - } + /** Gets the last node in this basic block */ + ControlFlowNode getLastNode() { + exists(int i | + this.getNode(i) = result and + i = max(int j | py_flow_bb_node(_, _, this, j)) + ) + } - private predicate oneNodeBlock() { this.firstNode() = this.getLastNode() } + private predicate oneNodeBlock() { this.firstNode() = this.getLastNode() } - private predicate startLocationInfo(string file, int line, int col) { - if this.firstNode().getNode() instanceof Scope - then this.firstNode().getASuccessor().getLocation().hasLocationInfo(file, line, col, _, _) - else this.firstNode().getLocation().hasLocationInfo(file, line, col, _, _) - } + private predicate startLocationInfo(string file, int line, int col) { + if this.firstNode().getNode() instanceof Scope + then this.firstNode().getASuccessor().getLocation().hasLocationInfo(file, line, col, _, _) + else this.firstNode().getLocation().hasLocationInfo(file, line, col, _, _) + } - private predicate endLocationInfo(int endl, int endc) { - if this.getLastNode().getNode() instanceof Scope and not this.oneNodeBlock() - then this.getLastNode().getAPredecessor().getLocation().hasLocationInfo(_, _, _, endl, endc) - else this.getLastNode().getLocation().hasLocationInfo(_, _, _, endl, endc) - } + private predicate endLocationInfo(int endl, int endc) { + if this.getLastNode().getNode() instanceof Scope and not this.oneNodeBlock() + then this.getLastNode().getAPredecessor().getLocation().hasLocationInfo(_, _, _, endl, endc) + else this.getLastNode().getLocation().hasLocationInfo(_, _, _, endl, endc) + } - /** Gets a successor to this basic block */ - BasicBlock getASuccessor() { result = this.getLastNode().getASuccessor().getBasicBlock() } + /** Gets a successor to this basic block */ + BasicBlock getASuccessor() { result = this.getLastNode().getASuccessor().getBasicBlock() } - /** Gets a predecessor to this basic block */ - BasicBlock getAPredecessor() { result.getASuccessor() = this } + /** Gets a predecessor to this basic block */ + BasicBlock getAPredecessor() { result.getASuccessor() = this } - /** Whether flow from this basic block reaches a normal exit from its scope */ - predicate reachesExit() { - exists(Scope s | s.getANormalExit().getBasicBlock() = this) - or - this.getASuccessor().reachesExit() - } + /** Whether flow from this basic block reaches a normal exit from its scope */ + predicate reachesExit() { + exists(Scope s | s.getANormalExit().getBasicBlock() = this) + or + this.getASuccessor().reachesExit() + } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { this.startLocationInfo(filepath, startline, startcolumn) and - this.endLocationInfo(endline, endcolumn) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.startLocationInfo(filepath, startline, startcolumn) and + this.endLocationInfo(endline, endcolumn) + } - /** Gets a true successor to this basic block */ - BasicBlock getATrueSuccessor() { result = this.getLastNode().getATrueSuccessor().getBasicBlock() } + /** Gets a true successor to this basic block */ + BasicBlock getATrueSuccessor() { result = this.getLastNode().getATrueSuccessor().getBasicBlock() } - /** Gets a false successor to this basic block */ - BasicBlock getAFalseSuccessor() { - result = this.getLastNode().getAFalseSuccessor().getBasicBlock() - } + /** Gets a false successor to this basic block */ + BasicBlock getAFalseSuccessor() { + result = this.getLastNode().getAFalseSuccessor().getBasicBlock() + } - /** Gets an unconditional successor to this basic block */ - BasicBlock getAnUnconditionalSuccessor() { - result = this.getASuccessor() and - not result = this.getATrueSuccessor() and - not result = this.getAFalseSuccessor() - } + /** Gets an unconditional successor to this basic block */ + BasicBlock getAnUnconditionalSuccessor() { + result = this.getASuccessor() and + not result = this.getATrueSuccessor() and + not result = this.getAFalseSuccessor() + } - /** Gets an exceptional successor to this basic block */ - BasicBlock getAnExceptionalSuccessor() { - result = this.getLastNode().getAnExceptionalSuccessor().getBasicBlock() - } + /** Gets an exceptional successor to this basic block */ + BasicBlock getAnExceptionalSuccessor() { + result = this.getLastNode().getAnExceptionalSuccessor().getBasicBlock() + } - /** Gets the scope of this block */ - pragma[nomagic] - Scope getScope() { - exists(ControlFlowNode n | n.getBasicBlock() = this | - /* Take care not to use an entry or exit node as that node's scope will be the outer scope */ - not py_scope_flow(n, _, -1) and - not py_scope_flow(n, _, 0) and - not py_scope_flow(n, _, 2) and - result = n.getScope() - or - py_scope_flow(n, result, _) - ) - } + /** Gets the scope of this block */ + pragma[nomagic] + Scope getScope() { + exists(ControlFlowNode n | n.getBasicBlock() = this | + /* Take care not to use an entry or exit node as that node's scope will be the outer scope */ + not py_scope_flow(n, _, -1) and + not py_scope_flow(n, _, 0) and + not py_scope_flow(n, _, 2) and + result = n.getScope() + or + py_scope_flow(n, result, _) + ) + } - /** - * Whether (as inferred by type inference) it is highly unlikely (or impossible) for control to flow from this to succ. - */ - predicate unlikelySuccessor(BasicBlock succ) { - this.getLastNode().(RaisingNode).unlikelySuccessor(succ.firstNode()) - or - not end_bb_likely_reachable(this) and succ = this.getASuccessor() - } + /** + * Whether (as inferred by type inference) it is highly unlikely (or impossible) for control to flow from this to succ. + */ + predicate unlikelySuccessor(BasicBlock succ) { + this.getLastNode().(RaisingNode).unlikelySuccessor(succ.firstNode()) + or + not end_bb_likely_reachable(this) and succ = this.getASuccessor() + } - /** Holds if this basic block strictly reaches the other. Is the start of other reachable from the end of this. */ - predicate strictlyReaches(BasicBlock other) { this.getASuccessor+() = other } + /** Holds if this basic block strictly reaches the other. Is the start of other reachable from the end of this. */ + predicate strictlyReaches(BasicBlock other) { this.getASuccessor+() = other } - /** Holds if this basic block reaches the other. Is the start of other reachable from the end of this. */ - predicate reaches(BasicBlock other) { this = other or this.strictlyReaches(other) } + /** Holds if this basic block reaches the other. Is the start of other reachable from the end of this. */ + predicate reaches(BasicBlock other) { this = other or this.strictlyReaches(other) } - /** - * Whether (as inferred by type inference) this basic block is likely to be reachable. - */ - predicate likelyReachable() { start_bb_likely_reachable(this) } + /** + * Whether (as inferred by type inference) this basic block is likely to be reachable. + */ + predicate likelyReachable() { start_bb_likely_reachable(this) } - /** - * Gets the `ConditionBlock`, if any, that controls this block and - * does not control any other `ConditionBlock`s that control this block. - * That is the `ConditionBlock` that is closest dominator. - */ - ConditionBlock getImmediatelyControllingBlock() { - result = this.nonControllingImmediateDominator*().getImmediateDominator() - } + /** + * Gets the `ConditionBlock`, if any, that controls this block and + * does not control any other `ConditionBlock`s that control this block. + * That is the `ConditionBlock` that is closest dominator. + */ + ConditionBlock getImmediatelyControllingBlock() { + result = this.nonControllingImmediateDominator*().getImmediateDominator() + } - private BasicBlock nonControllingImmediateDominator() { - result = this.getImmediateDominator() and - not result.(ConditionBlock).controls(this, _) - } + private BasicBlock nonControllingImmediateDominator() { + result = this.getImmediateDominator() and + not result.(ConditionBlock).controls(this, _) + } - /** - * Holds if flow from this BasicBlock always reaches `succ` - */ - predicate alwaysReaches(BasicBlock succ) { - succ = this - or - strictcount(this.getASuccessor()) = 1 and - succ = this.getASuccessor() - or - forex(BasicBlock immsucc | immsucc = this.getASuccessor() | immsucc.alwaysReaches(succ)) - } + /** + * Holds if flow from this BasicBlock always reaches `succ` + */ + predicate alwaysReaches(BasicBlock succ) { + succ = this + or + strictcount(this.getASuccessor()) = 1 and + succ = this.getASuccessor() + or + forex(BasicBlock immsucc | immsucc = this.getASuccessor() | immsucc.alwaysReaches(succ)) + } } private predicate start_bb_likely_reachable(BasicBlock b) { - exists(Scope s | s.getEntryNode() = b.getNode(_)) - or - exists(BasicBlock pred | - pred = b.getAPredecessor() and - end_bb_likely_reachable(pred) and - not pred.getLastNode().(RaisingNode).unlikelySuccessor(b) - ) + exists(Scope s | s.getEntryNode() = b.getNode(_)) + or + exists(BasicBlock pred | + pred = b.getAPredecessor() and + end_bb_likely_reachable(pred) and + not pred.getLastNode().(RaisingNode).unlikelySuccessor(b) + ) } private predicate end_bb_likely_reachable(BasicBlock b) { - start_bb_likely_reachable(b) and - not exists(ControlFlowNode p, ControlFlowNode s | - p.(RaisingNode).unlikelySuccessor(s) and - p = b.getNode(_) and - s = b.getNode(_) and - not p = b.getLastNode() - ) + start_bb_likely_reachable(b) and + not exists(ControlFlowNode p, ControlFlowNode s | + p.(RaisingNode).unlikelySuccessor(s) and + p = b.getNode(_) and + s = b.getNode(_) and + not p = b.getLastNode() + ) } diff --git a/python/ql/src/semmle/python/Function.qll b/python/ql/src/semmle/python/Function.qll index b5cd9b4db4f..4ec4576bcd8 100644 --- a/python/ql/src/semmle/python/Function.qll +++ b/python/ql/src/semmle/python/Function.qll @@ -5,336 +5,336 @@ import python * It is the syntactic entity that is compiled to a code object. */ class Function extends Function_, Scope, AstNode { - /** The expression defining this function */ - CallableExpr getDefinition() { result = this.getParent() } + /** The expression defining this function */ + CallableExpr getDefinition() { result = this.getParent() } - /** - * The scope in which this function occurs, will be a class for a method, - * another function for nested functions, generator expressions or comprehensions, - * or a module for a plain function. - */ - override Scope getEnclosingScope() { result = this.getParent().(Expr).getScope() } + /** + * The scope in which this function occurs, will be a class for a method, + * another function for nested functions, generator expressions or comprehensions, + * or a module for a plain function. + */ + override Scope getEnclosingScope() { result = this.getParent().(Expr).getScope() } - override Scope getScope() { result = this.getEnclosingScope() } + override Scope getScope() { result = this.getEnclosingScope() } - /** Whether this function is declared in a class */ - predicate isMethod() { exists(Class cls | this.getEnclosingScope() = cls) } + /** Whether this function is declared in a class */ + predicate isMethod() { exists(Class cls | this.getEnclosingScope() = cls) } - /** Whether this is a special method, that is does its name have the form `__xxx__` (except `__init__`) */ - predicate isSpecialMethod() { - this.isMethod() and - exists(string name | this.getName() = name | - name.matches("\\_\\_%\\_\\_") and - name != "__init__" - ) - } + /** Whether this is a special method, that is does its name have the form `__xxx__` (except `__init__`) */ + predicate isSpecialMethod() { + this.isMethod() and + exists(string name | this.getName() = name | + name.matches("\\_\\_%\\_\\_") and + name != "__init__" + ) + } - /** - * Whether this function is a generator function, - * that is whether it contains a yield or yield-from expression - */ - predicate isGenerator() { - exists(Yield y | y.getScope() = this) - or - exists(YieldFrom y | y.getScope() = this) - } + /** + * Whether this function is a generator function, + * that is whether it contains a yield or yield-from expression + */ + predicate isGenerator() { + exists(Yield y | y.getScope() = this) + or + exists(YieldFrom y | y.getScope() = this) + } - /** Whether this function is declared in a class and is named `__init__` */ - predicate isInitMethod() { this.isMethod() and this.getName() = "__init__" } + /** Whether this function is declared in a class and is named `__init__` */ + predicate isInitMethod() { this.isMethod() and this.getName() = "__init__" } - /** Gets a decorator of this function */ - Expr getADecorator() { result = this.getDefinition().(FunctionExpr).getADecorator() } + /** Gets a decorator of this function */ + Expr getADecorator() { result = this.getDefinition().(FunctionExpr).getADecorator() } - /** Gets the name of the nth argument (for simple arguments) */ - string getArgName(int index) { result = this.getArg(index).(Name).getId() } + /** Gets the name of the nth argument (for simple arguments) */ + string getArgName(int index) { result = this.getArg(index).(Name).getId() } - Parameter getArgByName(string name) { - ( - result = this.getAnArg() - or - result = this.getAKeywordOnlyArg() - ) and - result.(Name).getId() = name - } + Parameter getArgByName(string name) { + ( + result = this.getAnArg() + or + result = this.getAKeywordOnlyArg() + ) and + result.(Name).getId() = name + } - override Location getLocation() { py_scope_location(result, this) } + override Location getLocation() { py_scope_location(result, this) } - override string toString() { result = "Function " + this.getName() } + override string toString() { result = "Function " + this.getName() } - /** Gets the statements forming the body of this function */ - override StmtList getBody() { result = Function_.super.getBody() } + /** Gets the statements forming the body of this function */ + override StmtList getBody() { result = Function_.super.getBody() } - /** Gets the nth statement in the function */ - override Stmt getStmt(int index) { result = Function_.super.getStmt(index) } + /** Gets the nth statement in the function */ + override Stmt getStmt(int index) { result = Function_.super.getStmt(index) } - /** Gets a statement in the function */ - override Stmt getAStmt() { result = Function_.super.getAStmt() } + /** Gets a statement in the function */ + override Stmt getAStmt() { result = Function_.super.getAStmt() } - /** Gets the name used to define this function */ - override string getName() { result = Function_.super.getName() } + /** Gets the name used to define this function */ + override string getName() { result = Function_.super.getName() } - /** Gets the metrics for this function */ - FunctionMetrics getMetrics() { result = this } + /** Gets the metrics for this function */ + FunctionMetrics getMetrics() { result = this } - /** Gets the FunctionObject corresponding to this function */ - FunctionObject getFunctionObject() { result.getOrigin() = this.getDefinition() } + /** Gets the FunctionObject corresponding to this function */ + FunctionObject getFunctionObject() { result.getOrigin() = this.getDefinition() } - /** - * Whether this function is a procedure, that is, it has no explicit return statement and always returns None. - * Note that generator and async functions are not procedures as they return generators and coroutines respectively. - */ - predicate isProcedure() { - not exists(this.getReturnNode()) and - exists(this.getFallthroughNode()) and - not this.isGenerator() and - not this.isAsync() - } + /** + * Whether this function is a procedure, that is, it has no explicit return statement and always returns None. + * Note that generator and async functions are not procedures as they return generators and coroutines respectively. + */ + predicate isProcedure() { + not exists(this.getReturnNode()) and + exists(this.getFallthroughNode()) and + not this.isGenerator() and + not this.isAsync() + } - /** Gets the number of positional parameters */ - int getPositionalParameterCount() { result = count(this.getAnArg()) } + /** Gets the number of positional parameters */ + int getPositionalParameterCount() { result = count(this.getAnArg()) } - /** Gets the number of keyword-only parameters */ - int getKeywordOnlyParameterCount() { result = count(this.getAKeywordOnlyArg()) } + /** Gets the number of keyword-only parameters */ + int getKeywordOnlyParameterCount() { result = count(this.getAKeywordOnlyArg()) } - /** Whether this function accepts a variable number of arguments. That is, whether it has a starred (*arg) parameter. */ - predicate hasVarArg() { exists(this.getVararg()) } + /** Whether this function accepts a variable number of arguments. That is, whether it has a starred (*arg) parameter. */ + predicate hasVarArg() { exists(this.getVararg()) } - /** Whether this function accepts arbitrary keyword arguments. That is, whether it has a double-starred (**kwarg) parameter. */ - predicate hasKwArg() { exists(this.getKwarg()) } + /** Whether this function accepts arbitrary keyword arguments. That is, whether it has a double-starred (**kwarg) parameter. */ + predicate hasKwArg() { exists(this.getKwarg()) } - override AstNode getAChildNode() { - result = this.getAStmt() or - result = this.getAnArg() or - result = this.getVararg() or - result = this.getAKeywordOnlyArg() or - result = this.getKwarg() - } + override AstNode getAChildNode() { + result = this.getAStmt() or + result = this.getAnArg() or + result = this.getVararg() or + result = this.getAKeywordOnlyArg() or + result = this.getKwarg() + } - /** - * Gets the qualified name for this function. - * Should return the same name as the `__qualname__` attribute on functions in Python 3. - */ - string getQualifiedName() { - this.getEnclosingScope() instanceof Module and result = this.getName() - or - exists(string enclosing_name | - enclosing_name = this.getEnclosingScope().(Function).getQualifiedName() - or - enclosing_name = this.getEnclosingScope().(Class).getQualifiedName() - | - result = enclosing_name + "." + this.getName() - ) - } + /** + * Gets the qualified name for this function. + * Should return the same name as the `__qualname__` attribute on functions in Python 3. + */ + string getQualifiedName() { + this.getEnclosingScope() instanceof Module and result = this.getName() + or + exists(string enclosing_name | + enclosing_name = this.getEnclosingScope().(Function).getQualifiedName() + or + enclosing_name = this.getEnclosingScope().(Class).getQualifiedName() + | + result = enclosing_name + "." + this.getName() + ) + } - /** Gets the nth keyword-only parameter of this function. */ - Name getKeywordOnlyArg(int n) { result = Function_.super.getKwonlyarg(n) } + /** Gets the nth keyword-only parameter of this function. */ + Name getKeywordOnlyArg(int n) { result = Function_.super.getKwonlyarg(n) } - /** Gets a keyword-only parameter of this function. */ - Name getAKeywordOnlyArg() { result = this.getKeywordOnlyArg(_) } + /** Gets a keyword-only parameter of this function. */ + Name getAKeywordOnlyArg() { result = this.getKeywordOnlyArg(_) } - override Scope getEvaluatingScope() { - major_version() = 2 and - exists(Comp comp | comp.getFunction() = this | result = comp.getEvaluatingScope()) - or - not exists(Comp comp | comp.getFunction() = this) and result = this - or - major_version() = 3 and result = this - } + override Scope getEvaluatingScope() { + major_version() = 2 and + exists(Comp comp | comp.getFunction() = this | result = comp.getEvaluatingScope()) + or + not exists(Comp comp | comp.getFunction() = this) and result = this + or + major_version() = 3 and result = this + } - override predicate containsInScope(AstNode inner) { Scope.super.containsInScope(inner) } + override predicate containsInScope(AstNode inner) { Scope.super.containsInScope(inner) } - override predicate contains(AstNode inner) { Scope.super.contains(inner) } + override predicate contains(AstNode inner) { Scope.super.contains(inner) } - /** Gets a control flow node for a return value of this function */ - ControlFlowNode getAReturnValueFlowNode() { - exists(Return ret | - ret.getScope() = this and - ret.getValue() = result.getNode() - ) - } + /** Gets a control flow node for a return value of this function */ + ControlFlowNode getAReturnValueFlowNode() { + exists(Return ret | + ret.getScope() = this and + ret.getValue() = result.getNode() + ) + } } /** A def statement. Note that FunctionDef extends Assign as a function definition binds the newly created function */ class FunctionDef extends Assign { - /* syntax: def name(...): ... */ - FunctionDef() { - /* This is an artificial assignment the rhs of which is a (possibly decorated) FunctionExpr */ - exists(FunctionExpr f | this.getValue() = f or this.getValue() = f.getADecoratorCall()) - } + /* syntax: def name(...): ... */ + FunctionDef() { + /* This is an artificial assignment the rhs of which is a (possibly decorated) FunctionExpr */ + exists(FunctionExpr f | this.getValue() = f or this.getValue() = f.getADecoratorCall()) + } - override string toString() { result = "FunctionDef" } + override string toString() { result = "FunctionDef" } - /** Gets the function for this statement */ - Function getDefinedFunction() { - exists(FunctionExpr func | this.containsInScope(func) and result = func.getInnerScope()) - } + /** Gets the function for this statement */ + Function getDefinedFunction() { + exists(FunctionExpr func | this.containsInScope(func) and result = func.getInnerScope()) + } - override Stmt getLastStatement() { result = this.getDefinedFunction().getLastStatement() } + override Stmt getLastStatement() { result = this.getDefinedFunction().getLastStatement() } } class FastLocalsFunction extends Function { - /** A function that uses 'fast' locals, stored in the frame not in a dictionary. */ - FastLocalsFunction() { - not exists(ImportStar i | i.getScope() = this) and - not exists(Exec e | e.getScope() = this) - } + /** A function that uses 'fast' locals, stored in the frame not in a dictionary. */ + FastLocalsFunction() { + not exists(ImportStar i | i.getScope() = this) and + not exists(Exec e | e.getScope() = this) + } } /** A parameter. Either a Tuple or a Name (always a Name for Python 3) */ class Parameter extends Parameter_ { - Parameter() { - /* Parameter_ is just defined as a Name or Tuple, narrow to actual parameters */ - exists(ParameterList pl | py_exprs(this, _, pl, _)) - or - exists(Function f | - f.getVararg() = this - or - f.getKwarg() = this - or - f.getAKeywordOnlyArg() = this - ) - } + Parameter() { + /* Parameter_ is just defined as a Name or Tuple, narrow to actual parameters */ + exists(ParameterList pl | py_exprs(this, _, pl, _)) + or + exists(Function f | + f.getVararg() = this + or + f.getKwarg() = this + or + f.getAKeywordOnlyArg() = this + ) + } - Location getLocation() { - result = this.asName().getLocation() - or - result = this.asTuple().getLocation() - } + Location getLocation() { + result = this.asName().getLocation() + or + result = this.asTuple().getLocation() + } - /** Gets this parameter if it is a Name (not a Tuple) */ - Name asName() { result = this } + /** Gets this parameter if it is a Name (not a Tuple) */ + Name asName() { result = this } - /** Gets this parameter if it is a Tuple (not a Name) */ - Tuple asTuple() { result = this } + /** Gets this parameter if it is a Tuple (not a Name) */ + Tuple asTuple() { result = this } - /** Gets the expression for the default value of this parameter */ - Expr getDefault() { - exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | - // positional (normal) - f.getArg(i) = this and - result = args.getDefault(i) - ) - or - exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | - // keyword-only - f.getKeywordOnlyArg(i) = this and - result = args.getKwDefault(i) - ) - } + /** Gets the expression for the default value of this parameter */ + Expr getDefault() { + exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | + // positional (normal) + f.getArg(i) = this and + result = args.getDefault(i) + ) + or + exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | + // keyword-only + f.getKeywordOnlyArg(i) = this and + result = args.getKwDefault(i) + ) + } - /** Gets the annotation expression of this parameter */ - Expr getAnnotation() { - exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | - // positional (normal) - f.getArg(i) = this and - result = args.getAnnotation(i) - ) - or - exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | - // keyword-only - f.getKeywordOnlyArg(i) = this and - result = args.getKwAnnotation(i) - ) - or - exists(Function f, Arguments args | args = f.getDefinition().getArgs() | - f.getKwarg() = this and - result = args.getKwargannotation() - or - f.getVararg() = this and - result = args.getVarargannotation() - ) - } + /** Gets the annotation expression of this parameter */ + Expr getAnnotation() { + exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | + // positional (normal) + f.getArg(i) = this and + result = args.getAnnotation(i) + ) + or + exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | + // keyword-only + f.getKeywordOnlyArg(i) = this and + result = args.getKwAnnotation(i) + ) + or + exists(Function f, Arguments args | args = f.getDefinition().getArgs() | + f.getKwarg() = this and + result = args.getKwargannotation() + or + f.getVararg() = this and + result = args.getVarargannotation() + ) + } - Variable getVariable() { result.getAnAccess() = this.asName() } + Variable getVariable() { result.getAnAccess() = this.asName() } - /** - * Gets the position of this parameter (if any). - * No result if this is a "varargs", "kwargs", or keyword-only parameter. - */ - int getPosition() { exists(Function f | f.getArg(result) = this) } + /** + * Gets the position of this parameter (if any). + * No result if this is a "varargs", "kwargs", or keyword-only parameter. + */ + int getPosition() { exists(Function f | f.getArg(result) = this) } - /** Gets the name of this parameter */ - string getName() { result = this.asName().getId() } + /** Gets the name of this parameter */ + string getName() { result = this.asName().getId() } - /** Holds if this parameter is the first parameter of a method. It is not necessarily called "self" */ - predicate isSelf() { - exists(Function f | - f.getArg(0) = this and - f.isMethod() - ) - } + /** Holds if this parameter is the first parameter of a method. It is not necessarily called "self" */ + predicate isSelf() { + exists(Function f | + f.getArg(0) = this and + f.isMethod() + ) + } - /** - * Holds if this parameter is a "varargs" parameter. - * The `varargs` in `f(a, b, *varargs)`. - */ - predicate isVarargs() { exists(Function func | func.getVararg() = this) } + /** + * Holds if this parameter is a "varargs" parameter. + * The `varargs` in `f(a, b, *varargs)`. + */ + predicate isVarargs() { exists(Function func | func.getVararg() = this) } - /** - * Holds if this parameter is a "kwargs" parameter. - * The `kwargs` in `f(a, b, **kwargs)`. - */ - predicate isKwargs() { exists(Function func | func.getKwarg() = this) } + /** + * Holds if this parameter is a "kwargs" parameter. + * The `kwargs` in `f(a, b, **kwargs)`. + */ + predicate isKwargs() { exists(Function func | func.getKwarg() = this) } } /** An expression that generates a callable object, either a function expression or a lambda */ abstract class CallableExpr extends Expr { - /** - * Gets The default values and annotations (type-hints) for the arguments of this callable. - * - * This predicate is called getArgs(), rather than getParameters() for compatibility with Python's AST module. - */ - abstract Arguments getArgs(); + /** + * Gets The default values and annotations (type-hints) for the arguments of this callable. + * + * This predicate is called getArgs(), rather than getParameters() for compatibility with Python's AST module. + */ + abstract Arguments getArgs(); - /** Gets the function scope of this code expression. */ - abstract Function getInnerScope(); + /** Gets the function scope of this code expression. */ + abstract Function getInnerScope(); } /** An (artificial) expression corresponding to a function definition. */ class FunctionExpr extends FunctionExpr_, CallableExpr { - override Expr getASubExpression() { - result = this.getArgs().getASubExpression() or - result = this.getReturns() - } + override Expr getASubExpression() { + result = this.getArgs().getASubExpression() or + result = this.getReturns() + } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } - Call getADecoratorCall() { - result.getArg(0) = this or - result.getArg(0) = this.getADecoratorCall() - } + Call getADecoratorCall() { + result.getArg(0) = this or + result.getArg(0) = this.getADecoratorCall() + } - /** Gets a decorator of this function expression */ - Expr getADecorator() { result = this.getADecoratorCall().getFunc() } + /** Gets a decorator of this function expression */ + Expr getADecorator() { result = this.getADecoratorCall().getFunc() } - override AstNode getAChildNode() { - result = this.getASubExpression() - or - result = this.getInnerScope() - } + override AstNode getAChildNode() { + result = this.getASubExpression() + or + result = this.getInnerScope() + } - override Function getInnerScope() { result = FunctionExpr_.super.getInnerScope() } + override Function getInnerScope() { result = FunctionExpr_.super.getInnerScope() } - override Arguments getArgs() { result = FunctionExpr_.super.getArgs() } + override Arguments getArgs() { result = FunctionExpr_.super.getArgs() } } /** A lambda expression, such as `lambda x: x+1` */ class Lambda extends Lambda_, CallableExpr { - /** Gets the expression to the right of the colon in this lambda expression */ - Expr getExpression() { - exists(Return ret | ret = this.getInnerScope().getStmt(0) | result = ret.getValue()) - } + /** Gets the expression to the right of the colon in this lambda expression */ + Expr getExpression() { + exists(Return ret | ret = this.getInnerScope().getStmt(0) | result = ret.getValue()) + } - override Expr getASubExpression() { result = this.getArgs().getASubExpression() } + override Expr getASubExpression() { result = this.getArgs().getASubExpression() } - override AstNode getAChildNode() { - result = this.getASubExpression() or - result = this.getInnerScope() - } + override AstNode getAChildNode() { + result = this.getASubExpression() or + result = this.getInnerScope() + } - override Function getInnerScope() { result = Lambda_.super.getInnerScope() } + override Function getInnerScope() { result = Lambda_.super.getInnerScope() } - override Arguments getArgs() { result = Lambda_.super.getArgs() } + override Arguments getArgs() { result = Lambda_.super.getArgs() } } /** @@ -344,29 +344,27 @@ class Lambda extends Lambda_, CallableExpr { * that is generally only used for type hints today (PEP 484). */ class Arguments extends Arguments_ { + Expr getASubExpression() { + result = this.getADefault() or + result = this.getAKwDefault() or + // + result = this.getAnAnnotation() or + result = this.getVarargannotation() or + result = this.getAKwAnnotation() or + result = this.getKwargannotation() + } - Expr getASubExpression() { - result = this.getADefault() or - result = this.getAKwDefault() or - // - result = this.getAnAnnotation() or - result = this.getVarargannotation() or - result = this.getAKwAnnotation() or - result = this.getKwargannotation() - } + // The following 4 methods are overwritten to provide better QLdoc. Since the + // Arguments_ is auto-generated, we can't change the poor auto-generated docs there :( + /** Gets the default value for the `index`'th positional parameter. */ + override Expr getDefault(int index) { result = super.getDefault(index) } - // The following 4 methods are overwritten to provide better QLdoc. Since the - // Arguments_ is auto-generated, we can't change the poor auto-generated docs there :( + /** Gets the default value for the `index`'th keyword-only parameter. */ + override Expr getKwDefault(int index) { result = super.getKwDefault(index) } - /** Gets the default value for the `index`'th positional parameter. */ - override Expr getDefault(int index) { result = super.getDefault(index) } + /** Gets the annotation for the `index`'th positional parameter. */ + override Expr getAnnotation(int index) { result = super.getAnnotation(index) } - /** Gets the default value for the `index`'th keyword-only parameter. */ - override Expr getKwDefault(int index) { result = super.getKwDefault(index) } - - /** Gets the annotation for the `index`'th positional parameter. */ - override Expr getAnnotation(int index) { result = super.getAnnotation(index) } - - /** Gets the annotation for the `index`'th keyword-only parameter. */ - override Expr getKwAnnotation(int index) { result = super.getKwAnnotation(index) } + /** Gets the annotation for the `index`'th keyword-only parameter. */ + override Expr getKwAnnotation(int index) { result = super.getKwAnnotation(index) } } diff --git a/python/ql/src/semmle/python/GuardedControlFlow.qll b/python/ql/src/semmle/python/GuardedControlFlow.qll index 0675f336828..37ecfee37d5 100644 --- a/python/ql/src/semmle/python/GuardedControlFlow.qll +++ b/python/ql/src/semmle/python/GuardedControlFlow.qll @@ -2,67 +2,67 @@ import python /** A basic block which terminates in a condition, splitting the subsequent control flow */ class ConditionBlock extends BasicBlock { - ConditionBlock() { - exists(ControlFlowNode succ | - succ = this.getATrueSuccessor() or succ = this.getAFalseSuccessor() - ) - } + ConditionBlock() { + exists(ControlFlowNode succ | + succ = this.getATrueSuccessor() or succ = this.getAFalseSuccessor() + ) + } - /** Basic blocks controlled by this condition, i.e. those BBs for which the condition is testIsTrue */ - predicate controls(BasicBlock controlled, boolean testIsTrue) { - /* - * For this block to control the block 'controlled' with 'testIsTrue' the following must be true: - * Execution must have passed through the test i.e. 'this' must strictly dominate 'controlled'. - * Execution must have passed through the 'testIsTrue' edge leaving 'this'. - * - * Although "passed through the true edge" implies that this.getATrueSuccessor() dominates 'controlled', - * the reverse is not true, as flow may have passed through another edge to get to this.getATrueSuccessor() - * so we need to assert that this.getATrueSuccessor() dominates 'controlled' *and* that - * all predecessors of this.getATrueSuccessor() are either this or dominated by this.getATrueSuccessor(). - * - * For example, in the following python snippet: - * - * if x: - * controlled - * false_successor - * uncontrolled - * - * false_successor dominates uncontrolled, but not all of its predecessors are this (if x) - * or dominated by itself. Whereas in the following code: - * - * if x: - * while controlled: - * also_controlled - * false_successor - * uncontrolled - * - * the block 'while controlled' is controlled because all of its predecessors are this (if x) - * or (in the case of 'also_controlled') dominated by itself. - * - * The additional constraint on the predecessors of the test successor implies - * that `this` strictly dominates `controlled` so that isn't necessary to check - * directly. - */ + /** Basic blocks controlled by this condition, i.e. those BBs for which the condition is testIsTrue */ + predicate controls(BasicBlock controlled, boolean testIsTrue) { + /* + * For this block to control the block 'controlled' with 'testIsTrue' the following must be true: + * Execution must have passed through the test i.e. 'this' must strictly dominate 'controlled'. + * Execution must have passed through the 'testIsTrue' edge leaving 'this'. + * + * Although "passed through the true edge" implies that this.getATrueSuccessor() dominates 'controlled', + * the reverse is not true, as flow may have passed through another edge to get to this.getATrueSuccessor() + * so we need to assert that this.getATrueSuccessor() dominates 'controlled' *and* that + * all predecessors of this.getATrueSuccessor() are either this or dominated by this.getATrueSuccessor(). + * + * For example, in the following python snippet: + * + * if x: + * controlled + * false_successor + * uncontrolled + * + * false_successor dominates uncontrolled, but not all of its predecessors are this (if x) + * or dominated by itself. Whereas in the following code: + * + * if x: + * while controlled: + * also_controlled + * false_successor + * uncontrolled + * + * the block 'while controlled' is controlled because all of its predecessors are this (if x) + * or (in the case of 'also_controlled') dominated by itself. + * + * The additional constraint on the predecessors of the test successor implies + * that `this` strictly dominates `controlled` so that isn't necessary to check + * directly. + */ - exists(BasicBlock succ | - testIsTrue = true and succ = this.getATrueSuccessor() - or - testIsTrue = false and succ = this.getAFalseSuccessor() - | - succ.dominates(controlled) and - forall(BasicBlock pred | pred.getASuccessor() = succ | pred = this or succ.dominates(pred)) - ) - } + exists(BasicBlock succ | + testIsTrue = true and succ = this.getATrueSuccessor() + or + testIsTrue = false and succ = this.getAFalseSuccessor() + | + succ.dominates(controlled) and + forall(BasicBlock pred | pred.getASuccessor() = succ | pred = this or succ.dominates(pred)) + ) + } - /** Holds if this condition controls the edge `pred->succ`, i.e. those edges for which the condition is `testIsTrue`. */ - predicate controlsEdge(BasicBlock pred, BasicBlock succ, boolean testIsTrue) { - this.controls(pred, testIsTrue) and succ = pred.getASuccessor() - or - pred = this and - ( - testIsTrue = true and succ = this.getATrueSuccessor() - or - testIsTrue = false and succ = this.getAFalseSuccessor() - ) - } + /** Holds if this condition controls the edge `pred->succ`, i.e. those edges for which the condition is `testIsTrue`. */ + predicate controlsEdge(BasicBlock pred, BasicBlock succ, boolean testIsTrue) { + this.controls(pred, testIsTrue) and succ = pred.getASuccessor() + or + pred = this and + ( + testIsTrue = true and succ = this.getATrueSuccessor() + or + testIsTrue = false and succ = this.getAFalseSuccessor() + ) + } } diff --git a/python/ql/src/semmle/python/Import.qll b/python/ql/src/semmle/python/Import.qll index 4e52d08dc68..40c1c27a851 100644 --- a/python/ql/src/semmle/python/Import.qll +++ b/python/ql/src/semmle/python/Import.qll @@ -6,229 +6,229 @@ private import semmle.python.types.Builtins * `import x` is transformed into `import x as x` */ class Alias extends Alias_ { - Location getLocation() { result = this.getValue().getLocation() } + Location getLocation() { result = this.getValue().getLocation() } } private predicate valid_module_name(string name) { - exists(Module m | m.getName() = name) - or - exists(Builtin cmod | cmod.getClass() = Builtin::special("ModuleType") and cmod.getName() = name) + exists(Module m | m.getName() = name) + or + exists(Builtin cmod | cmod.getClass() = Builtin::special("ModuleType") and cmod.getName() = name) } /** An artificial expression representing an import */ class ImportExpr extends ImportExpr_ { - private string basePackageName(int n) { - n = 1 and result = this.getEnclosingModule().getPackageName() - or - exists(string bpnm1 | - bpnm1 = this.basePackageName(n - 1) and - bpnm1.matches("%.%") and - result = bpnm1.regexpReplaceAll("\\.[^.]*$", "") - ) - } + private string basePackageName(int n) { + n = 1 and result = this.getEnclosingModule().getPackageName() + or + exists(string bpnm1 | + bpnm1 = this.basePackageName(n - 1) and + bpnm1.matches("%.%") and + result = bpnm1.regexpReplaceAll("\\.[^.]*$", "") + ) + } - private predicate implicitRelativeImportsAllowed() { - // relative imports are no longer allowed in Python 3 - major_version() < 3 and - // and can be explicitly turned off in later versions of Python 2 - not getEnclosingModule().hasFromFuture("absolute_import") - } + private predicate implicitRelativeImportsAllowed() { + // relative imports are no longer allowed in Python 3 + major_version() < 3 and + // and can be explicitly turned off in later versions of Python 2 + not getEnclosingModule().hasFromFuture("absolute_import") + } - /** - * The language specifies level as -1 if relative imports are to be tried first, 0 for absolute imports, - * and level > 0 for explicit relative imports. - */ - override int getLevel() { - exists(int l | l = super.getLevel() | - l > 0 and result = l - or - /* The extractor may set level to 0 even though relative imports apply */ - l = 0 and - (if this.implicitRelativeImportsAllowed() then result = -1 else result = 0) - ) - } + /** + * The language specifies level as -1 if relative imports are to be tried first, 0 for absolute imports, + * and level > 0 for explicit relative imports. + */ + override int getLevel() { + exists(int l | l = super.getLevel() | + l > 0 and result = l + or + /* The extractor may set level to 0 even though relative imports apply */ + l = 0 and + (if this.implicitRelativeImportsAllowed() then result = -1 else result = 0) + ) + } - /** - * If this import is relative, and relative imports are allowed, compute - * the name of the topmost module that will be imported. - */ - private string relativeTopName() { - getLevel() = -1 and - result = basePackageName(1) + "." + this.getTopName() and - valid_module_name(result) - } + /** + * If this import is relative, and relative imports are allowed, compute + * the name of the topmost module that will be imported. + */ + private string relativeTopName() { + getLevel() = -1 and + result = basePackageName(1) + "." + this.getTopName() and + valid_module_name(result) + } - private string qualifiedTopName() { - if this.getLevel() <= 0 - then result = this.getTopName() - else ( - result = basePackageName(this.getLevel()) and - valid_module_name(result) - ) - } + private string qualifiedTopName() { + if this.getLevel() <= 0 + then result = this.getTopName() + else ( + result = basePackageName(this.getLevel()) and + valid_module_name(result) + ) + } - /** - * Gets the name by which the lowest level module or package is imported. - * NOTE: This is the name that used to import the module, - * which may not be the name of the module. - */ - string bottomModuleName() { - result = relativeTopName() + this.remainderOfName() - or - not exists(relativeTopName()) and - result = this.qualifiedTopName() + this.remainderOfName() - } + /** + * Gets the name by which the lowest level module or package is imported. + * NOTE: This is the name that used to import the module, + * which may not be the name of the module. + */ + string bottomModuleName() { + result = relativeTopName() + this.remainderOfName() + or + not exists(relativeTopName()) and + result = this.qualifiedTopName() + this.remainderOfName() + } - /** Gets the name of topmost module or package being imported */ - string topModuleName() { - result = relativeTopName() - or - not exists(relativeTopName()) and - result = this.qualifiedTopName() - } + /** Gets the name of topmost module or package being imported */ + string topModuleName() { + result = relativeTopName() + or + not exists(relativeTopName()) and + result = this.qualifiedTopName() + } - /** - * Gets the full name of the module resulting from evaluating this import. - * NOTE: This is the name that used to import the module, - * which may not be the name of the module. - */ - string getImportedModuleName() { - exists(string bottomName | bottomName = this.bottomModuleName() | - if this.isTop() then result = topModuleName() else result = bottomName - ) - } + /** + * Gets the full name of the module resulting from evaluating this import. + * NOTE: This is the name that used to import the module, + * which may not be the name of the module. + */ + string getImportedModuleName() { + exists(string bottomName | bottomName = this.bottomModuleName() | + if this.isTop() then result = topModuleName() else result = bottomName + ) + } - /** - * Gets the names of the modules that may be imported by this import. - * For example this predicate would return 'x' and 'x.y' for `import x.y` - */ - string getAnImportedModuleName() { - result = this.bottomModuleName() - or - result = this.getAnImportedModuleName().regexpReplaceAll("\\.[^.]*$", "") - } + /** + * Gets the names of the modules that may be imported by this import. + * For example this predicate would return 'x' and 'x.y' for `import x.y` + */ + string getAnImportedModuleName() { + result = this.bottomModuleName() + or + result = this.getAnImportedModuleName().regexpReplaceAll("\\.[^.]*$", "") + } - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } - private string getTopName() { result = this.getName().regexpReplaceAll("\\..*", "") } + private string getTopName() { result = this.getName().regexpReplaceAll("\\..*", "") } - private string remainderOfName() { - not exists(this.getName()) and result = "" - or - this.getLevel() <= 0 and result = this.getName().regexpReplaceAll("^[^\\.]*", "") - or - this.getLevel() > 0 and result = "." + this.getName() - } + private string remainderOfName() { + not exists(this.getName()) and result = "" + or + this.getLevel() <= 0 and result = this.getName().regexpReplaceAll("^[^\\.]*", "") + or + this.getLevel() > 0 and result = "." + this.getName() + } - /** - * Whether this import is relative, that is not absolute. - * See https://www.python.org/dev/peps/pep-0328/ - */ - predicate isRelative() { - /* Implicit */ - exists(this.relativeTopName()) - or - /* Explicit */ - this.getLevel() > 0 - } + /** + * Whether this import is relative, that is not absolute. + * See https://www.python.org/dev/peps/pep-0328/ + */ + predicate isRelative() { + /* Implicit */ + exists(this.relativeTopName()) + or + /* Explicit */ + this.getLevel() > 0 + } } /** A `from ... import ...` expression */ class ImportMember extends ImportMember_ { - override Expr getASubExpression() { result = this.getModule() } + override Expr getASubExpression() { result = this.getModule() } - override predicate hasSideEffects() { - /* Strictly this only has side-effects if the module is a package */ - any() - } + override predicate hasSideEffects() { + /* Strictly this only has side-effects if the module is a package */ + any() + } - /** - * Gets the full name of the module resulting from evaluating this import. - * NOTE: This is the name that used to import the module, - * which may not be the name of the module. - */ - string getImportedModuleName() { - result = this.getModule().(ImportExpr).getImportedModuleName() + "." + this.getName() - } + /** + * Gets the full name of the module resulting from evaluating this import. + * NOTE: This is the name that used to import the module, + * which may not be the name of the module. + */ + string getImportedModuleName() { + result = this.getModule().(ImportExpr).getImportedModuleName() + "." + this.getName() + } - override ImportMemberNode getAFlowNode() { result = super.getAFlowNode() } + override ImportMemberNode getAFlowNode() { result = super.getAFlowNode() } } /** An import statement */ class Import extends Import_ { - /* syntax: import modname */ - private ImportExpr getAModuleExpr() { - result = this.getAName().getValue() - or - result = this.getAName().getValue().(ImportMember).getModule() - } + /* syntax: import modname */ + private ImportExpr getAModuleExpr() { + result = this.getAName().getValue() + or + result = this.getAName().getValue().(ImportMember).getModule() + } - /** - * Use getAnImportedModuleName(), - * possibly combined with ModuleObject.importedAs() - * Gets a module imported by this import statement - */ - deprecated Module getAModule() { result.getName() = this.getAnImportedModuleName() } + /** + * Use getAnImportedModuleName(), + * possibly combined with ModuleObject.importedAs() + * Gets a module imported by this import statement + */ + deprecated Module getAModule() { result.getName() = this.getAnImportedModuleName() } - /** Whether this a `from ... import ...` statement */ - predicate isFromImport() { this.getAName().getValue() instanceof ImportMember } + /** Whether this a `from ... import ...` statement */ + predicate isFromImport() { this.getAName().getValue() instanceof ImportMember } - override Expr getASubExpression() { - result = this.getAModuleExpr() or - result = this.getAName().getAsname() or - result = this.getAName().getValue() - } + override Expr getASubExpression() { + result = this.getAModuleExpr() or + result = this.getAName().getAsname() or + result = this.getAName().getValue() + } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } - /** - * Gets the name of an imported module. - * For example, for the import statement `import bar` which - * is a relative import in package "foo", this would return - * "foo.bar". - * The import statment `from foo import bar` would return - * `foo` and `foo.bar` - */ - string getAnImportedModuleName() { - result = this.getAModuleExpr().getAnImportedModuleName() - or - exists(ImportMember m, string modname | - m = this.getAName().getValue() and - modname = m.getModule().(ImportExpr).getImportedModuleName() - | - result = modname - or - result = modname + "." + m.getName() - ) - } + /** + * Gets the name of an imported module. + * For example, for the import statement `import bar` which + * is a relative import in package "foo", this would return + * "foo.bar". + * The import statment `from foo import bar` would return + * `foo` and `foo.bar` + */ + string getAnImportedModuleName() { + result = this.getAModuleExpr().getAnImportedModuleName() + or + exists(ImportMember m, string modname | + m = this.getAName().getValue() and + modname = m.getModule().(ImportExpr).getImportedModuleName() + | + result = modname + or + result = modname + "." + m.getName() + ) + } } /** An import * statement */ class ImportStar extends ImportStar_ { - /* syntax: from modname import * */ - ImportExpr getModuleExpr() { - result = this.getModule() - or - result = this.getModule().(ImportMember).getModule() - } + /* syntax: from modname import * */ + ImportExpr getModuleExpr() { + result = this.getModule() + or + result = this.getModule().(ImportMember).getModule() + } - override string toString() { result = "from " + this.getModuleExpr().getName() + " import *" } + override string toString() { result = "from " + this.getModuleExpr().getName() + " import *" } - /** - * Use getAnImportedModuleName(), - * possibly combined with ModuleObject.importedAs() - * Gets the module imported by this import * statement - */ - deprecated Module getTheModule() { result.getName() = this.getImportedModuleName() } + /** + * Use getAnImportedModuleName(), + * possibly combined with ModuleObject.importedAs() + * Gets the module imported by this import * statement + */ + deprecated Module getTheModule() { result.getName() = this.getImportedModuleName() } - override Expr getASubExpression() { result = this.getModule() } + override Expr getASubExpression() { result = this.getModule() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } - /** Gets the name of the imported module. */ - string getImportedModuleName() { result = this.getModuleExpr().getImportedModuleName() } + /** Gets the name of the imported module. */ + string getImportedModuleName() { result = this.getModuleExpr().getImportedModuleName() } } /** @@ -236,16 +236,16 @@ class ImportStar extends ImportStar_ { * such as `import sys`, `from sys import version` or `from sys import *`. */ class ImportingStmt extends Stmt { - ImportingStmt() { - this instanceof Import - or - this instanceof ImportStar - } + ImportingStmt() { + this instanceof Import + or + this instanceof ImportStar + } - /** Gets the name of an imported module. */ - string getAnImportedModuleName() { - result = this.(Import).getAnImportedModuleName() - or - result = this.(ImportStar).getImportedModuleName() - } + /** Gets the name of an imported module. */ + string getAnImportedModuleName() { + result = this.(Import).getAnImportedModuleName() + or + result = this.(ImportStar).getImportedModuleName() + } } diff --git a/python/ql/src/semmle/python/Keywords.qll b/python/ql/src/semmle/python/Keywords.qll index 7d8b687f83c..406ba833fb8 100644 --- a/python/ql/src/semmle/python/Keywords.qll +++ b/python/ql/src/semmle/python/Keywords.qll @@ -1,60 +1,60 @@ import python class KeyValuePair extends KeyValuePair_, DictDisplayItem { - /* syntax: Expr : Expr */ - override Location getLocation() { result = KeyValuePair_.super.getLocation() } + /* syntax: Expr : Expr */ + override Location getLocation() { result = KeyValuePair_.super.getLocation() } - override string toString() { result = KeyValuePair_.super.toString() } + override string toString() { result = KeyValuePair_.super.toString() } - /** Gets the value of this dictionary unpacking. */ - override Expr getValue() { result = KeyValuePair_.super.getValue() } + /** Gets the value of this dictionary unpacking. */ + override Expr getValue() { result = KeyValuePair_.super.getValue() } - override Scope getScope() { result = this.getValue().getScope() } + override Scope getScope() { result = this.getValue().getScope() } - override AstNode getAChildNode() { - result = this.getKey() - or - result = this.getValue() - } + override AstNode getAChildNode() { + result = this.getKey() + or + result = this.getValue() + } } /** A double-starred expression in a call or dict literal. */ class DictUnpacking extends DictUnpacking_, DictUnpackingOrKeyword, DictDisplayItem { - override Location getLocation() { result = DictUnpacking_.super.getLocation() } + override Location getLocation() { result = DictUnpacking_.super.getLocation() } - override string toString() { result = DictUnpacking_.super.toString() } + override string toString() { result = DictUnpacking_.super.toString() } - /** Gets the value of this dictionary unpacking. */ - override Expr getValue() { result = DictUnpacking_.super.getValue() } + /** Gets the value of this dictionary unpacking. */ + override Expr getValue() { result = DictUnpacking_.super.getValue() } - override Scope getScope() { result = this.getValue().getScope() } + override Scope getScope() { result = this.getValue().getScope() } - override AstNode getAChildNode() { result = this.getValue() } + override AstNode getAChildNode() { result = this.getValue() } } abstract class DictUnpackingOrKeyword extends DictItem { - abstract Expr getValue(); + abstract Expr getValue(); - override string toString() { result = "DictUnpackingOrKeyword with missing toString" } + override string toString() { result = "DictUnpackingOrKeyword with missing toString" } } abstract class DictDisplayItem extends DictItem { - abstract Expr getValue(); + abstract Expr getValue(); - override string toString() { result = "DictDisplayItem with missing toString" } + override string toString() { result = "DictDisplayItem with missing toString" } } /** A keyword argument in a call. For example `arg=expr` in `foo(0, arg=expr)` */ class Keyword extends Keyword_, DictUnpackingOrKeyword { - /* syntax: name = Expr */ - override Location getLocation() { result = Keyword_.super.getLocation() } + /* syntax: name = Expr */ + override Location getLocation() { result = Keyword_.super.getLocation() } - override string toString() { result = Keyword_.super.toString() } + override string toString() { result = Keyword_.super.toString() } - /** Gets the value of this keyword argument. */ - override Expr getValue() { result = Keyword_.super.getValue() } + /** Gets the value of this keyword argument. */ + override Expr getValue() { result = Keyword_.super.getValue() } - override Scope getScope() { result = this.getValue().getScope() } + override Scope getScope() { result = this.getValue().getScope() } - override AstNode getAChildNode() { result = this.getValue() } + override AstNode getAChildNode() { result = this.getValue() } } diff --git a/python/ql/src/semmle/python/Metrics.qll b/python/ql/src/semmle/python/Metrics.qll index 694843600bd..1526578f6c2 100644 --- a/python/ql/src/semmle/python/Metrics.qll +++ b/python/ql/src/semmle/python/Metrics.qll @@ -2,332 +2,332 @@ import python /** The metrics for a function */ class FunctionMetrics extends Function { - /** - * Gets the total number of lines (including blank lines) - * from the definition to the end of the function - */ - int getNumberOfLines() { py_alllines(this, result) } + /** + * Gets the total number of lines (including blank lines) + * from the definition to the end of the function + */ + int getNumberOfLines() { py_alllines(this, result) } - /** Gets the number of lines of code in the function */ - int getNumberOfLinesOfCode() { py_codelines(this, result) } + /** Gets the number of lines of code in the function */ + int getNumberOfLinesOfCode() { py_codelines(this, result) } - /** Gets the number of lines of comments in the function */ - int getNumberOfLinesOfComments() { py_commentlines(this, result) } + /** Gets the number of lines of comments in the function */ + int getNumberOfLinesOfComments() { py_commentlines(this, result) } - /** Gets the number of lines of docstring in the function */ - int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) } + /** Gets the number of lines of docstring in the function */ + int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) } - /** - * Cyclomatic complexity: - * The number of linearly independent paths through the source code. - * Computed as E - N + 2P, - * where - * E = the number of edges of the graph. - * N = the number of nodes of the graph. - * P = the number of connected components, which for a single function is 1. - */ - int getCyclomaticComplexity() { - exists(int E, int N | - N = count(BasicBlock b | b = this.getABasicBlock() and b.likelyReachable()) and - E = - count(BasicBlock b1, BasicBlock b2 | - b1 = this.getABasicBlock() and - b1.likelyReachable() and - b2 = this.getABasicBlock() and - b2.likelyReachable() and - b2 = b1.getASuccessor() and - not b1.unlikelySuccessor(b2) - ) - | - result = E - N + 2 + /** + * Cyclomatic complexity: + * The number of linearly independent paths through the source code. + * Computed as E - N + 2P, + * where + * E = the number of edges of the graph. + * N = the number of nodes of the graph. + * P = the number of connected components, which for a single function is 1. + */ + int getCyclomaticComplexity() { + exists(int E, int N | + N = count(BasicBlock b | b = this.getABasicBlock() and b.likelyReachable()) and + E = + count(BasicBlock b1, BasicBlock b2 | + b1 = this.getABasicBlock() and + b1.likelyReachable() and + b2 = this.getABasicBlock() and + b2.likelyReachable() and + b2 = b1.getASuccessor() and + not b1.unlikelySuccessor(b2) ) - } + | + result = E - N + 2 + ) + } - private BasicBlock getABasicBlock() { - result = this.getEntryNode().getBasicBlock() + private BasicBlock getABasicBlock() { + result = this.getEntryNode().getBasicBlock() + or + exists(BasicBlock mid | mid = this.getABasicBlock() and result = mid.getASuccessor()) + } + + /** + * Dependency of Callables + * One callable "this" depends on another callable "result" + * if "this" makes some call to a method that may end up being "result". + */ + FunctionMetrics getADependency() { + result != this and + not non_coupling_method(result) and + exists(Call call | call.getScope() = this | + exists(FunctionObject callee | callee.getFunction() = result | + call.getAFlowNode().getFunction().refersTo(callee) + ) + or + exists(Attribute a | call.getFunc() = a | + unique_root_method(result, a.getName()) or - exists(BasicBlock mid | mid = this.getABasicBlock() and result = mid.getASuccessor()) - } - - /** - * Dependency of Callables - * One callable "this" depends on another callable "result" - * if "this" makes some call to a method that may end up being "result". - */ - FunctionMetrics getADependency() { - result != this and - not non_coupling_method(result) and - exists(Call call | call.getScope() = this | - exists(FunctionObject callee | callee.getFunction() = result | - call.getAFlowNode().getFunction().refersTo(callee) - ) - or - exists(Attribute a | call.getFunc() = a | - unique_root_method(result, a.getName()) - or - exists(Name n | a.getObject() = n and n.getId() = "self" | - result.getScope() = this.getScope() and - result.getName() = a.getName() - ) - ) + exists(Name n | a.getObject() = n and n.getId() = "self" | + result.getScope() = this.getScope() and + result.getName() = a.getName() ) - } + ) + ) + } - /** - * Afferent Coupling - * the number of callables that depend on this method. - * This is sometimes called the "fan-in" of a method. - */ - int getAfferentCoupling() { result = count(FunctionMetrics m | m.getADependency() = this) } + /** + * Afferent Coupling + * the number of callables that depend on this method. + * This is sometimes called the "fan-in" of a method. + */ + int getAfferentCoupling() { result = count(FunctionMetrics m | m.getADependency() = this) } - /** - * Efferent Coupling - * the number of methods that this method depends on - * This is sometimes called the "fan-out" of a method. - */ - int getEfferentCoupling() { result = count(FunctionMetrics m | this.getADependency() = m) } + /** + * Efferent Coupling + * the number of methods that this method depends on + * This is sometimes called the "fan-out" of a method. + */ + int getEfferentCoupling() { result = count(FunctionMetrics m | this.getADependency() = m) } - int getNumberOfParametersWithoutDefault() { - result = - this.getPositionalParameterCount() - - count(this.getDefinition().(FunctionExpr).getArgs().getADefault()) - } + int getNumberOfParametersWithoutDefault() { + result = + this.getPositionalParameterCount() - + count(this.getDefinition().(FunctionExpr).getArgs().getADefault()) + } - int getStatementNestingDepth() { result = max(Stmt s | s.getScope() = this | getNestingDepth(s)) } + int getStatementNestingDepth() { result = max(Stmt s | s.getScope() = this | getNestingDepth(s)) } - int getNumberOfCalls() { result = count(Call c | c.getScope() = this) } + int getNumberOfCalls() { result = count(Call c | c.getScope() = this) } } /** The metrics for a class */ class ClassMetrics extends Class { - /** - * Gets the total number of lines (including blank lines) - * from the definition to the end of the class - */ - int getNumberOfLines() { py_alllines(this, result) } + /** + * Gets the total number of lines (including blank lines) + * from the definition to the end of the class + */ + int getNumberOfLines() { py_alllines(this, result) } - /** Gets the number of lines of code in the class */ - int getNumberOfLinesOfCode() { py_codelines(this, result) } + /** Gets the number of lines of code in the class */ + int getNumberOfLinesOfCode() { py_codelines(this, result) } - /** Gets the number of lines of comments in the class */ - int getNumberOfLinesOfComments() { py_commentlines(this, result) } + /** Gets the number of lines of comments in the class */ + int getNumberOfLinesOfComments() { py_commentlines(this, result) } - /** Gets the number of lines of docstrings in the class */ - int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) } + /** Gets the number of lines of docstrings in the class */ + int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) } - private predicate dependsOn(Class other) { - other != this and - ( - exists(FunctionMetrics f1, FunctionMetrics f2 | f1.getADependency() = f2 | - f1.getScope() = this and f2.getScope() = other - ) - or - exists(Function f, Call c, ClassObject cls | c.getScope() = f and f.getScope() = this | - c.getFunc().refersTo(cls) and - cls.getPyClass() = other - ) - ) - } + private predicate dependsOn(Class other) { + other != this and + ( + exists(FunctionMetrics f1, FunctionMetrics f2 | f1.getADependency() = f2 | + f1.getScope() = this and f2.getScope() = other + ) + or + exists(Function f, Call c, ClassObject cls | c.getScope() = f and f.getScope() = this | + c.getFunc().refersTo(cls) and + cls.getPyClass() = other + ) + ) + } - /** - * The afferent coupling of a class is the number of classes that - * directly depend on it. - */ - int getAfferentCoupling() { result = count(ClassMetrics t | t.dependsOn(this)) } + /** + * The afferent coupling of a class is the number of classes that + * directly depend on it. + */ + int getAfferentCoupling() { result = count(ClassMetrics t | t.dependsOn(this)) } - /** - * The efferent coupling of a class is the number of classes that - * it directly depends on. - */ - int getEfferentCoupling() { result = count(ClassMetrics t | this.dependsOn(t)) } + /** + * The efferent coupling of a class is the number of classes that + * it directly depends on. + */ + int getEfferentCoupling() { result = count(ClassMetrics t | this.dependsOn(t)) } - int getInheritanceDepth() { - exists(ClassObject cls | cls.getPyClass() = this | result = max(classInheritanceDepth(cls))) - } + int getInheritanceDepth() { + exists(ClassObject cls | cls.getPyClass() = this | result = max(classInheritanceDepth(cls))) + } - /* -------- CHIDAMBER AND KEMERER LACK OF COHESION IN METHODS ------------ */ - /* - * The aim of this metric is to try and determine whether a class - * represents one abstraction (good) or multiple abstractions (bad). - * If a class represents multiple abstractions, it should be split - * up into multiple classes. - * - * In the Chidamber and Kemerer method, this is measured as follows: - * n1 = number of pairs of distinct methods in a class that do *not* - * have at least one commonly accessed field - * n2 = number of pairs of distinct methods in a class that do - * have at least one commonly accessed field - * lcom = ((n1 - n2)/2 max 0) - * - * We divide by 2 because each pair (m1,m2) is counted twice in n1 and n2. - */ + /* -------- CHIDAMBER AND KEMERER LACK OF COHESION IN METHODS ------------ */ + /* + * The aim of this metric is to try and determine whether a class + * represents one abstraction (good) or multiple abstractions (bad). + * If a class represents multiple abstractions, it should be split + * up into multiple classes. + * + * In the Chidamber and Kemerer method, this is measured as follows: + * n1 = number of pairs of distinct methods in a class that do *not* + * have at least one commonly accessed field + * n2 = number of pairs of distinct methods in a class that do + * have at least one commonly accessed field + * lcom = ((n1 - n2)/2 max 0) + * + * We divide by 2 because each pair (m1,m2) is counted twice in n1 and n2. + */ - /** should function f be excluded from the cohesion computation? */ - predicate ignoreLackOfCohesion(Function f) { f.isInitMethod() or f.isSpecialMethod() } + /** should function f be excluded from the cohesion computation? */ + predicate ignoreLackOfCohesion(Function f) { f.isInitMethod() or f.isSpecialMethod() } - private predicate methodPair(Function m1, Function m2) { - m1.getScope() = this and - m2.getScope() = this and - not this.ignoreLackOfCohesion(m1) and - not this.ignoreLackOfCohesion(m2) and - m1 != m2 - } + private predicate methodPair(Function m1, Function m2) { + m1.getScope() = this and + m2.getScope() = this and + not this.ignoreLackOfCohesion(m1) and + not this.ignoreLackOfCohesion(m2) and + m1 != m2 + } - private predicate one_accesses_other(Function m1, Function m2) { - this.methodPair(m1, m2) and - ( - exists(SelfAttributeRead sa | - sa.getName() = m1.getName() and - sa.getScope() = m2 - ) - or - exists(SelfAttributeRead sa | - sa.getName() = m2.getName() and - sa.getScope() = m1 - ) - ) - } + private predicate one_accesses_other(Function m1, Function m2) { + this.methodPair(m1, m2) and + ( + exists(SelfAttributeRead sa | + sa.getName() = m1.getName() and + sa.getScope() = m2 + ) + or + exists(SelfAttributeRead sa | + sa.getName() = m2.getName() and + sa.getScope() = m1 + ) + ) + } - /** do m1 and m2 access a common field or one calls the other? */ - private predicate shareField(Function m1, Function m2) { - this.methodPair(m1, m2) and - exists(string name | - exists(SelfAttributeRead sa | - sa.getName() = name and - sa.getScope() = m1 - ) and - exists(SelfAttributeRead sa | - sa.getName() = name and - sa.getScope() = m2 - ) - ) - } + /** do m1 and m2 access a common field or one calls the other? */ + private predicate shareField(Function m1, Function m2) { + this.methodPair(m1, m2) and + exists(string name | + exists(SelfAttributeRead sa | + sa.getName() = name and + sa.getScope() = m1 + ) and + exists(SelfAttributeRead sa | + sa.getName() = name and + sa.getScope() = m2 + ) + ) + } - private int similarMethodPairs() { - result = - count(Function m1, Function m2 | - this.methodPair(m1, m2) and - (this.shareField(m1, m2) or this.one_accesses_other(m1, m2)) - ) / 2 - } + private int similarMethodPairs() { + result = + count(Function m1, Function m2 | + this.methodPair(m1, m2) and + (this.shareField(m1, m2) or this.one_accesses_other(m1, m2)) + ) / 2 + } - private int methodPairs() { - result = count(Function m1, Function m2 | this.methodPair(m1, m2)) / 2 - } + private int methodPairs() { + result = count(Function m1, Function m2 | this.methodPair(m1, m2)) / 2 + } - /** return Chidamber and Kemerer Lack of Cohesion */ - int getLackOfCohesionCK() { - exists(int n | - n = this.methodPairs() - 2 * this.similarMethodPairs() and - result = n.maximum(0) - ) - } + /** return Chidamber and Kemerer Lack of Cohesion */ + int getLackOfCohesionCK() { + exists(int n | + n = this.methodPairs() - 2 * this.similarMethodPairs() and + result = n.maximum(0) + ) + } - private predicate similarMethodPairDag(Function m1, Function m2, int line) { - (this.shareField(m1, m2) or this.one_accesses_other(m1, m2)) and - line = m1.getLocation().getStartLine() and - line < m2.getLocation().getStartLine() - } + private predicate similarMethodPairDag(Function m1, Function m2, int line) { + (this.shareField(m1, m2) or this.one_accesses_other(m1, m2)) and + line = m1.getLocation().getStartLine() and + line < m2.getLocation().getStartLine() + } - private predicate subgraph(Function m, int line) { - this.similarMethodPairDag(m, _, line) and not this.similarMethodPairDag(_, m, _) - or - exists(Function other | this.subgraph(other, line) | - this.similarMethodPairDag(other, m, _) or - this.similarMethodPairDag(m, other, _) - ) - } + private predicate subgraph(Function m, int line) { + this.similarMethodPairDag(m, _, line) and not this.similarMethodPairDag(_, m, _) + or + exists(Function other | this.subgraph(other, line) | + this.similarMethodPairDag(other, m, _) or + this.similarMethodPairDag(m, other, _) + ) + } - predicate unionSubgraph(Function m, int line) { line = min(int l | this.subgraph(m, l)) } + predicate unionSubgraph(Function m, int line) { line = min(int l | this.subgraph(m, l)) } - /** return Hitz and Montazeri Lack of Cohesion */ - int getLackOfCohesionHM() { result = count(int line | this.unionSubgraph(_, line)) } + /** return Hitz and Montazeri Lack of Cohesion */ + int getLackOfCohesionHM() { result = count(int line | this.unionSubgraph(_, line)) } } private int classInheritanceDepth(ClassObject cls) { - /* Prevent run-away recursion in case of circular inheritance */ - not cls.getASuperType() = cls and + /* Prevent run-away recursion in case of circular inheritance */ + not cls.getASuperType() = cls and + ( + exists(ClassObject sup | cls.getABaseType() = sup | result = classInheritanceDepth(sup) + 1) + or + not exists(cls.getABaseType()) and ( - exists(ClassObject sup | cls.getABaseType() = sup | result = classInheritanceDepth(sup) + 1) - or - not exists(cls.getABaseType()) and - ( - major_version() = 2 and result = 0 - or - major_version() > 2 and result = 1 - ) + major_version() = 2 and result = 0 + or + major_version() > 2 and result = 1 ) + ) } class ModuleMetrics extends Module { - /** Gets the total number of lines (including blank lines) in the module */ - int getNumberOfLines() { py_alllines(this, result) } + /** Gets the total number of lines (including blank lines) in the module */ + int getNumberOfLines() { py_alllines(this, result) } - /** Gets the number of lines of code in the module */ - int getNumberOfLinesOfCode() { py_codelines(this, result) } + /** Gets the number of lines of code in the module */ + int getNumberOfLinesOfCode() { py_codelines(this, result) } - /** Gets the number of lines of comments in the module */ - int getNumberOfLinesOfComments() { py_commentlines(this, result) } + /** Gets the number of lines of comments in the module */ + int getNumberOfLinesOfComments() { py_commentlines(this, result) } - /** Gets the number of lines of docstrings in the module */ - int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) } + /** Gets the number of lines of docstrings in the module */ + int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) } - /** - * The afferent coupling of a class is the number of classes that - * directly depend on it. - */ - int getAfferentCoupling() { result = count(ModuleMetrics t | t.dependsOn(this)) } + /** + * The afferent coupling of a class is the number of classes that + * directly depend on it. + */ + int getAfferentCoupling() { result = count(ModuleMetrics t | t.dependsOn(this)) } - /** - * The efferent coupling of a class is the number of classes that - * it directly depends on. - */ - int getEfferentCoupling() { result = count(ModuleMetrics t | this.dependsOn(t)) } + /** + * The efferent coupling of a class is the number of classes that + * it directly depends on. + */ + int getEfferentCoupling() { result = count(ModuleMetrics t | this.dependsOn(t)) } - private predicate dependsOn(Module other) { - other != this and - ( - exists(FunctionMetrics f1, FunctionMetrics f2 | f1.getADependency() = f2 | - f1.getEnclosingModule() = this and f2.getEnclosingModule() = other - ) - or - exists(Function f, Call c, ClassObject cls | c.getScope() = f and f.getScope() = this | - c.getFunc().refersTo(cls) and - cls.getPyClass().getEnclosingModule() = other - ) - ) - } + private predicate dependsOn(Module other) { + other != this and + ( + exists(FunctionMetrics f1, FunctionMetrics f2 | f1.getADependency() = f2 | + f1.getEnclosingModule() = this and f2.getEnclosingModule() = other + ) + or + exists(Function f, Call c, ClassObject cls | c.getScope() = f and f.getScope() = this | + c.getFunc().refersTo(cls) and + cls.getPyClass().getEnclosingModule() = other + ) + ) + } } /** Helpers for coupling */ predicate unique_root_method(Function func, string name) { - name = func.getName() and - not exists(FunctionObject f, FunctionObject other | - f.getFunction() = func and - other.getName() = name - | - not other.overrides(f) - ) + name = func.getName() and + not exists(FunctionObject f, FunctionObject other | + f.getFunction() = func and + other.getName() = name + | + not other.overrides(f) + ) } predicate non_coupling_method(Function f) { - f.isSpecialMethod() or - f.isInitMethod() or - f.getName() = "close" or - f.getName() = "write" or - f.getName() = "read" or - f.getName() = "get" or - f.getName() = "set" + f.isSpecialMethod() or + f.isInitMethod() or + f.getName() = "close" or + f.getName() = "write" or + f.getName() = "read" or + f.getName() = "get" or + f.getName() = "set" } private int getNestingDepth(Stmt s) { - not exists(Stmt outer | outer.getASubStatement() = s) and result = 1 - or - exists(Stmt outer | outer.getASubStatement() = s | - if s.(If).isElif() or s instanceof ExceptStmt - then - /* If statement is an `elif` or `except` then it is not indented relative to its parent */ - result = getNestingDepth(outer) - else result = getNestingDepth(outer) + 1 - ) + not exists(Stmt outer | outer.getASubStatement() = s) and result = 1 + or + exists(Stmt outer | outer.getASubStatement() = s | + if s.(If).isElif() or s instanceof ExceptStmt + then + /* If statement is an `elif` or `except` then it is not indented relative to its parent */ + result = getNestingDepth(outer) + else result = getNestingDepth(outer) + 1 + ) } diff --git a/python/ql/src/semmle/python/Module.qll b/python/ql/src/semmle/python/Module.qll index 4d428636893..fcf1c0b2925 100644 --- a/python/ql/src/semmle/python/Module.qll +++ b/python/ql/src/semmle/python/Module.qll @@ -7,174 +7,174 @@ private import semmle.python.objects.Modules * It is also a Scope; the scope of global variables. */ class Module extends Module_, Scope, AstNode { - override string toString() { - result = this.getKind() + " " + this.getName() - or - /* No name is defined, which means that this module is not on an import path. So it must be a script */ - not exists(this.getName()) and - not this.isPackage() and - result = "Script " + this.getFile().getShortName() - or - /* Package missing name, so just use the path instead */ - not exists(this.getName()) and - this.isPackage() and - result = "Package at " + this.getPath().getAbsolutePath() - } + override string toString() { + result = this.getKind() + " " + this.getName() + or + /* No name is defined, which means that this module is not on an import path. So it must be a script */ + not exists(this.getName()) and + not this.isPackage() and + result = "Script " + this.getFile().getShortName() + or + /* Package missing name, so just use the path instead */ + not exists(this.getName()) and + this.isPackage() and + result = "Package at " + this.getPath().getAbsolutePath() + } - /** - * This method will be deprecated in the next release. Please use `getEnclosingScope()` instead. - * The enclosing scope of this module (always none) - */ - override Scope getScope() { none() } + /** + * This method will be deprecated in the next release. Please use `getEnclosingScope()` instead. + * The enclosing scope of this module (always none) + */ + override Scope getScope() { none() } - /** The enclosing scope of this module (always none) */ - override Scope getEnclosingScope() { none() } + /** The enclosing scope of this module (always none) */ + override Scope getEnclosingScope() { none() } - /** Gets the statements forming the body of this module */ - override StmtList getBody() { result = Module_.super.getBody() } + /** Gets the statements forming the body of this module */ + override StmtList getBody() { result = Module_.super.getBody() } - /** Gets the nth statement of this module */ - override Stmt getStmt(int n) { result = Module_.super.getStmt(n) } + /** Gets the nth statement of this module */ + override Stmt getStmt(int n) { result = Module_.super.getStmt(n) } - /** Gets a top-level statement in this module */ - override Stmt getAStmt() { result = Module_.super.getAStmt() } + /** Gets a top-level statement in this module */ + override Stmt getAStmt() { result = Module_.super.getAStmt() } - /** Gets the name of this module */ - override string getName() { - result = Module_.super.getName() and legalDottedName(result) - or - not exists(Module_.super.getName()) and - result = moduleNameFromFile(this.getPath()) - } + /** Gets the name of this module */ + override string getName() { + result = Module_.super.getName() and legalDottedName(result) + or + not exists(Module_.super.getName()) and + result = moduleNameFromFile(this.getPath()) + } - /** Gets the short name of the module. For example the short name of module x.y.z is 'z' */ - string getShortName() { - result = this.getName().suffix(this.getPackage().getName().length() + 1) - or - result = this.getName() and not exists(this.getPackage()) - } + /** Gets the short name of the module. For example the short name of module x.y.z is 'z' */ + string getShortName() { + result = this.getName().suffix(this.getPackage().getName().length() + 1) + or + result = this.getName() and not exists(this.getPackage()) + } - /** Gets this module */ - override Module getEnclosingModule() { result = this } + /** Gets this module */ + override Module getEnclosingModule() { result = this } - /** Gets the __init__ module of this module if the module is a package and it has an __init__ module */ - Module getInitModule() { - /* this.isPackage() and */ result.getName() = this.getName() + ".__init__" - } + /** Gets the __init__ module of this module if the module is a package and it has an __init__ module */ + Module getInitModule() { + /* this.isPackage() and */ result.getName() = this.getName() + ".__init__" + } - /** Whether this module is a package initializer */ - predicate isPackageInit() { this.getName().matches("%\\_\\_init\\_\\_") and not this.isPackage() } + /** Whether this module is a package initializer */ + predicate isPackageInit() { this.getName().matches("%\\_\\_init\\_\\_") and not this.isPackage() } - /** Gets a name exported by this module, that is the names that will be added to a namespace by 'from this-module import *' */ - string getAnExport() { - py_exports(this, result) - or - exists(ModuleObjectInternal mod | mod.getSource() = this.getEntryNode() | - mod.(ModuleValue).exports(result) - ) - } + /** Gets a name exported by this module, that is the names that will be added to a namespace by 'from this-module import *' */ + string getAnExport() { + py_exports(this, result) + or + exists(ModuleObjectInternal mod | mod.getSource() = this.getEntryNode() | + mod.(ModuleValue).exports(result) + ) + } - /** Gets the source file for this module */ - File getFile() { py_module_path(this, result) } + /** Gets the source file for this module */ + File getFile() { py_module_path(this, result) } - /** Gets the source file or folder for this module or package */ - Container getPath() { py_module_path(this, result) } + /** Gets the source file or folder for this module or package */ + Container getPath() { py_module_path(this, result) } - /** Whether this is a package */ - predicate isPackage() { this.getPath() instanceof Folder } + /** Whether this is a package */ + predicate isPackage() { this.getPath() instanceof Folder } - /** Gets the package containing this module (or parent package if this is a package) */ - Module getPackage() { - this.getName().matches("%.%") and - result.getName() = getName().regexpReplaceAll("\\.[^.]*$", "") - } + /** Gets the package containing this module (or parent package if this is a package) */ + Module getPackage() { + this.getName().matches("%.%") and + result.getName() = getName().regexpReplaceAll("\\.[^.]*$", "") + } - /** Gets the name of the package containing this module */ - string getPackageName() { - this.getName().matches("%.%") and - result = getName().regexpReplaceAll("\\.[^.]*$", "") - } + /** Gets the name of the package containing this module */ + string getPackageName() { + this.getName().matches("%.%") and + result = getName().regexpReplaceAll("\\.[^.]*$", "") + } - /** Gets the metrics for this module */ - ModuleMetrics getMetrics() { result = this } + /** Gets the metrics for this module */ + ModuleMetrics getMetrics() { result = this } - /** - * Use ModuleObject.getAnImportedModule() instead. - * Gets a module imported by this module - */ - deprecated Module getAnImportedModule() { result.getName() = this.getAnImportedModuleName() } + /** + * Use ModuleObject.getAnImportedModule() instead. + * Gets a module imported by this module + */ + deprecated Module getAnImportedModule() { result.getName() = this.getAnImportedModuleName() } - string getAnImportedModuleName() { - exists(Import i | i.getEnclosingModule() = this | result = i.getAnImportedModuleName()) - or - exists(ImportStar i | i.getEnclosingModule() = this | result = i.getImportedModuleName()) - } + string getAnImportedModuleName() { + exists(Import i | i.getEnclosingModule() = this | result = i.getAnImportedModuleName()) + or + exists(ImportStar i | i.getEnclosingModule() = this | result = i.getImportedModuleName()) + } - override Location getLocation() { - py_scope_location(result, this) - or - not py_scope_location(_, this) and - locations_ast(result, this, 0, 0, 0, 0) - } + override Location getLocation() { + py_scope_location(result, this) + or + not py_scope_location(_, this) and + locations_ast(result, this, 0, 0, 0, 0) + } - /** Gets a child module or package of this package */ - Module getSubModule(string name) { - result.getPackage() = this and - name = result.getName().regexpReplaceAll(".*\\.", "") - } + /** Gets a child module or package of this package */ + Module getSubModule(string name) { + result.getPackage() = this and + name = result.getName().regexpReplaceAll(".*\\.", "") + } - /** Whether name is declared in the __all__ list of this module */ - predicate declaredInAll(string name) { - exists(AssignStmt a, GlobalVariable all | - a.defines(all) and - a.getScope() = this and - all.getId() = "__all__" and - a.getValue().(List).getAnElt().(StrConst).getText() = name - ) - } + /** Whether name is declared in the __all__ list of this module */ + predicate declaredInAll(string name) { + exists(AssignStmt a, GlobalVariable all | + a.defines(all) and + a.getScope() = this and + all.getId() = "__all__" and + a.getValue().(List).getAnElt().(StrConst).getText() = name + ) + } - override AstNode getAChildNode() { result = this.getAStmt() } + override AstNode getAChildNode() { result = this.getAStmt() } - predicate hasFromFuture(string attr) { - exists(Import i, ImportMember im, ImportExpr ie, Alias a, Name name | - im.getModule() = ie and - ie.getName() = "__future__" and - a.getAsname() = name and - name.getId() = attr and - i.getASubExpression() = im and - i.getAName() = a and - i.getEnclosingModule() = this - ) - } + predicate hasFromFuture(string attr) { + exists(Import i, ImportMember im, ImportExpr ie, Alias a, Name name | + im.getModule() = ie and + ie.getName() = "__future__" and + a.getAsname() = name and + name.getId() = attr and + i.getASubExpression() = im and + i.getAName() = a and + i.getEnclosingModule() = this + ) + } - /** Gets the path element from which this module was loaded. */ - Container getLoadPath() { result = this.getPath().getImportRoot() } + /** Gets the path element from which this module was loaded. */ + Container getLoadPath() { result = this.getPath().getImportRoot() } - /** Holds if this module is in the standard library for version `major.minor` */ - predicate inStdLib(int major, int minor) { this.getLoadPath().isStdLibRoot(major, minor) } + /** Holds if this module is in the standard library for version `major.minor` */ + predicate inStdLib(int major, int minor) { this.getLoadPath().isStdLibRoot(major, minor) } - /** Holds if this module is in the standard library */ - predicate inStdLib() { this.getLoadPath().isStdLibRoot() } + /** Holds if this module is in the standard library */ + predicate inStdLib() { this.getLoadPath().isStdLibRoot() } - override predicate containsInScope(AstNode inner) { Scope.super.containsInScope(inner) } + override predicate containsInScope(AstNode inner) { Scope.super.containsInScope(inner) } - override predicate contains(AstNode inner) { Scope.super.contains(inner) } + override predicate contains(AstNode inner) { Scope.super.contains(inner) } - /** Gets the kind of this module. */ - override string getKind() { - if this.isPackage() - then result = "Package" - else ( - not exists(Module_.super.getKind()) and result = "Module" - or - result = Module_.super.getKind() - ) - } + /** Gets the kind of this module. */ + override string getKind() { + if this.isPackage() + then result = "Package" + else ( + not exists(Module_.super.getKind()) and result = "Module" + or + result = Module_.super.getKind() + ) + } } bindingset[name] private predicate legalDottedName(string name) { - name.regexpMatch("(\\p{L}|_)(\\p{L}|\\d|_)*(\\.(\\p{L}|_)(\\p{L}|\\d|_)*)*") + name.regexpMatch("(\\p{L}|_)(\\p{L}|\\d|_)*(\\.(\\p{L}|_)(\\p{L}|\\d|_)*)*") } bindingset[name] @@ -185,44 +185,44 @@ private predicate legalShortName(string name) { name.regexpMatch("(\\p{L}|_)(\\p * Does it have an __init__.py file (or --respect-init=False for Python 2) and is it within the source archive? */ private predicate isPotentialSourcePackage(Folder f) { - f.getRelativePath() != "" and - isPotentialPackage(f) + f.getRelativePath() != "" and + isPotentialPackage(f) } private predicate isPotentialPackage(Folder f) { - exists(f.getFile("__init__.py")) - or - py_flags_versioned("options.respect_init", "False", _) and major_version() = 2 + exists(f.getFile("__init__.py")) + or + py_flags_versioned("options.respect_init", "False", _) and major_version() = 2 } private string moduleNameFromBase(Container file) { - isPotentialPackage(file) and result = file.getBaseName() - or - file instanceof File and result = file.getStem() + isPotentialPackage(file) and result = file.getBaseName() + or + file instanceof File and result = file.getStem() } string moduleNameFromFile(Container file) { - exists(string basename | - basename = moduleNameFromBase(file) and - legalShortName(basename) and - result = moduleNameFromFile(file.getParent()) + "." + basename - ) - or - isPotentialSourcePackage(file) and - result = file.getStem() and - ( - not isPotentialSourcePackage(file.getParent()) or - not legalShortName(file.getParent().getBaseName()) - ) - or - result = file.getStem() and file.getParent() = file.getImportRoot() - or - result = file.getStem() and isStubRoot(file.getParent()) + exists(string basename | + basename = moduleNameFromBase(file) and + legalShortName(basename) and + result = moduleNameFromFile(file.getParent()) + "." + basename + ) + or + isPotentialSourcePackage(file) and + result = file.getStem() and + ( + not isPotentialSourcePackage(file.getParent()) or + not legalShortName(file.getParent().getBaseName()) + ) + or + result = file.getStem() and file.getParent() = file.getImportRoot() + or + result = file.getStem() and isStubRoot(file.getParent()) } private predicate isStubRoot(Folder f) { - not f.getParent*().isImportRoot() and - f.getAbsolutePath().matches("%/data/python/stubs") + not f.getParent*().isImportRoot() and + f.getAbsolutePath().matches("%/data/python/stubs") } /** @@ -233,22 +233,22 @@ private predicate isStubRoot(Folder f) { * this is the module most likely to be imported under that name. */ predicate isPreferredModuleForName(Container c, string name) { - exists(int p | - p = min(int x | x = priorityForName(_, name)) and - p = priorityForName(c, name) - ) + exists(int p | + p = min(int x | x = priorityForName(_, name)) and + p = priorityForName(c, name) + ) } private int priorityForName(Container c, string name) { - name = moduleNameFromFile(c) and - ( - // In the source - exists(c.getRelativePath()) and result = -1 - or - // On an import path - exists(c.getImportRoot(result)) - or - // Otherwise - result = 10000 - ) + name = moduleNameFromFile(c) and + ( + // In the source + exists(c.getRelativePath()) and result = -1 + or + // On an import path + exists(c.getImportRoot(result)) + or + // Otherwise + result = 10000 + ) } diff --git a/python/ql/src/semmle/python/Operations.qll b/python/ql/src/semmle/python/Operations.qll index c6dd102350c..b057fde5155 100644 --- a/python/ql/src/semmle/python/Operations.qll +++ b/python/ql/src/semmle/python/Operations.qll @@ -2,133 +2,133 @@ import python /** Base class for operators */ class Operator extends Operator_ { - /** Gets the name of the special method used to implement this operator */ - string getSpecialMethodName() { none() } + /** Gets the name of the special method used to implement this operator */ + string getSpecialMethodName() { none() } } /* Unary Expression and its operators */ /** A unary expression: (`+x`), (`-x`) or (`~x`) */ class UnaryExpr extends UnaryExpr_ { - override Expr getASubExpression() { result = this.getOperand() } + override Expr getASubExpression() { result = this.getOperand() } } /** A unary operator: `+`, `-`, `~` or `not` */ class Unaryop extends Unaryop_ { - /** Gets the name of the special method used to implement this operator */ - string getSpecialMethodName() { none() } + /** Gets the name of the special method used to implement this operator */ + string getSpecialMethodName() { none() } } /** An invert (`~`) unary operator */ class Invert extends Invert_ { - override string getSpecialMethodName() { result = "__invert__" } + override string getSpecialMethodName() { result = "__invert__" } } /** A positive (`+`) unary operator */ class UAdd extends UAdd_ { - override string getSpecialMethodName() { result = "__pos__" } + override string getSpecialMethodName() { result = "__pos__" } } /** A negation (`-`) unary operator */ class USub extends USub_ { - override string getSpecialMethodName() { result = "__neg__" } + override string getSpecialMethodName() { result = "__neg__" } } /** A `not` unary operator */ class Not extends Not_ { - override string getSpecialMethodName() { none() } + override string getSpecialMethodName() { none() } } /* Binary Operation and its operators */ /** A binary expression, such as `x + y` */ class BinaryExpr extends BinaryExpr_ { - override Expr getASubExpression() { result = this.getLeft() or result = this.getRight() } + override Expr getASubExpression() { result = this.getLeft() or result = this.getRight() } } /** A power (`**`) binary operator */ class Pow extends Pow_ { - override string getSpecialMethodName() { result = "__pow__" } + override string getSpecialMethodName() { result = "__pow__" } } /** A right shift (`>>`) binary operator */ class RShift extends RShift_ { - override string getSpecialMethodName() { result = "__rshift__" } + override string getSpecialMethodName() { result = "__rshift__" } } /** A subtract (`-`) binary operator */ class Sub extends Sub_ { - override string getSpecialMethodName() { result = "__sub__" } + override string getSpecialMethodName() { result = "__sub__" } } /** A bitwise and (`&`) binary operator */ class BitAnd extends BitAnd_ { - override string getSpecialMethodName() { result = "__and__" } + override string getSpecialMethodName() { result = "__and__" } } /** A bitwise or (`|`) binary operator */ class BitOr extends BitOr_ { - override string getSpecialMethodName() { result = "__or__" } + override string getSpecialMethodName() { result = "__or__" } } /** A bitwise exclusive-or (`^`) binary operator */ class BitXor extends BitXor_ { - override string getSpecialMethodName() { result = "__xor__" } + override string getSpecialMethodName() { result = "__xor__" } } /** An add (`+`) binary operator */ class Add extends Add_ { - override string getSpecialMethodName() { result = "__add__" } + override string getSpecialMethodName() { result = "__add__" } } /** An (true) divide (`/`) binary operator */ class Div extends Div_ { - override string getSpecialMethodName() { - result = "__truediv__" - or - major_version() = 2 and result = "__div__" - } + override string getSpecialMethodName() { + result = "__truediv__" + or + major_version() = 2 and result = "__div__" + } } /** An floor divide (`//`) binary operator */ class FloorDiv extends FloorDiv_ { - override string getSpecialMethodName() { result = "__floordiv__" } + override string getSpecialMethodName() { result = "__floordiv__" } } /** A left shift (`<<`) binary operator */ class LShift extends LShift_ { - override string getSpecialMethodName() { result = "__lshift__" } + override string getSpecialMethodName() { result = "__lshift__" } } /** A modulo (`%`) binary operator, which includes string formatting */ class Mod extends Mod_ { - override string getSpecialMethodName() { result = "__mod__" } + override string getSpecialMethodName() { result = "__mod__" } } /** A multiplication (`*`) binary operator */ class Mult extends Mult_ { - override string getSpecialMethodName() { result = "__mul__" } + override string getSpecialMethodName() { result = "__mul__" } } /** A matrix multiplication (`@`) binary operator */ class MatMult extends MatMult_ { - override string getSpecialMethodName() { result = "__matmul__" } + override string getSpecialMethodName() { result = "__matmul__" } } /* Comparison Operation and its operators */ /** A comparison operation, such as `x`) comparison operator */ class Gt extends Gt_ { - override string getSymbol() { result = ">" } + override string getSymbol() { result = ">" } - override string getSpecialMethodName() { result = "__gt__" } + override string getSpecialMethodName() { result = "__gt__" } } /** A greater than or equals (`>=`) comparison operator */ class GtE extends GtE_ { - override string getSymbol() { result = ">=" } + override string getSymbol() { result = ">=" } - override string getSpecialMethodName() { result = "__ge__" } + override string getSpecialMethodName() { result = "__ge__" } } /** An `in` comparison operator */ class In extends In_ { - override string getSymbol() { result = "in" } + override string getSymbol() { result = "in" } } /** An `is` comparison operator */ class Is extends Is_ { - override string getSymbol() { result = "is" } + override string getSymbol() { result = "is" } } /** An `is not` comparison operator */ class IsNot extends IsNot_ { - override string getSymbol() { result = "is not" } + override string getSymbol() { result = "is not" } } /** An equals (`==`) comparison operator */ class Eq extends Eq_ { - override string getSymbol() { result = "==" } + override string getSymbol() { result = "==" } - override string getSpecialMethodName() { result = "__eq__" } + override string getSpecialMethodName() { result = "__eq__" } } /** A less than (`<`) comparison operator */ class Lt extends Lt_ { - override string getSymbol() { result = "<" } + override string getSymbol() { result = "<" } - override string getSpecialMethodName() { result = "__lt__" } + override string getSpecialMethodName() { result = "__lt__" } } /** A less than or equals (`<=`) comparison operator */ class LtE extends LtE_ { - override string getSymbol() { result = "<=" } + override string getSymbol() { result = "<=" } - override string getSpecialMethodName() { result = "__le__" } + override string getSpecialMethodName() { result = "__le__" } } /** A not equals (`!=`) comparison operator */ class NotEq extends NotEq_ { - override string getSymbol() { result = "!=" } + override string getSymbol() { result = "!=" } - override string getSpecialMethodName() { result = "__ne__" } + override string getSpecialMethodName() { result = "__ne__" } } /** An `not in` comparison operator */ class NotIn extends NotIn_ { - override string getSymbol() { result = "not in" } + override string getSymbol() { result = "not in" } } /* Boolean Operation (and/or) and its operators */ /** A boolean shortcut (and/or) operation */ class BoolExpr extends BoolExpr_ { - override Expr getASubExpression() { result = this.getAValue() } + override Expr getASubExpression() { result = this.getAValue() } - string getOperator() { - this.getOp() instanceof And and result = "and" - or - this.getOp() instanceof Or and result = "or" - } + string getOperator() { + this.getOp() instanceof And and result = "and" + or + this.getOp() instanceof Or and result = "or" + } - /** Whether part evaluates to partIsTrue if this evaluates to wholeIsTrue */ - predicate impliesValue(Expr part, boolean partIsTrue, boolean wholeIsTrue) { - if this.getOp() instanceof And - then ( - wholeIsTrue = true and partIsTrue = true and part = this.getAValue() - or - wholeIsTrue = true and this.getAValue().(BoolExpr).impliesValue(part, partIsTrue, true) - ) else ( - wholeIsTrue = false and partIsTrue = false and part = this.getAValue() - or - wholeIsTrue = false and this.getAValue().(BoolExpr).impliesValue(part, partIsTrue, false) - ) - } + /** Whether part evaluates to partIsTrue if this evaluates to wholeIsTrue */ + predicate impliesValue(Expr part, boolean partIsTrue, boolean wholeIsTrue) { + if this.getOp() instanceof And + then ( + wholeIsTrue = true and partIsTrue = true and part = this.getAValue() + or + wholeIsTrue = true and this.getAValue().(BoolExpr).impliesValue(part, partIsTrue, true) + ) else ( + wholeIsTrue = false and partIsTrue = false and part = this.getAValue() + or + wholeIsTrue = false and this.getAValue().(BoolExpr).impliesValue(part, partIsTrue, false) + ) + } } /** A short circuit boolean operator, and/or */ diff --git a/python/ql/src/semmle/python/SSA.qll b/python/ql/src/semmle/python/SSA.qll index ac7ab8e7d32..deaa0db914e 100644 --- a/python/ql/src/semmle/python/SSA.qll +++ b/python/ql/src/semmle/python/SSA.qll @@ -9,202 +9,202 @@ import python * Definitions without uses do not have a SSA variable. */ class SsaVariable extends @py_ssa_var { - SsaVariable() { py_ssa_var(this, _) } + SsaVariable() { py_ssa_var(this, _) } - /** Gets the source variable */ - Variable getVariable() { py_ssa_var(this, result) } + /** Gets the source variable */ + Variable getVariable() { py_ssa_var(this, result) } - /** Gets a use of this variable */ - ControlFlowNode getAUse() { py_ssa_use(result, this) } + /** Gets a use of this variable */ + ControlFlowNode getAUse() { py_ssa_use(result, this) } - /** Gets the definition (which may be a deletion) of this SSA variable */ - ControlFlowNode getDefinition() { py_ssa_defn(this, result) } + /** Gets the definition (which may be a deletion) of this SSA variable */ + ControlFlowNode getDefinition() { py_ssa_defn(this, result) } - /** - * Gets an argument of the phi function defining this variable. - * This predicate uses the raw SSA form produced by the extractor. - * In general, you should use `getAPrunedPhiInput()` instead. - */ - SsaVariable getAPhiInput() { py_ssa_phi(this, result) } - - /** - * Gets the edge(s) (result->this.getDefinition()) on which the SSA variable 'input' defines this SSA variable. - * For each incoming edge `X->B`, where `B` is the basic block containing this phi-node, only one of the input SSA variables - * for this phi-node is live. This predicate returns the predecessor block such that the variable 'input' - * is the live variable on the edge result->B. - */ - BasicBlock getPredecessorBlockForPhiArgument(SsaVariable input) { - input = this.getAPhiInput() and - result = this.getAPredecessorBlockForPhi() and - input.getDefinition().getBasicBlock().dominates(result) and - /* - * Beware the case where an SSA variable that is an input on one edge dominates another edge. - * Consider (in SSA form): - * x0 = 0 - * if cond: - * x1 = 1 - * x2 = phi(x0, x1) - * use(x2) - * - * The definition of x0 dominates the exit from the block x1=1, even though it does not reach it. - * Hence we need to check that no other definition dominates the edge and actually reaches it. - * Note that if a dominates c and b dominates c, then either a dominates b or vice-versa. - */ - - not exists(SsaVariable other, BasicBlock other_def | - not other = input and - other = this.getAPhiInput() and - other_def = other.getDefinition().getBasicBlock() - | - other_def.dominates(result) and - input.getDefinition().getBasicBlock().strictlyDominates(other_def) - ) - } - - /** Gets an argument of the phi function defining this variable, pruned of unlikely edges. */ - SsaVariable getAPrunedPhiInput() { - result = this.getAPhiInput() and - exists(BasicBlock incoming | incoming = this.getPredecessorBlockForPhiArgument(result) | - not incoming.getLastNode().(RaisingNode).unlikelySuccessor(this.getDefinition()) - ) - } - - /** Gets a variable that ultimately defines this variable and is not itself defined by another variable */ - SsaVariable getAnUltimateDefinition() { - result = this and not exists(this.getAPhiInput()) - or - result = this.getAPhiInput().getAnUltimateDefinition() - } - - /** Gets a textual representation of this element. */ - string toString() { result = "SSA Variable " + this.getId() } - - Location getLocation() { result = this.getDefinition().getLocation() } - - /** Gets the id (name) of this variable */ - string getId() { result = this.getVariable().getId() } - - /** Gets the incoming edges for a Phi node. */ - private BasicBlock getAPredecessorBlockForPhi() { - exists(getAPhiInput()) and - result.getASuccessor() = this.getDefinition().getBasicBlock() - } - - /** Gets the incoming edges for a Phi node, pruned of unlikely edges. */ - private BasicBlock getAPrunedPredecessorBlockForPhi() { - result = this.getAPredecessorBlockForPhi() and - not result.unlikelySuccessor(this.getDefinition().getBasicBlock()) - } - - /** Whether it is possible to reach a use of this variable without passing a definition */ - predicate reachableWithoutDefinition() { - not exists(this.getDefinition()) and not py_ssa_phi(this, _) - or - exists(SsaVariable var | var = this.getAPhiInput() | var.reachableWithoutDefinition()) - or - /* - * For phi-nodes, there must be a corresponding phi-input for each control-flow - * predecessor. Otherwise, the variable will be undefined on that incoming edge. - * WARNING: the same phi-input may cover multiple predecessors, so this check - * cannot be done by counting. - */ - - exists(BasicBlock incoming | - incoming = this.getAPredecessorBlockForPhi() and - not this.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming) - ) - } - - /** Whether this variable may be undefined */ - predicate maybeUndefined() { - not exists(this.getDefinition()) and not py_ssa_phi(this, _) and not this.implicitlyDefined() - or - this.getDefinition().isDelete() - or - exists(SsaVariable var | var = this.getAPrunedPhiInput() | var.maybeUndefined()) - or - /* - * For phi-nodes, there must be a corresponding phi-input for each control-flow - * predecessor. Otherwise, the variable will be undefined on that incoming edge. - * WARNING: the same phi-input may cover multiple predecessors, so this check - * cannot be done by counting. - */ - - exists(BasicBlock incoming | - reaches_end(incoming) and - incoming = this.getAPrunedPredecessorBlockForPhi() and - not this.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming) - ) - } - - private predicate implicitlyDefined() { - not exists(this.getDefinition()) and - not py_ssa_phi(this, _) and - exists(GlobalVariable var | this.getVariable() = var | - globallyDefinedName(var.getId()) - or - var.getId() = "__path__" and var.getScope().(Module).isPackageInit() - ) - } - - /** - * Gets the global variable that is accessed if this local is undefined. - * Only applies to local variables in class scopes. - */ - GlobalVariable getFallbackGlobal() { - exists(LocalVariable local, Class cls | this.getVariable() = local | - local.getScope() = cls and - result.getScope() = cls.getScope() and - result.getId() = local.getId() and - not exists(this.getDefinition()) - ) - } + /** + * Gets an argument of the phi function defining this variable. + * This predicate uses the raw SSA form produced by the extractor. + * In general, you should use `getAPrunedPhiInput()` instead. + */ + SsaVariable getAPhiInput() { py_ssa_phi(this, result) } + /** + * Gets the edge(s) (result->this.getDefinition()) on which the SSA variable 'input' defines this SSA variable. + * For each incoming edge `X->B`, where `B` is the basic block containing this phi-node, only one of the input SSA variables + * for this phi-node is live. This predicate returns the predecessor block such that the variable 'input' + * is the live variable on the edge result->B. + */ + BasicBlock getPredecessorBlockForPhiArgument(SsaVariable input) { + input = this.getAPhiInput() and + result = this.getAPredecessorBlockForPhi() and + input.getDefinition().getBasicBlock().dominates(result) and /* - * Whether this SSA variable is the first parameter of a method - * (regardless of whether it is actually called self or not) + * Beware the case where an SSA variable that is an input on one edge dominates another edge. + * Consider (in SSA form): + * x0 = 0 + * if cond: + * x1 = 1 + * x2 = phi(x0, x1) + * use(x2) + * + * The definition of x0 dominates the exit from the block x1=1, even though it does not reach it. + * Hence we need to check that no other definition dominates the edge and actually reaches it. + * Note that if a dominates c and b dominates c, then either a dominates b or vice-versa. */ - predicate isSelf() { - exists(Function func | - func.isMethod() and - this.getDefinition().getNode() = func.getArg(0) - ) - } + not exists(SsaVariable other, BasicBlock other_def | + not other = input and + other = this.getAPhiInput() and + other_def = other.getDefinition().getBasicBlock() + | + other_def.dominates(result) and + input.getDefinition().getBasicBlock().strictlyDominates(other_def) + ) + } + + /** Gets an argument of the phi function defining this variable, pruned of unlikely edges. */ + SsaVariable getAPrunedPhiInput() { + result = this.getAPhiInput() and + exists(BasicBlock incoming | incoming = this.getPredecessorBlockForPhiArgument(result) | + not incoming.getLastNode().(RaisingNode).unlikelySuccessor(this.getDefinition()) + ) + } + + /** Gets a variable that ultimately defines this variable and is not itself defined by another variable */ + SsaVariable getAnUltimateDefinition() { + result = this and not exists(this.getAPhiInput()) + or + result = this.getAPhiInput().getAnUltimateDefinition() + } + + /** Gets a textual representation of this element. */ + string toString() { result = "SSA Variable " + this.getId() } + + Location getLocation() { result = this.getDefinition().getLocation() } + + /** Gets the id (name) of this variable */ + string getId() { result = this.getVariable().getId() } + + /** Gets the incoming edges for a Phi node. */ + private BasicBlock getAPredecessorBlockForPhi() { + exists(getAPhiInput()) and + result.getASuccessor() = this.getDefinition().getBasicBlock() + } + + /** Gets the incoming edges for a Phi node, pruned of unlikely edges. */ + private BasicBlock getAPrunedPredecessorBlockForPhi() { + result = this.getAPredecessorBlockForPhi() and + not result.unlikelySuccessor(this.getDefinition().getBasicBlock()) + } + + /** Whether it is possible to reach a use of this variable without passing a definition */ + predicate reachableWithoutDefinition() { + not exists(this.getDefinition()) and not py_ssa_phi(this, _) + or + exists(SsaVariable var | var = this.getAPhiInput() | var.reachableWithoutDefinition()) + or + /* + * For phi-nodes, there must be a corresponding phi-input for each control-flow + * predecessor. Otherwise, the variable will be undefined on that incoming edge. + * WARNING: the same phi-input may cover multiple predecessors, so this check + * cannot be done by counting. + */ + + exists(BasicBlock incoming | + incoming = this.getAPredecessorBlockForPhi() and + not this.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming) + ) + } + + /** Whether this variable may be undefined */ + predicate maybeUndefined() { + not exists(this.getDefinition()) and not py_ssa_phi(this, _) and not this.implicitlyDefined() + or + this.getDefinition().isDelete() + or + exists(SsaVariable var | var = this.getAPrunedPhiInput() | var.maybeUndefined()) + or + /* + * For phi-nodes, there must be a corresponding phi-input for each control-flow + * predecessor. Otherwise, the variable will be undefined on that incoming edge. + * WARNING: the same phi-input may cover multiple predecessors, so this check + * cannot be done by counting. + */ + + exists(BasicBlock incoming | + reaches_end(incoming) and + incoming = this.getAPrunedPredecessorBlockForPhi() and + not this.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming) + ) + } + + private predicate implicitlyDefined() { + not exists(this.getDefinition()) and + not py_ssa_phi(this, _) and + exists(GlobalVariable var | this.getVariable() = var | + globallyDefinedName(var.getId()) + or + var.getId() = "__path__" and var.getScope().(Module).isPackageInit() + ) + } + + /** + * Gets the global variable that is accessed if this local is undefined. + * Only applies to local variables in class scopes. + */ + GlobalVariable getFallbackGlobal() { + exists(LocalVariable local, Class cls | this.getVariable() = local | + local.getScope() = cls and + result.getScope() = cls.getScope() and + result.getId() = local.getId() and + not exists(this.getDefinition()) + ) + } + + /* + * Whether this SSA variable is the first parameter of a method + * (regardless of whether it is actually called self or not) + */ + + predicate isSelf() { + exists(Function func | + func.isMethod() and + this.getDefinition().getNode() = func.getArg(0) + ) + } } private predicate reaches_end(BasicBlock b) { - not exits_early(b) and - ( - /* Entry point */ - not exists(BasicBlock prev | prev.getASuccessor() = b) - or - exists(BasicBlock prev | prev.getASuccessor() = b | reaches_end(prev)) - ) + not exits_early(b) and + ( + /* Entry point */ + not exists(BasicBlock prev | prev.getASuccessor() = b) + or + exists(BasicBlock prev | prev.getASuccessor() = b | reaches_end(prev)) + ) } private predicate exits_early(BasicBlock b) { - exists(FunctionObject f | - f.neverReturns() and - f.getACall().getBasicBlock() = b - ) + exists(FunctionObject f | + f.neverReturns() and + f.getACall().getBasicBlock() = b + ) } private predicate gettext_installed() { - // Good enough (and fast) approximation - exists(Module m | m.getName() = "gettext") + // Good enough (and fast) approximation + exists(Module m | m.getName() = "gettext") } private predicate builtin_constant(string name) { - exists(Object::builtin(name)) - or - name = "WindowsError" - or - name = "_" and gettext_installed() + exists(Object::builtin(name)) + or + name = "WindowsError" + or + name = "_" and gettext_installed() } private predicate auto_name(string name) { - name = "__file__" or name = "__builtins__" or name = "__name__" + name = "__file__" or name = "__builtins__" or name = "__name__" } /** Whether this name is (almost) always defined, ie. it is a builtin or VM defined name */ @@ -212,11 +212,11 @@ predicate globallyDefinedName(string name) { builtin_constant(name) or auto_name /** An SSA variable that is backed by a global variable */ class GlobalSsaVariable extends EssaVariable { - GlobalSsaVariable() { this.getSourceVariable() instanceof GlobalVariable } + GlobalSsaVariable() { this.getSourceVariable() instanceof GlobalVariable } - GlobalVariable getVariable() { result = this.getSourceVariable() } + GlobalVariable getVariable() { result = this.getSourceVariable() } - string getId() { result = this.getVariable().getId() } + string getId() { result = this.getVariable().getId() } - override string toString() { result = "GSSA Variable " + this.getId() } + override string toString() { result = "GSSA Variable " + this.getId() } } diff --git a/python/ql/src/semmle/python/Scope.qll b/python/ql/src/semmle/python/Scope.qll index 1fab7f77013..60e69673bbe 100755 --- a/python/ql/src/semmle/python/Scope.qll +++ b/python/ql/src/semmle/python/Scope.qll @@ -6,148 +6,148 @@ import python * The scopes for expressions that create new scopes, lambdas and comprehensions, are handled by creating an anonymous Function. */ class Scope extends Scope_ { - Module getEnclosingModule() { result = this.getEnclosingScope().getEnclosingModule() } + Module getEnclosingModule() { result = this.getEnclosingScope().getEnclosingModule() } - /** - * This method will be deprecated in the next release. Please use `getEnclosingScope()` instead. - * The reason for this is to avoid confusion around use of `x.getScope+()` where `x` might be an - * `AstNode` or a `Variable`. Forcing the users to write `x.getScope().getEnclosingScope*()` ensures that - * the apparent semantics and the actual semantics coincide. - * [ Gets the scope enclosing this scope (modules have no enclosing scope) ] - */ - Scope getScope() { none() } + /** + * This method will be deprecated in the next release. Please use `getEnclosingScope()` instead. + * The reason for this is to avoid confusion around use of `x.getScope+()` where `x` might be an + * `AstNode` or a `Variable`. Forcing the users to write `x.getScope().getEnclosingScope*()` ensures that + * the apparent semantics and the actual semantics coincide. + * [ Gets the scope enclosing this scope (modules have no enclosing scope) ] + */ + Scope getScope() { none() } - /** Gets the scope enclosing this scope (modules have no enclosing scope) */ - Scope getEnclosingScope() { none() } + /** Gets the scope enclosing this scope (modules have no enclosing scope) */ + Scope getEnclosingScope() { none() } - /** Gets the statements forming the body of this scope */ - StmtList getBody() { none() } + /** Gets the statements forming the body of this scope */ + StmtList getBody() { none() } - /** Gets the nth statement of this scope */ - Stmt getStmt(int n) { none() } + /** Gets the nth statement of this scope */ + Stmt getStmt(int n) { none() } - /** Gets a top-level statement in this scope */ - Stmt getAStmt() { none() } + /** Gets a top-level statement in this scope */ + Stmt getAStmt() { none() } - Location getLocation() { none() } + Location getLocation() { none() } - /** Gets the name of this scope */ - string getName() { py_strs(result, this, 0) } + /** Gets the name of this scope */ + string getName() { py_strs(result, this, 0) } - /** Gets the docstring for this scope */ - StrConst getDocString() { result = this.getStmt(0).(ExprStmt).getValue() } + /** Gets the docstring for this scope */ + StrConst getDocString() { result = this.getStmt(0).(ExprStmt).getValue() } - /** Gets the entry point into this Scope's control flow graph */ - ControlFlowNode getEntryNode() { py_scope_flow(result, this, -1) } + /** Gets the entry point into this Scope's control flow graph */ + ControlFlowNode getEntryNode() { py_scope_flow(result, this, -1) } - /** Gets the non-explicit exit from this Scope's control flow graph */ - ControlFlowNode getFallthroughNode() { py_scope_flow(result, this, 0) } + /** Gets the non-explicit exit from this Scope's control flow graph */ + ControlFlowNode getFallthroughNode() { py_scope_flow(result, this, 0) } - /** Gets the exit of this scope following from a return statement */ - ControlFlowNode getReturnNode() { py_scope_flow(result, this, 2) } + /** Gets the exit of this scope following from a return statement */ + ControlFlowNode getReturnNode() { py_scope_flow(result, this, 2) } - /** Gets an exit from this Scope's control flow graph */ - ControlFlowNode getAnExitNode() { exists(int i | py_scope_flow(result, this, i) and i >= 0) } + /** Gets an exit from this Scope's control flow graph */ + ControlFlowNode getAnExitNode() { exists(int i | py_scope_flow(result, this, i) and i >= 0) } - /** - * Gets an exit from this Scope's control flow graph, - * that does not result from an exception - */ - ControlFlowNode getANormalExit() { - result = this.getFallthroughNode() + /** + * Gets an exit from this Scope's control flow graph, + * that does not result from an exception + */ + ControlFlowNode getANormalExit() { + result = this.getFallthroughNode() + or + result = this.getReturnNode() + } + + /** Holds if this a top-level (non-nested) class or function */ + predicate isTopLevel() { this.getEnclosingModule() = this.getEnclosingScope() } + + /** Holds if this scope is deemed to be public */ + predicate isPublic() { + /* Not inside a function */ + not this.getEnclosingScope() instanceof Function and + /* Not implicitly private */ + this.getName().charAt(0) != "_" and + ( + this instanceof Module + or + exists(Module m | m = this.getEnclosingScope() and m.isPublic() | + /* If the module has an __all__, is this in it */ + not exists(m.getAnExport()) or - result = this.getReturnNode() - } + m.getAnExport() = this.getName() + ) + or + exists(Class c | c = this.getEnclosingScope() | + this instanceof Function and + c.isPublic() + ) + ) + } - /** Holds if this a top-level (non-nested) class or function */ - predicate isTopLevel() { this.getEnclosingModule() = this.getEnclosingScope() } + predicate contains(AstNode a) { + this.getBody().contains(a) + or + exists(Scope inner | inner.getEnclosingScope() = this | inner.contains(a)) + } - /** Holds if this scope is deemed to be public */ - predicate isPublic() { - /* Not inside a function */ - not this.getEnclosingScope() instanceof Function and - /* Not implicitly private */ - this.getName().charAt(0) != "_" and - ( - this instanceof Module - or - exists(Module m | m = this.getEnclosingScope() and m.isPublic() | - /* If the module has an __all__, is this in it */ - not exists(m.getAnExport()) - or - m.getAnExport() = this.getName() - ) - or - exists(Class c | c = this.getEnclosingScope() | - this instanceof Function and - c.isPublic() - ) - ) - } - - predicate contains(AstNode a) { - this.getBody().contains(a) + /** + * Holds if this scope can be expected to execute before `other`. + * Modules precede functions and methods in those modules + * `__init__` precedes other methods. `__enter__` precedes `__exit__`. + * NOTE that this is context-insensitive, so a module "precedes" a function + * in that module, even if that function is called from the module scope. + */ + predicate precedes(Scope other) { + exists(Function f, string name | f = other and name = f.getName() | + if f.isMethod() + then + // The __init__ method is preceded by the enclosing module + this = f.getEnclosingModule() and name = "__init__" or - exists(Scope inner | inner.getEnclosingScope() = this | inner.contains(a)) - } - - /** - * Holds if this scope can be expected to execute before `other`. - * Modules precede functions and methods in those modules - * `__init__` precedes other methods. `__enter__` precedes `__exit__`. - * NOTE that this is context-insensitive, so a module "precedes" a function - * in that module, even if that function is called from the module scope. - */ - predicate precedes(Scope other) { - exists(Function f, string name | f = other and name = f.getName() | - if f.isMethod() - then - // The __init__ method is preceded by the enclosing module - this = f.getEnclosingModule() and name = "__init__" - or - exists(Class c, string pred_name | - // __init__ -> __enter__ -> __exit__ - // __init__ -> other-methods - f.getScope() = c and - ( - pred_name = "__init__" and not name = "__init__" and not name = "__exit__" - or - pred_name = "__enter__" and name = "__exit__" - ) - | - this.getScope() = c and - pred_name = this.(Function).getName() - or - not exists(Function pre_func | - pre_func.getName() = pred_name and - pre_func.getScope() = c - ) and - this = other.getEnclosingModule() - ) - else - // Normal functions are preceded by the enclosing module - this = f.getEnclosingModule() + exists(Class c, string pred_name | + // __init__ -> __enter__ -> __exit__ + // __init__ -> other-methods + f.getScope() = c and + ( + pred_name = "__init__" and not name = "__init__" and not name = "__exit__" + or + pred_name = "__enter__" and name = "__exit__" + ) + | + this.getScope() = c and + pred_name = this.(Function).getName() + or + not exists(Function pre_func | + pre_func.getName() = pred_name and + pre_func.getScope() = c + ) and + this = other.getEnclosingModule() ) - } + else + // Normal functions are preceded by the enclosing module + this = f.getEnclosingModule() + ) + } - /** - * Gets the evaluation scope for code in this (lexical) scope. - * This is usually the scope itself, but may be an enclosing scope. - * Notably, for list comprehensions in Python 2. - */ - Scope getEvaluatingScope() { result = this } + /** + * Gets the evaluation scope for code in this (lexical) scope. + * This is usually the scope itself, but may be an enclosing scope. + * Notably, for list comprehensions in Python 2. + */ + Scope getEvaluatingScope() { result = this } - /** - * Holds if this scope is in the source archive, - * that is it is part of the code specified, not library code - */ - predicate inSource() { exists(this.getEnclosingModule().getFile().getRelativePath()) } + /** + * Holds if this scope is in the source archive, + * that is it is part of the code specified, not library code + */ + predicate inSource() { exists(this.getEnclosingModule().getFile().getRelativePath()) } - Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } + Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } - /** Whether this contains `inner` syntactically and `inner` has the same scope as `this` */ - predicate containsInScope(AstNode inner) { - this.getBody().contains(inner) and - this = inner.getScope() - } + /** Whether this contains `inner` syntactically and `inner` has the same scope as `this` */ + predicate containsInScope(AstNode inner) { + this.getBody().contains(inner) and + this = inner.getScope() + } } diff --git a/python/ql/src/semmle/python/SelfAttribute.qll b/python/ql/src/semmle/python/SelfAttribute.qll index c40b3b3c0be..6fe5942a128 100644 --- a/python/ql/src/semmle/python/SelfAttribute.qll +++ b/python/ql/src/semmle/python/SelfAttribute.qll @@ -11,80 +11,80 @@ private import semmle.python.pointsto.Filters * is `self`. */ class SelfAttribute extends Attribute { - SelfAttribute() { self_attribute(this, _) } + SelfAttribute() { self_attribute(this, _) } - Class getClass() { self_attribute(this, result) } + Class getClass() { self_attribute(this, result) } } /** Whether variable 'self' is the self variable in method 'method' */ private predicate self_variable(Function method, Variable self) { - self.isParameter() and - method.isMethod() and - method.getArg(0).asName() = self.getAnAccess() + self.isParameter() and + method.isMethod() and + method.getArg(0).asName() = self.getAnAccess() } /** Whether attribute is an access of the form `self.attr` in the body of the class 'cls' */ private predicate self_attribute(Attribute attr, Class cls) { - exists(Function f, Variable self | self_variable(f, self) | - self.getAnAccess() = attr.getObject() and - cls = f.getScope+() - ) + exists(Function f, Variable self | self_variable(f, self) | + self.getAnAccess() = attr.getObject() and + cls = f.getScope+() + ) } /** Helper class for UndefinedClassAttribute.ql & MaybeUndefinedClassAttribute.ql */ class SelfAttributeRead extends SelfAttribute { - SelfAttributeRead() { - this.getCtx() instanceof Load and - // Be stricter for loads. - // We want to generous as to what is defined (i.e. stores), - // but strict as to what needs to be defined (i.e. loads). - exists(ClassObject cls, FunctionObject func | cls.declaredAttribute(_) = func | - func.getFunction() = this.getScope() and - cls.getPyClass() = this.getClass() - ) - } + SelfAttributeRead() { + this.getCtx() instanceof Load and + // Be stricter for loads. + // We want to generous as to what is defined (i.e. stores), + // but strict as to what needs to be defined (i.e. loads). + exists(ClassObject cls, FunctionObject func | cls.declaredAttribute(_) = func | + func.getFunction() = this.getScope() and + cls.getPyClass() = this.getClass() + ) + } - predicate guardedByHasattr() { - exists(Variable var, ControlFlowNode n | - var.getAUse() = this.getObject().getAFlowNode() and - hasattr(n, var.getAUse(), this.getName()) and - n.strictlyDominates(this.getAFlowNode()) - ) - } + predicate guardedByHasattr() { + exists(Variable var, ControlFlowNode n | + var.getAUse() = this.getObject().getAFlowNode() and + hasattr(n, var.getAUse(), this.getName()) and + n.strictlyDominates(this.getAFlowNode()) + ) + } - pragma[noinline] - predicate locallyDefined() { - exists(SelfAttributeStore store | - this.getName() = store.getName() and - this.getScope() = store.getScope() - | - store.getAFlowNode().strictlyDominates(this.getAFlowNode()) - ) - } + pragma[noinline] + predicate locallyDefined() { + exists(SelfAttributeStore store | + this.getName() = store.getName() and + this.getScope() = store.getScope() + | + store.getAFlowNode().strictlyDominates(this.getAFlowNode()) + ) + } } class SelfAttributeStore extends SelfAttribute { - SelfAttributeStore() { this.getCtx() instanceof Store } + SelfAttributeStore() { this.getCtx() instanceof Store } - Expr getAssignedValue() { exists(Assign a | a.getATarget() = this | result = a.getValue()) } + Expr getAssignedValue() { exists(Assign a | a.getATarget() = this | result = a.getValue()) } } private predicate attr_assigned_in_method_arg_n(FunctionObject method, string name, int n) { - exists(SsaVariable param | - method.getFunction().getArg(n).asName() = param.getDefinition().getNode() - | - exists(AttrNode attr | - attr.getObject(name) = param.getAUse() and - attr.isStore() - ) - or - exists(CallNode call, FunctionObject callee, int m | - callee.getArgumentForCall(call, m) = param.getAUse() and - attr_assigned_in_method_arg_n(callee, name, m) - ) + exists(SsaVariable param | + method.getFunction().getArg(n).asName() = param.getDefinition().getNode() + | + exists(AttrNode attr | + attr.getObject(name) = param.getAUse() and + attr.isStore() ) + or + exists(CallNode call, FunctionObject callee, int m | + callee.getArgumentForCall(call, m) = param.getAUse() and + attr_assigned_in_method_arg_n(callee, name, m) + ) + ) } predicate attribute_assigned_in_method(FunctionObject method, string name) { - attr_assigned_in_method_arg_n(method, name, 0) + attr_assigned_in_method_arg_n(method, name, 0) } diff --git a/python/ql/src/semmle/python/Stmts.qll b/python/ql/src/semmle/python/Stmts.qll index 660ecc5982e..0aa34c2a3fe 100644 --- a/python/ql/src/semmle/python/Stmts.qll +++ b/python/ql/src/semmle/python/Stmts.qll @@ -2,436 +2,436 @@ import python /** A statement */ class Stmt extends Stmt_, AstNode { - /** Gets the scope immediately enclosing this statement */ - override Scope getScope() { py_scopes(this, result) } + /** Gets the scope immediately enclosing this statement */ + override Scope getScope() { py_scopes(this, result) } - override string toString() { result = "Stmt" } + override string toString() { result = "Stmt" } - /** Gets the module enclosing this statement */ - Module getEnclosingModule() { result = this.getScope().getEnclosingModule() } + /** Gets the module enclosing this statement */ + Module getEnclosingModule() { result = this.getScope().getEnclosingModule() } - override Location getLocation() { result = Stmt_.super.getLocation() } + override Location getLocation() { result = Stmt_.super.getLocation() } - /** Gets an immediate (non-nested) sub-expression of this statement */ - Expr getASubExpression() { none() } + /** Gets an immediate (non-nested) sub-expression of this statement */ + Expr getASubExpression() { none() } - /** Gets an immediate (non-nested) sub-statement of this statement */ - Stmt getASubStatement() { none() } + /** Gets an immediate (non-nested) sub-statement of this statement */ + Stmt getASubStatement() { none() } - override AstNode getAChildNode() { - result = this.getASubExpression() - or - result = this.getASubStatement() - } + override AstNode getAChildNode() { + result = this.getASubExpression() + or + result = this.getASubStatement() + } - private ControlFlowNode possibleEntryNode() { - result.getNode() = this or - this.containsInScope(result.getNode()) - } + private ControlFlowNode possibleEntryNode() { + result.getNode() = this or + this.containsInScope(result.getNode()) + } - /** - * Gets a control flow node for an entry into this statement. - */ - ControlFlowNode getAnEntryNode() { - result = this.possibleEntryNode() and - exists(ControlFlowNode pred | - pred.getASuccessor() = result and - not pred = this.possibleEntryNode() - ) - } + /** + * Gets a control flow node for an entry into this statement. + */ + ControlFlowNode getAnEntryNode() { + result = this.possibleEntryNode() and + exists(ControlFlowNode pred | + pred.getASuccessor() = result and + not pred = this.possibleEntryNode() + ) + } - /** Holds if this statement cannot be reached */ - predicate isUnreachable() { - not exists(this.getAnEntryNode()) - or - exists(If ifstmt | - ifstmt.getTest().(ImmutableLiteral).booleanValue() = false and ifstmt.getBody().contains(this) - or - ifstmt.getTest().(ImmutableLiteral).booleanValue() = true and - ifstmt.getOrelse().contains(this) - ) - or - exists(While whilestmt | - whilestmt.getTest().(ImmutableLiteral).booleanValue() = false and - whilestmt.getBody().contains(this) - ) - } + /** Holds if this statement cannot be reached */ + predicate isUnreachable() { + not exists(this.getAnEntryNode()) + or + exists(If ifstmt | + ifstmt.getTest().(ImmutableLiteral).booleanValue() = false and ifstmt.getBody().contains(this) + or + ifstmt.getTest().(ImmutableLiteral).booleanValue() = true and + ifstmt.getOrelse().contains(this) + ) + or + exists(While whilestmt | + whilestmt.getTest().(ImmutableLiteral).booleanValue() = false and + whilestmt.getBody().contains(this) + ) + } - /** - * Gets the final statement in this statement, ordered by location. - * Will be this statement if not a compound statement. - */ - Stmt getLastStatement() { result = this } + /** + * Gets the final statement in this statement, ordered by location. + * Will be this statement if not a compound statement. + */ + Stmt getLastStatement() { result = this } } /** A statement that includes a binding (except imports) */ class Assign extends Assign_ { - /** Use ControlFlowNodes and SsaVariables for data-flow analysis. */ - predicate defines(Variable v) { this.getATarget().defines(v) } + /** Use ControlFlowNodes and SsaVariables for data-flow analysis. */ + predicate defines(Variable v) { this.getATarget().defines(v) } - override Expr getASubExpression() { - result = this.getATarget() or - result = this.getValue() - } + override Expr getASubExpression() { + result = this.getATarget() or + result = this.getValue() + } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** An assignment statement */ class AssignStmt extends Assign { - /* syntax: Expr, ... = Expr */ - AssignStmt() { not this instanceof FunctionDef and not this instanceof ClassDef } + /* syntax: Expr, ... = Expr */ + AssignStmt() { not this instanceof FunctionDef and not this instanceof ClassDef } - override string toString() { result = "AssignStmt" } + override string toString() { result = "AssignStmt" } } /** An augmented assignment statement, such as `x += y` */ class AugAssign extends AugAssign_ { - /* syntax: Expr += Expr */ - override Expr getASubExpression() { result = this.getOperation() } + /* syntax: Expr += Expr */ + override Expr getASubExpression() { result = this.getOperation() } - /** - * Gets the target of this augmented assignment statement. - * That is, the `a` in `a += b`. - */ - Expr getTarget() { result = this.getOperation().(BinaryExpr).getLeft() } + /** + * Gets the target of this augmented assignment statement. + * That is, the `a` in `a += b`. + */ + Expr getTarget() { result = this.getOperation().(BinaryExpr).getLeft() } - /** - * Gets the value of this augmented assignment statement. - * That is, the `b` in `a += b`. - */ - Expr getValue() { result = this.getOperation().(BinaryExpr).getRight() } + /** + * Gets the value of this augmented assignment statement. + * That is, the `b` in `a += b`. + */ + Expr getValue() { result = this.getOperation().(BinaryExpr).getRight() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** An annotated assignment statement, such as `x: int = 0` */ class AnnAssign extends AnnAssign_ { - /* syntax: Expr: Expr = Expr */ - override Expr getASubExpression() { - result = this.getAnnotation() or - result = this.getTarget() or - result = this.getValue() - } + /* syntax: Expr: Expr = Expr */ + override Expr getASubExpression() { + result = this.getAnnotation() or + result = this.getTarget() or + result = this.getValue() + } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } - /** Holds if the value of the annotation of this assignment is stored at runtime. */ - predicate isStored() { - not this.getScope() instanceof Function and - exists(Name n | - n = this.getTarget() and - not n.isParenthesized() - ) - } + /** Holds if the value of the annotation of this assignment is stored at runtime. */ + predicate isStored() { + not this.getScope() instanceof Function and + exists(Name n | + n = this.getTarget() and + not n.isParenthesized() + ) + } } /** An exec statement */ class Exec extends Exec_ { - /* syntax: exec Expr */ - override Expr getASubExpression() { - result = this.getBody() or - result = this.getGlobals() or - result = this.getLocals() - } + /* syntax: exec Expr */ + override Expr getASubExpression() { + result = this.getBody() or + result = this.getGlobals() or + result = this.getLocals() + } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** An except statement (part of a `try` statement), such as `except IOError as err:` */ class ExceptStmt extends ExceptStmt_ { - /* syntax: except Expr [ as Expr ]: */ - /** Gets the immediately enclosing try statement */ - Try getTry() { result.getAHandler() = this } + /* syntax: except Expr [ as Expr ]: */ + /** Gets the immediately enclosing try statement */ + Try getTry() { result.getAHandler() = this } - override Expr getASubExpression() { - result = this.getName() - or - result = this.getType() - } + override Expr getASubExpression() { + result = this.getName() + or + result = this.getType() + } - override Stmt getASubStatement() { result = this.getAStmt() } + override Stmt getASubStatement() { result = this.getAStmt() } - override Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } + override Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } } /** An assert statement, such as `assert a == b, "A is not equal to b"` */ class Assert extends Assert_ { - /* syntax: assert Expr [, Expr] */ - override Expr getASubExpression() { result = this.getMsg() or result = this.getTest() } + /* syntax: assert Expr [, Expr] */ + override Expr getASubExpression() { result = this.getMsg() or result = this.getTest() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** A break statement */ class Break extends Break_ { - /* syntax: assert Expr [, Expr] */ - override Expr getASubExpression() { none() } + /* syntax: assert Expr [, Expr] */ + override Expr getASubExpression() { none() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** A continue statement */ class Continue extends Continue_ { - /* syntax: continue */ - override Expr getASubExpression() { none() } + /* syntax: continue */ + override Expr getASubExpression() { none() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** A delete statement, such as `del x[-1]` */ class Delete extends Delete_ { - /* syntax: del Expr, ... */ - override Expr getASubExpression() { result = this.getATarget() } + /* syntax: del Expr, ... */ + override Expr getASubExpression() { result = this.getATarget() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** An expression statement, such as `len(x)` or `yield y` */ class ExprStmt extends ExprStmt_ { - /* syntax: Expr */ - override Expr getASubExpression() { result = this.getValue() } + /* syntax: Expr */ + override Expr getASubExpression() { result = this.getValue() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** A for statement, such as `for x in y: print(x)` */ class For extends For_ { - /* syntax: for varname in Expr: ... */ - override Stmt getASubStatement() { - result = this.getAStmt() or - result = this.getAnOrelse() - } + /* syntax: for varname in Expr: ... */ + override Stmt getASubStatement() { + result = this.getAStmt() or + result = this.getAnOrelse() + } - override Expr getASubExpression() { - result = this.getTarget() or - result = this.getIter() - } + override Expr getASubExpression() { + result = this.getTarget() or + result = this.getIter() + } - override Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } + override Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } } /** A global statement, such as `global var` */ class Global extends Global_ { - /* syntax: global varname */ - override Expr getASubExpression() { none() } + /* syntax: global varname */ + override Expr getASubExpression() { none() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** An if statement, such as `if eggs: print("spam")` */ class If extends If_ { - /* syntax: if Expr: ... */ - override Stmt getASubStatement() { - result = this.getAStmt() or - result = this.getAnOrelse() - } + /* syntax: if Expr: ... */ + override Stmt getASubStatement() { + result = this.getAStmt() or + result = this.getAnOrelse() + } - override Expr getASubExpression() { result = this.getTest() } + override Expr getASubExpression() { result = this.getTest() } - /** Whether this if statement takes the form `if __name__ == "__main__":` */ - predicate isNameEqMain() { - exists(StrConst m, Name n, Compare c | - this.getTest() = c and - c.getOp(0) instanceof Eq and - ( - c.getLeft() = n and c.getComparator(0) = m - or - c.getLeft() = m and c.getComparator(0) = n - ) and - n.getId() = "__name__" and - m.getText() = "__main__" - ) - } - - /** Whether this if statement starts with the keyword `elif` */ - predicate isElif() { - /* - * The Python parser turns all elif chains into nested if-else statements. - * An `elif` can be identified as it is the first statement in an `else` block - * and it is not indented relative to its parent `if`. - */ - - exists(If i | - i.getOrelse(0) = this and - this.getLocation().getStartColumn() = i.getLocation().getStartColumn() - ) - } - - /** Gets the `elif` branch of this `if`-statement, if present */ - If getElif() { - result = this.getOrelse(0) and - result.isElif() - } - - override Stmt getLastStatement() { - result = this.getOrelse().getLastItem().getLastStatement() + /** Whether this if statement takes the form `if __name__ == "__main__":` */ + predicate isNameEqMain() { + exists(StrConst m, Name n, Compare c | + this.getTest() = c and + c.getOp(0) instanceof Eq and + ( + c.getLeft() = n and c.getComparator(0) = m or - not exists(this.getOrelse()) and - result = this.getBody().getLastItem().getLastStatement() - } + c.getLeft() = m and c.getComparator(0) = n + ) and + n.getId() = "__name__" and + m.getText() = "__main__" + ) + } + + /** Whether this if statement starts with the keyword `elif` */ + predicate isElif() { + /* + * The Python parser turns all elif chains into nested if-else statements. + * An `elif` can be identified as it is the first statement in an `else` block + * and it is not indented relative to its parent `if`. + */ + + exists(If i | + i.getOrelse(0) = this and + this.getLocation().getStartColumn() = i.getLocation().getStartColumn() + ) + } + + /** Gets the `elif` branch of this `if`-statement, if present */ + If getElif() { + result = this.getOrelse(0) and + result.isElif() + } + + override Stmt getLastStatement() { + result = this.getOrelse().getLastItem().getLastStatement() + or + not exists(this.getOrelse()) and + result = this.getBody().getLastItem().getLastStatement() + } } /** A nonlocal statement, such as `nonlocal var` */ class Nonlocal extends Nonlocal_ { - /* syntax: nonlocal varname */ - override Stmt getASubStatement() { none() } + /* syntax: nonlocal varname */ + override Stmt getASubStatement() { none() } - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } - Variable getAVariable() { - result.getScope() = this.getScope() and - result.getId() = this.getAName() - } + Variable getAVariable() { + result.getScope() = this.getScope() and + result.getId() = this.getAName() + } } /** A pass statement */ class Pass extends Pass_ { - /* syntax: pass */ - override Stmt getASubStatement() { none() } + /* syntax: pass */ + override Stmt getASubStatement() { none() } - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } } /** A print statement (Python 2 only), such as `print 0` */ class Print extends Print_ { - /* syntax: print Expr, ... */ - override Stmt getASubStatement() { none() } + /* syntax: print Expr, ... */ + override Stmt getASubStatement() { none() } - override Expr getASubExpression() { - result = this.getAValue() or - result = this.getDest() - } + override Expr getASubExpression() { + result = this.getAValue() or + result = this.getDest() + } } /** A raise statement, such as `raise CompletelyDifferentException()` */ class Raise extends Raise_ { - /* syntax: raise Expr */ - override Stmt getASubStatement() { none() } + /* syntax: raise Expr */ + override Stmt getASubStatement() { none() } - override Expr getASubExpression() { py_exprs(result, _, this, _) } + override Expr getASubExpression() { py_exprs(result, _, this, _) } - /** - * The expression immediately following the `raise`, this is the - * exception raised, but not accounting for tuples in Python 2. - */ - Expr getException() { - result = this.getType() - or - result = this.getExc() - } + /** + * The expression immediately following the `raise`, this is the + * exception raised, but not accounting for tuples in Python 2. + */ + Expr getException() { + result = this.getType() + or + result = this.getExc() + } - /** The exception raised, accounting for tuples in Python 2. */ - Expr getRaised() { - exists(Expr raw | raw = this.getException() | - if not major_version() = 2 or not exists(raw.(Tuple).getAnElt()) - then result = raw - else - /* In Python 2 raising a tuple will result in the first element of the tuple being raised. */ - result = raw.(Tuple).getElt(0) - ) - } + /** The exception raised, accounting for tuples in Python 2. */ + Expr getRaised() { + exists(Expr raw | raw = this.getException() | + if not major_version() = 2 or not exists(raw.(Tuple).getAnElt()) + then result = raw + else + /* In Python 2 raising a tuple will result in the first element of the tuple being raised. */ + result = raw.(Tuple).getElt(0) + ) + } } /** A return statement, such as return None */ class Return extends Return_ { - /* syntax: return Expr */ - override Stmt getASubStatement() { none() } + /* syntax: return Expr */ + override Stmt getASubStatement() { none() } - override Expr getASubExpression() { result = this.getValue() } + override Expr getASubExpression() { result = this.getValue() } } /** A try statement */ class Try extends Try_ { - /* syntax: try: ... */ - override Expr getASubExpression() { none() } + /* syntax: try: ... */ + override Expr getASubExpression() { none() } - override Stmt getASubStatement() { - result = this.getAHandler() or - result = this.getAStmt() or - result = this.getAFinalstmt() or - result = this.getAnOrelse() - } + override Stmt getASubStatement() { + result = this.getAHandler() or + result = this.getAStmt() or + result = this.getAFinalstmt() or + result = this.getAnOrelse() + } - override ExceptStmt getHandler(int i) { result = Try_.super.getHandler(i) } + override ExceptStmt getHandler(int i) { result = Try_.super.getHandler(i) } - /** Gets an exception handler of this try statement. */ - override ExceptStmt getAHandler() { result = Try_.super.getAHandler() } + /** Gets an exception handler of this try statement. */ + override ExceptStmt getAHandler() { result = Try_.super.getAHandler() } - override Stmt getLastStatement() { - result = this.getFinalbody().getLastItem().getLastStatement() - or - not exists(this.getFinalbody()) and - result = this.getOrelse().getLastItem().getLastStatement() - or - not exists(this.getFinalbody()) and - not exists(this.getOrelse()) and - result = this.getHandlers().getLastItem().getLastStatement() - or - not exists(this.getFinalbody()) and - not exists(this.getOrelse()) and - not exists(this.getHandlers()) and - result = this.getBody().getLastItem().getLastStatement() - } + override Stmt getLastStatement() { + result = this.getFinalbody().getLastItem().getLastStatement() + or + not exists(this.getFinalbody()) and + result = this.getOrelse().getLastItem().getLastStatement() + or + not exists(this.getFinalbody()) and + not exists(this.getOrelse()) and + result = this.getHandlers().getLastItem().getLastStatement() + or + not exists(this.getFinalbody()) and + not exists(this.getOrelse()) and + not exists(this.getHandlers()) and + result = this.getBody().getLastItem().getLastStatement() + } } /** A while statement, such as `while parrot_resting():` */ class While extends While_ { - /* syntax: while Expr: ... */ - override Expr getASubExpression() { result = this.getTest() } + /* syntax: while Expr: ... */ + override Expr getASubExpression() { result = this.getTest() } - override Stmt getASubStatement() { - result = this.getAStmt() or - result = this.getAnOrelse() - } + override Stmt getASubStatement() { + result = this.getAStmt() or + result = this.getAnOrelse() + } - override Stmt getLastStatement() { - result = this.getOrelse().getLastItem().getLastStatement() - or - not exists(this.getOrelse()) and - result = this.getBody().getLastItem().getLastStatement() - } + override Stmt getLastStatement() { + result = this.getOrelse().getLastItem().getLastStatement() + or + not exists(this.getOrelse()) and + result = this.getBody().getLastItem().getLastStatement() + } } /** A with statement such as `with f as open("file"): text = f.read()` */ class With extends With_ { - /* syntax: with Expr as varname: ... */ - override Expr getASubExpression() { - result = this.getContextExpr() or - result = this.getOptionalVars() - } + /* syntax: with Expr as varname: ... */ + override Expr getASubExpression() { + result = this.getContextExpr() or + result = this.getOptionalVars() + } - override Stmt getASubStatement() { result = this.getAStmt() } + override Stmt getASubStatement() { result = this.getAStmt() } - override Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } + override Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } } /** A plain text used in a template is wrapped in a TemplateWrite statement */ class TemplateWrite extends TemplateWrite_ { - override Expr getASubExpression() { result = this.getValue() } + override Expr getASubExpression() { result = this.getValue() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** An asynchronous `for` statement, such as `async for varname in Expr: ...` */ class AsyncFor extends For { - /* syntax: async for varname in Expr: ... */ - AsyncFor() { this.isAsync() } + /* syntax: async for varname in Expr: ... */ + AsyncFor() { this.isAsync() } } /** An asynchronous `with` statement, such as `async with varname as Expr: ...` */ class AsyncWith extends With { - /* syntax: async with Expr as varname: ... */ - AsyncWith() { this.isAsync() } + /* syntax: async with Expr as varname: ... */ + AsyncWith() { this.isAsync() } } /** A list of statements */ class StmtList extends StmtList_ { - /** Holds if this list of statements contains the AST node `a` */ - predicate contains(AstNode a) { - exists(Stmt item | item = this.getAnItem() | item = a or item.contains(a)) - } + /** Holds if this list of statements contains the AST node `a` */ + predicate contains(AstNode a) { + exists(Stmt item | item = this.getAnItem() | item = a or item.contains(a)) + } - /** Gets the last item in this list of statements, if any. */ - Stmt getLastItem() { result = this.getItem(max(int i | exists(this.getItem(i)))) } + /** Gets the last item in this list of statements, if any. */ + Stmt getLastItem() { result = this.getItem(max(int i | exists(this.getItem(i)))) } } diff --git a/python/ql/src/semmle/python/TestUtils.qll b/python/ql/src/semmle/python/TestUtils.qll index a51d68dcf73..d7b8fc24676 100644 --- a/python/ql/src/semmle/python/TestUtils.qll +++ b/python/ql/src/semmle/python/TestUtils.qll @@ -4,13 +4,13 @@ import python /** Removes everything up to the occurrence of `sub` in the string `str` */ bindingset[str, sub] string remove_prefix_before_substring(string str, string sub) { - exists(int index | - index = str.indexOf(sub) and - result = str.suffix(index) - ) - or - not exists(str.indexOf(sub)) and - result = str + exists(int index | + index = str.indexOf(sub) and + result = str.suffix(index) + ) + or + not exists(str.indexOf(sub)) and + result = str } /** @@ -18,12 +18,12 @@ string remove_prefix_before_substring(string str, string sub) { * from machine to machine. */ string remove_library_prefix(Location loc) { - result = remove_prefix_before_substring(loc.toString(), "resources/lib") + result = remove_prefix_before_substring(loc.toString(), "resources/lib") } /** Returns the location of an AST node in compact form: `basename:line:column` */ string compact_location(AstNode a) { - exists(Location l | l = a.getLocation() | - result = l.getFile().getBaseName() + ":" + l.getStartLine() + ":" + l.getStartColumn() - ) + exists(Location l | l = a.getLocation() | + result = l.getFile().getBaseName() + ":" + l.getStartLine() + ":" + l.getStartColumn() + ) } diff --git a/python/ql/src/semmle/python/Variables.qll b/python/ql/src/semmle/python/Variables.qll index 896057fa2df..9740d658572 100644 --- a/python/ql/src/semmle/python/Variables.qll +++ b/python/ql/src/semmle/python/Variables.qll @@ -2,71 +2,71 @@ import python /** A variable, either a global or local variable (including parameters) */ class Variable extends @py_variable { - Variable() { - exists(string name | - variable(this, _, name) and - not name = "*" and - not name = "$" - ) - } + Variable() { + exists(string name | + variable(this, _, name) and + not name = "*" and + not name = "$" + ) + } - /** Gets the identifier (name) of this variable */ - string getId() { variable(this, _, result) } + /** Gets the identifier (name) of this variable */ + string getId() { variable(this, _, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Variable " + this.getId() } + /** Gets a textual representation of this element. */ + string toString() { result = "Variable " + this.getId() } - /** Gets an access (load or store) of this variable */ - Name getAnAccess() { - result = this.getALoad() - or - result = this.getAStore() - } + /** Gets an access (load or store) of this variable */ + Name getAnAccess() { + result = this.getALoad() + or + result = this.getAStore() + } - /** Gets a load of this variable */ - Name getALoad() { result.uses(this) } + /** Gets a load of this variable */ + Name getALoad() { result.uses(this) } - /** Gets a store of this variable */ - Name getAStore() { result.defines(this) } + /** Gets a store of this variable */ + Name getAStore() { result.defines(this) } - /** Gets a use of this variable */ - NameNode getAUse() { result.uses(this) } + /** Gets a use of this variable */ + NameNode getAUse() { result.uses(this) } - /** Gets the scope of this variable */ - Scope getScope() { variable(this, result, _) } + /** Gets the scope of this variable */ + Scope getScope() { variable(this, result, _) } - /** - * Whether there is an access to this variable outside - * of its own scope. Usually occurs in nested functions - * or for global variables. - */ - predicate escapes() { exists(Name n | n = this.getAnAccess() | n.getScope() != this.getScope()) } + /** + * Whether there is an access to this variable outside + * of its own scope. Usually occurs in nested functions + * or for global variables. + */ + predicate escapes() { exists(Name n | n = this.getAnAccess() | n.getScope() != this.getScope()) } - /** Whether this variable is a parameter */ - predicate isParameter() { none() } + /** Whether this variable is a parameter */ + predicate isParameter() { none() } - predicate isSelf() { none() } + predicate isSelf() { none() } } /** A local (function or class) variable */ class LocalVariable extends Variable { - LocalVariable() { - exists(Scope s | s = this.getScope() | s instanceof Function or s instanceof Class) - } + LocalVariable() { + exists(Scope s | s = this.getScope() | s instanceof Function or s instanceof Class) + } - override string toString() { result = "Local Variable " + this.getId() } + override string toString() { result = "Local Variable " + this.getId() } - /** Whether this variable is a parameter */ - override predicate isParameter() { exists(Parameter p | this.getAnAccess() = p) } + /** Whether this variable is a parameter */ + override predicate isParameter() { exists(Parameter p | this.getAnAccess() = p) } - /** Holds if this variable is the first parameter of a method. It is not necessarily called "self" */ - override predicate isSelf() { - exists(Function f, Parameter self | - this.getAnAccess() = self and - f.isMethod() and - f.getArg(0) = self - ) - } + /** Holds if this variable is the first parameter of a method. It is not necessarily called "self" */ + override predicate isSelf() { + exists(Function f, Parameter self | + this.getAnAccess() = self and + f.isMethod() and + f.getArg(0) = self + ) + } } /** @@ -74,7 +74,7 @@ class LocalVariable extends Variable { * If the variable is undefined, then raise an exception. */ class FastLocalVariable extends LocalVariable { - FastLocalVariable() { this.getScope() instanceof FastLocalsFunction } + FastLocalVariable() { this.getScope() instanceof FastLocalsFunction } } /** @@ -82,12 +82,12 @@ class FastLocalVariable extends LocalVariable { * If the variable is undefined, then lookup the value in globals(). */ class NameLocalVariable extends LocalVariable { - NameLocalVariable() { not this instanceof FastLocalVariable } + NameLocalVariable() { not this instanceof FastLocalVariable } } /** A global (module-level) variable */ class GlobalVariable extends Variable { - GlobalVariable() { exists(Module m | m = this.getScope()) } + GlobalVariable() { exists(Module m | m = this.getScope()) } - override string toString() { result = "Global Variable " + this.getId() } + override string toString() { result = "Global Variable " + this.getId() } } diff --git a/python/ql/src/semmle/python/dataflow/Configuration.qll b/python/ql/src/semmle/python/dataflow/Configuration.qll index 91a9971c97d..04d89265f2a 100644 --- a/python/ql/src/semmle/python/dataflow/Configuration.qll +++ b/python/ql/src/semmle/python/dataflow/Configuration.qll @@ -4,138 +4,138 @@ private import semmle.python.objects.ObjectInternal private import semmle.python.dataflow.Implementation module TaintTracking { - class Source = TaintSource; + class Source = TaintSource; - class Sink = TaintSink; + class Sink = TaintSink; - class Extension = DataFlowExtension::DataFlowNode; + class Extension = DataFlowExtension::DataFlowNode; - class PathSource = TaintTrackingNode; + class PathSource = TaintTrackingNode; - class PathSink = TaintTrackingNode; + class PathSink = TaintTrackingNode; - abstract class Configuration extends string { - /* Required to prevent compiler warning */ - bindingset[this] - Configuration() { this = this } + abstract class Configuration extends string { + /* Required to prevent compiler warning */ + bindingset[this] + Configuration() { this = this } - /* Old implementation API */ - predicate isSource(Source src) { none() } + /* Old implementation API */ + predicate isSource(Source src) { none() } - predicate isSink(Sink sink) { none() } + predicate isSink(Sink sink) { none() } - predicate isSanitizer(Sanitizer sanitizer) { none() } + predicate isSanitizer(Sanitizer sanitizer) { none() } - predicate isExtension(Extension extension) { none() } + predicate isExtension(Extension extension) { none() } - /* New implementation API */ - /** - * Holds if `src` is a source of taint of `kind` that is relevant - * for this configuration. - */ - predicate isSource(DataFlow::Node src, TaintKind kind) { - exists(TaintSource taintSrc | - this.isSource(taintSrc) and - src.asCfgNode() = taintSrc and - taintSrc.isSourceOf(kind) - ) - } - - /** - * Holds if `sink` is a sink of taint of `kind` that is relevant - * for this configuration. - */ - predicate isSink(DataFlow::Node sink, TaintKind kind) { - exists(TaintSink taintSink | - this.isSink(taintSink) and - sink.asCfgNode() = taintSink and - taintSink.sinks(kind) - ) - } - - /** - * Holds if `src -> dest` should be considered as a flow edge - * in addition to standard data flow edges. - */ - predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dest) { none() } - - /** - * Holds if `src -> dest` is a flow edge converting taint from `srckind` to `destkind`. - */ - predicate isAdditionalFlowStep( - DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind - ) { - none() - } - - /** - * Holds if `node` should be considered as a barrier to flow of any kind. - */ - predicate isBarrier(DataFlow::Node node) { none() } - - /** - * Holds if `node` should be considered as a barrier to flow of `kind`. - */ - predicate isBarrier(DataFlow::Node node, TaintKind kind) { - exists(Sanitizer sanitizer | this.isSanitizer(sanitizer) | - sanitizer.sanitizingNode(kind, node.asCfgNode()) - or - sanitizer.sanitizingEdge(kind, node.asVariable()) - or - sanitizer.sanitizingSingleEdge(kind, node.asVariable()) - or - sanitizer.sanitizingDefinition(kind, node.asVariable()) - or - exists(MethodCallsiteRefinement call, FunctionObject callee | - call = node.asVariable().getDefinition() and - callee.getACall() = call.getCall() and - sanitizer.sanitizingCall(kind, callee) - ) - ) - } - - /** - * Holds if flow from `src` to `dest` is prohibited. - */ - predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node dest) { none() } - - /** - * Holds if control flow from `test` along the `isTrue` edge is prohibited. - */ - predicate isBarrierTest(ControlFlowNode test, boolean isTrue) { none() } - - /** - * Holds if flow from `src` to `dest` is prohibited when the incoming taint is `srckind` and the outgoing taint is `destkind`. - * Note that `srckind` and `destkind` can be the same. - */ - predicate isBarrierEdge( - DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind - ) { - none() - } - - /* Common query API */ - predicate hasFlowPath(PathSource src, PathSink sink) { - this.(TaintTrackingImplementation).hasFlowPath(src, sink) - } - - /* Old query API */ - /* deprecated */ - deprecated predicate hasFlow(Source src, Sink sink) { - exists(PathSource psrc, PathSink psink | - this.hasFlowPath(psrc, psink) and - src = psrc.getNode().asCfgNode() and - sink = psink.getNode().asCfgNode() - ) - } - - /* New query API */ - predicate hasSimpleFlow(DataFlow::Node src, DataFlow::Node sink) { - exists(PathSource psrc, PathSink psink | - this.hasFlowPath(psrc, psink) and - src = psrc.getNode() and - sink = psink.getNode() - ) - } + /* New implementation API */ + /** + * Holds if `src` is a source of taint of `kind` that is relevant + * for this configuration. + */ + predicate isSource(DataFlow::Node src, TaintKind kind) { + exists(TaintSource taintSrc | + this.isSource(taintSrc) and + src.asCfgNode() = taintSrc and + taintSrc.isSourceOf(kind) + ) } + + /** + * Holds if `sink` is a sink of taint of `kind` that is relevant + * for this configuration. + */ + predicate isSink(DataFlow::Node sink, TaintKind kind) { + exists(TaintSink taintSink | + this.isSink(taintSink) and + sink.asCfgNode() = taintSink and + taintSink.sinks(kind) + ) + } + + /** + * Holds if `src -> dest` should be considered as a flow edge + * in addition to standard data flow edges. + */ + predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dest) { none() } + + /** + * Holds if `src -> dest` is a flow edge converting taint from `srckind` to `destkind`. + */ + predicate isAdditionalFlowStep( + DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind + ) { + none() + } + + /** + * Holds if `node` should be considered as a barrier to flow of any kind. + */ + predicate isBarrier(DataFlow::Node node) { none() } + + /** + * Holds if `node` should be considered as a barrier to flow of `kind`. + */ + predicate isBarrier(DataFlow::Node node, TaintKind kind) { + exists(Sanitizer sanitizer | this.isSanitizer(sanitizer) | + sanitizer.sanitizingNode(kind, node.asCfgNode()) + or + sanitizer.sanitizingEdge(kind, node.asVariable()) + or + sanitizer.sanitizingSingleEdge(kind, node.asVariable()) + or + sanitizer.sanitizingDefinition(kind, node.asVariable()) + or + exists(MethodCallsiteRefinement call, FunctionObject callee | + call = node.asVariable().getDefinition() and + callee.getACall() = call.getCall() and + sanitizer.sanitizingCall(kind, callee) + ) + ) + } + + /** + * Holds if flow from `src` to `dest` is prohibited. + */ + predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node dest) { none() } + + /** + * Holds if control flow from `test` along the `isTrue` edge is prohibited. + */ + predicate isBarrierTest(ControlFlowNode test, boolean isTrue) { none() } + + /** + * Holds if flow from `src` to `dest` is prohibited when the incoming taint is `srckind` and the outgoing taint is `destkind`. + * Note that `srckind` and `destkind` can be the same. + */ + predicate isBarrierEdge( + DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind + ) { + none() + } + + /* Common query API */ + predicate hasFlowPath(PathSource src, PathSink sink) { + this.(TaintTrackingImplementation).hasFlowPath(src, sink) + } + + /* Old query API */ + /* deprecated */ + deprecated predicate hasFlow(Source src, Sink sink) { + exists(PathSource psrc, PathSink psink | + this.hasFlowPath(psrc, psink) and + src = psrc.getNode().asCfgNode() and + sink = psink.getNode().asCfgNode() + ) + } + + /* New query API */ + predicate hasSimpleFlow(DataFlow::Node src, DataFlow::Node sink) { + exists(PathSource psrc, PathSink psink | + this.hasFlowPath(psrc, psink) and + src = psrc.getNode() and + sink = psink.getNode() + ) + } + } } diff --git a/python/ql/src/semmle/python/dataflow/Files.qll b/python/ql/src/semmle/python/dataflow/Files.qll index a0cd1753f9d..3439e559efa 100644 --- a/python/ql/src/semmle/python/dataflow/Files.qll +++ b/python/ql/src/semmle/python/dataflow/Files.qll @@ -2,18 +2,18 @@ import python import semmle.python.dataflow.TaintTracking class OpenFile extends TaintKind { - OpenFile() { this = "file.open" } + OpenFile() { this = "file.open" } - override string repr() { result = "an open file" } + override string repr() { result = "an open file" } } class OpenFileConfiguration extends TaintTracking::Configuration { - OpenFileConfiguration() { this = "Open file configuration" } + OpenFileConfiguration() { this = "Open file configuration" } - override predicate isSource(DataFlow::Node src, TaintKind kind) { - src.asCfgNode() = Value::named("open").getACall() and - kind instanceof OpenFile - } + override predicate isSource(DataFlow::Node src, TaintKind kind) { + src.asCfgNode() = Value::named("open").getACall() and + kind instanceof OpenFile + } - override predicate isSink(DataFlow::Node sink, TaintKind kind) { none() } + override predicate isSink(DataFlow::Node sink, TaintKind kind) { none() } } diff --git a/python/ql/src/semmle/python/dataflow/Implementation.qll b/python/ql/src/semmle/python/dataflow/Implementation.qll index c62641e497e..5a2a0e7eeee 100644 --- a/python/ql/src/semmle/python/dataflow/Implementation.qll +++ b/python/ql/src/semmle/python/dataflow/Implementation.qll @@ -11,10 +11,10 @@ import semmle.python.dataflow.Legacy */ newtype TTaintTrackingContext = - TNoParam() or - TParamContext(TaintKind param, AttributePath path, int n) { - any(TaintTrackingImplementation impl).callWithTaintedArgument(_, _, _, _, n, path, param) - } + TNoParam() or + TParamContext(TaintKind param, AttributePath path, int n) { + any(TaintTrackingImplementation impl).callWithTaintedArgument(_, _, _, _, n, path, param) + } /** * The context for taint-tracking. @@ -24,42 +24,42 @@ newtype TTaintTrackingContext = * Used to track taint through calls accurately and reasonably efficiently. */ class TaintTrackingContext extends TTaintTrackingContext { - /** Gets a textual representation of this element. */ - string toString() { - this = TNoParam() and result = "" - or - exists(TaintKind param, AttributePath path, int n | - this = TParamContext(param, path, n) and - result = "p" + n.toString() + path.extension() + " = " + param - ) - } + /** Gets a textual representation of this element. */ + string toString() { + this = TNoParam() and result = "" + or + exists(TaintKind param, AttributePath path, int n | + this = TParamContext(param, path, n) and + result = "p" + n.toString() + path.extension() + " = " + param + ) + } - TaintKind getParameterTaint(int n) { this = TParamContext(result, _, n) } + TaintKind getParameterTaint(int n) { this = TParamContext(result, _, n) } - AttributePath getAttributePath() { this = TParamContext(_, result, _) } + AttributePath getAttributePath() { this = TParamContext(_, result, _) } - TaintTrackingContext getCaller() { result = this.getCaller(_) } + TaintTrackingContext getCaller() { result = this.getCaller(_) } - TaintTrackingContext getCaller(CallNode call) { - exists(TaintKind param, AttributePath path, int n | - this = TParamContext(param, path, n) and - exists(TaintTrackingImplementation impl | - impl.callWithTaintedArgument(_, call, result, _, n, path, param) - ) - ) - } + TaintTrackingContext getCaller(CallNode call) { + exists(TaintKind param, AttributePath path, int n | + this = TParamContext(param, path, n) and + exists(TaintTrackingImplementation impl | + impl.callWithTaintedArgument(_, call, result, _, n, path, param) + ) + ) + } - predicate isTop() { this = TNoParam() } + predicate isTop() { this = TNoParam() } } private newtype TAttributePath = - TNoAttribute() or - /* - * It might make sense to add another level, attribute of attribute. - * But some experimentation would be needed. - */ + TNoAttribute() or + /* + * It might make sense to add another level, attribute of attribute. + * But some experimentation would be needed. + */ - TAttribute(string name) { exists(Attribute a | a.getName() = name) } + TAttribute(string name) { exists(Attribute a | a.getName() = name) } /** * The attribute of the tracked value holding the taint. @@ -67,46 +67,46 @@ private newtype TAttributePath = * Used for tracking tainted attributes of objects. */ abstract class AttributePath extends TAttributePath { - /** Gets a textual representation of this element. */ - abstract string toString(); + /** Gets a textual representation of this element. */ + abstract string toString(); - abstract string extension(); + abstract string extension(); - abstract AttributePath fromAttribute(string name); + abstract AttributePath fromAttribute(string name); - AttributePath getAttribute(string name) { this = result.fromAttribute(name) } + AttributePath getAttribute(string name) { this = result.fromAttribute(name) } - predicate noAttribute() { this = TNoAttribute() } + predicate noAttribute() { this = TNoAttribute() } } /** AttributePath for no attribute. */ class NoAttribute extends TNoAttribute, AttributePath { - override string toString() { result = "no attribute" } + override string toString() { result = "no attribute" } - override string extension() { result = "" } + override string extension() { result = "" } - override AttributePath fromAttribute(string name) { none() } + override AttributePath fromAttribute(string name) { none() } } /** AttributePath for an attribute. */ class NamedAttributePath extends TAttribute, AttributePath { - override string toString() { - exists(string attr | - this = TAttribute(attr) and - result = "attribute " + attr - ) - } + override string toString() { + exists(string attr | + this = TAttribute(attr) and + result = "attribute " + attr + ) + } - override string extension() { - exists(string attr | - this = TAttribute(attr) and - result = "." + attr - ) - } + override string extension() { + exists(string attr | + this = TAttribute(attr) and + result = "." + attr + ) + } - override AttributePath fromAttribute(string name) { - this = TAttribute(name) and result = TNoAttribute() - } + override AttributePath fromAttribute(string name) { + this = TAttribute(name) and result = TNoAttribute() + } } /** @@ -114,81 +114,81 @@ class NamedAttributePath extends TAttribute, AttributePath { * Construction of this type is mutually recursive with `TaintTrackingImplementation.flowStep(...)` */ newtype TTaintTrackingNode = - TTaintTrackingNode_( - DataFlow::Node node, TaintTrackingContext context, AttributePath path, TaintKind kind, - TaintTracking::Configuration config - ) { - config.(TaintTrackingImplementation).flowSource(node, context, path, kind) - or - config.(TaintTrackingImplementation).flowStep(_, node, context, path, kind, _) - } + TTaintTrackingNode_( + DataFlow::Node node, TaintTrackingContext context, AttributePath path, TaintKind kind, + TaintTracking::Configuration config + ) { + config.(TaintTrackingImplementation).flowSource(node, context, path, kind) + or + config.(TaintTrackingImplementation).flowStep(_, node, context, path, kind, _) + } /** * Class representing the (node, context, path, kind) tuple. * Used for context-sensitive path-aware taint-tracking. */ class TaintTrackingNode extends TTaintTrackingNode { - /** Gets a textual representation of this element. */ - string toString() { - if this.getPath() instanceof NoAttribute - then result = this.getTaintKind().repr() - else result = this.getPath().extension() + " = " + this.getTaintKind().repr() - } + /** Gets a textual representation of this element. */ + string toString() { + if this.getPath() instanceof NoAttribute + then result = this.getTaintKind().repr() + else result = this.getPath().extension() + " = " + this.getTaintKind().repr() + } - /** Gets the data flow node for this taint-tracking node */ - DataFlow::Node getNode() { this = TTaintTrackingNode_(result, _, _, _, _) } + /** Gets the data flow node for this taint-tracking node */ + DataFlow::Node getNode() { this = TTaintTrackingNode_(result, _, _, _, _) } - /** Gets the taint kind for this taint-tracking node */ - TaintKind getTaintKind() { this = TTaintTrackingNode_(_, _, _, result, _) } + /** Gets the taint kind for this taint-tracking node */ + TaintKind getTaintKind() { this = TTaintTrackingNode_(_, _, _, result, _) } - /** Gets the taint-tracking context for this taint-tracking node */ - TaintTrackingContext getContext() { this = TTaintTrackingNode_(_, result, _, _, _) } + /** Gets the taint-tracking context for this taint-tracking node */ + TaintTrackingContext getContext() { this = TTaintTrackingNode_(_, result, _, _, _) } - /** Gets the attribute path context for this taint-tracking node */ - AttributePath getPath() { this = TTaintTrackingNode_(_, _, result, _, _) } + /** Gets the attribute path context for this taint-tracking node */ + AttributePath getPath() { this = TTaintTrackingNode_(_, _, result, _, _) } - TaintTracking::Configuration getConfiguration() { this = TTaintTrackingNode_(_, _, _, _, result) } + TaintTracking::Configuration getConfiguration() { this = TTaintTrackingNode_(_, _, _, _, result) } - Location getLocation() { result = this.getNode().getLocation() } + Location getLocation() { result = this.getNode().getLocation() } - predicate isSource() { this.getConfiguration().(TaintTrackingImplementation).isPathSource(this) } + predicate isSource() { this.getConfiguration().(TaintTrackingImplementation).isPathSource(this) } - predicate isSink() { this.getConfiguration().(TaintTrackingImplementation).isPathSink(this) } + predicate isSink() { this.getConfiguration().(TaintTrackingImplementation).isPathSink(this) } - ControlFlowNode getCfgNode() { result = this.getNode().asCfgNode() } + ControlFlowNode getCfgNode() { result = this.getNode().asCfgNode() } - /** Get the AST node for this node. */ - AstNode getAstNode() { result = this.getCfgNode().getNode() } + /** Get the AST node for this node. */ + AstNode getAstNode() { result = this.getCfgNode().getNode() } - TaintTrackingNode getASuccessor(string edgeLabel) { - this.isVisible() and - result = this.unlabeledSuccessor*().labeledSuccessor(edgeLabel) - } + TaintTrackingNode getASuccessor(string edgeLabel) { + this.isVisible() and + result = this.unlabeledSuccessor*().labeledSuccessor(edgeLabel) + } - TaintTrackingNode getASuccessor() { - result = this.getASuccessor(_) - or - this.isVisible() and - result = this.unlabeledSuccessor+() and - result.isSink() - } + TaintTrackingNode getASuccessor() { + result = this.getASuccessor(_) + or + this.isVisible() and + result = this.unlabeledSuccessor+() and + result.isSink() + } - private TaintTrackingNode unlabeledSuccessor() { - this.getConfiguration().(TaintTrackingImplementation).flowStep(this, result, "") - } + private TaintTrackingNode unlabeledSuccessor() { + this.getConfiguration().(TaintTrackingImplementation).flowStep(this, result, "") + } - private TaintTrackingNode labeledSuccessor(string label) { - not label = "" and - this.getConfiguration().(TaintTrackingImplementation).flowStep(this, result, label) - } + private TaintTrackingNode labeledSuccessor(string label) { + not label = "" and + this.getConfiguration().(TaintTrackingImplementation).flowStep(this, result, label) + } - private predicate isVisible() { - any(TaintTrackingNode pred).labeledSuccessor(_) = this - or - this.isSource() - } + private predicate isVisible() { + any(TaintTrackingNode pred).labeledSuccessor(_) = this + or + this.isSource() + } - predicate flowsTo(TaintTrackingNode other) { this.getASuccessor*() = other } + predicate flowsTo(TaintTrackingNode other) { this.getASuccessor*() = other } } /** @@ -198,449 +198,449 @@ class TaintTrackingNode extends TTaintTrackingNode { * in `TaintTracking::Configuration` simpler. */ class TaintTrackingImplementation extends string { - TaintTrackingImplementation() { this instanceof TaintTracking::Configuration } + TaintTrackingImplementation() { this instanceof TaintTracking::Configuration } - /** - * Hold if there is a flow from `source`, which is a taint source, to - * `sink`, which is a taint sink, with this configuration. - */ - predicate hasFlowPath(TaintTrackingNode source, TaintTrackingNode sink) { - this.isPathSource(source) and - this.isPathSink(sink) and - source.flowsTo(sink) - } + /** + * Hold if there is a flow from `source`, which is a taint source, to + * `sink`, which is a taint sink, with this configuration. + */ + predicate hasFlowPath(TaintTrackingNode source, TaintTrackingNode sink) { + this.isPathSource(source) and + this.isPathSink(sink) and + source.flowsTo(sink) + } - /** - * Hold if `node` is a source of taint `kind` with context `context` and attribute path `path`. - */ - predicate flowSource( - DataFlow::Node node, TaintTrackingContext context, AttributePath path, TaintKind kind - ) { - context = TNoParam() and - path = TNoAttribute() and - this.(TaintTracking::Configuration).isSource(node, kind) - } + /** + * Hold if `node` is a source of taint `kind` with context `context` and attribute path `path`. + */ + predicate flowSource( + DataFlow::Node node, TaintTrackingContext context, AttributePath path, TaintKind kind + ) { + context = TNoParam() and + path = TNoAttribute() and + this.(TaintTracking::Configuration).isSource(node, kind) + } - /** Hold if `source` is a source of taint. */ - predicate isPathSource(TaintTrackingNode source) { - exists(DataFlow::Node node, TaintTrackingContext context, AttributePath path, TaintKind kind | - source = TTaintTrackingNode_(node, context, path, kind, this) and - this.flowSource(node, context, path, kind) + /** Hold if `source` is a source of taint. */ + predicate isPathSource(TaintTrackingNode source) { + exists(DataFlow::Node node, TaintTrackingContext context, AttributePath path, TaintKind kind | + source = TTaintTrackingNode_(node, context, path, kind, this) and + this.flowSource(node, context, path, kind) + ) + } + + /** Hold if `sink` is a taint sink. */ + predicate isPathSink(TaintTrackingNode sink) { + exists(DataFlow::Node node, AttributePath path, TaintKind kind | + sink = TTaintTrackingNode_(node, _, path, kind, this) and + path = TNoAttribute() and + this.(TaintTracking::Configuration).isSink(node, kind) + ) + } + + /** + * Hold if taint flows to `src` to `dest` in a single step, labeled with `edgeLabel` + * `edgeLabel` is purely informative. + */ + predicate flowStep(TaintTrackingNode src, TaintTrackingNode dest, string edgeLabel) { + exists(DataFlow::Node node, TaintTrackingContext ctx, AttributePath path, TaintKind kind | + dest = TTaintTrackingNode_(node, ctx, path, kind, this) and + this.flowStep(src, node, ctx, path, kind, edgeLabel) + ) + } + + /** + * Hold if taint flows to `src` to `(node, context, path, kind)` in a single step, labelled with `egdeLabel` with this configuration. + * `edgeLabel` is purely informative. + */ + predicate flowStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + this.unprunedStep(src, node, context, path, kind, edgeLabel) and + node.getBasicBlock().likelyReachable() and + not this.(TaintTracking::Configuration).isBarrier(node) and + ( + not path = TNoAttribute() + or + not this.(TaintTracking::Configuration).isBarrier(node, kind) and + exists(DataFlow::Node srcnode, TaintKind srckind | + src = TTaintTrackingNode_(srcnode, _, _, srckind, this) and + not this.prunedEdge(srcnode, node, srckind, kind) + ) + ) + } + + private predicate prunedEdge( + DataFlow::Node srcnode, DataFlow::Node destnode, TaintKind srckind, TaintKind destkind + ) { + this.(TaintTracking::Configuration).isBarrierEdge(srcnode, destnode, srckind, destkind) + or + srckind = destkind and this.(TaintTracking::Configuration).isBarrierEdge(srcnode, destnode) + } + + private predicate unprunedStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + this.importStep(src, node, context, path, kind, edgeLabel) + or + this.fromImportStep(src, node, context, path, kind, edgeLabel) + or + this.attributeLoadStep(src, node, context, path, kind, edgeLabel) + or + this.getattrStep(src, node, context, path, kind, edgeLabel) + or + this.useStep(src, node, context, path, kind, edgeLabel) + or + this.callTaintStep(src, node, context, path, kind, edgeLabel) + or + this.returnFlowStep(src, node, context, path, kind, edgeLabel) + or + this.callFlowStep(src, node, context, path, kind, edgeLabel) + or + this.iterationStep(src, node, context, path, kind, edgeLabel) + or + this.yieldStep(src, node, context, path, kind, edgeLabel) + or + this.parameterStep(src, node, context, path, kind, edgeLabel) + or + this.ifExpStep(src, node, context, path, kind, edgeLabel) + or + this.essaFlowStep(src, node, context, path, kind, edgeLabel) + or + this.instantiationStep(src, node, context, path, kind, edgeLabel) + or + this.legacyExtensionStep(src, node, context, path, kind, edgeLabel) + or + exists(DataFlow::Node srcnode, TaintKind srckind | + this.(TaintTracking::Configuration).isAdditionalFlowStep(srcnode, node, srckind, kind) and + src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and + path.noAttribute() and + edgeLabel = "additional with kind" + ) + or + exists(DataFlow::Node srcnode | + this.(TaintTracking::Configuration).isAdditionalFlowStep(srcnode, node) and + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + path.noAttribute() and + edgeLabel = "additional" + ) + or + exists(DataFlow::Node srcnode, TaintKind srckind | + src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and + path.noAttribute() + | + kind = srckind.getTaintForFlowStep(srcnode.asCfgNode(), node.asCfgNode(), edgeLabel) + or + kind = srckind.(CollectionKind).getMember() and + srckind.(CollectionKind).flowToMember(srcnode, node) and + edgeLabel = "to member" + or + srckind = kind.(CollectionKind).getMember() and + kind.(CollectionKind).flowFromMember(srcnode, node) and + edgeLabel = "from member" + or + kind = srckind and srckind.flowStep(srcnode, node, edgeLabel) + or + kind = srckind and + srckind instanceof DictKind and + DictKind::flowStep(srcnode.asCfgNode(), node.asCfgNode(), edgeLabel) + or + kind = srckind and + srckind instanceof SequenceKind and + SequenceKind::flowStep(srcnode.asCfgNode(), node.asCfgNode(), edgeLabel) + ) + } + + pragma[noinline] + predicate importStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + edgeLabel = "import" and + exists(ModuleValue m, string name, AttributePath srcpath | + src = TTaintTrackingNode_(_, context, srcpath, kind, this) and + this.moduleAttributeTainted(m, name, src) and + node.asCfgNode().pointsTo(m) and + path = srcpath.getAttribute(name) + ) + } + + pragma[noinline] + predicate fromImportStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + edgeLabel = "from import" and + exists(ModuleValue m, string name | + src = TTaintTrackingNode_(_, context, path, kind, this) and + this.moduleAttributeTainted(m, name, src) and + node.asCfgNode().(ImportMemberNode).getModule(name).pointsTo(m) + ) + } + + pragma[noinline] + predicate attributeLoadStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(DataFlow::Node srcnode, AttributePath srcpath, string attrname | + src = TTaintTrackingNode_(srcnode, context, srcpath, kind, this) and + srcnode.asCfgNode() = node.asCfgNode().(AttrNode).getObject(attrname) and + path = srcpath.fromAttribute(attrname) and + edgeLabel = "from path attribute" + ) + or + exists(DataFlow::Node srcnode, TaintKind srckind, string attrname | + src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and + srcnode.asCfgNode() = node.asCfgNode().(AttrNode).getObject(attrname) and + kind = srckind.getTaintOfAttribute(attrname) and + edgeLabel = "from taint attribute" and + path instanceof NoAttribute + ) + } + + pragma[noinline] + predicate getattrStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(DataFlow::Node srcnode, AttributePath srcpath, TaintKind srckind, string attrname | + src = TTaintTrackingNode_(srcnode, context, srcpath, srckind, this) and + exists(CallNode call, ControlFlowNode arg | + call = node.asCfgNode() and + call.getFunction().pointsTo(ObjectInternal::builtin("getattr")) and + arg = call.getArg(0) and + attrname = call.getArg(1).getNode().(StrConst).getText() and + arg = srcnode.asCfgNode() + | + path = srcpath.fromAttribute(attrname) and + kind = srckind + or + path = srcpath and + kind = srckind.getTaintOfAttribute(attrname) + ) + ) and + edgeLabel = "getattr" + } + + pragma[noinline] + predicate useStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + node.asCfgNode() = srcnode.asVariable().getASourceUse() + ) and + edgeLabel = "use" + } + + /* If the return value is tainted without context, then it always flows back to the caller */ + pragma[noinline] + predicate returnFlowStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(CallNode call, PythonFunctionObjectInternal pyfunc, DataFlow::Node retval | + pyfunc.getACall() = call and + context = TNoParam() and + src = TTaintTrackingNode_(retval, TNoParam(), path, kind, this) and + node.asCfgNode() = call and + retval.asCfgNode() = + any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode() + ) and + edgeLabel = "return" + } + + /* + * Avoid taint flow from return value to caller as it can produce imprecise flow graphs + * Step directly from tainted argument to call result. + */ + + pragma[noinline] + predicate callFlowStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists( + CallNode call, PythonFunctionObjectInternal pyfunc, TaintTrackingContext callee, + DataFlow::Node retval, TaintTrackingNode retnode + | + this.callContexts(call, src, pyfunc, context, callee) and + retnode = TTaintTrackingNode_(retval, callee, path, kind, this) and + node.asCfgNode() = call and + retval.asCfgNode() = + any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode() + ) and + edgeLabel = "call" + } + + pragma[noinline] + predicate callContexts( + CallNode call, TaintTrackingNode argnode, PythonFunctionObjectInternal pyfunc, + TaintTrackingContext caller, TaintTrackingContext callee + ) { + exists(int arg, TaintKind callerKind, AttributePath callerPath | + this.callWithTaintedArgument(argnode, call, caller, pyfunc, arg, callerPath, callerKind) and + callee = TParamContext(callerKind, callerPath, arg) + ) + } + + predicate callWithTaintedArgument( + TaintTrackingNode src, CallNode call, TaintTrackingContext caller, CallableValue pyfunc, + int arg, AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, caller, path, kind, this) and + srcnode.asCfgNode() = pyfunc.getArgumentForCall(call, arg) + ) + } + + predicate instantiationStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(PythonFunctionValue init, EssaVariable self, TaintTrackingContext callee | + instantiationCall(node.asCfgNode(), src, init, context, callee) and + this.(EssaTaintTracking).taintedDefinition(_, self.getDefinition(), callee, path, kind) and + self.getSourceVariable().(Variable).isSelf() and + BaseFlow::reaches_exit(self) and + self.getScope() = init.getScope() + ) and + edgeLabel = "instantiation" + } + + predicate instantiationCall( + CallNode call, TaintTrackingNode argnode, PythonFunctionObjectInternal init, + TaintTrackingContext caller, TaintTrackingContext callee + ) { + exists(ClassValue cls | + call.getFunction().pointsTo(cls) and + cls.lookup("__init__") = init + | + exists(int arg, TaintKind callerKind, AttributePath callerPath, DataFlow::Node argument | + argnode = TTaintTrackingNode_(argument, caller, callerPath, callerKind, this) and + call.getArg(arg - 1) = argument.asCfgNode() and + callee = TParamContext(callerKind, callerPath, arg) + ) + ) + } + + pragma[noinline] + predicate callTaintStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(DataFlow::Node srcnode, CallNode call, TaintKind srckind, string name | + src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and + call.getFunction().(AttrNode).getObject(name) = src.getNode().asCfgNode() and + kind = srckind.getTaintOfMethodResult(name) and + node.asCfgNode() = call + ) and + edgeLabel = "call" + } + + pragma[noinline] + predicate iterationStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(ForNode for, DataFlow::Node sequence, TaintKind seqkind | + src = TTaintTrackingNode_(sequence, context, path, seqkind, this) and + for.iterates(_, sequence.asCfgNode()) and + node.asCfgNode() = for and + path.noAttribute() and + kind = seqkind.getTaintForIteration() + ) and + edgeLabel = "iteration" + } + + pragma[noinline] + predicate parameterStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(CallNode call, PythonFunctionObjectInternal pyfunc, int arg | + this.callWithTaintedArgument(src, call, _, pyfunc, arg, path, kind) and + node.asCfgNode() = pyfunc.getParameter(arg) and + context = TParamContext(kind, path, arg) + ) and + edgeLabel = "parameter" + } + + pragma[noinline] + predicate yieldStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(DataFlow::Node srcnode, TaintKind itemkind | + src = TTaintTrackingNode_(srcnode, context, path, itemkind, this) and + itemkind = kind.getTaintForIteration() and + exists(PyFunctionObject func | + func.getFunction().isGenerator() and + func.getACall() = node.asCfgNode() and + exists(Yield yield | + yield.getScope() = func.getFunction() and + yield.getValue() = srcnode.asCfgNode().getNode() ) - } + ) + ) and + edgeLabel = "yield" + } - /** Hold if `sink` is a taint sink. */ - predicate isPathSink(TaintTrackingNode sink) { - exists(DataFlow::Node node, AttributePath path, TaintKind kind | - sink = TTaintTrackingNode_(node, _, path, kind, this) and - path = TNoAttribute() and - this.(TaintTracking::Configuration).isSink(node, kind) - ) - } + pragma[noinline] + predicate ifExpStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + srcnode.asCfgNode() = node.asCfgNode().(IfExprNode).getAnOperand() + ) and + edgeLabel = "if exp" + } - /** - * Hold if taint flows to `src` to `dest` in a single step, labeled with `edgeLabel` - * `edgeLabel` is purely informative. - */ - predicate flowStep(TaintTrackingNode src, TaintTrackingNode dest, string edgeLabel) { - exists(DataFlow::Node node, TaintTrackingContext ctx, AttributePath path, TaintKind kind | - dest = TTaintTrackingNode_(node, ctx, path, kind, this) and - this.flowStep(src, node, ctx, path, kind, edgeLabel) - ) - } + pragma[noinline] + predicate essaFlowStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + this + .(EssaTaintTracking) + .taintedDefinition(src, node.asVariable().getDefinition(), context, path, kind) and + edgeLabel = "" + } - /** - * Hold if taint flows to `src` to `(node, context, path, kind)` in a single step, labelled with `egdeLabel` with this configuration. - * `edgeLabel` is purely informative. - */ - predicate flowStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - this.unprunedStep(src, node, context, path, kind, edgeLabel) and - node.getBasicBlock().likelyReachable() and - not this.(TaintTracking::Configuration).isBarrier(node) and - ( - not path = TNoAttribute() - or - not this.(TaintTracking::Configuration).isBarrier(node, kind) and - exists(DataFlow::Node srcnode, TaintKind srckind | - src = TTaintTrackingNode_(srcnode, _, _, srckind, this) and - not this.prunedEdge(srcnode, node, srckind, kind) - ) - ) - } + pragma[noinline] + predicate legacyExtensionStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(TaintTracking::Extension extension, DataFlow::Node srcnode, TaintKind srckind | + this.(TaintTracking::Configuration).isExtension(extension) and + src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and + srcnode.asCfgNode() = extension + | + extension.getASuccessorNode() = node.asCfgNode() and kind = srckind + or + extension.getASuccessorNode(srckind, kind) = node.asCfgNode() + or + extension.getASuccessorVariable() = node.asVariable() and kind = srckind + ) and + edgeLabel = "legacy extension" + } - private predicate prunedEdge( - DataFlow::Node srcnode, DataFlow::Node destnode, TaintKind srckind, TaintKind destkind - ) { - this.(TaintTracking::Configuration).isBarrierEdge(srcnode, destnode, srckind, destkind) - or - srckind = destkind and this.(TaintTracking::Configuration).isBarrierEdge(srcnode, destnode) - } - - private predicate unprunedStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - this.importStep(src, node, context, path, kind, edgeLabel) - or - this.fromImportStep(src, node, context, path, kind, edgeLabel) - or - this.attributeLoadStep(src, node, context, path, kind, edgeLabel) - or - this.getattrStep(src, node, context, path, kind, edgeLabel) - or - this.useStep(src, node, context, path, kind, edgeLabel) - or - this.callTaintStep(src, node, context, path, kind, edgeLabel) - or - this.returnFlowStep(src, node, context, path, kind, edgeLabel) - or - this.callFlowStep(src, node, context, path, kind, edgeLabel) - or - this.iterationStep(src, node, context, path, kind, edgeLabel) - or - this.yieldStep(src, node, context, path, kind, edgeLabel) - or - this.parameterStep(src, node, context, path, kind, edgeLabel) - or - this.ifExpStep(src, node, context, path, kind, edgeLabel) - or - this.essaFlowStep(src, node, context, path, kind, edgeLabel) - or - this.instantiationStep(src, node, context, path, kind, edgeLabel) - or - this.legacyExtensionStep(src, node, context, path, kind, edgeLabel) - or - exists(DataFlow::Node srcnode, TaintKind srckind | - this.(TaintTracking::Configuration).isAdditionalFlowStep(srcnode, node, srckind, kind) and - src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and - path.noAttribute() and - edgeLabel = "additional with kind" - ) - or - exists(DataFlow::Node srcnode | - this.(TaintTracking::Configuration).isAdditionalFlowStep(srcnode, node) and - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - path.noAttribute() and - edgeLabel = "additional" - ) - or - exists(DataFlow::Node srcnode, TaintKind srckind | - src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and - path.noAttribute() - | - kind = srckind.getTaintForFlowStep(srcnode.asCfgNode(), node.asCfgNode(), edgeLabel) - or - kind = srckind.(CollectionKind).getMember() and - srckind.(CollectionKind).flowToMember(srcnode, node) and - edgeLabel = "to member" - or - srckind = kind.(CollectionKind).getMember() and - kind.(CollectionKind).flowFromMember(srcnode, node) and - edgeLabel = "from member" - or - kind = srckind and srckind.flowStep(srcnode, node, edgeLabel) - or - kind = srckind and - srckind instanceof DictKind and - DictKind::flowStep(srcnode.asCfgNode(), node.asCfgNode(), edgeLabel) - or - kind = srckind and - srckind instanceof SequenceKind and - SequenceKind::flowStep(srcnode.asCfgNode(), node.asCfgNode(), edgeLabel) - ) - } - - pragma[noinline] - predicate importStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - edgeLabel = "import" and - exists(ModuleValue m, string name, AttributePath srcpath | - src = TTaintTrackingNode_(_, context, srcpath, kind, this) and - this.moduleAttributeTainted(m, name, src) and - node.asCfgNode().pointsTo(m) and - path = srcpath.getAttribute(name) - ) - } - - pragma[noinline] - predicate fromImportStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - edgeLabel = "from import" and - exists(ModuleValue m, string name | - src = TTaintTrackingNode_(_, context, path, kind, this) and - this.moduleAttributeTainted(m, name, src) and - node.asCfgNode().(ImportMemberNode).getModule(name).pointsTo(m) - ) - } - - pragma[noinline] - predicate attributeLoadStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(DataFlow::Node srcnode, AttributePath srcpath, string attrname | - src = TTaintTrackingNode_(srcnode, context, srcpath, kind, this) and - srcnode.asCfgNode() = node.asCfgNode().(AttrNode).getObject(attrname) and - path = srcpath.fromAttribute(attrname) and - edgeLabel = "from path attribute" - ) - or - exists(DataFlow::Node srcnode, TaintKind srckind, string attrname | - src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and - srcnode.asCfgNode() = node.asCfgNode().(AttrNode).getObject(attrname) and - kind = srckind.getTaintOfAttribute(attrname) and - edgeLabel = "from taint attribute" and - path instanceof NoAttribute - ) - } - - pragma[noinline] - predicate getattrStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(DataFlow::Node srcnode, AttributePath srcpath, TaintKind srckind, string attrname | - src = TTaintTrackingNode_(srcnode, context, srcpath, srckind, this) and - exists(CallNode call, ControlFlowNode arg | - call = node.asCfgNode() and - call.getFunction().pointsTo(ObjectInternal::builtin("getattr")) and - arg = call.getArg(0) and - attrname = call.getArg(1).getNode().(StrConst).getText() and - arg = srcnode.asCfgNode() - | - path = srcpath.fromAttribute(attrname) and - kind = srckind - or - path = srcpath and - kind = srckind.getTaintOfAttribute(attrname) - ) - ) and - edgeLabel = "getattr" - } - - pragma[noinline] - predicate useStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - node.asCfgNode() = srcnode.asVariable().getASourceUse() - ) and - edgeLabel = "use" - } - - /* If the return value is tainted without context, then it always flows back to the caller */ - pragma[noinline] - predicate returnFlowStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(CallNode call, PythonFunctionObjectInternal pyfunc, DataFlow::Node retval | - pyfunc.getACall() = call and - context = TNoParam() and - src = TTaintTrackingNode_(retval, TNoParam(), path, kind, this) and - node.asCfgNode() = call and - retval.asCfgNode() = - any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode() - ) and - edgeLabel = "return" - } - - /* - * Avoid taint flow from return value to caller as it can produce imprecise flow graphs - * Step directly from tainted argument to call result. - */ - - pragma[noinline] - predicate callFlowStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists( - CallNode call, PythonFunctionObjectInternal pyfunc, TaintTrackingContext callee, - DataFlow::Node retval, TaintTrackingNode retnode - | - this.callContexts(call, src, pyfunc, context, callee) and - retnode = TTaintTrackingNode_(retval, callee, path, kind, this) and - node.asCfgNode() = call and - retval.asCfgNode() = - any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode() - ) and - edgeLabel = "call" - } - - pragma[noinline] - predicate callContexts( - CallNode call, TaintTrackingNode argnode, PythonFunctionObjectInternal pyfunc, - TaintTrackingContext caller, TaintTrackingContext callee - ) { - exists(int arg, TaintKind callerKind, AttributePath callerPath | - this.callWithTaintedArgument(argnode, call, caller, pyfunc, arg, callerPath, callerKind) and - callee = TParamContext(callerKind, callerPath, arg) - ) - } - - predicate callWithTaintedArgument( - TaintTrackingNode src, CallNode call, TaintTrackingContext caller, CallableValue pyfunc, - int arg, AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, caller, path, kind, this) and - srcnode.asCfgNode() = pyfunc.getArgumentForCall(call, arg) - ) - } - - predicate instantiationStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(PythonFunctionValue init, EssaVariable self, TaintTrackingContext callee | - instantiationCall(node.asCfgNode(), src, init, context, callee) and - this.(EssaTaintTracking).taintedDefinition(_, self.getDefinition(), callee, path, kind) and - self.getSourceVariable().(Variable).isSelf() and - BaseFlow::reaches_exit(self) and - self.getScope() = init.getScope() - ) and - edgeLabel = "instantiation" - } - - predicate instantiationCall( - CallNode call, TaintTrackingNode argnode, PythonFunctionObjectInternal init, - TaintTrackingContext caller, TaintTrackingContext callee - ) { - exists(ClassValue cls | - call.getFunction().pointsTo(cls) and - cls.lookup("__init__") = init - | - exists(int arg, TaintKind callerKind, AttributePath callerPath, DataFlow::Node argument | - argnode = TTaintTrackingNode_(argument, caller, callerPath, callerKind, this) and - call.getArg(arg - 1) = argument.asCfgNode() and - callee = TParamContext(callerKind, callerPath, arg) - ) - ) - } - - pragma[noinline] - predicate callTaintStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(DataFlow::Node srcnode, CallNode call, TaintKind srckind, string name | - src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and - call.getFunction().(AttrNode).getObject(name) = src.getNode().asCfgNode() and - kind = srckind.getTaintOfMethodResult(name) and - node.asCfgNode() = call - ) and - edgeLabel = "call" - } - - pragma[noinline] - predicate iterationStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(ForNode for, DataFlow::Node sequence, TaintKind seqkind | - src = TTaintTrackingNode_(sequence, context, path, seqkind, this) and - for.iterates(_, sequence.asCfgNode()) and - node.asCfgNode() = for and - path.noAttribute() and - kind = seqkind.getTaintForIteration() - ) and - edgeLabel = "iteration" - } - - pragma[noinline] - predicate parameterStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(CallNode call, PythonFunctionObjectInternal pyfunc, int arg | - this.callWithTaintedArgument(src, call, _, pyfunc, arg, path, kind) and - node.asCfgNode() = pyfunc.getParameter(arg) and - context = TParamContext(kind, path, arg) - ) and - edgeLabel = "parameter" - } - - pragma[noinline] - predicate yieldStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(DataFlow::Node srcnode, TaintKind itemkind | - src = TTaintTrackingNode_(srcnode, context, path, itemkind, this) and - itemkind = kind.getTaintForIteration() and - exists(PyFunctionObject func | - func.getFunction().isGenerator() and - func.getACall() = node.asCfgNode() and - exists(Yield yield | - yield.getScope() = func.getFunction() and - yield.getValue() = srcnode.asCfgNode().getNode() - ) - ) - ) and - edgeLabel = "yield" - } - - pragma[noinline] - predicate ifExpStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - srcnode.asCfgNode() = node.asCfgNode().(IfExprNode).getAnOperand() - ) and - edgeLabel = "if exp" - } - - pragma[noinline] - predicate essaFlowStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - this - .(EssaTaintTracking) - .taintedDefinition(src, node.asVariable().getDefinition(), context, path, kind) and - edgeLabel = "" - } - - pragma[noinline] - predicate legacyExtensionStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(TaintTracking::Extension extension, DataFlow::Node srcnode, TaintKind srckind | - this.(TaintTracking::Configuration).isExtension(extension) and - src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and - srcnode.asCfgNode() = extension - | - extension.getASuccessorNode() = node.asCfgNode() and kind = srckind - or - extension.getASuccessorNode(srckind, kind) = node.asCfgNode() - or - extension.getASuccessorVariable() = node.asVariable() and kind = srckind - ) and - edgeLabel = "legacy extension" - } - - predicate moduleAttributeTainted(ModuleValue m, string name, TaintTrackingNode taint) { - exists(DataFlow::Node srcnode, EssaVariable var | - taint = TTaintTrackingNode_(srcnode, TNoParam(), _, _, this) and - var = srcnode.asVariable() and - var.getName() = name and - BaseFlow::reaches_exit(var) and - var.getScope() = m.getScope() - ) - } + predicate moduleAttributeTainted(ModuleValue m, string name, TaintTrackingNode taint) { + exists(DataFlow::Node srcnode, EssaVariable var | + taint = TTaintTrackingNode_(srcnode, TNoParam(), _, _, this) and + var = srcnode.asVariable() and + var.getName() = name and + BaseFlow::reaches_exit(var) and + var.getScope() = m.getScope() + ) + } } /** @@ -648,326 +648,326 @@ class TaintTrackingImplementation extends string { * This class handle tracking of ESSA variables. */ private class EssaTaintTracking extends string { - EssaTaintTracking() { this instanceof TaintTracking::Configuration } + EssaTaintTracking() { this instanceof TaintTracking::Configuration } - pragma[noinline] - predicate taintedDefinition( - TaintTrackingNode src, EssaDefinition defn, TaintTrackingContext context, AttributePath path, - TaintKind kind - ) { - this.taintedPhi(src, defn, context, path, kind) - or - this.taintedAssignment(src, defn, context, path, kind) - or - this.taintedMultiAssignment(src, defn, context, path, kind) - or - this.taintedAttributeAssignment(src, defn, context, path, kind) - or - this.taintedParameterDefinition(src, defn, context, path, kind) - or - this.taintedCallsite(src, defn, context, path, kind) - or - this.taintedMethodCallsite(src, defn, context, path, kind) - or - this.taintedUniEdge(src, defn, context, path, kind) - or - this.taintedPiNode(src, defn, context, path, kind) - or - this.taintedArgument(src, defn, context, path, kind) - or - this.taintedExceptionCapture(src, defn, context, path, kind) - or - this.taintedScopeEntryDefinition(src, defn, context, path, kind) - or - this.taintedWith(src, defn, context, path, kind) - } + pragma[noinline] + predicate taintedDefinition( + TaintTrackingNode src, EssaDefinition defn, TaintTrackingContext context, AttributePath path, + TaintKind kind + ) { + this.taintedPhi(src, defn, context, path, kind) + or + this.taintedAssignment(src, defn, context, path, kind) + or + this.taintedMultiAssignment(src, defn, context, path, kind) + or + this.taintedAttributeAssignment(src, defn, context, path, kind) + or + this.taintedParameterDefinition(src, defn, context, path, kind) + or + this.taintedCallsite(src, defn, context, path, kind) + or + this.taintedMethodCallsite(src, defn, context, path, kind) + or + this.taintedUniEdge(src, defn, context, path, kind) + or + this.taintedPiNode(src, defn, context, path, kind) + or + this.taintedArgument(src, defn, context, path, kind) + or + this.taintedExceptionCapture(src, defn, context, path, kind) + or + this.taintedScopeEntryDefinition(src, defn, context, path, kind) + or + this.taintedWith(src, defn, context, path, kind) + } - pragma[noinline] - private predicate taintedPhi( - TaintTrackingNode src, PhiFunction defn, TaintTrackingContext context, AttributePath path, - TaintKind kind - ) { - exists(DataFlow::Node srcnode, BasicBlock pred, EssaVariable predvar, DataFlow::Node phi | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - defn = phi.asVariable().getDefinition() and - predvar = defn.getInput(pred) and - not pred.unlikelySuccessor(defn.getBasicBlock()) and - not this.(TaintTracking::Configuration).isBarrierEdge(srcnode, phi) and - srcnode.asVariable() = predvar - ) - } + pragma[noinline] + private predicate taintedPhi( + TaintTrackingNode src, PhiFunction defn, TaintTrackingContext context, AttributePath path, + TaintKind kind + ) { + exists(DataFlow::Node srcnode, BasicBlock pred, EssaVariable predvar, DataFlow::Node phi | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + defn = phi.asVariable().getDefinition() and + predvar = defn.getInput(pred) and + not pred.unlikelySuccessor(defn.getBasicBlock()) and + not this.(TaintTracking::Configuration).isBarrierEdge(srcnode, phi) and + srcnode.asVariable() = predvar + ) + } - pragma[noinline] - private predicate taintedAssignment( - TaintTrackingNode src, AssignmentDefinition defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - defn.getValue() = srcnode.asCfgNode() - ) - } + pragma[noinline] + private predicate taintedAssignment( + TaintTrackingNode src, AssignmentDefinition defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + defn.getValue() = srcnode.asCfgNode() + ) + } - pragma[noinline] - private predicate taintedMultiAssignment( - TaintTrackingNode src, MultiAssignmentDefinition defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode, TaintKind srckind, Assign assign, int depth | - src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and - path.noAttribute() - | - assign.getValue().getAFlowNode() = srcnode.asCfgNode() and - depth = iterable_unpacking_descent(assign.getATarget().getAFlowNode(), defn.getDefiningNode()) and - kind = taint_at_depth(srckind, depth) - ) - } + pragma[noinline] + private predicate taintedMultiAssignment( + TaintTrackingNode src, MultiAssignmentDefinition defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode, TaintKind srckind, Assign assign, int depth | + src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and + path.noAttribute() + | + assign.getValue().getAFlowNode() = srcnode.asCfgNode() and + depth = iterable_unpacking_descent(assign.getATarget().getAFlowNode(), defn.getDefiningNode()) and + kind = taint_at_depth(srckind, depth) + ) + } - pragma[noinline] - private predicate taintedAttributeAssignment( - TaintTrackingNode src, AttributeAssignment defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode, AttributePath srcpath, string attrname | - src = TTaintTrackingNode_(srcnode, context, srcpath, kind, this) and - defn.getValue() = srcnode.asCfgNode() and - defn.getName() = attrname and - path = srcpath.getAttribute(attrname) - ) - } + pragma[noinline] + private predicate taintedAttributeAssignment( + TaintTrackingNode src, AttributeAssignment defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode, AttributePath srcpath, string attrname | + src = TTaintTrackingNode_(srcnode, context, srcpath, kind, this) and + defn.getValue() = srcnode.asCfgNode() and + defn.getName() = attrname and + path = srcpath.getAttribute(attrname) + ) + } - pragma[noinline] - private predicate taintedParameterDefinition( - TaintTrackingNode src, ParameterDefinition defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - srcnode.asCfgNode() = defn.getDefiningNode() - ) - } + pragma[noinline] + private predicate taintedParameterDefinition( + TaintTrackingNode src, ParameterDefinition defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + srcnode.asCfgNode() = defn.getDefiningNode() + ) + } - pragma[noinline] - private predicate taintedCallsite( - TaintTrackingNode src, CallsiteRefinement defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - /* - * In the interest of simplicity and performance we assume that tainted escaping variables remain tainted across calls. - * In the cases were this assumption is false, it is easy enough to add an additional barrier. - */ - - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - srcnode.asVariable() = defn.getInput() - ) - } - - pragma[noinline] - private predicate taintedMethodCallsite( - TaintTrackingNode src, MethodCallsiteRefinement defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - srcnode.asVariable() = defn.getInput() - ) - } - - pragma[noinline] - private predicate taintedUniEdge( - TaintTrackingNode src, SingleSuccessorGuard defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - srcnode.asVariable() = defn.getInput() and - not this.(TaintTracking::Configuration).isBarrierTest(defn.getTest(), defn.getSense()) - ) - } - - private predicate taintedPiNode( - TaintTrackingNode src, PyEdgeRefinement defn, TaintTrackingContext context, AttributePath path, - TaintKind kind - ) { - taintedPiNodeOneway(src, defn, context, path, kind) - or - taintedPiNodeBothways(src, defn, context, path, kind) - } - - pragma[noinline] - private predicate taintedPiNodeOneway( - TaintTrackingNode src, PyEdgeRefinement defn, TaintTrackingContext context, AttributePath path, - TaintKind kind - ) { - exists(DataFlow::Node srcnode, ControlFlowNode use | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - not this.(TaintTracking::Configuration).isBarrierTest(defn.getTest(), defn.getSense()) and - defn.getSense() = testEvaluates(defn, defn.getTest(), use, src) - ) - } - - pragma[noinline] - private predicate taintedPiNodeBothways( - TaintTrackingNode src, PyEdgeRefinement defn, TaintTrackingContext context, AttributePath path, - TaintKind kind - ) { - exists(DataFlow::Node srcnode, ControlFlowNode test, ControlFlowNode use | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - piNodeTestAndUse(defn, test, use) and - srcnode.asVariable() = defn.getInput() and - not this.(TaintTracking::Configuration).isBarrierTest(test, defn.getSense()) and - testEvaluatesMaybe(test, use) - ) - } - - pragma[noinline] - private predicate taintedArgument( - TaintTrackingNode src, ArgumentRefinement defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - defn.getInput() = srcnode.asVariable() - ) - } - - pragma[noinline] - private predicate taintedExceptionCapture( - TaintTrackingNode src, ExceptionCapture defn, TaintTrackingContext context, AttributePath path, - TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - srcnode.asCfgNode() = defn.getDefiningNode() - ) - } - - pragma[noinline] - private predicate taintedScopeEntryDefinition( - TaintTrackingNode src, ScopeEntryDefinition defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(EssaVariable var | - BaseFlow::scope_entry_value_transfer_from_earlier(var, _, defn, _) and - this.taintedDefinition(src, var.getDefinition(), context, path, kind) - ) - } - - pragma[noinline] - private predicate taintedWith( - TaintTrackingNode src, WithDefinition defn, TaintTrackingContext context, AttributePath path, - TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - with_flow(_, srcnode.asCfgNode(), defn.getDefiningNode()) - ) - } - - /** - * Gets the boolean value that `test` evaluates to when `use` is tainted with `kind` - * and `test` and `use` are part of a test in a branch. + pragma[noinline] + private predicate taintedCallsite( + TaintTrackingNode src, CallsiteRefinement defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + /* + * In the interest of simplicity and performance we assume that tainted escaping variables remain tainted across calls. + * In the cases were this assumption is false, it is easy enough to add an additional barrier. */ - private boolean testEvaluates( - PyEdgeRefinement defn, ControlFlowNode test, ControlFlowNode use, TaintTrackingNode src - ) { - defn.getTest().getAChild*() = use and - exists(DataFlow::Node srcnode, TaintKind kind | - srcnode.asVariable() = defn.getInput() and - srcnode.asVariable().getASourceUse() = use and - src = TTaintTrackingNode_(srcnode, _, TNoAttribute(), kind, this) - | - test = use and result = kind.booleanValue() - or - exists(ControlFlowNode const | - Filters::equality_test(test, use, result.booleanNot(), const) and - const.getNode() instanceof ImmutableLiteral - ) - or - exists(ControlFlowNode c, ClassValue cls | - Filters::isinstance(test, c, use) and - c.pointsTo(cls) - | - exists(ClassValue scls | scls = kind.getType() | - scls.getASuperType() = cls and result = true - or - not scls.getASuperType() = cls and result = false - ) - or - not exists(kind.getType()) and result = maybe() - ) + + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + srcnode.asVariable() = defn.getInput() + ) + } + + pragma[noinline] + private predicate taintedMethodCallsite( + TaintTrackingNode src, MethodCallsiteRefinement defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + srcnode.asVariable() = defn.getInput() + ) + } + + pragma[noinline] + private predicate taintedUniEdge( + TaintTrackingNode src, SingleSuccessorGuard defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + srcnode.asVariable() = defn.getInput() and + not this.(TaintTracking::Configuration).isBarrierTest(defn.getTest(), defn.getSense()) + ) + } + + private predicate taintedPiNode( + TaintTrackingNode src, PyEdgeRefinement defn, TaintTrackingContext context, AttributePath path, + TaintKind kind + ) { + taintedPiNodeOneway(src, defn, context, path, kind) + or + taintedPiNodeBothways(src, defn, context, path, kind) + } + + pragma[noinline] + private predicate taintedPiNodeOneway( + TaintTrackingNode src, PyEdgeRefinement defn, TaintTrackingContext context, AttributePath path, + TaintKind kind + ) { + exists(DataFlow::Node srcnode, ControlFlowNode use | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + not this.(TaintTracking::Configuration).isBarrierTest(defn.getTest(), defn.getSense()) and + defn.getSense() = testEvaluates(defn, defn.getTest(), use, src) + ) + } + + pragma[noinline] + private predicate taintedPiNodeBothways( + TaintTrackingNode src, PyEdgeRefinement defn, TaintTrackingContext context, AttributePath path, + TaintKind kind + ) { + exists(DataFlow::Node srcnode, ControlFlowNode test, ControlFlowNode use | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + piNodeTestAndUse(defn, test, use) and + srcnode.asVariable() = defn.getInput() and + not this.(TaintTracking::Configuration).isBarrierTest(test, defn.getSense()) and + testEvaluatesMaybe(test, use) + ) + } + + pragma[noinline] + private predicate taintedArgument( + TaintTrackingNode src, ArgumentRefinement defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + defn.getInput() = srcnode.asVariable() + ) + } + + pragma[noinline] + private predicate taintedExceptionCapture( + TaintTrackingNode src, ExceptionCapture defn, TaintTrackingContext context, AttributePath path, + TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + srcnode.asCfgNode() = defn.getDefiningNode() + ) + } + + pragma[noinline] + private predicate taintedScopeEntryDefinition( + TaintTrackingNode src, ScopeEntryDefinition defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(EssaVariable var | + BaseFlow::scope_entry_value_transfer_from_earlier(var, _, defn, _) and + this.taintedDefinition(src, var.getDefinition(), context, path, kind) + ) + } + + pragma[noinline] + private predicate taintedWith( + TaintTrackingNode src, WithDefinition defn, TaintTrackingContext context, AttributePath path, + TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + with_flow(_, srcnode.asCfgNode(), defn.getDefiningNode()) + ) + } + + /** + * Gets the boolean value that `test` evaluates to when `use` is tainted with `kind` + * and `test` and `use` are part of a test in a branch. + */ + private boolean testEvaluates( + PyEdgeRefinement defn, ControlFlowNode test, ControlFlowNode use, TaintTrackingNode src + ) { + defn.getTest().getAChild*() = use and + exists(DataFlow::Node srcnode, TaintKind kind | + srcnode.asVariable() = defn.getInput() and + srcnode.asVariable().getASourceUse() = use and + src = TTaintTrackingNode_(srcnode, _, TNoAttribute(), kind, this) + | + test = use and result = kind.booleanValue() + or + exists(ControlFlowNode const | + Filters::equality_test(test, use, result.booleanNot(), const) and + const.getNode() instanceof ImmutableLiteral + ) + or + exists(ControlFlowNode c, ClassValue cls | + Filters::isinstance(test, c, use) and + c.pointsTo(cls) + | + exists(ClassValue scls | scls = kind.getType() | + scls.getASuperType() = cls and result = true + or + not scls.getASuperType() = cls and result = false ) or - result = testEvaluates(defn, not_operand(test), use, src).booleanNot() - } + not exists(kind.getType()) and result = maybe() + ) + ) + or + result = testEvaluates(defn, not_operand(test), use, src).booleanNot() + } - /** - * Holds if `test` is the test in a branch and `use` is that test - * with all the `not` prefixes removed. - */ - private predicate boolean_filter(ControlFlowNode test, ControlFlowNode use) { - any(PyEdgeRefinement ref).getTest() = test and - ( - use = test - or - exists(ControlFlowNode notuse | - boolean_filter(test, notuse) and - use = not_operand(notuse) - ) - ) - } + /** + * Holds if `test` is the test in a branch and `use` is that test + * with all the `not` prefixes removed. + */ + private predicate boolean_filter(ControlFlowNode test, ControlFlowNode use) { + any(PyEdgeRefinement ref).getTest() = test and + ( + use = test + or + exists(ControlFlowNode notuse | + boolean_filter(test, notuse) and + use = not_operand(notuse) + ) + ) + } } private predicate testEvaluatesMaybe(ControlFlowNode test, ControlFlowNode use) { - any(PyEdgeRefinement ref).getTest().getAChild*() = test and - test.getAChild*() = use and - not test.(UnaryExprNode).getNode().getOp() instanceof Not and - not exists(ControlFlowNode const | - Filters::equality_test(test, use, _, const) and - const.getNode() instanceof ImmutableLiteral - ) and - not Filters::isinstance(test, _, use) and - not test = use - or - testEvaluatesMaybe(not_operand(test), use) + any(PyEdgeRefinement ref).getTest().getAChild*() = test and + test.getAChild*() = use and + not test.(UnaryExprNode).getNode().getOp() instanceof Not and + not exists(ControlFlowNode const | + Filters::equality_test(test, use, _, const) and + const.getNode() instanceof ImmutableLiteral + ) and + not Filters::isinstance(test, _, use) and + not test = use + or + testEvaluatesMaybe(not_operand(test), use) } /** Gets the operand of a unary `not` expression. */ private ControlFlowNode not_operand(ControlFlowNode expr) { - expr.(UnaryExprNode).getNode().getOp() instanceof Not and - result = expr.(UnaryExprNode).getOperand() + expr.(UnaryExprNode).getNode().getOp() instanceof Not and + result = expr.(UnaryExprNode).getOperand() } /* Helper predicate for tainted_with */ private predicate with_flow(With with, ControlFlowNode contextManager, ControlFlowNode var) { - with.getContextExpr() = contextManager.getNode() and - with.getOptionalVars() = var.getNode() and - contextManager.strictlyDominates(var) + with.getContextExpr() = contextManager.getNode() and + with.getOptionalVars() = var.getNode() and + contextManager.strictlyDominates(var) } /* Helper predicate for taintedPiNode */ pragma[noinline] private predicate piNodeTestAndUse(PyEdgeRefinement defn, ControlFlowNode test, ControlFlowNode use) { - test = defn.getTest() and use = defn.getInput().getASourceUse() and test.getAChild*() = use + test = defn.getTest() and use = defn.getInput().getASourceUse() and test.getAChild*() = use } /** Helper predicate for taintedMultiAssignment */ private TaintKind taint_at_depth(SequenceKind parent_kind, int depth) { - depth >= 0 and - ( - // base-case #0 - depth = 0 and - result = parent_kind - or - // base-case #1 - depth = 1 and - result = parent_kind.getMember() - or - // recursive case - depth > 1 and - result = taint_at_depth(parent_kind.getMember(), depth - 1) - ) + depth >= 0 and + ( + // base-case #0 + depth = 0 and + result = parent_kind + or + // base-case #1 + depth = 1 and + result = parent_kind.getMember() + or + // recursive case + depth > 1 and + result = taint_at_depth(parent_kind.getMember(), depth - 1) + ) } /** @@ -983,25 +983,25 @@ private TaintKind taint_at_depth(SequenceKind parent_kind, int depth) { * - with `left_defn` = `*y`, `left_parent` = `((x, *y), ...)`, result = 1 */ int iterable_unpacking_descent(SequenceNode left_parent, ControlFlowNode left_defn) { - exists(Assign a | a.getATarget().getASubExpression*().getAFlowNode() = left_parent) and - left_parent.getAnElement() = left_defn and - // Handle `a, *b = some_iterable` - if left_defn instanceof StarredNode then result = 0 else result = 1 - or - result = 1 + iterable_unpacking_descent(left_parent.getAnElement(), left_defn) + exists(Assign a | a.getATarget().getASubExpression*().getAFlowNode() = left_parent) and + left_parent.getAnElement() = left_defn and + // Handle `a, *b = some_iterable` + if left_defn instanceof StarredNode then result = 0 else result = 1 + or + result = 1 + iterable_unpacking_descent(left_parent.getAnElement(), left_defn) } module Implementation { - /* A call that returns a copy (or similar) of the argument */ - predicate copyCall(ControlFlowNode fromnode, CallNode tonode) { - tonode.getFunction().(AttrNode).getObject("copy") = fromnode - or - exists(ModuleObject copy, string name | name = "copy" or name = "deepcopy" | - copy.attr(name).(FunctionObject).getACall() = tonode and - tonode.getArg(0) = fromnode - ) - or - tonode.getFunction().pointsTo(ObjectInternal::builtin("reversed")) and - tonode.getArg(0) = fromnode - } + /* A call that returns a copy (or similar) of the argument */ + predicate copyCall(ControlFlowNode fromnode, CallNode tonode) { + tonode.getFunction().(AttrNode).getObject("copy") = fromnode + or + exists(ModuleObject copy, string name | name = "copy" or name = "deepcopy" | + copy.attr(name).(FunctionObject).getACall() = tonode and + tonode.getArg(0) = fromnode + ) + or + tonode.getFunction().pointsTo(ObjectInternal::builtin("reversed")) and + tonode.getArg(0) = fromnode + } } diff --git a/python/ql/src/semmle/python/dataflow/Legacy.qll b/python/ql/src/semmle/python/dataflow/Legacy.qll index ffdb7aee869..df0649963d0 100644 --- a/python/ql/src/semmle/python/dataflow/Legacy.qll +++ b/python/ql/src/semmle/python/dataflow/Legacy.qll @@ -4,65 +4,65 @@ import semmle.python.dataflow.Implementation /* For backwards compatibility -- Use `TaintTrackingContext` instead. */ deprecated class CallContext extends TaintTrackingContext { - TaintTrackingContext getCallee(CallNode call) { result.getCaller(call) = this } + TaintTrackingContext getCallee(CallNode call) { result.getCaller(call) = this } - predicate appliesToScope(Scope s) { - exists(PythonFunctionObjectInternal func, TaintKind param, AttributePath path, int n | - this = TParamContext(param, path, n) and - exists(TaintTrackingImplementation impl | - impl.callWithTaintedArgument(_, _, _, func, n, path, param) and - s = func.getScope() - ) - ) - or - this.isTop() - } + predicate appliesToScope(Scope s) { + exists(PythonFunctionObjectInternal func, TaintKind param, AttributePath path, int n | + this = TParamContext(param, path, n) and + exists(TaintTrackingImplementation impl | + impl.callWithTaintedArgument(_, _, _, func, n, path, param) and + s = func.getScope() + ) + ) + or + this.isTop() + } } /* Backwards compatibility with config-less taint-tracking */ private class LegacyConfiguration extends TaintTracking::Configuration { - LegacyConfiguration() { - /* A name that won't be accidentally chosen by users */ - this = "Semmle: Internal legacy configuration" - } + LegacyConfiguration() { + /* A name that won't be accidentally chosen by users */ + this = "Semmle: Internal legacy configuration" + } - override predicate isSource(TaintSource src) { src = src } + override predicate isSource(TaintSource src) { src = src } - override predicate isSink(TaintSink sink) { sink = sink } + override predicate isSink(TaintSink sink) { sink = sink } - override predicate isSanitizer(Sanitizer sanitizer) { sanitizer = sanitizer } + override predicate isSanitizer(Sanitizer sanitizer) { sanitizer = sanitizer } - override predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dest) { - exists(DataFlowExtension::DataFlowNode legacyExtension | src.asCfgNode() = legacyExtension | - dest.asCfgNode() = legacyExtension.getASuccessorNode() - or - dest.asVariable() = legacyExtension.getASuccessorVariable() - or - dest.asCfgNode() = legacyExtension.getAReturnSuccessorNode(_) - or - dest.asCfgNode() = legacyExtension.getACalleeSuccessorNode(_) - ) - } + override predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dest) { + exists(DataFlowExtension::DataFlowNode legacyExtension | src.asCfgNode() = legacyExtension | + dest.asCfgNode() = legacyExtension.getASuccessorNode() + or + dest.asVariable() = legacyExtension.getASuccessorVariable() + or + dest.asCfgNode() = legacyExtension.getAReturnSuccessorNode(_) + or + dest.asCfgNode() = legacyExtension.getACalleeSuccessorNode(_) + ) + } - override predicate isAdditionalFlowStep( - DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind - ) { - exists(DataFlowExtension::DataFlowNode legacyExtension | src.asCfgNode() = legacyExtension | - dest.asCfgNode() = legacyExtension.getASuccessorNode(srckind, destkind) - ) - } + override predicate isAdditionalFlowStep( + DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind + ) { + exists(DataFlowExtension::DataFlowNode legacyExtension | src.asCfgNode() = legacyExtension | + dest.asCfgNode() = legacyExtension.getASuccessorNode(srckind, destkind) + ) + } - override predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node dest) { - ( - exists(DataFlowExtension::DataFlowVariable legacyExtension | - src.asVariable() = legacyExtension and - legacyExtension.prunedSuccessor(dest.asVariable()) - ) - or - exists(DataFlowExtension::DataFlowNode legacyExtension | - src.asCfgNode() = legacyExtension and - legacyExtension.prunedSuccessor(dest.asCfgNode()) - ) - ) - } + override predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node dest) { + ( + exists(DataFlowExtension::DataFlowVariable legacyExtension | + src.asVariable() = legacyExtension and + legacyExtension.prunedSuccessor(dest.asVariable()) + ) + or + exists(DataFlowExtension::DataFlowNode legacyExtension | + src.asCfgNode() = legacyExtension and + legacyExtension.prunedSuccessor(dest.asCfgNode()) + ) + ) + } } diff --git a/python/ql/src/semmle/python/dataflow/StateTracking.qll b/python/ql/src/semmle/python/dataflow/StateTracking.qll index 55bed5bbfff..d302ea5c1ba 100644 --- a/python/ql/src/semmle/python/dataflow/StateTracking.qll +++ b/python/ql/src/semmle/python/dataflow/StateTracking.qll @@ -16,159 +16,159 @@ private import semmle.python.objects.ObjectInternal /** A state that should be tracked. */ abstract class TrackableState extends string { - bindingset[this] - TrackableState() { this = this } + bindingset[this] + TrackableState() { this = this } - /** Holds if this state may apply to the control flow node `f`, regardless of the context. */ - final predicate appliesTo(ControlFlowNode f) { this.appliesTo(f, _) } + /** Holds if this state may apply to the control flow node `f`, regardless of the context. */ + final predicate appliesTo(ControlFlowNode f) { this.appliesTo(f, _) } - /** Holds if this state may not apply to the control flow node `f`, given the context `ctx`. */ - final predicate appliesTo(ControlFlowNode f, Context ctx) { - StateTracking::appliesToNode(this, f, ctx, true) - } + /** Holds if this state may not apply to the control flow node `f`, given the context `ctx`. */ + final predicate appliesTo(ControlFlowNode f, Context ctx) { + StateTracking::appliesToNode(this, f, ctx, true) + } - /** Holds if this state may apply to the control flow node `f`, given the context `ctx`. */ - final predicate mayNotApplyTo(ControlFlowNode f, Context ctx) { - StateTracking::appliesToNode(this, f, ctx, false) - } + /** Holds if this state may apply to the control flow node `f`, given the context `ctx`. */ + final predicate mayNotApplyTo(ControlFlowNode f, Context ctx) { + StateTracking::appliesToNode(this, f, ctx, false) + } - /** Holds if this state may apply to the control flow node `f`, regardless of the context. */ - final predicate mayNotApplyTo(ControlFlowNode f) { this.mayNotApplyTo(f, _) } + /** Holds if this state may apply to the control flow node `f`, regardless of the context. */ + final predicate mayNotApplyTo(ControlFlowNode f) { this.mayNotApplyTo(f, _) } - /** Holds if `test` shows value to be untainted with `taint`, given the context `ctx`. */ - predicate testsFor(PyEdgeRefinement test, Context ctx, boolean sense) { - ctx.appliesToScope(test.getScope()) and this.testsFor(test, sense) - } + /** Holds if `test` shows value to be untainted with `taint`, given the context `ctx`. */ + predicate testsFor(PyEdgeRefinement test, Context ctx, boolean sense) { + ctx.appliesToScope(test.getScope()) and this.testsFor(test, sense) + } - /** Holds if `test` shows value to be untainted with `taint` */ - predicate testsFor(PyEdgeRefinement test, boolean sense) { none() } + /** Holds if `test` shows value to be untainted with `taint` */ + predicate testsFor(PyEdgeRefinement test, boolean sense) { none() } - /** - * Holds if state starts at `f`. - * Either this predicate or `startsAt(ControlFlowNode f, Context ctx)` - * should be overriden by sub-classes. - */ - predicate startsAt(ControlFlowNode f) { none() } + /** + * Holds if state starts at `f`. + * Either this predicate or `startsAt(ControlFlowNode f, Context ctx)` + * should be overriden by sub-classes. + */ + predicate startsAt(ControlFlowNode f) { none() } - /** - * Holds if state starts at `f` given context `ctx`. - * Either this predicate or `startsAt(ControlFlowNode f)` - * should be overriden by sub-classes. - */ - pragma[noinline] - predicate startsAt(ControlFlowNode f, Context ctx) { ctx.appliesTo(f) and this.startsAt(f) } + /** + * Holds if state starts at `f` given context `ctx`. + * Either this predicate or `startsAt(ControlFlowNode f)` + * should be overriden by sub-classes. + */ + pragma[noinline] + predicate startsAt(ControlFlowNode f, Context ctx) { ctx.appliesTo(f) and this.startsAt(f) } - /** - * Holds if state ends at `f`. - * Either this predicate or `endsAt(ControlFlowNode f, Context ctx)` - * may be overriden by sub-classes. - */ - predicate endsAt(ControlFlowNode f) { none() } + /** + * Holds if state ends at `f`. + * Either this predicate or `endsAt(ControlFlowNode f, Context ctx)` + * may be overriden by sub-classes. + */ + predicate endsAt(ControlFlowNode f) { none() } - /** - * Holds if state ends at `f` given context `ctx`. - * Either this predicate or `endsAt(ControlFlowNode f)` - * may be overriden by sub-classes. - */ - pragma[noinline] - predicate endsAt(ControlFlowNode f, Context ctx) { ctx.appliesTo(f) and this.endsAt(f) } + /** + * Holds if state ends at `f` given context `ctx`. + * Either this predicate or `endsAt(ControlFlowNode f)` + * may be overriden by sub-classes. + */ + pragma[noinline] + predicate endsAt(ControlFlowNode f, Context ctx) { ctx.appliesTo(f) and this.endsAt(f) } } module StateTracking { - private predicate not_allowed(TrackableState state, ControlFlowNode f, Context ctx, boolean sense) { - state.endsAt(f, ctx) and sense = true - or - state.startsAt(f, ctx) and sense = false - } + private predicate not_allowed(TrackableState state, ControlFlowNode f, Context ctx, boolean sense) { + state.endsAt(f, ctx) and sense = true + or + state.startsAt(f, ctx) and sense = false + } - /** - * Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) to - * control flow node `f` given the context `ctx`. - */ - predicate appliesToNode(TrackableState state, ControlFlowNode f, Context ctx, boolean sense) { - state.endsAt(f, ctx) and sense = false + /** + * Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) to + * control flow node `f` given the context `ctx`. + */ + predicate appliesToNode(TrackableState state, ControlFlowNode f, Context ctx, boolean sense) { + state.endsAt(f, ctx) and sense = false + or + state.startsAt(f, ctx) and sense = true + or + not not_allowed(state, f, ctx, sense) and + ( + exists(BasicBlock b | + /* First node in a block */ + f = b.getNode(0) and appliesAtBlockStart(state, b, ctx, sense) or - state.startsAt(f, ctx) and sense = true - or - not not_allowed(state, f, ctx, sense) and - ( - exists(BasicBlock b | - /* First node in a block */ - f = b.getNode(0) and appliesAtBlockStart(state, b, ctx, sense) - or - /* Other nodes in block, except trackable calls */ - exists(int n | - f = b.getNode(n) and - appliesToNode(state, b.getNode(n - 1), ctx, sense) and - not exists(PythonFunctionObjectInternal func, Context callee | - callee.fromCall(f, func, ctx) - ) - ) - ) - or - /* Function entry via call */ - exists(PythonFunctionObjectInternal func, CallNode call, Context caller | - ctx.fromCall(call, func, caller) and - func.getScope().getEntryNode() = f and - appliesToNode(state, call.getAPredecessor(), caller, sense) - ) - or - /* Function return */ - exists(PythonFunctionObjectInternal func, Context callee | - callee.fromCall(f, func, ctx) and - appliesToNode(state, func.getScope().getANormalExit(), callee, sense) - ) - or - /* Other scope entries */ - exists(Scope s | - s.getEntryNode() = f and - ctx.appliesToScope(s) - | - not exists(Scope pred | pred.precedes(s)) and - (ctx.isImport() or ctx.isRuntime()) and - sense = false - or - exists(Scope pred, Context pred_ctx | - appliesToNode(state, pred.getANormalExit(), pred_ctx, sense) and - pred.precedes(s) and - ctx.isRuntime() - | - pred_ctx.isRuntime() or pred_ctx.isImport() - ) - ) + /* Other nodes in block, except trackable calls */ + exists(int n | + f = b.getNode(n) and + appliesToNode(state, b.getNode(n - 1), ctx, sense) and + not exists(PythonFunctionObjectInternal func, Context callee | + callee.fromCall(f, func, ctx) + ) ) - } + ) + or + /* Function entry via call */ + exists(PythonFunctionObjectInternal func, CallNode call, Context caller | + ctx.fromCall(call, func, caller) and + func.getScope().getEntryNode() = f and + appliesToNode(state, call.getAPredecessor(), caller, sense) + ) + or + /* Function return */ + exists(PythonFunctionObjectInternal func, Context callee | + callee.fromCall(f, func, ctx) and + appliesToNode(state, func.getScope().getANormalExit(), callee, sense) + ) + or + /* Other scope entries */ + exists(Scope s | + s.getEntryNode() = f and + ctx.appliesToScope(s) + | + not exists(Scope pred | pred.precedes(s)) and + (ctx.isImport() or ctx.isRuntime()) and + sense = false + or + exists(Scope pred, Context pred_ctx | + appliesToNode(state, pred.getANormalExit(), pred_ctx, sense) and + pred.precedes(s) and + ctx.isRuntime() + | + pred_ctx.isRuntime() or pred_ctx.isImport() + ) + ) + ) + } - /** - * Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) at the - * start of basic block `block` given the context `ctx`. - */ - private predicate appliesAtBlockStart( - TrackableState state, BasicBlock block, Context ctx, boolean sense - ) { - exists(PyEdgeRefinement test | - test.getSuccessor() = block and - state.testsFor(test, ctx, sense) - ) - or - exists(BasicBlock pred | - pred.getASuccessor() = block and - appliesAtBlockEnd(state, pred, ctx, sense) and - not exists(PyEdgeRefinement test | - test.getPredecessor() = pred and - test.getSuccessor() = block and - state.testsFor(test, sense.booleanNot()) - ) - ) - } + /** + * Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) at the + * start of basic block `block` given the context `ctx`. + */ + private predicate appliesAtBlockStart( + TrackableState state, BasicBlock block, Context ctx, boolean sense + ) { + exists(PyEdgeRefinement test | + test.getSuccessor() = block and + state.testsFor(test, ctx, sense) + ) + or + exists(BasicBlock pred | + pred.getASuccessor() = block and + appliesAtBlockEnd(state, pred, ctx, sense) and + not exists(PyEdgeRefinement test | + test.getPredecessor() = pred and + test.getSuccessor() = block and + state.testsFor(test, sense.booleanNot()) + ) + ) + } - /** - * Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) at the - * end of basic block `block` given the context `ctx`. - */ - private predicate appliesAtBlockEnd( - TrackableState state, BasicBlock block, Context ctx, boolean sense - ) { - appliesToNode(state, block.getLastNode(), ctx, sense) - } + /** + * Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) at the + * end of basic block `block` given the context `ctx`. + */ + private predicate appliesAtBlockEnd( + TrackableState state, BasicBlock block, Context ctx, boolean sense + ) { + appliesToNode(state, block.getLastNode(), ctx, sense) + } } diff --git a/python/ql/src/semmle/python/dataflow/TaintTracking.qll b/python/ql/src/semmle/python/dataflow/TaintTracking.qll index f264e2dd600..d6d1f9388c4 100755 --- a/python/ql/src/semmle/python/dataflow/TaintTracking.qll +++ b/python/ql/src/semmle/python/dataflow/TaintTracking.qll @@ -101,82 +101,82 @@ import semmle.python.dataflow.Configuration * the local file system. */ abstract class TaintKind extends string { - bindingset[this] - TaintKind() { any() } + bindingset[this] + TaintKind() { any() } - /** - * Gets the kind of taint that the named attribute will have if an object is tainted with this taint. - * In other words, if `x` has this kind of taint then it implies that `x.name` - * has `result` kind of taint. + /** + * Gets the kind of taint that the named attribute will have if an object is tainted with this taint. + * In other words, if `x` has this kind of taint then it implies that `x.name` + * has `result` kind of taint. + */ + TaintKind getTaintOfAttribute(string name) { none() } + + /** + * Gets the kind of taint results from calling the named method if an object is tainted with this taint. + * In other words, if `x` has this kind of taint then it implies that `x.name()` + * has `result` kind of taint. + */ + TaintKind getTaintOfMethodResult(string name) { none() } + + /** + * Gets the taint resulting from the flow step `fromnode` -> `tonode`. + */ + TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { none() } + + /** + * Gets the taint resulting from the flow step `fromnode` -> `tonode`, with `edgeLabel` + */ + TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode, string edgeLabel) { + result = this.getTaintForFlowStep(fromnode, tonode) and + edgeLabel = "custom taint flow step for " + this + } + + /** + * Holds if this kind of taint "taints" `expr`. + */ + final predicate taints(ControlFlowNode expr) { + exists(TaintedNode n | n.getTaintKind() = this and n.getCfgNode() = expr) + } + + /** DEPRECATED -- Use getType() instead */ + deprecated ClassObject getClass() { none() } + + /** + * Gets the class of this kind of taint. + * For example, if this were a kind of string taint + * the `result` would be `theStrType()`. + */ + ClassValue getType() { none() } + + /** + * Gets the boolean values (may be one, neither, or both) that + * may result from the Python expression `bool(this)` + */ + boolean booleanValue() { + /* + * Default to true as the vast majority of taint is strings and + * the empty string is almost always benign. */ - TaintKind getTaintOfAttribute(string name) { none() } - /** - * Gets the kind of taint results from calling the named method if an object is tainted with this taint. - * In other words, if `x` has this kind of taint then it implies that `x.name()` - * has `result` kind of taint. - */ - TaintKind getTaintOfMethodResult(string name) { none() } + result = true + } - /** - * Gets the taint resulting from the flow step `fromnode` -> `tonode`. - */ - TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { none() } + string repr() { result = this } - /** - * Gets the taint resulting from the flow step `fromnode` -> `tonode`, with `edgeLabel` - */ - TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode, string edgeLabel) { - result = this.getTaintForFlowStep(fromnode, tonode) and - edgeLabel = "custom taint flow step for " + this - } + /** + * Gets the taint resulting from iterating over this kind of taint. + * For example iterating over a text file produces lines. So iterating + * over a tainted file would result in tainted strings + */ + TaintKind getTaintForIteration() { none() } - /** - * Holds if this kind of taint "taints" `expr`. - */ - final predicate taints(ControlFlowNode expr) { - exists(TaintedNode n | n.getTaintKind() = this and n.getCfgNode() = expr) - } - - /** DEPRECATED -- Use getType() instead */ - deprecated ClassObject getClass() { none() } - - /** - * Gets the class of this kind of taint. - * For example, if this were a kind of string taint - * the `result` would be `theStrType()`. - */ - ClassValue getType() { none() } - - /** - * Gets the boolean values (may be one, neither, or both) that - * may result from the Python expression `bool(this)` - */ - boolean booleanValue() { - /* - * Default to true as the vast majority of taint is strings and - * the empty string is almost always benign. - */ - - result = true - } - - string repr() { result = this } - - /** - * Gets the taint resulting from iterating over this kind of taint. - * For example iterating over a text file produces lines. So iterating - * over a tainted file would result in tainted strings - */ - TaintKind getTaintForIteration() { none() } - - predicate flowStep(DataFlow::Node fromnode, DataFlow::Node tonode, string edgeLabel) { - exists(DataFlowExtension::DataFlowVariable v | - v = fromnode.asVariable() and - v.getASuccessorVariable() = tonode.asVariable() - ) and - edgeLabel = "custom taint variable step" - } + predicate flowStep(DataFlow::Node fromnode, DataFlow::Node tonode, string edgeLabel) { + exists(DataFlowExtension::DataFlowVariable v | + v = fromnode.asVariable() and + v.getASuccessorVariable() = tonode.asVariable() + ) and + edgeLabel = "custom taint variable step" + } } /** @@ -193,19 +193,19 @@ class FlowLabel = TaintKind; * and ease of preventing infinite recursion. */ abstract class CollectionKind extends TaintKind { - bindingset[this] - CollectionKind() { - (this.charAt(0) = "[" or this.charAt(0) = "{") and - /* Prevent any collection kinds more than 2 deep */ - not this.charAt(2) = "[" and - not this.charAt(2) = "{" - } + bindingset[this] + CollectionKind() { + (this.charAt(0) = "[" or this.charAt(0) = "{") and + /* Prevent any collection kinds more than 2 deep */ + not this.charAt(2) = "[" and + not this.charAt(2) = "{" + } - abstract TaintKind getMember(); + abstract TaintKind getMember(); - abstract predicate flowFromMember(DataFlow::Node fromnode, DataFlow::Node tonode); + abstract predicate flowFromMember(DataFlow::Node fromnode, DataFlow::Node tonode); - abstract predicate flowToMember(DataFlow::Node fromnode, DataFlow::Node tonode); + abstract predicate flowToMember(DataFlow::Node fromnode, DataFlow::Node tonode); } /** @@ -213,82 +213,82 @@ abstract class CollectionKind extends TaintKind { * Typically a sequence, but can include sets. */ class SequenceKind extends CollectionKind { - TaintKind itemKind; + TaintKind itemKind; - SequenceKind() { this = "[" + itemKind + "]" } + SequenceKind() { this = "[" + itemKind + "]" } - TaintKind getItem() { result = itemKind } + TaintKind getItem() { result = itemKind } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - exists(BinaryExprNode mod | - mod = tonode and - mod.getOp() instanceof Mod and - mod.getAnOperand() = fromnode and - result = this.getItem() and - result.getType() = ObjectInternal::builtin("str") - ) - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + exists(BinaryExprNode mod | + mod = tonode and + mod.getOp() instanceof Mod and + mod.getAnOperand() = fromnode and + result = this.getItem() and + result.getType() = ObjectInternal::builtin("str") + ) + } - override TaintKind getTaintOfMethodResult(string name) { - name = "pop" and result = this.getItem() - } + override TaintKind getTaintOfMethodResult(string name) { + name = "pop" and result = this.getItem() + } - override string repr() { result = "sequence of " + itemKind } + override string repr() { result = "sequence of " + itemKind } - override TaintKind getTaintForIteration() { result = itemKind } + override TaintKind getTaintForIteration() { result = itemKind } - override TaintKind getMember() { result = itemKind } + override TaintKind getMember() { result = itemKind } - override predicate flowFromMember(DataFlow::Node fromnode, DataFlow::Node tonode) { - sequence_construct(fromnode.asCfgNode(), tonode.asCfgNode()) - } + override predicate flowFromMember(DataFlow::Node fromnode, DataFlow::Node tonode) { + sequence_construct(fromnode.asCfgNode(), tonode.asCfgNode()) + } - override predicate flowToMember(DataFlow::Node fromnode, DataFlow::Node tonode) { - SequenceKind::itemFlowStep(fromnode.asCfgNode(), tonode.asCfgNode()) - } + override predicate flowToMember(DataFlow::Node fromnode, DataFlow::Node tonode) { + SequenceKind::itemFlowStep(fromnode.asCfgNode(), tonode.asCfgNode()) + } } module SequenceKind { - predicate flowStep(ControlFlowNode fromnode, ControlFlowNode tonode, string edgeLabel) { - tonode.(BinaryExprNode).getAnOperand() = fromnode and edgeLabel = "binary operation" - or - Implementation::copyCall(fromnode, tonode) and - edgeLabel = "dict copy" - or - sequence_call(fromnode, tonode) and edgeLabel = "sequence construction" - or - subscript_slice(fromnode, tonode) and edgeLabel = "slicing" - } + predicate flowStep(ControlFlowNode fromnode, ControlFlowNode tonode, string edgeLabel) { + tonode.(BinaryExprNode).getAnOperand() = fromnode and edgeLabel = "binary operation" + or + Implementation::copyCall(fromnode, tonode) and + edgeLabel = "dict copy" + or + sequence_call(fromnode, tonode) and edgeLabel = "sequence construction" + or + subscript_slice(fromnode, tonode) and edgeLabel = "slicing" + } - predicate itemFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - subscript_index(fromnode, tonode) - } + predicate itemFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + subscript_index(fromnode, tonode) + } } module DictKind { - predicate flowStep(ControlFlowNode fromnode, ControlFlowNode tonode, string edgeLabel) { - Implementation::copyCall(fromnode, tonode) and - edgeLabel = "dict copy" - or - tonode.(CallNode).getFunction().pointsTo(ObjectInternal::builtin("dict")) and - tonode.(CallNode).getArg(0) = fromnode and - edgeLabel = "dict() call" - } + predicate flowStep(ControlFlowNode fromnode, ControlFlowNode tonode, string edgeLabel) { + Implementation::copyCall(fromnode, tonode) and + edgeLabel = "dict copy" + or + tonode.(CallNode).getFunction().pointsTo(ObjectInternal::builtin("dict")) and + tonode.(CallNode).getArg(0) = fromnode and + edgeLabel = "dict() call" + } } /* Helper for sequence flow steps */ pragma[noinline] private predicate subscript_index(ControlFlowNode obj, SubscriptNode sub) { - sub.isLoad() and - sub.getObject() = obj and - not sub.getNode().getIndex() instanceof Slice + sub.isLoad() and + sub.getObject() = obj and + not sub.getNode().getIndex() instanceof Slice } pragma[noinline] private predicate subscript_slice(ControlFlowNode obj, SubscriptNode sub) { - sub.isLoad() and - sub.getObject() = obj and - sub.getNode().getIndex() instanceof Slice + sub.isLoad() and + sub.getObject() = obj and + sub.getNode().getIndex() instanceof Slice } /** @@ -296,31 +296,31 @@ private predicate subscript_slice(ControlFlowNode obj, SubscriptNode sub) { * Typically a dict, but can include other mappings. */ class DictKind extends CollectionKind { - TaintKind valueKind; + TaintKind valueKind; - DictKind() { this = "{" + valueKind + "}" } + DictKind() { this = "{" + valueKind + "}" } - TaintKind getValue() { result = valueKind } + TaintKind getValue() { result = valueKind } - override TaintKind getTaintOfMethodResult(string name) { - name = "get" and result = valueKind - or - name = "values" and result.(SequenceKind).getItem() = valueKind - or - name = "itervalues" and result.(SequenceKind).getItem() = valueKind - } + override TaintKind getTaintOfMethodResult(string name) { + name = "get" and result = valueKind + or + name = "values" and result.(SequenceKind).getItem() = valueKind + or + name = "itervalues" and result.(SequenceKind).getItem() = valueKind + } - override string repr() { result = "dict of " + valueKind } + override string repr() { result = "dict of " + valueKind } - override TaintKind getMember() { result = valueKind } + override TaintKind getMember() { result = valueKind } - override predicate flowFromMember(DataFlow::Node fromnode, DataFlow::Node tonode) { - dict_construct(fromnode.asCfgNode(), tonode.asCfgNode()) - } + override predicate flowFromMember(DataFlow::Node fromnode, DataFlow::Node tonode) { + dict_construct(fromnode.asCfgNode(), tonode.asCfgNode()) + } - override predicate flowToMember(DataFlow::Node fromnode, DataFlow::Node tonode) { - subscript_index(fromnode.asCfgNode(), tonode.asCfgNode()) - } + override predicate flowToMember(DataFlow::Node fromnode, DataFlow::Node tonode) { + subscript_index(fromnode.asCfgNode(), tonode.asCfgNode()) + } } /** @@ -330,23 +330,23 @@ class DictKind extends CollectionKind { * For example, a sanitizer for DB commands would not be safe to use for http responses. */ abstract class Sanitizer extends string { - bindingset[this] - Sanitizer() { any() } + bindingset[this] + Sanitizer() { any() } - /** Holds if `taint` cannot flow through `node`. */ - predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { none() } + /** Holds if `taint` cannot flow through `node`. */ + predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { none() } - /** Holds if `call` removes removes the `taint` */ - predicate sanitizingCall(TaintKind taint, FunctionObject callee) { none() } + /** Holds if `call` removes removes the `taint` */ + predicate sanitizingCall(TaintKind taint, FunctionObject callee) { none() } - /** Holds if `test` shows value to be untainted with `taint` */ - predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { none() } + /** Holds if `test` shows value to be untainted with `taint` */ + predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { none() } - /** Holds if `test` shows value to be untainted with `taint` */ - predicate sanitizingSingleEdge(TaintKind taint, SingleSuccessorGuard test) { none() } + /** Holds if `test` shows value to be untainted with `taint` */ + predicate sanitizingSingleEdge(TaintKind taint, SingleSuccessorGuard test) { none() } - /** Holds if `def` shows value to be untainted with `taint` */ - predicate sanitizingDefinition(TaintKind taint, EssaDefinition def) { none() } + /** Holds if `def` shows value to be untainted with `taint` */ + predicate sanitizingDefinition(TaintKind taint, EssaDefinition def) { none() } } /** @@ -355,65 +355,66 @@ abstract class Sanitizer extends string { * class to provide their own sources. */ abstract class TaintSource extends @py_flow_node { - /** Gets a textual representation of this element. */ - string toString() { result = "Taint source" } + /** Gets a textual representation of this element. */ + string toString() { result = "Taint source" } - /** - * Holds if `this` is a source of taint kind `kind` - * - * This must be overridden by subclasses to specify sources of taint. - * - * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. - */ - abstract predicate isSourceOf(TaintKind kind); + /** + * Holds if `this` is a source of taint kind `kind` + * + * This must be overridden by subclasses to specify sources of taint. + * + * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. + */ + abstract predicate isSourceOf(TaintKind kind); - /** - * Holds if `this` is a source of taint kind `kind` for the given context. - * Generally, this should not need to be overridden; overriding `isSourceOf(kind)` should be sufficient. - * - * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. - */ - predicate isSourceOf(TaintKind kind, TaintTrackingContext context) { - context.isTop() and this.isSourceOf(kind) - } + /** + * Holds if `this` is a source of taint kind `kind` for the given context. + * Generally, this should not need to be overridden; overriding `isSourceOf(kind)` should be sufficient. + * + * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. + */ + predicate isSourceOf(TaintKind kind, TaintTrackingContext context) { + context.isTop() and this.isSourceOf(kind) + } - Location getLocation() { result = this.(ControlFlowNode).getLocation() } + Location getLocation() { result = this.(ControlFlowNode).getLocation() } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } - /** Gets a TaintedNode for this taint source */ - TaintedNode getATaintNode() { - result.getCfgNode() = this and - this.isSourceOf(result.getTaintKind(), result.getContext()) and - result.getPath().noAttribute() - } + /** Gets a TaintedNode for this taint source */ + TaintedNode getATaintNode() { + result.getCfgNode() = this and + this.isSourceOf(result.getTaintKind(), result.getContext()) and + result.getPath().noAttribute() + } - /** Holds if taint can flow from this source to sink `sink` */ - final predicate flowsToSink(TaintKind srckind, TaintSink sink) { - exists(TaintedNode src, TaintedNode tsink | - src = this.getATaintNode() and - src.getTaintKind() = srckind and - src.flowsTo(tsink) and - this.isSourceOf(srckind, _) and - sink = tsink.getCfgNode() and - sink.sinks(tsink.getTaintKind()) and - tsink.getPath().noAttribute() and - tsink.isSink() - ) - } + /** Holds if taint can flow from this source to sink `sink` */ + final predicate flowsToSink(TaintKind srckind, TaintSink sink) { + exists(TaintedNode src, TaintedNode tsink | + src = this.getATaintNode() and + src.getTaintKind() = srckind and + src.flowsTo(tsink) and + this.isSourceOf(srckind, _) and + sink = tsink.getCfgNode() and + sink.sinks(tsink.getTaintKind()) and + tsink.getPath().noAttribute() and + tsink.isSink() + ) + } - /** Holds if taint can flow from this source to taint sink `sink` */ - final predicate flowsToSink(TaintSink sink) { this.flowsToSink(_, sink) } + /** Holds if taint can flow from this source to taint sink `sink` */ + final predicate flowsToSink(TaintSink sink) { this.flowsToSink(_, sink) } } /** @@ -423,50 +424,50 @@ abstract class TaintSource extends @py_flow_node { * class to provide their own sources on the ESSA graph. */ abstract class TaintedDefinition extends EssaNodeDefinition { - /** - * Holds if `this` is a source of taint kind `kind` - * - * This should be overridden by subclasses to specify sources of taint. - * - * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. - */ - abstract predicate isSourceOf(TaintKind kind); + /** + * Holds if `this` is a source of taint kind `kind` + * + * This should be overridden by subclasses to specify sources of taint. + * + * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. + */ + abstract predicate isSourceOf(TaintKind kind); - /** - * Holds if `this` is a source of taint kind `kind` for the given context. - * Generally, this should not need to be overridden; overriding `isSourceOf(kind)` should be sufficient. - * - * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. - */ - predicate isSourceOf(TaintKind kind, TaintTrackingContext context) { - context.isTop() and this.isSourceOf(kind) - } + /** + * Holds if `this` is a source of taint kind `kind` for the given context. + * Generally, this should not need to be overridden; overriding `isSourceOf(kind)` should be sufficient. + * + * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. + */ + predicate isSourceOf(TaintKind kind, TaintTrackingContext context) { + context.isTop() and this.isSourceOf(kind) + } } private class DictUpdate extends DataFlowExtension::DataFlowNode { - MethodCallsiteRefinement call; + MethodCallsiteRefinement call; - DictUpdate() { - exists(CallNode c | c = call.getCall() | - c.getFunction().(AttrNode).getName() = "update" and - c.getArg(0) = this - ) - } + DictUpdate() { + exists(CallNode c | c = call.getCall() | + c.getFunction().(AttrNode).getName() = "update" and + c.getArg(0) = this + ) + } - override EssaVariable getASuccessorVariable() { call.getVariable() = result } + override EssaVariable getASuccessorVariable() { call.getVariable() = result } } private class SequenceExtends extends DataFlowExtension::DataFlowNode { - MethodCallsiteRefinement call; + MethodCallsiteRefinement call; - SequenceExtends() { - exists(CallNode c | c = call.getCall() | - c.getFunction().(AttrNode).getName() = "extend" and - c.getArg(0) = this - ) - } + SequenceExtends() { + exists(CallNode c | c = call.getCall() | + c.getFunction().(AttrNode).getName() = "extend" and + c.getArg(0) = this + ) + } - override EssaVariable getASuccessorVariable() { call.getVariable() = result } + override EssaVariable getASuccessorVariable() { call.getVariable() = result } } /** @@ -479,30 +480,31 @@ private class SequenceExtends extends DataFlowExtension::DataFlowNode { * class to provide their own sink nodes. */ abstract class TaintSink extends @py_flow_node { - /** Gets a textual representation of this element. */ - string toString() { result = "Taint sink" } + /** Gets a textual representation of this element. */ + string toString() { result = "Taint sink" } - /** - * Holds if `this` "sinks" taint kind `kind` - * Typically this means that `this` is vulnerable to taint kind `kind`. - * - * This must be overridden by subclasses to specify vulnerabilities or other sinks of taint. - */ - abstract predicate sinks(TaintKind taint); + /** + * Holds if `this` "sinks" taint kind `kind` + * Typically this means that `this` is vulnerable to taint kind `kind`. + * + * This must be overridden by subclasses to specify vulnerabilities or other sinks of taint. + */ + abstract predicate sinks(TaintKind taint); - Location getLocation() { result = this.(ControlFlowNode).getLocation() } + Location getLocation() { result = this.(ControlFlowNode).getLocation() } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } } /** @@ -511,87 +513,87 @@ abstract class TaintSink extends @py_flow_node { * data-flow machinery. */ module DataFlowExtension { - /** A control flow node that modifies the basic data-flow. */ - abstract class DataFlowNode extends @py_flow_node { - /** Gets a textual representation of this element. */ - string toString() { result = "Dataflow extension node" } + /** A control flow node that modifies the basic data-flow. */ + abstract class DataFlowNode extends @py_flow_node { + /** Gets a textual representation of this element. */ + string toString() { result = "Dataflow extension node" } - /** - * Gets a successor node for data-flow. - * Data (all forms) is assumed to flow from `this` to `result` - */ - ControlFlowNode getASuccessorNode() { none() } + /** + * Gets a successor node for data-flow. + * Data (all forms) is assumed to flow from `this` to `result` + */ + ControlFlowNode getASuccessorNode() { none() } - /** - * Gets a successor variable for data-flow. - * Data (all forms) is assumed to flow from `this` to `result`. - * Note: This is an unlikely form of flow. See `DataFlowVariable.getASuccessorVariable()` - */ - EssaVariable getASuccessorVariable() { none() } + /** + * Gets a successor variable for data-flow. + * Data (all forms) is assumed to flow from `this` to `result`. + * Note: This is an unlikely form of flow. See `DataFlowVariable.getASuccessorVariable()` + */ + EssaVariable getASuccessorVariable() { none() } - /** - * Holds if data cannot flow from `this` to `succ`, - * even though it would normally do so. - */ - predicate prunedSuccessor(ControlFlowNode succ) { none() } + /** + * Holds if data cannot flow from `this` to `succ`, + * even though it would normally do so. + */ + predicate prunedSuccessor(ControlFlowNode succ) { none() } - /** - * Gets a successor node, where the successor node will be tainted with `tokind` - * when `this` is tainted with `fromkind`. - * Extensions to `DataFlowNode` should override this to provide additional taint steps. - */ - ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { none() } + /** + * Gets a successor node, where the successor node will be tainted with `tokind` + * when `this` is tainted with `fromkind`. + * Extensions to `DataFlowNode` should override this to provide additional taint steps. + */ + ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { none() } - /** - * Gets a successor node for data-flow with a change of context from callee to caller - * (going *up* the call-stack) across call-site `call`. - * Data (all forms) is assumed to flow from `this` to `result` - * Extensions to `DataFlowNode` should override this to provide additional taint steps. - */ - ControlFlowNode getAReturnSuccessorNode(CallNode call) { none() } + /** + * Gets a successor node for data-flow with a change of context from callee to caller + * (going *up* the call-stack) across call-site `call`. + * Data (all forms) is assumed to flow from `this` to `result` + * Extensions to `DataFlowNode` should override this to provide additional taint steps. + */ + ControlFlowNode getAReturnSuccessorNode(CallNode call) { none() } - /** - * Gets a successor node for data-flow with a change of context from caller to callee - * (going *down* the call-stack) across call-site `call`. - * Data (all forms) is assumed to flow from `this` to `result` - * Extensions to `DataFlowNode` should override this to provide additional taint steps. - */ - ControlFlowNode getACalleeSuccessorNode(CallNode call) { none() } - } + /** + * Gets a successor node for data-flow with a change of context from caller to callee + * (going *down* the call-stack) across call-site `call`. + * Data (all forms) is assumed to flow from `this` to `result` + * Extensions to `DataFlowNode` should override this to provide additional taint steps. + */ + ControlFlowNode getACalleeSuccessorNode(CallNode call) { none() } + } - /** Data flow variable that modifies the basic data-flow. */ - class DataFlowVariable extends EssaVariable { - /** - * Gets a successor node for data-flow. - * Data (all forms) is assumed to flow from `this` to `result` - * Note: This is an unlikely form of flow. See `DataFlowNode.getASuccessorNode()` - */ - ControlFlowNode getASuccessorNode() { none() } + /** Data flow variable that modifies the basic data-flow. */ + class DataFlowVariable extends EssaVariable { + /** + * Gets a successor node for data-flow. + * Data (all forms) is assumed to flow from `this` to `result` + * Note: This is an unlikely form of flow. See `DataFlowNode.getASuccessorNode()` + */ + ControlFlowNode getASuccessorNode() { none() } - /** - * Gets a successor variable for data-flow. - * Data (all forms) is assumed to flow from `this` to `result`. - */ - EssaVariable getASuccessorVariable() { none() } + /** + * Gets a successor variable for data-flow. + * Data (all forms) is assumed to flow from `this` to `result`. + */ + EssaVariable getASuccessorVariable() { none() } - /** - * Holds if data cannot flow from `this` to `succ`, - * even though it would normally do so. - */ - predicate prunedSuccessor(EssaVariable succ) { none() } - } + /** + * Holds if data cannot flow from `this` to `succ`, + * even though it would normally do so. + */ + predicate prunedSuccessor(EssaVariable succ) { none() } + } } class TaintedPathSource extends TaintTrackingNode { - TaintedPathSource() { this.isSource() } + TaintedPathSource() { this.isSource() } - DataFlow::Node getSource() { result = this.getNode() } + DataFlow::Node getSource() { result = this.getNode() } } class TaintedPathSink extends TaintTrackingNode { - TaintedPathSink() { this.isSink() } + TaintedPathSink() { this.isSink() } - DataFlow::Node getSink() { result = this.getNode() } + DataFlow::Node getSink() { result = this.getNode() } } /* Backwards compatible name */ @@ -605,145 +607,145 @@ private import semmle.python.pointsto.PointsTo * the other language implementations. */ module DataFlow { - /** - * Generic taint kind, source and sink classes for convenience and - * compatibility with other language libraries - */ - class Extension = DataFlowExtension::DataFlowNode; + /** + * Generic taint kind, source and sink classes for convenience and + * compatibility with other language libraries + */ + class Extension = DataFlowExtension::DataFlowNode; - abstract deprecated class Configuration extends string { - bindingset[this] - Configuration() { this = this } + abstract deprecated class Configuration extends string { + bindingset[this] + Configuration() { this = this } - abstract predicate isSource(ControlFlowNode source); + abstract predicate isSource(ControlFlowNode source); - abstract predicate isSink(ControlFlowNode sink); + abstract predicate isSink(ControlFlowNode sink); - private predicate hasFlowPath(TaintedNode source, TaintedNode sink) { - source.getConfiguration() = this and - this.isSource(source.getCfgNode()) and - this.isSink(sink.getCfgNode()) and - source.flowsTo(sink) - } - - predicate hasFlow(ControlFlowNode source, ControlFlowNode sink) { - exists(TaintedNode psource, TaintedNode psink | - psource.getCfgNode() = source and - psink.getCfgNode() = sink and - this.isSource(source) and - this.isSink(sink) and - this.hasFlowPath(psource, psink) - ) - } + private predicate hasFlowPath(TaintedNode source, TaintedNode sink) { + source.getConfiguration() = this and + this.isSource(source.getCfgNode()) and + this.isSink(sink.getCfgNode()) and + source.flowsTo(sink) } - deprecated private class ConfigurationAdapter extends TaintTracking::Configuration { - ConfigurationAdapter() { this instanceof Configuration } + predicate hasFlow(ControlFlowNode source, ControlFlowNode sink) { + exists(TaintedNode psource, TaintedNode psink | + psource.getCfgNode() = source and + psink.getCfgNode() = sink and + this.isSource(source) and + this.isSink(sink) and + this.hasFlowPath(psource, psink) + ) + } + } - override predicate isSource(DataFlow::Node node, TaintKind kind) { - this.(Configuration).isSource(node.asCfgNode()) and - kind instanceof DataFlowType - } + deprecated private class ConfigurationAdapter extends TaintTracking::Configuration { + ConfigurationAdapter() { this instanceof Configuration } - override predicate isSink(DataFlow::Node node, TaintKind kind) { - this.(Configuration).isSink(node.asCfgNode()) and - kind instanceof DataFlowType - } + override predicate isSource(DataFlow::Node node, TaintKind kind) { + this.(Configuration).isSource(node.asCfgNode()) and + kind instanceof DataFlowType } - private newtype TDataFlowNode = - TEssaNode(EssaVariable var) or - TCfgNode(ControlFlowNode node) + override predicate isSink(DataFlow::Node node, TaintKind kind) { + this.(Configuration).isSink(node.asCfgNode()) and + kind instanceof DataFlowType + } + } - abstract class Node extends TDataFlowNode { - abstract ControlFlowNode asCfgNode(); + private newtype TDataFlowNode = + TEssaNode(EssaVariable var) or + TCfgNode(ControlFlowNode node) - abstract EssaVariable asVariable(); + abstract class Node extends TDataFlowNode { + abstract ControlFlowNode asCfgNode(); - /** Gets a textual representation of this element. */ - abstract string toString(); + abstract EssaVariable asVariable(); - abstract Scope getScope(); + /** Gets a textual representation of this element. */ + abstract string toString(); - abstract BasicBlock getBasicBlock(); + abstract Scope getScope(); - abstract Location getLocation(); + abstract BasicBlock getBasicBlock(); - AstNode asAstNode() { result = this.asCfgNode().getNode() } + abstract Location getLocation(); - /** For backwards compatibility -- Use asAstNode() instead */ - deprecated AstNode getNode() { result = this.asAstNode() } + AstNode asAstNode() { result = this.asCfgNode().getNode() } + + /** For backwards compatibility -- Use asAstNode() instead */ + deprecated AstNode getNode() { result = this.asAstNode() } + } + + class CfgNode extends Node, TCfgNode { + override ControlFlowNode asCfgNode() { this = TCfgNode(result) } + + override EssaVariable asVariable() { none() } + + /** Gets a textual representation of this element. */ + override string toString() { result = this.asAstNode().toString() } + + override Scope getScope() { result = this.asCfgNode().getScope() } + + override BasicBlock getBasicBlock() { result = this.asCfgNode().getBasicBlock() } + + override Location getLocation() { result = this.asCfgNode().getLocation() } + } + + class EssaNode extends Node, TEssaNode { + override ControlFlowNode asCfgNode() { none() } + + override EssaVariable asVariable() { this = TEssaNode(result) } + + /** Gets a textual representation of this element. */ + override string toString() { result = this.asVariable().toString() } + + override Scope getScope() { result = this.asVariable().getScope() } + + override BasicBlock getBasicBlock() { + result = this.asVariable().getDefinition().getBasicBlock() } - class CfgNode extends Node, TCfgNode { - override ControlFlowNode asCfgNode() { this = TCfgNode(result) } - - override EssaVariable asVariable() { none() } - - /** Gets a textual representation of this element. */ - override string toString() { result = this.asAstNode().toString() } - - override Scope getScope() { result = this.asCfgNode().getScope() } - - override BasicBlock getBasicBlock() { result = this.asCfgNode().getBasicBlock() } - - override Location getLocation() { result = this.asCfgNode().getLocation() } - } - - class EssaNode extends Node, TEssaNode { - override ControlFlowNode asCfgNode() { none() } - - override EssaVariable asVariable() { this = TEssaNode(result) } - - /** Gets a textual representation of this element. */ - override string toString() { result = this.asVariable().toString() } - - override Scope getScope() { result = this.asVariable().getScope() } - - override BasicBlock getBasicBlock() { - result = this.asVariable().getDefinition().getBasicBlock() - } - - override Location getLocation() { result = this.asVariable().getDefinition().getLocation() } - } + override Location getLocation() { result = this.asVariable().getDefinition().getLocation() } + } } deprecated private class DataFlowType extends TaintKind { - DataFlowType() { - this = "Data flow" and - exists(DataFlow::Configuration c) - } + DataFlowType() { + this = "Data flow" and + exists(DataFlow::Configuration c) + } } pragma[noinline] private predicate dict_construct(ControlFlowNode itemnode, ControlFlowNode dictnode) { - dictnode.(DictNode).getAValue() = itemnode - or - dictnode.(CallNode).getFunction().pointsTo(ObjectInternal::builtin("dict")) and - dictnode.(CallNode).getArgByName(_) = itemnode + dictnode.(DictNode).getAValue() = itemnode + or + dictnode.(CallNode).getFunction().pointsTo(ObjectInternal::builtin("dict")) and + dictnode.(CallNode).getArgByName(_) = itemnode } pragma[noinline] private predicate sequence_construct(ControlFlowNode itemnode, ControlFlowNode seqnode) { - seqnode.isLoad() and - ( - seqnode.(ListNode).getElement(_) = itemnode - or - seqnode.(TupleNode).getElement(_) = itemnode - or - seqnode.(SetNode).getAnElement() = itemnode - ) + seqnode.isLoad() and + ( + seqnode.(ListNode).getElement(_) = itemnode + or + seqnode.(TupleNode).getElement(_) = itemnode + or + seqnode.(SetNode).getAnElement() = itemnode + ) } /* A call to construct a sequence from a sequence or iterator*/ pragma[noinline] private predicate sequence_call(ControlFlowNode fromnode, CallNode tonode) { - tonode.getArg(0) = fromnode and - exists(ControlFlowNode cls | cls = tonode.getFunction() | - cls.pointsTo(ObjectInternal::builtin("list")) - or - cls.pointsTo(ObjectInternal::builtin("tuple")) - or - cls.pointsTo(ObjectInternal::builtin("set")) - ) + tonode.getArg(0) = fromnode and + exists(ControlFlowNode cls | cls = tonode.getFunction() | + cls.pointsTo(ObjectInternal::builtin("list")) + or + cls.pointsTo(ObjectInternal::builtin("tuple")) + or + cls.pointsTo(ObjectInternal::builtin("set")) + ) } diff --git a/python/ql/src/semmle/python/dependencies/Dependencies.qll b/python/ql/src/semmle/python/dependencies/Dependencies.qll index 6d2052f0dfb..3c018a13837 100644 --- a/python/ql/src/semmle/python/dependencies/Dependencies.qll +++ b/python/ql/src/semmle/python/dependencies/Dependencies.qll @@ -2,72 +2,72 @@ import python import semmle.python.dependencies.DependencyKind private predicate importDependency(Object target, AstNode source) { - source.getScope() != target.getOrigin() and - /* Imports of own module are ignored */ - ( - exists(ModuleObject importee, ImportingStmt imp_stmt | - source = imp_stmt and - importee = target - | - exists(ImportMember im | imp_stmt.contains(im) | - importee.importedAs(im.getImportedModuleName()) - ) - or - exists(ImportExpr im | imp_stmt.contains(im) | - importee.importedAs(im.getImportedModuleName()) - ) - or - exists(ModuleObject mod | - importDependency(mod, source) and - target = mod.getPackage+() - ) - ) - or - /* from m import name, where m.name is not a submodule */ - exists(PythonModuleObject importee, ImportingStmt imp_stmt | source = imp_stmt | - exists(ImportMember im | imp_stmt.contains(im) | - importee.importedAs(im.getModule().(ImportExpr).getImportedModuleName()) and - defn_of_module_attribute(target, importee.getModule(), im.getName()) - ) - ) + source.getScope() != target.getOrigin() and + /* Imports of own module are ignored */ + ( + exists(ModuleObject importee, ImportingStmt imp_stmt | + source = imp_stmt and + importee = target + | + exists(ImportMember im | imp_stmt.contains(im) | + importee.importedAs(im.getImportedModuleName()) + ) + or + exists(ImportExpr im | imp_stmt.contains(im) | + importee.importedAs(im.getImportedModuleName()) + ) + or + exists(ModuleObject mod | + importDependency(mod, source) and + target = mod.getPackage+() + ) ) + or + /* from m import name, where m.name is not a submodule */ + exists(PythonModuleObject importee, ImportingStmt imp_stmt | source = imp_stmt | + exists(ImportMember im | imp_stmt.contains(im) | + importee.importedAs(im.getModule().(ImportExpr).getImportedModuleName()) and + defn_of_module_attribute(target, importee.getModule(), im.getName()) + ) + ) + ) } class PythonImport extends DependencyKind { - PythonImport() { this = "import" } + PythonImport() { this = "import" } - override predicate isADependency(AstNode source, Object target) { - this = this and - importDependency(target, source) - } + override predicate isADependency(AstNode source, Object target) { + this = this and + importDependency(target, source) + } } private predicate interesting(Object target) { - target.(ControlFlowNode).getNode() instanceof Scope - or - target instanceof FunctionObject - or - target instanceof ClassObject - or - target instanceof ModuleObject + target.(ControlFlowNode).getNode() instanceof Scope + or + target instanceof FunctionObject + or + target instanceof ClassObject + or + target instanceof ModuleObject } class PythonUse extends DependencyKind { - PythonUse() { this = "use" } + PythonUse() { this = "use" } - override predicate isADependency(AstNode source, Object target) { - interesting(target) and - this = this and - source != target.(ControlFlowNode).getNode() and - exists(ControlFlowNode use, Object obj | - use.getNode() = source and - use.refersTo(obj) and - use.isLoad() - | - interesting(obj) and target = obj - ) and - not has_more_specific_dependency_source(source) - } + override predicate isADependency(AstNode source, Object target) { + interesting(target) and + this = this and + source != target.(ControlFlowNode).getNode() and + exists(ControlFlowNode use, Object obj | + use.getNode() = source and + use.refersTo(obj) and + use.isLoad() + | + interesting(obj) and target = obj + ) and + not has_more_specific_dependency_source(source) + } } /** @@ -76,66 +76,66 @@ class PythonUse extends DependencyKind { * don't make pack.mod depend on the module 'pack.mod' */ private predicate has_more_specific_dependency_source(Expr e) { - exists(Attribute member | member.getObject() = e | - attribute_access_dependency(_, member) - or - has_more_specific_dependency_source(member) - ) + exists(Attribute member | member.getObject() = e | + attribute_access_dependency(_, member) + or + has_more_specific_dependency_source(member) + ) } class PythonInheritance extends DependencyKind { - PythonInheritance() { this = "inheritance" } + PythonInheritance() { this = "inheritance" } - override predicate isADependency(AstNode source, Object target) { - this = this and - exists(ClassObject cls | source = cls.getOrigin() | - target = cls.getASuperType() - or - target = cls.getAnInferredType() - ) - } + override predicate isADependency(AstNode source, Object target) { + this = this and + exists(ClassObject cls | source = cls.getOrigin() | + target = cls.getASuperType() + or + target = cls.getAnInferredType() + ) + } } class PythonAttribute extends DependencyKind { - PythonAttribute() { this = "attribute" } + PythonAttribute() { this = "attribute" } - override predicate isADependency(AstNode source, Object target) { - this = this and - attribute_access_dependency(target, source) - } + override predicate isADependency(AstNode source, Object target) { + this = this and + attribute_access_dependency(target, source) + } } private predicate attribute_access_dependency(Object target, AstNode source) { - exists(Scope s, string name | - use_of_attribute(source, s, name) and - defn_of_attribute(target, s, name) - ) + exists(Scope s, string name | + use_of_attribute(source, s, name) and + defn_of_attribute(target, s, name) + ) } private predicate use_of_attribute(Attribute attr, Scope s, string name) { - exists(AttrNode cfg | cfg.isLoad() and cfg.getNode() = attr | - exists(Object obj | cfg.getObject(name).refersTo(obj) | - s = obj.(PythonModuleObject).getModule() or - s = obj.(ClassObject).getPyClass() - ) - or - exists(ClassObject cls | cfg.getObject(name).refersTo(_, cls, _) | s = cls.getPyClass()) + exists(AttrNode cfg | cfg.isLoad() and cfg.getNode() = attr | + exists(Object obj | cfg.getObject(name).refersTo(obj) | + s = obj.(PythonModuleObject).getModule() or + s = obj.(ClassObject).getPyClass() ) or - exists(SelfAttributeRead sar | sar = attr | - sar.getClass() = s and - sar.getName() = name - ) + exists(ClassObject cls | cfg.getObject(name).refersTo(_, cls, _) | s = cls.getPyClass()) + ) + or + exists(SelfAttributeRead sar | sar = attr | + sar.getClass() = s and + sar.getName() = name + ) } private predicate defn_of_attribute(Object target, Scope s, string name) { - exists(Assign asgn | target.(ControlFlowNode).getNode() = asgn | - defn_of_instance_attribute(asgn, s, name) - or - defn_of_class_attribute(asgn, s, name) - ) + exists(Assign asgn | target.(ControlFlowNode).getNode() = asgn | + defn_of_instance_attribute(asgn, s, name) or - defn_of_module_attribute(target, s, name) + defn_of_class_attribute(asgn, s, name) + ) + or + defn_of_module_attribute(target, s, name) } /* @@ -145,30 +145,30 @@ private predicate defn_of_attribute(Object target, Scope s, string name) { */ private predicate defn_of_instance_attribute(Assign asgn, Class c, string name) { - exists(SelfAttributeStore sas | asgn.getATarget() = sas | - sas.getClass() = c and - sas.getName() = name and - not exists(SelfAttributeStore in_init | - not sas.getScope().(Function).isInitMethod() and - not sas = in_init and - in_init.getClass() = c and - in_init.getName() = name and - in_init.getScope().(Function).isInitMethod() - ) + exists(SelfAttributeStore sas | asgn.getATarget() = sas | + sas.getClass() = c and + sas.getName() = name and + not exists(SelfAttributeStore in_init | + not sas.getScope().(Function).isInitMethod() and + not sas = in_init and + in_init.getClass() = c and + in_init.getName() = name and + in_init.getScope().(Function).isInitMethod() ) + ) } /* Whether asgn defines an attribute of a class */ private predicate defn_of_class_attribute(Assign asgn, Class c, string name) { - asgn.getScope() = c and - asgn.getATarget().(Name).getId() = name + asgn.getScope() = c and + asgn.getATarget().(Name).getId() = name } /* Holds if `value` is a value assigned to the `name`d attribute of module `m`. */ private predicate defn_of_module_attribute(ControlFlowNode value, Module m, string name) { - exists(DefinitionNode def | - def.getScope() = m and - def.getValue() = value and - def.(NameNode).getId() = name - ) + exists(DefinitionNode def | + def.getScope() = m and + def.getValue() = value and + def.(NameNode).getId() = name + ) } diff --git a/python/ql/src/semmle/python/dependencies/DependencyKind.qll b/python/ql/src/semmle/python/dependencies/DependencyKind.qll index ae91593d251..2e4fab1af0b 100644 --- a/python/ql/src/semmle/python/dependencies/DependencyKind.qll +++ b/python/ql/src/semmle/python/dependencies/DependencyKind.qll @@ -13,15 +13,15 @@ import semmle.python.dependencies.Dependencies */ abstract class DependencyKind extends string { - bindingset[this] - DependencyKind() { this = this } + bindingset[this] + DependencyKind() { this = this } - /* Tech inventory interface */ - /** - * Identify dependencies associated with this category. - *

- * The source element is the source of the dependency. - *

- */ - abstract predicate isADependency(AstNode source, Object target); + /* Tech inventory interface */ + /** + * Identify dependencies associated with this category. + *

+ * The source element is the source of the dependency. + *

+ */ + abstract predicate isADependency(AstNode source, Object target); } diff --git a/python/ql/src/semmle/python/dependencies/TechInventory.qll b/python/ql/src/semmle/python/dependencies/TechInventory.qll index 43a7cf55ec2..0c48b5ecfbb 100644 --- a/python/ql/src/semmle/python/dependencies/TechInventory.qll +++ b/python/ql/src/semmle/python/dependencies/TechInventory.qll @@ -7,23 +7,23 @@ import semmle.python.dependencies.DependencyKind * /path/to/file.py<|>package-name-and-version */ string munge(File sourceFile, ExternalPackage package) { - result = - "/" + sourceFile.getRelativePath() + "<|>" + package.getName() + "<|>" + package.getVersion() - or - not exists(package.getVersion()) and - result = "/" + sourceFile.getRelativePath() + "<|>" + package.getName() + "<|>unknown" + result = + "/" + sourceFile.getRelativePath() + "<|>" + package.getName() + "<|>" + package.getVersion() + or + not exists(package.getVersion()) and + result = "/" + sourceFile.getRelativePath() + "<|>" + package.getName() + "<|>unknown" } abstract class ExternalPackage extends Object { - ExternalPackage() { this instanceof ModuleObject } + ExternalPackage() { this instanceof ModuleObject } - abstract string getName(); + abstract string getName(); - abstract string getVersion(); + abstract string getVersion(); - Object getAttribute(string name) { result = this.(ModuleObject).attr(name) } + Object getAttribute(string name) { result = this.(ModuleObject).attr(name) } - PackageObject getPackage() { result = this.(ModuleObject).getPackage() } + PackageObject getPackage() { result = this.(ModuleObject).getPackage() } } bindingset[text] @@ -31,80 +31,80 @@ private predicate is_version(string text) { text.regexpMatch("\\d+\\.\\d+(\\.\\d bindingset[v] private string version_format(float v) { - exists(int i, int f | i = (v + 0.05).floor() and f = ((v + 0.05 - i) * 10).floor() | - result = i + "." + f - ) + exists(int i, int f | i = (v + 0.05).floor() and f = ((v + 0.05 - i) * 10).floor() | + result = i + "." + f + ) } class DistPackage extends ExternalPackage { - DistPackage() { - exists(Folder parent | - parent = this.(ModuleObject).getPath().getParent() and - parent.isImportRoot() and - /* Not in standard library */ - not parent.isStdLibRoot() and - /* Not in the source */ - not exists(parent.getRelativePath()) - ) - } + DistPackage() { + exists(Folder parent | + parent = this.(ModuleObject).getPath().getParent() and + parent.isImportRoot() and + /* Not in standard library */ + not parent.isStdLibRoot() and + /* Not in the source */ + not exists(parent.getRelativePath()) + ) + } - /* - * We don't extract the meta-data for dependencies (yet), so make a best guess from the source - * https://www.python.org/dev/peps/pep-0396/ - */ + /* + * We don't extract the meta-data for dependencies (yet), so make a best guess from the source + * https://www.python.org/dev/peps/pep-0396/ + */ - private predicate possibleVersion(string version, int priority) { - exists(Object v | v = this.getAttribute("__version__") and priority = 3 | - version = v.(StringObject).getText() and is_version(version) - or - version = version_format(v.(NumericObject).floatValue()) - or - version = version_format(v.(NumericObject).intValue()) - ) - or - exists(SequenceObject tuple, NumericObject major, NumericObject minor, string base_version | - this.getAttribute("version_info") = tuple and - major = tuple.getInferredElement(0) and - minor = tuple.getInferredElement(1) and - base_version = major.intValue() + "." + minor.intValue() - | - version = base_version + "." + tuple.getBuiltinElement(2).(NumericObject).intValue() - or - not exists(tuple.getBuiltinElement(2)) and version = base_version - ) and - priority = 2 - or - exists(string v | v.toLowerCase() = "version" | - is_version(version) and - version = this.getAttribute(v).(StringObject).getText() - ) and - priority = 1 - } + private predicate possibleVersion(string version, int priority) { + exists(Object v | v = this.getAttribute("__version__") and priority = 3 | + version = v.(StringObject).getText() and is_version(version) + or + version = version_format(v.(NumericObject).floatValue()) + or + version = version_format(v.(NumericObject).intValue()) + ) + or + exists(SequenceObject tuple, NumericObject major, NumericObject minor, string base_version | + this.getAttribute("version_info") = tuple and + major = tuple.getInferredElement(0) and + minor = tuple.getInferredElement(1) and + base_version = major.intValue() + "." + minor.intValue() + | + version = base_version + "." + tuple.getBuiltinElement(2).(NumericObject).intValue() + or + not exists(tuple.getBuiltinElement(2)) and version = base_version + ) and + priority = 2 + or + exists(string v | v.toLowerCase() = "version" | + is_version(version) and + version = this.getAttribute(v).(StringObject).getText() + ) and + priority = 1 + } - override string getVersion() { - this.possibleVersion(result, max(int priority | this.possibleVersion(_, priority))) - } + override string getVersion() { + this.possibleVersion(result, max(int priority | this.possibleVersion(_, priority))) + } - override string getName() { result = this.(ModuleObject).getShortName() } + override string getName() { result = this.(ModuleObject).getShortName() } - predicate fromSource(Object src) { - exists(ModuleObject m | - m.getModule() = src.(ControlFlowNode).getEnclosingModule() or - src = m - | - m = this - or - m.getPackage+() = this and - not exists(DistPackage inter | - m.getPackage*() = inter and - inter.getPackage+() = this - ) - ) - } + predicate fromSource(Object src) { + exists(ModuleObject m | + m.getModule() = src.(ControlFlowNode).getEnclosingModule() or + src = m + | + m = this + or + m.getPackage+() = this and + not exists(DistPackage inter | + m.getPackage*() = inter and + inter.getPackage+() = this + ) + ) + } } predicate dependency(AstNode src, DistPackage package) { - exists(DependencyKind cat, Object target | cat.isADependency(src, target) | - package.fromSource(target) - ) + exists(DependencyKind cat, Object target | cat.isADependency(src, target) | + package.fromSource(target) + ) } diff --git a/python/ql/src/semmle/python/essa/Definitions.qll b/python/ql/src/semmle/python/essa/Definitions.qll index 9e203f36ec6..752ff9da329 100644 --- a/python/ql/src/semmle/python/essa/Definitions.qll +++ b/python/ql/src/semmle/python/essa/Definitions.qll @@ -14,345 +14,345 @@ import python /** A source language variable, to be converted into a set of SSA variables. */ abstract class SsaSourceVariable extends @py_variable { - SsaSourceVariable() { - /* Exclude `True`, `False` and `None` */ - not this.(Variable).getALoad() instanceof NameConstant - } + SsaSourceVariable() { + /* Exclude `True`, `False` and `None` */ + not this.(Variable).getALoad() instanceof NameConstant + } - /** Gets the name of this variable */ - string getName() { variable(this, _, result) } + /** Gets the name of this variable */ + string getName() { variable(this, _, result) } - Scope getScope() { variable(this, result, _) } + Scope getScope() { variable(this, result, _) } - /** Gets an implicit use of this variable */ - abstract ControlFlowNode getAnImplicitUse(); + /** Gets an implicit use of this variable */ + abstract ControlFlowNode getAnImplicitUse(); - abstract ControlFlowNode getScopeEntryDefinition(); + abstract ControlFlowNode getScopeEntryDefinition(); - /** Gets a textual representation of this element. */ - string toString() { result = "SsaSourceVariable " + this.getName() } + /** Gets a textual representation of this element. */ + string toString() { result = "SsaSourceVariable " + this.getName() } - /** Gets a use of this variable, either explicit or implicit. */ - ControlFlowNode getAUse() { - result = this.getASourceUse() - or - result = this.getAnImplicitUse() - or - /* - * `import *` is a definition of *all* variables, so must be a use as well, for pass-through - * once we have established that a variable is not redefined. - */ - - SsaSource::import_star_refinement(this, result, _) - or - /* - * Add a use at the end of scope for all variables to keep them live - * This is necessary for taint-tracking. - */ - - result = this.getScope().getANormalExit() - } - - /** Holds if `def` defines an ESSA variable for this variable. */ - predicate hasDefiningNode(ControlFlowNode def) { - def = this.getScopeEntryDefinition() - or - SsaSource::assignment_definition(this, def, _) - or - SsaSource::multi_assignment_definition(this, def, _, _) - or - SsaSource::deletion_definition(this, def) - or - SsaSource::init_module_submodule_defn(this, def) - or - SsaSource::parameter_definition(this, def) - or - SsaSource::exception_capture(this, def) - or - SsaSource::with_definition(this, def) - } - - /** - * Holds if `def` defines an ESSA variable for this variable in such a way - * that the new variable is a refinement in some way of the variable used at `use`. + /** Gets a use of this variable, either explicit or implicit. */ + ControlFlowNode getAUse() { + result = this.getASourceUse() + or + result = this.getAnImplicitUse() + or + /* + * `import *` is a definition of *all* variables, so must be a use as well, for pass-through + * once we have established that a variable is not redefined. */ - predicate hasRefinement(ControlFlowNode use, ControlFlowNode def) { - this.hasDefiningNode(_) and - /* Can't have a refinement unless there is a definition */ - refinement(this, use, def) - } - /** - * Holds if the edge `pred`->`succ` defines an ESSA variable for this variable in such a way - * that the new variable is a refinement in some way of the variable used at `use`. + SsaSource::import_star_refinement(this, result, _) + or + /* + * Add a use at the end of scope for all variables to keep them live + * This is necessary for taint-tracking. */ - predicate hasRefinementEdge(ControlFlowNode use, BasicBlock pred, BasicBlock succ) { - test_contains(pred.getLastNode(), use) and - use.(NameNode).uses(this) and - (pred.getAFalseSuccessor() = succ or pred.getATrueSuccessor() = succ) and - /* There is a store to this variable -- We don't want to refine builtins */ - exists(this.(Variable).getAStore()) - } - /** Gets a use of this variable that corresponds to an explicit use in the source. */ - ControlFlowNode getASourceUse() { - result.(NameNode).uses(this) - or - result.(NameNode).deletes(this) - } + result = this.getScope().getANormalExit() + } - abstract CallNode redefinedAtCallSite(); + /** Holds if `def` defines an ESSA variable for this variable. */ + predicate hasDefiningNode(ControlFlowNode def) { + def = this.getScopeEntryDefinition() + or + SsaSource::assignment_definition(this, def, _) + or + SsaSource::multi_assignment_definition(this, def, _, _) + or + SsaSource::deletion_definition(this, def) + or + SsaSource::init_module_submodule_defn(this, def) + or + SsaSource::parameter_definition(this, def) + or + SsaSource::exception_capture(this, def) + or + SsaSource::with_definition(this, def) + } + + /** + * Holds if `def` defines an ESSA variable for this variable in such a way + * that the new variable is a refinement in some way of the variable used at `use`. + */ + predicate hasRefinement(ControlFlowNode use, ControlFlowNode def) { + this.hasDefiningNode(_) and + /* Can't have a refinement unless there is a definition */ + refinement(this, use, def) + } + + /** + * Holds if the edge `pred`->`succ` defines an ESSA variable for this variable in such a way + * that the new variable is a refinement in some way of the variable used at `use`. + */ + predicate hasRefinementEdge(ControlFlowNode use, BasicBlock pred, BasicBlock succ) { + test_contains(pred.getLastNode(), use) and + use.(NameNode).uses(this) and + (pred.getAFalseSuccessor() = succ or pred.getATrueSuccessor() = succ) and + /* There is a store to this variable -- We don't want to refine builtins */ + exists(this.(Variable).getAStore()) + } + + /** Gets a use of this variable that corresponds to an explicit use in the source. */ + ControlFlowNode getASourceUse() { + result.(NameNode).uses(this) + or + result.(NameNode).deletes(this) + } + + abstract CallNode redefinedAtCallSite(); } private predicate refinement(SsaSourceVariable v, ControlFlowNode use, ControlFlowNode def) { - SsaSource::import_star_refinement(v, use, def) - or - SsaSource::attribute_assignment_refinement(v, use, def) - or - SsaSource::argument_refinement(v, use, def) - or - SsaSource::attribute_deletion_refinement(v, use, def) - or - SsaSource::test_refinement(v, use, def) - or - SsaSource::method_call_refinement(v, use, def) - or - def = v.redefinedAtCallSite() and def = use + SsaSource::import_star_refinement(v, use, def) + or + SsaSource::attribute_assignment_refinement(v, use, def) + or + SsaSource::argument_refinement(v, use, def) + or + SsaSource::attribute_deletion_refinement(v, use, def) + or + SsaSource::test_refinement(v, use, def) + or + SsaSource::method_call_refinement(v, use, def) + or + def = v.redefinedAtCallSite() and def = use } class FunctionLocalVariable extends SsaSourceVariable { - FunctionLocalVariable() { - this.(LocalVariable).getScope() instanceof Function and - not this instanceof NonLocalVariable - } + FunctionLocalVariable() { + this.(LocalVariable).getScope() instanceof Function and + not this instanceof NonLocalVariable + } - override ControlFlowNode getAnImplicitUse() { - this.(Variable).isSelf() and this.(Variable).getScope().getANormalExit() = result - } + override ControlFlowNode getAnImplicitUse() { + this.(Variable).isSelf() and this.(Variable).getScope().getANormalExit() = result + } - override ControlFlowNode getScopeEntryDefinition() { - exists(Scope s | s.getEntryNode() = result | - s = this.(LocalVariable).getScope() and - not this.(LocalVariable).isParameter() - or - s != this.(LocalVariable).getScope() and - s = this.(LocalVariable).getALoad().getScope() - ) - } + override ControlFlowNode getScopeEntryDefinition() { + exists(Scope s | s.getEntryNode() = result | + s = this.(LocalVariable).getScope() and + not this.(LocalVariable).isParameter() + or + s != this.(LocalVariable).getScope() and + s = this.(LocalVariable).getALoad().getScope() + ) + } - override CallNode redefinedAtCallSite() { none() } + override CallNode redefinedAtCallSite() { none() } } class NonLocalVariable extends SsaSourceVariable { - NonLocalVariable() { - exists(Function f | - this.(LocalVariable).getScope() = f and - this.(LocalVariable).getAStore().getScope() != f - ) - } + NonLocalVariable() { + exists(Function f | + this.(LocalVariable).getScope() = f and + this.(LocalVariable).getAStore().getScope() != f + ) + } - override ControlFlowNode getAnImplicitUse() { - result.(CallNode).getScope().getScope*() = this.(LocalVariable).getScope() - } + override ControlFlowNode getAnImplicitUse() { + result.(CallNode).getScope().getScope*() = this.(LocalVariable).getScope() + } - override ControlFlowNode getScopeEntryDefinition() { - exists(Function f | - f.getScope+() = this.(LocalVariable).getScope() and - f.getEntryNode() = result - ) - or - not this.(LocalVariable).isParameter() and - this.(LocalVariable).getScope().getEntryNode() = result - } + override ControlFlowNode getScopeEntryDefinition() { + exists(Function f | + f.getScope+() = this.(LocalVariable).getScope() and + f.getEntryNode() = result + ) + or + not this.(LocalVariable).isParameter() and + this.(LocalVariable).getScope().getEntryNode() = result + } - pragma[noinline] - Scope scope_as_local_variable() { result = this.(LocalVariable).getScope() } + pragma[noinline] + Scope scope_as_local_variable() { result = this.(LocalVariable).getScope() } - override CallNode redefinedAtCallSite() { - result.getScope().getScope*() = this.scope_as_local_variable() - } + override CallNode redefinedAtCallSite() { + result.getScope().getScope*() = this.scope_as_local_variable() + } } class ClassLocalVariable extends SsaSourceVariable { - ClassLocalVariable() { this.(LocalVariable).getScope() instanceof Class } + ClassLocalVariable() { this.(LocalVariable).getScope() instanceof Class } - override ControlFlowNode getAnImplicitUse() { none() } + override ControlFlowNode getAnImplicitUse() { none() } - override ControlFlowNode getScopeEntryDefinition() { - result = this.(LocalVariable).getScope().getEntryNode() - } + override ControlFlowNode getScopeEntryDefinition() { + result = this.(LocalVariable).getScope().getEntryNode() + } - override CallNode redefinedAtCallSite() { none() } + override CallNode redefinedAtCallSite() { none() } } class BuiltinVariable extends SsaSourceVariable { - BuiltinVariable() { - this instanceof GlobalVariable and - not exists(this.(Variable).getAStore()) and - not this.(Variable).getId() = "__name__" and - not this.(Variable).getId() = "__package__" and - not exists(ImportStar is | is.getScope() = this.(Variable).getScope()) - } + BuiltinVariable() { + this instanceof GlobalVariable and + not exists(this.(Variable).getAStore()) and + not this.(Variable).getId() = "__name__" and + not this.(Variable).getId() = "__package__" and + not exists(ImportStar is | is.getScope() = this.(Variable).getScope()) + } - override ControlFlowNode getAnImplicitUse() { none() } + override ControlFlowNode getAnImplicitUse() { none() } - override ControlFlowNode getScopeEntryDefinition() { none() } + override ControlFlowNode getScopeEntryDefinition() { none() } - override CallNode redefinedAtCallSite() { none() } + override CallNode redefinedAtCallSite() { none() } } class ModuleVariable extends SsaSourceVariable { - ModuleVariable() { - this instanceof GlobalVariable and - ( - exists(this.(Variable).getAStore()) - or - this.(Variable).getId() = "__name__" - or - this.(Variable).getId() = "__package__" - or - exists(ImportStar is | is.getScope() = this.(Variable).getScope()) - ) - } + ModuleVariable() { + this instanceof GlobalVariable and + ( + exists(this.(Variable).getAStore()) + or + this.(Variable).getId() = "__name__" + or + this.(Variable).getId() = "__package__" + or + exists(ImportStar is | is.getScope() = this.(Variable).getScope()) + ) + } - pragma[noinline] - CallNode global_variable_callnode() { result.getScope() = this.(GlobalVariable).getScope() } + pragma[noinline] + CallNode global_variable_callnode() { result.getScope() = this.(GlobalVariable).getScope() } - pragma[noinline] - ImportMemberNode global_variable_import() { - result.getScope() = this.(GlobalVariable).getScope() and - import_from_dot_in_init(result.(ImportMemberNode).getModule(this.getName())) - } + pragma[noinline] + ImportMemberNode global_variable_import() { + result.getScope() = this.(GlobalVariable).getScope() and + import_from_dot_in_init(result.(ImportMemberNode).getModule(this.getName())) + } - override ControlFlowNode getAnImplicitUse() { - result = global_variable_callnode() - or - result = global_variable_import() - or - exists(ImportTimeScope scope | scope.entryEdge(result, _) | - this = scope.getOuterVariable(_) or - this.(Variable).getAUse().getScope() = scope - ) - or - /* For implicit use of __metaclass__ when constructing class */ - exists(Class c | - class_with_global_metaclass(c, this) and - c.(ImportTimeScope).entryEdge(result, _) - ) - or - exists(ImportTimeScope s | - result = s.getANormalExit() and - this.(Variable).getScope() = s and - implicit_definition(this) - ) - } + override ControlFlowNode getAnImplicitUse() { + result = global_variable_callnode() + or + result = global_variable_import() + or + exists(ImportTimeScope scope | scope.entryEdge(result, _) | + this = scope.getOuterVariable(_) or + this.(Variable).getAUse().getScope() = scope + ) + or + /* For implicit use of __metaclass__ when constructing class */ + exists(Class c | + class_with_global_metaclass(c, this) and + c.(ImportTimeScope).entryEdge(result, _) + ) + or + exists(ImportTimeScope s | + result = s.getANormalExit() and + this.(Variable).getScope() = s and + implicit_definition(this) + ) + } - override ControlFlowNode getScopeEntryDefinition() { - exists(Scope s | s.getEntryNode() = result | - /* Module entry point */ - this.(GlobalVariable).getScope() = s - or - /* For implicit use of __metaclass__ when constructing class */ - class_with_global_metaclass(s, this) - or - /* Variable is used in scope */ - this.(GlobalVariable).getAUse().getScope() = s - ) - or - exists(ImportTimeScope scope | scope.entryEdge(_, result) | - this = scope.getOuterVariable(_) or - this.(Variable).getAUse().getScope() = scope - ) - } + override ControlFlowNode getScopeEntryDefinition() { + exists(Scope s | s.getEntryNode() = result | + /* Module entry point */ + this.(GlobalVariable).getScope() = s + or + /* For implicit use of __metaclass__ when constructing class */ + class_with_global_metaclass(s, this) + or + /* Variable is used in scope */ + this.(GlobalVariable).getAUse().getScope() = s + ) + or + exists(ImportTimeScope scope | scope.entryEdge(_, result) | + this = scope.getOuterVariable(_) or + this.(Variable).getAUse().getScope() = scope + ) + } - override CallNode redefinedAtCallSite() { none() } + override CallNode redefinedAtCallSite() { none() } } class NonEscapingGlobalVariable extends ModuleVariable { - NonEscapingGlobalVariable() { - this instanceof GlobalVariable and - exists(this.(Variable).getAStore()) and - not variable_or_attribute_defined_out_of_scope(this) - } + NonEscapingGlobalVariable() { + this instanceof GlobalVariable and + exists(this.(Variable).getAStore()) and + not variable_or_attribute_defined_out_of_scope(this) + } } class EscapingGlobalVariable extends ModuleVariable { - EscapingGlobalVariable() { - this instanceof GlobalVariable and - exists(this.(Variable).getAStore()) and - variable_or_attribute_defined_out_of_scope(this) - } + EscapingGlobalVariable() { + this instanceof GlobalVariable and + exists(this.(Variable).getAStore()) and + variable_or_attribute_defined_out_of_scope(this) + } - override ControlFlowNode getAnImplicitUse() { - result = ModuleVariable.super.getAnImplicitUse() - or - result.(CallNode).getScope().getScope+() = this.(GlobalVariable).getScope() - or - result = this.innerScope().getANormalExit() - } + override ControlFlowNode getAnImplicitUse() { + result = ModuleVariable.super.getAnImplicitUse() + or + result.(CallNode).getScope().getScope+() = this.(GlobalVariable).getScope() + or + result = this.innerScope().getANormalExit() + } - private Scope innerScope() { - result.getScope+() = this.(GlobalVariable).getScope() and - not result instanceof ImportTimeScope - } + private Scope innerScope() { + result.getScope+() = this.(GlobalVariable).getScope() and + not result instanceof ImportTimeScope + } - override ControlFlowNode getScopeEntryDefinition() { - result = ModuleVariable.super.getScopeEntryDefinition() - or - result = this.innerScope().getEntryNode() - } + override ControlFlowNode getScopeEntryDefinition() { + result = ModuleVariable.super.getScopeEntryDefinition() + or + result = this.innerScope().getEntryNode() + } - pragma[noinline] - Scope scope_as_global_variable() { result = this.(GlobalVariable).getScope() } + pragma[noinline] + Scope scope_as_global_variable() { result = this.(GlobalVariable).getScope() } - override CallNode redefinedAtCallSite() { - result.(CallNode).getScope().getScope*() = this.scope_as_global_variable() - } + override CallNode redefinedAtCallSite() { + result.(CallNode).getScope().getScope*() = this.scope_as_global_variable() + } } class EscapingAssignmentGlobalVariable extends EscapingGlobalVariable { - EscapingAssignmentGlobalVariable() { - exists(NameNode n | n.defines(this) and not n.getScope() = this.getScope()) - } + EscapingAssignmentGlobalVariable() { + exists(NameNode n | n.defines(this) and not n.getScope() = this.getScope()) + } } class SpecialSsaSourceVariable extends SsaSourceVariable { - SpecialSsaSourceVariable() { variable(this, _, "*") or variable(this, _, "$") } + SpecialSsaSourceVariable() { variable(this, _, "*") or variable(this, _, "$") } - override ControlFlowNode getAnImplicitUse() { - exists(ImportTimeScope s | result = s.getANormalExit() and this.getScope() = s) - } + override ControlFlowNode getAnImplicitUse() { + exists(ImportTimeScope s | result = s.getANormalExit() and this.getScope() = s) + } - override ControlFlowNode getScopeEntryDefinition() { - /* Module entry point */ - this.getScope().getEntryNode() = result - } + override ControlFlowNode getScopeEntryDefinition() { + /* Module entry point */ + this.getScope().getEntryNode() = result + } - pragma[noinline] - Scope scope_as_global_variable() { result = this.(GlobalVariable).getScope() } + pragma[noinline] + Scope scope_as_global_variable() { result = this.(GlobalVariable).getScope() } - override CallNode redefinedAtCallSite() { - result.(CallNode).getScope().getScope*() = this.scope_as_global_variable() - } + override CallNode redefinedAtCallSite() { + result.(CallNode).getScope().getScope*() = this.scope_as_global_variable() + } } /** Holds if this variable is implicitly defined */ private predicate implicit_definition(Variable v) { - v.getId() = "*" or - v.getId() = "$" or - exists(ImportStar is | is.getScope() = v.getScope()) + v.getId() = "*" or + v.getId() = "$" or + exists(ImportStar is | is.getScope() = v.getScope()) } private predicate variable_or_attribute_defined_out_of_scope(Variable v) { - exists(NameNode n | n.defines(v) and not n.getScope() = v.getScope()) - or - exists(AttrNode a | - a.isStore() and a.getObject() = v.getAUse() and not a.getScope() = v.getScope() - ) + exists(NameNode n | n.defines(v) and not n.getScope() = v.getScope()) + or + exists(AttrNode a | + a.isStore() and a.getObject() = v.getAUse() and not a.getScope() = v.getScope() + ) } private predicate class_with_global_metaclass(Class cls, GlobalVariable metaclass) { - metaclass.getId() = "__metaclass__" and - major_version() = 2 and - cls.getEnclosingModule() = metaclass.getScope() + metaclass.getId() = "__metaclass__" and + major_version() = 2 and + cls.getEnclosingModule() = metaclass.getScope() } diff --git a/python/ql/src/semmle/python/essa/Essa.qll b/python/ql/src/semmle/python/essa/Essa.qll index ef169d736a8..b44b784b48d 100644 --- a/python/ql/src/semmle/python/essa/Essa.qll +++ b/python/ql/src/semmle/python/essa/Essa.qll @@ -8,59 +8,59 @@ import semmle.python.essa.Definitions /** An (enhanced) SSA variable derived from `SsaSourceVariable`. */ class EssaVariable extends TEssaDefinition { - /** Gets the (unique) definition of this variable. */ - EssaDefinition getDefinition() { this = result } + /** Gets the (unique) definition of this variable. */ + EssaDefinition getDefinition() { this = result } - /** - * Gets a use of this variable, where a "use" is defined by - * `SsaSourceVariable.getAUse()`. - * Note that this differs from `EssaVariable.getASourceUse()`. - */ - ControlFlowNode getAUse() { result = this.getDefinition().getAUse() } + /** + * Gets a use of this variable, where a "use" is defined by + * `SsaSourceVariable.getAUse()`. + * Note that this differs from `EssaVariable.getASourceUse()`. + */ + ControlFlowNode getAUse() { result = this.getDefinition().getAUse() } - /** Gets the source variable from which this variable is derived. */ - SsaSourceVariable getSourceVariable() { result = this.getDefinition().getSourceVariable() } + /** Gets the source variable from which this variable is derived. */ + SsaSourceVariable getSourceVariable() { result = this.getDefinition().getSourceVariable() } - /** Gets the name of this variable. */ - string getName() { result = this.getSourceVariable().getName() } + /** Gets the name of this variable. */ + string getName() { result = this.getSourceVariable().getName() } - /** Gets a textual representation of this element. */ - string toString() { result = "SSA variable " + this.getName() } + /** Gets a textual representation of this element. */ + string toString() { result = "SSA variable " + this.getName() } - /** - * Gets a string representation of this variable. - * WARNING: The format of this may change and it may be very inefficient to compute. - * To used for debugging and testing only. - */ - string getRepresentation() { result = this.getSourceVariable().getName() + "_" + var_rank(this) } + /** + * Gets a string representation of this variable. + * WARNING: The format of this may change and it may be very inefficient to compute. + * To used for debugging and testing only. + */ + string getRepresentation() { result = this.getSourceVariable().getName() + "_" + var_rank(this) } - /** - * Gets a use of this variable, where a "use" is defined by - * `SsaSourceVariable.getASourceUse()`. - * Note that this differs from `EssaVariable.getAUse()`. - */ - ControlFlowNode getASourceUse() { - exists(SsaSourceVariable var | - result = use_for_var(var) and - result = var.getASourceUse() - ) - } + /** + * Gets a use of this variable, where a "use" is defined by + * `SsaSourceVariable.getASourceUse()`. + * Note that this differs from `EssaVariable.getAUse()`. + */ + ControlFlowNode getASourceUse() { + exists(SsaSourceVariable var | + result = use_for_var(var) and + result = var.getASourceUse() + ) + } - pragma[nomagic] - private ControlFlowNode use_for_var(SsaSourceVariable var) { - result = this.getAUse() and - var = this.getSourceVariable() - } + pragma[nomagic] + private ControlFlowNode use_for_var(SsaSourceVariable var) { + result = this.getAUse() and + var = this.getSourceVariable() + } - /** Gets the scope of this variable. */ - Scope getScope() { result = this.getDefinition().getScope() } + /** Gets the scope of this variable. */ + Scope getScope() { result = this.getDefinition().getScope() } - /** - * Holds if this the meta-variable for a scope. - * This is used to attach attributes for undeclared variables implicitly - * defined by `from ... import *` and the like. - */ - predicate isMetaVariable() { this.getName() = "$" } + /** + * Holds if this the meta-variable for a scope. + * This is used to attach attributes for undeclared variables implicitly + * defined by `from ... import *` and the like. + */ + predicate isMetaVariable() { this.getName() = "$" } } /* @@ -69,62 +69,62 @@ class EssaVariable extends TEssaDefinition { */ private int exception_handling(BasicBlock b) { - b.reachesExit() and result = 0 - or - not b.reachesExit() and result = 1 + b.reachesExit() and result = 0 + or + not b.reachesExit() and result = 1 } /* Helper for var_index. Come up with a (probably) unique string per location. */ pragma[noinline] private string location_string(EssaVariable v) { - exists(EssaDefinition def, BasicBlock b, int index, int line, int col | - def = v.getDefinition() and - ( - if b.getNode(0).isNormalExit() - then line = 100000 and col = 0 - else b.hasLocationInfo(_, line, col, _, _) - ) and - /* Add large numbers to values to prevent 1000 sorting before 99 */ - result = - (line + 100000) + ":" + (col * 2 + 10000 + exception_handling(b)) + ":" + (index + 100003) - | - def = TEssaNodeDefinition(_, b, index) - or - def = TEssaNodeRefinement(_, b, index) - or - def = TEssaEdgeDefinition(_, _, b) and index = piIndex() - or - def = TPhiFunction(_, b) and index = phiIndex() - ) + exists(EssaDefinition def, BasicBlock b, int index, int line, int col | + def = v.getDefinition() and + ( + if b.getNode(0).isNormalExit() + then line = 100000 and col = 0 + else b.hasLocationInfo(_, line, col, _, _) + ) and + /* Add large numbers to values to prevent 1000 sorting before 99 */ + result = + (line + 100000) + ":" + (col * 2 + 10000 + exception_handling(b)) + ":" + (index + 100003) + | + def = TEssaNodeDefinition(_, b, index) + or + def = TEssaNodeRefinement(_, b, index) + or + def = TEssaEdgeDefinition(_, _, b) and index = piIndex() + or + def = TPhiFunction(_, b) and index = phiIndex() + ) } /* Helper to compute an index for this SSA variable. */ private int var_index(EssaVariable v) { - location_string(v) = rank[result](string s | exists(EssaVariable x | location_string(x) = s) | s) + location_string(v) = rank[result](string s | exists(EssaVariable x | location_string(x) = s) | s) } /* Helper for `v.getRepresentation()` */ private int var_rank(EssaVariable v) { - exists(int r, SsaSourceVariable var | - var = v.getSourceVariable() and - var_index(v) = rank[r](EssaVariable x | x.getSourceVariable() = var | var_index(x)) and - result = r - 1 - ) + exists(int r, SsaSourceVariable var | + var = v.getSourceVariable() and + var_index(v) = rank[r](EssaVariable x | x.getSourceVariable() = var | var_index(x)) and + result = r - 1 + ) } /** Underlying IPA type for EssaDefinition and EssaVariable. */ cached private newtype TEssaDefinition = - TEssaNodeDefinition(SsaSourceVariable v, BasicBlock b, int i) { - EssaDefinitions::variableDefinition(v, _, b, _, i) - } or - TEssaNodeRefinement(SsaSourceVariable v, BasicBlock b, int i) { - EssaDefinitions::variableRefinement(v, _, b, _, i) - } or - TEssaEdgeDefinition(SsaSourceVariable v, BasicBlock pred, BasicBlock succ) { - EssaDefinitions::piNode(v, pred, succ) - } or - TPhiFunction(SsaSourceVariable v, BasicBlock b) { EssaDefinitions::phiNode(v, b) } + TEssaNodeDefinition(SsaSourceVariable v, BasicBlock b, int i) { + EssaDefinitions::variableDefinition(v, _, b, _, i) + } or + TEssaNodeRefinement(SsaSourceVariable v, BasicBlock b, int i) { + EssaDefinitions::variableRefinement(v, _, b, _, i) + } or + TEssaEdgeDefinition(SsaSourceVariable v, BasicBlock pred, BasicBlock succ) { + EssaDefinitions::piNode(v, pred, succ) + } or + TPhiFunction(SsaSourceVariable v, BasicBlock b) { EssaDefinitions::phiNode(v, b) } /** * Definition of an extended-SSA (ESSA) variable. @@ -132,38 +132,38 @@ private newtype TEssaDefinition = * and exactly one variable for each definition. */ abstract class EssaDefinition extends TEssaDefinition { - /** Gets a textual representation of this element. */ - string toString() { result = "EssaDefinition" } + /** Gets a textual representation of this element. */ + string toString() { result = "EssaDefinition" } - /** Gets the source variable for which this a definition, either explicit or implicit. */ - abstract SsaSourceVariable getSourceVariable(); + /** Gets the source variable for which this a definition, either explicit or implicit. */ + abstract SsaSourceVariable getSourceVariable(); - /** Gets a use of this definition as defined by the `SsaSourceVariable` class. */ - abstract ControlFlowNode getAUse(); + /** Gets a use of this definition as defined by the `SsaSourceVariable` class. */ + abstract ControlFlowNode getAUse(); - /** Holds if this definition reaches the end of `b`. */ - abstract predicate reachesEndOfBlock(BasicBlock b); + /** Holds if this definition reaches the end of `b`. */ + abstract predicate reachesEndOfBlock(BasicBlock b); - /** - * Gets the location of a control flow node that is indicative of this definition. - * Since definitions may occur on edges of the control flow graph, the given location may - * be imprecise. - * Distinct `EssaDefinitions` may return the same ControlFlowNode even for - * the same variable. - */ - abstract Location getLocation(); + /** + * Gets the location of a control flow node that is indicative of this definition. + * Since definitions may occur on edges of the control flow graph, the given location may + * be imprecise. + * Distinct `EssaDefinitions` may return the same ControlFlowNode even for + * the same variable. + */ + abstract Location getLocation(); - /** - * Gets a representation of this SSA definition for debugging purposes. - * Since this is primarily for debugging and testing, performance may be poor. - */ - abstract string getRepresentation(); + /** + * Gets a representation of this SSA definition for debugging purposes. + * Since this is primarily for debugging and testing, performance may be poor. + */ + abstract string getRepresentation(); - abstract Scope getScope(); + abstract Scope getScope(); - EssaVariable getVariable() { result.getDefinition() = this } + EssaVariable getVariable() { result.getDefinition() = this } - abstract BasicBlock getBasicBlock(); + abstract BasicBlock getBasicBlock(); } /** @@ -172,193 +172,193 @@ abstract class EssaDefinition extends TEssaDefinition { * variable. On one edge the test is true, on the other it is false. */ class EssaEdgeRefinement extends EssaDefinition, TEssaEdgeDefinition { - override string toString() { result = "SSA filter definition" } + override string toString() { result = "SSA filter definition" } - boolean getSense() { - this.getPredecessor().getATrueSuccessor() = this.getSuccessor() and result = true - or - this.getPredecessor().getAFalseSuccessor() = this.getSuccessor() and result = false - } + boolean getSense() { + this.getPredecessor().getATrueSuccessor() = this.getSuccessor() and result = true + or + this.getPredecessor().getAFalseSuccessor() = this.getSuccessor() and result = false + } - override SsaSourceVariable getSourceVariable() { this = TEssaEdgeDefinition(result, _, _) } + override SsaSourceVariable getSourceVariable() { this = TEssaEdgeDefinition(result, _, _) } - /** Gets the basic block preceding the edge on which this refinement occurs. */ - BasicBlock getPredecessor() { this = TEssaEdgeDefinition(_, result, _) } + /** Gets the basic block preceding the edge on which this refinement occurs. */ + BasicBlock getPredecessor() { this = TEssaEdgeDefinition(_, result, _) } - /** Gets the basic block succeeding the edge on which this refinement occurs. */ - BasicBlock getSuccessor() { this = TEssaEdgeDefinition(_, _, result) } + /** Gets the basic block succeeding the edge on which this refinement occurs. */ + BasicBlock getSuccessor() { this = TEssaEdgeDefinition(_, _, result) } - override ControlFlowNode getAUse() { - SsaDefinitions::reachesUse(this.getSourceVariable(), this.getSuccessor(), piIndex(), result) - } + override ControlFlowNode getAUse() { + SsaDefinitions::reachesUse(this.getSourceVariable(), this.getSuccessor(), piIndex(), result) + } - override predicate reachesEndOfBlock(BasicBlock b) { - SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), this.getSuccessor(), piIndex(), b) - } + override predicate reachesEndOfBlock(BasicBlock b) { + SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), this.getSuccessor(), piIndex(), b) + } - override Location getLocation() { result = this.getSuccessor().getNode(0).getLocation() } + override Location getLocation() { result = this.getSuccessor().getNode(0).getLocation() } - /** Gets the SSA variable to which this refinement applies. */ - EssaVariable getInput() { - exists(SsaSourceVariable var, EssaDefinition def | - var = this.getSourceVariable() and - var = def.getSourceVariable() and - def.reachesEndOfBlock(this.getPredecessor()) and - result.getDefinition() = def - ) - } + /** Gets the SSA variable to which this refinement applies. */ + EssaVariable getInput() { + exists(SsaSourceVariable var, EssaDefinition def | + var = this.getSourceVariable() and + var = def.getSourceVariable() and + def.reachesEndOfBlock(this.getPredecessor()) and + result.getDefinition() = def + ) + } - override string getRepresentation() { - result = this.getAQlClass() + "(" + this.getInput().getRepresentation() + ")" - } + override string getRepresentation() { + result = this.getAQlClass() + "(" + this.getInput().getRepresentation() + ")" + } - /** Gets the scope of the variable defined by this definition. */ - override Scope getScope() { result = this.getPredecessor().getScope() } + /** Gets the scope of the variable defined by this definition. */ + override Scope getScope() { result = this.getPredecessor().getScope() } - override BasicBlock getBasicBlock() { result = this.getSuccessor() } + override BasicBlock getBasicBlock() { result = this.getSuccessor() } } /** A Phi-function as specified in classic SSA form. */ class PhiFunction extends EssaDefinition, TPhiFunction { - override ControlFlowNode getAUse() { - SsaDefinitions::reachesUse(this.getSourceVariable(), this.getBasicBlock(), phiIndex(), result) - } + override ControlFlowNode getAUse() { + SsaDefinitions::reachesUse(this.getSourceVariable(), this.getBasicBlock(), phiIndex(), result) + } - override predicate reachesEndOfBlock(BasicBlock b) { - SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), this.getBasicBlock(), phiIndex(), b) - } + override predicate reachesEndOfBlock(BasicBlock b) { + SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), this.getBasicBlock(), phiIndex(), b) + } - override SsaSourceVariable getSourceVariable() { this = TPhiFunction(result, _) } + override SsaSourceVariable getSourceVariable() { this = TPhiFunction(result, _) } - /** Gets an input refinement that exists on one of the incoming edges to this phi node. */ - private EssaEdgeRefinement inputEdgeRefinement(BasicBlock pred) { - result.getSourceVariable() = this.getSourceVariable() and - result.getSuccessor() = this.getBasicBlock() and - result.getPredecessor() = pred - } + /** Gets an input refinement that exists on one of the incoming edges to this phi node. */ + private EssaEdgeRefinement inputEdgeRefinement(BasicBlock pred) { + result.getSourceVariable() = this.getSourceVariable() and + result.getSuccessor() = this.getBasicBlock() and + result.getPredecessor() = pred + } - private BasicBlock nonPiInput() { - result = this.getBasicBlock().getAPredecessor() and - not exists(this.inputEdgeRefinement(result)) - } + private BasicBlock nonPiInput() { + result = this.getBasicBlock().getAPredecessor() and + not exists(this.inputEdgeRefinement(result)) + } - pragma[noinline] - private SsaSourceVariable pred_var(BasicBlock pred) { - result = this.getSourceVariable() and - pred = this.nonPiInput() - } + pragma[noinline] + private SsaSourceVariable pred_var(BasicBlock pred) { + result = this.getSourceVariable() and + pred = this.nonPiInput() + } - /** Gets another definition of the same source variable that reaches this definition. */ - private EssaDefinition reachingDefinition(BasicBlock pred) { - result.getScope() = this.getScope() and - result.getSourceVariable() = pred_var(pred) and - result.reachesEndOfBlock(pred) - } + /** Gets another definition of the same source variable that reaches this definition. */ + private EssaDefinition reachingDefinition(BasicBlock pred) { + result.getScope() = this.getScope() and + result.getSourceVariable() = pred_var(pred) and + result.reachesEndOfBlock(pred) + } - /** Gets the input variable for this phi node on the edge `pred` -> `this.getBasicBlock()`, if any. */ - cached - EssaVariable getInput(BasicBlock pred) { - result.getDefinition() = this.reachingDefinition(pred) - or - result.getDefinition() = this.inputEdgeRefinement(pred) - } + /** Gets the input variable for this phi node on the edge `pred` -> `this.getBasicBlock()`, if any. */ + cached + EssaVariable getInput(BasicBlock pred) { + result.getDefinition() = this.reachingDefinition(pred) + or + result.getDefinition() = this.inputEdgeRefinement(pred) + } - /** Gets an input variable for this phi node. */ - EssaVariable getAnInput() { result = this.getInput(_) } + /** Gets an input variable for this phi node. */ + EssaVariable getAnInput() { result = this.getInput(_) } - /** Holds if forall incoming edges in the flow graph, there is an input variable */ - predicate isComplete() { - forall(BasicBlock pred | pred = this.getBasicBlock().getAPredecessor() | - exists(this.getInput(pred)) - ) - } + /** Holds if forall incoming edges in the flow graph, there is an input variable */ + predicate isComplete() { + forall(BasicBlock pred | pred = this.getBasicBlock().getAPredecessor() | + exists(this.getInput(pred)) + ) + } - override string toString() { result = "SSA Phi Function" } + override string toString() { result = "SSA Phi Function" } - /** Gets the basic block that succeeds this phi node. */ - override BasicBlock getBasicBlock() { this = TPhiFunction(_, result) } + /** Gets the basic block that succeeds this phi node. */ + override BasicBlock getBasicBlock() { this = TPhiFunction(_, result) } - override Location getLocation() { result = this.getBasicBlock().getNode(0).getLocation() } + override Location getLocation() { result = this.getBasicBlock().getNode(0).getLocation() } - /** Helper for `argList(n)`. */ - private int rankInput(EssaVariable input) { - input = this.getAnInput() and - var_index(input) = rank[result](EssaVariable v | v = this.getAnInput() | var_index(v)) - } + /** Helper for `argList(n)`. */ + private int rankInput(EssaVariable input) { + input = this.getAnInput() and + var_index(input) = rank[result](EssaVariable v | v = this.getAnInput() | var_index(v)) + } - /** Helper for `argList()`. */ - private string argList(int n) { - exists(EssaVariable input | n = this.rankInput(input) | - n = 1 and result = input.getRepresentation() - or - n > 1 and result = this.argList(n - 1) + ", " + input.getRepresentation() - ) - } + /** Helper for `argList()`. */ + private string argList(int n) { + exists(EssaVariable input | n = this.rankInput(input) | + n = 1 and result = input.getRepresentation() + or + n > 1 and result = this.argList(n - 1) + ", " + input.getRepresentation() + ) + } - /** Helper for `getRepresentation()`. */ - private string argList() { - exists(int last | - last = (max(int x | x = this.rankInput(_))) and - result = this.argList(last) - ) - } + /** Helper for `getRepresentation()`. */ + private string argList() { + exists(int last | + last = (max(int x | x = this.rankInput(_))) and + result = this.argList(last) + ) + } - override string getRepresentation() { - not exists(this.getAnInput()) and result = "phi()" - or - result = "phi(" + this.argList() + ")" - or - exists(this.getAnInput()) and - not exists(this.argList()) and - result = "phi(" + this.getSourceVariable().getName() + "??)" - } + override string getRepresentation() { + not exists(this.getAnInput()) and result = "phi()" + or + result = "phi(" + this.argList() + ")" + or + exists(this.getAnInput()) and + not exists(this.argList()) and + result = "phi(" + this.getSourceVariable().getName() + "??)" + } - override Scope getScope() { result = this.getBasicBlock().getScope() } + override Scope getScope() { result = this.getBasicBlock().getScope() } - private EssaEdgeRefinement piInputDefinition(EssaVariable input) { - input = this.getAnInput() and - result = input.getDefinition() - or - input = this.getAnInput() and result = input.getDefinition().(PhiFunction).piInputDefinition(_) - } + private EssaEdgeRefinement piInputDefinition(EssaVariable input) { + input = this.getAnInput() and + result = input.getDefinition() + or + input = this.getAnInput() and result = input.getDefinition().(PhiFunction).piInputDefinition(_) + } - /** - * Gets the variable which is the common and complete input to all pi-nodes that are themselves - * inputs to this phi-node. - * For example: - * ``` - * x = y() - * if complicated_test(x): - * do_a() - * else: - * do_b() - * phi - * ``` - * Which gives us the ESSA form: - * x0 = y() - * x1 = pi(x0, complicated_test(x0)) - * x2 = pi(x0, not complicated_test(x0)) - * x3 = phi(x1, x2) - * However we may not be able to track the value of `x` through `compilated_test` - * meaning that we cannot track `x` from `x0` to `x3`. - * By using `getShortCircuitInput()` we can do so, since the short-circuit input of `x3` is `x0`. - */ - pragma[noinline] - EssaVariable getShortCircuitInput() { - exists(BasicBlock common | - forall(EssaVariable input | input = this.getAnInput() | - common = this.piInputDefinition(input).getPredecessor() - ) and - forall(BasicBlock succ | succ = common.getASuccessor() | - succ = this.piInputDefinition(_).getSuccessor() - ) and - exists(EssaEdgeRefinement ref | - ref = this.piInputDefinition(_) and - ref.getPredecessor() = common and - ref.getInput() = result - ) - ) - } + /** + * Gets the variable which is the common and complete input to all pi-nodes that are themselves + * inputs to this phi-node. + * For example: + * ``` + * x = y() + * if complicated_test(x): + * do_a() + * else: + * do_b() + * phi + * ``` + * Which gives us the ESSA form: + * x0 = y() + * x1 = pi(x0, complicated_test(x0)) + * x2 = pi(x0, not complicated_test(x0)) + * x3 = phi(x1, x2) + * However we may not be able to track the value of `x` through `compilated_test` + * meaning that we cannot track `x` from `x0` to `x3`. + * By using `getShortCircuitInput()` we can do so, since the short-circuit input of `x3` is `x0`. + */ + pragma[noinline] + EssaVariable getShortCircuitInput() { + exists(BasicBlock common | + forall(EssaVariable input | input = this.getAnInput() | + common = this.piInputDefinition(input).getPredecessor() + ) and + forall(BasicBlock succ | succ = common.getASuccessor() | + succ = this.piInputDefinition(_).getSuccessor() + ) and + exists(EssaEdgeRefinement ref | + ref = this.piInputDefinition(_) and + ref.getPredecessor() = common and + ref.getInput() = result + ) + ) + } } /** @@ -366,114 +366,114 @@ class PhiFunction extends EssaDefinition, TPhiFunction { * another ESSA variable. */ class EssaNodeDefinition extends EssaDefinition, TEssaNodeDefinition { - override string toString() { result = "Essa node definition" } + override string toString() { result = "Essa node definition" } - override ControlFlowNode getAUse() { - exists(SsaSourceVariable v, BasicBlock b, int i | - this = TEssaNodeDefinition(v, b, i) and - SsaDefinitions::reachesUse(v, b, i, result) - ) - } + override ControlFlowNode getAUse() { + exists(SsaSourceVariable v, BasicBlock b, int i | + this = TEssaNodeDefinition(v, b, i) and + SsaDefinitions::reachesUse(v, b, i, result) + ) + } - override predicate reachesEndOfBlock(BasicBlock b) { - exists(BasicBlock defb, int i | - this = TEssaNodeDefinition(_, defb, i) and - SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), defb, i, b) - ) - } + override predicate reachesEndOfBlock(BasicBlock b) { + exists(BasicBlock defb, int i | + this = TEssaNodeDefinition(_, defb, i) and + SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), defb, i, b) + ) + } - override SsaSourceVariable getSourceVariable() { this = TEssaNodeDefinition(result, _, _) } + override SsaSourceVariable getSourceVariable() { this = TEssaNodeDefinition(result, _, _) } - /** Gets the ControlFlowNode corresponding to this definition */ - ControlFlowNode getDefiningNode() { this.definedBy(_, result) } + /** Gets the ControlFlowNode corresponding to this definition */ + ControlFlowNode getDefiningNode() { this.definedBy(_, result) } - override Location getLocation() { result = this.getDefiningNode().getLocation() } + override Location getLocation() { result = this.getDefiningNode().getLocation() } - override string getRepresentation() { result = this.getAQlClass() } + override string getRepresentation() { result = this.getAQlClass() } - override Scope getScope() { - exists(BasicBlock defb | - this = TEssaNodeDefinition(_, defb, _) and - result = defb.getScope() - ) - } + override Scope getScope() { + exists(BasicBlock defb | + this = TEssaNodeDefinition(_, defb, _) and + result = defb.getScope() + ) + } - predicate definedBy(SsaSourceVariable v, ControlFlowNode def) { - exists(BasicBlock b, int i | def = b.getNode(i) | - this = TEssaNodeDefinition(v, b, i + i) - or - this = TEssaNodeDefinition(v, b, i + i + 1) - ) - } + predicate definedBy(SsaSourceVariable v, ControlFlowNode def) { + exists(BasicBlock b, int i | def = b.getNode(i) | + this = TEssaNodeDefinition(v, b, i + i) + or + this = TEssaNodeDefinition(v, b, i + i + 1) + ) + } - override BasicBlock getBasicBlock() { result = this.getDefiningNode().getBasicBlock() } + override BasicBlock getBasicBlock() { result = this.getDefiningNode().getBasicBlock() } } /** A definition of an ESSA variable that takes another ESSA variable as an input. */ class EssaNodeRefinement extends EssaDefinition, TEssaNodeRefinement { - override string toString() { result = "SSA filter definition" } + override string toString() { result = "SSA filter definition" } - /** Gets the SSA variable to which this refinement applies. */ - EssaVariable getInput() { - result = potential_input(this) and - not result = potential_input(potential_input(this).getDefinition()) - } + /** Gets the SSA variable to which this refinement applies. */ + EssaVariable getInput() { + result = potential_input(this) and + not result = potential_input(potential_input(this).getDefinition()) + } - override ControlFlowNode getAUse() { - exists(SsaSourceVariable v, BasicBlock b, int i | - this = TEssaNodeRefinement(v, b, i) and - SsaDefinitions::reachesUse(v, b, i, result) - ) - } + override ControlFlowNode getAUse() { + exists(SsaSourceVariable v, BasicBlock b, int i | + this = TEssaNodeRefinement(v, b, i) and + SsaDefinitions::reachesUse(v, b, i, result) + ) + } - override predicate reachesEndOfBlock(BasicBlock b) { - exists(BasicBlock defb, int i | - this = TEssaNodeRefinement(_, defb, i) and - SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), defb, i, b) - ) - } + override predicate reachesEndOfBlock(BasicBlock b) { + exists(BasicBlock defb, int i | + this = TEssaNodeRefinement(_, defb, i) and + SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), defb, i, b) + ) + } - override SsaSourceVariable getSourceVariable() { this = TEssaNodeRefinement(result, _, _) } + override SsaSourceVariable getSourceVariable() { this = TEssaNodeRefinement(result, _, _) } - /** Gets the ControlFlowNode corresponding to this definition */ - ControlFlowNode getDefiningNode() { this.definedBy(_, result) } + /** Gets the ControlFlowNode corresponding to this definition */ + ControlFlowNode getDefiningNode() { this.definedBy(_, result) } - override Location getLocation() { result = this.getDefiningNode().getLocation() } + override Location getLocation() { result = this.getDefiningNode().getLocation() } - override string getRepresentation() { - result = this.getAQlClass() + "(" + this.getInput().getRepresentation() + ")" - or - not exists(this.getInput()) and - result = this.getAQlClass() + "(" + this.getSourceVariable().getName() + "??)" - } + override string getRepresentation() { + result = this.getAQlClass() + "(" + this.getInput().getRepresentation() + ")" + or + not exists(this.getInput()) and + result = this.getAQlClass() + "(" + this.getSourceVariable().getName() + "??)" + } - override Scope getScope() { - exists(BasicBlock defb | - this = TEssaNodeRefinement(_, defb, _) and - result = defb.getScope() - ) - } + override Scope getScope() { + exists(BasicBlock defb | + this = TEssaNodeRefinement(_, defb, _) and + result = defb.getScope() + ) + } - predicate definedBy(SsaSourceVariable v, ControlFlowNode def) { - exists(BasicBlock b, int i | def = b.getNode(i) | - this = TEssaNodeRefinement(v, b, i + i) - or - this = TEssaNodeRefinement(v, b, i + i + 1) - ) - } + predicate definedBy(SsaSourceVariable v, ControlFlowNode def) { + exists(BasicBlock b, int i | def = b.getNode(i) | + this = TEssaNodeRefinement(v, b, i + i) + or + this = TEssaNodeRefinement(v, b, i + i + 1) + ) + } - override BasicBlock getBasicBlock() { result = this.getDefiningNode().getBasicBlock() } + override BasicBlock getBasicBlock() { result = this.getDefiningNode().getBasicBlock() } } pragma[noopt] private EssaVariable potential_input(EssaNodeRefinement ref) { - exists(ControlFlowNode use, SsaSourceVariable var, ControlFlowNode def | - var.hasRefinement(use, def) and - use = result.getAUse() and - var = result.getSourceVariable() and - def = ref.getDefiningNode() and - var = ref.getSourceVariable() - ) + exists(ControlFlowNode use, SsaSourceVariable var, ControlFlowNode def | + var.hasRefinement(use, def) and + use = result.getAUse() and + var = result.getSourceVariable() and + def = ref.getDefiningNode() and + var = ref.getSourceVariable() + ) } /* For backwards compatibility */ @@ -484,97 +484,97 @@ deprecated class PyNodeRefinement = EssaNodeRefinement; /** An assignment to a variable `v = val` */ class AssignmentDefinition extends EssaNodeDefinition { - AssignmentDefinition() { - SsaSource::assignment_definition(this.getSourceVariable(), this.getDefiningNode(), _) - } + AssignmentDefinition() { + SsaSource::assignment_definition(this.getSourceVariable(), this.getDefiningNode(), _) + } - ControlFlowNode getValue() { - SsaSource::assignment_definition(this.getSourceVariable(), this.getDefiningNode(), result) - } + ControlFlowNode getValue() { + SsaSource::assignment_definition(this.getSourceVariable(), this.getDefiningNode(), result) + } - override string getRepresentation() { result = this.getValue().getNode().toString() } + override string getRepresentation() { result = this.getValue().getNode().toString() } } /** Capture of a raised exception `except ExceptionType ex:` */ class ExceptionCapture extends EssaNodeDefinition { - ExceptionCapture() { - SsaSource::exception_capture(this.getSourceVariable(), this.getDefiningNode()) - } + ExceptionCapture() { + SsaSource::exception_capture(this.getSourceVariable(), this.getDefiningNode()) + } - ControlFlowNode getType() { - exists(ExceptFlowNode ex | - ex.getName() = this.getDefiningNode() and - result = ex.getType() - ) - } + ControlFlowNode getType() { + exists(ExceptFlowNode ex | + ex.getName() = this.getDefiningNode() and + result = ex.getType() + ) + } - override string getRepresentation() { result = "except " + this.getSourceVariable().getName() } + override string getRepresentation() { result = "except " + this.getSourceVariable().getName() } } /** An assignment to a variable as part of a multiple assignment `..., v, ... = val` */ class MultiAssignmentDefinition extends EssaNodeDefinition { - MultiAssignmentDefinition() { - SsaSource::multi_assignment_definition(this.getSourceVariable(), this.getDefiningNode(), _, _) - } + MultiAssignmentDefinition() { + SsaSource::multi_assignment_definition(this.getSourceVariable(), this.getDefiningNode(), _, _) + } - override string getRepresentation() { - exists(ControlFlowNode value, int n | - this.indexOf(n, value) and - result = value.(DefinitionNode).getValue().getNode().toString() + "[" + n + "]" - ) - } + override string getRepresentation() { + exists(ControlFlowNode value, int n | + this.indexOf(n, value) and + result = value.(DefinitionNode).getValue().getNode().toString() + "[" + n + "]" + ) + } - /** Holds if `this` has (zero-based) index `index` in `lhs`. */ - predicate indexOf(int index, SequenceNode lhs) { - SsaSource::multi_assignment_definition(this.getSourceVariable(), this.getDefiningNode(), index, - lhs) - } + /** Holds if `this` has (zero-based) index `index` in `lhs`. */ + predicate indexOf(int index, SequenceNode lhs) { + SsaSource::multi_assignment_definition(this.getSourceVariable(), this.getDefiningNode(), index, + lhs) + } } /** A definition of a variable in a `with` statement */ class WithDefinition extends EssaNodeDefinition { - WithDefinition() { SsaSource::with_definition(this.getSourceVariable(), this.getDefiningNode()) } + WithDefinition() { SsaSource::with_definition(this.getSourceVariable(), this.getDefiningNode()) } - override string getRepresentation() { result = "with" } + override string getRepresentation() { result = "with" } } /** A definition of a variable by declaring it as a parameter */ class ParameterDefinition extends EssaNodeDefinition { - ParameterDefinition() { - SsaSource::parameter_definition(this.getSourceVariable(), this.getDefiningNode()) - } + ParameterDefinition() { + SsaSource::parameter_definition(this.getSourceVariable(), this.getDefiningNode()) + } - predicate isSelf() { this.getDefiningNode().getNode().(Parameter).isSelf() } + predicate isSelf() { this.getDefiningNode().getNode().(Parameter).isSelf() } - /** Gets the control flow node for the default value of this parameter */ - ControlFlowNode getDefault() { result.getNode() = this.getParameter().getDefault() } + /** Gets the control flow node for the default value of this parameter */ + ControlFlowNode getDefault() { result.getNode() = this.getParameter().getDefault() } - /** Gets the annotation control flow node of this parameter */ - ControlFlowNode getAnnotation() { result.getNode() = this.getParameter().getAnnotation() } + /** Gets the annotation control flow node of this parameter */ + ControlFlowNode getAnnotation() { result.getNode() = this.getParameter().getAnnotation() } - /** Gets the name of this parameter definition */ - string getName() { result = this.getParameter().asName().getId() } + /** Gets the name of this parameter definition */ + string getName() { result = this.getParameter().asName().getId() } - predicate isVarargs() { - exists(Function func | func.getVararg() = this.getDefiningNode().getNode()) - } + predicate isVarargs() { + exists(Function func | func.getVararg() = this.getDefiningNode().getNode()) + } - /** - * Holds if this parameter is a 'kwargs' parameter. - * The `kwargs` in `f(a, b, **kwargs)`. - */ - predicate isKwargs() { - exists(Function func | func.getKwarg() = this.getDefiningNode().getNode()) - } + /** + * Holds if this parameter is a 'kwargs' parameter. + * The `kwargs` in `f(a, b, **kwargs)`. + */ + predicate isKwargs() { + exists(Function func | func.getKwarg() = this.getDefiningNode().getNode()) + } - Parameter getParameter() { result = this.getDefiningNode().getNode() } + Parameter getParameter() { result = this.getDefiningNode().getNode() } } /** A deletion of a variable `del v` */ class DeletionDefinition extends EssaNodeDefinition { - DeletionDefinition() { - SsaSource::deletion_definition(this.getSourceVariable(), this.getDefiningNode()) - } + DeletionDefinition() { + SsaSource::deletion_definition(this.getSourceVariable(), this.getDefiningNode()) + } } /** @@ -582,88 +582,88 @@ class DeletionDefinition extends EssaNodeDefinition { * a global or non-local variable from one scope to another. */ class ScopeEntryDefinition extends EssaNodeDefinition { - ScopeEntryDefinition() { - this.getDefiningNode() = this.getSourceVariable().getScopeEntryDefinition() and - not this instanceof ImplicitSubModuleDefinition - } + ScopeEntryDefinition() { + this.getDefiningNode() = this.getSourceVariable().getScopeEntryDefinition() and + not this instanceof ImplicitSubModuleDefinition + } - override Scope getScope() { result.getEntryNode() = this.getDefiningNode() } + override Scope getScope() { result.getEntryNode() = this.getDefiningNode() } } /** Possible redefinition of variable via `from ... import *` */ class ImportStarRefinement extends EssaNodeRefinement { - ImportStarRefinement() { - SsaSource::import_star_refinement(this.getSourceVariable(), _, this.getDefiningNode()) - } + ImportStarRefinement() { + SsaSource::import_star_refinement(this.getSourceVariable(), _, this.getDefiningNode()) + } } /** Assignment of an attribute `obj.attr = val` */ class AttributeAssignment extends EssaNodeRefinement { - AttributeAssignment() { - SsaSource::attribute_assignment_refinement(this.getSourceVariable(), _, this.getDefiningNode()) - } + AttributeAssignment() { + SsaSource::attribute_assignment_refinement(this.getSourceVariable(), _, this.getDefiningNode()) + } - string getName() { result = this.getDefiningNode().(AttrNode).getName() } + string getName() { result = this.getDefiningNode().(AttrNode).getName() } - ControlFlowNode getValue() { result = this.getDefiningNode().(DefinitionNode).getValue() } + ControlFlowNode getValue() { result = this.getDefiningNode().(DefinitionNode).getValue() } - override string getRepresentation() { - result = - this.getAQlClass() + " '" + this.getName() + "'(" + this.getInput().getRepresentation() + ")" - or - not exists(this.getInput()) and - result = - this.getAQlClass() + " '" + this.getName() + "'(" + this.getSourceVariable().getName() + "??)" - } + override string getRepresentation() { + result = + this.getAQlClass() + " '" + this.getName() + "'(" + this.getInput().getRepresentation() + ")" + or + not exists(this.getInput()) and + result = + this.getAQlClass() + " '" + this.getName() + "'(" + this.getSourceVariable().getName() + "??)" + } } /** A use of a variable as an argument, `foo(v)`, which might modify the object referred to. */ class ArgumentRefinement extends EssaNodeRefinement { - ControlFlowNode argument; + ControlFlowNode argument; - ArgumentRefinement() { - SsaSource::argument_refinement(this.getSourceVariable(), argument, this.getDefiningNode()) - } + ArgumentRefinement() { + SsaSource::argument_refinement(this.getSourceVariable(), argument, this.getDefiningNode()) + } - ControlFlowNode getArgument() { result = argument } + ControlFlowNode getArgument() { result = argument } - CallNode getCall() { result = this.getDefiningNode() } + CallNode getCall() { result = this.getDefiningNode() } } /** Deletion of an attribute `del obj.attr`. */ class EssaAttributeDeletion extends EssaNodeRefinement { - EssaAttributeDeletion() { - SsaSource::attribute_deletion_refinement(this.getSourceVariable(), _, this.getDefiningNode()) - } + EssaAttributeDeletion() { + SsaSource::attribute_deletion_refinement(this.getSourceVariable(), _, this.getDefiningNode()) + } - string getName() { result = this.getDefiningNode().(AttrNode).getName() } + string getName() { result = this.getDefiningNode().(AttrNode).getName() } } /** A pi-node (guard) with only one successor. */ class SingleSuccessorGuard extends EssaNodeRefinement { - SingleSuccessorGuard() { - SsaSource::test_refinement(this.getSourceVariable(), _, this.getDefiningNode()) - } + SingleSuccessorGuard() { + SsaSource::test_refinement(this.getSourceVariable(), _, this.getDefiningNode()) + } - boolean getSense() { - exists(this.getDefiningNode().getAFalseSuccessor()) and result = false - or - exists(this.getDefiningNode().getATrueSuccessor()) and result = true - } + boolean getSense() { + exists(this.getDefiningNode().getAFalseSuccessor()) and result = false + or + exists(this.getDefiningNode().getATrueSuccessor()) and result = true + } - override string getRepresentation() { - result = EssaNodeRefinement.super.getRepresentation() + " [" + this.getSense().toString() + "]" - or - not exists(this.getSense()) and - result = EssaNodeRefinement.super.getRepresentation() + " [??]" - } + override string getRepresentation() { + result = EssaNodeRefinement.super.getRepresentation() + " [" + this.getSense().toString() + "]" + or + not exists(this.getSense()) and + result = EssaNodeRefinement.super.getRepresentation() + " [??]" + } - ControlFlowNode getTest() { result = this.getDefiningNode() } + ControlFlowNode getTest() { result = this.getDefiningNode() } - predicate useAndTest(ControlFlowNode use, ControlFlowNode test) { - test = this.getDefiningNode() and - SsaSource::test_refinement(this.getSourceVariable(), use, test) - } + predicate useAndTest(ControlFlowNode use, ControlFlowNode test) { + test = this.getDefiningNode() and + SsaSource::test_refinement(this.getSourceVariable(), use, test) + } } /** @@ -672,56 +672,56 @@ class SingleSuccessorGuard extends EssaNodeRefinement { * as they are imported, this is a good approximation for static analysis. */ class ImplicitSubModuleDefinition extends EssaNodeDefinition { - ImplicitSubModuleDefinition() { - SsaSource::init_module_submodule_defn(this.getSourceVariable(), this.getDefiningNode()) - } + ImplicitSubModuleDefinition() { + SsaSource::init_module_submodule_defn(this.getSourceVariable(), this.getDefiningNode()) + } } /** An implicit (possible) definition of an escaping variable at a call-site */ class CallsiteRefinement extends EssaNodeRefinement { - override string toString() { result = "CallsiteRefinement" } + override string toString() { result = "CallsiteRefinement" } - CallsiteRefinement() { - exists(SsaSourceVariable var, ControlFlowNode defn | - defn = var.redefinedAtCallSite() and - this.definedBy(var, defn) and - not this instanceof ArgumentRefinement and - not this instanceof MethodCallsiteRefinement and - not this instanceof SingleSuccessorGuard - ) - } + CallsiteRefinement() { + exists(SsaSourceVariable var, ControlFlowNode defn | + defn = var.redefinedAtCallSite() and + this.definedBy(var, defn) and + not this instanceof ArgumentRefinement and + not this instanceof MethodCallsiteRefinement and + not this instanceof SingleSuccessorGuard + ) + } - CallNode getCall() { this.getDefiningNode() = result } + CallNode getCall() { this.getDefiningNode() = result } } /** An implicit (possible) modification of the object referred at a method call */ class MethodCallsiteRefinement extends EssaNodeRefinement { - MethodCallsiteRefinement() { - SsaSource::method_call_refinement(this.getSourceVariable(), _, this.getDefiningNode()) and - not this instanceof SingleSuccessorGuard - } + MethodCallsiteRefinement() { + SsaSource::method_call_refinement(this.getSourceVariable(), _, this.getDefiningNode()) and + not this instanceof SingleSuccessorGuard + } - CallNode getCall() { this.getDefiningNode() = result } + CallNode getCall() { this.getDefiningNode() = result } } /** An implicit (possible) modification of `self` at a method call */ class SelfCallsiteRefinement extends MethodCallsiteRefinement { - SelfCallsiteRefinement() { this.getSourceVariable().(Variable).isSelf() } + SelfCallsiteRefinement() { this.getSourceVariable().(Variable).isSelf() } } /** Python specific sub-class of generic EssaEdgeRefinement */ class PyEdgeRefinement extends EssaEdgeRefinement { - override string getRepresentation() { - /* - * This is for testing so use capital 'P' to make it sort before 'phi' and - * be more visually distinctive. - */ + override string getRepresentation() { + /* + * This is for testing so use capital 'P' to make it sort before 'phi' and + * be more visually distinctive. + */ - result = "Pi(" + this.getInput().getRepresentation() + ") [" + this.getSense() + "]" - or - not exists(this.getInput()) and - result = "Pi(" + this.getSourceVariable().getName() + "??) [" + this.getSense() + "]" - } + result = "Pi(" + this.getInput().getRepresentation() + ") [" + this.getSense() + "]" + or + not exists(this.getInput()) and + result = "Pi(" + this.getSourceVariable().getName() + "??) [" + this.getSense() + "]" + } - ControlFlowNode getTest() { result = this.getPredecessor().getLastNode() } + ControlFlowNode getTest() { result = this.getPredecessor().getLastNode() } } diff --git a/python/ql/src/semmle/python/essa/SsaCompute.qll b/python/ql/src/semmle/python/essa/SsaCompute.qll index 5a0980ed973..a3344e036e9 100644 --- a/python/ql/src/semmle/python/essa/SsaCompute.qll +++ b/python/ql/src/semmle/python/essa/SsaCompute.qll @@ -93,261 +93,261 @@ import python cached private module SsaComputeImpl { + cached + module EssaDefinitionsImpl { + /** Whether `n` is a live update that is a definition of the variable `v`. */ cached - module EssaDefinitionsImpl { - /** Whether `n` is a live update that is a definition of the variable `v`. */ - cached - predicate variableDefinition( - SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int rankix, int i - ) { - SsaComputeImpl::variableDefine(v, n, b, i) and - SsaComputeImpl::defUseRank(v, b, rankix, i) and - ( - SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and - not SsaComputeImpl::defRank(v, b, rankix + 1, _) - or - not SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and Liveness::liveAtExit(v, b) - ) - } - - /** Whether `n` is a live update that is a definition of the variable `v`. */ - cached - predicate variableRefinement( - SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int rankix, int i - ) { - SsaComputeImpl::variableRefine(v, n, b, i) and - SsaComputeImpl::defUseRank(v, b, rankix, i) and - ( - SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and - not SsaComputeImpl::defRank(v, b, rankix + 1, _) - or - not SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and Liveness::liveAtExit(v, b) - ) - } - - cached - predicate variableUpdate(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int rankix, int i) { - variableDefinition(v, n, b, rankix, i) - or - variableRefinement(v, n, b, rankix, i) - } - - /** Holds if `def` is a pi-node for `v` on the edge `pred` -> `succ` */ - cached - predicate piNode(SsaSourceVariable v, BasicBlock pred, BasicBlock succ) { - v.hasRefinementEdge(_, pred, succ) and - Liveness::liveAtEntry(v, succ) - } - - /** A phi node for `v` at the beginning of basic block `b`. */ - cached - predicate phiNode(SsaSourceVariable v, BasicBlock b) { - ( - exists(BasicBlock def | def.dominanceFrontier(b) | SsaComputeImpl::ssaDef(v, def)) - or - piNode(v, _, b) and strictcount(b.getAPredecessor()) > 1 - ) and - Liveness::liveAtEntry(v, b) - } - } - - cached - predicate variableDefine(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) { - v.hasDefiningNode(n) and - exists(int j | - n = b.getNode(j) and - i = j * 2 + 1 - ) - } - - cached - predicate variableRefine(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) { - v.hasRefinement(_, n) and - exists(int j | - n = b.getNode(j) and - i = j * 2 + 1 - ) - } - - cached - predicate variableDef(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) { - variableDefine(v, n, b, i) or variableRefine(v, n, b, i) - } - - /** - * A ranking of the indices `i` at which there is an SSA definition or use of - * `v` in the basic block `b`. - * - * Basic block indices are translated to rank indices in order to skip - * irrelevant indices at which there is no definition or use when traversing - * basic blocks. - */ - cached - predicate defUseRank(SsaSourceVariable v, BasicBlock b, int rankix, int i) { - i = rank[rankix](int j | variableDef(v, _, b, j) or variableUse(v, _, b, j)) - } - - /** A definition of a variable occurring at the specified rank index in basic block `b`. */ - cached - predicate defRank(SsaSourceVariable v, BasicBlock b, int rankix, int i) { - variableDef(v, _, b, i) and - defUseRank(v, b, rankix, i) - } - - /** A `VarAccess` `use` of `v` in `b` at index `i`. */ - cached - predicate variableUse(SsaSourceVariable v, ControlFlowNode use, BasicBlock b, int i) { - (v.getAUse() = use or v.hasRefinement(use, _)) and - exists(int j | - b.getNode(j) = use and - i = 2 * j - ) - } - - /** - * A definition of an SSA variable occurring at the specified position. - * This is either a phi node, a `VariableUpdate`, or a parameter. - */ - cached - predicate ssaDef(SsaSourceVariable v, BasicBlock b) { - EssaDefinitions::phiNode(v, b) - or - EssaDefinitions::variableUpdate(v, _, b, _, _) - or - EssaDefinitions::piNode(v, _, b) - } - - /* - * The construction of SSA form ensures that each use of a variable is - * dominated by its definition. A definition of an SSA variable therefore - * reaches a `ControlFlowNode` if it is the _closest_ SSA variable definition - * that dominates the node. If two definitions dominate a node then one must - * dominate the other, so therefore the definition of _closest_ is given by the - * dominator tree. Thus, reaching definitions can be calculated in terms of - * dominance. - */ - - /** The maximum rank index for the given variable and basic block. */ - cached - int lastRank(SsaSourceVariable v, BasicBlock b) { - result = max(int rankix | defUseRank(v, b, rankix, _)) - or - not defUseRank(v, b, _, _) and - (EssaDefinitions::phiNode(v, b) or EssaDefinitions::piNode(v, _, b)) and - result = 0 - } - - private predicate ssaDefRank(SsaSourceVariable v, BasicBlock b, int rankix, int i) { - EssaDefinitions::variableUpdate(v, _, b, rankix, i) - or - EssaDefinitions::phiNode(v, b) and rankix = 0 and i = phiIndex() - or - EssaDefinitions::piNode(v, _, b) and - EssaDefinitions::phiNode(v, b) and - rankix = -1 and - i = piIndex() - or - EssaDefinitions::piNode(v, _, b) and - not EssaDefinitions::phiNode(v, b) and - rankix = 0 and - i = piIndex() - } - - /** The SSA definition reaches the rank index `rankix` in its own basic block `b`. */ - cached - predicate ssaDefReachesRank(SsaSourceVariable v, BasicBlock b, int i, int rankix) { - ssaDefRank(v, b, rankix, i) - or - ssaDefReachesRank(v, b, i, rankix - 1) and - rankix <= lastRank(v, b) and - not ssaDefRank(v, b, rankix, _) - } - - /** - * The SSA definition of `v` at `def` reaches `use` in the same basic block - * without crossing another SSA definition of `v`. - */ - cached - predicate ssaDefReachesUseWithinBlock( - SsaSourceVariable v, BasicBlock b, int i, ControlFlowNode use + predicate variableDefinition( + SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int rankix, int i ) { - exists(int rankix, int useix | - ssaDefReachesRank(v, b, i, rankix) and - defUseRank(v, b, rankix, useix) and - variableUse(v, use, b, useix) - ) + SsaComputeImpl::variableDefine(v, n, b, i) and + SsaComputeImpl::defUseRank(v, b, rankix, i) and + ( + SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and + not SsaComputeImpl::defRank(v, b, rankix + 1, _) + or + not SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and Liveness::liveAtExit(v, b) + ) + } + + /** Whether `n` is a live update that is a definition of the variable `v`. */ + cached + predicate variableRefinement( + SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int rankix, int i + ) { + SsaComputeImpl::variableRefine(v, n, b, i) and + SsaComputeImpl::defUseRank(v, b, rankix, i) and + ( + SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and + not SsaComputeImpl::defRank(v, b, rankix + 1, _) + or + not SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and Liveness::liveAtExit(v, b) + ) } cached - module LivenessImpl { - cached - predicate liveAtExit(SsaSourceVariable v, BasicBlock b) { liveAtEntry(v, b.getASuccessor()) } - - cached - predicate liveAtEntry(SsaSourceVariable v, BasicBlock b) { - SsaComputeImpl::defUseRank(v, b, 1, _) and not SsaComputeImpl::defRank(v, b, 1, _) - or - not SsaComputeImpl::defUseRank(v, b, _, _) and liveAtExit(v, b) - } + predicate variableUpdate(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int rankix, int i) { + variableDefinition(v, n, b, rankix, i) + or + variableRefinement(v, n, b, rankix, i) } + /** Holds if `def` is a pi-node for `v` on the edge `pred` -> `succ` */ + cached + predicate piNode(SsaSourceVariable v, BasicBlock pred, BasicBlock succ) { + v.hasRefinementEdge(_, pred, succ) and + Liveness::liveAtEntry(v, succ) + } + + /** A phi node for `v` at the beginning of basic block `b`. */ + cached + predicate phiNode(SsaSourceVariable v, BasicBlock b) { + ( + exists(BasicBlock def | def.dominanceFrontier(b) | SsaComputeImpl::ssaDef(v, def)) + or + piNode(v, _, b) and strictcount(b.getAPredecessor()) > 1 + ) and + Liveness::liveAtEntry(v, b) + } + } + + cached + predicate variableDefine(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) { + v.hasDefiningNode(n) and + exists(int j | + n = b.getNode(j) and + i = j * 2 + 1 + ) + } + + cached + predicate variableRefine(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) { + v.hasRefinement(_, n) and + exists(int j | + n = b.getNode(j) and + i = j * 2 + 1 + ) + } + + cached + predicate variableDef(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) { + variableDefine(v, n, b, i) or variableRefine(v, n, b, i) + } + + /** + * A ranking of the indices `i` at which there is an SSA definition or use of + * `v` in the basic block `b`. + * + * Basic block indices are translated to rank indices in order to skip + * irrelevant indices at which there is no definition or use when traversing + * basic blocks. + */ + cached + predicate defUseRank(SsaSourceVariable v, BasicBlock b, int rankix, int i) { + i = rank[rankix](int j | variableDef(v, _, b, j) or variableUse(v, _, b, j)) + } + + /** A definition of a variable occurring at the specified rank index in basic block `b`. */ + cached + predicate defRank(SsaSourceVariable v, BasicBlock b, int rankix, int i) { + variableDef(v, _, b, i) and + defUseRank(v, b, rankix, i) + } + + /** A `VarAccess` `use` of `v` in `b` at index `i`. */ + cached + predicate variableUse(SsaSourceVariable v, ControlFlowNode use, BasicBlock b, int i) { + (v.getAUse() = use or v.hasRefinement(use, _)) and + exists(int j | + b.getNode(j) = use and + i = 2 * j + ) + } + + /** + * A definition of an SSA variable occurring at the specified position. + * This is either a phi node, a `VariableUpdate`, or a parameter. + */ + cached + predicate ssaDef(SsaSourceVariable v, BasicBlock b) { + EssaDefinitions::phiNode(v, b) + or + EssaDefinitions::variableUpdate(v, _, b, _, _) + or + EssaDefinitions::piNode(v, _, b) + } + + /* + * The construction of SSA form ensures that each use of a variable is + * dominated by its definition. A definition of an SSA variable therefore + * reaches a `ControlFlowNode` if it is the _closest_ SSA variable definition + * that dominates the node. If two definitions dominate a node then one must + * dominate the other, so therefore the definition of _closest_ is given by the + * dominator tree. Thus, reaching definitions can be calculated in terms of + * dominance. + */ + + /** The maximum rank index for the given variable and basic block. */ + cached + int lastRank(SsaSourceVariable v, BasicBlock b) { + result = max(int rankix | defUseRank(v, b, rankix, _)) + or + not defUseRank(v, b, _, _) and + (EssaDefinitions::phiNode(v, b) or EssaDefinitions::piNode(v, _, b)) and + result = 0 + } + + private predicate ssaDefRank(SsaSourceVariable v, BasicBlock b, int rankix, int i) { + EssaDefinitions::variableUpdate(v, _, b, rankix, i) + or + EssaDefinitions::phiNode(v, b) and rankix = 0 and i = phiIndex() + or + EssaDefinitions::piNode(v, _, b) and + EssaDefinitions::phiNode(v, b) and + rankix = -1 and + i = piIndex() + or + EssaDefinitions::piNode(v, _, b) and + not EssaDefinitions::phiNode(v, b) and + rankix = 0 and + i = piIndex() + } + + /** The SSA definition reaches the rank index `rankix` in its own basic block `b`. */ + cached + predicate ssaDefReachesRank(SsaSourceVariable v, BasicBlock b, int i, int rankix) { + ssaDefRank(v, b, rankix, i) + or + ssaDefReachesRank(v, b, i, rankix - 1) and + rankix <= lastRank(v, b) and + not ssaDefRank(v, b, rankix, _) + } + + /** + * The SSA definition of `v` at `def` reaches `use` in the same basic block + * without crossing another SSA definition of `v`. + */ + cached + predicate ssaDefReachesUseWithinBlock( + SsaSourceVariable v, BasicBlock b, int i, ControlFlowNode use + ) { + exists(int rankix, int useix | + ssaDefReachesRank(v, b, i, rankix) and + defUseRank(v, b, rankix, useix) and + variableUse(v, use, b, useix) + ) + } + + cached + module LivenessImpl { + cached + predicate liveAtExit(SsaSourceVariable v, BasicBlock b) { liveAtEntry(v, b.getASuccessor()) } + cached - module SsaDefinitionsImpl { - pragma[noinline] - private predicate reachesEndOfBlockRec( - SsaSourceVariable v, BasicBlock defbb, int defindex, BasicBlock b - ) { - exists(BasicBlock idom | reachesEndOfBlock(v, defbb, defindex, idom) | - idom = b.getImmediateDominator() - ) - } - - /** - * The SSA definition of `v` at `def` reaches the end of a basic block `b`, at - * which point it is still live, without crossing another SSA definition of `v`. - */ - cached - predicate reachesEndOfBlock(SsaSourceVariable v, BasicBlock defbb, int defindex, BasicBlock b) { - Liveness::liveAtExit(v, b) and - ( - defbb = b and - SsaComputeImpl::ssaDefReachesRank(v, defbb, defindex, SsaComputeImpl::lastRank(v, b)) - or - // It is sufficient to traverse the dominator graph, cf. discussion above. - reachesEndOfBlockRec(v, defbb, defindex, b) and - not SsaComputeImpl::ssaDef(v, b) - ) - } - - /** - * The SSA definition of `v` at `(defbb, defindex)` reaches `use` without crossing another - * SSA definition of `v`. - */ - cached - predicate reachesUse(SsaSourceVariable v, BasicBlock defbb, int defindex, ControlFlowNode use) { - SsaComputeImpl::ssaDefReachesUseWithinBlock(v, defbb, defindex, use) - or - exists(BasicBlock b | - SsaComputeImpl::variableUse(v, use, b, _) and - reachesEndOfBlock(v, defbb, defindex, b.getAPredecessor()) and - not SsaComputeImpl::ssaDefReachesUseWithinBlock(v, b, _, use) - ) - } - - /** - * Holds if `(defbb, defindex)` is an SSA definition of `v` that reaches an exit without crossing another - * SSA definition of `v`. - */ - cached - predicate reachesExit(SsaSourceVariable v, BasicBlock defbb, int defindex) { - exists(BasicBlock last, ControlFlowNode use, int index | - not Liveness::liveAtExit(v, last) and - reachesUse(v, defbb, defindex, use) and - SsaComputeImpl::defUseRank(v, last, SsaComputeImpl::lastRank(v, last), index) and - SsaComputeImpl::variableUse(v, use, last, index) - ) - } + predicate liveAtEntry(SsaSourceVariable v, BasicBlock b) { + SsaComputeImpl::defUseRank(v, b, 1, _) and not SsaComputeImpl::defRank(v, b, 1, _) + or + not SsaComputeImpl::defUseRank(v, b, _, _) and liveAtExit(v, b) } + } + + cached + module SsaDefinitionsImpl { + pragma[noinline] + private predicate reachesEndOfBlockRec( + SsaSourceVariable v, BasicBlock defbb, int defindex, BasicBlock b + ) { + exists(BasicBlock idom | reachesEndOfBlock(v, defbb, defindex, idom) | + idom = b.getImmediateDominator() + ) + } + + /** + * The SSA definition of `v` at `def` reaches the end of a basic block `b`, at + * which point it is still live, without crossing another SSA definition of `v`. + */ + cached + predicate reachesEndOfBlock(SsaSourceVariable v, BasicBlock defbb, int defindex, BasicBlock b) { + Liveness::liveAtExit(v, b) and + ( + defbb = b and + SsaComputeImpl::ssaDefReachesRank(v, defbb, defindex, SsaComputeImpl::lastRank(v, b)) + or + // It is sufficient to traverse the dominator graph, cf. discussion above. + reachesEndOfBlockRec(v, defbb, defindex, b) and + not SsaComputeImpl::ssaDef(v, b) + ) + } + + /** + * The SSA definition of `v` at `(defbb, defindex)` reaches `use` without crossing another + * SSA definition of `v`. + */ + cached + predicate reachesUse(SsaSourceVariable v, BasicBlock defbb, int defindex, ControlFlowNode use) { + SsaComputeImpl::ssaDefReachesUseWithinBlock(v, defbb, defindex, use) + or + exists(BasicBlock b | + SsaComputeImpl::variableUse(v, use, b, _) and + reachesEndOfBlock(v, defbb, defindex, b.getAPredecessor()) and + not SsaComputeImpl::ssaDefReachesUseWithinBlock(v, b, _, use) + ) + } + + /** + * Holds if `(defbb, defindex)` is an SSA definition of `v` that reaches an exit without crossing another + * SSA definition of `v`. + */ + cached + predicate reachesExit(SsaSourceVariable v, BasicBlock defbb, int defindex) { + exists(BasicBlock last, ControlFlowNode use, int index | + not Liveness::liveAtExit(v, last) and + reachesUse(v, defbb, defindex, use) and + SsaComputeImpl::defUseRank(v, last, SsaComputeImpl::lastRank(v, last), index) and + SsaComputeImpl::variableUse(v, use, last, index) + ) + } + } } import SsaComputeImpl::SsaDefinitionsImpl as SsaDefinitions diff --git a/python/ql/src/semmle/python/essa/SsaDefinitions.qll b/python/ql/src/semmle/python/essa/SsaDefinitions.qll index 992a2bd96ac..0de3507f45e 100644 --- a/python/ql/src/semmle/python/essa/SsaDefinitions.qll +++ b/python/ql/src/semmle/python/essa/SsaDefinitions.qll @@ -8,142 +8,142 @@ private import semmle.python.pointsto.Base cached module SsaSource { - /** Holds if `v` is used as the receiver in a method call. */ - cached - predicate method_call_refinement(Variable v, ControlFlowNode use, CallNode call) { - use = v.getAUse() and - call.getFunction().(AttrNode).getObject() = use and - not test_contains(_, call) - } + /** Holds if `v` is used as the receiver in a method call. */ + cached + predicate method_call_refinement(Variable v, ControlFlowNode use, CallNode call) { + use = v.getAUse() and + call.getFunction().(AttrNode).getObject() = use and + not test_contains(_, call) + } - /** Holds if `v` is defined by assignment at `defn` and given `value`. */ - cached - predicate assignment_definition(Variable v, ControlFlowNode defn, ControlFlowNode value) { - defn.(NameNode).defines(v) and defn.(DefinitionNode).getValue() = value - } + /** Holds if `v` is defined by assignment at `defn` and given `value`. */ + cached + predicate assignment_definition(Variable v, ControlFlowNode defn, ControlFlowNode value) { + defn.(NameNode).defines(v) and defn.(DefinitionNode).getValue() = value + } - /** Holds if `v` is defined by assignment of the captured exception. */ - cached - predicate exception_capture(Variable v, NameNode defn) { - defn.defines(v) and - exists(ExceptFlowNode ex | ex.getName() = defn) - } + /** Holds if `v` is defined by assignment of the captured exception. */ + cached + predicate exception_capture(Variable v, NameNode defn) { + defn.defines(v) and + exists(ExceptFlowNode ex | ex.getName() = defn) + } - /** Holds if `v` is defined by a with statement. */ - cached - predicate with_definition(Variable v, ControlFlowNode defn) { - exists(With with, Name var | - with.getOptionalVars() = var and - var.getAFlowNode() = defn - | - var = v.getAStore() - ) - } + /** Holds if `v` is defined by a with statement. */ + cached + predicate with_definition(Variable v, ControlFlowNode defn) { + exists(With with, Name var | + with.getOptionalVars() = var and + var.getAFlowNode() = defn + | + var = v.getAStore() + ) + } - /** Holds if `v` is defined by multiple assignment at `defn`. */ - cached - predicate multi_assignment_definition(Variable v, ControlFlowNode defn, int n, SequenceNode lhs) { - ( - defn.(NameNode).defines(v) - or - defn.(StarredNode).getValue().(NameNode).defines(v) - ) and - not exists(defn.(DefinitionNode).getValue()) and - lhs.getElement(n) = defn and - lhs.getBasicBlock().dominates(defn.getBasicBlock()) - } + /** Holds if `v` is defined by multiple assignment at `defn`. */ + cached + predicate multi_assignment_definition(Variable v, ControlFlowNode defn, int n, SequenceNode lhs) { + ( + defn.(NameNode).defines(v) + or + defn.(StarredNode).getValue().(NameNode).defines(v) + ) and + not exists(defn.(DefinitionNode).getValue()) and + lhs.getElement(n) = defn and + lhs.getBasicBlock().dominates(defn.getBasicBlock()) + } - /** Holds if `v` is defined by a `for` statement, the definition being `defn` */ - cached - predicate iteration_defined_variable(Variable v, ControlFlowNode defn, ControlFlowNode sequence) { - exists(ForNode for | for.iterates(defn, sequence)) and - defn.(NameNode).defines(v) - } + /** Holds if `v` is defined by a `for` statement, the definition being `defn` */ + cached + predicate iteration_defined_variable(Variable v, ControlFlowNode defn, ControlFlowNode sequence) { + exists(ForNode for | for.iterates(defn, sequence)) and + defn.(NameNode).defines(v) + } - /** Holds if `v` is a parameter variable and `defn` is the CFG node for that parameter. */ - cached - predicate parameter_definition(Variable v, ControlFlowNode defn) { - exists(Function f, Name param | - f.getAnArg() = param or - f.getVararg() = param or - f.getKwarg() = param or - f.getKeywordOnlyArg(_) = param - | - defn.getNode() = param and - param.getVariable() = v - ) - } + /** Holds if `v` is a parameter variable and `defn` is the CFG node for that parameter. */ + cached + predicate parameter_definition(Variable v, ControlFlowNode defn) { + exists(Function f, Name param | + f.getAnArg() = param or + f.getVararg() = param or + f.getKwarg() = param or + f.getKeywordOnlyArg(_) = param + | + defn.getNode() = param and + param.getVariable() = v + ) + } - /** Holds if `v` is deleted at `del`. */ - cached - predicate deletion_definition(Variable v, DeletionNode del) { - del.getTarget().(NameNode).deletes(v) - } + /** Holds if `v` is deleted at `del`. */ + cached + predicate deletion_definition(Variable v, DeletionNode del) { + del.getTarget().(NameNode).deletes(v) + } - /** - * Holds if the name of `var` refers to a submodule of a package and `f` is the entry point - * to the __init__ module of that package. - */ - cached - predicate init_module_submodule_defn(SsaSourceVariable var, ControlFlowNode f) { - var instanceof GlobalVariable and - exists(Module init | - init.isPackageInit() and - exists(init.getPackage().getSubModule(var.getName())) and - init.getEntryNode() = f and - var.getScope() = init - ) - } + /** + * Holds if the name of `var` refers to a submodule of a package and `f` is the entry point + * to the __init__ module of that package. + */ + cached + predicate init_module_submodule_defn(SsaSourceVariable var, ControlFlowNode f) { + var instanceof GlobalVariable and + exists(Module init | + init.isPackageInit() and + exists(init.getPackage().getSubModule(var.getName())) and + init.getEntryNode() = f and + var.getScope() = init + ) + } - /** Holds if the `v` is in scope at a `from import ... *` and may thus be redefined by that statement */ - cached - predicate import_star_refinement(SsaSourceVariable v, ControlFlowNode use, ControlFlowNode def) { - use = def and - def instanceof ImportStarNode and - ( - v.getScope() = def.getScope() - or - exists(NameNode other | - other.uses(v) and - def.getScope() = other.getScope() - ) - ) - } + /** Holds if the `v` is in scope at a `from import ... *` and may thus be redefined by that statement */ + cached + predicate import_star_refinement(SsaSourceVariable v, ControlFlowNode use, ControlFlowNode def) { + use = def and + def instanceof ImportStarNode and + ( + v.getScope() = def.getScope() + or + exists(NameNode other | + other.uses(v) and + def.getScope() = other.getScope() + ) + ) + } - /** Holds if an attribute is assigned at `def` and `use` is the use of `v` for that assignment */ - cached - predicate attribute_assignment_refinement(Variable v, ControlFlowNode use, ControlFlowNode def) { - use.(NameNode).uses(v) and - def.isStore() and - def.(AttrNode).getObject() = use - } + /** Holds if an attribute is assigned at `def` and `use` is the use of `v` for that assignment */ + cached + predicate attribute_assignment_refinement(Variable v, ControlFlowNode use, ControlFlowNode def) { + use.(NameNode).uses(v) and + def.isStore() and + def.(AttrNode).getObject() = use + } - /** Holds if a `v` is used as an argument to `call`, which *may* modify the object referred to by `v` */ - cached - predicate argument_refinement(Variable v, ControlFlowNode use, CallNode call) { - use.(NameNode).uses(v) and - call.getArg(0) = use and - not method_call_refinement(v, _, call) and - not test_contains(_, call) - } + /** Holds if a `v` is used as an argument to `call`, which *may* modify the object referred to by `v` */ + cached + predicate argument_refinement(Variable v, ControlFlowNode use, CallNode call) { + use.(NameNode).uses(v) and + call.getArg(0) = use and + not method_call_refinement(v, _, call) and + not test_contains(_, call) + } - /** Holds if an attribute is deleted at `def` and `use` is the use of `v` for that deletion */ - cached - predicate attribute_deletion_refinement(Variable v, NameNode use, DeletionNode def) { - use.uses(v) and - def.getTarget().(AttrNode).getObject() = use - } + /** Holds if an attribute is deleted at `def` and `use` is the use of `v` for that deletion */ + cached + predicate attribute_deletion_refinement(Variable v, NameNode use, DeletionNode def) { + use.uses(v) and + def.getTarget().(AttrNode).getObject() = use + } - /** Holds if the set of possible values for `v` is refined by `test` and `use` is the use of `v` in that test. */ - cached - predicate test_refinement(Variable v, ControlFlowNode use, ControlFlowNode test) { - use.(NameNode).uses(v) and - test.getAChild*() = use and - test.isBranch() and - exists(BasicBlock block | - block = use.getBasicBlock() and - block = test.getBasicBlock() and - not block.getLastNode() = test - ) - } + /** Holds if the set of possible values for `v` is refined by `test` and `use` is the use of `v` in that test. */ + cached + predicate test_refinement(Variable v, ControlFlowNode use, ControlFlowNode test) { + use.(NameNode).uses(v) and + test.getAChild*() = use and + test.isBranch() and + exists(BasicBlock block | + block = use.getBasicBlock() and + block = test.getBasicBlock() and + not block.getLastNode() = test + ) + } } diff --git a/python/ql/src/semmle/python/filters/GeneratedCode.qll b/python/ql/src/semmle/python/filters/GeneratedCode.qll index 5b1721945da..a818f172637 100644 --- a/python/ql/src/semmle/python/filters/GeneratedCode.qll +++ b/python/ql/src/semmle/python/filters/GeneratedCode.qll @@ -5,7 +5,7 @@ import semmle.python.templates.Templates * A file that is detected as being generated. */ abstract class GeneratedFile extends File { - abstract string getTool(); + abstract string getTool(); } /* @@ -15,173 +15,173 @@ abstract class GeneratedFile extends File { */ library class GenericGeneratedFile extends GeneratedFile { - GenericGeneratedFile() { - not this instanceof SpecificGeneratedFile and - ( - (lax_generated_by(this, _) or lax_generated_from(this, _)) and - dont_modify(this) - or - strict_generated_by(this, _) - or - strict_generated_from(this, _) - or - auto_generated(this) - ) - } + GenericGeneratedFile() { + not this instanceof SpecificGeneratedFile and + ( + (lax_generated_by(this, _) or lax_generated_from(this, _)) and + dont_modify(this) + or + strict_generated_by(this, _) + or + strict_generated_from(this, _) + or + auto_generated(this) + ) + } - override string getTool() { lax_generated_by(this, result) or strict_generated_by(this, result) } + override string getTool() { lax_generated_by(this, result) or strict_generated_by(this, result) } } private string comment_or_docstring(File f, boolean before_code) { - exists(Comment c | - c.getLocation().getFile() = f and - result = c.getText() - | - if - exists(Stmt s | - s.getEnclosingModule().getFile() = f and - s.getLocation().getStartLine() < c.getLocation().getStartLine() - ) - then before_code = false - else before_code = true - ) - or - exists(Module m | m.getFile() = f | - result = m.getDocString().getText() and - before_code = true - ) + exists(Comment c | + c.getLocation().getFile() = f and + result = c.getText() + | + if + exists(Stmt s | + s.getEnclosingModule().getFile() = f and + s.getLocation().getStartLine() < c.getLocation().getStartLine() + ) + then before_code = false + else before_code = true + ) + or + exists(Module m | m.getFile() = f | + result = m.getDocString().getText() and + before_code = true + ) } private predicate lax_generated_by(File f, string tool) { - exists(string comment | comment = comment_or_docstring(f, _) | - tool = - comment - .regexpCapture("(?is).*\\b(?:(?:auto[ -]?)?generated|created automatically) by (?:the )?([-/\\w.]+[-/\\w]).*", - 1) - ) + exists(string comment | comment = comment_or_docstring(f, _) | + tool = + comment + .regexpCapture("(?is).*\\b(?:(?:auto[ -]?)?generated|created automatically) by (?:the )?([-/\\w.]+[-/\\w]).*", + 1) + ) } private predicate lax_generated_from(File f, string src) { - exists(string comment | comment = comment_or_docstring(f, _) | - src = - comment - .regexpCapture("(?is).*\\b((?:auto[ -]?)?generated|created automatically) from ([-/\\w.]+[-/\\w]).*", - 1) - ) + exists(string comment | comment = comment_or_docstring(f, _) | + src = + comment + .regexpCapture("(?is).*\\b((?:auto[ -]?)?generated|created automatically) from ([-/\\w.]+[-/\\w]).*", + 1) + ) } private predicate strict_generated_by(File f, string tool) { - exists(string comment | comment = comment_or_docstring(f, true) | - tool = - comment - .regexpCapture("(?is)# *(?:this +)?(?:(?:code|file) +)?(?:is +)?(?:(?:auto(?:matically)?[ -]?)?generated|created automatically) by (?:the )?([-/\\w.]+[-/\\w]).*", - 1) - ) + exists(string comment | comment = comment_or_docstring(f, true) | + tool = + comment + .regexpCapture("(?is)# *(?:this +)?(?:(?:code|file) +)?(?:is +)?(?:(?:auto(?:matically)?[ -]?)?generated|created automatically) by (?:the )?([-/\\w.]+[-/\\w]).*", + 1) + ) } private predicate strict_generated_from(File f, string src) { - exists(string comment | comment = comment_or_docstring(f, true) | - src = - comment - .regexpCapture("(?is)# *(?:this +)?(?:(?:code|file) +)?(?:is +)?(?:(?:auto(?:matically)?[ -]?)?generated|created automatically) from ([-/\\w.]+[-/\\w]).*", - 1) - ) + exists(string comment | comment = comment_or_docstring(f, true) | + src = + comment + .regexpCapture("(?is)# *(?:this +)?(?:(?:code|file) +)?(?:is +)?(?:(?:auto(?:matically)?[ -]?)?generated|created automatically) from ([-/\\w.]+[-/\\w]).*", + 1) + ) } private predicate dont_modify(File f) { - comment_or_docstring(f, _).regexpMatch("(?is).*\\b(Do not|Don't) (edit|modify|make changes)\\b.*") + comment_or_docstring(f, _).regexpMatch("(?is).*\\b(Do not|Don't) (edit|modify|make changes)\\b.*") } private predicate auto_generated(File f) { - exists(Comment c | - c.getLocation().getFile() = f and - c - .getText() - .regexpMatch("(?is)# *this +(code|file) +is +(auto(matically)?[ -]?generated|created automatically).*") - ) + exists(Comment c | + c.getLocation().getFile() = f and + c + .getText() + .regexpMatch("(?is)# *this +(code|file) +is +(auto(matically)?[ -]?generated|created automatically).*") + ) } /** * A file generated by a template engine */ abstract library class SpecificGeneratedFile extends GeneratedFile { - /* - * Currently cover Spitfire, Pyxl and Mako. - * Django templates are not compiled to Python. - * Jinja2 templates are compiled direct to bytecode via the ast. - */ + /* + * Currently cover Spitfire, Pyxl and Mako. + * Django templates are not compiled to Python. + * Jinja2 templates are compiled direct to bytecode via the ast. + */ - } + } /** File generated by the spitfire templating engine */ class SpitfireGeneratedFile extends SpecificGeneratedFile { - SpitfireGeneratedFile() { - exists(Module m | m.getFile() = this and not m instanceof SpitfireTemplate | - exists(ImportMember template_method, ImportExpr spitfire_runtime_template | - spitfire_runtime_template.getName() = "spitfire.runtime.template" and - template_method.getModule() = spitfire_runtime_template and - template_method.getName() = "template_method" - ) - ) - } + SpitfireGeneratedFile() { + exists(Module m | m.getFile() = this and not m instanceof SpitfireTemplate | + exists(ImportMember template_method, ImportExpr spitfire_runtime_template | + spitfire_runtime_template.getName() = "spitfire.runtime.template" and + template_method.getModule() = spitfire_runtime_template and + template_method.getName() = "template_method" + ) + ) + } - override string getTool() { result = "spitfire" } + override string getTool() { result = "spitfire" } } /** File generated by the pyxl templating engine */ class PyxlGeneratedFile extends SpecificGeneratedFile { - PyxlGeneratedFile() { this.getSpecifiedEncoding() = "pyxl" } + PyxlGeneratedFile() { this.getSpecifiedEncoding() = "pyxl" } - override string getTool() { result = "pyxl" } + override string getTool() { result = "pyxl" } } /** File generated by the mako templating engine */ class MakoGeneratedFile extends SpecificGeneratedFile { - MakoGeneratedFile() { - exists(Module m | m.getFile() = this | - from_mako_import(m) = "runtime" and - from_mako_import(m) = "filters" and - from_mako_import(m) = "cache" and - exists(Assign a, Name n | - a.getScope() = m and a.getATarget() = n and n.getId() = "__M_dict_builtin" - ) and - exists(Assign a, Name n | - a.getScope() = m and a.getATarget() = n and n.getId() = "__M_locals_builtin" - ) and - exists(Assign a, Name n | - a.getScope() = m and a.getATarget() = n and n.getId() = "_magic_number" - ) - ) - } + MakoGeneratedFile() { + exists(Module m | m.getFile() = this | + from_mako_import(m) = "runtime" and + from_mako_import(m) = "filters" and + from_mako_import(m) = "cache" and + exists(Assign a, Name n | + a.getScope() = m and a.getATarget() = n and n.getId() = "__M_dict_builtin" + ) and + exists(Assign a, Name n | + a.getScope() = m and a.getATarget() = n and n.getId() = "__M_locals_builtin" + ) and + exists(Assign a, Name n | + a.getScope() = m and a.getATarget() = n and n.getId() = "_magic_number" + ) + ) + } - override string getTool() { result = "mako" } + override string getTool() { result = "mako" } } string from_mako_import(Module m) { - exists(ImportMember member, ImportExpr mako | - member.getScope() = m and - member.getModule() = mako and - mako.getName() = "mako" - | - result = member.getName() - ) + exists(ImportMember member, ImportExpr mako | + member.getScope() = m and + member.getModule() = mako and + mako.getName() = "mako" + | + result = member.getName() + ) } /** File generated by Google's protobuf tool. */ class ProtobufGeneratedFile extends SpecificGeneratedFile { - ProtobufGeneratedFile() { - this.getAbsolutePath().regexpMatch(".*_pb2?.py") and - exists(Module m | m.getFile() = this | - exists(ImportExpr imp | imp.getEnclosingModule() = m | - imp.getImportedModuleName() = "google.net.proto2.python.public" - ) and - exists(AssignStmt a, Name n | - a.getEnclosingModule() = m and - a.getATarget() = n and - n.getId() = "DESCRIPTOR" - ) - ) - } + ProtobufGeneratedFile() { + this.getAbsolutePath().regexpMatch(".*_pb2?.py") and + exists(Module m | m.getFile() = this | + exists(ImportExpr imp | imp.getEnclosingModule() = m | + imp.getImportedModuleName() = "google.net.proto2.python.public" + ) and + exists(AssignStmt a, Name n | + a.getEnclosingModule() = m and + a.getATarget() = n and + n.getId() = "DESCRIPTOR" + ) + ) + } - override string getTool() { result = "protobuf" } + override string getTool() { result = "protobuf" } } diff --git a/python/ql/src/semmle/python/filters/Tests.qll b/python/ql/src/semmle/python/filters/Tests.qll index b4fb59fda07..b35f275aab5 100644 --- a/python/ql/src/semmle/python/filters/Tests.qll +++ b/python/ql/src/semmle/python/filters/Tests.qll @@ -4,43 +4,43 @@ abstract class TestScope extends Scope { } // don't extend Class directly to avoid ambiguous method warnings class UnitTestClass extends TestScope { - UnitTestClass() { - exists(ClassValue cls | this = cls.getScope() | - cls.getABaseType+() = Module::named("unittest").attr(_) - or - cls.getABaseType+().getName().toLowerCase() = "testcase" - ) - } + UnitTestClass() { + exists(ClassValue cls | this = cls.getScope() | + cls.getABaseType+() = Module::named("unittest").attr(_) + or + cls.getABaseType+().getName().toLowerCase() = "testcase" + ) + } } abstract class Test extends TestScope { } /** Class of test function that uses the `unittest` framework */ class UnitTestFunction extends Test { - UnitTestFunction() { - this.getScope+() instanceof UnitTestClass and - this.(Function).getName().matches("test%") - } + UnitTestFunction() { + this.getScope+() instanceof UnitTestClass and + this.(Function).getName().matches("test%") + } } class PyTestFunction extends Test { - PyTestFunction() { - exists(Module pytest | pytest.getName() = "pytest") and - this.(Function).getName().matches("test%") - } + PyTestFunction() { + exists(Module pytest | pytest.getName() = "pytest") and + this.(Function).getName().matches("test%") + } } class NoseTestFunction extends Test { - NoseTestFunction() { - exists(Module nose | nose.getName() = "nose") and - this.(Function).getName().matches("test%") - } + NoseTestFunction() { + exists(Module nose | nose.getName() = "nose") and + this.(Function).getName().matches("test%") + } } /** Class of functions that are clearly tests, but don't belong to a specific framework */ class UnknownTestFunction extends Test { - UnknownTestFunction() { - this.(Function).getName().matches("test%") and - this.getEnclosingModule().getFile().getShortName().matches("test_%.py") - } + UnknownTestFunction() { + this.(Function).getName().matches("test%") and + this.getEnclosingModule().getFile().getShortName().matches("test_%.py") + } } diff --git a/python/ql/src/semmle/python/libraries/Zope.qll b/python/ql/src/semmle/python/libraries/Zope.qll index 381e40edeab..728c334352b 100644 --- a/python/ql/src/semmle/python/libraries/Zope.qll +++ b/python/ql/src/semmle/python/libraries/Zope.qll @@ -5,43 +5,43 @@ private import semmle.python.pointsto.PointsTo /** A method that to a sub-class of `zope.interface.Interface` */ deprecated class ZopeInterfaceMethod extends PyFunctionObject { - /** Holds if this method belongs to a class that sub-classes `zope.interface.Interface` */ - ZopeInterfaceMethod() { - exists(Object interface, ClassObject owner | - interface = ModuleObject::named("zope.interface").attr("Interface") and - owner.declaredAttribute(_) = this and - owner.getAnImproperSuperType().getABaseType() = interface - ) - } + /** Holds if this method belongs to a class that sub-classes `zope.interface.Interface` */ + ZopeInterfaceMethod() { + exists(Object interface, ClassObject owner | + interface = ModuleObject::named("zope.interface").attr("Interface") and + owner.declaredAttribute(_) = this and + owner.getAnImproperSuperType().getABaseType() = interface + ) + } - override int minParameters() { result = super.minParameters() + 1 } + override int minParameters() { result = super.minParameters() + 1 } - override int maxParameters() { - if exists(this.getFunction().getVararg()) - then result = super.maxParameters() - else result = super.maxParameters() + 1 - } + override int maxParameters() { + if exists(this.getFunction().getVararg()) + then result = super.maxParameters() + else result = super.maxParameters() + 1 + } } /** A method that belongs to a sub-class of `zope.interface.Interface` */ class ZopeInterfaceMethodValue extends PythonFunctionValue { - /** Holds if this method belongs to a class that sub-classes `zope.interface.Interface` */ - ZopeInterfaceMethodValue() { - exists(Value interface, ClassValue owner | - interface = Module::named("zope.interface").attr("Interface") and - owner.declaredAttribute(_) = this and - // `zope.interface.Interface` will be recognized as a Value by the pointsTo analysis, - // because it is the result of instantiating a "meta" class. getASuperType only returns - // ClassValues, so we do this little trick to make things work - Types::getBase(owner.getASuperType(), _) = interface - ) - } + /** Holds if this method belongs to a class that sub-classes `zope.interface.Interface` */ + ZopeInterfaceMethodValue() { + exists(Value interface, ClassValue owner | + interface = Module::named("zope.interface").attr("Interface") and + owner.declaredAttribute(_) = this and + // `zope.interface.Interface` will be recognized as a Value by the pointsTo analysis, + // because it is the result of instantiating a "meta" class. getASuperType only returns + // ClassValues, so we do this little trick to make things work + Types::getBase(owner.getASuperType(), _) = interface + ) + } - override int minParameters() { result = super.minParameters() + 1 } + override int minParameters() { result = super.minParameters() + 1 } - override int maxParameters() { - if exists(this.getScope().getVararg()) - then result = super.maxParameters() - else result = super.maxParameters() + 1 - } + override int maxParameters() { + if exists(this.getScope().getVararg()) + then result = super.maxParameters() + else result = super.maxParameters() + 1 + } } diff --git a/python/ql/src/semmle/python/objects/Callables.qll b/python/ql/src/semmle/python/objects/Callables.qll index 4021f04c510..2bac030d89e 100644 --- a/python/ql/src/semmle/python/objects/Callables.qll +++ b/python/ql/src/semmle/python/objects/Callables.qll @@ -7,376 +7,376 @@ private import semmle.python.pointsto.MRO private import semmle.python.types.Builtins abstract class CallableObjectInternal extends ObjectInternal { - /** Gets the scope of this callable if it has one */ - abstract Function getScope(); + /** Gets the scope of this callable if it has one */ + abstract Function getScope(); - /** Gets a call to this callable from the given context */ - abstract CallNode getACall(PointsToContext ctx); + /** Gets a call to this callable from the given context */ + abstract CallNode getACall(PointsToContext ctx); - /** Gets a call to this callable */ - CallNode getACall() { result = this.getACall(_) } + /** Gets a call to this callable */ + CallNode getACall() { result = this.getACall(_) } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - /** Gets the `n`th parameter node of this callable. */ - abstract NameNode getParameter(int n); + /** Gets the `n`th parameter node of this callable. */ + abstract NameNode getParameter(int n); - /** Gets the `name`d parameter node of this callable. */ - abstract NameNode getParameterByName(string name); + /** Gets the `name`d parameter node of this callable. */ + abstract NameNode getParameterByName(string name); - abstract predicate neverReturns(); + abstract predicate neverReturns(); - override int length() { none() } + override int length() { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - /* Callables aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* Callables aren't iterable */ + override ObjectInternal getIterNext() { none() } } /** Class representing Python functions */ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFunctionObject { - override Function getScope() { - exists(CallableExpr expr | - this = TPythonFunctionObject(expr.getAFlowNode()) and - result = expr.getInnerScope() - ) - } + override Function getScope() { + exists(CallableExpr expr | + this = TPythonFunctionObject(expr.getAFlowNode()) and + result = expr.getInnerScope() + ) + } - override string toString() { result = "Function " + this.getScope().getQualifiedName() } + override string toString() { result = "Function " + this.getScope().getQualifiedName() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - this = TPythonFunctionObject(node) and context.appliesTo(node) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + this = TPythonFunctionObject(node) and context.appliesTo(node) + } - override ObjectInternal getClass() { - result = TBuiltinClassObject(Builtin::special("FunctionType")) - } + override ObjectInternal getClass() { + result = TBuiltinClassObject(Builtin::special("FunctionType")) + } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { this = TPythonFunctionObject(result) } + override ControlFlowNode getOrigin() { this = TPythonFunctionObject(result) } - pragma[nomagic] - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - exists(Function func, ControlFlowNode rval, ControlFlowNode forigin | - func = this.getScope() and - callee.appliesToScope(func) - | - rval = func.getAReturnValueFlowNode() and - PointsToInternal::pointsTo(rval, callee, obj, forigin) and - origin = CfgOrigin::fromCfgNode(forigin) - ) - or - procedureReturnsNone(callee, obj, origin) - } + pragma[nomagic] + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + exists(Function func, ControlFlowNode rval, ControlFlowNode forigin | + func = this.getScope() and + callee.appliesToScope(func) + | + rval = func.getAReturnValueFlowNode() and + PointsToInternal::pointsTo(rval, callee, obj, forigin) and + origin = CfgOrigin::fromCfgNode(forigin) + ) + or + procedureReturnsNone(callee, obj, origin) + } - private predicate procedureReturnsNone( - PointsToContext callee, ObjectInternal obj, CfgOrigin origin - ) { - exists(Function func | - func = this.getScope() and - callee.appliesToScope(func) - | - PointsToInternal::reachableBlock(blockReturningNone(func), callee) and - obj = ObjectInternal::none_() and - origin = CfgOrigin::unknown() - ) - } + private predicate procedureReturnsNone( + PointsToContext callee, ObjectInternal obj, CfgOrigin origin + ) { + exists(Function func | + func = this.getScope() and + callee.appliesToScope(func) + | + PointsToInternal::reachableBlock(blockReturningNone(func), callee) and + obj = ObjectInternal::none_() and + origin = CfgOrigin::unknown() + ) + } - pragma[noinline] - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - this.getScope().isProcedure() and - obj = ObjectInternal::none_() and - origin = CfgOrigin::fromCfgNode(this.getScope().getEntryNode()) - } + pragma[noinline] + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + this.getScope().isProcedure() and + obj = ObjectInternal::none_() and + origin = CfgOrigin::fromCfgNode(this.getScope().getEntryNode()) + } - override predicate calleeAndOffset(Function scope, int paramOffset) { - scope = this.getScope() and paramOffset = 0 - } + override predicate calleeAndOffset(Function scope, int paramOffset) { + scope = this.getScope() and paramOffset = 0 + } - override string getName() { result = this.getScope().getName() } + override string getName() { result = this.getScope().getName() } - override boolean isDescriptor() { result = true } + override boolean isDescriptor() { result = true } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - any(ObjectInternal obj).binds(cls, _, this) and - value = this and - origin = CfgOrigin::fromCfgNode(this.getOrigin()) - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + any(ObjectInternal obj).binds(cls, _, this) and + value = this and + origin = CfgOrigin::fromCfgNode(this.getOrigin()) + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - value = TBoundMethod(instance, this) and origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + value = TBoundMethod(instance, this) and origin = CfgOrigin::unknown() + } - override CallNode getACall(PointsToContext ctx) { - PointsTo::pointsTo(result.getFunction(), ctx, this, _) - or - exists(BoundMethodObjectInternal bm | bm.getACall(ctx) = result and this = bm.getFunction()) - } + override CallNode getACall(PointsToContext ctx) { + PointsTo::pointsTo(result.getFunction(), ctx, this, _) + or + exists(BoundMethodObjectInternal bm | bm.getACall(ctx) = result and this = bm.getFunction()) + } - override NameNode getParameter(int n) { result.getNode() = this.getScope().getArg(n) } + override NameNode getParameter(int n) { result.getNode() = this.getScope().getArg(n) } - override NameNode getParameterByName(string name) { - result.getNode() = this.getScope().getArgByName(name) - } + override NameNode getParameterByName(string name) { + result.getNode() = this.getScope().getArgByName(name) + } - override predicate neverReturns() { InterProceduralPointsTo::neverReturns(this.getScope()) } + override predicate neverReturns() { InterProceduralPointsTo::neverReturns(this.getScope()) } - override predicate functionAndOffset(CallableObjectInternal function, int offset) { - function = this and offset = 0 - } + override predicate functionAndOffset(CallableObjectInternal function, int offset) { + function = this and offset = 0 + } - override predicate contextSensitiveCallee() { any() } + override predicate contextSensitiveCallee() { any() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } private BasicBlock blockReturningNone(Function func) { - exists(Return ret | - not exists(ret.getValue()) and - ret.getScope() = func and - result = ret.getAFlowNode().getBasicBlock() - ) + exists(Return ret | + not exists(ret.getValue()) and + ret.getScope() = func and + result = ret.getAFlowNode().getBasicBlock() + ) } /** Class representing built-in functions such as `len` or `print`. */ class BuiltinFunctionObjectInternal extends CallableObjectInternal, TBuiltinFunctionObject { - override Builtin getBuiltin() { this = TBuiltinFunctionObject(result) } + override Builtin getBuiltin() { this = TBuiltinFunctionObject(result) } - override string toString() { result = "Builtin-function " + this.getBuiltin().getName() } + override string toString() { result = "Builtin-function " + this.getBuiltin().getName() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override ObjectInternal getClass() { result = TBuiltinClassObject(this.getBuiltin().getClass()) } + override ObjectInternal getClass() { result = TBuiltinClassObject(this.getBuiltin().getClass()) } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - exists(Builtin func, BuiltinClassObjectInternal cls | - func = this.getBuiltin() and - func != Builtin::builtin("isinstance") and - func != Builtin::builtin("issubclass") and - func != Builtin::builtin("callable") and - cls = ObjectInternal::fromBuiltin(this.getReturnType()) - | - obj = TUnknownInstance(cls) - or - cls = ObjectInternal::noneType() and obj = ObjectInternal::none_() - or - cls = ObjectInternal::builtin("bool") and obj = ObjectInternal::bool(_) - ) and - origin = CfgOrigin::unknown() - or - this.returnTypeUnknown() and - obj = ObjectInternal::unknown() and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + exists(Builtin func, BuiltinClassObjectInternal cls | + func = this.getBuiltin() and + func != Builtin::builtin("isinstance") and + func != Builtin::builtin("issubclass") and + func != Builtin::builtin("callable") and + cls = ObjectInternal::fromBuiltin(this.getReturnType()) + | + obj = TUnknownInstance(cls) + or + cls = ObjectInternal::noneType() and obj = ObjectInternal::none_() + or + cls = ObjectInternal::builtin("bool") and obj = ObjectInternal::bool(_) + ) and + origin = CfgOrigin::unknown() + or + this.returnTypeUnknown() and + obj = ObjectInternal::unknown() and + origin = CfgOrigin::unknown() + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override string getName() { result = this.getBuiltin().getName() } + override string getName() { result = this.getBuiltin().getName() } - Builtin getReturnType() { - exists(Builtin func | - func = this.getBuiltin() and - result = getBuiltinFunctionReturnType(func) - ) - } + Builtin getReturnType() { + exists(Builtin func | + func = this.getBuiltin() and + result = getBuiltinFunctionReturnType(func) + ) + } - private predicate returnTypeUnknown() { - exists(Builtin func | - func = this.getBuiltin() and - not exists(getBuiltinFunctionReturnType(func)) - ) - } + private predicate returnTypeUnknown() { + exists(Builtin func | + func = this.getBuiltin() and + not exists(getBuiltinFunctionReturnType(func)) + ) + } - override Function getScope() { none() } + override Function getScope() { none() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - override CallNode getACall(PointsToContext ctx) { - PointsTo::pointsTo(result.getFunction(), ctx, this, _) - } + override CallNode getACall(PointsToContext ctx) { + PointsTo::pointsTo(result.getFunction(), ctx, this, _) + } - override NameNode getParameter(int n) { none() } + override NameNode getParameter(int n) { none() } - override NameNode getParameterByName(string name) { none() } + override NameNode getParameterByName(string name) { none() } - override predicate neverReturns() { - exists(ModuleObjectInternal sys | - sys.getName() = "sys" and - sys.attribute("exit", this, _) - ) - } + override predicate neverReturns() { + exists(ModuleObjectInternal sys | + sys.getName() = "sys" and + sys.attribute("exit", this, _) + ) + } - override predicate functionAndOffset(CallableObjectInternal function, int offset) { - function = this and offset = 0 - } + override predicate functionAndOffset(CallableObjectInternal function, int offset) { + function = this and offset = 0 + } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } private Builtin getBuiltinFunctionReturnType(Builtin func) { - /* Enumerate the types of a few builtin functions, that the CPython analysis misses. */ - func = Builtin::builtin("hex") and result = Builtin::special("str") + /* Enumerate the types of a few builtin functions, that the CPython analysis misses. */ + func = Builtin::builtin("hex") and result = Builtin::special("str") + or + func = Builtin::builtin("oct") and result = Builtin::special("str") + or + func = Builtin::builtin("intern") and result = Builtin::special("str") + or + func = Builtin::builtin("__import__") and result = Builtin::special("ModuleType") + or + /* Fix a few minor inaccuracies in the CPython analysis */ + ext_rettype(func, result) and + not ( + func = Builtin::builtin("__import__") or - func = Builtin::builtin("oct") and result = Builtin::special("str") + func = Builtin::builtin("compile") and result = Builtin::special("NoneType") or - func = Builtin::builtin("intern") and result = Builtin::special("str") + func = Builtin::builtin("sum") or - func = Builtin::builtin("__import__") and result = Builtin::special("ModuleType") - or - /* Fix a few minor inaccuracies in the CPython analysis */ - ext_rettype(func, result) and - not ( - func = Builtin::builtin("__import__") - or - func = Builtin::builtin("compile") and result = Builtin::special("NoneType") - or - func = Builtin::builtin("sum") - or - func = Builtin::builtin("filter") - ) + func = Builtin::builtin("filter") + ) } /** Class representing methods of built-in classes (otherwise known as method-descriptors) such as `list.append`. */ class BuiltinMethodObjectInternal extends CallableObjectInternal, TBuiltinMethodObject { - override Builtin getBuiltin() { this = TBuiltinMethodObject(result) } + override Builtin getBuiltin() { this = TBuiltinMethodObject(result) } - override string toString() { result = "builtin method " + this.getBuiltin().getName() } + override string toString() { result = "builtin method " + this.getBuiltin().getName() } - override ObjectInternal getClass() { result = TBuiltinClassObject(this.getBuiltin().getClass()) } + override ObjectInternal getClass() { result = TBuiltinClassObject(this.getBuiltin().getClass()) } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - exists(Builtin func, BuiltinClassObjectInternal cls | - func = this.getBuiltin() and - cls = ObjectInternal::fromBuiltin(this.getReturnType()) - | - obj = TUnknownInstance(cls) - or - cls = ObjectInternal::noneType() and obj = ObjectInternal::none_() - or - cls = ObjectInternal::builtin("bool") and obj = ObjectInternal::bool(_) - ) and - origin = CfgOrigin::unknown() - or - this.returnTypeUnknown() and - obj = ObjectInternal::unknown() and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + exists(Builtin func, BuiltinClassObjectInternal cls | + func = this.getBuiltin() and + cls = ObjectInternal::fromBuiltin(this.getReturnType()) + | + obj = TUnknownInstance(cls) + or + cls = ObjectInternal::noneType() and obj = ObjectInternal::none_() + or + cls = ObjectInternal::builtin("bool") and obj = ObjectInternal::bool(_) + ) and + origin = CfgOrigin::unknown() + or + this.returnTypeUnknown() and + obj = ObjectInternal::unknown() and + origin = CfgOrigin::unknown() + } - Builtin getReturnType() { - /* If we have a record of the return type in our stubs, use that. */ - exists(Builtin func | func = this.getBuiltin() | ext_rettype(func, result)) - } + Builtin getReturnType() { + /* If we have a record of the return type in our stubs, use that. */ + exists(Builtin func | func = this.getBuiltin() | ext_rettype(func, result)) + } - private predicate returnTypeUnknown() { - exists(Builtin func | func = this.getBuiltin() | not ext_rettype(func, _)) - } + private predicate returnTypeUnknown() { + exists(Builtin func | func = this.getBuiltin() | not ext_rettype(func, _)) + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override string getName() { result = this.getBuiltin().getName() } + override string getName() { result = this.getBuiltin().getName() } - override Function getScope() { none() } + override Function getScope() { none() } - override boolean isDescriptor() { result = true } + override boolean isDescriptor() { result = true } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - any(ObjectInternal obj).binds(cls, _, this) and - value = this and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + any(ObjectInternal obj).binds(cls, _, this) and + value = this and + origin = CfgOrigin::unknown() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - value = TBoundMethod(instance, this) and origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + value = TBoundMethod(instance, this) and origin = CfgOrigin::unknown() + } - override CallNode getACall(PointsToContext ctx) { - PointsTo::pointsTo(result.getFunction(), ctx, this, _) - } + override CallNode getACall(PointsToContext ctx) { + PointsTo::pointsTo(result.getFunction(), ctx, this, _) + } - override NameNode getParameter(int n) { none() } + override NameNode getParameter(int n) { none() } - override NameNode getParameterByName(string name) { none() } + override NameNode getParameterByName(string name) { none() } - override predicate neverReturns() { none() } + override predicate neverReturns() { none() } - override predicate functionAndOffset(CallableObjectInternal function, int offset) { - function = this and offset = 0 - } + override predicate functionAndOffset(CallableObjectInternal function, int offset) { + function = this and offset = 0 + } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** @@ -386,89 +386,89 @@ class BuiltinMethodObjectInternal extends CallableObjectInternal, TBuiltinMethod * is the same and we treat them identically. */ class BoundMethodObjectInternal extends CallableObjectInternal, TBoundMethod { - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - CallableObjectInternal getFunction() { this = TBoundMethod(_, result) } + CallableObjectInternal getFunction() { this = TBoundMethod(_, result) } - ObjectInternal getSelf() { this = TBoundMethod(result, _) } + ObjectInternal getSelf() { this = TBoundMethod(result, _) } - override string toString() { - result = "Method(" + this.getFunction() + ", " + this.getSelf() + ")" - } + override string toString() { + result = "Method(" + this.getFunction() + ", " + this.getSelf() + ")" + } - override ObjectInternal getClass() { - result = TBuiltinClassObject(Builtin::special("MethodType")) - } + override ObjectInternal getClass() { + result = TBuiltinClassObject(Builtin::special("MethodType")) + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - this.getFunction().callResult(callee, obj, origin) - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + this.getFunction().callResult(callee, obj, origin) + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - this.getFunction().callResult(obj, origin) - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + this.getFunction().callResult(obj, origin) + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { - this.getFunction().calleeAndOffset(scope, paramOffset - 1) - } + override predicate calleeAndOffset(Function scope, int paramOffset) { + this.getFunction().calleeAndOffset(scope, paramOffset - 1) + } - override string getName() { result = this.getFunction().getName() } + override string getName() { result = this.getFunction().getName() } - override Function getScope() { result = this.getFunction().getScope() } + override Function getScope() { result = this.getFunction().getScope() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - override CallNode getACall(PointsToContext ctx) { - PointsTo::pointsTo(result.getFunction(), ctx, this, _) - } + override CallNode getACall(PointsToContext ctx) { + PointsTo::pointsTo(result.getFunction(), ctx, this, _) + } - /** Gets the parameter node that will be used for `self`. */ - NameNode getSelfParameter() { result = this.getFunction().getParameter(0) } + /** Gets the parameter node that will be used for `self`. */ + NameNode getSelfParameter() { result = this.getFunction().getParameter(0) } - override NameNode getParameter(int n) { - result = this.getFunction().getParameter(n + 1) and - // don't return the parameter for `self` at `n = -1` - n >= 0 - } + override NameNode getParameter(int n) { + result = this.getFunction().getParameter(n + 1) and + // don't return the parameter for `self` at `n = -1` + n >= 0 + } - /** - * Gets the `name`d parameter node of this callable. - * Will not return the parameter node for `self`, instead use `getSelfParameter`. - */ - override NameNode getParameterByName(string name) { - result = this.getFunction().getParameterByName(name) and - not result = this.getSelfParameter() - } + /** + * Gets the `name`d parameter node of this callable. + * Will not return the parameter node for `self`, instead use `getSelfParameter`. + */ + override NameNode getParameterByName(string name) { + result = this.getFunction().getParameterByName(name) and + not result = this.getSelfParameter() + } - override predicate neverReturns() { this.getFunction().neverReturns() } + override predicate neverReturns() { this.getFunction().neverReturns() } - override predicate functionAndOffset(CallableObjectInternal function, int offset) { - function = this.getFunction() and offset = 1 - or - function = this and offset = 0 - } + override predicate functionAndOffset(CallableObjectInternal function, int offset) { + function = this.getFunction() and offset = 1 + or + function = this and offset = 0 + } - override predicate useOriginAsLegacyObject() { any() } + override predicate useOriginAsLegacyObject() { any() } - override predicate contextSensitiveCallee() { this.getFunction().contextSensitiveCallee() } + override predicate contextSensitiveCallee() { this.getFunction().contextSensitiveCallee() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } diff --git a/python/ql/src/semmle/python/objects/Classes.qll b/python/ql/src/semmle/python/objects/Classes.qll index b509268a80a..ec45772bccc 100644 --- a/python/ql/src/semmle/python/objects/Classes.qll +++ b/python/ql/src/semmle/python/objects/Classes.qll @@ -8,377 +8,377 @@ private import semmle.python.types.Builtins /** Class representing classes */ abstract class ClassObjectInternal extends ObjectInternal { - override string getName() { result = this.getClassDeclaration().getName() } + override string getName() { result = this.getClassDeclaration().getName() } - /** - * Holds if this is a class whose instances we treat specially, rather than as a generic instance. - * For example, `type` or `int`. - */ - boolean isSpecial() { result = Types::getMro(this).containsSpecial() } + /** + * Holds if this is a class whose instances we treat specially, rather than as a generic instance. + * For example, `type` or `int`. + */ + boolean isSpecial() { result = Types::getMro(this).containsSpecial() } - /** - * Looks up the attribute `name` on this class. - * Note that this may be different from `this.attr(name)`. - * For example given the class: - * ```class C: - * @classmethod - * def f(cls): pass - * ``` - * `this.lookup("f")` is equivalent to `C.__dict__['f']`, which is the class-method - * whereas - * `this.attr("f") is equivalent to `C.f`, which is a bound-method. - */ - abstract predicate lookup(string name, ObjectInternal value, CfgOrigin origin); + /** + * Looks up the attribute `name` on this class. + * Note that this may be different from `this.attr(name)`. + * For example given the class: + * ```class C: + * @classmethod + * def f(cls): pass + * ``` + * `this.lookup("f")` is equivalent to `C.__dict__['f']`, which is the class-method + * whereas + * `this.attr("f") is equivalent to `C.f`, which is a bound-method. + */ + abstract predicate lookup(string name, ObjectInternal value, CfgOrigin origin); - /** Holds if this is a subclass of the `Iterable` abstract base class. */ - boolean isIterableSubclass() { - this = ObjectInternal::builtin("list") and result = true - or - this = ObjectInternal::builtin("set") and result = true - or - this = ObjectInternal::builtin("dict") and result = true - or - this != ObjectInternal::builtin("list") and - this != ObjectInternal::builtin("set") and - this != ObjectInternal::builtin("dict") and - result = false - } + /** Holds if this is a subclass of the `Iterable` abstract base class. */ + boolean isIterableSubclass() { + this = ObjectInternal::builtin("list") and result = true + or + this = ObjectInternal::builtin("set") and result = true + or + this = ObjectInternal::builtin("dict") and result = true + or + this != ObjectInternal::builtin("list") and + this != ObjectInternal::builtin("set") and + this != ObjectInternal::builtin("dict") and + result = false + } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - instance = this and - PointsToInternal::attributeRequired(this, name) and - this.lookup(name, descriptor, _) and - descriptor.isDescriptor() = true - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + instance = this and + PointsToInternal::attributeRequired(this, name) and + this.lookup(name, descriptor, _) and + descriptor.isDescriptor() = true + } - /** Approximation to descriptor protocol, skipping meta-descriptor protocol */ - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - exists(ObjectInternal descriptor, CfgOrigin desc_origin | - this.lookup(name, descriptor, desc_origin) - | - descriptor.isDescriptor() = false and - value = descriptor and - origin = desc_origin - or - descriptor.isDescriptor() = true and - descriptor.descriptorGetClass(this, value, origin) - ) - } + /** Approximation to descriptor protocol, skipping meta-descriptor protocol */ + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + exists(ObjectInternal descriptor, CfgOrigin desc_origin | + this.lookup(name, descriptor, desc_origin) + | + descriptor.isDescriptor() = false and + value = descriptor and + origin = desc_origin + or + descriptor.isDescriptor() = true and + descriptor.descriptorGetClass(this, value, origin) + ) + } - override int length() { none() } + override int length() { none() } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override boolean isClass() { result = true } + override boolean isClass() { result = true } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /* Classes aren't usually iterable, but can e.g. Enums */ - override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } + /* Classes aren't usually iterable, but can e.g. Enums */ + override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } - override predicate hasAttribute(string name) { - this.getClassDeclaration().declaresAttribute(name) - or - Types::getBase(this, _).hasAttribute(name) - } + override predicate hasAttribute(string name) { + this.getClassDeclaration().declaresAttribute(name) + or + Types::getBase(this, _).hasAttribute(name) + } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** Class representing Python source classes */ class PythonClassObjectInternal extends ClassObjectInternal, TPythonClassObject { - /** Gets the scope for this Python class */ - Class getScope() { - exists(ClassExpr expr | - this = TPythonClassObject(expr.getAFlowNode()) and - result = expr.getInnerScope() - ) - } + /** Gets the scope for this Python class */ + Class getScope() { + exists(ClassExpr expr | + this = TPythonClassObject(expr.getAFlowNode()) and + result = expr.getInnerScope() + ) + } - override string toString() { result = "class " + this.getScope().getName() } + override string toString() { result = "class " + this.getScope().getName() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - this = TPythonClassObject(node) and context.appliesTo(node) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + this = TPythonClassObject(node) and context.appliesTo(node) + } - override ClassDecl getClassDeclaration() { this = TPythonClassObject(result) } + override ClassDecl getClassDeclaration() { this = TPythonClassObject(result) } - override ObjectInternal getClass() { result = Types::getMetaClass(this) } + override ObjectInternal getClass() { result = Types::getMetaClass(this) } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { this = TPythonClassObject(result) } + override ControlFlowNode getOrigin() { this = TPythonClassObject(result) } - override predicate calleeAndOffset(Function scope, int paramOffset) { - exists(PythonFunctionObjectInternal init | - this.lookup("__init__", init, _) and - init.calleeAndOffset(scope, paramOffset - 1) - ) - } + override predicate calleeAndOffset(Function scope, int paramOffset) { + exists(PythonFunctionObjectInternal init | + this.lookup("__init__", init, _) and + init.calleeAndOffset(scope, paramOffset - 1) + ) + } - override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { - Types::getMro(this).lookup(name, value, origin) - } + override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { + Types::getMro(this).lookup(name, value, origin) + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // Handled by Instance classes. - none() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // Handled by Instance classes. + none() + } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override predicate functionAndOffset(CallableObjectInternal function, int offset) { - this.lookup("__init__", function, _) and offset = 1 - } + override predicate functionAndOffset(CallableObjectInternal function, int offset) { + this.lookup("__init__", function, _) and offset = 1 + } } /** Class representing built-in classes, except `type` */ class BuiltinClassObjectInternal extends ClassObjectInternal, TBuiltinClassObject { - override Builtin getBuiltin() { this = TBuiltinClassObject(result) } + override Builtin getBuiltin() { this = TBuiltinClassObject(result) } - override string toString() { result = "builtin-class " + this.getBuiltin().getName() } + override string toString() { result = "builtin-class " + this.getBuiltin().getName() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override ClassDecl getClassDeclaration() { this = TBuiltinClassObject(result) } + override ClassDecl getClassDeclaration() { this = TBuiltinClassObject(result) } - override ObjectInternal getClass() { - result = TBuiltinClassObject(this.getBuiltin().getClass()) - or - this.getBuiltin().getClass() = Builtin::special("type") and - result = TType() - } + override ObjectInternal getClass() { + result = TBuiltinClassObject(this.getBuiltin().getClass()) + or + this.getBuiltin().getClass() = Builtin::special("type") and + result = TType() + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { - Types::getMro(this).lookup(name, value, origin) - } + override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { + Types::getMro(this).lookup(name, value, origin) + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // Handled by Instance classes. - none() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // Handled by Instance classes. + none() + } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } } /** A class representing an unknown class */ class UnknownClassInternal extends ClassObjectInternal, TUnknownClass { - override string toString() { result = "Unknown class" } + override string toString() { result = "Unknown class" } - override ClassDecl getClassDeclaration() { result = Builtin::unknownType() } + override ClassDecl getClassDeclaration() { result = Builtin::unknownType() } - override ObjectInternal getClass() { result = this } + override ObjectInternal getClass() { result = this } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override Builtin getBuiltin() { result = Builtin::unknownType() } + override Builtin getBuiltin() { result = Builtin::unknownType() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { none() } + override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { none() } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } } /** A class representing the built-in class `type`. */ class TypeInternal extends ClassObjectInternal, TType { - override string toString() { result = "builtin-class type" } + override string toString() { result = "builtin-class type" } - override ClassDecl getClassDeclaration() { result = Builtin::special("type") } + override ClassDecl getClassDeclaration() { result = Builtin::special("type") } - override ObjectInternal getClass() { result = this } + override ObjectInternal getClass() { result = this } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override Builtin getBuiltin() { result = Builtin::special("type") } + override Builtin getBuiltin() { result = Builtin::special("type") } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { - Types::getMro(this).lookup(name, value, origin) - } + override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { + Types::getMro(this).lookup(name, value, origin) + } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } } /** A class representing a dynamically created class `type(name, *args, **kwargs)`. */ class DynamicallyCreatedClass extends ClassObjectInternal, TDynamicClass { - override string toString() { result = this.getOrigin().getNode().toString() } + override string toString() { result = this.getOrigin().getNode().toString() } - override ObjectInternal getClass() { this = TDynamicClass(_, result, _) } + override ObjectInternal getClass() { this = TDynamicClass(_, result, _) } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { - exists(ClassObjectInternal decl | decl = Types::getMro(this).findDeclaringClass(name) | - Types::declaredAttribute(decl, name, value, origin) - ) - } + override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { + exists(ClassObjectInternal decl | decl = Types::getMro(this).findDeclaringClass(name) | + Types::declaredAttribute(decl, name, value, origin) + ) + } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { this = TDynamicClass(result, _, _) } + override ControlFlowNode getOrigin() { this = TDynamicClass(result, _, _) } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - this = TDynamicClass(node, _, context) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + this = TDynamicClass(node, _, context) + } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } } class SubscriptedTypeInternal extends ObjectInternal, TSubscriptedType { - ObjectInternal getGeneric() { this = TSubscriptedType(result, _) } + ObjectInternal getGeneric() { this = TSubscriptedType(result, _) } - ObjectInternal getSpecializer() { this = TSubscriptedType(_, result) } + ObjectInternal getSpecializer() { this = TSubscriptedType(_, result) } - override string getName() { result = this.getGeneric().getName() } + override string getName() { result = this.getGeneric().getName() } - override string toString() { - result = this.getGeneric().toString() + "[" + this.getSpecializer().toString() + "]" - } + override string toString() { + result = this.getGeneric().toString() + "[" + this.getSpecializer().toString() + "]" + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - exists(ObjectInternal generic, ObjectInternal index | - this = TSubscriptedType(generic, index) and - Expressions::subscriptPartsPointsTo(node, context, generic, index) - ) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + exists(ObjectInternal generic, ObjectInternal index | + this = TSubscriptedType(generic, index) and + Expressions::subscriptPartsPointsTo(node, context, generic, index) + ) + } - /** Gets the class declaration for this object, if it is a class with a declaration. */ - override ClassDecl getClassDeclaration() { result = this.getGeneric().getClassDeclaration() } + /** Gets the class declaration for this object, if it is a class with a declaration. */ + override ClassDecl getClassDeclaration() { result = this.getGeneric().getClassDeclaration() } - /** True if this "object" is a class. That is, its class inherits from `type` */ - override boolean isClass() { result = true } + /** True if this "object" is a class. That is, its class inherits from `type` */ + override boolean isClass() { result = true } - override ObjectInternal getClass() { result = this.getGeneric().getClass() } + override ObjectInternal getClass() { result = this.getGeneric().getClass() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - override predicate attributesUnknown() { none() } + override predicate attributesUnknown() { none() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { none() } + override int length() { none() } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /* Classes aren't usually iterable, but can e.g. Enums */ - override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } + /* Classes aren't usually iterable, but can e.g. Enums */ + override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } - override predicate isNotSubscriptedType() { none() } + override predicate isNotSubscriptedType() { none() } } diff --git a/python/ql/src/semmle/python/objects/Constants.qll b/python/ql/src/semmle/python/objects/Constants.qll index 8cc0911af53..d2ba2f44655 100644 --- a/python/ql/src/semmle/python/objects/Constants.qll +++ b/python/ql/src/semmle/python/objects/Constants.qll @@ -12,290 +12,290 @@ private import semmle.python.types.Builtins * well as strings and integers. */ abstract class ConstantObjectInternal extends ObjectInternal { - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - // Constants aren't callable - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + // Constants aren't callable + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // Constants aren't callable - none() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // Constants aren't callable + none() + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - PointsToInternal::attributeRequired(this, name) and - exists(ObjectInternal cls_attr, CfgOrigin attr_orig | - this.getClass().(ClassObjectInternal).lookup(name, cls_attr, attr_orig) and - cls_attr.isDescriptor() = true and - cls_attr.descriptorGetInstance(this, value, origin) - ) - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + PointsToInternal::attributeRequired(this, name) and + exists(ObjectInternal cls_attr, CfgOrigin attr_orig | + this.getClass().(ClassObjectInternal).lookup(name, cls_attr, attr_orig) and + cls_attr.isDescriptor() = true and + cls_attr.descriptorGetInstance(this, value, origin) + ) + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - exists(ClassObjectInternal cls | - receiver_type(_, name, this, cls) and - cls.lookup(name, descriptor, _) and - descriptor.isDescriptor() = true - ) and - this = instance - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + exists(ClassObjectInternal cls | + receiver_type(_, name, this, cls) and + cls.lookup(name, descriptor, _) and + descriptor.isDescriptor() = true + ) and + this = instance + } - override string getName() { none() } + override string getName() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /** Gets an AST literal with the same value as this object */ - abstract ImmutableLiteral getLiteral(); + /** Gets an AST literal with the same value as this object */ + abstract ImmutableLiteral getLiteral(); - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } pragma[nomagic] private boolean callToBool(CallNode call, PointsToContext context) { - PointsToInternal::pointsTo(call.getFunction(), context, ClassValue::bool(), _) and - exists(ObjectInternal arg | - PointsToInternal::pointsTo(call.getArg(0), context, arg, _) and - arg.booleanValue() = result - ) + PointsToInternal::pointsTo(call.getFunction(), context, ClassValue::bool(), _) and + exists(ObjectInternal arg | + PointsToInternal::pointsTo(call.getArg(0), context, arg, _) and + arg.booleanValue() = result + ) } abstract private class BooleanObjectInternal extends ConstantObjectInternal { - override ObjectInternal getClass() { result = ClassValue::bool() } + override ObjectInternal getClass() { result = ClassValue::bool() } - override int length() { none() } + override int length() { none() } - override string strValue() { none() } + override string strValue() { none() } - /* Booleans aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* Booleans aren't iterable */ + override ObjectInternal getIterNext() { none() } - override ImmutableLiteral getLiteral() { - result.(BooleanLiteral).booleanValue() = this.booleanValue() - } + override ImmutableLiteral getLiteral() { + result.(BooleanLiteral).booleanValue() = this.booleanValue() + } } private class TrueObjectInternal extends BooleanObjectInternal, TTrue { - override string toString() { result = "bool True" } + override string toString() { result = "bool True" } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - node.(NameNode).getId() = "True" and context.appliesTo(node) - or - callToBool(node, context) = true - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + node.(NameNode).getId() = "True" and context.appliesTo(node) + or + callToBool(node, context) = true + } - override int intValue() { result = 1 } + override int intValue() { result = 1 } - override Builtin getBuiltin() { result = Builtin::special("True") } + override Builtin getBuiltin() { result = Builtin::special("True") } } private class FalseObjectInternal extends BooleanObjectInternal, TFalse { - override string toString() { result = "bool False" } + override string toString() { result = "bool False" } - override boolean booleanValue() { result = false } + override boolean booleanValue() { result = false } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - node.(NameNode).getId() = "False" and context.appliesTo(node) - or - callToBool(node, context) = false - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + node.(NameNode).getId() = "False" and context.appliesTo(node) + or + callToBool(node, context) = false + } - override int intValue() { result = 0 } + override int intValue() { result = 0 } - override Builtin getBuiltin() { result = Builtin::special("False") } + override Builtin getBuiltin() { result = Builtin::special("False") } } private class NoneObjectInternal extends ConstantObjectInternal, TNone { - override string toString() { result = "None" } + override string toString() { result = "None" } - override boolean booleanValue() { result = false } + override boolean booleanValue() { result = false } - override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("NoneType")) } + override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("NoneType")) } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - node.(NameNode).getId() = "None" and context.appliesTo(node) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + node.(NameNode).getId() = "None" and context.appliesTo(node) + } - override Builtin getBuiltin() { result = Builtin::special("None") } + override Builtin getBuiltin() { result = Builtin::special("None") } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override int length() { none() } + override int length() { none() } - /* None isn't iterable */ - override ObjectInternal getIterNext() { none() } + /* None isn't iterable */ + override ObjectInternal getIterNext() { none() } - override ImmutableLiteral getLiteral() { result instanceof None } + override ImmutableLiteral getLiteral() { result instanceof None } } class IntObjectInternal extends ConstantObjectInternal, TInt { - override string toString() { result = "int " + this.intValue().toString() } + override string toString() { result = "int " + this.intValue().toString() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - context.appliesTo(node) and - node.getNode().(IntegerLiteral).getValue() = this.intValue() - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + context.appliesTo(node) and + node.getNode().(IntegerLiteral).getValue() = this.intValue() + } - override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("int")) } + override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("int")) } - override Builtin getBuiltin() { result.intValue() = this.intValue() } + override Builtin getBuiltin() { result.intValue() = this.intValue() } - override int intValue() { this = TInt(result) } + override int intValue() { this = TInt(result) } - override string strValue() { none() } + override string strValue() { none() } - override boolean booleanValue() { - this.intValue() = 0 and result = false - or - this.intValue() != 0 and result = true - } + override boolean booleanValue() { + this.intValue() = 0 and result = false + or + this.intValue() != 0 and result = true + } - override int length() { none() } + override int length() { none() } - /* ints aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* ints aren't iterable */ + override ObjectInternal getIterNext() { none() } - override ImmutableLiteral getLiteral() { - result.(IntegerLiteral).getValue() = this.intValue() - or - result.(NegativeIntegerLiteral).getOperand().(IntegerLiteral).getValue() = -this.intValue() - } + override ImmutableLiteral getLiteral() { + result.(IntegerLiteral).getValue() = this.intValue() + or + result.(NegativeIntegerLiteral).getOperand().(IntegerLiteral).getValue() = -this.intValue() + } } class FloatObjectInternal extends ConstantObjectInternal, TFloat { - override string toString() { - if this.floatValue() = this.floatValue().floor() - then result = "float " + this.floatValue().floor().toString() + ".0" - else result = "float " + this.floatValue().toString() - } + override string toString() { + if this.floatValue() = this.floatValue().floor() + then result = "float " + this.floatValue().floor().toString() + ".0" + else result = "float " + this.floatValue().toString() + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - context.appliesTo(node) and - node.getNode().(FloatLiteral).getValue() = this.floatValue() - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + context.appliesTo(node) and + node.getNode().(FloatLiteral).getValue() = this.floatValue() + } - override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("float")) } + override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("float")) } - override Builtin getBuiltin() { result.floatValue() = this.floatValue() } + override Builtin getBuiltin() { result.floatValue() = this.floatValue() } - float floatValue() { this = TFloat(result) } + float floatValue() { this = TFloat(result) } - override int intValue() { this = TFloat(result) } + override int intValue() { this = TFloat(result) } - override string strValue() { none() } + override string strValue() { none() } - override boolean booleanValue() { - this.floatValue() = 0.0 and result = false - or - this.floatValue() != 0.0 and result = true - } + override boolean booleanValue() { + this.floatValue() = 0.0 and result = false + or + this.floatValue() != 0.0 and result = true + } - override int length() { none() } + override int length() { none() } - /* floats aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* floats aren't iterable */ + override ObjectInternal getIterNext() { none() } - override ImmutableLiteral getLiteral() { result.(FloatLiteral).getValue() = this.floatValue() } + override ImmutableLiteral getLiteral() { result.(FloatLiteral).getValue() = this.floatValue() } } class UnicodeObjectInternal extends ConstantObjectInternal, TUnicode { - override string toString() { result = "'" + this.strValue() + "'" } + override string toString() { result = "'" + this.strValue() + "'" } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - context.appliesTo(node) and - node.getNode().(StrConst).getText() = this.strValue() and - node.getNode().(StrConst).isUnicode() - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + context.appliesTo(node) and + node.getNode().(StrConst).getText() = this.strValue() and + node.getNode().(StrConst).isUnicode() + } - override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("unicode")) } + override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("unicode")) } - override Builtin getBuiltin() { - result.(Builtin).strValue() = this.strValue() and - result.getClass() = Builtin::special("unicode") - } + override Builtin getBuiltin() { + result.(Builtin).strValue() = this.strValue() and + result.getClass() = Builtin::special("unicode") + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { this = TUnicode(result) } + override string strValue() { this = TUnicode(result) } - override boolean booleanValue() { - this.strValue() = "" and result = false - or - this.strValue() != "" and result = true - } + override boolean booleanValue() { + this.strValue() = "" and result = false + or + this.strValue() != "" and result = true + } - override int length() { result = this.strValue().length() } + override int length() { result = this.strValue().length() } - override ObjectInternal getIterNext() { result = TUnknownInstance(this.getClass()) } + override ObjectInternal getIterNext() { result = TUnknownInstance(this.getClass()) } - override ImmutableLiteral getLiteral() { result.(Unicode).getText() = this.strValue() } + override ImmutableLiteral getLiteral() { result.(Unicode).getText() = this.strValue() } } class BytesObjectInternal extends ConstantObjectInternal, TBytes { - override string toString() { result = "'" + this.strValue() + "'" } + override string toString() { result = "'" + this.strValue() + "'" } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - context.appliesTo(node) and - node.getNode().(StrConst).getText() = this.strValue() and - not node.getNode().(StrConst).isUnicode() - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + context.appliesTo(node) and + node.getNode().(StrConst).getText() = this.strValue() and + not node.getNode().(StrConst).isUnicode() + } - override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("bytes")) } + override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("bytes")) } - override Builtin getBuiltin() { - result.(Builtin).strValue() = this.strValue() and - result.getClass() = Builtin::special("bytes") - } + override Builtin getBuiltin() { + result.(Builtin).strValue() = this.strValue() and + result.getClass() = Builtin::special("bytes") + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { this = TBytes(result) } + override string strValue() { this = TBytes(result) } - override boolean booleanValue() { - this.strValue() = "" and result = false - or - this.strValue() != "" and result = true - } + override boolean booleanValue() { + this.strValue() = "" and result = false + or + this.strValue() != "" and result = true + } - override int length() { result = this.strValue().length() } + override int length() { result = this.strValue().length() } - override ObjectInternal getIterNext() { result = TUnknownInstance(this.getClass()) } + override ObjectInternal getIterNext() { result = TUnknownInstance(this.getClass()) } - override ImmutableLiteral getLiteral() { result.(Bytes).getText() = this.strValue() } + override ImmutableLiteral getLiteral() { result.(Bytes).getText() = this.strValue() } } diff --git a/python/ql/src/semmle/python/objects/Descriptors.qll b/python/ql/src/semmle/python/objects/Descriptors.qll index ece746d8625..9a11a9fcae4 100644 --- a/python/ql/src/semmle/python/objects/Descriptors.qll +++ b/python/ql/src/semmle/python/objects/Descriptors.qll @@ -8,376 +8,376 @@ private import semmle.python.types.Builtins /** Class representing property objects in Python */ class PropertyInternal extends ObjectInternal, TProperty { - /** Gets the name of this property */ - override string getName() { result = this.getGetter().getName() } + /** Gets the name of this property */ + override string getName() { result = this.getGetter().getName() } - /** Gets the getter function of this property */ - CallableObjectInternal getGetter() { this = TProperty(_, _, result) } + /** Gets the getter function of this property */ + CallableObjectInternal getGetter() { this = TProperty(_, _, result) } - private CallNode getCallNode() { this = TProperty(result, _, _) } + private CallNode getCallNode() { this = TProperty(result, _, _) } - /** Gets the setter function of this property */ - CallableObjectInternal getSetter() { - // @x.setter - exists(CallNode call, AttrNode setter | - call.getFunction() = setter and - PointsToInternal::pointsTo(setter.getObject("setter"), this.getContext(), this, _) and - PointsToInternal::pointsTo(call.getArg(0), this.getContext(), result, _) - ) - or - // x = property(getter, setter, deleter) - exists(ControlFlowNode setter_arg | - setter_arg = getCallNode().getArg(1) or setter_arg = getCallNode().getArgByName("fset") - | - PointsToInternal::pointsTo(setter_arg, this.getContext(), result, _) - ) - } + /** Gets the setter function of this property */ + CallableObjectInternal getSetter() { + // @x.setter + exists(CallNode call, AttrNode setter | + call.getFunction() = setter and + PointsToInternal::pointsTo(setter.getObject("setter"), this.getContext(), this, _) and + PointsToInternal::pointsTo(call.getArg(0), this.getContext(), result, _) + ) + or + // x = property(getter, setter, deleter) + exists(ControlFlowNode setter_arg | + setter_arg = getCallNode().getArg(1) or setter_arg = getCallNode().getArgByName("fset") + | + PointsToInternal::pointsTo(setter_arg, this.getContext(), result, _) + ) + } - /** Gets the setter function of this property */ - CallableObjectInternal getDeleter() { - exists(CallNode call, AttrNode setter | - call.getFunction() = setter and - PointsToInternal::pointsTo(setter.getObject("deleter"), this.getContext(), this, _) and - PointsToInternal::pointsTo(call.getArg(0), this.getContext(), result, _) - ) - or - // x = property(getter, setter, deleter) - exists(ControlFlowNode deleter_arg | - deleter_arg = getCallNode().getArg(2) or deleter_arg = getCallNode().getArgByName("fdel") - | - PointsToInternal::pointsTo(deleter_arg, this.getContext(), result, _) - ) - } + /** Gets the setter function of this property */ + CallableObjectInternal getDeleter() { + exists(CallNode call, AttrNode setter | + call.getFunction() = setter and + PointsToInternal::pointsTo(setter.getObject("deleter"), this.getContext(), this, _) and + PointsToInternal::pointsTo(call.getArg(0), this.getContext(), result, _) + ) + or + // x = property(getter, setter, deleter) + exists(ControlFlowNode deleter_arg | + deleter_arg = getCallNode().getArg(2) or deleter_arg = getCallNode().getArgByName("fdel") + | + PointsToInternal::pointsTo(deleter_arg, this.getContext(), result, _) + ) + } - private Context getContext() { this = TProperty(_, result, _) } + private Context getContext() { this = TProperty(_, result, _) } - override string toString() { result = "property " + this.getName() } + override string toString() { result = "property " + this.getName() } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - this = TProperty(node, context, _) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + this = TProperty(node, context, _) + } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = ObjectInternal::property() } + override ObjectInternal getClass() { result = ObjectInternal::property() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { this = TProperty(result, _, _) } + override ControlFlowNode getOrigin() { this = TProperty(result, _, _) } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - value = TPropertySetterOrDeleter(this, name) and origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + value = TPropertySetterOrDeleter(this, name) and origin = CfgOrigin::unknown() + } - override predicate attributesUnknown() { none() } + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override boolean isDescriptor() { result = true } + override boolean isDescriptor() { result = true } - override int length() { none() } + override int length() { none() } - override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) { none() } + override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) { none() } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - any(ObjectInternal obj).binds(cls, _, this) and - value = this and - origin = CfgOrigin::fromCfgNode(this.getOrigin()) - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + any(ObjectInternal obj).binds(cls, _, this) and + value = this and + origin = CfgOrigin::fromCfgNode(this.getOrigin()) + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - /* - * Just give an unknown value for now. We could improve this, but it would mean - * changing Contexts to account for property accesses. - */ + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + /* + * Just give an unknown value for now. We could improve this, but it would mean + * changing Contexts to account for property accesses. + */ - exists(ClassObjectInternal cls, string name | - name = this.getName() and - receiver_type(_, name, instance, cls) and - cls.lookup(name, this, _) and - origin = CfgOrigin::unknown() and - value = ObjectInternal::unknown() - ) - } + exists(ClassObjectInternal cls, string name | + name = this.getName() and + receiver_type(_, name, instance, cls) and + cls.lookup(name, this, _) and + origin = CfgOrigin::unknown() and + value = ObjectInternal::unknown() + ) + } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /* Properties aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* Properties aren't iterable */ + override ObjectInternal getIterNext() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } private class PropertySetterOrDeleter extends ObjectInternal, TPropertySetterOrDeleter { - override string toString() { result = this.getProperty().toString() + "." + this.getName() } + override string toString() { result = this.getProperty().toString() + "." + this.getName() } - override string getName() { this = TPropertySetterOrDeleter(_, result) } + override string getName() { this = TPropertySetterOrDeleter(_, result) } - PropertyInternal getProperty() { this = TPropertySetterOrDeleter(result, _) } + PropertyInternal getProperty() { this = TPropertySetterOrDeleter(result, _) } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - exists(ControlFlowNode call | - obj = this.getProperty() and - obj = TProperty(call, _, _) and - origin = CfgOrigin::fromCfgNode(call) - ) - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + exists(ControlFlowNode call | + obj = this.getProperty() and + obj = TProperty(call, _, _) and + origin = CfgOrigin::fromCfgNode(call) + ) + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { - result = TBuiltinClassObject(Builtin::special("MethodType")) - } + override ObjectInternal getClass() { + result = TBuiltinClassObject(Builtin::special("MethodType")) + } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - override predicate attributesUnknown() { none() } + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override boolean isDescriptor() { result = true } + override boolean isDescriptor() { result = true } - override int length() { none() } + override int length() { none() } - override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) { none() } + override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override ObjectInternal getIterNext() { none() } + override ObjectInternal getIterNext() { none() } - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** A class representing classmethods in Python */ class ClassMethodObjectInternal extends ObjectInternal, TClassMethod { - override string toString() { result = "classmethod(" + this.getFunction() + ")" } + override string toString() { result = "classmethod(" + this.getFunction() + ")" } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - exists(CallableObjectInternal function | - this = TClassMethod(node, function) and - class_method(node, function, context) - ) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + exists(CallableObjectInternal function | + this = TClassMethod(node, function) and + class_method(node, function, context) + ) + } - /** Gets the function wrapped by this classmethod object */ - CallableObjectInternal getFunction() { this = TClassMethod(_, result) } + /** Gets the function wrapped by this classmethod object */ + CallableObjectInternal getFunction() { this = TClassMethod(_, result) } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = ObjectInternal::classMethod() } + override ObjectInternal getClass() { result = ObjectInternal::classMethod() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { this = TClassMethod(result, _) } + override ControlFlowNode getOrigin() { this = TClassMethod(result, _) } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - override predicate attributesUnknown() { none() } + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override boolean isDescriptor() { result = true } + override boolean isDescriptor() { result = true } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - value = TBoundMethod(cls, this.getFunction()) and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + value = TBoundMethod(cls, this.getFunction()) and + origin = CfgOrigin::unknown() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - any(ObjectInternal obj).binds(instance, _, this) and - value = TBoundMethod(instance.getClass(), this.getFunction()) and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + any(ObjectInternal obj).binds(instance, _, this) and + value = TBoundMethod(instance.getClass(), this.getFunction()) and + origin = CfgOrigin::unknown() + } - /** - * Holds if attribute lookup on this object may "bind" `cls` to `descriptor`. - * `cls` will always be a class as this is a classmethod. - * Here "bind" means that `instance` is passed to the `classmethod.__get__()` method - * at runtime. The term "bind" is used as this most likely results in a bound-method. - */ - pragma[noinline] - override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) { - descriptor = this.getFunction() and - exists(ObjectInternal instance | any(ObjectInternal obj).binds(instance, name, this) | - instance.isClass() = false and cls = instance.getClass() - or - instance.isClass() = true and cls = instance - ) - } + /** + * Holds if attribute lookup on this object may "bind" `cls` to `descriptor`. + * `cls` will always be a class as this is a classmethod. + * Here "bind" means that `instance` is passed to the `classmethod.__get__()` method + * at runtime. The term "bind" is used as this most likely results in a bound-method. + */ + pragma[noinline] + override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) { + descriptor = this.getFunction() and + exists(ObjectInternal instance | any(ObjectInternal obj).binds(instance, name, this) | + instance.isClass() = false and cls = instance.getClass() + or + instance.isClass() = true and cls = instance + ) + } - override int length() { none() } + override int length() { none() } - override string getName() { result = this.getFunction().getName() } + override string getName() { result = this.getFunction().getName() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /* Classmethods aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* Classmethods aren't iterable */ + override ObjectInternal getIterNext() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } class StaticMethodObjectInternal extends ObjectInternal, TStaticMethod { - override string toString() { result = "staticmethod()" } + override string toString() { result = "staticmethod()" } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - exists(CallableObjectInternal function | - this = TStaticMethod(node, function) and - static_method(node, function, context) - ) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + exists(CallableObjectInternal function | + this = TStaticMethod(node, function) and + static_method(node, function, context) + ) + } - CallableObjectInternal getFunction() { this = TStaticMethod(_, result) } + CallableObjectInternal getFunction() { this = TStaticMethod(_, result) } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = ObjectInternal::builtin("staticmethod") } + override ObjectInternal getClass() { result = ObjectInternal::builtin("staticmethod") } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { this = TStaticMethod(result, _) } + override ControlFlowNode getOrigin() { this = TStaticMethod(result, _) } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { - this.getFunction().calleeAndOffset(scope, paramOffset) - } + override predicate calleeAndOffset(Function scope, int paramOffset) { + this.getFunction().calleeAndOffset(scope, paramOffset) + } - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - override predicate attributesUnknown() { none() } + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override boolean isDescriptor() { result = true } + override boolean isDescriptor() { result = true } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - any(ObjectInternal obj).binds(cls, _, this) and - value = this.getFunction() and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + any(ObjectInternal obj).binds(cls, _, this) and + value = this.getFunction() and + origin = CfgOrigin::unknown() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - any(ObjectInternal obj).binds(instance, _, this) and - value = this.getFunction() and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + any(ObjectInternal obj).binds(instance, _, this) and + value = this.getFunction() and + origin = CfgOrigin::unknown() + } - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { none() } + override int length() { none() } - override string getName() { result = this.getFunction().getName() } + override string getName() { result = this.getFunction().getName() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /* Staticmethods aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* Staticmethods aren't iterable */ + override ObjectInternal getIterNext() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } diff --git a/python/ql/src/semmle/python/objects/Instances.qll b/python/ql/src/semmle/python/objects/Instances.qll index 396e26d8aad..0f29d16b447 100644 --- a/python/ql/src/semmle/python/objects/Instances.qll +++ b/python/ql/src/semmle/python/objects/Instances.qll @@ -8,59 +8,59 @@ private import semmle.python.types.Builtins /** A class representing instances */ abstract class InstanceObject extends ObjectInternal { - pragma[nomagic] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - exists(ObjectInternal cls_attr | this.classAttribute(name, cls_attr) | - /* - * If class attribute is not a descriptor, that usually means it is some sort of - * default value and likely overridden by an instance attribute. In that case - * use `unknown` to signal that an attribute exists but to avoid false positives - * f using the default value. - */ + pragma[nomagic] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + exists(ObjectInternal cls_attr | this.classAttribute(name, cls_attr) | + /* + * If class attribute is not a descriptor, that usually means it is some sort of + * default value and likely overridden by an instance attribute. In that case + * use `unknown` to signal that an attribute exists but to avoid false positives + * f using the default value. + */ - cls_attr.isDescriptor() = false and - value = ObjectInternal::unknown() and - origin = CfgOrigin::unknown() - or - cls_attr.isDescriptor() = true and cls_attr.descriptorGetInstance(this, value, origin) - ) - or - this.selfAttribute(name, value, origin) - } + cls_attr.isDescriptor() = false and + value = ObjectInternal::unknown() and + origin = CfgOrigin::unknown() + or + cls_attr.isDescriptor() = true and cls_attr.descriptorGetInstance(this, value, origin) + ) + or + this.selfAttribute(name, value, origin) + } - pragma[noinline] - private predicate classAttribute(string name, ObjectInternal cls_attr) { - PointsToInternal::attributeRequired(this, name) and - this.getClass().(ClassObjectInternal).lookup(name, cls_attr, _) - } + pragma[noinline] + private predicate classAttribute(string name, ObjectInternal cls_attr) { + PointsToInternal::attributeRequired(this, name) and + this.getClass().(ClassObjectInternal).lookup(name, cls_attr, _) + } - pragma[noinline] - private predicate selfAttribute(string name, ObjectInternal value, CfgOrigin origin) { - PointsToInternal::attributeRequired(this, name) and - exists(EssaVariable self, PythonFunctionObjectInternal init, Context callee | - this.initializer(init, callee) and - self_variable_reaching_init_exit(self) and - self.getScope() = init.getScope() and - AttributePointsTo::variableAttributePointsTo(self, callee, name, value, origin) - ) - } + pragma[noinline] + private predicate selfAttribute(string name, ObjectInternal value, CfgOrigin origin) { + PointsToInternal::attributeRequired(this, name) and + exists(EssaVariable self, PythonFunctionObjectInternal init, Context callee | + this.initializer(init, callee) and + self_variable_reaching_init_exit(self) and + self.getScope() = init.getScope() and + AttributePointsTo::variableAttributePointsTo(self, callee, name, value, origin) + ) + } - /** Holds if `init` in the context `callee` is the initializer of this instance */ - abstract predicate initializer(PythonFunctionObjectInternal init, Context callee); + /** Holds if `init` in the context `callee` is the initializer of this instance */ + abstract predicate initializer(PythonFunctionObjectInternal init, Context callee); - override string getName() { none() } + override string getName() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } + override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } private predicate self_variable_reaching_init_exit(EssaVariable self) { - BaseFlow::reaches_exit(self) and - self.getSourceVariable().(Variable).isSelf() and - self.getScope().getName() = "__init__" + BaseFlow::reaches_exit(self) and + self.getSourceVariable().(Variable).isSelf() and + self.getScope().getName() = "__init__" } /** @@ -68,421 +68,421 @@ private predicate self_variable_reaching_init_exit(EssaVariable self) { * For example the code `C()` would be a specific instance of `C`. */ class SpecificInstanceInternal extends TSpecificInstance, InstanceObject { - override string toString() { result = this.getOrigin().getNode().toString() } + override string toString() { result = this.getOrigin().getNode().toString() } - override boolean booleanValue() { - //result = this.getClass().instancesBooleanValue() - result = maybe() - } + override boolean booleanValue() { + //result = this.getClass().instancesBooleanValue() + result = maybe() + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - this = TSpecificInstance(node, _, context) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + this = TSpecificInstance(node, _, context) + } - /** Gets the class declaration for this object, if it is a declared class. */ - override ClassDecl getClassDeclaration() { none() } + /** Gets the class declaration for this object, if it is a declared class. */ + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override ObjectInternal getClass() { - exists(ClassObjectInternal cls, ClassDecl decl | - this = TSpecificInstance(_, cls, _) and - decl = cls.getClassDeclaration() - | - if decl.callReturnsInstance() then result = cls else result = TUnknownClass() - ) - } + override ObjectInternal getClass() { + exists(ClassObjectInternal cls, ClassDecl decl | + this = TSpecificInstance(_, cls, _) and + decl = cls.getClassDeclaration() + | + if decl.callReturnsInstance() then result = cls else result = TUnknownClass() + ) + } - /** - * Gets the `Builtin` for this object, if any. - * All objects (except unknown and undefined values) should return - * exactly one result for either this method or `getOrigin()`. - */ - override Builtin getBuiltin() { none() } + /** + * Gets the `Builtin` for this object, if any. + * All objects (except unknown and undefined values) should return + * exactly one result for either this method or `getOrigin()`. + */ + override Builtin getBuiltin() { none() } - /** - * Gets a control flow node that represents the source origin of this - * objects. - * All objects (except unknown and undefined values) should return - * exactly one result for either this method or `getBuiltin()`. - */ - override ControlFlowNode getOrigin() { this = TSpecificInstance(result, _, _) } + /** + * Gets a control flow node that represents the source origin of this + * objects. + * All objects (except unknown and undefined values) should return + * exactly one result for either this method or `getBuiltin()`. + */ + override ControlFlowNode getOrigin() { this = TSpecificInstance(result, _, _) } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // TO DO -- Handle cases where class overrides __call__ in more detail, like normal calls. - this.getClass().(ClassObjectInternal).lookup("__call__", _, _) and - obj = ObjectInternal::unknown() and - origin = CfgOrigin::unknown() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // TO DO -- Handle cases where class overrides __call__ in more detail, like normal calls. + this.getClass().(ClassObjectInternal).lookup("__call__", _, _) and + obj = ObjectInternal::unknown() and + origin = CfgOrigin::unknown() + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } - override predicate subscriptUnknown() { any() } + override predicate subscriptUnknown() { any() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - exists(ClassObjectInternal cls | - receiver_type(_, name, this, cls) and - cls.lookup(name, descriptor, _) and - descriptor.isDescriptor() = true - ) and - this = instance - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + exists(ClassObjectInternal cls | + receiver_type(_, name, this, cls) and + cls.lookup(name, descriptor, _) and + descriptor.isDescriptor() = true + ) and + this = instance + } - override int length() { result = lengthFromClass(this.getClass()) } + override int length() { result = lengthFromClass(this.getClass()) } - override predicate initializer(PythonFunctionObjectInternal init, Context callee) { - exists(CallNode call, Context caller, ClassObjectInternal cls | - this = TSpecificInstance(call, cls, caller) and - callee.fromCall(this.getOrigin(), caller) and - cls.lookup("__init__", init, _) - ) - } + override predicate initializer(PythonFunctionObjectInternal init, Context callee) { + exists(CallNode call, Context caller, ClassObjectInternal cls | + this = TSpecificInstance(call, cls, caller) and + callee.fromCall(this.getOrigin(), caller) and + cls.lookup("__init__", init, _) + ) + } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } } /** * A class representing context-free instances represented by `self` in the source code */ class SelfInstanceInternal extends TSelfInstance, InstanceObject { - override string toString() { - result = "self instance of " + this.getClass().(ClassObjectInternal).getName() - } + override string toString() { + result = "self instance of " + this.getClass().(ClassObjectInternal).getName() + } - /** The boolean value of this object, if it has one */ - override boolean booleanValue() { - //result = this.getClass().instancesBooleanValue() - result = maybe() - } + /** The boolean value of this object, if it has one */ + override boolean booleanValue() { + //result = this.getClass().instancesBooleanValue() + result = maybe() + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - predicate parameterAndContext(ParameterDefinition def, PointsToContext context) { - this = TSelfInstance(def, context, _) - } + predicate parameterAndContext(ParameterDefinition def, PointsToContext context) { + this = TSelfInstance(def, context, _) + } - /** Gets the class declaration for this object, if it is a declared class. */ - override ClassDecl getClassDeclaration() { none() } + /** Gets the class declaration for this object, if it is a declared class. */ + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override ObjectInternal getClass() { this = TSelfInstance(_, _, result) } + override ObjectInternal getClass() { this = TSelfInstance(_, _, result) } - ParameterDefinition getParameter() { this = TSelfInstance(result, _, _) } + ParameterDefinition getParameter() { this = TSelfInstance(result, _, _) } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { - exists(ParameterDefinition def | - this = TSelfInstance(def, _, _) and - result = def.getDefiningNode() - ) - } + override ControlFlowNode getOrigin() { + exists(ParameterDefinition def | + this = TSelfInstance(def, _, _) and + result = def.getDefiningNode() + ) + } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // In general instances aren't callable, but some are... - // TO DO -- Handle cases where class overrides __call__ - none() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // In general instances aren't callable, but some are... + // TO DO -- Handle cases where class overrides __call__ + none() + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } - override predicate subscriptUnknown() { any() } + override predicate subscriptUnknown() { any() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - exists(AttrNode attr, ClassObjectInternal cls | - receiver_type(attr, name, this, cls) and - cls_descriptor(cls, name, descriptor) - ) and - instance = this - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + exists(AttrNode attr, ClassObjectInternal cls | + receiver_type(attr, name, this, cls) and + cls_descriptor(cls, name, descriptor) + ) and + instance = this + } - override int length() { result = lengthFromClass(this.getClass()) } + override int length() { result = lengthFromClass(this.getClass()) } - override predicate initializer(PythonFunctionObjectInternal init, Context callee) { - callee.isRuntime() and - init.getScope() != this.getParameter().getScope() and - this.getClass().attribute("__init__", init, _) - } + override predicate initializer(PythonFunctionObjectInternal init, Context callee) { + callee.isRuntime() and + init.getScope() != this.getParameter().getScope() and + this.getClass().attribute("__init__", init, _) + } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } } /** A class representing a value that has a known class, but no other information */ class UnknownInstanceInternal extends TUnknownInstance, ObjectInternal { - override string toString() { - result = "instance of " + this.getClass().(ClassObjectInternal).getName() - } + override string toString() { + result = "instance of " + this.getClass().(ClassObjectInternal).getName() + } - override boolean booleanValue() { result = maybe() } + override boolean booleanValue() { result = maybe() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - /** Gets the class declaration for this object, if it is a declared class. */ - override ClassDecl getClassDeclaration() { none() } + /** Gets the class declaration for this object, if it is a declared class. */ + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override ObjectInternal getClass() { this = TUnknownInstance(result) } + override ObjectInternal getClass() { this = TUnknownInstance(result) } - /** - * Gets the `Builtin` for this object, if any. - * All objects (except unknown and undefined values) should return - * exactly one result for either this method or `getOrigin()`. - */ - override Builtin getBuiltin() { none() } + /** + * Gets the `Builtin` for this object, if any. + * All objects (except unknown and undefined values) should return + * exactly one result for either this method or `getOrigin()`. + */ + override Builtin getBuiltin() { none() } - /** - * Gets a control flow node that represents the source origin of this - * objects. - * All objects (except unknown and undefined values) should return - * exactly one result for either this method or `getBuiltin()`. - */ - override ControlFlowNode getOrigin() { none() } + /** + * Gets a control flow node that represents the source origin of this + * objects. + * All objects (except unknown and undefined values) should return + * exactly one result for either this method or `getBuiltin()`. + */ + override ControlFlowNode getOrigin() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // In general instances aren't callable, but some are... - // TO DO -- Handle cases where class overrides __call__ - none() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // In general instances aren't callable, but some are... + // TO DO -- Handle cases where class overrides __call__ + none() + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - PointsToInternal::attributeRequired(this, name) and - exists(ObjectInternal cls_attr, CfgOrigin attr_orig | - this.getClass().(ClassObjectInternal).lookup(name, cls_attr, attr_orig) - | - cls_attr.isDescriptor() = false and value = cls_attr and origin = attr_orig - or - cls_attr.isDescriptor() = true and cls_attr.descriptorGetInstance(this, value, origin) - ) - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + PointsToInternal::attributeRequired(this, name) and + exists(ObjectInternal cls_attr, CfgOrigin attr_orig | + this.getClass().(ClassObjectInternal).lookup(name, cls_attr, attr_orig) + | + cls_attr.isDescriptor() = false and value = cls_attr and origin = attr_orig + or + cls_attr.isDescriptor() = true and cls_attr.descriptorGetInstance(this, value, origin) + ) + } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } - override predicate subscriptUnknown() { any() } + override predicate subscriptUnknown() { any() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - exists(AttrNode attr, ClassObjectInternal cls | - receiver_type(attr, name, this, cls) and - cls_descriptor(cls, name, descriptor) - ) and - instance = this - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + exists(AttrNode attr, ClassObjectInternal cls | + receiver_type(attr, name, this, cls) and + cls_descriptor(cls, name, descriptor) + ) and + instance = this + } - override int length() { result = lengthFromClass(this.getClass()) } + override int length() { result = lengthFromClass(this.getClass()) } - override string getName() { none() } + override string getName() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { any() } + override predicate useOriginAsLegacyObject() { any() } - override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } + override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } private int lengthFromClass(ClassObjectInternal cls) { - Types::getMro(cls).declares("__len__") and result = -1 + Types::getMro(cls).declares("__len__") and result = -1 } private predicate cls_descriptor(ClassObjectInternal cls, string name, ObjectInternal descriptor) { - cls.lookup(name, descriptor, _) and - descriptor.isDescriptor() = true + cls.lookup(name, descriptor, _) and + descriptor.isDescriptor() = true } /** A class representing an instance of the `super` class */ class SuperInstance extends TSuperInstance, ObjectInternal { - override string toString() { - result = "super(" + this.getStartClass().toString() + ", " + this.getSelf().toString() + ")" - } + override string toString() { + result = "super(" + this.getStartClass().toString() + ", " + this.getSelf().toString() + ")" + } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - exists(ObjectInternal self, ClassObjectInternal startclass | - super_instantiation(node, self, startclass, context) and - this = TSuperInstance(self, startclass) - ) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + exists(ObjectInternal self, ClassObjectInternal startclass | + super_instantiation(node, self, startclass, context) and + this = TSuperInstance(self, startclass) + ) + } - /** Gets the class declared as the starting point for MRO lookup. */ - ClassObjectInternal getStartClass() { this = TSuperInstance(_, result) } + /** Gets the class declared as the starting point for MRO lookup. */ + ClassObjectInternal getStartClass() { this = TSuperInstance(_, result) } - /** Gets 'self' object */ - ObjectInternal getSelf() { this = TSuperInstance(result, _) } + /** Gets 'self' object */ + ObjectInternal getSelf() { this = TSuperInstance(result, _) } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = ObjectInternal::superType() } + override ObjectInternal getClass() { result = ObjectInternal::superType() } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { any() } + override predicate subscriptUnknown() { any() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - exists(ObjectInternal cls_attr, CfgOrigin attr_orig | - this.attribute_descriptor(name, cls_attr, attr_orig) - | - cls_attr.isDescriptor() = false and value = cls_attr and origin = attr_orig - or - cls_attr.isDescriptor() = true and - cls_attr.descriptorGetInstance(this.getSelf(), value, origin) - ) - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + exists(ObjectInternal cls_attr, CfgOrigin attr_orig | + this.attribute_descriptor(name, cls_attr, attr_orig) + | + cls_attr.isDescriptor() = false and value = cls_attr and origin = attr_orig + or + cls_attr.isDescriptor() = true and + cls_attr.descriptorGetInstance(this.getSelf(), value, origin) + ) + } - /* Helper for `attribute` */ - pragma[noinline] - private predicate attribute_descriptor(string name, ObjectInternal cls_attr, CfgOrigin attr_orig) { - PointsToInternal::attributeRequired(this, name) and - this.lookup(name, cls_attr, attr_orig) - } + /* Helper for `attribute` */ + pragma[noinline] + private predicate attribute_descriptor(string name, ObjectInternal cls_attr, CfgOrigin attr_orig) { + PointsToInternal::attributeRequired(this, name) and + this.lookup(name, cls_attr, attr_orig) + } - private predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { - Types::getMro(this.getSelf().getClass()) - .startingAt(this.getStartClass()) - .getTail() - .lookup(name, value, origin) - } + private predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { + Types::getMro(this.getSelf().getClass()) + .startingAt(this.getStartClass()) + .getTail() + .lookup(name, value, origin) + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - descriptor.isDescriptor() = true and - this.lookup(name, descriptor, _) and - instance = this.getSelf() and - receiver_type(_, name, this, _) - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + descriptor.isDescriptor() = true and + this.lookup(name, descriptor, _) and + instance = this.getSelf() and + receiver_type(_, name, this, _) + } - override int length() { none() } + override int length() { none() } - override string getName() { none() } + override string getName() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { any() } + override predicate useOriginAsLegacyObject() { any() } - override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } + override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } diff --git a/python/ql/src/semmle/python/objects/Modules.qll b/python/ql/src/semmle/python/objects/Modules.qll index 83b8fb5769c..ce40f28d74e 100644 --- a/python/ql/src/semmle/python/objects/Modules.qll +++ b/python/ql/src/semmle/python/objects/Modules.qll @@ -8,388 +8,388 @@ private import semmle.python.types.Builtins /** A class representing modules */ abstract class ModuleObjectInternal extends ObjectInternal { - /** Gets the source scope of this module, if it has one. */ - abstract Module getSourceModule(); + /** Gets the source scope of this module, if it has one. */ + abstract Module getSourceModule(); - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // Modules aren't callable - none() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // Modules aren't callable + none() + } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - // Modules aren't callable - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + // Modules aren't callable + none() + } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override ObjectInternal getClass() { result = ObjectInternal::moduleType() } + override ObjectInternal getClass() { result = ObjectInternal::moduleType() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { none() } + override int length() { none() } - override predicate subscriptUnknown() { any() } + override predicate subscriptUnknown() { any() } - /** Holds if this module is a `__init__.py` module. */ - predicate isInitModule() { any(PackageObjectInternal package).getInitModule() = this } + /** Holds if this module is a `__init__.py` module. */ + predicate isInitModule() { any(PackageObjectInternal package).getInitModule() = this } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /* Modules aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* Modules aren't iterable */ + override ObjectInternal getIterNext() { none() } - /** - * Holds if this module "exports" name. - * That is, does it define `name` in `__all__` or is - * `__all__` not defined and `name` a global variable that does not start with "_" - * This is the set of names imported by `from ... import *`. - */ - predicate exports(string name) { - not this.(ModuleObjectInternal).attribute("__all__", _, _) and - this.hasAttribute(name) and - not name.charAt(0) = "_" - or - py_exports(this.getSourceModule(), name) - } + /** + * Holds if this module "exports" name. + * That is, does it define `name` in `__all__` or is + * `__all__` not defined and `name` a global variable that does not start with "_" + * This is the set of names imported by `from ... import *`. + */ + predicate exports(string name) { + not this.(ModuleObjectInternal).attribute("__all__", _, _) and + this.hasAttribute(name) and + not name.charAt(0) = "_" + or + py_exports(this.getSourceModule(), name) + } - /** Whether the complete set of names "exported" by this module can be accurately determined */ - abstract predicate hasCompleteExportInfo(); + /** Whether the complete set of names "exported" by this module can be accurately determined */ + abstract predicate hasCompleteExportInfo(); - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** A class representing built-in modules */ class BuiltinModuleObjectInternal extends ModuleObjectInternal, TBuiltinModuleObject { - override Builtin getBuiltin() { this = TBuiltinModuleObject(result) } + override Builtin getBuiltin() { this = TBuiltinModuleObject(result) } - override string toString() { result = "Module " + this.getBuiltin().getName() } + override string toString() { result = "Module " + this.getBuiltin().getName() } - override string getName() { result = this.getBuiltin().getName() } + override string getName() { result = this.getBuiltin().getName() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override Module getSourceModule() { none() } + override Module getSourceModule() { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - value = ObjectInternal::fromBuiltin(this.getBuiltin().getMember(name)) and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + value = ObjectInternal::fromBuiltin(this.getBuiltin().getMember(name)) and + origin = CfgOrigin::unknown() + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate hasCompleteExportInfo() { any() } + override predicate hasCompleteExportInfo() { any() } } /** A class representing packages */ class PackageObjectInternal extends ModuleObjectInternal, TPackageObject { - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override string toString() { result = "Package " + this.getName() } + override string toString() { result = "Package " + this.getName() } - /** Gets the folder for this package */ - Folder getFolder() { this = TPackageObject(result) } + /** Gets the folder for this package */ + Folder getFolder() { this = TPackageObject(result) } - override string getName() { result = moduleNameFromFile(this.getFolder()) } + override string getName() { result = moduleNameFromFile(this.getFolder()) } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override Module getSourceModule() { result.getFile() = this.getFolder().getFile("__init__.py") } + override Module getSourceModule() { result.getFile() = this.getFolder().getFile("__init__.py") } - /** Gets the init module of this package */ - PythonModuleObjectInternal getInitModule() { result = TPythonModule(this.getSourceModule()) } + /** Gets the init module of this package */ + PythonModuleObjectInternal getInitModule() { result = TPythonModule(this.getSourceModule()) } - predicate hasNoInitModule() { - exists(Folder f | - f = this.getFolder() and - not exists(f.getFile("__init__.py")) - ) - } + predicate hasNoInitModule() { + exists(Folder f | + f = this.getFolder() and + not exists(f.getFile("__init__.py")) + ) + } - /** Gets the submodule `name` of this package */ - ModuleObjectInternal submodule(string name) { - exists(string fullName, int lastDotIndex | - fullName = result.getName() and - lastDotIndex = max(fullName.indexOf(".")) and - name = fullName.substring(lastDotIndex + 1, fullName.length()) and - this.getName() = fullName.substring(0, lastDotIndex) - ) - } + /** Gets the submodule `name` of this package */ + ModuleObjectInternal submodule(string name) { + exists(string fullName, int lastDotIndex | + fullName = result.getName() and + lastDotIndex = max(fullName.indexOf(".")) and + name = fullName.substring(lastDotIndex + 1, fullName.length()) and + this.getName() = fullName.substring(0, lastDotIndex) + ) + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - this.getInitModule().attribute(name, value, origin) - or - exists(Module init | - init = this.getSourceModule() and - /* The variable shadowing the name of the child module is undefined at exit */ - ModuleAttributes::pointsToAtExit(init, name, ObjectInternal::undefined(), _) and - not name = "__init__" and - value = this.submodule(name) and - origin = CfgOrigin::fromObject(value) - ) - or - this.hasNoInitModule() and - exists(ModuleObjectInternal mod | - mod = this.submodule(name) and - value = mod - | - origin = CfgOrigin::fromObject(mod) - ) - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + this.getInitModule().attribute(name, value, origin) + or + exists(Module init | + init = this.getSourceModule() and + /* The variable shadowing the name of the child module is undefined at exit */ + ModuleAttributes::pointsToAtExit(init, name, ObjectInternal::undefined(), _) and + not name = "__init__" and + value = this.submodule(name) and + origin = CfgOrigin::fromObject(value) + ) + or + this.hasNoInitModule() and + exists(ModuleObjectInternal mod | + mod = this.submodule(name) and + value = mod + | + origin = CfgOrigin::fromObject(mod) + ) + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override ControlFlowNode getOrigin() { - exists(Module package | - package.isPackage() and - package.getPath() = this.getFolder() and - result = package.getEntryNode() - ) - } + override ControlFlowNode getOrigin() { + exists(Module package | + package.isPackage() and + package.getPath() = this.getFolder() and + result = package.getEntryNode() + ) + } - /** Holds if this value has the attribute `name` */ - override predicate hasAttribute(string name) { - this.getInitModule().hasAttribute(name) - or - exists(this.submodule(name)) - } + /** Holds if this value has the attribute `name` */ + override predicate hasAttribute(string name) { + this.getInitModule().hasAttribute(name) + or + exists(this.submodule(name)) + } - override predicate hasCompleteExportInfo() { - not exists(this.getInitModule()) - or - this.getInitModule().hasCompleteExportInfo() - } + override predicate hasCompleteExportInfo() { + not exists(this.getInitModule()) + or + this.getInitModule().hasCompleteExportInfo() + } } /** A class representing Python modules */ class PythonModuleObjectInternal extends ModuleObjectInternal, TPythonModule { - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override string toString() { result = this.getSourceModule().toString() } + override string toString() { result = this.getSourceModule().toString() } - override string getName() { result = this.getSourceModule().getName() } + override string getName() { result = this.getSourceModule().getName() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override Module getSourceModule() { this = TPythonModule(result) } + override Module getSourceModule() { this = TPythonModule(result) } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - value != ObjectInternal::undefined() and - ModuleAttributes::pointsToAtExit(this.getSourceModule(), name, value, origin) - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + value != ObjectInternal::undefined() and + ModuleAttributes::pointsToAtExit(this.getSourceModule(), name, value, origin) + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override ControlFlowNode getOrigin() { result = this.getSourceModule().getEntryNode() } + override ControlFlowNode getOrigin() { result = this.getSourceModule().getEntryNode() } - /** Holds if this value has the attribute `name` */ - override predicate hasAttribute(string name) { - name = "__name__" - or - this.getSourceModule().(ImportTimeScope).definesName(name) - or - exists(ModuleObjectInternal mod, ImportStarNode imp | - PointsToInternal::pointsTo(imp, _, mod, _) and - imp.getScope() = this.getSourceModule() and - mod.exports(name) - ) - or - exists(ObjectInternal defined | - this.attribute(name, defined, _) and - not defined instanceof UndefinedInternal - ) - } + /** Holds if this value has the attribute `name` */ + override predicate hasAttribute(string name) { + name = "__name__" + or + this.getSourceModule().(ImportTimeScope).definesName(name) + or + exists(ModuleObjectInternal mod, ImportStarNode imp | + PointsToInternal::pointsTo(imp, _, mod, _) and + imp.getScope() = this.getSourceModule() and + mod.exports(name) + ) + or + exists(ObjectInternal defined | + this.attribute(name, defined, _) and + not defined instanceof UndefinedInternal + ) + } - override predicate hasCompleteExportInfo() { - not exists(Call modify, Attribute attr, GlobalVariable all | - modify.getScope() = this.getSourceModule() and - modify.getFunc() = attr and - all.getId() = "__all__" - | - attr.getObject().(Name).uses(all) - ) - } + override predicate hasCompleteExportInfo() { + not exists(Call modify, Attribute attr, GlobalVariable all | + modify.getScope() = this.getSourceModule() and + modify.getFunc() = attr and + all.getId() = "__all__" + | + attr.getObject().(Name).uses(all) + ) + } } /** A class representing a module that is missing from the DB, but inferred to exists from imports. */ class AbsentModuleObjectInternal extends ModuleObjectInternal, TAbsentModule { - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override string toString() { - if - exists(Module m, SyntaxError se | se.getFile() = m.getFile() and m.getName() = this.getName()) - then result = "Unparsable module " + this.getName() - else result = "Missing module " + this.getName() - } + override string toString() { + if + exists(Module m, SyntaxError se | se.getFile() = m.getFile() and m.getName() = this.getName()) + then result = "Unparsable module " + this.getName() + else result = "Missing module " + this.getName() + } - override string getName() { this = TAbsentModule(result) } + override string getName() { this = TAbsentModule(result) } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - missing_imported_module(node, context, this.getName()) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + missing_imported_module(node, context, this.getName()) + } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override Module getSourceModule() { none() } + override Module getSourceModule() { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - value = TAbsentModuleAttribute(this, name) and origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + value = TAbsentModuleAttribute(this, name) and origin = CfgOrigin::unknown() + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate hasCompleteExportInfo() { none() } + override predicate hasCompleteExportInfo() { none() } } /** A class representing an attribute of a missing module. */ class AbsentModuleAttributeObjectInternal extends ObjectInternal, TAbsentModuleAttribute { - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override string toString() { - exists(ModuleObjectInternal mod, string name | - this = TAbsentModuleAttribute(mod, name) and - result = "Missing module attribute " + mod.getName() + "." + name - ) - } + override string toString() { + exists(ModuleObjectInternal mod, string name | + this = TAbsentModuleAttribute(mod, name) and + result = "Missing module attribute " + mod.getName() + "." + name + ) + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - exists(ModuleObjectInternal mod, string name | this = TAbsentModuleAttribute(mod, name) | - PointsToInternal::pointsTo(node.(AttrNode).getObject(name), context, mod, _) - or - PointsToInternal::pointsTo(node.(ImportMemberNode).getModule(name), context, mod, _) - ) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + exists(ModuleObjectInternal mod, string name | this = TAbsentModuleAttribute(mod, name) | + PointsToInternal::pointsTo(node.(AttrNode).getObject(name), context, mod, _) + or + PointsToInternal::pointsTo(node.(ImportMemberNode).getModule(name), context, mod, _) + ) + } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() + } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override boolean isClass() { result = maybe() } + override boolean isClass() { result = maybe() } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override boolean booleanValue() { result = maybe() } + override boolean booleanValue() { result = maybe() } - override ObjectInternal getClass() { result = ObjectInternal::unknownClass() } + override ObjectInternal getClass() { result = ObjectInternal::unknownClass() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { none() } + override int length() { none() } - override predicate subscriptUnknown() { any() } + override predicate subscriptUnknown() { any() } - /* - * We know what this is called, but not its innate name. - * However, if we are looking for things by name, this is a reasonable approximation - */ + /* + * We know what this is called, but not its innate name. + * However, if we are looking for things by name, this is a reasonable approximation + */ - override string getName() { this = TAbsentModuleAttribute(_, result) } + override string getName() { this = TAbsentModuleAttribute(_, result) } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /* Modules aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* Modules aren't iterable */ + override ObjectInternal getIterNext() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } diff --git a/python/ql/src/semmle/python/objects/ObjectAPI.qll b/python/ql/src/semmle/python/objects/ObjectAPI.qll index 833a755c059..9af9074cefc 100644 --- a/python/ql/src/semmle/python/objects/ObjectAPI.qll +++ b/python/ql/src/semmle/python/objects/ObjectAPI.qll @@ -30,118 +30,118 @@ class ModuleScope = Module; * Each `Value` is a static approximation to a set of one or more real objects. */ class Value extends TObject { - Value() { - this != ObjectInternal::unknown() and - this != ObjectInternal::unknownClass() and - this != ObjectInternal::undefined() - } + Value() { + this != ObjectInternal::unknown() and + this != ObjectInternal::unknownClass() and + this != ObjectInternal::undefined() + } - /** Gets a textual representation of this element. */ - string toString() { result = this.(ObjectInternal).toString() } + /** Gets a textual representation of this element. */ + string toString() { result = this.(ObjectInternal).toString() } - /** Gets a `ControlFlowNode` that refers to this object. */ - ControlFlowNode getAReference() { PointsToInternal::pointsTo(result, _, this, _) } + /** Gets a `ControlFlowNode` that refers to this object. */ + ControlFlowNode getAReference() { PointsToInternal::pointsTo(result, _, this, _) } - /** Gets the origin CFG node for this value. */ - ControlFlowNode getOrigin() { result = this.(ObjectInternal).getOrigin() } + /** Gets the origin CFG node for this value. */ + ControlFlowNode getOrigin() { result = this.(ObjectInternal).getOrigin() } - /** - * Gets the class of this object. - * Strictly, the `Value` representing the class of the objects - * represented by this Value. - */ - ClassValue getClass() { result = this.(ObjectInternal).getClass() } + /** + * Gets the class of this object. + * Strictly, the `Value` representing the class of the objects + * represented by this Value. + */ + ClassValue getClass() { result = this.(ObjectInternal).getClass() } - /** Gets a call to this object */ - CallNode getACall() { result = this.getACall(_) } + /** Gets a call to this object */ + CallNode getACall() { result = this.getACall(_) } - /** Gets a call to this object with the given `caller` context. */ - CallNode getACall(PointsToContext caller) { - PointsToInternal::pointsTo(result.getFunction(), caller, this, _) - or - exists(BoundMethodObjectInternal bm | - PointsToInternal::pointsTo(result.getFunction(), caller, bm, _) and - bm.getFunction() = this - ) - } + /** Gets a call to this object with the given `caller` context. */ + CallNode getACall(PointsToContext caller) { + PointsToInternal::pointsTo(result.getFunction(), caller, this, _) + or + exists(BoundMethodObjectInternal bm | + PointsToInternal::pointsTo(result.getFunction(), caller, bm, _) and + bm.getFunction() = this + ) + } - /** Gets a `Value` that represents the attribute `name` of this object. */ - Value attr(string name) { this.(ObjectInternal).attribute(name, result, _) } + /** Gets a `Value` that represents the attribute `name` of this object. */ + Value attr(string name) { this.(ObjectInternal).attribute(name, result, _) } - /** - * Holds if this value is builtin. Applies to built-in functions and methods, - * but also integers and strings. - */ - predicate isBuiltin() { this.(ObjectInternal).isBuiltin() } + /** + * Holds if this value is builtin. Applies to built-in functions and methods, + * but also integers and strings. + */ + predicate isBuiltin() { this.(ObjectInternal).isBuiltin() } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this - .(ObjectInternal) - .getOrigin() - .getLocation() - .hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - or - not exists(this.(ObjectInternal).getOrigin()) and - filepath = "" and - startline = 0 and - startcolumn = 0 and - endline = 0 and - endcolumn = 0 - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this + .(ObjectInternal) + .getOrigin() + .getLocation() + .hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + or + not exists(this.(ObjectInternal).getOrigin()) and + filepath = "" and + startline = 0 and + startcolumn = 0 and + endline = 0 and + endcolumn = 0 + } - /** - * Gets the name of this value, if it has one. - * Note this is the innate name of the - * object, not necessarily all the names by which it can be called. - */ - final string getName() { result = this.(ObjectInternal).getName() } + /** + * Gets the name of this value, if it has one. + * Note this is the innate name of the + * object, not necessarily all the names by which it can be called. + */ + final string getName() { result = this.(ObjectInternal).getName() } - /** Holds if this value has the attribute `name` */ - predicate hasAttribute(string name) { this.(ObjectInternal).hasAttribute(name) } + /** Holds if this value has the attribute `name` */ + predicate hasAttribute(string name) { this.(ObjectInternal).hasAttribute(name) } - /** Whether this value is absent from the database, but has been inferred to likely exist */ - predicate isAbsent() { - this instanceof AbsentModuleObjectInternal - or - this instanceof AbsentModuleAttributeObjectInternal - } + /** Whether this value is absent from the database, but has been inferred to likely exist */ + predicate isAbsent() { + this instanceof AbsentModuleObjectInternal + or + this instanceof AbsentModuleAttributeObjectInternal + } - /** - * Whether this overrides v. In this context, "overrides" means that this object - * is a named attribute of a some class C and `v` is a named attribute of another - * class S, both attributes having the same name, and S is a super class of C. - */ - predicate overrides(Value v) { - exists(ClassValue my_class, ClassValue other_class, string name | - my_class.declaredAttribute(name) = this and - other_class.declaredAttribute(name) = v and - my_class.getABaseType+() = other_class - ) - } + /** + * Whether this overrides v. In this context, "overrides" means that this object + * is a named attribute of a some class C and `v` is a named attribute of another + * class S, both attributes having the same name, and S is a super class of C. + */ + predicate overrides(Value v) { + exists(ClassValue my_class, ClassValue other_class, string name | + my_class.declaredAttribute(name) = this and + other_class.declaredAttribute(name) = v and + my_class.getABaseType+() = other_class + ) + } - /** - * Gets the boolean interpretation of this value. - * Could be both `true` and `false`, if we can't determine the result more precisely. - */ - boolean getABooleanValue() { result = this.(ObjectInternal).booleanValue() } + /** + * Gets the boolean interpretation of this value. + * Could be both `true` and `false`, if we can't determine the result more precisely. + */ + boolean getABooleanValue() { result = this.(ObjectInternal).booleanValue() } - /** - * Gets the boolean interpretation of this value, only if we can determine the result precisely. - * The result can be `none()`, but never both `true` and `false`. - */ - boolean getDefiniteBooleanValue() { - result = getABooleanValue() and - not (getABooleanValue() = true and getABooleanValue() = false) - } + /** + * Gets the boolean interpretation of this value, only if we can determine the result precisely. + * The result can be `none()`, but never both `true` and `false`. + */ + boolean getDefiniteBooleanValue() { + result = getABooleanValue() and + not (getABooleanValue() = true and getABooleanValue() = false) + } } /** @@ -149,196 +149,196 @@ class Value extends TObject { * Each `ModuleValue` represents a module object in the Python program. */ class ModuleValue extends Value { - ModuleValue() { this instanceof ModuleObjectInternal } + ModuleValue() { this instanceof ModuleObjectInternal } - /** - * Holds if this module "exports" name. - * That is, does it define `name` in `__all__` or is - * `__all__` not defined and `name` a global variable that does not start with "_" - * This is the set of names imported by `from ... import *`. - */ - predicate exports(string name) { PointsTo::moduleExports(this, name) } + /** + * Holds if this module "exports" name. + * That is, does it define `name` in `__all__` or is + * `__all__` not defined and `name` a global variable that does not start with "_" + * This is the set of names imported by `from ... import *`. + */ + predicate exports(string name) { PointsTo::moduleExports(this, name) } - /** Gets the scope for this module, provided that it is a Python module. */ - ModuleScope getScope() { result = this.(ModuleObjectInternal).getSourceModule() } + /** Gets the scope for this module, provided that it is a Python module. */ + ModuleScope getScope() { result = this.(ModuleObjectInternal).getSourceModule() } - /** - * Gets the container path for this module. Will be the file for a Python module, - * the folder for a package and no result for a builtin module. - */ - Container getPath() { - result = this.(PackageObjectInternal).getFolder() - or - result = this.(PythonModuleObjectInternal).getSourceModule().getFile() - } + /** + * Gets the container path for this module. Will be the file for a Python module, + * the folder for a package and no result for a builtin module. + */ + Container getPath() { + result = this.(PackageObjectInternal).getFolder() + or + result = this.(PythonModuleObjectInternal).getSourceModule().getFile() + } - /** - * Whether this module is imported by 'import name'. For example on a linux system, - * the module 'posixpath' is imported as 'os.path' or as 'posixpath' - */ - predicate importedAs(string name) { PointsToInternal::module_imported_as(this, name) } + /** + * Whether this module is imported by 'import name'. For example on a linux system, + * the module 'posixpath' is imported as 'os.path' or as 'posixpath' + */ + predicate importedAs(string name) { PointsToInternal::module_imported_as(this, name) } - /** Whether this module is a package. */ - predicate isPackage() { this instanceof PackageObjectInternal } + /** Whether this module is a package. */ + predicate isPackage() { this instanceof PackageObjectInternal } - /** Whether the complete set of names "exported" by this module can be accurately determined */ - predicate hasCompleteExportInfo() { this.(ModuleObjectInternal).hasCompleteExportInfo() } + /** Whether the complete set of names "exported" by this module can be accurately determined */ + predicate hasCompleteExportInfo() { this.(ModuleObjectInternal).hasCompleteExportInfo() } - /** Get a module that this module imports */ - ModuleValue getAnImportedModule() { result.importedAs(this.getScope().getAnImportedModuleName()) } + /** Get a module that this module imports */ + ModuleValue getAnImportedModule() { result.importedAs(this.getScope().getAnImportedModuleName()) } - /** When used as a normal module (for example, imported and used by other modules) */ - predicate isUsedAsModule() { - this.isBuiltin() - or - this.isPackage() - or - exists(ImportingStmt i | this.importedAs(i.getAnImportedModuleName())) - or - this.getPath().getBaseName() = "__init__.py" - } + /** When used as a normal module (for example, imported and used by other modules) */ + predicate isUsedAsModule() { + this.isBuiltin() + or + this.isPackage() + or + exists(ImportingStmt i | this.importedAs(i.getAnImportedModuleName())) + or + this.getPath().getBaseName() = "__init__.py" + } - /** When used (exclusively) as a script (will not include normal modules that can also be run as a script) */ - predicate isUsedAsScript() { - not isUsedAsModule() and - ( - not this.getPath().getExtension() = "py" - or - exists(If i, Name name, StrConst main, Cmpop op | - i.getScope() = this.getScope() and - op instanceof Eq and - i.getTest().(Compare).compares(name, op, main) and - name.getId() = "__name__" and - main.getText() = "__main__" - ) - or - exists(Comment c | - c.getLocation().getFile() = this.getPath() and - c.getLocation().getStartLine() = 1 and - c.getText().regexpMatch("^#!/.*python(2|3)?[ \\\\t]*$") - ) - ) - } + /** When used (exclusively) as a script (will not include normal modules that can also be run as a script) */ + predicate isUsedAsScript() { + not isUsedAsModule() and + ( + not this.getPath().getExtension() = "py" + or + exists(If i, Name name, StrConst main, Cmpop op | + i.getScope() = this.getScope() and + op instanceof Eq and + i.getTest().(Compare).compares(name, op, main) and + name.getId() = "__name__" and + main.getText() = "__main__" + ) + or + exists(Comment c | + c.getLocation().getFile() = this.getPath() and + c.getLocation().getStartLine() = 1 and + c.getText().regexpMatch("^#!/.*python(2|3)?[ \\\\t]*$") + ) + ) + } } module Module { - /** - * Gets the `ModuleValue` named `name`. - * - * Note that the name used to refer to a module is not - * necessarily its name. For example, - * there are modules referred to by the name `os.path`, - * but that are not named `os.path`, for example the module `posixpath`. - * Such that the following is true: - * `Module::named("os.path").getName() = "posixpath" - */ - ModuleValue named(string name) { - result.getName() = name - or - result = named(name, _) - } + /** + * Gets the `ModuleValue` named `name`. + * + * Note that the name used to refer to a module is not + * necessarily its name. For example, + * there are modules referred to by the name `os.path`, + * but that are not named `os.path`, for example the module `posixpath`. + * Such that the following is true: + * `Module::named("os.path").getName() = "posixpath" + */ + ModuleValue named(string name) { + result.getName() = name + or + result = named(name, _) + } - /* Prevent runaway recursion when a module has itself as an attribute. */ - private ModuleValue named(string name, int dots) { - dots = 0 and - not name.charAt(_) = "." and - result.getName() = name - or - dots <= 3 and - exists(string modname, string attrname | name = modname + "." + attrname | - result = named(modname, dots - 1).attr(attrname) - ) - } + /* Prevent runaway recursion when a module has itself as an attribute. */ + private ModuleValue named(string name, int dots) { + dots = 0 and + not name.charAt(_) = "." and + result.getName() = name + or + dots <= 3 and + exists(string modname, string attrname | name = modname + "." + attrname | + result = named(modname, dots - 1).attr(attrname) + ) + } - /** Get the `ModuleValue` for the `builtin` module. */ - ModuleValue builtinModule() { result = TBuiltinModuleObject(Builtin::builtinModule()) } + /** Get the `ModuleValue` for the `builtin` module. */ + ModuleValue builtinModule() { result = TBuiltinModuleObject(Builtin::builtinModule()) } } module Value { - /** - * Gets the `Value` named `name`. - * If there is at least one '.' in `name`, then the part of - * the name to the left of the rightmost '.' is interpreted as a module name - * and the part after the rightmost '.' as an attribute of that module. - * For example, `Value::named("os.path.join")` is the `Value` representing the - * `join` function of the `os.path` module. - * If there is no '.' in `name`, then the `Value` returned is the builtin - * object of that name. - * For example `Value::named("len")` is the `Value` representing the `len` built-in function. - */ - Value named(string name) { - exists(string modname, string attrname | name = modname + "." + attrname | - result = Module::named(modname).attr(attrname) - ) - or - result = ObjectInternal::builtin(name) - or - name = "None" and result = ObjectInternal::none_() - or - name = "True" and result = TTrue() - or - name = "False" and result = TFalse() - } + /** + * Gets the `Value` named `name`. + * If there is at least one '.' in `name`, then the part of + * the name to the left of the rightmost '.' is interpreted as a module name + * and the part after the rightmost '.' as an attribute of that module. + * For example, `Value::named("os.path.join")` is the `Value` representing the + * `join` function of the `os.path` module. + * If there is no '.' in `name`, then the `Value` returned is the builtin + * object of that name. + * For example `Value::named("len")` is the `Value` representing the `len` built-in function. + */ + Value named(string name) { + exists(string modname, string attrname | name = modname + "." + attrname | + result = Module::named(modname).attr(attrname) + ) + or + result = ObjectInternal::builtin(name) + or + name = "None" and result = ObjectInternal::none_() + or + name = "True" and result = TTrue() + or + name = "False" and result = TFalse() + } - /** - * Gets the `NumericValue` for the integer constant `i`, if it exists. - * There will be no `NumericValue` for most integers, but the following are - * guaranteed to exist: - * * From zero to 511 inclusive. - * * All powers of 2 (up to 2**30) - * * Any integer explicitly mentioned in the source program. - */ - NumericValue forInt(int i) { result.(IntObjectInternal).intValue() = i } + /** + * Gets the `NumericValue` for the integer constant `i`, if it exists. + * There will be no `NumericValue` for most integers, but the following are + * guaranteed to exist: + * * From zero to 511 inclusive. + * * All powers of 2 (up to 2**30) + * * Any integer explicitly mentioned in the source program. + */ + NumericValue forInt(int i) { result.(IntObjectInternal).intValue() = i } - /** - * Gets the `Value` for the bytes constant `bytes`, if it exists. - * There will be no `Value` for most byte strings, unless it is explicitly - * declared in the source program. - */ - StringValue forBytes(string bytes) { result.(BytesObjectInternal).strValue() = bytes } + /** + * Gets the `Value` for the bytes constant `bytes`, if it exists. + * There will be no `Value` for most byte strings, unless it is explicitly + * declared in the source program. + */ + StringValue forBytes(string bytes) { result.(BytesObjectInternal).strValue() = bytes } - /** - * Gets the `Value` for the unicode constant `text`, if it exists. - * There will be no `Value` for most text strings, unless it is explicitly - * declared in the source program. - */ - StringValue forUnicode(string text) { result.(UnicodeObjectInternal).strValue() = text } + /** + * Gets the `Value` for the unicode constant `text`, if it exists. + * There will be no `Value` for most text strings, unless it is explicitly + * declared in the source program. + */ + StringValue forUnicode(string text) { result.(UnicodeObjectInternal).strValue() = text } - /** - * Gets a `Value` for the string `text`. May be a bytes or unicode string for Python 2. - * There will be no `Value` for most strings, unless it is explicitly - * declared in the source program. - */ - StringValue forString(string text) { - result.(UnicodeObjectInternal).strValue() = text - or - major_version() = 2 and - result.(BytesObjectInternal).strValue() = text - } + /** + * Gets a `Value` for the string `text`. May be a bytes or unicode string for Python 2. + * There will be no `Value` for most strings, unless it is explicitly + * declared in the source program. + */ + StringValue forString(string text) { + result.(UnicodeObjectInternal).strValue() = text + or + major_version() = 2 and + result.(BytesObjectInternal).strValue() = text + } - /** Gets the `Value` for the bool constant `b`. */ - Value forBool(boolean b) { - b = true and result = TTrue() - or - b = false and result = TFalse() - } + /** Gets the `Value` for the bool constant `b`. */ + Value forBool(boolean b) { + b = true and result = TTrue() + or + b = false and result = TFalse() + } - /** Gets the `Value` for `None`. */ - Value none_() { result = ObjectInternal::none_() } + /** Gets the `Value` for `None`. */ + Value none_() { result = ObjectInternal::none_() } - /** - * Shorcuts added by the `site` module to exit your interactive session. - * - * see https://docs.python.org/3/library/constants.html#constants-added-by-the-site-module - */ - Value siteQuitter(string name) { - ( - name = "exit" - or - name = "quit" - ) and - result = Value::named(name) - } + /** + * Shorcuts added by the `site` module to exit your interactive session. + * + * see https://docs.python.org/3/library/constants.html#constants-added-by-the-site-module + */ + Value siteQuitter(string name) { + ( + name = "exit" + or + name = "quit" + ) and + result = Value::named(name) + } } /** @@ -347,106 +347,106 @@ module Value { * but not classes. */ class CallableValue extends Value { - CallableValue() { this instanceof CallableObjectInternal } + CallableValue() { this instanceof CallableObjectInternal } - /** - * Holds if this callable never returns once called. - * For example, `sys.exit` - */ - predicate neverReturns() { this.(CallableObjectInternal).neverReturns() } + /** + * Holds if this callable never returns once called. + * For example, `sys.exit` + */ + predicate neverReturns() { this.(CallableObjectInternal).neverReturns() } - /** Gets the scope for this function, provided that it is a Python function. */ - FunctionScope getScope() { result = this.(PythonFunctionObjectInternal).getScope() } + /** Gets the scope for this function, provided that it is a Python function. */ + FunctionScope getScope() { result = this.(PythonFunctionObjectInternal).getScope() } - /** Gets the `n`th parameter node of this callable. */ - NameNode getParameter(int n) { result = this.(CallableObjectInternal).getParameter(n) } + /** Gets the `n`th parameter node of this callable. */ + NameNode getParameter(int n) { result = this.(CallableObjectInternal).getParameter(n) } - /** Gets the `name`d parameter node of this callable. */ - NameNode getParameterByName(string name) { - result = this.(CallableObjectInternal).getParameterByName(name) - } + /** Gets the `name`d parameter node of this callable. */ + NameNode getParameterByName(string name) { + result = this.(CallableObjectInternal).getParameterByName(name) + } - /** - * Gets the argument in `call` corresponding to the `n`'th positional parameter of this callable. - * - * Use this method instead of `call.getArg(n)` to handle the fact that this function might be used as - * a bound-method, such that argument `n` of the call corresponds to the `n+1` parameter of the callable. - * - * This method also gives results when the argument is passed as a keyword argument in `call`, as long - * as `this` is not a builtin function or a builtin method. - * - * Examples: - * - * - if `this` represents the `PythonFunctionValue` for `def func(a, b):`, and `call` represents - * `func(10, 20)`, then `getArgumentForCall(call, 0)` will give the `ControlFlowNode` for `10`. - * - * - with `call` representing `func(b=20, a=10)`, `getArgumentForCall(call, 0)` will give - * the `ControlFlowNode` for `10`. - * - * - if `this` represents the `PythonFunctionValue` for `def func(self, a, b):`, and `call` - * represents `foo.func(10, 20)`, then `getArgumentForCall(call, 1)` will give the - * `ControlFlowNode` for `10`. - * Note: There will also exist a `BoundMethodValue bm` where `bm.getArgumentForCall(call, 0)` - * will give the `ControlFlowNode` for `10` (notice the shift in index used). - */ - cached - ControlFlowNode getArgumentForCall(CallNode call, int n) { - exists(ObjectInternal called, int offset | - PointsToInternal::pointsTo(call.getFunction(), _, called, _) and - called.functionAndOffset(this, offset) - | - call.getArg(n - offset) = result - or - exists(string name | - call.getArgByName(name) = result and - this.getParameter(n).getId() = name - ) - or - called instanceof BoundMethodObjectInternal and - offset = 1 and - n = 0 and - result = call.getFunction().(AttrNode).getObject() - ) - } + /** + * Gets the argument in `call` corresponding to the `n`'th positional parameter of this callable. + * + * Use this method instead of `call.getArg(n)` to handle the fact that this function might be used as + * a bound-method, such that argument `n` of the call corresponds to the `n+1` parameter of the callable. + * + * This method also gives results when the argument is passed as a keyword argument in `call`, as long + * as `this` is not a builtin function or a builtin method. + * + * Examples: + * + * - if `this` represents the `PythonFunctionValue` for `def func(a, b):`, and `call` represents + * `func(10, 20)`, then `getArgumentForCall(call, 0)` will give the `ControlFlowNode` for `10`. + * + * - with `call` representing `func(b=20, a=10)`, `getArgumentForCall(call, 0)` will give + * the `ControlFlowNode` for `10`. + * + * - if `this` represents the `PythonFunctionValue` for `def func(self, a, b):`, and `call` + * represents `foo.func(10, 20)`, then `getArgumentForCall(call, 1)` will give the + * `ControlFlowNode` for `10`. + * Note: There will also exist a `BoundMethodValue bm` where `bm.getArgumentForCall(call, 0)` + * will give the `ControlFlowNode` for `10` (notice the shift in index used). + */ + cached + ControlFlowNode getArgumentForCall(CallNode call, int n) { + exists(ObjectInternal called, int offset | + PointsToInternal::pointsTo(call.getFunction(), _, called, _) and + called.functionAndOffset(this, offset) + | + call.getArg(n - offset) = result + or + exists(string name | + call.getArgByName(name) = result and + this.getParameter(n).getId() = name + ) + or + called instanceof BoundMethodObjectInternal and + offset = 1 and + n = 0 and + result = call.getFunction().(AttrNode).getObject() + ) + } - /** - * Gets the argument in `call` corresponding to the `name`d keyword parameter of this callable. - * - * This method also gives results when the argument is passed as a positional argument in `call`, as long - * as `this` is not a builtin function or a builtin method. - * - * Examples: - * - * - if `this` represents the `PythonFunctionValue` for `def func(a, b):`, and `call` represents - * `func(10, 20)`, then `getNamedArgumentForCall(call, "a")` will give the `ControlFlowNode` for `10`. - * - * - with `call` representing `func(b=20, a=10)`, `getNamedArgumentForCall(call, "a")` will give - * the `ControlFlowNode` for `10`. - * - * - if `this` represents the `PythonFunctionValue` for `def func(self, a, b):`, and `call` - * represents `foo.func(10, 20)`, then `getNamedArgumentForCall(call, "a")` will give the - * `ControlFlowNode` for `10`. - */ - cached - ControlFlowNode getNamedArgumentForCall(CallNode call, string name) { - exists(CallableObjectInternal called, int offset | - PointsToInternal::pointsTo(call.getFunction(), _, called, _) and - called.functionAndOffset(this, offset) - | - call.getArgByName(name) = result - or - exists(int n | - call.getArg(n) = result and - this.getParameter(n + offset).getId() = name - // TODO: and not positional only argument (Python 3.8+) - ) - or - called instanceof BoundMethodObjectInternal and - offset = 1 and - name = "self" and - result = call.getFunction().(AttrNode).getObject() - ) - } + /** + * Gets the argument in `call` corresponding to the `name`d keyword parameter of this callable. + * + * This method also gives results when the argument is passed as a positional argument in `call`, as long + * as `this` is not a builtin function or a builtin method. + * + * Examples: + * + * - if `this` represents the `PythonFunctionValue` for `def func(a, b):`, and `call` represents + * `func(10, 20)`, then `getNamedArgumentForCall(call, "a")` will give the `ControlFlowNode` for `10`. + * + * - with `call` representing `func(b=20, a=10)`, `getNamedArgumentForCall(call, "a")` will give + * the `ControlFlowNode` for `10`. + * + * - if `this` represents the `PythonFunctionValue` for `def func(self, a, b):`, and `call` + * represents `foo.func(10, 20)`, then `getNamedArgumentForCall(call, "a")` will give the + * `ControlFlowNode` for `10`. + */ + cached + ControlFlowNode getNamedArgumentForCall(CallNode call, string name) { + exists(CallableObjectInternal called, int offset | + PointsToInternal::pointsTo(call.getFunction(), _, called, _) and + called.functionAndOffset(this, offset) + | + call.getArgByName(name) = result + or + exists(int n | + call.getArg(n) = result and + this.getParameter(n + offset).getId() = name + // TODO: and not positional only argument (Python 3.8+) + ) + or + called instanceof BoundMethodObjectInternal and + offset = 1 and + name = "self" and + result = call.getFunction().(AttrNode).getObject() + ) + } } /** @@ -454,209 +454,209 @@ class CallableValue extends Value { * of a class that has a callable attribute `func`. */ class BoundMethodValue extends CallableValue { - BoundMethodValue() { this instanceof BoundMethodObjectInternal } + BoundMethodValue() { this instanceof BoundMethodObjectInternal } - /** - * Gets the callable that will be used when `this` is called. - * The actual callable for `func` in `o.func`. - */ - CallableValue getFunction() { result = this.(BoundMethodObjectInternal).getFunction() } + /** + * Gets the callable that will be used when `this` is called. + * The actual callable for `func` in `o.func`. + */ + CallableValue getFunction() { result = this.(BoundMethodObjectInternal).getFunction() } - /** - * Gets the value that will be used for the `self` parameter when `this` is called. - * The value for `o` in `o.func`. - */ - Value getSelf() { result = this.(BoundMethodObjectInternal).getSelf() } + /** + * Gets the value that will be used for the `self` parameter when `this` is called. + * The value for `o` in `o.func`. + */ + Value getSelf() { result = this.(BoundMethodObjectInternal).getSelf() } - /** Gets the parameter node that will be used for `self`. */ - NameNode getSelfParameter() { result = this.(BoundMethodObjectInternal).getSelfParameter() } + /** Gets the parameter node that will be used for `self`. */ + NameNode getSelfParameter() { result = this.(BoundMethodObjectInternal).getSelfParameter() } } /** * Class representing classes in the Python program, both Python and built-in. */ class ClassValue extends Value { - ClassValue() { this.(ObjectInternal).isClass() = true } + ClassValue() { this.(ObjectInternal).isClass() = true } - /** Gets an improper super type of this class. */ - ClassValue getASuperType() { result = this.getABaseType*() } + /** Gets an improper super type of this class. */ + ClassValue getASuperType() { result = this.getABaseType*() } - /** - * Looks up the attribute `name` on this class. - * Note that this may be different from `this.attr(name)`. - * For example given the class: - * ```class C: - * @classmethod - * def f(cls): pass - * ``` - * `this.lookup("f")` is equivalent to `C.__dict__['f']`, which is the class-method - * whereas - * `this.attr("f")` is equivalent to `C.f`, which is a bound-method. + /** + * Looks up the attribute `name` on this class. + * Note that this may be different from `this.attr(name)`. + * For example given the class: + * ```class C: + * @classmethod + * def f(cls): pass + * ``` + * `this.lookup("f")` is equivalent to `C.__dict__['f']`, which is the class-method + * whereas + * `this.attr("f")` is equivalent to `C.f`, which is a bound-method. + */ + Value lookup(string name) { this.(ClassObjectInternal).lookup(name, result, _) } + + predicate isCallable() { this.(ClassObjectInternal).lookup("__call__", _, _) } + + /** Holds if this class is an iterable. */ + predicate isIterable() { + this.hasAttribute("__iter__") + or + this.hasAttribute("__aiter__") + or + this.hasAttribute("__getitem__") + } + + /** Holds if this class is an iterator. */ + predicate isIterator() { + this.hasAttribute("__iter__") and + ( + major_version() = 3 and this.hasAttribute("__next__") + or + /* + * Because 'next' is a common method name we need to check that an __iter__ + * method actually returns this class. This is not needed for Py3 as the + * '__next__' method exists to define a class as an iterator. + */ + + major_version() = 2 and + this.hasAttribute("next") and + exists(ClassValue other, FunctionValue iter | other.declaredAttribute("__iter__") = iter | + iter.getAnInferredReturnType() = this + ) + ) + or + /* This will be redundant when we have C class information */ + this = ClassValue::generator() + } + + /** Holds if this class is a container(). That is, does it have a __getitem__ method. */ + predicate isContainer() { exists(this.lookup("__getitem__")) } + + /** + * Holds if this class is a sequence. Mutually exclusive with `isMapping()`. + * + * Following the definition from + * https://docs.python.org/3/glossary.html#term-sequence. + * We don't look at the keys accepted by `__getitem__, but default to treating a class + * as a sequence (so might treat some mappings as sequences). + */ + predicate isSequence() { + /* + * To determine whether something is a sequence or a mapping is not entirely clear, + * so we need to guess a bit. */ - Value lookup(string name) { this.(ClassObjectInternal).lookup(name, result, _) } - predicate isCallable() { this.(ClassObjectInternal).lookup("__call__", _, _) } + this.getASuperType() = ClassValue::tuple() + or + this.getASuperType() = ClassValue::list() + or + this.getASuperType() = ClassValue::range() + or + this.getASuperType() = ClassValue::bytes() + or + this.getASuperType() = ClassValue::unicode() + or + major_version() = 2 and this.getASuperType() = Value::named("collections.Sequence") + or + major_version() = 3 and this.getASuperType() = Value::named("collections.abc.Sequence") + or + this.hasAttribute("__getitem__") and + this.hasAttribute("__len__") and + not this.getASuperType() = ClassValue::dict() and + not this.getASuperType() = Value::named("collections.Mapping") and + not this.getASuperType() = Value::named("collections.abc.Mapping") + } - /** Holds if this class is an iterable. */ - predicate isIterable() { - this.hasAttribute("__iter__") - or - this.hasAttribute("__aiter__") - or - this.hasAttribute("__getitem__") - } + /** + * Holds if this class is a mapping. Mutually exclusive with `isSequence()`. + * + * Although a class will satisfy the requirement by the definition in + * https://docs.python.org/3.8/glossary.html#term-mapping, we don't look at the keys + * accepted by `__getitem__, but default to treating a class as a sequence (so might + * treat some mappings as sequences). + */ + predicate isMapping() { + major_version() = 2 and this.getASuperType() = Value::named("collections.Mapping") + or + major_version() = 3 and this.getASuperType() = Value::named("collections.abc.Mapping") + or + this.hasAttribute("__getitem__") and + not this.isSequence() + } - /** Holds if this class is an iterator. */ - predicate isIterator() { - this.hasAttribute("__iter__") and - ( - major_version() = 3 and this.hasAttribute("__next__") - or - /* - * Because 'next' is a common method name we need to check that an __iter__ - * method actually returns this class. This is not needed for Py3 as the - * '__next__' method exists to define a class as an iterator. - */ + /** Holds if this class is a descriptor. */ + predicate isDescriptorType() { this.hasAttribute("__get__") } - major_version() = 2 and - this.hasAttribute("next") and - exists(ClassValue other, FunctionValue iter | other.declaredAttribute("__iter__") = iter | - iter.getAnInferredReturnType() = this - ) - ) - or - /* This will be redundant when we have C class information */ - this = ClassValue::generator() - } + /** Holds if this class is a context manager. */ + predicate isContextManager() { + this.hasAttribute("__enter__") and + this.hasAttribute("__exit__") + } - /** Holds if this class is a container(). That is, does it have a __getitem__ method. */ - predicate isContainer() { exists(this.lookup("__getitem__")) } + /** + * Gets the qualified name for this class. + * Should return the same name as the `__qualname__` attribute on classes in Python 3. + */ + string getQualifiedName() { + result = this.(ClassObjectInternal).getBuiltin().getName() + or + result = this.(PythonClassObjectInternal).getScope().getQualifiedName() + } - /** - * Holds if this class is a sequence. Mutually exclusive with `isMapping()`. - * - * Following the definition from - * https://docs.python.org/3/glossary.html#term-sequence. - * We don't look at the keys accepted by `__getitem__, but default to treating a class - * as a sequence (so might treat some mappings as sequences). - */ - predicate isSequence() { - /* - * To determine whether something is a sequence or a mapping is not entirely clear, - * so we need to guess a bit. - */ + /** Gets the MRO for this class */ + MRO getMro() { result = Types::getMro(this) } - this.getASuperType() = ClassValue::tuple() - or - this.getASuperType() = ClassValue::list() - or - this.getASuperType() = ClassValue::range() - or - this.getASuperType() = ClassValue::bytes() - or - this.getASuperType() = ClassValue::unicode() - or - major_version() = 2 and this.getASuperType() = Value::named("collections.Sequence") - or - major_version() = 3 and this.getASuperType() = Value::named("collections.abc.Sequence") - or - this.hasAttribute("__getitem__") and - this.hasAttribute("__len__") and - not this.getASuperType() = ClassValue::dict() and - not this.getASuperType() = Value::named("collections.Mapping") and - not this.getASuperType() = Value::named("collections.abc.Mapping") - } + predicate failedInference(string reason) { Types::failedInference(this, reason) } - /** - * Holds if this class is a mapping. Mutually exclusive with `isSequence()`. - * - * Although a class will satisfy the requirement by the definition in - * https://docs.python.org/3.8/glossary.html#term-mapping, we don't look at the keys - * accepted by `__getitem__, but default to treating a class as a sequence (so might - * treat some mappings as sequences). - */ - predicate isMapping() { - major_version() = 2 and this.getASuperType() = Value::named("collections.Mapping") - or - major_version() = 3 and this.getASuperType() = Value::named("collections.abc.Mapping") - or - this.hasAttribute("__getitem__") and - not this.isSequence() - } + /** Gets the nth immediate base type of this class. */ + ClassValue getBaseType(int n) { result = Types::getBase(this, n) } - /** Holds if this class is a descriptor. */ - predicate isDescriptorType() { this.hasAttribute("__get__") } + /** Gets an immediate base type of this class. */ + ClassValue getABaseType() { result = Types::getBase(this, _) } - /** Holds if this class is a context manager. */ - predicate isContextManager() { - this.hasAttribute("__enter__") and - this.hasAttribute("__exit__") - } + /** + * Holds if this class is a new style class. + * A new style class is one that implicitly or explicitly inherits from `object`. + */ + predicate isNewStyle() { Types::isNewStyle(this) } - /** - * Gets the qualified name for this class. - * Should return the same name as the `__qualname__` attribute on classes in Python 3. - */ - string getQualifiedName() { - result = this.(ClassObjectInternal).getBuiltin().getName() - or - result = this.(PythonClassObjectInternal).getScope().getQualifiedName() - } + /** + * Holds if this class is an old style class. + * An old style class is one that does not inherit from `object`. + */ + predicate isOldStyle() { Types::isOldStyle(this) } - /** Gets the MRO for this class */ - MRO getMro() { result = Types::getMro(this) } + /** Gets the scope associated with this class, if it is not a builtin class */ + ClassScope getScope() { result = this.(PythonClassObjectInternal).getScope() } - predicate failedInference(string reason) { Types::failedInference(this, reason) } + /** Gets the attribute declared in this class */ + Value declaredAttribute(string name) { Types::declaredAttribute(this, name, result, _) } - /** Gets the nth immediate base type of this class. */ - ClassValue getBaseType(int n) { result = Types::getBase(this, n) } + /** + * Holds if this class has the attribute `name`, including attributes + * declared by super classes. + */ + override predicate hasAttribute(string name) { this.getMro().declares(name) } - /** Gets an immediate base type of this class. */ - ClassValue getABaseType() { result = Types::getBase(this, _) } + /** + * Holds if this class declares the attribute `name`, + * *not* including attributes declared by super classes. + */ + predicate declaresAttribute(string name) { + this.(ClassObjectInternal).getClassDeclaration().declaresAttribute(name) + } - /** - * Holds if this class is a new style class. - * A new style class is one that implicitly or explicitly inherits from `object`. - */ - predicate isNewStyle() { Types::isNewStyle(this) } - - /** - * Holds if this class is an old style class. - * An old style class is one that does not inherit from `object`. - */ - predicate isOldStyle() { Types::isOldStyle(this) } - - /** Gets the scope associated with this class, if it is not a builtin class */ - ClassScope getScope() { result = this.(PythonClassObjectInternal).getScope() } - - /** Gets the attribute declared in this class */ - Value declaredAttribute(string name) { Types::declaredAttribute(this, name, result, _) } - - /** - * Holds if this class has the attribute `name`, including attributes - * declared by super classes. - */ - override predicate hasAttribute(string name) { this.getMro().declares(name) } - - /** - * Holds if this class declares the attribute `name`, - * *not* including attributes declared by super classes. - */ - predicate declaresAttribute(string name) { - this.(ClassObjectInternal).getClassDeclaration().declaresAttribute(name) - } - - /** - * Whether this class is a legal exception class. - * What constitutes a legal exception class differs between major versions - */ - predicate isLegalExceptionType() { - not this.isNewStyle() - or - this.getASuperType() = ClassValue::baseException() - or - major_version() = 2 and this = ClassValue::tuple() - } + /** + * Whether this class is a legal exception class. + * What constitutes a legal exception class differs between major versions + */ + predicate isLegalExceptionType() { + not this.isNewStyle() + or + this.getASuperType() = ClassValue::baseException() + or + major_version() = 2 and this = ClassValue::tuple() + } } /** @@ -664,182 +664,182 @@ class ClassValue extends Value { * Note that this does not include other callables such as bound-methods. */ abstract class FunctionValue extends CallableValue { - /** - * Gets the qualified name for this function. - * Should return the same name as the `__qualname__` attribute on functions in Python 3. - */ - abstract string getQualifiedName(); + /** + * Gets the qualified name for this function. + * Should return the same name as the `__qualname__` attribute on functions in Python 3. + */ + abstract string getQualifiedName(); - /** Gets a longer, more descriptive version of toString() */ - abstract string descriptiveString(); + /** Gets a longer, more descriptive version of toString() */ + abstract string descriptiveString(); - /** Gets the minimum number of parameters that can be correctly passed to this function */ - abstract int minParameters(); + /** Gets the minimum number of parameters that can be correctly passed to this function */ + abstract int minParameters(); - /** Gets the maximum number of parameters that can be correctly passed to this function */ - abstract int maxParameters(); + /** Gets the maximum number of parameters that can be correctly passed to this function */ + abstract int maxParameters(); - predicate isOverridingMethod() { exists(Value f | this.overrides(f)) } + predicate isOverridingMethod() { exists(Value f | this.overrides(f)) } - predicate isOverriddenMethod() { exists(Value f | f.overrides(this)) } + predicate isOverriddenMethod() { exists(Value f | f.overrides(this)) } - /** Whether `name` is a legal argument name for this function */ - bindingset[name] - predicate isLegalArgumentName(string name) { - this.getScope().getAnArg().asName().getId() = name - or - this.getScope().getAKeywordOnlyArg().getId() = name - or - this.getScope().hasKwArg() - } + /** Whether `name` is a legal argument name for this function */ + bindingset[name] + predicate isLegalArgumentName(string name) { + this.getScope().getAnArg().asName().getId() = name + or + this.getScope().getAKeywordOnlyArg().getId() = name + or + this.getScope().hasKwArg() + } - /** - * Whether this is a "normal" method, that is, it is exists as a class attribute - * which is not a lambda and not the __new__ method. - */ - predicate isNormalMethod() { - exists(ClassValue cls, string name | - cls.declaredAttribute(name) = this and - name != "__new__" and - exists(Expr expr, AstNode origin | expr.pointsTo(this, origin) | not origin instanceof Lambda) - ) - } + /** + * Whether this is a "normal" method, that is, it is exists as a class attribute + * which is not a lambda and not the __new__ method. + */ + predicate isNormalMethod() { + exists(ClassValue cls, string name | + cls.declaredAttribute(name) = this and + name != "__new__" and + exists(Expr expr, AstNode origin | expr.pointsTo(this, origin) | not origin instanceof Lambda) + ) + } - /** Gets a class that may be raised by this function */ - abstract ClassValue getARaisedType(); + /** Gets a class that may be raised by this function */ + abstract ClassValue getARaisedType(); - /** Gets a call-site from where this function is called as a function */ - CallNode getAFunctionCall() { result.getFunction().pointsTo() = this } + /** Gets a call-site from where this function is called as a function */ + CallNode getAFunctionCall() { result.getFunction().pointsTo() = this } - /** Gets a call-site from where this function is called as a method */ - CallNode getAMethodCall() { - exists(BoundMethodObjectInternal bm | - result.getFunction().pointsTo() = bm and - bm.getFunction() = this - ) - } + /** Gets a call-site from where this function is called as a method */ + CallNode getAMethodCall() { + exists(BoundMethodObjectInternal bm | + result.getFunction().pointsTo() = bm and + bm.getFunction() = this + ) + } - /** Gets a class that this function may return */ - abstract ClassValue getAnInferredReturnType(); + /** Gets a class that this function may return */ + abstract ClassValue getAnInferredReturnType(); } /** Class representing Python functions */ class PythonFunctionValue extends FunctionValue { - PythonFunctionValue() { this instanceof PythonFunctionObjectInternal } + PythonFunctionValue() { this instanceof PythonFunctionObjectInternal } - override string getQualifiedName() { - result = this.(PythonFunctionObjectInternal).getScope().getQualifiedName() - } + override string getQualifiedName() { + result = this.(PythonFunctionObjectInternal).getScope().getQualifiedName() + } - override string descriptiveString() { - if this.getScope().isMethod() - then - exists(Class cls | this.getScope().getScope() = cls | - result = "method " + this.getQualifiedName() - ) - else result = "function " + this.getQualifiedName() - } + override string descriptiveString() { + if this.getScope().isMethod() + then + exists(Class cls | this.getScope().getScope() = cls | + result = "method " + this.getQualifiedName() + ) + else result = "function " + this.getQualifiedName() + } - override int minParameters() { - exists(Function f | - f = this.getScope() and - result = count(f.getAnArg()) - count(f.getDefinition().getArgs().getADefault()) - ) - } + override int minParameters() { + exists(Function f | + f = this.getScope() and + result = count(f.getAnArg()) - count(f.getDefinition().getArgs().getADefault()) + ) + } - override int maxParameters() { - exists(Function f | - f = this.getScope() and - if exists(f.getVararg()) - then result = 2147483647 // INT_MAX - else result = count(f.getAnArg()) - ) - } + override int maxParameters() { + exists(Function f | + f = this.getScope() and + if exists(f.getVararg()) + then result = 2147483647 // INT_MAX + else result = count(f.getAnArg()) + ) + } - /** Gets a control flow node corresponding to a return statement in this function */ - ControlFlowNode getAReturnedNode() { result = this.getScope().getAReturnValueFlowNode() } + /** Gets a control flow node corresponding to a return statement in this function */ + ControlFlowNode getAReturnedNode() { result = this.getScope().getAReturnValueFlowNode() } - override ClassValue getARaisedType() { scope_raises(result, this.getScope()) } + override ClassValue getARaisedType() { scope_raises(result, this.getScope()) } - override ClassValue getAnInferredReturnType() { - /* - * We have to do a special version of this because builtin functions have no - * explicit return nodes that we can query and get the class of. - */ + override ClassValue getAnInferredReturnType() { + /* + * We have to do a special version of this because builtin functions have no + * explicit return nodes that we can query and get the class of. + */ - result = this.getAReturnedNode().pointsTo().getClass() - } + result = this.getAReturnedNode().pointsTo().getClass() + } } /** Class representing builtin functions, such as `len` or `print` */ class BuiltinFunctionValue extends FunctionValue { - BuiltinFunctionValue() { this instanceof BuiltinFunctionObjectInternal } + BuiltinFunctionValue() { this instanceof BuiltinFunctionObjectInternal } - override string getQualifiedName() { result = this.(BuiltinFunctionObjectInternal).getName() } + override string getQualifiedName() { result = this.(BuiltinFunctionObjectInternal).getName() } - override string descriptiveString() { result = "builtin-function " + this.getName() } + override string descriptiveString() { result = "builtin-function " + this.getName() } - override int minParameters() { none() } + override int minParameters() { none() } - override int maxParameters() { none() } + override int maxParameters() { none() } - override ClassValue getARaisedType() { - /* Information is unavailable for C code in general */ - none() - } + override ClassValue getARaisedType() { + /* Information is unavailable for C code in general */ + none() + } - override ClassValue getAnInferredReturnType() { - /* - * We have to do a special version of this because builtin functions have no - * explicit return nodes that we can query and get the class of. - */ + override ClassValue getAnInferredReturnType() { + /* + * We have to do a special version of this because builtin functions have no + * explicit return nodes that we can query and get the class of. + */ - result = TBuiltinClassObject(this.(BuiltinFunctionObjectInternal).getReturnType()) - } + result = TBuiltinClassObject(this.(BuiltinFunctionObjectInternal).getReturnType()) + } } /** Class representing builtin methods, such as `list.append` or `set.add` */ class BuiltinMethodValue extends FunctionValue { - BuiltinMethodValue() { this instanceof BuiltinMethodObjectInternal } + BuiltinMethodValue() { this instanceof BuiltinMethodObjectInternal } - override string getQualifiedName() { - exists(Builtin cls | - cls.isClass() and - cls.getMember(_) = this.(BuiltinMethodObjectInternal).getBuiltin() and - result = cls.getName() + "." + this.getName() - ) - } + override string getQualifiedName() { + exists(Builtin cls | + cls.isClass() and + cls.getMember(_) = this.(BuiltinMethodObjectInternal).getBuiltin() and + result = cls.getName() + "." + this.getName() + ) + } - override string descriptiveString() { result = "builtin-method " + this.getQualifiedName() } + override string descriptiveString() { result = "builtin-method " + this.getQualifiedName() } - override int minParameters() { none() } + override int minParameters() { none() } - override int maxParameters() { none() } + override int maxParameters() { none() } - override ClassValue getARaisedType() { - /* Information is unavailable for C code in general */ - none() - } + override ClassValue getARaisedType() { + /* Information is unavailable for C code in general */ + none() + } - override ClassValue getAnInferredReturnType() { - result = TBuiltinClassObject(this.(BuiltinMethodObjectInternal).getReturnType()) - } + override ClassValue getAnInferredReturnType() { + result = TBuiltinClassObject(this.(BuiltinMethodObjectInternal).getReturnType()) + } } /** * A class representing sequence objects with a length and tracked items. */ class SequenceValue extends Value { - SequenceValue() { this instanceof SequenceObjectInternal } + SequenceValue() { this instanceof SequenceObjectInternal } - Value getItem(int n) { result = this.(SequenceObjectInternal).getItem(n) } + Value getItem(int n) { result = this.(SequenceObjectInternal).getItem(n) } - int length() { result = this.(SequenceObjectInternal).length() } + int length() { result = this.(SequenceObjectInternal).length() } } /** A class representing tuple objects */ class TupleValue extends SequenceValue { - TupleValue() { this instanceof TupleObjectInternal } + TupleValue() { this instanceof TupleObjectInternal } } /** @@ -847,16 +847,16 @@ class TupleValue extends SequenceValue { * in a builtin as a value. */ class StringValue extends Value { - StringValue() { - this instanceof BytesObjectInternal or - this instanceof UnicodeObjectInternal - } + StringValue() { + this instanceof BytesObjectInternal or + this instanceof UnicodeObjectInternal + } - string getText() { - result = this.(BytesObjectInternal).strValue() - or - result = this.(UnicodeObjectInternal).strValue() - } + string getText() { + result = this.(BytesObjectInternal).strValue() + or + result = this.(UnicodeObjectInternal).strValue() + } } /** @@ -864,16 +864,16 @@ class StringValue extends Value { * or in a builtin as a value. */ class NumericValue extends Value { - NumericValue() { - this instanceof IntObjectInternal or - this instanceof FloatObjectInternal - } + NumericValue() { + this instanceof IntObjectInternal or + this instanceof FloatObjectInternal + } - /** Gets the integer-value if it is a constant integer, and it fits in a QL int */ - int getIntValue() { result = this.(IntObjectInternal).intValue() } + /** Gets the integer-value if it is a constant integer, and it fits in a QL int */ + int getIntValue() { result = this.(IntObjectInternal).intValue() } - /** Gets the float-value if it is a constant float */ - int getFloatValue() { result = this.(FloatObjectInternal).floatValue() } + /** Gets the float-value if it is a constant float */ + int getFloatValue() { result = this.(FloatObjectInternal).floatValue() } } /** @@ -886,193 +886,193 @@ class NumericValue extends Value { * https://docs.python.org/3/library/functions.html#property */ class PropertyValue extends Value { - PropertyValue() { this instanceof PropertyInternal } + PropertyValue() { this instanceof PropertyInternal } - CallableValue getGetter() { result = this.(PropertyInternal).getGetter() } + CallableValue getGetter() { result = this.(PropertyInternal).getGetter() } - CallableValue getSetter() { result = this.(PropertyInternal).getSetter() } + CallableValue getSetter() { result = this.(PropertyInternal).getSetter() } - CallableValue getDeleter() { result = this.(PropertyInternal).getDeleter() } + CallableValue getDeleter() { result = this.(PropertyInternal).getDeleter() } } /** A method-resolution-order sequence of classes */ class MRO extends TClassList { - /** Gets a textual representation of this element. */ - string toString() { result = this.(ClassList).toString() } + /** Gets a textual representation of this element. */ + string toString() { result = this.(ClassList).toString() } - /** Gets the `n`th class in this MRO */ - ClassValue getItem(int n) { result = this.(ClassList).getItem(n) } + /** Gets the `n`th class in this MRO */ + ClassValue getItem(int n) { result = this.(ClassList).getItem(n) } - /** Holds if any class in this MRO declares the attribute `name` */ - predicate declares(string name) { this.(ClassList).declares(name) } + /** Holds if any class in this MRO declares the attribute `name` */ + predicate declares(string name) { this.(ClassList).declares(name) } - /** Gets the length of this MRO */ - int length() { result = this.(ClassList).length() } + /** Gets the length of this MRO */ + int length() { result = this.(ClassList).length() } - /** Holds if this MRO contains `cls` */ - predicate contains(ClassValue cls) { this.(ClassList).contains(cls) } + /** Holds if this MRO contains `cls` */ + predicate contains(ClassValue cls) { this.(ClassList).contains(cls) } - /** Gets the value from scanning for the attribute `name` in this MRO. */ - Value lookup(string name) { this.(ClassList).lookup(name, result, _) } + /** Gets the value from scanning for the attribute `name` in this MRO. */ + Value lookup(string name) { this.(ClassList).lookup(name, result, _) } - /** - * Gets the MRO formed by removing all classes before `cls` - * from this MRO. - */ - MRO startingAt(ClassValue cls) { result = this.(ClassList).startingAt(cls) } + /** + * Gets the MRO formed by removing all classes before `cls` + * from this MRO. + */ + MRO startingAt(ClassValue cls) { result = this.(ClassList).startingAt(cls) } } module ClassValue { - /** Get the `ClassValue` for the `bool` class. */ - ClassValue bool() { result = TBuiltinClassObject(Builtin::special("bool")) } + /** Get the `ClassValue` for the `bool` class. */ + ClassValue bool() { result = TBuiltinClassObject(Builtin::special("bool")) } - /** Get the `ClassValue` for the `tuple` class. */ - ClassValue tuple() { result = TBuiltinClassObject(Builtin::special("tuple")) } + /** Get the `ClassValue` for the `tuple` class. */ + ClassValue tuple() { result = TBuiltinClassObject(Builtin::special("tuple")) } - /** Get the `ClassValue` for the `list` class. */ - ClassValue list() { result = TBuiltinClassObject(Builtin::special("list")) } + /** Get the `ClassValue` for the `list` class. */ + ClassValue list() { result = TBuiltinClassObject(Builtin::special("list")) } - /** Get the `ClassValue` for `xrange` (Python 2), or `range` (only Python 3) */ - ClassValue range() { - major_version() = 2 and result = TBuiltinClassObject(Builtin::special("xrange")) - or - major_version() = 3 and result = TBuiltinClassObject(Builtin::special("range")) - } + /** Get the `ClassValue` for `xrange` (Python 2), or `range` (only Python 3) */ + ClassValue range() { + major_version() = 2 and result = TBuiltinClassObject(Builtin::special("xrange")) + or + major_version() = 3 and result = TBuiltinClassObject(Builtin::special("range")) + } - /** Get the `ClassValue` for the `dict` class. */ - ClassValue dict() { result = TBuiltinClassObject(Builtin::special("dict")) } + /** Get the `ClassValue` for the `dict` class. */ + ClassValue dict() { result = TBuiltinClassObject(Builtin::special("dict")) } - /** Get the `ClassValue` for the `set` class. */ - ClassValue set() { result = TBuiltinClassObject(Builtin::special("set")) } + /** Get the `ClassValue` for the `set` class. */ + ClassValue set() { result = TBuiltinClassObject(Builtin::special("set")) } - /** Get the `ClassValue` for the `object` class. */ - ClassValue object() { result = TBuiltinClassObject(Builtin::special("object")) } + /** Get the `ClassValue` for the `object` class. */ + ClassValue object() { result = TBuiltinClassObject(Builtin::special("object")) } - /** Get the `ClassValue` for the `int` class. */ - ClassValue int_() { result = TBuiltinClassObject(Builtin::special("int")) } + /** Get the `ClassValue` for the `int` class. */ + ClassValue int_() { result = TBuiltinClassObject(Builtin::special("int")) } - /** Get the `ClassValue` for the `long` class. */ - ClassValue long() { result = TBuiltinClassObject(Builtin::special("long")) } + /** Get the `ClassValue` for the `long` class. */ + ClassValue long() { result = TBuiltinClassObject(Builtin::special("long")) } - /** Get the `ClassValue` for the `float` class. */ - ClassValue float_() { result = TBuiltinClassObject(Builtin::special("float")) } + /** Get the `ClassValue` for the `float` class. */ + ClassValue float_() { result = TBuiltinClassObject(Builtin::special("float")) } - /** Get the `ClassValue` for the `complex` class. */ - ClassValue complex() { result = TBuiltinClassObject(Builtin::special("complex")) } + /** Get the `ClassValue` for the `complex` class. */ + ClassValue complex() { result = TBuiltinClassObject(Builtin::special("complex")) } - /** Get the `ClassValue` for the `bytes` class (also called `str` in Python 2). */ - ClassValue bytes() { result = TBuiltinClassObject(Builtin::special("bytes")) } + /** Get the `ClassValue` for the `bytes` class (also called `str` in Python 2). */ + ClassValue bytes() { result = TBuiltinClassObject(Builtin::special("bytes")) } - /** - * Get the `ClassValue` for the class of unicode strings. - * `str` in Python 3 and `unicode` in Python 2. - */ - ClassValue unicode() { result = TBuiltinClassObject(Builtin::special("unicode")) } + /** + * Get the `ClassValue` for the class of unicode strings. + * `str` in Python 3 and `unicode` in Python 2. + */ + ClassValue unicode() { result = TBuiltinClassObject(Builtin::special("unicode")) } - /** - * Get the `ClassValue` for the `str` class. This is `bytes` in Python 2, - * and `str` in Python 3. - */ - ClassValue str() { if major_version() = 2 then result = bytes() else result = unicode() } + /** + * Get the `ClassValue` for the `str` class. This is `bytes` in Python 2, + * and `str` in Python 3. + */ + ClassValue str() { if major_version() = 2 then result = bytes() else result = unicode() } - /** Get the `ClassValue` for the `property` class. */ - ClassValue property() { result = TBuiltinClassObject(Builtin::special("property")) } + /** Get the `ClassValue` for the `property` class. */ + ClassValue property() { result = TBuiltinClassObject(Builtin::special("property")) } - /** Get the `ClassValue` for the class of Python functions. */ - ClassValue functionType() { result = TBuiltinClassObject(Builtin::special("FunctionType")) } + /** Get the `ClassValue` for the class of Python functions. */ + ClassValue functionType() { result = TBuiltinClassObject(Builtin::special("FunctionType")) } - /** Get the `ClassValue` for the class of builtin functions. */ - ClassValue builtinFunction() { result = Value::named("len").getClass() } + /** Get the `ClassValue` for the class of builtin functions. */ + ClassValue builtinFunction() { result = Value::named("len").getClass() } - /** Get the `ClassValue` for the `generatorType` class. */ - ClassValue generator() { result = TBuiltinClassObject(Builtin::special("generator")) } + /** Get the `ClassValue` for the `generatorType` class. */ + ClassValue generator() { result = TBuiltinClassObject(Builtin::special("generator")) } - /** Get the `ClassValue` for the `type` class. */ - ClassValue type() { result = TType() } + /** Get the `ClassValue` for the `type` class. */ + ClassValue type() { result = TType() } - /** Get the `ClassValue` for `ClassType`. */ - ClassValue classType() { result = TBuiltinClassObject(Builtin::special("ClassType")) } + /** Get the `ClassValue` for `ClassType`. */ + ClassValue classType() { result = TBuiltinClassObject(Builtin::special("ClassType")) } - /** Get the `ClassValue` for `InstanceType`. */ - ClassValue instanceType() { result = TBuiltinClassObject(Builtin::special("InstanceType")) } + /** Get the `ClassValue` for `InstanceType`. */ + ClassValue instanceType() { result = TBuiltinClassObject(Builtin::special("InstanceType")) } - /** Get the `ClassValue` for `super`. */ - ClassValue super_() { result = TBuiltinClassObject(Builtin::special("super")) } + /** Get the `ClassValue` for `super`. */ + ClassValue super_() { result = TBuiltinClassObject(Builtin::special("super")) } - /** Get the `ClassValue` for the `classmethod` class. */ - ClassValue classmethod() { result = TBuiltinClassObject(Builtin::special("ClassMethod")) } + /** Get the `ClassValue` for the `classmethod` class. */ + ClassValue classmethod() { result = TBuiltinClassObject(Builtin::special("ClassMethod")) } - /** Get the `ClassValue` for the `staticmethod` class. */ - ClassValue staticmethod() { result = TBuiltinClassObject(Builtin::special("StaticMethod")) } + /** Get the `ClassValue` for the `staticmethod` class. */ + ClassValue staticmethod() { result = TBuiltinClassObject(Builtin::special("StaticMethod")) } - /** Get the `ClassValue` for the `MethodType` class. */ - pragma[noinline] - ClassValue methodType() { result = TBuiltinClassObject(Builtin::special("MethodType")) } + /** Get the `ClassValue` for the `MethodType` class. */ + pragma[noinline] + ClassValue methodType() { result = TBuiltinClassObject(Builtin::special("MethodType")) } - /** Get the `ClassValue` for the `MethodDescriptorType` class. */ - ClassValue methodDescriptorType() { - result = TBuiltinClassObject(Builtin::special("MethodDescriptorType")) - } + /** Get the `ClassValue` for the `MethodDescriptorType` class. */ + ClassValue methodDescriptorType() { + result = TBuiltinClassObject(Builtin::special("MethodDescriptorType")) + } - /** Get the `ClassValue` for the `GetSetDescriptorType` class. */ - ClassValue getSetDescriptorType() { - result = TBuiltinClassObject(Builtin::special("GetSetDescriptorType")) - } + /** Get the `ClassValue` for the `GetSetDescriptorType` class. */ + ClassValue getSetDescriptorType() { + result = TBuiltinClassObject(Builtin::special("GetSetDescriptorType")) + } - /** Get the `ClassValue` for the `StopIteration` class. */ - ClassValue stopIteration() { result = TBuiltinClassObject(Builtin::builtin("StopIteration")) } + /** Get the `ClassValue` for the `StopIteration` class. */ + ClassValue stopIteration() { result = TBuiltinClassObject(Builtin::builtin("StopIteration")) } - /** Get the `ClassValue` for the class of modules. */ - ClassValue module_() { result = TBuiltinClassObject(Builtin::special("ModuleType")) } + /** Get the `ClassValue` for the class of modules. */ + ClassValue module_() { result = TBuiltinClassObject(Builtin::special("ModuleType")) } - /** Get the `ClassValue` for the `Exception` class. */ - ClassValue exception() { result = TBuiltinClassObject(Builtin::special("Exception")) } + /** Get the `ClassValue` for the `Exception` class. */ + ClassValue exception() { result = TBuiltinClassObject(Builtin::special("Exception")) } - /** Get the `ClassValue` for the `BaseException` class. */ - ClassValue baseException() { result = TBuiltinClassObject(Builtin::special("BaseException")) } + /** Get the `ClassValue` for the `BaseException` class. */ + ClassValue baseException() { result = TBuiltinClassObject(Builtin::special("BaseException")) } - /** Get the `ClassValue` for the `NoneType` class. */ - ClassValue nonetype() { result = TBuiltinClassObject(Builtin::special("NoneType")) } + /** Get the `ClassValue` for the `NoneType` class. */ + ClassValue nonetype() { result = TBuiltinClassObject(Builtin::special("NoneType")) } - /** Get the `ClassValue` for the `TypeError` class */ - ClassValue typeError() { result = TBuiltinClassObject(Builtin::special("TypeError")) } + /** Get the `ClassValue` for the `TypeError` class */ + ClassValue typeError() { result = TBuiltinClassObject(Builtin::special("TypeError")) } - /** Get the `ClassValue` for the `NameError` class. */ - ClassValue nameError() { result = TBuiltinClassObject(Builtin::builtin("NameError")) } + /** Get the `ClassValue` for the `NameError` class. */ + ClassValue nameError() { result = TBuiltinClassObject(Builtin::builtin("NameError")) } - /** Get the `ClassValue` for the `AttributeError` class. */ - ClassValue attributeError() { result = TBuiltinClassObject(Builtin::builtin("AttributeError")) } + /** Get the `ClassValue` for the `AttributeError` class. */ + ClassValue attributeError() { result = TBuiltinClassObject(Builtin::builtin("AttributeError")) } - /** Get the `ClassValue` for the `KeyError` class. */ - ClassValue keyError() { result = TBuiltinClassObject(Builtin::builtin("KeyError")) } + /** Get the `ClassValue` for the `KeyError` class. */ + ClassValue keyError() { result = TBuiltinClassObject(Builtin::builtin("KeyError")) } - /** Get the `ClassValue` for the `LookupError` class. */ - ClassValue lookupError() { result = TBuiltinClassObject(Builtin::builtin("LookupError")) } + /** Get the `ClassValue` for the `LookupError` class. */ + ClassValue lookupError() { result = TBuiltinClassObject(Builtin::builtin("LookupError")) } - /** Get the `ClassValue` for the `IndexError` class. */ - ClassValue indexError() { result = TBuiltinClassObject(Builtin::builtin("IndexError")) } + /** Get the `ClassValue` for the `IndexError` class. */ + ClassValue indexError() { result = TBuiltinClassObject(Builtin::builtin("IndexError")) } - /** Get the `ClassValue` for the `IOError` class. */ - ClassValue ioError() { result = TBuiltinClassObject(Builtin::builtin("IOError")) } + /** Get the `ClassValue` for the `IOError` class. */ + ClassValue ioError() { result = TBuiltinClassObject(Builtin::builtin("IOError")) } - /** Get the `ClassValue` for the `NotImplementedError` class. */ - ClassValue notImplementedError() { - result = TBuiltinClassObject(Builtin::builtin("NotImplementedError")) - } + /** Get the `ClassValue` for the `NotImplementedError` class. */ + ClassValue notImplementedError() { + result = TBuiltinClassObject(Builtin::builtin("NotImplementedError")) + } - /** Get the `ClassValue` for the `ImportError` class. */ - ClassValue importError() { result = TBuiltinClassObject(Builtin::builtin("ImportError")) } + /** Get the `ClassValue` for the `ImportError` class. */ + ClassValue importError() { result = TBuiltinClassObject(Builtin::builtin("ImportError")) } - /** Get the `ClassValue` for the `UnicodeEncodeError` class. */ - ClassValue unicodeEncodeError() { - result = TBuiltinClassObject(Builtin::builtin("UnicodeEncodeError")) - } + /** Get the `ClassValue` for the `UnicodeEncodeError` class. */ + ClassValue unicodeEncodeError() { + result = TBuiltinClassObject(Builtin::builtin("UnicodeEncodeError")) + } - /** Get the `ClassValue` for the `UnicodeDecodeError` class. */ - ClassValue unicodeDecodeError() { - result = TBuiltinClassObject(Builtin::builtin("UnicodeDecodeError")) - } + /** Get the `ClassValue` for the `UnicodeDecodeError` class. */ + ClassValue unicodeDecodeError() { + result = TBuiltinClassObject(Builtin::builtin("UnicodeDecodeError")) + } - /** Get the `ClassValue` for the `SystemExit` class. */ - ClassValue systemExit() { result = TBuiltinClassObject(Builtin::builtin("SystemExit")) } + /** Get the `ClassValue` for the `SystemExit` class. */ + ClassValue systemExit() { result = TBuiltinClassObject(Builtin::builtin("SystemExit")) } } diff --git a/python/ql/src/semmle/python/objects/ObjectInternal.qll b/python/ql/src/semmle/python/objects/ObjectInternal.qll index 21ba4b24211..f70df7f7c57 100644 --- a/python/ql/src/semmle/python/objects/ObjectInternal.qll +++ b/python/ql/src/semmle/python/objects/ObjectInternal.qll @@ -17,571 +17,571 @@ import semmle.python.objects.Sequences import semmle.python.objects.Descriptors class ObjectInternal extends TObject { - /** Gets a textual representation of this element. */ - abstract string toString(); + /** Gets a textual representation of this element. */ + abstract string toString(); - /** - * The boolean value of this object, this may be both - * true and false if the "object" represents a set of possible objects. - */ - abstract boolean booleanValue(); + /** + * The boolean value of this object, this may be both + * true and false if the "object" represents a set of possible objects. + */ + abstract boolean booleanValue(); - /** - * Holds if this object is introduced into the code base at `node` given the `context` - * This means that `node`, in `context`, points-to this object, but the object has not flowed - * there from anywhere else. - * Examples: - * * The object `None` is "introduced" by the keyword "None". - * * A bound method would be "introduced" when relevant attribute on an instance - * is accessed. In `x = X(); x.m` `x.m` introduces the bound method. - */ - abstract predicate introducedAt(ControlFlowNode node, PointsToContext context); + /** + * Holds if this object is introduced into the code base at `node` given the `context` + * This means that `node`, in `context`, points-to this object, but the object has not flowed + * there from anywhere else. + * Examples: + * * The object `None` is "introduced" by the keyword "None". + * * A bound method would be "introduced" when relevant attribute on an instance + * is accessed. In `x = X(); x.m` `x.m` introduces the bound method. + */ + abstract predicate introducedAt(ControlFlowNode node, PointsToContext context); - /** Gets the class declaration for this object, if it is a class with a declaration. */ - abstract ClassDecl getClassDeclaration(); + /** Gets the class declaration for this object, if it is a class with a declaration. */ + abstract ClassDecl getClassDeclaration(); - /** True if this "object" is a class. That is, its class inherits from `type` */ - abstract boolean isClass(); + /** True if this "object" is a class. That is, its class inherits from `type` */ + abstract boolean isClass(); - /** Gets the class of this object. */ - abstract ObjectInternal getClass(); + /** Gets the class of this object. */ + abstract ObjectInternal getClass(); - /** - * True if this "object" can be meaningfully analysed to determine the boolean value of - * equality tests on it. - * For example, `None` or `int` can be, but `int()` or an unknown string cannot. - */ - abstract predicate notTestableForEquality(); + /** + * True if this "object" can be meaningfully analysed to determine the boolean value of + * equality tests on it. + * For example, `None` or `int` can be, but `int()` or an unknown string cannot. + */ + abstract predicate notTestableForEquality(); - /** - * Gets the `Builtin` for this object, if any. - * Objects (except unknown and undefined values) should attempt to return - * exactly one result for either this method or `getOrigin()`. - */ - abstract Builtin getBuiltin(); + /** + * Gets the `Builtin` for this object, if any. + * Objects (except unknown and undefined values) should attempt to return + * exactly one result for either this method or `getOrigin()`. + */ + abstract Builtin getBuiltin(); - /** - * Gets a control flow node that represents the source origin of this - * object, if it has a meaningful location in the source code. - * This method exists primarily for providing backwards compatibility and - * locations for source objects. - * Source code objects should attempt to return exactly one result for this method. - */ - abstract ControlFlowNode getOrigin(); + /** + * Gets a control flow node that represents the source origin of this + * object, if it has a meaningful location in the source code. + * This method exists primarily for providing backwards compatibility and + * locations for source objects. + * Source code objects should attempt to return exactly one result for this method. + */ + abstract ControlFlowNode getOrigin(); - /** - * Holds if `obj` is the result of calling `this` and `origin` is - * the origin of `obj`. - * - * This is the context-insensitive version. - * Generally, if this holds for any object `obj` then `callResult/3` should never hold for that object. - */ - abstract predicate callResult(ObjectInternal obj, CfgOrigin origin); + /** + * Holds if `obj` is the result of calling `this` and `origin` is + * the origin of `obj`. + * + * This is the context-insensitive version. + * Generally, if this holds for any object `obj` then `callResult/3` should never hold for that object. + */ + abstract predicate callResult(ObjectInternal obj, CfgOrigin origin); - /** - * Holds if `obj` is the result of calling `this` and `origin` is - * the origin of `obj` with callee context `callee`. - * - * This is the context-sensitive version. - * Generally, if this holds for any object `obj` then `callResult/2` should never hold for that object. - */ - abstract predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin); + /** + * Holds if `obj` is the result of calling `this` and `origin` is + * the origin of `obj` with callee context `callee`. + * + * This is the context-sensitive version. + * Generally, if this holds for any object `obj` then `callResult/2` should never hold for that object. + */ + abstract predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin); - /** - * The integer value of things that have integer values and whose integer value is - * tracked. - * That is, some ints, mainly small numbers, and bools. - */ - abstract int intValue(); + /** + * The integer value of things that have integer values and whose integer value is + * tracked. + * That is, some ints, mainly small numbers, and bools. + */ + abstract int intValue(); - /** - * The string value of things that have string values. - * That is, strings. - */ - abstract string strValue(); + /** + * The string value of things that have string values. + * That is, strings. + */ + abstract string strValue(); - /** - * Holds if the function `scope` is called when this object is called and `paramOffset` - * is the difference from the parameter position and the argument position. - * For a normal function `paramOffset` is 0. For classes and bound-methods it is 1. - * Used by points-to to help determine flow from arguments to parameters. - */ - abstract predicate calleeAndOffset(Function scope, int paramOffset); + /** + * Holds if the function `scope` is called when this object is called and `paramOffset` + * is the difference from the parameter position and the argument position. + * For a normal function `paramOffset` is 0. For classes and bound-methods it is 1. + * Used by points-to to help determine flow from arguments to parameters. + */ + abstract predicate calleeAndOffset(Function scope, int paramOffset); - final predicate isBuiltin() { exists(this.getBuiltin()) } + final predicate isBuiltin() { exists(this.getBuiltin()) } - /** - * Holds if the result of getting the attribute `name` is `value` and that `value` comes - * from `origin`. Note this is *not* the same as class lookup. For example - * for an object `x` the attribute `name` (`x.name`) may refer to a bound-method, an attribute of the - * instance, or an attribute of the class. - */ - pragma[nomagic] - abstract predicate attribute(string name, ObjectInternal value, CfgOrigin origin); + /** + * Holds if the result of getting the attribute `name` is `value` and that `value` comes + * from `origin`. Note this is *not* the same as class lookup. For example + * for an object `x` the attribute `name` (`x.name`) may refer to a bound-method, an attribute of the + * instance, or an attribute of the class. + */ + pragma[nomagic] + abstract predicate attribute(string name, ObjectInternal value, CfgOrigin origin); - /** Holds if the attributes of this object are wholly or partly unknowable */ - abstract predicate attributesUnknown(); + /** Holds if the attributes of this object are wholly or partly unknowable */ + abstract predicate attributesUnknown(); - /** Holds if the result of subscripting this object are wholly or partly unknowable */ - abstract predicate subscriptUnknown(); + /** Holds if the result of subscripting this object are wholly or partly unknowable */ + abstract predicate subscriptUnknown(); - /** - * For backwards compatibility shim -- Not all objects have a "source". - * Objects (except unknown and undefined values) should attempt to return - * exactly one result for this method. - */ - @py_object getSource() { - result = this.getOrigin() - or - result = this.getBuiltin() - } + /** + * For backwards compatibility shim -- Not all objects have a "source". + * Objects (except unknown and undefined values) should attempt to return + * exactly one result for this method. + */ + @py_object getSource() { + result = this.getOrigin() + or + result = this.getBuiltin() + } - /** - * Holds if this object is a descriptor. - * Holds, for example, for functions and properties and not for integers. - */ - abstract boolean isDescriptor(); + /** + * Holds if this object is a descriptor. + * Holds, for example, for functions and properties and not for integers. + */ + abstract boolean isDescriptor(); - /** - * Holds if the result of attribute access on the class holding this descriptor is `value`, originating at `origin` - * For example, although `T.__dict__['name'] = classmethod(f)`, `T.name` is a bound-method, binding `f` and `T` - */ - pragma[nomagic] - abstract predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin); + /** + * Holds if the result of attribute access on the class holding this descriptor is `value`, originating at `origin` + * For example, although `T.__dict__['name'] = classmethod(f)`, `T.name` is a bound-method, binding `f` and `T` + */ + pragma[nomagic] + abstract predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin); - /** - * Holds if the result of attribute access on an instance of a class holding this descriptor is `value`, originating at `origin` - * For example, with `T.__dict__['name'] = classmethod(f)`, `T().name` is a bound-method, binding `f` and `T` - */ - pragma[nomagic] - abstract predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ); + /** + * Holds if the result of attribute access on an instance of a class holding this descriptor is `value`, originating at `origin` + * For example, with `T.__dict__['name'] = classmethod(f)`, `T().name` is a bound-method, binding `f` and `T` + */ + pragma[nomagic] + abstract predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ); - /** - * Holds if attribute lookup on this object may "bind" `instance` to `descriptor`. - * Here "bind" means that `instance` is passed to the `descriptor.__get__()` method - * at runtime. The term "bind" is used as this most likely results in a bound-method. - */ - abstract predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor); + /** + * Holds if attribute lookup on this object may "bind" `instance` to `descriptor`. + * Here "bind" means that `instance` is passed to the `descriptor.__get__()` method + * at runtime. The term "bind" is used as this most likely results in a bound-method. + */ + abstract predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor); - /** - * Gets the length of the sequence that this "object" represents. - * Always returns a value for a sequence, will be -1 if the object has no fixed length. - */ - abstract int length(); + /** + * Gets the length of the sequence that this "object" represents. + * Always returns a value for a sequence, will be -1 if the object has no fixed length. + */ + abstract int length(); - /** - * Holds if the object `function` is called when this object is called and `paramOffset` - * is the difference from the parameter position and the argument position. - * For a normal function `paramOffset` is 0. For classes and bound-methods it is 1. - * This is used to implement the `CallableValue` public API. - */ - predicate functionAndOffset(CallableObjectInternal function, int offset) { none() } + /** + * Holds if the object `function` is called when this object is called and `paramOffset` + * is the difference from the parameter position and the argument position. + * For a normal function `paramOffset` is 0. For classes and bound-methods it is 1. + * This is used to implement the `CallableValue` public API. + */ + predicate functionAndOffset(CallableObjectInternal function, int offset) { none() } - /** - * Holds if this 'object' represents an entity that should be exposed to the legacy points_to API - * This should hold for almost all objects that do not have an underlying DB object representing their source, - * for example `super` objects and bound-method. This should not hold for objects that are inferred to exists by - * an import statements or the like, but which aren't in the database. - */ - /* This predicate can be removed when the legacy points_to API is removed. */ - abstract predicate useOriginAsLegacyObject(); + /** + * Holds if this 'object' represents an entity that should be exposed to the legacy points_to API + * This should hold for almost all objects that do not have an underlying DB object representing their source, + * for example `super` objects and bound-method. This should not hold for objects that are inferred to exists by + * an import statements or the like, but which aren't in the database. + */ + /* This predicate can be removed when the legacy points_to API is removed. */ + abstract predicate useOriginAsLegacyObject(); - /** - * Gets the name of this of this object if it has a meaningful name. - * Note that the name of an object is not necessarily the name by which it is called - * For example the function named `posixpath.join` will be called `os.path.join`. - */ - abstract string getName(); + /** + * Gets the name of this of this object if it has a meaningful name. + * Note that the name of an object is not necessarily the name by which it is called + * For example the function named `posixpath.join` will be called `os.path.join`. + */ + abstract string getName(); - abstract predicate contextSensitiveCallee(); + abstract predicate contextSensitiveCallee(); - /** - * Gets the 'object' resulting from iterating over this object. - * Used in the context `for i in this:`. The result is the 'object' - * assigned to `i`. - */ - abstract ObjectInternal getIterNext(); + /** + * Gets the 'object' resulting from iterating over this object. + * Used in the context `for i in this:`. The result is the 'object' + * assigned to `i`. + */ + abstract ObjectInternal getIterNext(); - /** Holds if this value has the attribute `name` */ - predicate hasAttribute(string name) { this.(ObjectInternal).attribute(name, _, _) } + /** Holds if this value has the attribute `name` */ + predicate hasAttribute(string name) { this.(ObjectInternal).attribute(name, _, _) } - abstract predicate isNotSubscriptedType(); + abstract predicate isNotSubscriptedType(); } class BuiltinOpaqueObjectInternal extends ObjectInternal, TBuiltinOpaqueObject { - override Builtin getBuiltin() { this = TBuiltinOpaqueObject(result) } + override Builtin getBuiltin() { this = TBuiltinOpaqueObject(result) } - override string toString() { result = this.getBuiltin().getClass().getName() + " object" } + override string toString() { result = this.getBuiltin().getClass().getName() + " object" } - override boolean booleanValue() { - // TO DO ... Depends on class. `result = this.getClass().instancesBooleanValue()` - result = maybe() - } + override boolean booleanValue() { + // TO DO ... Depends on class. `result = this.getClass().instancesBooleanValue()` + result = maybe() + } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = TBuiltinClassObject(this.getBuiltin().getClass()) } + override ObjectInternal getClass() { result = TBuiltinClassObject(this.getBuiltin().getClass()) } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - value = ObjectInternal::fromBuiltin(this.getBuiltin().getMember(name)) and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + value = ObjectInternal::fromBuiltin(this.getBuiltin().getMember(name)) and + origin = CfgOrigin::unknown() + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { exists(this.getBuiltin().getItem(_)) } + override predicate subscriptUnknown() { exists(this.getBuiltin().getItem(_)) } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { none() } + override int length() { none() } - override string getName() { result = this.getBuiltin().getName() } + override string getName() { result = this.getBuiltin().getName() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } + override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } class UnknownInternal extends ObjectInternal, TUnknown { - override string toString() { result = "Unknown value" } + override string toString() { result = "Unknown value" } - override boolean booleanValue() { result = maybe() } + override boolean booleanValue() { result = maybe() } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = TUnknownClass() } + override ObjectInternal getClass() { result = TUnknownClass() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override Builtin getBuiltin() { result = Builtin::unknown() } + override Builtin getBuiltin() { result = Builtin::unknown() } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() + } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } - override predicate subscriptUnknown() { any() } + override predicate subscriptUnknown() { any() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { result = -1 } + override int length() { result = -1 } - override string getName() { none() } + override string getName() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } + override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } class UndefinedInternal extends ObjectInternal, TUndefined { - override string toString() { result = "Undefined variable" } + override string toString() { result = "Undefined variable" } - override boolean booleanValue() { none() } + override boolean booleanValue() { none() } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override ObjectInternal getClass() { none() } + override ObjectInternal getClass() { none() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // Accessing an undefined value raises a NameError, but if during import it probably - // means that we missed an import. - obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // Accessing an undefined value raises a NameError, but if during import it probably + // means that we missed an import. + obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override boolean isDescriptor() { none() } + override boolean isDescriptor() { none() } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { none() } + override int length() { none() } - override string getName() { none() } + override string getName() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /** - * Holds if this object requires context to determine the object resulting from a call to it. - * True for most callables. - */ - override predicate contextSensitiveCallee() { none() } + /** + * Holds if this object requires context to determine the object resulting from a call to it. + * True for most callables. + */ + override predicate contextSensitiveCallee() { none() } - override ObjectInternal getIterNext() { none() } + override ObjectInternal getIterNext() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } module ObjectInternal { - ObjectInternal bool(boolean b) { - b = true and result = TTrue() - or - b = false and result = TFalse() - } + ObjectInternal bool(boolean b) { + b = true and result = TTrue() + or + b = false and result = TFalse() + } - ObjectInternal none_() { result = TNone() } + ObjectInternal none_() { result = TNone() } - ObjectInternal unknown() { result = TUnknown() } + ObjectInternal unknown() { result = TUnknown() } - ClassObjectInternal unknownClass() { result = TUnknownClass() } + ClassObjectInternal unknownClass() { result = TUnknownClass() } - ObjectInternal undefined() { result = TUndefined() } + ObjectInternal undefined() { result = TUndefined() } - ObjectInternal builtin(string name) { - result = TBuiltinClassObject(Builtin::builtin(name)) - or - result = TBuiltinFunctionObject(Builtin::builtin(name)) - or - result = TBuiltinOpaqueObject(Builtin::builtin(name)) - or - name = "type" and result = TType() - } + ObjectInternal builtin(string name) { + result = TBuiltinClassObject(Builtin::builtin(name)) + or + result = TBuiltinFunctionObject(Builtin::builtin(name)) + or + result = TBuiltinOpaqueObject(Builtin::builtin(name)) + or + name = "type" and result = TType() + } - ObjectInternal sysModules() { - result = TBuiltinOpaqueObject(Builtin::special("sys").getMember("modules")) - } + ObjectInternal sysModules() { + result = TBuiltinOpaqueObject(Builtin::special("sys").getMember("modules")) + } - ObjectInternal fromInt(int n) { result = TInt(n) } + ObjectInternal fromInt(int n) { result = TInt(n) } - ObjectInternal fromBuiltin(Builtin b) { - b = result.getBuiltin() and - not b = Builtin::unknown() and - not b = Builtin::unknownType() and - not b = Builtin::special("sys").getMember("version_info") - or - b = Builtin::special("sys").getMember("version_info") and result = TSysVersionInfo() - } + ObjectInternal fromBuiltin(Builtin b) { + b = result.getBuiltin() and + not b = Builtin::unknown() and + not b = Builtin::unknownType() and + not b = Builtin::special("sys").getMember("version_info") + or + b = Builtin::special("sys").getMember("version_info") and result = TSysVersionInfo() + } - ObjectInternal classMethod() { result = TBuiltinClassObject(Builtin::special("ClassMethod")) } + ObjectInternal classMethod() { result = TBuiltinClassObject(Builtin::special("ClassMethod")) } - ObjectInternal staticMethod() { result = TBuiltinClassObject(Builtin::special("StaticMethod")) } + ObjectInternal staticMethod() { result = TBuiltinClassObject(Builtin::special("StaticMethod")) } - ObjectInternal boundMethod() { result = TBuiltinClassObject(Builtin::special("MethodType")) } + ObjectInternal boundMethod() { result = TBuiltinClassObject(Builtin::special("MethodType")) } - ObjectInternal moduleType() { result = TBuiltinClassObject(Builtin::special("ModuleType")) } + ObjectInternal moduleType() { result = TBuiltinClassObject(Builtin::special("ModuleType")) } - ObjectInternal noneType() { result = TBuiltinClassObject(Builtin::special("NoneType")) } + ObjectInternal noneType() { result = TBuiltinClassObject(Builtin::special("NoneType")) } - ObjectInternal type() { result = TType() } + ObjectInternal type() { result = TType() } - ObjectInternal property() { result = TBuiltinClassObject(Builtin::special("property")) } + ObjectInternal property() { result = TBuiltinClassObject(Builtin::special("property")) } - ObjectInternal superType() { result = TBuiltinClassObject(Builtin::special("super")) } + ObjectInternal superType() { result = TBuiltinClassObject(Builtin::special("super")) } - /** The old-style class type (Python 2 only) */ - ObjectInternal classType() { result = TBuiltinClassObject(Builtin::special("ClassType")) } + /** The old-style class type (Python 2 only) */ + ObjectInternal classType() { result = TBuiltinClassObject(Builtin::special("ClassType")) } - ObjectInternal emptyTuple() { result.(BuiltinTupleObjectInternal).length() = 0 } + ObjectInternal emptyTuple() { result.(BuiltinTupleObjectInternal).length() = 0 } } class DecoratedFunction extends ObjectInternal, TDecoratedFunction { - CallNode getDecoratorCall() { this = TDecoratedFunction(result) } + CallNode getDecoratorCall() { this = TDecoratedFunction(result) } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - private ObjectInternal decoratedObject() { - PointsTo::pointsTo(this.getDecoratorCall().getArg(0), _, result, _) - } + private ObjectInternal decoratedObject() { + PointsTo::pointsTo(this.getDecoratorCall().getArg(0), _, result, _) + } - override string getName() { result = this.decoratedObject().getName() } + override string getName() { result = this.decoratedObject().getName() } - override string toString() { - result = "Decorated " + this.decoratedObject().toString() - or - not exists(this.decoratedObject()) and result = "Decorated function" - } + override string toString() { + result = "Decorated " + this.decoratedObject().toString() + or + not exists(this.decoratedObject()) and result = "Decorated function" + } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = TUnknownClass() } + override ObjectInternal getClass() { result = TUnknownClass() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() + } - override ControlFlowNode getOrigin() { result = this.getDecoratorCall() } + override ControlFlowNode getOrigin() { result = this.getDecoratorCall() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - override predicate attributesUnknown() { none() } + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { none() } + override int length() { none() } - override ObjectInternal getIterNext() { none() } + override ObjectInternal getIterNext() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** Helper for boolean predicates returning both `true` and `false` */ @@ -590,5 +590,5 @@ boolean maybe() { result = true or result = false } /** Helper for attributes */ pragma[nomagic] predicate receiver_type(AttrNode attr, string name, ObjectInternal value, ClassObjectInternal cls) { - PointsToInternal::pointsTo(attr.getObject(name), _, value, _) and value.getClass() = cls + PointsToInternal::pointsTo(attr.getObject(name), _, value, _) and value.getClass() = cls } diff --git a/python/ql/src/semmle/python/objects/Sequences.qll b/python/ql/src/semmle/python/objects/Sequences.qll index 1259aadc07b..c1aba84b137 100644 --- a/python/ql/src/semmle/python/objects/Sequences.qll +++ b/python/ql/src/semmle/python/objects/Sequences.qll @@ -7,187 +7,187 @@ private import semmle.python.pointsto.MRO private import semmle.python.types.Builtins abstract class SequenceObjectInternal extends ObjectInternal { - /** Gets the `n`th item of this sequence, if one exists. */ - abstract ObjectInternal getItem(int n); + /** Gets the `n`th item of this sequence, if one exists. */ + abstract ObjectInternal getItem(int n); - override boolean booleanValue() { - this.length() = 0 and result = false - or - this.length() != 0 and result = true - } + override boolean booleanValue() { + this.length() = 0 and result = false + or + this.length() != 0 and result = true + } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override string getName() { none() } + override string getName() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override ObjectInternal getIterNext() { result = this.getItem(_) } + override ObjectInternal getIterNext() { result = this.getItem(_) } } abstract class TupleObjectInternal extends SequenceObjectInternal { - override string toString() { result = "(" + this.contents(0) + ")" } + override string toString() { result = "(" + this.contents(0) + ")" } - private string contents(int n) { - n < 4 and n = this.length() and result = "" - or - n = 3 and this.length() > 3 and result = (this.length() - 3).toString() + " more..." - or - result = this.item(n) + ", " + this.contents(n + 1) - } + private string contents(int n) { + n < 4 and n = this.length() and result = "" + or + n = 3 and this.length() > 3 and result = (this.length() - 3).toString() + " more..." + or + result = this.item(n) + ", " + this.contents(n + 1) + } - private string item(int n) { - exists(ObjectInternal item | item = this.getItem(n) | - // To avoid infinite recursion, nested tuples are replaced with the string "...". - if item instanceof TupleObjectInternal then result = "(...)" else result = item.toString() - ) - or - n in [0 .. this.length() - 1] and - not exists(this.getItem(n)) and - result = "?" - } + private string item(int n) { + exists(ObjectInternal item | item = this.getItem(n) | + // To avoid infinite recursion, nested tuples are replaced with the string "...". + if item instanceof TupleObjectInternal then result = "(...)" else result = item.toString() + ) + or + n in [0 .. this.length() - 1] and + not exists(this.getItem(n)) and + result = "?" + } - /** Gets the class declaration for this object, if it is a declared class. */ - override ClassDecl getClassDeclaration() { none() } + /** Gets the class declaration for this object, if it is a declared class. */ + override ClassDecl getClassDeclaration() { none() } - /** True if this "object" is a class. */ - override boolean isClass() { result = false } + /** True if this "object" is a class. */ + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = ObjectInternal::builtin("tuple") } + override ObjectInternal getClass() { result = ObjectInternal::builtin("tuple") } - /** - * True if this "object" can be meaningfully analysed for - * truth or false in comparisons. For example, `None` or `int` can be, but `int()` - * or an unknown string cannot. - */ - override predicate notTestableForEquality() { none() } + /** + * True if this "object" can be meaningfully analysed for + * truth or false in comparisons. For example, `None` or `int` can be, but `int()` + * or an unknown string cannot. + */ + override predicate notTestableForEquality() { none() } - /** - * Holds if `obj` is the result of calling `this` and `origin` is - * the origin of `obj`. - */ - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + /** + * Holds if `obj` is the result of calling `this` and `origin` is + * the origin of `obj`. + */ + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - /** - * Holds if `obj` is the result of calling `this` and `origin` is - * the origin of `obj` with callee context `callee`. - */ - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + /** + * Holds if `obj` is the result of calling `this` and `origin` is + * the origin of `obj` with callee context `callee`. + */ + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - /** - * The integer value of things that have integer values. - * That is, ints and bools. - */ - override int intValue() { none() } + /** + * The integer value of things that have integer values. + * That is, ints and bools. + */ + override int intValue() { none() } - /** - * The integer value of things that have integer values. - * That is, strings. - */ - override string strValue() { none() } + /** + * The integer value of things that have integer values. + * That is, strings. + */ + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } } /** A tuple built-in to the interpreter, including the empty tuple. */ class BuiltinTupleObjectInternal extends TBuiltinTuple, TupleObjectInternal { - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override Builtin getBuiltin() { this = TBuiltinTuple(result) } + override Builtin getBuiltin() { this = TBuiltinTuple(result) } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override ObjectInternal getItem(int n) { result.getBuiltin() = this.getBuiltin().getItem(n) } + override ObjectInternal getItem(int n) { result.getBuiltin() = this.getBuiltin().getItem(n) } - override int length() { - exists(Builtin b | - b = this.getBuiltin() and - result = count(int n | exists(b.getItem(n))) - ) - } + override int length() { + exists(Builtin b | + b = this.getBuiltin() and + result = count(int n | exists(b.getItem(n))) + ) + } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** A tuple declared by a tuple expression in the Python source code */ class PythonTupleObjectInternal extends TPythonTuple, TupleObjectInternal { - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - this = TPythonTuple(node, context) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + this = TPythonTuple(node, context) + } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { this = TPythonTuple(result, _) } + override ControlFlowNode getOrigin() { this = TPythonTuple(result, _) } - override ObjectInternal getItem(int n) { - exists(TupleNode t, PointsToContext context | - this = TPythonTuple(t, context) and - PointsToInternal::pointsTo(t.getElement(n), context, result, _) - ) - } + override ObjectInternal getItem(int n) { + exists(TupleNode t, PointsToContext context | + this = TPythonTuple(t, context) and + PointsToInternal::pointsTo(t.getElement(n), context, result, _) + ) + } - override int length() { - exists(TupleNode t | - this = TPythonTuple(t, _) and - result = count(int n | exists(t.getElement(n))) - ) - } + override int length() { + exists(TupleNode t | + this = TPythonTuple(t, _) and + result = count(int n | exists(t.getElement(n))) + ) + } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** A tuple created by a `*` parameter */ class VarargsTupleObjectInternal extends TVarargsTuple, TupleObjectInternal { - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override ObjectInternal getItem(int n) { - exists(CallNode call, PointsToContext context, int offset, int length | - this = TVarargsTuple(call, context, offset, length) and - n < length and - InterProceduralPointsTo::positional_argument_points_to(call, offset + n, context, result, _) - ) - } + override ObjectInternal getItem(int n) { + exists(CallNode call, PointsToContext context, int offset, int length | + this = TVarargsTuple(call, context, offset, length) and + n < length and + InterProceduralPointsTo::positional_argument_points_to(call, offset + n, context, result, _) + ) + } - override int length() { this = TVarargsTuple(_, _, _, result) } + override int length() { this = TVarargsTuple(_, _, _, result) } - override predicate useOriginAsLegacyObject() { any() } + override predicate useOriginAsLegacyObject() { any() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** @@ -195,84 +195,84 @@ class VarargsTupleObjectInternal extends TVarargsTuple, TupleObjectInternal { * false positives when we are unsure of the actual version of Python that the code is expecting. */ class SysVersionInfoObjectInternal extends TSysVersionInfo, SequenceObjectInternal { - override string toString() { result = "sys.version_info" } + override string toString() { result = "sys.version_info" } - override ObjectInternal getItem(int n) { - n = 0 and result = TInt(major_version()) - or - n = 1 and result = TInt(minor_version()) - } + override ObjectInternal getItem(int n) { + n = 0 and result = TInt(major_version()) + or + n = 1 and result = TInt(minor_version()) + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - /** Gets the class declaration for this object, if it is a declared class. */ - override ClassDecl getClassDeclaration() { - result = Builtin::special("sys").getMember("version_info").getClass() - } + /** Gets the class declaration for this object, if it is a declared class. */ + override ClassDecl getClassDeclaration() { + result = Builtin::special("sys").getMember("version_info").getClass() + } - /** True if this "object" is a class. */ - override boolean isClass() { result = false } + /** True if this "object" is a class. */ + override boolean isClass() { result = false } - override ObjectInternal getClass() { result.getBuiltin() = this.getClassDeclaration() } + override ObjectInternal getClass() { result.getBuiltin() = this.getClassDeclaration() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - /** - * Gets the `Builtin` for this object, if any. - * Objects (except unknown and undefined values) should attempt to return - * exactly one result for either this method or `getOrigin()`. - */ - override Builtin getBuiltin() { none() } + /** + * Gets the `Builtin` for this object, if any. + * Objects (except unknown and undefined values) should attempt to return + * exactly one result for either this method or `getOrigin()`. + */ + override Builtin getBuiltin() { none() } - /** - * Gets a control flow node that represents the source origin of this - * objects. - */ - override ControlFlowNode getOrigin() { none() } + /** + * Gets a control flow node that represents the source origin of this + * objects. + */ + override ControlFlowNode getOrigin() { none() } - /** - * Holds if `obj` is the result of calling `this` and `origin` is - * the origin of `obj`. - */ - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + /** + * Holds if `obj` is the result of calling `this` and `origin` is + * the origin of `obj`. + */ + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - /** - * Holds if `obj` is the result of calling `this` and `origin` is - * the origin of `obj` with callee context `callee`. - */ - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + /** + * Holds if `obj` is the result of calling `this` and `origin` is + * the origin of `obj` with callee context `callee`. + */ + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - /** - * The integer value of things that have integer values. - * That is, ints and bools. - */ - override int intValue() { none() } + /** + * The integer value of things that have integer values. + * That is, ints and bools. + */ + override int intValue() { none() } - /** - * The integer value of things that have integer values. - * That is, strings. - */ - override string strValue() { none() } + /** + * The integer value of things that have integer values. + * That is, strings. + */ + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - override predicate attributesUnknown() { none() } + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - /** - * Gets the length of the sequence that this "object" represents. - * Always returns a value for a sequence, will be -1 if object has no fixed length. - */ - override int length() { result = 5 } + /** + * Gets the length of the sequence that this "object" represents. + * Always returns a value for a sequence, will be -1 if object has no fixed length. + */ + override int length() { result = 5 } - override predicate functionAndOffset(CallableObjectInternal function, int offset) { none() } + override predicate functionAndOffset(CallableObjectInternal function, int offset) { none() } - override predicate useOriginAsLegacyObject() { any() } + override predicate useOriginAsLegacyObject() { any() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } diff --git a/python/ql/src/semmle/python/objects/TObject.qll b/python/ql/src/semmle/python/objects/TObject.qll index 8d24b9d85d2..511bde44995 100644 --- a/python/ql/src/semmle/python/objects/TObject.qll +++ b/python/ql/src/semmle/python/objects/TObject.qll @@ -12,427 +12,426 @@ private import semmle.python.pointsto.PointsToContext */ cached newtype TObject = - /** Builtin class objects */ - TBuiltinClassObject(Builtin bltn) { - bltn.isClass() and - not bltn = Builtin::unknownType() and - not bltn = Builtin::special("type") - } or - /** Builtin function objects (module members) */ - TBuiltinFunctionObject(Builtin bltn) { bltn.isFunction() } or - /** Builtin method objects (class members) */ - TBuiltinMethodObject(Builtin bltn) { bltn.isMethod() } or - /** Builtin module objects */ - TBuiltinModuleObject(Builtin bltn) { bltn.isModule() } or - /** Other builtin objects from the interpreter */ - TBuiltinOpaqueObject(Builtin bltn) { - not bltn.isClass() and - not bltn.isFunction() and - not bltn.isMethod() and - not bltn.isModule() and - not bltn.getClass() = Builtin::special("tuple") and - not exists(bltn.intValue()) and - not exists(bltn.floatValue()) and - not exists(bltn.strValue()) and - not py_special_objects(bltn, _) - } or - /** Python function objects (including lambdas) */ - TPythonFunctionObject(ControlFlowNode callable) { callable.getNode() instanceof CallableExpr } or - /** Python class objects */ - TPythonClassObject(ControlFlowNode classexpr) { classexpr.getNode() instanceof ClassExpr } or - /** Package objects */ - TPackageObject(Folder f) { isPreferredModuleForName(f, _) } or - /** Python module objects */ - TPythonModule(Module m) { - not m.isPackage() and - isPreferredModuleForName(m.getFile(), _) and - not exists(SyntaxError se | se.getFile() = m.getFile()) - } or - /** `True` */ - TTrue() or - /** `False` */ - TFalse() or - /** `None` */ - TNone() or - /** Represents any value about which nothing useful is known */ - TUnknown() or - /** Represents any value known to be a class, but not known to be any specific class */ - TUnknownClass() or - /** Represents the absence of a value. Used by points-to for tracking undefined variables */ - TUndefined() or - /** The integer `n` */ - TInt(int n) { - // Powers of 2 are used for flags - is_power_2(n) - or - // And all combinations of flags up to 2^8 - n in [0 .. 511] - or - // Any number explicitly mentioned in the source code. - exists(IntegerLiteral num | - n = num.getValue() - or - exists(UnaryExpr neg | neg.getOp() instanceof USub and neg.getOperand() = num) and - n = -num.getN().toInt() - ) - or - n = any(Builtin b).intValue() - } or - /** The float `f` */ - TFloat(float f) { f = any(FloatLiteral num).getValue() } or - /** The unicode string `s` */ - TUnicode(string s) { - // Any string explicitly mentioned in the source code. - exists(StrConst str | - s = str.getText() and - str.isUnicode() - ) - or - // Any string from the library put in the DB by the extractor. - exists(Builtin b | - s = b.strValue() and - b.getClass() = Builtin::special("unicode") - ) - or - s = "__main__" - } or - /** The byte string `s` */ - TBytes(string s) { - // Any string explicitly mentioned in the source code. - exists(StrConst str | - s = str.getText() and - not str.isUnicode() - ) - or - // Any string from the library put in the DB by the extractor. - exists(Builtin b | - s = b.strValue() and - b.getClass() = Builtin::special("bytes") - ) - or - s = "__main__" - } or - /** An instance of `cls`, instantiated at `instantiation` given the `context`. */ - TSpecificInstance(ControlFlowNode instantiation, ClassObjectInternal cls, PointsToContext context) { - PointsToInternal::pointsTo(instantiation.(CallNode).getFunction(), context, cls, _) and - cls.isSpecial() = false - or - literal_instantiation(instantiation, cls, context) - } or - /** A non-specific instance `cls` which enters the scope at `def` given the callee `context`. */ - TSelfInstance(ParameterDefinition def, PointsToContext context, PythonClassObjectInternal cls) { - self_parameter(def, context, cls) - } or - /** A bound method */ - TBoundMethod(ObjectInternal self, CallableObjectInternal function) { - any(ObjectInternal obj).binds(self, _, function) and - function.isDescriptor() = true - } or - /** Represents any value whose class is known, but nothing else */ - TUnknownInstance(BuiltinClassObjectInternal cls) { - cls != ObjectInternal::superType() and - cls != ObjectInternal::builtin("bool") and - cls != ObjectInternal::noneType() - } or - /** Represents an instance of `super` */ - TSuperInstance(ObjectInternal self, ClassObjectInternal startclass) { - super_instantiation(_, self, startclass, _) - } or - /** Represents an instance of `classmethod` */ - TClassMethod(CallNode instantiation, CallableObjectInternal function) { - class_method(instantiation, function, _) - } or - /** Represents an instance of `staticmethod` */ - TStaticMethod(CallNode instantiation, CallableObjectInternal function) { - static_method(instantiation, function, _) - } or - /** Represents a builtin tuple */ - TBuiltinTuple(Builtin bltn) { bltn.getClass() = Builtin::special("tuple") } or - /** Represents a tuple in the Python source */ - TPythonTuple(TupleNode origin, PointsToContext context) { - origin.isLoad() and - context.appliesTo(origin) - } or - /** Varargs tuple */ - TVarargsTuple(CallNode call, PointsToContext context, int offset, int length) { - InterProceduralPointsTo::varargs_tuple(call, context, _, _, offset, length) - } or - /** `type` */ - TType() or - /** Represents an instance of `property` */ - TProperty(CallNode call, Context ctx, CallableObjectInternal getter) { - PointsToInternal::pointsTo(call.getFunction(), ctx, ObjectInternal::property(), _) and - PointsToInternal::pointsTo(call.getArg(0), ctx, getter, _) - } or - /** Represents the `setter` or `deleter` method of a property object. */ - TPropertySetterOrDeleter(PropertyInternal property, string method) { - exists(AttrNode attr | PointsToInternal::pointsTo(attr.getObject(method), _, property, _)) and - (method = "setter" or method = "deleter") - } or - /** Represents a dynamically created class */ - TDynamicClass(CallNode instantiation, ClassObjectInternal metacls, PointsToContext context) { - PointsToInternal::pointsTo(instantiation.getFunction(), context, metacls, _) and - not count(instantiation.getAnArg()) = 1 and - Types::getMro(metacls).contains(TType()) - } or - /** Represents `sys.version_info`. Acts like a tuple with a range of values depending on the version being analysed. */ - TSysVersionInfo() or - /** Represents a module that is inferred to perhaps exist, but is not present in the database. */ - TAbsentModule(string name) { missing_imported_module(_, _, name) } or - /** Represents an attribute of a module that is inferred to perhaps exist, but is not present in the database. */ - TAbsentModuleAttribute(AbsentModuleObjectInternal mod, string attrname) { - ( - PointsToInternal::pointsTo(any(AttrNode attr).getObject(attrname), _, mod, _) - or - PointsToInternal::pointsTo(any(ImportMemberNode imp).getModule(attrname), _, mod, _) - ) and - exists(string modname | - modname = mod.getName() and - not common_module_name(modname + "." + attrname) - ) - } or - /** Opaque object representing the result of calling a decorator on a function that we don't understand */ - TDecoratedFunction(CallNode call) { call.isFunctionDecoratorCall() } or - /** Represents a subscript operation applied to a type. For type-hint analysis */ - TSubscriptedType(ObjectInternal generic, ObjectInternal index) { - isType(generic) and - generic.isNotSubscriptedType() and - index.isNotSubscriptedType() and - Expressions::subscriptPartsPointsTo(_, _, generic, index) - } + /** Builtin class objects */ + TBuiltinClassObject(Builtin bltn) { + bltn.isClass() and + not bltn = Builtin::unknownType() and + not bltn = Builtin::special("type") + } or + /** Builtin function objects (module members) */ + TBuiltinFunctionObject(Builtin bltn) { bltn.isFunction() } or + /** Builtin method objects (class members) */ + TBuiltinMethodObject(Builtin bltn) { bltn.isMethod() } or + /** Builtin module objects */ + TBuiltinModuleObject(Builtin bltn) { bltn.isModule() } or + /** Other builtin objects from the interpreter */ + TBuiltinOpaqueObject(Builtin bltn) { + not bltn.isClass() and + not bltn.isFunction() and + not bltn.isMethod() and + not bltn.isModule() and + not bltn.getClass() = Builtin::special("tuple") and + not exists(bltn.intValue()) and + not exists(bltn.floatValue()) and + not exists(bltn.strValue()) and + not py_special_objects(bltn, _) + } or + /** Python function objects (including lambdas) */ + TPythonFunctionObject(ControlFlowNode callable) { callable.getNode() instanceof CallableExpr } or + /** Python class objects */ + TPythonClassObject(ControlFlowNode classexpr) { classexpr.getNode() instanceof ClassExpr } or + /** Package objects */ + TPackageObject(Folder f) { isPreferredModuleForName(f, _) } or + /** Python module objects */ + TPythonModule(Module m) { + not m.isPackage() and + isPreferredModuleForName(m.getFile(), _) and + not exists(SyntaxError se | se.getFile() = m.getFile()) + } or + /** `True` */ + TTrue() or + /** `False` */ + TFalse() or + /** `None` */ + TNone() or + /** Represents any value about which nothing useful is known */ + TUnknown() or + /** Represents any value known to be a class, but not known to be any specific class */ + TUnknownClass() or + /** Represents the absence of a value. Used by points-to for tracking undefined variables */ + TUndefined() or + /** The integer `n` */ + TInt(int n) { + // Powers of 2 are used for flags + is_power_2(n) + or + // And all combinations of flags up to 2^8 + n in [0 .. 511] + or + // Any number explicitly mentioned in the source code. + exists(IntegerLiteral num | + n = num.getValue() + or + exists(UnaryExpr neg | neg.getOp() instanceof USub and neg.getOperand() = num) and + n = -num.getN().toInt() + ) + or + n = any(Builtin b).intValue() + } or + /** The float `f` */ + TFloat(float f) { f = any(FloatLiteral num).getValue() } or + /** The unicode string `s` */ + TUnicode(string s) { + // Any string explicitly mentioned in the source code. + exists(StrConst str | + s = str.getText() and + str.isUnicode() + ) + or + // Any string from the library put in the DB by the extractor. + exists(Builtin b | + s = b.strValue() and + b.getClass() = Builtin::special("unicode") + ) + or + s = "__main__" + } or + /** The byte string `s` */ + TBytes(string s) { + // Any string explicitly mentioned in the source code. + exists(StrConst str | + s = str.getText() and + not str.isUnicode() + ) + or + // Any string from the library put in the DB by the extractor. + exists(Builtin b | + s = b.strValue() and + b.getClass() = Builtin::special("bytes") + ) + or + s = "__main__" + } or + /** An instance of `cls`, instantiated at `instantiation` given the `context`. */ + TSpecificInstance(ControlFlowNode instantiation, ClassObjectInternal cls, PointsToContext context) { + PointsToInternal::pointsTo(instantiation.(CallNode).getFunction(), context, cls, _) and + cls.isSpecial() = false + or + literal_instantiation(instantiation, cls, context) + } or + /** A non-specific instance `cls` which enters the scope at `def` given the callee `context`. */ + TSelfInstance(ParameterDefinition def, PointsToContext context, PythonClassObjectInternal cls) { + self_parameter(def, context, cls) + } or + /** A bound method */ + TBoundMethod(ObjectInternal self, CallableObjectInternal function) { + any(ObjectInternal obj).binds(self, _, function) and + function.isDescriptor() = true + } or + /** Represents any value whose class is known, but nothing else */ + TUnknownInstance(BuiltinClassObjectInternal cls) { + cls != ObjectInternal::superType() and + cls != ObjectInternal::builtin("bool") and + cls != ObjectInternal::noneType() + } or + /** Represents an instance of `super` */ + TSuperInstance(ObjectInternal self, ClassObjectInternal startclass) { + super_instantiation(_, self, startclass, _) + } or + /** Represents an instance of `classmethod` */ + TClassMethod(CallNode instantiation, CallableObjectInternal function) { + class_method(instantiation, function, _) + } or + /** Represents an instance of `staticmethod` */ + TStaticMethod(CallNode instantiation, CallableObjectInternal function) { + static_method(instantiation, function, _) + } or + /** Represents a builtin tuple */ + TBuiltinTuple(Builtin bltn) { bltn.getClass() = Builtin::special("tuple") } or + /** Represents a tuple in the Python source */ + TPythonTuple(TupleNode origin, PointsToContext context) { + origin.isLoad() and + context.appliesTo(origin) + } or + /** Varargs tuple */ + TVarargsTuple(CallNode call, PointsToContext context, int offset, int length) { + InterProceduralPointsTo::varargs_tuple(call, context, _, _, offset, length) + } or + /** `type` */ + TType() or + /** Represents an instance of `property` */ + TProperty(CallNode call, Context ctx, CallableObjectInternal getter) { + PointsToInternal::pointsTo(call.getFunction(), ctx, ObjectInternal::property(), _) and + PointsToInternal::pointsTo(call.getArg(0), ctx, getter, _) + } or + /** Represents the `setter` or `deleter` method of a property object. */ + TPropertySetterOrDeleter(PropertyInternal property, string method) { + exists(AttrNode attr | PointsToInternal::pointsTo(attr.getObject(method), _, property, _)) and + (method = "setter" or method = "deleter") + } or + /** Represents a dynamically created class */ + TDynamicClass(CallNode instantiation, ClassObjectInternal metacls, PointsToContext context) { + PointsToInternal::pointsTo(instantiation.getFunction(), context, metacls, _) and + not count(instantiation.getAnArg()) = 1 and + Types::getMro(metacls).contains(TType()) + } or + /** Represents `sys.version_info`. Acts like a tuple with a range of values depending on the version being analysed. */ + TSysVersionInfo() or + /** Represents a module that is inferred to perhaps exist, but is not present in the database. */ + TAbsentModule(string name) { missing_imported_module(_, _, name) } or + /** Represents an attribute of a module that is inferred to perhaps exist, but is not present in the database. */ + TAbsentModuleAttribute(AbsentModuleObjectInternal mod, string attrname) { + ( + PointsToInternal::pointsTo(any(AttrNode attr).getObject(attrname), _, mod, _) + or + PointsToInternal::pointsTo(any(ImportMemberNode imp).getModule(attrname), _, mod, _) + ) and + exists(string modname | + modname = mod.getName() and + not common_module_name(modname + "." + attrname) + ) + } or + /** Opaque object representing the result of calling a decorator on a function that we don't understand */ + TDecoratedFunction(CallNode call) { call.isFunctionDecoratorCall() } or + /** Represents a subscript operation applied to a type. For type-hint analysis */ + TSubscriptedType(ObjectInternal generic, ObjectInternal index) { + isType(generic) and + generic.isNotSubscriptedType() and + index.isNotSubscriptedType() and + Expressions::subscriptPartsPointsTo(_, _, generic, index) + } /** Holds if the object `t` is a type. */ predicate isType(ObjectInternal t) { - t.isClass() = true - or - t.getOrigin().getEnclosingModule().getName().matches("%typing") + t.isClass() = true + or + t.getOrigin().getEnclosingModule().getName().matches("%typing") } private predicate is_power_2(int n) { - n = 1 - or - exists(int half | is_power_2(half) and n = half * 2) + n = 1 + or + exists(int half | is_power_2(half) and n = half * 2) } predicate static_method( - CallNode instantiation, CallableObjectInternal function, PointsToContext context + CallNode instantiation, CallableObjectInternal function, PointsToContext context ) { - PointsToInternal::pointsTo(instantiation.getFunction(), context, - ObjectInternal::builtin("staticmethod"), _) and - PointsToInternal::pointsTo(instantiation.getArg(0), context, function, _) + PointsToInternal::pointsTo(instantiation.getFunction(), context, + ObjectInternal::builtin("staticmethod"), _) and + PointsToInternal::pointsTo(instantiation.getArg(0), context, function, _) } predicate class_method( - CallNode instantiation, CallableObjectInternal function, PointsToContext context + CallNode instantiation, CallableObjectInternal function, PointsToContext context ) { - PointsToInternal::pointsTo(instantiation.getFunction(), context, ObjectInternal::classMethod(), _) and - PointsToInternal::pointsTo(instantiation.getArg(0), context, function, _) + PointsToInternal::pointsTo(instantiation.getFunction(), context, ObjectInternal::classMethod(), _) and + PointsToInternal::pointsTo(instantiation.getArg(0), context, function, _) } predicate literal_instantiation(ControlFlowNode n, ClassObjectInternal cls, PointsToContext context) { - context.appliesTo(n) and - ( - n instanceof ListNode and cls = ObjectInternal::builtin("list") - or - n instanceof DictNode and cls = ObjectInternal::builtin("dict") - or - n instanceof SetNode and cls = ObjectInternal::builtin("set") - or - n.getNode() instanceof ImaginaryLiteral and cls = ObjectInternal::builtin("complex") - or - n.getNode() instanceof ListComp and cls = ObjectInternal::builtin("list") - or - n.getNode() instanceof SetComp and cls = ObjectInternal::builtin("set") - or - n.getNode() instanceof DictComp and cls = ObjectInternal::builtin("dict") - ) + context.appliesTo(n) and + ( + n instanceof ListNode and cls = ObjectInternal::builtin("list") + or + n instanceof DictNode and cls = ObjectInternal::builtin("dict") + or + n instanceof SetNode and cls = ObjectInternal::builtin("set") + or + n.getNode() instanceof ImaginaryLiteral and cls = ObjectInternal::builtin("complex") + or + n.getNode() instanceof ListComp and cls = ObjectInternal::builtin("list") + or + n.getNode() instanceof SetComp and cls = ObjectInternal::builtin("set") + or + n.getNode() instanceof DictComp and cls = ObjectInternal::builtin("dict") + ) } predicate super_instantiation( - CallNode instantiation, ObjectInternal self, ClassObjectInternal startclass, - PointsToContext context + CallNode instantiation, ObjectInternal self, ClassObjectInternal startclass, + PointsToContext context ) { - super_2args(instantiation, self, startclass, context) - or - super_noargs(instantiation, self, startclass, context) + super_2args(instantiation, self, startclass, context) + or + super_noargs(instantiation, self, startclass, context) } pragma[noinline] private predicate super_2args( - CallNode instantiation, ObjectInternal self, ClassObjectInternal startclass, - PointsToContext context + CallNode instantiation, ObjectInternal self, ClassObjectInternal startclass, + PointsToContext context ) { - exists(ControlFlowNode arg0, ControlFlowNode arg1 | - super_call2(instantiation, arg0, arg1, context) and - PointsToInternal::pointsTo(arg0, context, startclass, _) and - PointsToInternal::pointsTo(arg1, context, self, _) - ) + exists(ControlFlowNode arg0, ControlFlowNode arg1 | + super_call2(instantiation, arg0, arg1, context) and + PointsToInternal::pointsTo(arg0, context, startclass, _) and + PointsToInternal::pointsTo(arg1, context, self, _) + ) } pragma[noinline] private predicate super_call2( - CallNode call, ControlFlowNode arg0, ControlFlowNode arg1, PointsToContext context + CallNode call, ControlFlowNode arg0, ControlFlowNode arg1, PointsToContext context ) { - exists(ControlFlowNode func | - call2(call, func, arg0, arg1) and - PointsToInternal::pointsTo(func, context, ObjectInternal::superType(), _) - ) + exists(ControlFlowNode func | + call2(call, func, arg0, arg1) and + PointsToInternal::pointsTo(func, context, ObjectInternal::superType(), _) + ) } pragma[noinline] private predicate super_noargs( - CallNode instantiation, ObjectInternal self, ClassObjectInternal startclass, - PointsToContext context + CallNode instantiation, ObjectInternal self, ClassObjectInternal startclass, + PointsToContext context ) { - PointsToInternal::pointsTo(instantiation.getFunction(), context, ObjectInternal::builtin("super"), - _) and - not exists(instantiation.getArg(0)) and - exists(Function func | - instantiation.getScope() = func and - /* Implicit class argument is lexically enclosing scope */ - func.getScope() = startclass.(PythonClassObjectInternal).getScope() and - /* Implicit 'self' is the `self` parameter of the enclosing function */ - self.(SelfInstanceInternal).getParameter().getParameter() = func.getArg(0) - ) + PointsToInternal::pointsTo(instantiation.getFunction(), context, ObjectInternal::builtin("super"), + _) and + not exists(instantiation.getArg(0)) and + exists(Function func | + instantiation.getScope() = func and + /* Implicit class argument is lexically enclosing scope */ + func.getScope() = startclass.(PythonClassObjectInternal).getScope() and + /* Implicit 'self' is the `self` parameter of the enclosing function */ + self.(SelfInstanceInternal).getParameter().getParameter() = func.getArg(0) + ) } predicate call2(CallNode call, ControlFlowNode func, ControlFlowNode arg0, ControlFlowNode arg1) { - not exists(call.getArg(2)) and - func = call.getFunction() and - arg0 = call.getArg(0) and - arg1 = call.getArg(1) + not exists(call.getArg(2)) and + func = call.getFunction() and + arg0 = call.getArg(0) and + arg1 = call.getArg(1) } predicate call3( - CallNode call, ControlFlowNode func, ControlFlowNode arg0, ControlFlowNode arg1, - ControlFlowNode arg2 + CallNode call, ControlFlowNode func, ControlFlowNode arg0, ControlFlowNode arg1, + ControlFlowNode arg2 ) { - not exists(call.getArg(3)) and - func = call.getFunction() and - arg0 = call.getArg(0) and - arg1 = call.getArg(1) and - arg2 = call.getArg(2) + not exists(call.getArg(3)) and + func = call.getFunction() and + arg0 = call.getArg(0) and + arg1 = call.getArg(1) and + arg2 = call.getArg(2) } bindingset[self, function] predicate method_binding( - AttrNode instantiation, ObjectInternal self, CallableObjectInternal function, - PointsToContext context + AttrNode instantiation, ObjectInternal self, CallableObjectInternal function, + PointsToContext context ) { - exists(ObjectInternal obj, string name | receiver(instantiation, context, obj, name) | - exists(ObjectInternal cls | - cls = obj.getClass() and - cls != ObjectInternal::superType() and - cls.attribute(name, function, _) and - self = obj - ) - or - exists(SuperInstance sup, ClassObjectInternal decl | - sup = obj and - decl = Types::getMro(self.getClass()).startingAt(sup.getStartClass()).findDeclaringClass(name) and - Types::declaredAttribute(decl, name, function, _) and - self = sup.getSelf() - ) + exists(ObjectInternal obj, string name | receiver(instantiation, context, obj, name) | + exists(ObjectInternal cls | + cls = obj.getClass() and + cls != ObjectInternal::superType() and + cls.attribute(name, function, _) and + self = obj ) + or + exists(SuperInstance sup, ClassObjectInternal decl | + sup = obj and + decl = Types::getMro(self.getClass()).startingAt(sup.getStartClass()).findDeclaringClass(name) and + Types::declaredAttribute(decl, name, function, _) and + self = sup.getSelf() + ) + ) } /** Helper for method_binding */ pragma[noinline] predicate receiver(AttrNode instantiation, PointsToContext context, ObjectInternal obj, string name) { - PointsToInternal::pointsTo(instantiation.getObject(name), context, obj, _) + PointsToInternal::pointsTo(instantiation.getObject(name), context, obj, _) } /** Helper self parameters: `def meth(self, ...): ...`. */ pragma[noinline] private predicate self_parameter( - ParameterDefinition def, PointsToContext context, PythonClassObjectInternal cls + ParameterDefinition def, PointsToContext context, PythonClassObjectInternal cls ) { - def.isSelf() and - /* Exclude the special parameter name `.0` which is used for unfolded comprehensions. */ - def.getName() != ".0" and - exists(Function scope | - def.getScope() = scope and - context.isRuntime() and - context.appliesToScope(scope) and - scope.getScope() = cls.getScope() and - concrete_class(cls) and - /* - * We want to allow decorated functions, otherwise we lose a lot of useful information. - * However, we want to exclude any function whose arguments are permuted by the decorator. - * In general we can't do that, but we can special case the most common ones. - */ + def.isSelf() and + /* Exclude the special parameter name `.0` which is used for unfolded comprehensions. */ + def.getName() != ".0" and + exists(Function scope | + def.getScope() = scope and + context.isRuntime() and + context.appliesToScope(scope) and + scope.getScope() = cls.getScope() and + concrete_class(cls) and + /* + * We want to allow decorated functions, otherwise we lose a lot of useful information. + * However, we want to exclude any function whose arguments are permuted by the decorator. + * In general we can't do that, but we can special case the most common ones. + */ - neither_class_nor_static_method(scope) - ) + neither_class_nor_static_method(scope) + ) } cached private predicate concrete_class(PythonClassObjectInternal cls) { - cls.getClass() != abcMetaClassObject() - or - exists(Class c | - c = cls.getScope() and - not exists(c.getMetaClass()) - | - forall(Function f | f.getScope() = c | - not exists(Raise r, Name ex | - r.getScope() = f and - (r.getException() = ex or r.getException().(Call).getFunc() = ex) and - (ex.getId() = "NotImplementedError" or ex.getId() = "NotImplemented") - ) - ) + cls.getClass() != abcMetaClassObject() + or + exists(Class c | + c = cls.getScope() and + not exists(c.getMetaClass()) + | + forall(Function f | f.getScope() = c | + not exists(Raise r, Name ex | + r.getScope() = f and + (r.getException() = ex or r.getException().(Call).getFunc() = ex) and + (ex.getId() = "NotImplementedError" or ex.getId() = "NotImplemented") + ) ) + ) } private PythonClassObjectInternal abcMetaClassObject() { - /* Avoid using points-to and thus negative recursion */ - exists(Class abcmeta | result.getScope() = abcmeta | - abcmeta.getName() = "ABCMeta" and - abcmeta.getScope().getName() = "abc" - ) + /* Avoid using points-to and thus negative recursion */ + exists(Class abcmeta | result.getScope() = abcmeta | + abcmeta.getName() = "ABCMeta" and + abcmeta.getScope().getName() = "abc" + ) } private predicate neither_class_nor_static_method(Function f) { - not exists(f.getADecorator()) - or - exists(ControlFlowNode deco | deco = f.getADecorator().getAFlowNode() | - exists(ObjectInternal o | PointsToInternal::pointsTo(deco, _, o, _) | - o != ObjectInternal::staticMethod() and - o != ObjectInternal::classMethod() - ) - or - not deco instanceof NameNode + not exists(f.getADecorator()) + or + exists(ControlFlowNode deco | deco = f.getADecorator().getAFlowNode() | + exists(ObjectInternal o | PointsToInternal::pointsTo(deco, _, o, _) | + o != ObjectInternal::staticMethod() and + o != ObjectInternal::classMethod() ) + or + not deco instanceof NameNode + ) } predicate missing_imported_module(ControlFlowNode imp, Context ctx, string name) { - ctx.isImport() and - imp.(ImportExprNode).getNode().getAnImportedModuleName() = name and - ( - not exists(Module m | m.getName() = name) and - not exists(Builtin b | b.isModule() and b.getName() = name) - or - exists(Module m, SyntaxError se | - m.getName() = name and - se.getFile() = m.getFile() - ) - ) + ctx.isImport() and + imp.(ImportExprNode).getNode().getAnImportedModuleName() = name and + ( + not exists(Module m | m.getName() = name) and + not exists(Builtin b | b.isModule() and b.getName() = name) or - exists(AbsentModuleObjectInternal mod | - PointsToInternal::pointsTo(imp.(ImportMemberNode).getModule(name), ctx, mod, _) and - common_module_name(mod.getName() + "." + name) + exists(Module m, SyntaxError se | + m.getName() = name and + se.getFile() = m.getFile() ) + ) + or + exists(AbsentModuleObjectInternal mod | + PointsToInternal::pointsTo(imp.(ImportMemberNode).getModule(name), ctx, mod, _) and + common_module_name(mod.getName() + "." + name) + ) } /** * Helper for missing modules to determine if name `x.y` is a module `x.y` or * an attribute `y` of module `x`. This list should be added to as required. */ - predicate common_module_name(string name) { - name = "zope.interface" - or - name = "six.moves" + name = "zope.interface" + or + name = "six.moves" } /** @@ -441,78 +440,78 @@ predicate common_module_name(string name) { * recursion. */ library class ClassDecl extends @py_object { - ClassDecl() { - this.(Builtin).isClass() and not this = Builtin::unknownType() - or - this.(ControlFlowNode).getNode() instanceof ClassExpr - } + ClassDecl() { + this.(Builtin).isClass() and not this = Builtin::unknownType() + or + this.(ControlFlowNode).getNode() instanceof ClassExpr + } - /** Gets a textual representation of this element. */ - string toString() { result = "ClassDecl" } + /** Gets a textual representation of this element. */ + string toString() { result = "ClassDecl" } - /** Gets the class scope for Python class declarations */ - Class getClass() { result = this.(ControlFlowNode).getNode().(ClassExpr).getInnerScope() } + /** Gets the class scope for Python class declarations */ + Class getClass() { result = this.(ControlFlowNode).getNode().(ClassExpr).getInnerScope() } - /** Holds if this class declares the attribute `name` */ - predicate declaresAttribute(string name) { - exists(this.(Builtin).getMember(name)) - or - exists(SsaVariable var | - name = var.getId() and var.getAUse() = this.getClass().getANormalExit() - ) - } + /** Holds if this class declares the attribute `name` */ + predicate declaresAttribute(string name) { + exists(this.(Builtin).getMember(name)) + or + exists(SsaVariable var | + name = var.getId() and var.getAUse() = this.getClass().getANormalExit() + ) + } - /** Gets the name of this class */ - string getName() { - result = this.(Builtin).getName() - or - result = this.getClass().getName() - } + /** Gets the name of this class */ + string getName() { + result = this.(Builtin).getName() + or + result = this.getClass().getName() + } - /** - * Whether this is a class whose instances must be treated specially, rather than as generic instances. - */ - predicate isSpecial() { - exists(string name | this = Builtin::special(name) | - name = "type" or - name = "super" or - name = "bool" or - name = "NoneType" or - name = "tuple" or - name = "property" or - name = "ClassMethod" or - name = "StaticMethod" or - name = "MethodType" or - name = "ModuleType" - ) - } + /** + * Whether this is a class whose instances must be treated specially, rather than as generic instances. + */ + predicate isSpecial() { + exists(string name | this = Builtin::special(name) | + name = "type" or + name = "super" or + name = "bool" or + name = "NoneType" or + name = "tuple" or + name = "property" or + name = "ClassMethod" or + name = "StaticMethod" or + name = "MethodType" or + name = "ModuleType" + ) + } - /** Holds if for class `C`, `C()` returns an instance of `C` */ - predicate callReturnsInstance() { - exists(Class pycls | pycls = this.getClass() | - /* Django does this, so we need to account for it */ - not exists(Function init, LocalVariable self | - /* `self.__class__ = ...` in the `__init__` method */ - pycls.getInitMethod() = init and - self.isSelf() and - self.getScope() = init and - exists(AttrNode a | a.isStore() and a.getObject("__class__") = self.getAUse()) - ) and - not exists(Function new | new.getName() = "__new__" and new.getScope() = pycls) - ) - or - this instanceof Builtin - } + /** Holds if for class `C`, `C()` returns an instance of `C` */ + predicate callReturnsInstance() { + exists(Class pycls | pycls = this.getClass() | + /* Django does this, so we need to account for it */ + not exists(Function init, LocalVariable self | + /* `self.__class__ = ...` in the `__init__` method */ + pycls.getInitMethod() = init and + self.isSelf() and + self.getScope() = init and + exists(AttrNode a | a.isStore() and a.getObject("__class__") = self.getAUse()) + ) and + not exists(Function new | new.getName() = "__new__" and new.getScope() = pycls) + ) + or + this instanceof Builtin + } - /** Holds if this class is the abstract base class */ - predicate isAbstractBaseClass(string name) { - exists(Module m | - m.getName() = "_abcoll" - or - m.getName() = "_collections_abc" - | - this.getClass().getScope() = m and - this.getName() = name - ) - } + /** Holds if this class is the abstract base class */ + predicate isAbstractBaseClass(string name) { + exists(Module m | + m.getName() = "_abcoll" + or + m.getName() = "_collections_abc" + | + this.getClass().getScope() = m and + this.getName() = name + ) + } } diff --git a/python/ql/src/semmle/python/pointsto/Base.qll b/python/ql/src/semmle/python/pointsto/Base.qll index f18db539cc4..bff183d0efe 100644 --- a/python/ql/src/semmle/python/pointsto/Base.qll +++ b/python/ql/src/semmle/python/pointsto/Base.qll @@ -13,28 +13,28 @@ import semmle.python.essa.SsaDefinitions private import semmle.python.types.Builtins module BasePointsTo { - /** INTERNAL -- Use n.refersTo(value, _, origin) instead */ - pragma[noinline] - predicate points_to(ControlFlowNode f, Object value, ControlFlowNode origin) { - ( - f.isLiteral() and value = f and not f.getNode() instanceof ImmutableLiteral - or - f.isFunction() and value = f - ) and - origin = f - } + /** INTERNAL -- Use n.refersTo(value, _, origin) instead */ + pragma[noinline] + predicate points_to(ControlFlowNode f, Object value, ControlFlowNode origin) { + ( + f.isLiteral() and value = f and not f.getNode() instanceof ImmutableLiteral + or + f.isFunction() and value = f + ) and + origin = f + } } /** The kwargs parameter (**kwargs) in a function definition is always a dict */ predicate kwargs_points_to(ControlFlowNode f, ClassObject cls) { - exists(Function func | func.getKwarg() = f.getNode()) and - cls = theDictType() + exists(Function func | func.getKwarg() = f.getNode()) and + cls = theDictType() } /** The varargs (*varargs) in a function definition is always a tuple */ predicate varargs_points_to(ControlFlowNode f, ClassObject cls) { - exists(Function func | func.getVararg() = f.getNode()) and - cls = theTupleType() + exists(Function func | func.getVararg() = f.getNode()) and + cls = theTupleType() } /** @@ -45,88 +45,88 @@ predicate varargs_points_to(ControlFlowNode f, ClassObject cls) { */ pragma[noinline] ClassObject simple_types(Object obj) { - result = comprehension(obj.getOrigin()) - or - result = collection_literal(obj.getOrigin()) - or - obj.getOrigin() instanceof CallableExpr and result = thePyFunctionType() - or - obj.getOrigin() instanceof Module and result = theModuleType() - or - result.asBuiltin() = obj.asBuiltin().getClass() - or - obj = unknownValue() and result = theUnknownType() + result = comprehension(obj.getOrigin()) + or + result = collection_literal(obj.getOrigin()) + or + obj.getOrigin() instanceof CallableExpr and result = thePyFunctionType() + or + obj.getOrigin() instanceof Module and result = theModuleType() + or + result.asBuiltin() = obj.asBuiltin().getClass() + or + obj = unknownValue() and result = theUnknownType() } private ClassObject comprehension(Expr e) { - e instanceof ListComp and result = theListType() - or - e instanceof SetComp and result = theSetType() - or - e instanceof DictComp and result = theDictType() - or - e instanceof GeneratorExp and result = theGeneratorType() + e instanceof ListComp and result = theListType() + or + e instanceof SetComp and result = theSetType() + or + e instanceof DictComp and result = theDictType() + or + e instanceof GeneratorExp and result = theGeneratorType() } private ClassObject collection_literal(Expr e) { - e instanceof List and result = theListType() - or - e instanceof Set and result = theSetType() - or - e instanceof Dict and result = theDictType() - or - e instanceof Tuple and result = theTupleType() + e instanceof List and result = theListType() + or + e instanceof Set and result = theSetType() + or + e instanceof Dict and result = theDictType() + or + e instanceof Tuple and result = theTupleType() } private int tuple_index_value(Object t, int i) { - result = t.(TupleNode).getElement(i).getNode().(Num).getN().toInt() - or - exists(Object item | - py_citems(t, i, item) and - result = item.(NumericObject).intValue() - ) + result = t.(TupleNode).getElement(i).getNode().(Num).getN().toInt() + or + exists(Object item | + py_citems(t, i, item) and + result = item.(NumericObject).intValue() + ) } pragma[noinline] int version_tuple_value(Object t) { - not exists(tuple_index_value(t, 1)) and result = tuple_index_value(t, 0) * 10 - or - not exists(tuple_index_value(t, 2)) and - result = tuple_index_value(t, 0) * 10 + tuple_index_value(t, 1) - or - tuple_index_value(t, 2) = 0 and result = tuple_index_value(t, 0) * 10 + tuple_index_value(t, 1) - or - tuple_index_value(t, 2) > 0 and - result = tuple_index_value(t, 0) * 10 + tuple_index_value(t, 1) + 1 + not exists(tuple_index_value(t, 1)) and result = tuple_index_value(t, 0) * 10 + or + not exists(tuple_index_value(t, 2)) and + result = tuple_index_value(t, 0) * 10 + tuple_index_value(t, 1) + or + tuple_index_value(t, 2) = 0 and result = tuple_index_value(t, 0) * 10 + tuple_index_value(t, 1) + or + tuple_index_value(t, 2) > 0 and + result = tuple_index_value(t, 0) * 10 + tuple_index_value(t, 1) + 1 } /** Choose a version numbers that represent the extreme of supported versions. */ private int major_minor() { - if major_version() = 3 - then ( - result = 33 or result = 37 - ) else ( - // 3.3 to 3.7 - result = 25 or result = 27 - ) // 2.5 to 2.7 + if major_version() = 3 + then ( + result = 33 or result = 37 + ) else ( + // 3.3 to 3.7 + result = 25 or result = 27 + ) // 2.5 to 2.7 } /** Compares the given tuple object to both the maximum and minimum possible sys.version_info values */ int version_tuple_compare(Object t) { - version_tuple_value(t) < major_minor() and result = -1 - or - version_tuple_value(t) = major_minor() and result = 0 - or - version_tuple_value(t) > major_minor() and result = 1 + version_tuple_value(t) < major_minor() and result = -1 + or + version_tuple_value(t) = major_minor() and result = 0 + or + version_tuple_value(t) > major_minor() and result = 1 } /* Holds if `cls` is a new-style class if it were to have no explicit base classes */ predicate baseless_is_new_style(ClassObject cls) { - cls.isBuiltin() - or - major_version() = 3 - or - exists(cls.declaredMetaClass()) + cls.isBuiltin() + or + major_version() = 3 + or + exists(cls.declaredMetaClass()) } /* @@ -139,123 +139,123 @@ predicate baseless_is_new_style(ClassObject cls) { /** Holds if this class (not on a super-class) declares name */ pragma[noinline] predicate class_declares_attribute(ClassObject cls, string name) { - exists(Class defn | - defn = cls.getPyClass() and - class_defines_name(defn, name) - ) - or - exists(Builtin o | - o = cls.asBuiltin().getMember(name) and - not exists(Builtin sup | - sup = cls.asBuiltin().getBaseClass() and - o = sup.getMember(name) - ) + exists(Class defn | + defn = cls.getPyClass() and + class_defines_name(defn, name) + ) + or + exists(Builtin o | + o = cls.asBuiltin().getMember(name) and + not exists(Builtin sup | + sup = cls.asBuiltin().getBaseClass() and + o = sup.getMember(name) ) + ) } /** Holds if the class defines name */ private predicate class_defines_name(Class cls, string name) { - exists(SsaVariable var | name = var.getId() and var.getAUse() = cls.getANormalExit()) + exists(SsaVariable var | name = var.getId() and var.getAUse() = cls.getANormalExit()) } /** Gets a return value CFG node, provided that is safe to track across returns */ ControlFlowNode safe_return_node(PyFunctionObject func) { - result = func.getAReturnedNode() and - // Not a parameter - not exists(Parameter p, SsaVariable pvar | - p.asName().getAFlowNode() = pvar.getDefinition() and - result = pvar.getAUse() - ) and - // No alternatives - not exists(ControlFlowNode branch | branch.isBranch() and branch.getScope() = func.getFunction()) + result = func.getAReturnedNode() and + // Not a parameter + not exists(Parameter p, SsaVariable pvar | + p.asName().getAFlowNode() = pvar.getDefinition() and + result = pvar.getAUse() + ) and + // No alternatives + not exists(ControlFlowNode branch | branch.isBranch() and branch.getScope() = func.getFunction()) } /** Holds if it can be determined from the control flow graph alone that this function can never return */ predicate function_can_never_return(FunctionObject func) { - /* - * A Python function never returns if it has no normal exits that are not dominated by a - * call to a function which itself never returns. - */ + /* + * A Python function never returns if it has no normal exits that are not dominated by a + * call to a function which itself never returns. + */ - exists(Function f | - f = func.getFunction() and - not exists(f.getAnExitNode()) - ) - or - func = ModuleObject::named("sys").attr("exit") + exists(Function f | + f = func.getFunction() and + not exists(f.getAnExitNode()) + ) + or + func = ModuleObject::named("sys").attr("exit") } private newtype TIterationDefinition = - TIterationDefinition_(SsaSourceVariable var, ControlFlowNode def, ControlFlowNode sequence) { - SsaSource::iteration_defined_variable(var, def, sequence) - } + TIterationDefinition_(SsaSourceVariable var, ControlFlowNode def, ControlFlowNode sequence) { + SsaSource::iteration_defined_variable(var, def, sequence) + } /** * DEPRECATED. For backwards compatibility only. * A definition of a variable in a for loop `for v in ...:` */ deprecated class IterationDefinition extends TIterationDefinition { - /** Gets a textual representation of this element. */ - string toString() { result = "IterationDefinition" } + /** Gets a textual representation of this element. */ + string toString() { result = "IterationDefinition" } - ControlFlowNode getSequence() { this = TIterationDefinition_(_, _, result) } + ControlFlowNode getSequence() { this = TIterationDefinition_(_, _, result) } } /** Hold if outer contains inner, both are contained within a test and inner is a use is a plain use or an attribute lookup */ pragma[noinline] predicate contains_interesting_expression_within_test(ControlFlowNode outer, ControlFlowNode inner) { - inner.isLoad() and - exists(ControlFlowNode test | - outer.getAChild*() = inner and - test_contains(test, outer) and - test_contains(test, inner) - | - inner instanceof NameNode or - inner instanceof AttrNode - ) + inner.isLoad() and + exists(ControlFlowNode test | + outer.getAChild*() = inner and + test_contains(test, outer) and + test_contains(test, inner) + | + inner instanceof NameNode or + inner instanceof AttrNode + ) } /** Hold if `expr` is a test (a branch) and `use` is within that test */ predicate test_contains(ControlFlowNode expr, ControlFlowNode use) { - expr.getNode() instanceof Expr and - expr.isBranch() and - expr.getAChild*() = use + expr.getNode() instanceof Expr and + expr.isBranch() and + expr.getAChild*() = use } /** Holds if `test` is a test (a branch), `use` is within that test and `def` is an edge from that test with `sense` */ predicate refinement_test( - ControlFlowNode test, ControlFlowNode use, boolean sense, PyEdgeRefinement def + ControlFlowNode test, ControlFlowNode use, boolean sense, PyEdgeRefinement def ) { - /* - * Because calls such as `len` may create a new variable, we need to go via the source variable - * That is perfectly safe as we are only dealing with calls that do not mutate their arguments. - */ + /* + * Because calls such as `len` may create a new variable, we need to go via the source variable + * That is perfectly safe as we are only dealing with calls that do not mutate their arguments. + */ - use = def.getInput().getSourceVariable().(Variable).getAUse() and - test = def.getPredecessor().getLastNode() and - test_contains(test, use) and - sense = def.getSense() + use = def.getInput().getSourceVariable().(Variable).getAUse() and + test = def.getPredecessor().getLastNode() and + test_contains(test, use) and + sense = def.getSense() } /** Holds if `f` is an import of the form `from .[...] import name` and the enclosing scope is an __init__ module */ pragma[noinline] predicate live_import_from_dot_in_init(ImportMemberNode f, EssaVariable var) { - exists(string name | - import_from_dot_in_init(f.getModule(name)) and - var.getSourceVariable().getName() = name and - var.getAUse() = f - ) + exists(string name | + import_from_dot_in_init(f.getModule(name)) and + var.getSourceVariable().getName() = name and + var.getAUse() = f + ) } /** Holds if `f` is an import of the form `from .[...] import ...` and the enclosing scope is an __init__ module */ predicate import_from_dot_in_init(ImportExprNode f) { - f.getScope() = any(Module m).getInitModule() and - ( - f.getNode().getLevel() = 1 and - not exists(f.getNode().getName()) - or - f.getNode().getImportedModuleName() = f.getEnclosingModule().getPackage().getName() - ) + f.getScope() = any(Module m).getInitModule() and + ( + f.getNode().getLevel() = 1 and + not exists(f.getNode().getName()) + or + f.getNode().getImportedModuleName() = f.getEnclosingModule().getPackage().getName() + ) } /** Gets the pseudo-object representing the value referred to by an undefined variable */ @@ -265,73 +265,73 @@ Object undefinedVariable() { py_special_objects(result, "_semmle_undefined_value Object unknownValue() { result.asBuiltin() = Builtin::unknown() } BuiltinCallable theTypeNewMethod() { - result.asBuiltin() = theTypeType().asBuiltin().getMember("__new__") + 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 */ pragma[noinline] predicate potential_builtin_points_to( - NameNode f, Object value, ClassObject cls, ControlFlowNode origin + NameNode f, Object value, ClassObject cls, ControlFlowNode origin ) { - f.isGlobal() and - f.isLoad() and - origin = f and - ( - builtin_name_points_to(f.getId(), value, cls) - or - not exists(Object::builtin(f.getId())) and value = unknownValue() and cls = theUnknownType() - ) + f.isGlobal() and + f.isLoad() and + origin = f and + ( + builtin_name_points_to(f.getId(), value, cls) + or + not exists(Object::builtin(f.getId())) and value = unknownValue() and cls = theUnknownType() + ) } pragma[noinline] predicate builtin_name_points_to(string name, Object value, ClassObject cls) { - value = Object::builtin(name) and cls.asBuiltin() = value.asBuiltin().getClass() + value = Object::builtin(name) and cls.asBuiltin() = value.asBuiltin().getClass() } module BaseFlow { - predicate reaches_exit(EssaVariable var) { var.getAUse() = var.getScope().getANormalExit() } + predicate reaches_exit(EssaVariable var) { var.getAUse() = var.getScope().getANormalExit() } - /* Helper for this_scope_entry_value_transfer(...). Transfer of values from earlier scope to later on */ - cached - predicate scope_entry_value_transfer_from_earlier( - EssaVariable pred_var, Scope pred_scope, ScopeEntryDefinition succ_def, Scope succ_scope - ) { - exists(SsaSourceVariable var | - reaches_exit(pred_var) and - pred_var.getScope() = pred_scope and - var = pred_var.getSourceVariable() and - var = succ_def.getSourceVariable() and - succ_def.getScope() = succ_scope - | - pred_scope.precedes(succ_scope) - or - /* - * If an `__init__` method does not modify the global variable, then - * we can skip it and take the value directly from the module. - */ + /* Helper for this_scope_entry_value_transfer(...). Transfer of values from earlier scope to later on */ + cached + predicate scope_entry_value_transfer_from_earlier( + EssaVariable pred_var, Scope pred_scope, ScopeEntryDefinition succ_def, Scope succ_scope + ) { + exists(SsaSourceVariable var | + reaches_exit(pred_var) and + pred_var.getScope() = pred_scope and + var = pred_var.getSourceVariable() and + var = succ_def.getSourceVariable() and + succ_def.getScope() = succ_scope + | + pred_scope.precedes(succ_scope) + or + /* + * If an `__init__` method does not modify the global variable, then + * we can skip it and take the value directly from the module. + */ - exists(Scope init | - init.getName() = "__init__" and - init.precedes(succ_scope) and - pred_scope.precedes(init) and - not var.(Variable).getAStore().getScope() = init and - var instanceof GlobalVariable - ) - ) - } + exists(Scope init | + init.getName() = "__init__" and + init.precedes(succ_scope) and + pred_scope.precedes(init) and + not var.(Variable).getAStore().getScope() = init and + var instanceof GlobalVariable + ) + ) + } } /** Points-to for syntactic elements where context is not relevant */ predicate simple_points_to(ControlFlowNode f, Object value, ClassObject cls, ControlFlowNode origin) { - kwargs_points_to(f, cls) and value = f and origin = f - or - varargs_points_to(f, cls) and value = f and origin = f - or - BasePointsTo::points_to(f, value, origin) and cls = simple_types(value) - or - value = f.getNode().(ImmutableLiteral).getLiteralObject() and - cls = simple_types(value) and - origin = f + kwargs_points_to(f, cls) and value = f and origin = f + or + varargs_points_to(f, cls) and value = f and origin = f + or + BasePointsTo::points_to(f, value, origin) and cls = simple_types(value) + or + value = f.getNode().(ImmutableLiteral).getLiteralObject() and + cls = simple_types(value) and + origin = f } /** @@ -339,25 +339,25 @@ predicate simple_points_to(ControlFlowNode f, Object value, ClassObject cls, Con * Helper for `this_binary_expr_points_to`. */ predicate bitwise_expression_node(BinaryExprNode bit, ControlFlowNode left, ControlFlowNode right) { - exists(Operator op | op = bit.getNode().getOp() | - op instanceof BitAnd or - op instanceof BitOr or - op instanceof BitXor - ) and - left = bit.getLeft() and - right = bit.getRight() + exists(Operator op | op = bit.getNode().getOp() | + op instanceof BitAnd or + op instanceof BitOr or + op instanceof BitXor + ) and + left = bit.getLeft() and + right = bit.getRight() } private Module theCollectionsAbcModule() { - result.getName() = "_abcoll" - or - result.getName() = "_collections_abc" + result.getName() = "_abcoll" + or + result.getName() = "_collections_abc" } ClassObject collectionsAbcClass(string name) { - exists(Class cls | - result.getPyClass() = cls and - cls.getName() = name and - cls.getScope() = theCollectionsAbcModule() - ) + exists(Class cls | + result.getPyClass() = cls and + cls.getName() = name and + cls.getScope() = theCollectionsAbcModule() + ) } diff --git a/python/ql/src/semmle/python/pointsto/CallGraph.qll b/python/ql/src/semmle/python/pointsto/CallGraph.qll index 0c10e67a867..545dd945cf1 100644 --- a/python/ql/src/semmle/python/pointsto/CallGraph.qll +++ b/python/ql/src/semmle/python/pointsto/CallGraph.qll @@ -13,14 +13,14 @@ import python private import semmle.python.pointsto.PointsToContext private newtype TTInvocation = - TInvocation(FunctionObject f, Context c) { - exists(Context outer, CallNode call | - call = f.getACall(outer) and - c.fromCall(call, outer) - ) - or - c.appliesToScope(f.getFunction()) - } + TInvocation(FunctionObject f, Context c) { + exists(Context outer, CallNode call | + call = f.getACall(outer) and + c.fromCall(call, outer) + ) + or + c.appliesToScope(f.getFunction()) + } /** * This class represents a static approximation to the @@ -28,46 +28,46 @@ private newtype TTInvocation = * all calls made to a function for a given context. */ class FunctionInvocation extends TTInvocation { - /** Gets a textual representation of this element. */ - string toString() { result = "Invocation" } + /** Gets a textual representation of this element. */ + string toString() { result = "Invocation" } - FunctionObject getFunction() { this = TInvocation(result, _) } + FunctionObject getFunction() { this = TInvocation(result, _) } - Context getContext() { this = TInvocation(_, result) } + Context getContext() { this = TInvocation(_, result) } - /** - * Gets the callee invocation for the given callsite. - * The callsite must be within the function of this invocation. - */ - FunctionInvocation getCallee(CallNode call) { - exists( - FunctionObject callee, Context callee_context, FunctionObject caller, Context caller_context - | - this = TInvocation(caller, caller_context) and - result = TInvocation(callee, callee_context) and - call = callee.getACall(caller_context) and - callee_context.fromCall(call, caller_context) and - call.getScope() = caller.getFunction() - ) - } + /** + * Gets the callee invocation for the given callsite. + * The callsite must be within the function of this invocation. + */ + FunctionInvocation getCallee(CallNode call) { + exists( + FunctionObject callee, Context callee_context, FunctionObject caller, Context caller_context + | + this = TInvocation(caller, caller_context) and + result = TInvocation(callee, callee_context) and + call = callee.getACall(caller_context) and + callee_context.fromCall(call, caller_context) and + call.getScope() = caller.getFunction() + ) + } - /** - * Gets a callee invocation. - * That is any invocation made from within this invocation. - */ - FunctionInvocation getACallee() { result = this.getCallee(_) } + /** + * Gets a callee invocation. + * That is any invocation made from within this invocation. + */ + FunctionInvocation getACallee() { result = this.getCallee(_) } - /** Holds if this is an invocation `f` in the "runtime" context. */ - predicate runtime(FunctionObject f) { - exists(Context c | - c.isRuntime() and - this = TInvocation(f, c) - ) - } + /** Holds if this is an invocation `f` in the "runtime" context. */ + predicate runtime(FunctionObject f) { + exists(Context c | + c.isRuntime() and + this = TInvocation(f, c) + ) + } - /** Gets the call from which this invocation was made. */ - CallNode getCall() { this.getContext().fromCall(result, _) } + /** Gets the call from which this invocation was made. */ + CallNode getCall() { this.getContext().fromCall(result, _) } - /** Gets the caller invocation of this invocation, if any. */ - FunctionInvocation getCaller() { this = result.getCallee(_) } + /** Gets the caller invocation of this invocation, if any. */ + FunctionInvocation getCaller() { this = result.getCallee(_) } } diff --git a/python/ql/src/semmle/python/pointsto/Filters.qll b/python/ql/src/semmle/python/pointsto/Filters.qll index 114711b217c..117845d3c7f 100644 --- a/python/ql/src/semmle/python/pointsto/Filters.qll +++ b/python/ql/src/semmle/python/pointsto/Filters.qll @@ -7,45 +7,45 @@ import python /** Holds if `c` is a call to `hasattr(obj, attr)`. */ predicate hasattr(CallNode c, ControlFlowNode obj, string attr) { - c.getFunction().getNode().(Name).getId() = "hasattr" and - c.getArg(0) = obj and - c.getArg(1).getNode().(StrConst).getText() = attr + c.getFunction().getNode().(Name).getId() = "hasattr" and + c.getArg(0) = obj and + c.getArg(1).getNode().(StrConst).getText() = attr } /** Holds if `c` is a call to `callable(obj)`. */ predicate is_callable(CallNode c, ControlFlowNode obj) { - c.getFunction().(NameNode).getId() = "callable" and - obj = c.getArg(0) + c.getFunction().(NameNode).getId() = "callable" and + obj = c.getArg(0) } /** Holds if `c` is a call to `isinstance(use, cls)`. */ predicate isinstance(CallNode fc, ControlFlowNode cls, ControlFlowNode use) { - fc.getFunction().(NameNode).getId() = "isinstance" and - cls = fc.getArg(1) and - fc.getArg(0) = use + fc.getFunction().(NameNode).getId() = "isinstance" and + cls = fc.getArg(1) and + fc.getArg(0) = use } /** Holds if `c` is a call to `issubclass(use, cls)`. */ predicate issubclass(CallNode fc, ControlFlowNode cls, ControlFlowNode use) { - fc.getFunction().(NameNode).getId() = "issubclass" and - fc.getArg(0) = use and - cls = fc.getArg(1) + fc.getFunction().(NameNode).getId() = "issubclass" and + fc.getArg(0) = use and + cls = fc.getArg(1) } /** Holds if `c` is a test comparing `x` and `y`. `is` is true if the operator is `is` or `==`, it is false if the operator is `is not` or `!=`. */ predicate equality_test(CompareNode c, ControlFlowNode x, boolean is, ControlFlowNode y) { - exists(Cmpop op | - c.operands(x, op, y) or - c.operands(y, op, x) - | - ( - is = true and op instanceof Is - or - is = false and op instanceof IsNot - or - is = true and op instanceof Eq - or - is = false and op instanceof NotEq - ) + exists(Cmpop op | + c.operands(x, op, y) or + c.operands(y, op, x) + | + ( + is = true and op instanceof Is + or + is = false and op instanceof IsNot + or + is = true and op instanceof Eq + or + is = false and op instanceof NotEq ) + ) } diff --git a/python/ql/src/semmle/python/pointsto/MRO.qll b/python/ql/src/semmle/python/pointsto/MRO.qll index 1e8cd5a1908..34e6325b714 100644 --- a/python/ql/src/semmle/python/pointsto/MRO.qll +++ b/python/ql/src/semmle/python/pointsto/MRO.qll @@ -26,383 +26,383 @@ private import semmle.python.types.Builtins cached newtype TClassList = - Empty() or - Cons(ClassObjectInternal head, TClassList tail) { required_cons(head, tail) } + Empty() or + Cons(ClassObjectInternal head, TClassList tail) { required_cons(head, tail) } /* Keep ClassList finite and as small as possible */ private predicate required_cons(ClassObjectInternal head, ClassList tail) { - tail = Mro::newStyleMro(sole_base(head)) - or - tail = merge_of_linearization_of_bases(head) - or - exists(ClassObjectInternal cls, int n | - head = Types::getBase(cls, n) and tail = bases(cls, n + 1) - ) - or - head = ObjectInternal::builtin("object") and tail = Empty() - or - reverse_step(_, Cons(head, _), tail) - or - exists(ClassListList list | - merge_step(tail, list, _) and - head = list.bestMergeCandidate() - ) - or - exists(ClassList list, int n | - n = list.firstIndex(head) and - tail = list.deduplicate(n + 1) - ) - or - exists(ClassListList list, int n | - head = list.getHead().getItem(n) and - tail = flatten_list(list, n + 1) - ) - or - tail = list_old_style_base_mros(head).flatten() + tail = Mro::newStyleMro(sole_base(head)) + or + tail = merge_of_linearization_of_bases(head) + or + exists(ClassObjectInternal cls, int n | + head = Types::getBase(cls, n) and tail = bases(cls, n + 1) + ) + or + head = ObjectInternal::builtin("object") and tail = Empty() + or + reverse_step(_, Cons(head, _), tail) + or + exists(ClassListList list | + merge_step(tail, list, _) and + head = list.bestMergeCandidate() + ) + or + exists(ClassList list, int n | + n = list.firstIndex(head) and + tail = list.deduplicate(n + 1) + ) + or + exists(ClassListList list, int n | + head = list.getHead().getItem(n) and + tail = flatten_list(list, n + 1) + ) + or + tail = list_old_style_base_mros(head).flatten() } private ClassObjectInternal sole_base(ClassObjectInternal cls) { - Types::base_count(cls) = 1 and - result = Types::getBase(cls, 0) + Types::base_count(cls) = 1 and + result = Types::getBase(cls, 0) } /** A list of classes, used to represent the MRO of a class */ class ClassList extends TClassList { - /** Gets a textual representation of this element. */ - string toString() { result = "[" + this.contents() + "]" } + /** Gets a textual representation of this element. */ + string toString() { result = "[" + this.contents() + "]" } - string contents() { - this = Empty() and result = "" - or - exists(ClassObjectInternal head | head = this.getHead() | - this.getTail() = Empty() and result = className(head) - or - this.getTail() != Empty() and result = className(head) + ", " + this.getTail().contents() - ) - } + string contents() { + this = Empty() and result = "" + or + exists(ClassObjectInternal head | head = this.getHead() | + this.getTail() = Empty() and result = className(head) + or + this.getTail() != Empty() and result = className(head) + ", " + this.getTail().contents() + ) + } - private string className(ClassObjectInternal cls) { - result = cls.getName() - or - cls = ObjectInternal::unknownClass() and result = "??" - } + private string className(ClassObjectInternal cls) { + result = cls.getName() + or + cls = ObjectInternal::unknownClass() and result = "??" + } - int length() { - this = Empty() and result = 0 - or - result = this.getTail().length() + 1 - } + int length() { + this = Empty() and result = 0 + or + result = this.getTail().length() + 1 + } - ClassObjectInternal getHead() { this = Cons(result, _) } + ClassObjectInternal getHead() { this = Cons(result, _) } - ClassList getTail() { this = Cons(_, result) } + ClassList getTail() { this = Cons(_, result) } - ClassObjectInternal getItem(int n) { - n = 0 and result = this.getHead() - or - result = this.getTail().getItem(n - 1) - } + ClassObjectInternal getItem(int n) { + n = 0 and result = this.getHead() + or + result = this.getTail().getItem(n - 1) + } - ClassObjectInternal getAnItem() { result = this.getItem(_) } + ClassObjectInternal getAnItem() { result = this.getItem(_) } - pragma[inline] - ClassList removeHead(ClassObjectInternal cls) { - this.getHead() = cls and result = this.getTail() - or - this.getHead() != cls and result = this - or - this = Empty() and result = Empty() - } + pragma[inline] + ClassList removeHead(ClassObjectInternal cls) { + this.getHead() = cls and result = this.getTail() + or + this.getHead() != cls and result = this + or + this = Empty() and result = Empty() + } - predicate legalMergeHead(ClassObjectInternal cls) { - this.getTail().doesNotContain(cls) - or - this = Empty() - } + predicate legalMergeHead(ClassObjectInternal cls) { + this.getTail().doesNotContain(cls) + or + this = Empty() + } - predicate contains(ClassObjectInternal cls) { - cls = this.getHead() - or - this.getTail().contains(cls) - } + predicate contains(ClassObjectInternal cls) { + cls = this.getHead() + or + this.getTail().contains(cls) + } - /** Use negative formulation to avoid negative recursion */ - predicate doesNotContain(ClassObjectInternal cls) { - this.relevantForContains(cls) and - cls != this.getHead() and - this.getTail().doesNotContain(cls) - or - this = Empty() - } + /** Use negative formulation to avoid negative recursion */ + predicate doesNotContain(ClassObjectInternal cls) { + this.relevantForContains(cls) and + cls != this.getHead() and + this.getTail().doesNotContain(cls) + or + this = Empty() + } - private predicate relevantForContains(ClassObjectInternal cls) { - exists(ClassListList list | - list.getItem(_).getHead() = cls and - list.getItem(_) = this - ) - or - exists(ClassList l | - l.relevantForContains(cls) and - this = l.getTail() - ) - } + private predicate relevantForContains(ClassObjectInternal cls) { + exists(ClassListList list | + list.getItem(_).getHead() = cls and + list.getItem(_) = this + ) + or + exists(ClassList l | + l.relevantForContains(cls) and + this = l.getTail() + ) + } - ClassObjectInternal findDeclaringClass(string name) { - exists(ClassDecl head | head = this.getHead().getClassDeclaration() | - if head.declaresAttribute(name) - then result = this.getHead() - else result = this.getTail().findDeclaringClass(name) - ) - } + ClassObjectInternal findDeclaringClass(string name) { + exists(ClassDecl head | head = this.getHead().getClassDeclaration() | + if head.declaresAttribute(name) + then result = this.getHead() + else result = this.getTail().findDeclaringClass(name) + ) + } - predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { - exists(ClassObjectInternal decl | decl = this.findDeclaringClass(name) | - Types::declaredAttribute(decl, name, value, origin) - ) - } + predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { + exists(ClassObjectInternal decl | decl = this.findDeclaringClass(name) | + Types::declaredAttribute(decl, name, value, origin) + ) + } - predicate declares(string name) { - this.getHead().getClassDeclaration().declaresAttribute(name) - or - this.getTail().declares(name) - } + predicate declares(string name) { + this.getHead().getClassDeclaration().declaresAttribute(name) + or + this.getTail().declares(name) + } - ClassList startingAt(ClassObjectInternal cls) { - exists(ClassObjectInternal head | head = this.getHead() | - if head = cls then result = this else result = this.getTail().startingAt(cls) - ) - } + ClassList startingAt(ClassObjectInternal cls) { + exists(ClassObjectInternal head | head = this.getHead() | + if head = cls then result = this else result = this.getTail().startingAt(cls) + ) + } - ClassList deduplicate() { result = this.deduplicate(0) } + ClassList deduplicate() { result = this.deduplicate(0) } - /* Helpers for `deduplicate()` */ - int firstIndex(ClassObjectInternal cls) { result = this.firstIndex(cls, 0) } + /* Helpers for `deduplicate()` */ + int firstIndex(ClassObjectInternal cls) { result = this.firstIndex(cls, 0) } - /* Helper for firstIndex(cls), getting the first index of `cls` where result >= n */ - private int firstIndex(ClassObjectInternal cls, int n) { - this.getItem(n) = cls and result = n - or - this.getItem(n) != cls and result = this.firstIndex(cls, n + 1) - } + /* Helper for firstIndex(cls), getting the first index of `cls` where result >= n */ + private int firstIndex(ClassObjectInternal cls, int n) { + this.getItem(n) = cls and result = n + or + this.getItem(n) != cls and result = this.firstIndex(cls, n + 1) + } - /** Holds if the class at `n` is a duplicate of an earlier position. */ - private predicate duplicate(int n) { - exists(ClassObjectInternal cls | cls = this.getItem(n) and this.firstIndex(cls) < n) - } + /** Holds if the class at `n` is a duplicate of an earlier position. */ + private predicate duplicate(int n) { + exists(ClassObjectInternal cls | cls = this.getItem(n) and this.firstIndex(cls) < n) + } - /** - * Gets a class list which is the de-duplicated form of the list containing elements of - * this list from `n` onwards. - */ - ClassList deduplicate(int n) { - n = this.length() and result = Empty() - or - this.duplicate(n) and result = this.deduplicate(n + 1) - or - exists(ClassObjectInternal cls | - n = this.firstIndex(cls) and - result = Cons(cls, this.deduplicate(n + 1)) - ) - } + /** + * Gets a class list which is the de-duplicated form of the list containing elements of + * this list from `n` onwards. + */ + ClassList deduplicate(int n) { + n = this.length() and result = Empty() + or + this.duplicate(n) and result = this.deduplicate(n + 1) + or + exists(ClassObjectInternal cls | + n = this.firstIndex(cls) and + result = Cons(cls, this.deduplicate(n + 1)) + ) + } - predicate isEmpty() { this = Empty() } + predicate isEmpty() { this = Empty() } - ClassList reverse() { reverse_step(this, Empty(), result) } + ClassList reverse() { reverse_step(this, Empty(), result) } - /** - * Holds if this MRO contains a class whose instances we treat specially, rather than as a generic instance. - * For example, `type` or `int`. - */ - boolean containsSpecial() { - this = Empty() and result = false - or - exists(ClassDecl decl | decl = this.getHead().getClassDeclaration() | - if decl.isSpecial() then result = true else result = this.getTail().containsSpecial() - ) - } + /** + * Holds if this MRO contains a class whose instances we treat specially, rather than as a generic instance. + * For example, `type` or `int`. + */ + boolean containsSpecial() { + this = Empty() and result = false + or + exists(ClassDecl decl | decl = this.getHead().getClassDeclaration() | + if decl.isSpecial() then result = true else result = this.getTail().containsSpecial() + ) + } } private newtype TClassListList = - EmptyList() or - ConsList(TClassList head, TClassListList tail) { required_list(head, tail) } + EmptyList() or + ConsList(TClassList head, TClassListList tail) { required_list(head, tail) } /* Keep ClassListList finite and as small as possible */ private predicate required_list(ClassList head, ClassListList tail) { - any(ClassListList x).removedClassParts(_, head, tail, _) - or - head = bases(_) and tail = EmptyList() - or - exists(ClassObjectInternal cls, int n | - head = Mro::newStyleMro(Types::getBase(cls, n)) and - tail = list_of_linearization_of_bases_plus_bases(cls, n + 1) - ) - or - exists(ClassObjectInternal cls, int n | - head = Mro::oldStyleMro(Types::getBase(cls, n)) and - tail = list_old_style_base_mros(cls, n + 1) - ) + any(ClassListList x).removedClassParts(_, head, tail, _) + or + head = bases(_) and tail = EmptyList() + or + exists(ClassObjectInternal cls, int n | + head = Mro::newStyleMro(Types::getBase(cls, n)) and + tail = list_of_linearization_of_bases_plus_bases(cls, n + 1) + ) + or + exists(ClassObjectInternal cls, int n | + head = Mro::oldStyleMro(Types::getBase(cls, n)) and + tail = list_old_style_base_mros(cls, n + 1) + ) } private class ClassListList extends TClassListList { - /** Gets a textual representation of this element. */ - string toString() { result = "[" + this.contents() + "]" } + /** Gets a textual representation of this element. */ + string toString() { result = "[" + this.contents() + "]" } - string contents() { - this = EmptyList() and result = "" - or - exists(ClassList head | head = this.getHead() | - this.getTail() = EmptyList() and result = head.toString() - or - this.getTail() != EmptyList() and result = head.toString() + ", " + this.getTail().contents() - ) - } + string contents() { + this = EmptyList() and result = "" + or + exists(ClassList head | head = this.getHead() | + this.getTail() = EmptyList() and result = head.toString() + or + this.getTail() != EmptyList() and result = head.toString() + ", " + this.getTail().contents() + ) + } - int length() { - this = EmptyList() and result = 0 - or - result = this.getTail().length() + 1 - } + int length() { + this = EmptyList() and result = 0 + or + result = this.getTail().length() + 1 + } - ClassList getHead() { this = ConsList(result, _) } + ClassList getHead() { this = ConsList(result, _) } - ClassListList getTail() { this = ConsList(_, result) } + ClassListList getTail() { this = ConsList(_, result) } - ClassList getItem(int n) { - n = 0 and result = this.getHead() - or - result = this.getTail().getItem(n - 1) - } + ClassList getItem(int n) { + n = 0 and result = this.getHead() + or + result = this.getTail().getItem(n - 1) + } - private ClassObjectInternal getAHead() { - result = this.getHead().getHead() - or - result = this.getTail().getAHead() - } + private ClassObjectInternal getAHead() { + result = this.getHead().getHead() + or + result = this.getTail().getAHead() + } - pragma[nomagic] - ClassList merge() { - exists(ClassList reversed | - merge_step(reversed, EmptyList(), this) and - result = reversed.reverse() - ) - or - this = EmptyList() and result = Empty() - } + pragma[nomagic] + ClassList merge() { + exists(ClassList reversed | + merge_step(reversed, EmptyList(), this) and + result = reversed.reverse() + ) + or + this = EmptyList() and result = Empty() + } - /* Join ordering helper */ - pragma[noinline] - predicate removedClassParts( - ClassObjectInternal cls, ClassList removed_head, ClassListList removed_tail, int n - ) { - cls = this.bestMergeCandidate() and - n = this.length() - 1 and - removed_head = this.getItem(n).removeHead(cls) and - removed_tail = EmptyList() - or - exists(ClassList prev_head, ClassListList prev_tail | - this.removedClassParts(cls, prev_head, prev_tail, n + 1) and - removed_head = this.getItem(n).removeHead(cls) and - removed_tail = ConsList(prev_head, prev_tail) - ) - } + /* Join ordering helper */ + pragma[noinline] + predicate removedClassParts( + ClassObjectInternal cls, ClassList removed_head, ClassListList removed_tail, int n + ) { + cls = this.bestMergeCandidate() and + n = this.length() - 1 and + removed_head = this.getItem(n).removeHead(cls) and + removed_tail = EmptyList() + or + exists(ClassList prev_head, ClassListList prev_tail | + this.removedClassParts(cls, prev_head, prev_tail, n + 1) and + removed_head = this.getItem(n).removeHead(cls) and + removed_tail = ConsList(prev_head, prev_tail) + ) + } - ClassListList remove(ClassObjectInternal cls) { - exists(ClassList removed_head, ClassListList removed_tail | - this.removedClassParts(cls, removed_head, removed_tail, 0) and - result = ConsList(removed_head, removed_tail) - ) - or - this = EmptyList() and result = EmptyList() - } + ClassListList remove(ClassObjectInternal cls) { + exists(ClassList removed_head, ClassListList removed_tail | + this.removedClassParts(cls, removed_head, removed_tail, 0) and + result = ConsList(removed_head, removed_tail) + ) + or + this = EmptyList() and result = EmptyList() + } - predicate legalMergeCandidate(ClassObjectInternal cls, int n) { - cls = this.getAHead() and n = this.length() - or - this.getItem(n).legalMergeHead(cls) and - this.legalMergeCandidate(cls, n + 1) - } + predicate legalMergeCandidate(ClassObjectInternal cls, int n) { + cls = this.getAHead() and n = this.length() + or + this.getItem(n).legalMergeHead(cls) and + this.legalMergeCandidate(cls, n + 1) + } - predicate legalMergeCandidate(ClassObjectInternal cls) { this.legalMergeCandidate(cls, 0) } + predicate legalMergeCandidate(ClassObjectInternal cls) { this.legalMergeCandidate(cls, 0) } - predicate illegalMergeCandidate(ClassObjectInternal cls) { - cls = this.getAHead() and - this.getItem(_).getTail().contains(cls) - } + predicate illegalMergeCandidate(ClassObjectInternal cls) { + cls = this.getAHead() and + this.getItem(_).getTail().contains(cls) + } - ClassObjectInternal bestMergeCandidate(int n) { - exists(ClassObjectInternal head | head = this.getItem(n).getHead() | - legalMergeCandidate(head) and result = head - or - illegalMergeCandidate(head) and result = this.bestMergeCandidate(n + 1) - ) - } + ClassObjectInternal bestMergeCandidate(int n) { + exists(ClassObjectInternal head | head = this.getItem(n).getHead() | + legalMergeCandidate(head) and result = head + or + illegalMergeCandidate(head) and result = this.bestMergeCandidate(n + 1) + ) + } - ClassObjectInternal bestMergeCandidate() { result = this.bestMergeCandidate(0) } + ClassObjectInternal bestMergeCandidate() { result = this.bestMergeCandidate(0) } - /** - * Gets a ClassList representing the this list of list flattened into a single list. - * Used for old-style MRO computation. - */ - ClassList flatten() { - this = EmptyList() and result = Empty() - or - result = flatten_list(this, 0) - } + /** + * Gets a ClassList representing the this list of list flattened into a single list. + * Used for old-style MRO computation. + */ + ClassList flatten() { + this = EmptyList() and result = Empty() + or + result = flatten_list(this, 0) + } } private ClassList flatten_list(ClassListList list, int n) { - need_flattening(list) and - exists(ClassList head, ClassListList tail | list = ConsList(head, tail) | - n = head.length() and result = tail.flatten() - or - result = Cons(head.getItem(n), flatten_list(list, n + 1)) - ) + need_flattening(list) and + exists(ClassList head, ClassListList tail | list = ConsList(head, tail) | + n = head.length() and result = tail.flatten() + or + result = Cons(head.getItem(n), flatten_list(list, n + 1)) + ) } /* Restrict flattening to those lists that need to be flattened */ private predicate need_flattening(ClassListList list) { - list = list_old_style_base_mros(_) - or - exists(ClassListList toflatten | - need_flattening(toflatten) and - list = toflatten.getTail() - ) + list = list_old_style_base_mros(_) + or + exists(ClassListList toflatten | + need_flattening(toflatten) and + list = toflatten.getTail() + ) } private ClassList bases(ClassObjectInternal cls) { result = bases(cls, 0) } private ClassList bases(ClassObjectInternal cls, int n) { - result = Cons(Types::getBase(cls, n), bases(cls, n + 1)) - or - result = Empty() and n = Types::base_count(cls) + result = Cons(Types::getBase(cls, n), bases(cls, n + 1)) + or + result = Empty() and n = Types::base_count(cls) } private ClassListList list_of_linearization_of_bases_plus_bases(ClassObjectInternal cls) { - result = list_of_linearization_of_bases_plus_bases(cls, 0) + result = list_of_linearization_of_bases_plus_bases(cls, 0) } private ClassListList list_of_linearization_of_bases_plus_bases(ClassObjectInternal cls, int n) { - result = ConsList(bases(cls), EmptyList()) and n = Types::base_count(cls) and n > 1 - or - exists(ClassListList partial | - partial = list_of_linearization_of_bases_plus_bases(cls, n + 1) and - result = ConsList(Mro::newStyleMro(Types::getBase(cls, n)), partial) - ) + result = ConsList(bases(cls), EmptyList()) and n = Types::base_count(cls) and n > 1 + or + exists(ClassListList partial | + partial = list_of_linearization_of_bases_plus_bases(cls, n + 1) and + result = ConsList(Mro::newStyleMro(Types::getBase(cls, n)), partial) + ) } private ClassList merge_of_linearization_of_bases(ClassObjectInternal cls) { - result = list_of_linearization_of_bases_plus_bases(cls).merge() + result = list_of_linearization_of_bases_plus_bases(cls).merge() } private ClassListList list_old_style_base_mros(ClassObjectInternal cls) { - result = list_old_style_base_mros(cls, 0) + result = list_old_style_base_mros(cls, 0) } pragma[nomagic] private ClassListList list_old_style_base_mros(ClassObjectInternal cls, int n) { - n = Types::base_count(cls) and result = EmptyList() - or - result = ConsList(Mro::oldStyleMro(Types::getBase(cls, n)), list_old_style_base_mros(cls, n + 1)) + n = Types::base_count(cls) and result = EmptyList() + or + result = ConsList(Mro::oldStyleMro(Types::getBase(cls, n)), list_old_style_base_mros(cls, n + 1)) } /** @@ -410,52 +410,52 @@ private ClassListList list_old_style_base_mros(ClassObjectInternal cls, int n) { * of computing the C3 linearization of `original`. */ private predicate merge_step( - ClassList reversed_mro, ClassListList remaining_list, ClassListList original + ClassList reversed_mro, ClassListList remaining_list, ClassListList original ) { - remaining_list = list_of_linearization_of_bases_plus_bases(_) and - reversed_mro = Empty() and - remaining_list = original - or - /* Removes the best merge candidate from `remaining_list` and prepends it to `reversed_mro` */ - exists(ClassObjectInternal head, ClassList prev_reverse_mro, ClassListList prev_list | - merge_step(prev_reverse_mro, prev_list, original) and - head = prev_list.bestMergeCandidate() and - reversed_mro = Cons(head, prev_reverse_mro) and - remaining_list = prev_list.remove(head) - ) - or - merge_step(reversed_mro, ConsList(Empty(), remaining_list), original) + remaining_list = list_of_linearization_of_bases_plus_bases(_) and + reversed_mro = Empty() and + remaining_list = original + or + /* Removes the best merge candidate from `remaining_list` and prepends it to `reversed_mro` */ + exists(ClassObjectInternal head, ClassList prev_reverse_mro, ClassListList prev_list | + merge_step(prev_reverse_mro, prev_list, original) and + head = prev_list.bestMergeCandidate() and + reversed_mro = Cons(head, prev_reverse_mro) and + remaining_list = prev_list.remove(head) + ) + or + merge_step(reversed_mro, ConsList(Empty(), remaining_list), original) } /* Helpers for `ClassList.reverse()` */ private predicate needs_reversing(ClassList lst) { - merge_step(lst, EmptyList(), _) - or - lst = Empty() + merge_step(lst, EmptyList(), _) + or + lst = Empty() } private predicate reverse_step(ClassList lst, ClassList remainder, ClassList reversed) { - needs_reversing(lst) and remainder = lst and reversed = Empty() - or - exists(ClassObjectInternal head, ClassList tail | - reversed = Cons(head, tail) and - reverse_step(lst, Cons(head, remainder), tail) - ) + needs_reversing(lst) and remainder = lst and reversed = Empty() + or + exists(ClassObjectInternal head, ClassList tail | + reversed = Cons(head, tail) and + reverse_step(lst, Cons(head, remainder), tail) + ) } module Mro { - cached - ClassList newStyleMro(ClassObjectInternal cls) { - cls = ObjectInternal::builtin("object") and result = Cons(cls, Empty()) - or - result = Cons(cls, merge_of_linearization_of_bases(cls)) - or - result = Cons(cls, newStyleMro(sole_base(cls))) - } + cached + ClassList newStyleMro(ClassObjectInternal cls) { + cls = ObjectInternal::builtin("object") and result = Cons(cls, Empty()) + or + result = Cons(cls, merge_of_linearization_of_bases(cls)) + or + result = Cons(cls, newStyleMro(sole_base(cls))) + } - cached - ClassList oldStyleMro(ClassObjectInternal cls) { - Types::isOldStyle(cls) and - result = Cons(cls, list_old_style_base_mros(cls).flatten()).(ClassList).deduplicate() - } + cached + ClassList oldStyleMro(ClassObjectInternal cls) { + Types::isOldStyle(cls) and + result = Cons(cls, list_old_style_base_mros(cls).flatten()).(ClassList).deduplicate() + } } diff --git a/python/ql/src/semmle/python/pointsto/PointsTo.qll b/python/ql/src/semmle/python/pointsto/PointsTo.qll index b4ed40ab5d6..9614b02d2f6 100644 --- a/python/ql/src/semmle/python/pointsto/PointsTo.qll +++ b/python/ql/src/semmle/python/pointsto/PointsTo.qll @@ -9,41 +9,41 @@ private import semmle.python.types.Extensions /* Use this version for speed */ library class CfgOrigin extends @py_object { - /** Gets a textual representation of this element. */ - string toString() { - /* Not to be displayed */ - result = "CfgOrigin" - } + /** Gets a textual representation of this element. */ + string toString() { + /* Not to be displayed */ + result = "CfgOrigin" + } - /** - * Get a `ControlFlowNode` from `this` or `here`. - * If `this` is a ControlFlowNode then use that, otherwise fall back on `here` - */ - pragma[inline] - ControlFlowNode asCfgNodeOrHere(ControlFlowNode here) { - result = this - or - not this instanceof ControlFlowNode and result = here - } + /** + * Get a `ControlFlowNode` from `this` or `here`. + * If `this` is a ControlFlowNode then use that, otherwise fall back on `here` + */ + pragma[inline] + ControlFlowNode asCfgNodeOrHere(ControlFlowNode here) { + result = this + or + not this instanceof ControlFlowNode and result = here + } - ControlFlowNode toCfgNode() { result = this } + ControlFlowNode toCfgNode() { result = this } - pragma[inline] - CfgOrigin fix(ControlFlowNode here) { - if this = Builtin::unknown() then result = here else result = this - } + pragma[inline] + CfgOrigin fix(ControlFlowNode here) { + if this = Builtin::unknown() then result = here else result = this + } } module CfgOrigin { - CfgOrigin fromCfgNode(ControlFlowNode f) { result = f } + CfgOrigin fromCfgNode(ControlFlowNode f) { result = f } - CfgOrigin unknown() { result = Builtin::unknown() } + CfgOrigin unknown() { result = Builtin::unknown() } - CfgOrigin fromObject(ObjectInternal obj) { - obj.isBuiltin() and result = unknown() - or - result = obj.getOrigin() - } + CfgOrigin fromObject(ObjectInternal obj) { + obj.isBuiltin() and result = unknown() + or + result = obj.getOrigin() + } } /* Use this version for stronger type-checking */ @@ -101,2639 +101,2639 @@ module CfgOrigin { //} /* The API */ module PointsTo { - predicate pointsTo( - ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - PointsToInternal::pointsTo(f, context, value, origin) - } + predicate pointsTo( + ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + PointsToInternal::pointsTo(f, context, value, origin) + } - predicate variablePointsTo( - EssaVariable var, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - PointsToInternal::variablePointsTo(var, context, value, origin) - } + predicate variablePointsTo( + EssaVariable var, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + PointsToInternal::variablePointsTo(var, context, value, origin) + } - /* Backwards compatibility */ - cached - predicate points_to( - ControlFlowNode f, PointsToContext context, Object obj, ClassObject cls, ControlFlowNode origin - ) { - exists(ObjectInternal value | - PointsToInternal::pointsTo(f, context, value, origin) and - cls = value.getClass().getSource() - | - obj = value.getSource() - or - value.useOriginAsLegacyObject() and obj = origin - ) - or - /* Backwards compatibility for *args and **kwargs */ - exists(Function func | obj = f and origin = f and context.isRuntime() | - func.getVararg() = f.getNode() and cls = theTupleType() - or - func.getKwarg() = f.getNode() and cls = theDictType() - ) - or - not f.isParameter() and - exists(ObjectInternal value | - PointsToInternal::pointsTo(f.(DefinitionNode).getValue(), context, value, origin) and - cls = value.getClass().getSource() - | - obj = value.getSource() - or - value.useOriginAsLegacyObject() and obj = origin - ) - } + /* Backwards compatibility */ + cached + predicate points_to( + ControlFlowNode f, PointsToContext context, Object obj, ClassObject cls, ControlFlowNode origin + ) { + exists(ObjectInternal value | + PointsToInternal::pointsTo(f, context, value, origin) and + cls = value.getClass().getSource() + | + obj = value.getSource() + or + value.useOriginAsLegacyObject() and obj = origin + ) + or + /* Backwards compatibility for *args and **kwargs */ + exists(Function func | obj = f and origin = f and context.isRuntime() | + func.getVararg() = f.getNode() and cls = theTupleType() + or + func.getKwarg() = f.getNode() and cls = theDictType() + ) + or + not f.isParameter() and + exists(ObjectInternal value | + PointsToInternal::pointsTo(f.(DefinitionNode).getValue(), context, value, origin) and + cls = value.getClass().getSource() + | + obj = value.getSource() + or + value.useOriginAsLegacyObject() and obj = origin + ) + } - deprecated predicate ssa_variable_points_to( - EssaVariable var, PointsToContext context, Object obj, ClassObject cls, CfgOrigin origin - ) { - exists(ObjectInternal value | - PointsToInternal::variablePointsTo(var, context, value, origin) and - cls = value.getClass().getSource() - | - obj = value.getSource() - ) - } + deprecated predicate ssa_variable_points_to( + EssaVariable var, PointsToContext context, Object obj, ClassObject cls, CfgOrigin origin + ) { + exists(ObjectInternal value | + PointsToInternal::variablePointsTo(var, context, value, origin) and + cls = value.getClass().getSource() + | + obj = value.getSource() + ) + } - deprecated CallNode get_a_call(Object func, PointsToContext context) { - exists(ObjectInternal value | - result = value.(Value).getACall(context) and - func = value.getSource() - ) - } + deprecated CallNode get_a_call(Object func, PointsToContext context) { + exists(ObjectInternal value | + result = value.(Value).getACall(context) and + func = value.getSource() + ) + } - cached - predicate moduleExports(ModuleObjectInternal mod, string name) { - InterModulePointsTo::moduleExportsBoolean(mod, name) = true - } + cached + predicate moduleExports(ModuleObjectInternal mod, string name) { + InterModulePointsTo::moduleExportsBoolean(mod, name) = true + } } cached module PointsToInternal { - pragma[noinline] - cached - predicate importCtxPointsTo(ControlFlowNode f, ObjectInternal value, ControlFlowNode origin) { - PointsToInternal::pointsTo(f, any(Context ctx | ctx.isImport()), value, origin) - } + pragma[noinline] + cached + predicate importCtxPointsTo(ControlFlowNode f, ObjectInternal value, ControlFlowNode origin) { + PointsToInternal::pointsTo(f, any(Context ctx | ctx.isImport()), value, origin) + } - /** INTERNAL -- Use `f.refersTo(value, origin)` instead. */ - cached - predicate pointsTo( - ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - points_to_candidate(f, context, value, origin) and - reachableBlock(f.getBasicBlock(), context) - } + /** INTERNAL -- Use `f.refersTo(value, origin)` instead. */ + cached + predicate pointsTo( + ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + points_to_candidate(f, context, value, origin) and + reachableBlock(f.getBasicBlock(), context) + } - cached - predicate pointsToString(ControlFlowNode f, PointsToContext context, string value) { - exists(ObjectInternal str | - PointsToInternal::pointsTo(f, context, str, _) and - str.strValue() = value - ) - } + cached + predicate pointsToString(ControlFlowNode f, PointsToContext context, string value) { + exists(ObjectInternal str | + PointsToInternal::pointsTo(f, context, str, _) and + str.strValue() = value + ) + } - private predicate points_to_candidate( - ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - use_points_to(f, context, value, origin) - or - attribute_load_points_to(f, context, value, origin) - or - Expressions::pointsTo(f, context, value, origin, _, _) - or - if_exp_points_to(f, context, value, origin) - or - origin = f and value.introducedAt(f, context) - or - InterModulePointsTo::import_points_to(f, context, value, origin) - or - InterModulePointsTo::from_import_points_to(f, context, value, origin) - or - InterProceduralPointsTo::call_points_to(f, context, value, origin) - or - AttributePointsTo::pointsTo(f, context, value, origin) - or - f.(PointsToExtension).pointsTo(context, value, origin) - or - iteration_points_to(f, context, value, origin) - } + private predicate points_to_candidate( + ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + use_points_to(f, context, value, origin) + or + attribute_load_points_to(f, context, value, origin) + or + Expressions::pointsTo(f, context, value, origin, _, _) + or + if_exp_points_to(f, context, value, origin) + or + origin = f and value.introducedAt(f, context) + or + InterModulePointsTo::import_points_to(f, context, value, origin) + or + InterModulePointsTo::from_import_points_to(f, context, value, origin) + or + InterProceduralPointsTo::call_points_to(f, context, value, origin) + or + AttributePointsTo::pointsTo(f, context, value, origin) + or + f.(PointsToExtension).pointsTo(context, value, origin) + or + iteration_points_to(f, context, value, origin) + } - /** - * Holds if the attribute `name` is required for `obj` - * For object `x` and attribute `name` it means that there exists somewhere in the code - * `x.name` or `getattr(x, "name")`. - */ - cached - predicate attributeRequired(ObjectInternal obj, string name) { - pointsTo(any(AttrNode a).getObject(name), _, obj, _) + /** + * Holds if the attribute `name` is required for `obj` + * For object `x` and attribute `name` it means that there exists somewhere in the code + * `x.name` or `getattr(x, "name")`. + */ + cached + predicate attributeRequired(ObjectInternal obj, string name) { + pointsTo(any(AttrNode a).getObject(name), _, obj, _) + or + Expressions::getattr_call(_, _, _, obj, name) + } + + /* Holds if BasicBlock `b` is reachable, given the context `context`. */ + cached + predicate reachableBlock(BasicBlock b, PointsToContext context) { + exists(Scope scope | + context.appliesToScope(scope) and + scope.getEntryNode().getBasicBlock() = b + ) + or + reachableEdge(_, b, context) + or + exists(BasicBlock pred | + reachableBlock(pred, context) and + pred.alwaysReaches(b) + ) + } + + private predicate reachableEdge(BasicBlock pred, BasicBlock succ, PointsToContext context) { + reachableBlock(pred, context) and + ( + pred.getAnUnconditionalSuccessor() = succ + or + exists(ObjectInternal value, boolean sense, ControlFlowNode test | + test = pred.getLastNode() and + pointsTo(test, context, value, _) and + sense = value.booleanValue() + | + sense = true and succ = pred.getATrueSuccessor() or - Expressions::getattr_call(_, _, _, obj, name) - } + sense = false and succ = pred.getAFalseSuccessor() + ) + ) + } - /* Holds if BasicBlock `b` is reachable, given the context `context`. */ - cached - predicate reachableBlock(BasicBlock b, PointsToContext context) { - exists(Scope scope | - context.appliesToScope(scope) and - scope.getEntryNode().getBasicBlock() = b - ) - or - reachableEdge(_, b, context) - or - exists(BasicBlock pred | - reachableBlock(pred, context) and - pred.alwaysReaches(b) - ) - } + /** Gets an object pointed to by a use (of a variable). */ + pragma[noinline] + private predicate use_points_to( + NameNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(CfgOrigin origin_or_obj | + value != ObjectInternal::undefined() and + use_points_to_maybe_origin(f, context, value, origin_or_obj) + | + origin = origin_or_obj.asCfgNodeOrHere(f) + ) + } - private predicate reachableEdge(BasicBlock pred, BasicBlock succ, PointsToContext context) { - reachableBlock(pred, context) and - ( - pred.getAnUnconditionalSuccessor() = succ - or - exists(ObjectInternal value, boolean sense, ControlFlowNode test | - test = pred.getLastNode() and - pointsTo(test, context, value, _) and - sense = value.booleanValue() - | - sense = true and succ = pred.getATrueSuccessor() - or - sense = false and succ = pred.getAFalseSuccessor() - ) - ) - } + pragma[noinline] + private predicate use_points_to_maybe_origin( + NameNode f, PointsToContext context, ObjectInternal value, CfgOrigin origin_or_obj + ) { + variablePointsTo(fast_local_variable(f), context, value, origin_or_obj) + or + name_lookup_points_to_maybe_origin(f, context, value, origin_or_obj) + or + not exists(fast_local_variable(f)) and + not exists(name_local_variable(f)) and + global_lookup_points_to_maybe_origin(f, context, value, origin_or_obj) + } - /** Gets an object pointed to by a use (of a variable). */ - pragma[noinline] - private predicate use_points_to( - NameNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(CfgOrigin origin_or_obj | - value != ObjectInternal::undefined() and - use_points_to_maybe_origin(f, context, value, origin_or_obj) - | - origin = origin_or_obj.asCfgNodeOrHere(f) - ) - } - - pragma[noinline] - private predicate use_points_to_maybe_origin( - NameNode f, PointsToContext context, ObjectInternal value, CfgOrigin origin_or_obj - ) { - variablePointsTo(fast_local_variable(f), context, value, origin_or_obj) - or - name_lookup_points_to_maybe_origin(f, context, value, origin_or_obj) - or - not exists(fast_local_variable(f)) and - not exists(name_local_variable(f)) and - global_lookup_points_to_maybe_origin(f, context, value, origin_or_obj) - } - - /** Holds if `var` refers to `(value, origin)` given the context `context`. */ - pragma[noinline] - cached - predicate variablePointsTo( - EssaVariable var, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - ssa_definition_points_to(var.getDefinition(), context, value, origin) - or - exists(EssaVariable prev | - ssaShortCut+(prev, var) and - variablePointsTo(prev, context, value, origin) - ) - } - - private predicate ssaShortCut(EssaVariable start, EssaVariable end) { - end.getDefinition().(PhiFunction).getShortCircuitInput() = start - or - /* Attribute assignments have no effect as far as value tracking is concerned, except for `__class__`. */ - exists(AttributeAssignment def | - not def.getName() = "__class__" and - start = def.getInput() and - end.getDefinition() = def - ) - or - /* - * Ignore the effects of calls on their arguments. PointsTo is an approximation, - * but attempting to improve accuracy would be very expensive for very little gain. - */ - - exists(ArgumentRefinement def | - start = def.getInput() and - end.getDefinition() = def - ) - } - - pragma[noinline] - private predicate name_lookup_points_to_maybe_origin( - NameNode f, PointsToContext context, ObjectInternal value, CfgOrigin origin_or_obj - ) { - exists(EssaVariable var | var = name_local_variable(f) | - variablePointsTo(var, context, value, origin_or_obj) - ) - or - local_variable_undefined(f, context) and - global_lookup_points_to_maybe_origin(f, context, value, origin_or_obj) - } - - pragma[noinline] - private predicate local_variable_undefined(NameNode f, PointsToContext context) { - variablePointsTo(name_local_variable(f), context, ObjectInternal::undefined(), _) - } - - pragma[noinline] - private predicate global_lookup_points_to_maybe_origin( - NameNode f, PointsToContext context, ObjectInternal value, CfgOrigin origin_or_obj - ) { - variablePointsTo(global_variable(f), context, value, origin_or_obj) - or - exists(ControlFlowNode origin | origin_or_obj = CfgOrigin::fromCfgNode(origin) | - variablePointsTo(global_variable(f), context, ObjectInternal::undefined(), _) and - potential_builtin_points_to(f, value, origin) - or - not exists(global_variable(f)) and - context.appliesToScope(f.getScope()) and - potential_builtin_points_to(f, value, origin) - ) - } - - /** The ESSA variable with fast-local lookup (LOAD_FAST bytecode). */ - private EssaVariable fast_local_variable(NameNode n) { - n.isLoad() and - result.getASourceUse() = n and - result.getSourceVariable() instanceof FastLocalVariable - } - - /** The ESSA variable with name-local lookup (LOAD_NAME bytecode). */ - private EssaVariable name_local_variable(NameNode n) { - n.isLoad() and - result.getASourceUse() = n and - result.getSourceVariable() instanceof NameLocalVariable - } - - /** The ESSA variable for the global variable lookup. */ - private EssaVariable global_variable(NameNode n) { - n.isLoad() and - result.getASourceUse() = n and - result.getSourceVariable() instanceof GlobalVariable - } - - /** Holds if `f` is an attribute `x.attr` and points to `(value, cls, origin)`. */ - pragma[noinline] - private predicate attribute_load_points_to( - AttrNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - none() - // TO DO -- Support CustomPointsToAttribute - //or - //exists(CustomPointsToAttribute object, string name | - // pointsTo(f.getObject(name), context, object, _, _) and - // object.attributePointsTo(name, value, cls, origin) - //) - } + /** Holds if `var` refers to `(value, origin)` given the context `context`. */ + pragma[noinline] + cached + predicate variablePointsTo( + EssaVariable var, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + ssa_definition_points_to(var.getDefinition(), context, value, origin) + or + exists(EssaVariable prev | + ssaShortCut+(prev, var) and + variablePointsTo(prev, context, value, origin) + ) + } + private predicate ssaShortCut(EssaVariable start, EssaVariable end) { + end.getDefinition().(PhiFunction).getShortCircuitInput() = start + or + /* Attribute assignments have no effect as far as value tracking is concerned, except for `__class__`. */ + exists(AttributeAssignment def | + not def.getName() = "__class__" and + start = def.getInput() and + end.getDefinition() = def + ) + or /* - * Treat `ForNode` as intermediate step between sequence and iteration variable. - * In otherwords treat `for i in x:` as being equivalent to `i = next(iter(x))` - * attaching the value of `next(iter(x))` to the `ForNode`. + * Ignore the effects of calls on their arguments. PointsTo is an approximation, + * but attempting to improve accuracy would be very expensive for very little gain. + */ + + exists(ArgumentRefinement def | + start = def.getInput() and + end.getDefinition() = def + ) + } + + pragma[noinline] + private predicate name_lookup_points_to_maybe_origin( + NameNode f, PointsToContext context, ObjectInternal value, CfgOrigin origin_or_obj + ) { + exists(EssaVariable var | var = name_local_variable(f) | + variablePointsTo(var, context, value, origin_or_obj) + ) + or + local_variable_undefined(f, context) and + global_lookup_points_to_maybe_origin(f, context, value, origin_or_obj) + } + + pragma[noinline] + private predicate local_variable_undefined(NameNode f, PointsToContext context) { + variablePointsTo(name_local_variable(f), context, ObjectInternal::undefined(), _) + } + + pragma[noinline] + private predicate global_lookup_points_to_maybe_origin( + NameNode f, PointsToContext context, ObjectInternal value, CfgOrigin origin_or_obj + ) { + variablePointsTo(global_variable(f), context, value, origin_or_obj) + or + exists(ControlFlowNode origin | origin_or_obj = CfgOrigin::fromCfgNode(origin) | + variablePointsTo(global_variable(f), context, ObjectInternal::undefined(), _) and + potential_builtin_points_to(f, value, origin) + or + not exists(global_variable(f)) and + context.appliesToScope(f.getScope()) and + potential_builtin_points_to(f, value, origin) + ) + } + + /** The ESSA variable with fast-local lookup (LOAD_FAST bytecode). */ + private EssaVariable fast_local_variable(NameNode n) { + n.isLoad() and + result.getASourceUse() = n and + result.getSourceVariable() instanceof FastLocalVariable + } + + /** The ESSA variable with name-local lookup (LOAD_NAME bytecode). */ + private EssaVariable name_local_variable(NameNode n) { + n.isLoad() and + result.getASourceUse() = n and + result.getSourceVariable() instanceof NameLocalVariable + } + + /** The ESSA variable for the global variable lookup. */ + private EssaVariable global_variable(NameNode n) { + n.isLoad() and + result.getASourceUse() = n and + result.getSourceVariable() instanceof GlobalVariable + } + + /** Holds if `f` is an attribute `x.attr` and points to `(value, cls, origin)`. */ + pragma[noinline] + private predicate attribute_load_points_to( + AttrNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + none() + // TO DO -- Support CustomPointsToAttribute + //or + //exists(CustomPointsToAttribute object, string name | + // pointsTo(f.getObject(name), context, object, _, _) and + // object.attributePointsTo(name, value, cls, origin) + //) + } + + /* + * Treat `ForNode` as intermediate step between sequence and iteration variable. + * In otherwords treat `for i in x:` as being equivalent to `i = next(iter(x))` + * attaching the value of `next(iter(x))` to the `ForNode`. + */ + + pragma[noinline] + private predicate iteration_points_to( + ForNode for, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(ControlFlowNode seqNode, ObjectInternal seq | + for.iterates(_, seqNode) and + pointsTo(seqNode, context, seq, _) and + value = seq.getIterNext() and + origin = for + ) + } + + /** Holds if the ESSA definition `def` refers to `(value, origin)` given the context `context`. */ + private predicate ssa_definition_points_to( + EssaDefinition def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + ssa_phi_points_to(def, context, value, origin) + or + exists(ControlFlowNode orig | + ssa_node_definition_points_to(def, context, value, orig) and + origin = CfgOrigin::fromCfgNode(orig) + ) + or + ssa_filter_definition_points_to(def, context, value, origin) + or + ssa_node_refinement_points_to(def, context, value, origin) + } + + pragma[noinline] + private predicate ssa_node_definition_points_to( + EssaNodeDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + reachableBlock(def.getDefiningNode().getBasicBlock(), context) and + ssa_node_definition_points_to_unpruned(def, context, value, origin) + } + + pragma[nomagic] + private predicate ssa_node_definition_points_to_unpruned( + EssaNodeDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + InterProceduralPointsTo::parameter_points_to(def, context, value, origin) + or + assignment_points_to(def, context, value, origin) + or + multi_assignment_points_to(def, context, value, origin) + or + self_parameter_points_to(def, context, value, origin) + or + delete_points_to(def, context, value, origin) + or + module_name_points_to(def, context, value, origin) + or + scope_entry_points_to(def, context, value, origin) + or + InterModulePointsTo::implicit_submodule_points_to(def, value, origin) and context.isImport() + /* + * No points-to for non-local function entry definitions yet. */ - pragma[noinline] - private predicate iteration_points_to( - ForNode for, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(ControlFlowNode seqNode, ObjectInternal seq | - for.iterates(_, seqNode) and - pointsTo(seqNode, context, seq, _) and - value = seq.getIterNext() and - origin = for - ) } - /** Holds if the ESSA definition `def` refers to `(value, origin)` given the context `context`. */ - private predicate ssa_definition_points_to( - EssaDefinition def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - ssa_phi_points_to(def, context, value, origin) - or - exists(ControlFlowNode orig | - ssa_node_definition_points_to(def, context, value, orig) and - origin = CfgOrigin::fromCfgNode(orig) - ) - or - ssa_filter_definition_points_to(def, context, value, origin) - or - ssa_node_refinement_points_to(def, context, value, origin) - } + pragma[noinline] + private predicate ssa_node_refinement_points_to( + EssaNodeRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + method_callsite_points_to(def, context, value, origin) + or + InterModulePointsTo::import_star_points_to(def, context, value, origin) + or + attribute_assignment_points_to(def, context, value, origin) + or + InterProceduralPointsTo::callsite_points_to(def, context, value, origin) + or + attribute_delete_points_to(def, context, value, origin) + or + uni_edged_pi_points_to(def, context, value, origin) + } - pragma[noinline] - private predicate ssa_node_definition_points_to( - EssaNodeDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - reachableBlock(def.getDefiningNode().getBasicBlock(), context) and - ssa_node_definition_points_to_unpruned(def, context, value, origin) - } + /** Pass through for `self` for the implicit re-definition of `self` in `self.foo()`. */ + private predicate method_callsite_points_to( + MethodCallsiteRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + /* The value of self remains the same, only the attributes may change */ + variablePointsTo(def.getInput(), context, value, origin) + } - pragma[nomagic] - private predicate ssa_node_definition_points_to_unpruned( - EssaNodeDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - InterProceduralPointsTo::parameter_points_to(def, context, value, origin) - or - assignment_points_to(def, context, value, origin) - or - multi_assignment_points_to(def, context, value, origin) - or - self_parameter_points_to(def, context, value, origin) - or - delete_points_to(def, context, value, origin) - or - module_name_points_to(def, context, value, origin) - or - scope_entry_points_to(def, context, value, origin) - or - InterModulePointsTo::implicit_submodule_points_to(def, value, origin) and context.isImport() - /* - * No points-to for non-local function entry definitions yet. - */ + /** Attribute deletions have no effect as far as value tracking is concerned. */ + pragma[noinline] + private predicate attribute_delete_points_to( + EssaAttributeDeletion def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + variablePointsTo(def.getInput(), context, value, origin) + } - } + /** Attribute assignments have no effect as far as value tracking is concerned, except for `__class__`. */ + pragma[noinline] + private predicate attribute_assignment_points_to( + AttributeAssignment def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + def.getName() = "__class__" and + exists(ObjectInternal cls | + pointsTo(def.getValue(), context, cls, _) and + value = TUnknownInstance(cls) and + origin = CfgOrigin::fromCfgNode(def.getDefiningNode()) + ) + } - pragma[noinline] - private predicate ssa_node_refinement_points_to( - EssaNodeRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - method_callsite_points_to(def, context, value, origin) - or - InterModulePointsTo::import_star_points_to(def, context, value, origin) - or - attribute_assignment_points_to(def, context, value, origin) - or - InterProceduralPointsTo::callsite_points_to(def, context, value, origin) - or - attribute_delete_points_to(def, context, value, origin) - or - uni_edged_pi_points_to(def, context, value, origin) - } + private predicate self_parameter_points_to( + ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + origin = def.getDefiningNode() and + value.(SelfInstanceInternal).parameterAndContext(def, context) + } - /** Pass through for `self` for the implicit re-definition of `self` in `self.foo()`. */ - private predicate method_callsite_points_to( - MethodCallsiteRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - /* The value of self remains the same, only the attributes may change */ - variablePointsTo(def.getInput(), context, value, origin) - } + /** Holds if ESSA edge refinement, `def`, refers to `(value, cls, origin)`. */ + private predicate ssa_filter_definition_points_to( + PyEdgeRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + exists(ControlFlowNode orig | + def.getSense() = ssa_filter_definition_bool(def, context, value, orig) and + origin = CfgOrigin::fromCfgNode(orig) + ) + } - /** Attribute deletions have no effect as far as value tracking is concerned. */ - pragma[noinline] - private predicate attribute_delete_points_to( - EssaAttributeDeletion def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - variablePointsTo(def.getInput(), context, value, origin) - } + private boolean ssa_filter_definition_bool( + PyEdgeRefinement def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + result = + Conditionals::testEvaluates(def.getTest(), def.getInput().getASourceUse(), context, value, + origin) + } - /** Attribute assignments have no effect as far as value tracking is concerned, except for `__class__`. */ - pragma[noinline] - private predicate attribute_assignment_points_to( - AttributeAssignment def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - def.getName() = "__class__" and - exists(ObjectInternal cls | - pointsTo(def.getValue(), context, cls, _) and - value = TUnknownInstance(cls) and - origin = CfgOrigin::fromCfgNode(def.getDefiningNode()) - ) - } + /** Holds if ESSA definition, `unipi`, refers to `(value, origin)`. */ + pragma[noinline] + private predicate uni_edged_pi_points_to( + SingleSuccessorGuard unipi, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + exists(ControlFlowNode test, ControlFlowNode use, ControlFlowNode orig | + /* + * Because calls such as `len` may create a new variable, we need to go via the source variable + * That is perfectly safe as we are only dealing with calls that do not mutate their arguments. + */ - private predicate self_parameter_points_to( - ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - origin = def.getDefiningNode() and - value.(SelfInstanceInternal).parameterAndContext(def, context) - } + unipi.useAndTest(use, test) and + unipi.getSense() = Conditionals::testEvaluates(test, use, context, value, orig) and + origin = CfgOrigin::fromCfgNode(orig) + ) + } - /** Holds if ESSA edge refinement, `def`, refers to `(value, cls, origin)`. */ - private predicate ssa_filter_definition_points_to( - PyEdgeRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - exists(ControlFlowNode orig | - def.getSense() = ssa_filter_definition_bool(def, context, value, orig) and - origin = CfgOrigin::fromCfgNode(orig) - ) - } + /** Points-to for normal assignments `def = ...`. */ + pragma[noinline] + private predicate assignment_points_to( + AssignmentDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + pointsTo(def.getValue(), context, value, origin) + } - private boolean ssa_filter_definition_bool( - PyEdgeRefinement def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - result = - Conditionals::testEvaluates(def.getTest(), def.getInput().getASourceUse(), context, value, - origin) - } + pragma[nomagic] + private predicate sequence_index_points_to( + ControlFlowNode f, PointsToContext context, SequenceObjectInternal sequence, + ObjectInternal value, int index + ) { + pointsTo(f, context, sequence, _) and + value = sequence.getItem(index) + } - /** Holds if ESSA definition, `unipi`, refers to `(value, origin)`. */ - pragma[noinline] - private predicate uni_edged_pi_points_to( - SingleSuccessorGuard unipi, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - exists(ControlFlowNode test, ControlFlowNode use, ControlFlowNode orig | - /* - * Because calls such as `len` may create a new variable, we need to go via the source variable - * That is perfectly safe as we are only dealing with calls that do not mutate their arguments. - */ + pragma[noinline] + private predicate multi_assignment_points_to( + MultiAssignmentDefinition def, PointsToContext context, ObjectInternal value, + ControlFlowNode origin + ) { + exists(int index, ControlFlowNode lhs, ControlFlowNode rhs, ObjectInternal sequence | + def.indexOf(index, lhs) and + lhs.(DefinitionNode).getValue() = rhs and + origin = def.getDefiningNode() + | + sequence_index_points_to(rhs, context, sequence, value, index) + or + pointsTo(rhs, context, sequence, _) and + sequence.subscriptUnknown() and + value = TUnknown() + ) + } - unipi.useAndTest(use, test) and - unipi.getSense() = Conditionals::testEvaluates(test, use, context, value, orig) and - origin = CfgOrigin::fromCfgNode(orig) - ) - } + /** Points-to for deletion: `del name`. */ + pragma[noinline] + private predicate delete_points_to( + DeletionDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + value = ObjectInternal::undefined() and + origin = def.getDefiningNode() and + context.appliesToScope(def.getScope()) + } - /** Points-to for normal assignments `def = ...`. */ - pragma[noinline] - private predicate assignment_points_to( - AssignmentDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - pointsTo(def.getValue(), context, value, origin) - } + /** Implicit "definition" of `__name__` at the start of a module. */ + pragma[noinline] + private predicate module_name_points_to( + ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + def.getVariable().getName() = "__name__" and + exists(Module m | m = def.getScope() | + value = module_dunder_name(m) and context.isImport() + or + value.strValue() = "__main__" and context.isMain() and context.appliesToScope(m) + ) and + origin = def.getDefiningNode() + } - pragma[nomagic] - private predicate sequence_index_points_to( - ControlFlowNode f, PointsToContext context, SequenceObjectInternal sequence, - ObjectInternal value, int index - ) { - pointsTo(f, context, sequence, _) and - value = sequence.getItem(index) - } + private ObjectInternal module_dunder_name(Module m) { + exists(string name | result.strValue() = name | + if m.isPackageInit() then name = m.getPackage().getName() else name = m.getName() + ) + } - pragma[noinline] - private predicate multi_assignment_points_to( - MultiAssignmentDefinition def, PointsToContext context, ObjectInternal value, - ControlFlowNode origin - ) { - exists(int index, ControlFlowNode lhs, ControlFlowNode rhs, ObjectInternal sequence | - def.indexOf(index, lhs) and - lhs.(DefinitionNode).getValue() = rhs and - origin = def.getDefiningNode() - | - sequence_index_points_to(rhs, context, sequence, value, index) - or - pointsTo(rhs, context, sequence, _) and - sequence.subscriptUnknown() and - value = TUnknown() - ) - } + /** Holds if the phi-function `phi` refers to `(value, origin)` given the context `context`. */ + pragma[nomagic] + private predicate ssa_phi_points_to( + PhiFunction phi, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + exists(EssaVariable input | + ssa_phi_reachable_from_input(phi, context, input) and + variablePointsTo(input, context, value, origin) + ) + } - /** Points-to for deletion: `del name`. */ - pragma[noinline] - private predicate delete_points_to( - DeletionDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - value = ObjectInternal::undefined() and - origin = def.getDefiningNode() and - context.appliesToScope(def.getScope()) - } + /* Helper for ssa_phi_points_to */ + cached + predicate ssa_phi_reachable_from_input( + PhiFunction phi, PointsToContext context, EssaVariable input + ) { + exists(BasicBlock pred | + input = phi.getInput(pred) and + reachableEdge(pred, phi.getBasicBlock(), context) + ) + } - /** Implicit "definition" of `__name__` at the start of a module. */ - pragma[noinline] - private predicate module_name_points_to( - ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - def.getVariable().getName() = "__name__" and - exists(Module m | m = def.getScope() | - value = module_dunder_name(m) and context.isImport() - or - value.strValue() = "__main__" and context.isMain() and context.appliesToScope(m) - ) and - origin = def.getDefiningNode() - } + /** Points-to for implicit variable declarations at scope-entry. */ + pragma[noinline] + private predicate scope_entry_points_to( + ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + /* Transfer from another scope */ + exists(EssaVariable var, PointsToContext outer, CfgOrigin orig | + InterProceduralPointsTo::scope_entry_value_transfer(var, outer, def, context) and + variablePointsTo(var, outer, value, orig) and + origin = orig.asCfgNodeOrHere(def.getDefiningNode()) + ) + or + /* Undefined variable */ + undefined_variable(def, context, value, origin) + or + /* Builtin not defined in outer scope */ + builtin_not_in_outer_scope(def, context, value, origin) + } - private ObjectInternal module_dunder_name(Module m) { - exists(string name | result.strValue() = name | - if m.isPackageInit() then name = m.getPackage().getName() else name = m.getName() - ) - } + private predicate undefined_variable( + ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(Scope scope | + not def.getVariable().getName() = "__name__" and + not def.getVariable().isMetaVariable() and + def.getScope() = scope and + context.appliesToScope(scope) + | + def.getSourceVariable() instanceof GlobalVariable and scope instanceof Module + or + def.getSourceVariable() instanceof LocalVariable and + (context.isImport() or context.isRuntime() or context.isMain()) + ) and + value = ObjectInternal::undefined() and + origin = def.getDefiningNode() + } - /** Holds if the phi-function `phi` refers to `(value, origin)` given the context `context`. */ - pragma[nomagic] - private predicate ssa_phi_points_to( - PhiFunction phi, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - exists(EssaVariable input | - ssa_phi_reachable_from_input(phi, context, input) and - variablePointsTo(input, context, value, origin) - ) - } + private predicate builtin_not_in_outer_scope( + ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(Module mod, GlobalVariable var | + var = def.getSourceVariable() and + mod = def.getScope().getEnclosingModule() and + context.appliesToScope(def.getScope()) and + not exists(EssaVariable v | v.getSourceVariable() = var and v.getScope() = mod) and + value = ObjectInternal::builtin(var.getId()) and + origin = def.getDefiningNode() + ) + } - /* Helper for ssa_phi_points_to */ - cached - predicate ssa_phi_reachable_from_input( - PhiFunction phi, PointsToContext context, EssaVariable input - ) { - exists(BasicBlock pred | - input = phi.getInput(pred) and - reachableEdge(pred, phi.getBasicBlock(), context) - ) - } + /** Holds if `f` is an expression node `tval if cond else fval` and points to `(value, origin)`. */ + private predicate if_exp_points_to( + IfExprNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + pointsTo(f.getAnOperand(), context, value, origin) + } - /** Points-to for implicit variable declarations at scope-entry. */ - pragma[noinline] - private predicate scope_entry_points_to( - ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - /* Transfer from another scope */ - exists(EssaVariable var, PointsToContext outer, CfgOrigin orig | - InterProceduralPointsTo::scope_entry_value_transfer(var, outer, def, context) and - variablePointsTo(var, outer, value, orig) and - origin = orig.asCfgNodeOrHere(def.getDefiningNode()) - ) - or - /* Undefined variable */ - undefined_variable(def, context, value, origin) - or - /* Builtin not defined in outer scope */ - builtin_not_in_outer_scope(def, context, value, origin) - } - - private predicate undefined_variable( - ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(Scope scope | - not def.getVariable().getName() = "__name__" and - not def.getVariable().isMetaVariable() and - def.getScope() = scope and - context.appliesToScope(scope) - | - def.getSourceVariable() instanceof GlobalVariable and scope instanceof Module - or - def.getSourceVariable() instanceof LocalVariable and - (context.isImport() or context.isRuntime() or context.isMain()) - ) and - value = ObjectInternal::undefined() and - origin = def.getDefiningNode() - } - - private predicate builtin_not_in_outer_scope( - ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(Module mod, GlobalVariable var | - var = def.getSourceVariable() and - mod = def.getScope().getEnclosingModule() and - context.appliesToScope(def.getScope()) and - not exists(EssaVariable v | v.getSourceVariable() = var and v.getScope() = mod) and - value = ObjectInternal::builtin(var.getId()) and - origin = def.getDefiningNode() - ) - } - - /** Holds if `f` is an expression node `tval if cond else fval` and points to `(value, origin)`. */ - private predicate if_exp_points_to( - IfExprNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - pointsTo(f.getAnOperand(), context, value, origin) - } - - /* Holds if `import name` will import the module `m`. */ - cached - predicate module_imported_as(ModuleObjectInternal m, string name) { - /* Normal imports */ - m.getName() = name - or - /* sys.modules['name'] = m */ - exists(ControlFlowNode sys_modules_flow, ControlFlowNode n, ControlFlowNode mod | - /* Use previous points-to here to avoid slowing down the recursion too much */ - exists(SubscriptNode sub | - sub.getObject() = sys_modules_flow and - pointsTo(sys_modules_flow, _, ObjectInternal::sysModules(), _) and - sub.getIndex() = n and - n.getNode().(StrConst).getText() = name and - sub.(DefinitionNode).getValue() = mod and - pointsTo(mod, _, m, _) - ) - ) - } + /* Holds if `import name` will import the module `m`. */ + cached + predicate module_imported_as(ModuleObjectInternal m, string name) { + /* Normal imports */ + m.getName() = name + or + /* sys.modules['name'] = m */ + exists(ControlFlowNode sys_modules_flow, ControlFlowNode n, ControlFlowNode mod | + /* Use previous points-to here to avoid slowing down the recursion too much */ + exists(SubscriptNode sub | + sub.getObject() = sys_modules_flow and + pointsTo(sys_modules_flow, _, ObjectInternal::sysModules(), _) and + sub.getIndex() = n and + n.getNode().(StrConst).getText() = name and + sub.(DefinitionNode).getValue() = mod and + pointsTo(mod, _, m, _) + ) + ) + } } private module InterModulePointsTo { - pragma[noinline] - predicate import_points_to( - ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(string name, ImportExpr i | - i.getAFlowNode() = f and - i.getImportedModuleName() = name and - PointsToInternal::module_imported_as(value, name) and - origin = f and - context.appliesTo(f) - ) - } + pragma[noinline] + predicate import_points_to( + ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(string name, ImportExpr i | + i.getAFlowNode() = f and + i.getImportedModuleName() = name and + PointsToInternal::module_imported_as(value, name) and + origin = f and + context.appliesTo(f) + ) + } - predicate from_import_points_to( - ImportMemberNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - from_self_import_points_to(f, context, value, origin) + predicate from_import_points_to( + ImportMemberNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + from_self_import_points_to(f, context, value, origin) + or + from_other_import_points_to(f, context, value, origin) + } + + pragma[noinline] + predicate from_self_import_points_to( + ImportMemberNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(EssaVariable var, CfgOrigin orig | + var = ssa_variable_for_module_attribute(f, context) and + PointsToInternal::variablePointsTo(var, context, value, orig) and + origin = orig.asCfgNodeOrHere(f) + ) + } + + pragma[noinline] + predicate from_other_import_points_to( + ImportMemberNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(string name, ModuleObjectInternal mod, CfgOrigin orig | + from_import_imports(f, context, mod, name) and + (mod.getSourceModule() != f.getEnclosingModule() or mod.isBuiltin()) and + mod.attribute(name, value, orig) and + origin = orig.asCfgNodeOrHere(f) + ) + or + PointsToInternal::pointsTo(f.getModule(_), context, ObjectInternal::unknown(), _) and + value = ObjectInternal::unknown() and + origin = f + } + + private predicate from_import_imports( + ImportMemberNode f, PointsToContext context, ModuleObjectInternal mod, string name + ) { + PointsToInternal::pointsTo(f.getModule(name), context, mod, _) + } + + pragma[noinline] + private EssaVariable ssa_variable_for_module_attribute(ImportMemberNode f, PointsToContext context) { + exists(string name, ModuleObjectInternal mod, Module m | + mod.getSourceModule() = m and + m = result.getScope() and + PointsToInternal::pointsTo(f.getModule(name), context, mod, _) and + result = ssa_variable_for_module_attribute_helper(f, name, m) + ) + } + + pragma[noinline] + private EssaVariable ssa_variable_for_module_attribute_helper( + ImportMemberNode f, string name, Module m + ) { + result.getSourceVariable().getName() = name and + result.getAUse() = f and + m = f.getEnclosingModule() + } + + /* Helper for implicit_submodule_points_to */ + private ModuleObjectInternal getModule(ImplicitSubModuleDefinition def) { + exists(PackageObjectInternal package | + package.getSourceModule() = def.getDefiningNode().getScope() and + result = package.submodule(def.getSourceVariable().getName()) + ) + } + + /** + * Implicit "definition" of the names of submodules at the start of an `__init__.py` file. + * + * PointsTo isn't exactly how the interpreter works, but is the best approximation we can manage statically. + */ + pragma[noinline] + predicate implicit_submodule_points_to( + ImplicitSubModuleDefinition def, ModuleObjectInternal value, ControlFlowNode origin + ) { + value = getModule(def) and + origin = CfgOrigin::fromObject(value).asCfgNodeOrHere(def.getDefiningNode()) + } + + /** Points-to for `from ... import *`. */ + predicate import_star_points_to( + ImportStarRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + /* Attribute from imported module */ + exists(CfgOrigin orig, ImportStarNode imp, ModuleObjectInternal mod, string name | + imp = def.getDefiningNode() and + PointsToInternal::pointsTo(imp.getModule(), context, mod, _) and + name = def.getSourceVariable().getName() and + moduleExportsBoolean(mod, name) = true and + mod.attribute(name, value, orig) and + origin = orig.fix(imp) + ) + or + /* Retain value held before import */ + exists(EssaVariable var | + variable_not_redefined_by_import_star(var, context, def) and + PointsToInternal::variablePointsTo(var, context, value, origin) + ) + } + + /** Holds if `def` is technically a definition of `var`, but the `from ... import *` does not in fact define `var`. */ + cached + predicate variable_not_redefined_by_import_star( + EssaVariable var, PointsToContext context, ImportStarRefinement def + ) { + var = def.getInput() and + exists(ModuleObjectInternal mod | + PointsToInternal::pointsTo(def.getDefiningNode().(ImportStarNode).getModule(), context, mod, _) + | + moduleExportsBoolean(mod, var.getSourceVariable().getName()) = false + or + var.getSourceVariable().getName().charAt(0) = "_" + or + exists(Module m, string name | + m = mod.getSourceModule() and name = var.getSourceVariable().getName() + | + not m.declaredInAll(_) and name.charAt(0) = "_" + ) + ) + } + + predicate ofInterestInExports(ModuleObjectInternal mod, string name) { + exists(ImportStarNode imp, ImportStarRefinement def, EssaVariable var | + imp = def.getDefiningNode() and + PointsToInternal::importCtxPointsTo(imp.getModule(), mod, _) and + var = def.getVariable() + | + if var.isMetaVariable() + then ModuleAttributes::attributePointsTo(def.getInput().getDefinition(), name, _, _) + else def.getVariable().getName() = name + ) + or + exists(PackageObjectInternal package | + ofInterestInExports(package, name) and + package.getInitModule() = mod + ) + } + + private boolean pythonModuleExportsBoolean(PythonModuleObjectInternal mod, string name) { + exists(Module src | src = mod.getSourceModule() | + src.declaredInAll(name) and result = true + or + declared_all_is_simple(src) and + not src.declaredInAll(name) and + ofInterestInExports(mod, name) and + result = false + or + (not src.declaredInAll(name) and not declared_all_is_simple(src)) and + exists(ObjectInternal val | ModuleAttributes::pointsToAtExit(src, name, val, _) | + val = ObjectInternal::undefined() and result = false or - from_other_import_points_to(f, context, value, origin) - } + val != ObjectInternal::undefined() and result = true + ) + ) + } - pragma[noinline] - predicate from_self_import_points_to( - ImportMemberNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(EssaVariable var, CfgOrigin orig | - var = ssa_variable_for_module_attribute(f, context) and - PointsToInternal::variablePointsTo(var, context, value, orig) and - origin = orig.asCfgNodeOrHere(f) - ) - } + /** Holds if __all__ is declared and not mutated */ + private predicate declared_all_is_simple(Module m) { + exists(AssignStmt a, GlobalVariable all | + a.defines(all) and + a.getScope() = m and + all.getId() = "__all__" and + not exists(Attribute attr | all.getALoad() = attr.getObject()) + ) + } - pragma[noinline] - predicate from_other_import_points_to( - ImportMemberNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(string name, ModuleObjectInternal mod, CfgOrigin orig | - from_import_imports(f, context, mod, name) and - (mod.getSourceModule() != f.getEnclosingModule() or mod.isBuiltin()) and - mod.attribute(name, value, orig) and - origin = orig.asCfgNodeOrHere(f) - ) - or - PointsToInternal::pointsTo(f.getModule(_), context, ObjectInternal::unknown(), _) and - value = ObjectInternal::unknown() and - origin = f - } + private boolean packageExportsBoolean(PackageObjectInternal mod, string name) { + exists(Folder folder | folder = mod.getFolder() | + exportsSubmodule(folder, name) and result = true + or + not exportsSubmodule(folder, name) and + result = moduleExportsBoolean(mod.getInitModule(), name) + or + mod.hasNoInitModule() and + not exportsSubmodule(folder, name) and + ofInterestInExports(mod, name) and + result = false + ) + } - private predicate from_import_imports( - ImportMemberNode f, PointsToContext context, ModuleObjectInternal mod, string name - ) { - PointsToInternal::pointsTo(f.getModule(name), context, mod, _) - } + private predicate exportsSubmodule(Folder folder, string name) { + name.regexpMatch("\\p{L}(\\p{L}|\\d|_)*") and + ( + exists(Folder child | child = folder.getChildContainer(name)) + or + exists(folder.getFile(name + ".py")) + ) + } - pragma[noinline] - private EssaVariable ssa_variable_for_module_attribute(ImportMemberNode f, PointsToContext context) { - exists(string name, ModuleObjectInternal mod, Module m | - mod.getSourceModule() = m and - m = result.getScope() and - PointsToInternal::pointsTo(f.getModule(name), context, mod, _) and - result = ssa_variable_for_module_attribute_helper(f, name, m) - ) - } + boolean builtinModuleExportsBoolean(BuiltinModuleObjectInternal mod, string name) { + exists(Builtin bltn | bltn = mod.getBuiltin() | + exists(bltn.getMember(name)) and result = true + or + ofInterestInExports(mod, name) and not exists(bltn.getMember(name)) and result = false + ) + } - pragma[noinline] - private EssaVariable ssa_variable_for_module_attribute_helper( - ImportMemberNode f, string name, Module m - ) { - result.getSourceVariable().getName() = name and - result.getAUse() = f and - m = f.getEnclosingModule() - } - - /* Helper for implicit_submodule_points_to */ - private ModuleObjectInternal getModule(ImplicitSubModuleDefinition def) { - exists(PackageObjectInternal package | - package.getSourceModule() = def.getDefiningNode().getScope() and - result = package.submodule(def.getSourceVariable().getName()) - ) - } - - /** - * Implicit "definition" of the names of submodules at the start of an `__init__.py` file. - * - * PointsTo isn't exactly how the interpreter works, but is the best approximation we can manage statically. - */ - pragma[noinline] - predicate implicit_submodule_points_to( - ImplicitSubModuleDefinition def, ModuleObjectInternal value, ControlFlowNode origin - ) { - value = getModule(def) and - origin = CfgOrigin::fromObject(value).asCfgNodeOrHere(def.getDefiningNode()) - } - - /** Points-to for `from ... import *`. */ - predicate import_star_points_to( - ImportStarRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - /* Attribute from imported module */ - exists(CfgOrigin orig, ImportStarNode imp, ModuleObjectInternal mod, string name | - imp = def.getDefiningNode() and - PointsToInternal::pointsTo(imp.getModule(), context, mod, _) and - name = def.getSourceVariable().getName() and - moduleExportsBoolean(mod, name) = true and - mod.attribute(name, value, orig) and - origin = orig.fix(imp) - ) - or - /* Retain value held before import */ - exists(EssaVariable var | - variable_not_redefined_by_import_star(var, context, def) and - PointsToInternal::variablePointsTo(var, context, value, origin) - ) - } - - /** Holds if `def` is technically a definition of `var`, but the `from ... import *` does not in fact define `var`. */ - cached - predicate variable_not_redefined_by_import_star( - EssaVariable var, PointsToContext context, ImportStarRefinement def - ) { - var = def.getInput() and - exists(ModuleObjectInternal mod | - PointsToInternal::pointsTo(def.getDefiningNode().(ImportStarNode).getModule(), context, mod, _) - | - moduleExportsBoolean(mod, var.getSourceVariable().getName()) = false - or - var.getSourceVariable().getName().charAt(0) = "_" - or - exists(Module m, string name | - m = mod.getSourceModule() and name = var.getSourceVariable().getName() - | - not m.declaredInAll(_) and name.charAt(0) = "_" - ) - ) - } - - predicate ofInterestInExports(ModuleObjectInternal mod, string name) { - exists(ImportStarNode imp, ImportStarRefinement def, EssaVariable var | - imp = def.getDefiningNode() and - PointsToInternal::importCtxPointsTo(imp.getModule(), mod, _) and - var = def.getVariable() - | - if var.isMetaVariable() - then ModuleAttributes::attributePointsTo(def.getInput().getDefinition(), name, _, _) - else def.getVariable().getName() = name - ) - or - exists(PackageObjectInternal package | - ofInterestInExports(package, name) and - package.getInitModule() = mod - ) - } - - private boolean pythonModuleExportsBoolean(PythonModuleObjectInternal mod, string name) { - exists(Module src | src = mod.getSourceModule() | - src.declaredInAll(name) and result = true - or - declared_all_is_simple(src) and - not src.declaredInAll(name) and - ofInterestInExports(mod, name) and - result = false - or - (not src.declaredInAll(name) and not declared_all_is_simple(src)) and - exists(ObjectInternal val | ModuleAttributes::pointsToAtExit(src, name, val, _) | - val = ObjectInternal::undefined() and result = false - or - val != ObjectInternal::undefined() and result = true - ) - ) - } - - /** Holds if __all__ is declared and not mutated */ - private predicate declared_all_is_simple(Module m) { - exists(AssignStmt a, GlobalVariable all | - a.defines(all) and - a.getScope() = m and - all.getId() = "__all__" and - not exists(Attribute attr | all.getALoad() = attr.getObject()) - ) - } - - private boolean packageExportsBoolean(PackageObjectInternal mod, string name) { - exists(Folder folder | folder = mod.getFolder() | - exportsSubmodule(folder, name) and result = true - or - not exportsSubmodule(folder, name) and - result = moduleExportsBoolean(mod.getInitModule(), name) - or - mod.hasNoInitModule() and - not exportsSubmodule(folder, name) and - ofInterestInExports(mod, name) and - result = false - ) - } - - private predicate exportsSubmodule(Folder folder, string name) { - name.regexpMatch("\\p{L}(\\p{L}|\\d|_)*") and - ( - exists(Folder child | child = folder.getChildContainer(name)) - or - exists(folder.getFile(name + ".py")) - ) - } - - boolean builtinModuleExportsBoolean(BuiltinModuleObjectInternal mod, string name) { - exists(Builtin bltn | bltn = mod.getBuiltin() | - exists(bltn.getMember(name)) and result = true - or - ofInterestInExports(mod, name) and not exists(bltn.getMember(name)) and result = false - ) - } - - boolean moduleExportsBoolean(ModuleObjectInternal mod, string name) { - not name.charAt(0) = "_" and - ( - result = pythonModuleExportsBoolean(mod, name) - or - result = packageExportsBoolean(mod, name) - or - result = builtinModuleExportsBoolean(mod, name) - ) - } + boolean moduleExportsBoolean(ModuleObjectInternal mod, string name) { + not name.charAt(0) = "_" and + ( + result = pythonModuleExportsBoolean(mod, name) + or + result = packageExportsBoolean(mod, name) + or + result = builtinModuleExportsBoolean(mod, name) + ) + } } module InterProceduralPointsTo { - cached - predicate call(CallNode call, PointsToContext caller, ObjectInternal value) { - PointsToInternal::pointsTo(call.getFunction(), caller, value, _) - } + cached + predicate call(CallNode call, PointsToContext caller, ObjectInternal value) { + PointsToInternal::pointsTo(call.getFunction(), caller, value, _) + } - cached - predicate callWithContext( - CallNode call, PointsToContext caller, ObjectInternal value, PointsToContext callee - ) { - callee.fromCall(call, caller) and - PointsToInternal::pointsTo(call.getFunction(), caller, value, _) - } + cached + predicate callWithContext( + CallNode call, PointsToContext caller, ObjectInternal value, PointsToContext callee + ) { + callee.fromCall(call, caller) and + PointsToInternal::pointsTo(call.getFunction(), caller, value, _) + } - pragma[noinline] - predicate call_points_to( - CallNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - /* Either not a decorator, or we understand the return value */ - (value != ObjectInternal::unknown() or not f.isDecoratorCall()) and - call_points_to_from_callee(f, context, value, origin) - or - f.isFunctionDecoratorCall() and - call_points_to_from_callee(f, context, ObjectInternal::unknown(), _) and - value = TDecoratedFunction(f) and - origin = f - or - f.isClassDecoratorCall() and - call_points_to_from_callee(f, context, ObjectInternal::unknown(), _) and - PointsToInternal::pointsTo(f.getArg(0), context, value, origin) - or - Types::six_add_metaclass(f, context, _, _) and - PointsToInternal::pointsTo(f.getArg(0), context, value, origin) - or - Expressions::typeCallPointsTo(f, context, value, origin, _, _) - } + pragma[noinline] + predicate call_points_to( + CallNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + /* Either not a decorator, or we understand the return value */ + (value != ObjectInternal::unknown() or not f.isDecoratorCall()) and + call_points_to_from_callee(f, context, value, origin) + or + f.isFunctionDecoratorCall() and + call_points_to_from_callee(f, context, ObjectInternal::unknown(), _) and + value = TDecoratedFunction(f) and + origin = f + or + f.isClassDecoratorCall() and + call_points_to_from_callee(f, context, ObjectInternal::unknown(), _) and + PointsToInternal::pointsTo(f.getArg(0), context, value, origin) + or + Types::six_add_metaclass(f, context, _, _) and + PointsToInternal::pointsTo(f.getArg(0), context, value, origin) + or + Expressions::typeCallPointsTo(f, context, value, origin, _, _) + } - /** Helper for call_points_to to improve join-order */ - pragma[noinline] - private predicate call_points_to_from_callee( - CallNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(ObjectInternal func | call(f, context, func) | - exists(CfgOrigin orig, PointsToContext callee | - callee.fromCall(f, context) and - func.callResult(callee, value, orig) and - origin = orig.asCfgNodeOrHere(f) - ) - or - context.untrackableCall(f) and - func.contextSensitiveCallee() and - value = ObjectInternal::unknown() and - origin = f - or - exists(CfgOrigin orig | - func.callResult(value, orig) and - origin = orig.asCfgNodeOrHere(f) - ) and - context.appliesTo(f) + /** Helper for call_points_to to improve join-order */ + pragma[noinline] + private predicate call_points_to_from_callee( + CallNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(ObjectInternal func | call(f, context, func) | + exists(CfgOrigin orig, PointsToContext callee | + callee.fromCall(f, context) and + func.callResult(callee, value, orig) and + origin = orig.asCfgNodeOrHere(f) + ) + or + context.untrackableCall(f) and + func.contextSensitiveCallee() and + value = ObjectInternal::unknown() and + origin = f + or + exists(CfgOrigin orig | + func.callResult(value, orig) and + origin = orig.asCfgNodeOrHere(f) + ) and + context.appliesTo(f) + ) + } + + /** Points-to for parameter. `def foo(param): ...`. */ + pragma[noinline] + predicate parameter_points_to( + ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + self_parameter_points_to(def, context, value, origin) + or + normal_parameter_points_to(def, context, value, origin) + or + default_parameter_points_to(def, context, value, origin) + or + special_parameter_points_to(def, context, value, origin) + } + + /** Helper for `parameter_points_to` */ + pragma[noinline] + private predicate normal_parameter_points_to( + ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(PointsToContext caller, ControlFlowNode arg | + PointsToInternal::pointsTo(arg, caller, value, origin) and + named_argument_transfer(arg, caller, def, context) + ) + or + not def.isSelf() and + not def.isVarargs() and + not def.isKwargs() and + context.isRuntime() and + value = ObjectInternal::unknown() and + origin = def.getDefiningNode() + or + positional_parameter_points_to(def, context, value, origin) + } + + pragma[noinline] + private predicate self_parameter_points_to( + ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + def.isSelf() and + exists(CallNode call, BoundMethodObjectInternal method, Function func, PointsToContext caller | + callWithContext(call, caller, method, context) and + func = method.getScope() and + def.getScope() = func and + value = method.getSelf() and + origin = value.getOrigin() + ) + } + + predicate selfMethodCall( + SelfCallsiteRefinement def, PointsToContext caller, Function func, PointsToContext callee + ) { + def.getInput().getSourceVariable().(Variable).isSelf() and + exists(PythonFunctionObjectInternal method, CallNode call | + method.getScope() = func and + call = method.getACall() and + call = def.getDefiningNode() and + callee.fromCall(call, caller) + ) + } + + /** Helper for parameter_points_to */ + pragma[noinline] + private predicate default_parameter_points_to( + ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + PointsToInternal::importCtxPointsTo(def.getDefault(), value, origin) and + context_for_default_value(def, context) + } + + /** Helper for default_parameter_points_to */ + pragma[noinline] + private predicate context_for_default_value(ParameterDefinition def, PointsToContext context) { + context.isRuntime() + or + exists(PointsToContext caller, CallNode call, PythonFunctionObjectInternal func, int n | + context.fromCall(call, func, caller) and + func.getScope().getArg(n) = def.getParameter() and + not exists(call.getArg(n)) and + not exists(call.getArgByName(def.getVariable().getName())) and + not exists(call.getNode().getKwargs()) and + not exists(call.getNode().getStarargs()) + ) + } + + /** Helper for parameter_points_to */ + pragma[noinline] + private predicate special_parameter_points_to( + ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + /* Runtime: Just an unknown tuple (or dict for `**` args) */ + special_parameter_value(def, value) and + context.isRuntime() and + origin = def.getDefiningNode() + or + /* A tuple constructed from positional arguments for a `*` parameter. */ + def.isVarargs() and + exists(CallNode call, Function scope, PointsToContext caller, int offset, int length | + varargs_tuple(call, caller, scope, context, offset, length) and + value = TVarargsTuple(call, caller, offset, length) and + def.getScope() = scope + ) and + origin = def.getDefiningNode() + or + /* A `*` parameter with no surplus positional arguments; an empty tuple */ + def.isVarargs() and + exists(Function scope | + varargs_empty_tuple(scope, context) and + value = ObjectInternal::emptyTuple() and + def.getScope() = scope + ) and + origin = def.getDefiningNode() + } + + /** + * Holds if `call` in context `caller` calls into the function scope `scope` in context `callee` and + * that the number of position arguments (including expansion of `*` argument) exceeds the number of positional arguments by + * `length` and that the excess arguments start at `start`. + */ + predicate varargs_tuple( + CallNode call, PointsToContext caller, Function scope, PointsToContext callee, int start, + int length + ) { + exists(int parameter_offset | + callsite_calls_function(call, caller, scope, callee, parameter_offset) and + start = scope.getPositionalParameterCount() - parameter_offset and + length = positional_argument_count(call, caller) - start and + length > 0 + ) + } + + /** Holds if for function scope `func` in context `callee` the `*` parameter will hold the empty tuple. */ + predicate varargs_empty_tuple(Function func, PointsToContext callee) { + exists(CallNode call, PointsToContext caller, int parameter_offset | + callsite_calls_function(call, caller, func, callee, parameter_offset) and + func.getPositionalParameterCount() - parameter_offset >= + positional_argument_count(call, caller) + ) + } + + /** Helper predicate for special_parameter_points_to */ + private predicate special_parameter_value(ParameterDefinition p, ObjectInternal value) { + p.isVarargs() and value = TUnknownInstance(ObjectInternal::builtin("tuple")) + or + p.isKwargs() and value = TUnknownInstance(ObjectInternal::builtin("dict")) + } + + /** + * Holds if the `n`th argument in call `call` with context `caller` points-to `value` from `origin`, including values in tuples + * expanded by a `*` argument. For example, for the call `f('a', *(`x`,`y`))` the arguments are `('a', 'x', y')` + */ + predicate positional_argument_points_to( + CallNode call, int n, PointsToContext caller, ObjectInternal value, ControlFlowNode origin + ) { + PointsToInternal::pointsTo(call.getArg(n), caller, value, origin) + or + exists(SequenceObjectInternal arg, int pos | + pos = call.getNode().getPositionalArgumentCount() and + PointsToInternal::pointsTo(origin, caller, arg, _) and + value = arg.getItem(n - pos) and + origin = call.getStarArg() + ) + } + + /** Gets the number of positional arguments including values in tuples expanded by a `*` argument. */ + private int positional_argument_count(CallNode call, PointsToContext caller) { + result = call.getNode().getPositionalArgumentCount() and + not exists(call.getStarArg()) and + caller.appliesTo(call) + or + exists(SequenceObjectInternal arg, int pos | + pos = call.getNode().getPositionalArgumentCount() and + PointsToInternal::pointsTo(call.getStarArg(), caller, arg, _) and + result = pos + arg.length() + ) + } + + /** Holds if the parameter definition `def` points-to `value` from `origin` given the context `context` */ + predicate positional_parameter_points_to( + ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(CallNode call, int argument, PointsToContext caller, Function func, int offset | + positional_argument_points_to(call, argument, caller, value, origin) and + callsite_calls_function(call, caller, func, context, offset) and + def.getParameter() = func.getArg(argument + offset) + ) + } + + /** Holds if the named `argument` given the context `caller` is transferred to the parameter `param` with conntext `callee` by a call. */ + cached + predicate named_argument_transfer( + ControlFlowNode argument, PointsToContext caller, ParameterDefinition param, + PointsToContext callee + ) { + exists(CallNode call, Function func, int offset | + callsite_calls_function(call, caller, func, callee, offset) + | + exists(string name | + argument = call.getArgByName(name) and + param.getParameter() = func.getArgByName(name) + ) + ) + } + + /** + * Holds if the `call` with context `caller` calls the function `scope` in context `callee` + * and the offset from argument to parameter is `parameter_offset` + */ + cached + predicate callsite_calls_function( + CallNode call, PointsToContext caller, Function scope, PointsToContext callee, + int parameter_offset + ) { + exists(ObjectInternal func | + callWithContext(call, caller, func, callee) and + func.calleeAndOffset(scope, parameter_offset) + ) + } + + /** Model the transfer of values at scope-entry points. Transfer from `(pred_var, pred_context)` to `(succ_def, succ_context)`. */ + cached + predicate scope_entry_value_transfer( + EssaVariable pred_var, PointsToContext pred_context, ScopeEntryDefinition succ_def, + PointsToContext succ_context + ) { + scope_entry_value_transfer_from_earlier(pred_var, pred_context, succ_def, succ_context) + or + callsite_entry_value_transfer(pred_var, pred_context, succ_def, succ_context) + or + pred_context.isImport() and + pred_context = succ_context and + class_entry_value_transfer(pred_var, succ_def) + } + + /** + * Helper for `scope_entry_value_transfer`. Transfer of values from a temporally earlier scope to later scope. + * Earlier and later scopes are, for example, a module and functions in that module, or an __init__ method and another method. + */ + pragma[noinline] + private predicate scope_entry_value_transfer_from_earlier( + EssaVariable pred_var, PointsToContext pred_context, ScopeEntryDefinition succ_def, + PointsToContext succ_context + ) { + exists(Scope pred_scope, Scope succ_scope | + BaseFlow::scope_entry_value_transfer_from_earlier(pred_var, pred_scope, succ_def, succ_scope) and + succ_context.appliesToScope(succ_scope) + | + succ_context.isRuntime() and succ_context = pred_context + or + pred_context.isImport() and + pred_scope instanceof ImportTimeScope and + ( + succ_context.fromRuntime() + or + /* A call made at import time, but from another module. Assume this module has been fully imported. */ + succ_context.isCall() and + exists(CallNode call | + succ_context.fromCall(call, _) and call.getEnclosingModule() != pred_scope ) - } + ) + or + /* + * If predecessor scope is main, then we assume that any global defined exactly once + * is available to all functions. Although not strictly true, this gives less surprising + * results in practice. + */ - /** Points-to for parameter. `def foo(param): ...`. */ - pragma[noinline] - predicate parameter_points_to( - ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - self_parameter_points_to(def, context, value, origin) - or - normal_parameter_points_to(def, context, value, origin) - or - default_parameter_points_to(def, context, value, origin) - or - special_parameter_points_to(def, context, value, origin) - } + pred_context.isMain() and + pred_scope instanceof Module and + succ_context.fromRuntime() and + exists(Variable v | + v = pred_var.getSourceVariable() and + not strictcount(v.getAStore()) > 1 + ) + ) + or + exists(NonEscapingGlobalVariable var | + var = pred_var.getSourceVariable() and + var = succ_def.getSourceVariable() and + pred_var.getAUse() = succ_context.getRootCall() and + pred_context.isImport() and + succ_context.appliesToScope(succ_def.getScope()) + ) + } - /** Helper for `parameter_points_to` */ - pragma[noinline] - private predicate normal_parameter_points_to( - ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(PointsToContext caller, ControlFlowNode arg | - PointsToInternal::pointsTo(arg, caller, value, origin) and - named_argument_transfer(arg, caller, def, context) + /** + * Helper for `scope_entry_value_transfer`. + * Transfer of values from the callsite to the callee, for enclosing variables, but not arguments/parameters. + */ + pragma[noinline] + private predicate callsite_entry_value_transfer( + EssaVariable caller_var, PointsToContext caller, ScopeEntryDefinition entry_def, + PointsToContext callee + ) { + exists(ControlFlowNode use, SsaSourceVariable var | + var_and_use(caller_var, use, var) and + entry_def.getSourceVariable() = var and + callsite_calls_function(use, caller, entry_def.getScope(), callee, _) + ) + } + + pragma[nomagic] + private predicate var_and_use(EssaVariable caller_var, ControlFlowNode use, SsaSourceVariable var) { + use = caller_var.getAUse() and + var = caller_var.getSourceVariable() + } + + /** Helper for `scope_entry_value_transfer`. */ + private predicate class_entry_value_transfer(EssaVariable pred_var, ScopeEntryDefinition succ_def) { + exists(ImportTimeScope scope, ControlFlowNode class_def | + class_def = pred_var.getAUse() and + scope.entryEdge(class_def, succ_def.getDefiningNode()) and + pred_var.getSourceVariable() = succ_def.getSourceVariable() + ) + } + + /** + * Points-to for a variable (possibly) redefined by a call: + * `var = ...; foo(); use(var)` + * Where var may be redefined in call to `foo` if `var` escapes (is global or non-local). + */ + pragma[noinline] + predicate callsite_points_to( + CallsiteRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + exists(SsaSourceVariable srcvar | srcvar = def.getSourceVariable() | + if srcvar instanceof EscapingAssignmentGlobalVariable + then + /* If global variable can be reassigned, we need to track it through calls */ + exists(EssaVariable var, Function func, PointsToContext callee | + callsite_calls_function(def.getCall(), context, func, callee, _) and + var_at_exit(srcvar, func, var) and + PointsToInternal::variablePointsTo(var, callee, value, origin) ) or - not def.isSelf() and - not def.isVarargs() and - not def.isKwargs() and - context.isRuntime() and - value = ObjectInternal::unknown() and - origin = def.getDefiningNode() - or - positional_parameter_points_to(def, context, value, origin) - } - - pragma[noinline] - private predicate self_parameter_points_to( - ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - def.isSelf() and - exists(CallNode call, BoundMethodObjectInternal method, Function func, PointsToContext caller | - callWithContext(call, caller, method, context) and - func = method.getScope() and - def.getScope() = func and - value = method.getSelf() and - origin = value.getOrigin() + exists(ObjectInternal callable | + PointsToInternal::pointsTo(def.getCall().getFunction(), context, callable, _) and + exists(callable.getBuiltin()) and + PointsToInternal::variablePointsTo(def.getInput(), context, value, origin) ) - } + else + /* Otherwise we can assume its value (but not those of its attributes or members) has not changed. */ + PointsToInternal::variablePointsTo(def.getInput(), context, value, origin) + ) + } - predicate selfMethodCall( - SelfCallsiteRefinement def, PointsToContext caller, Function func, PointsToContext callee - ) { - def.getInput().getSourceVariable().(Variable).isSelf() and - exists(PythonFunctionObjectInternal method, CallNode call | - method.getScope() = func and - call = method.getACall() and - call = def.getDefiningNode() and - callee.fromCall(call, caller) - ) - } + /* Helper for computing ESSA variables at scope exit. */ + private predicate var_at_exit(Variable var, Scope scope, EssaVariable evar) { + not var instanceof LocalVariable and + evar.getSourceVariable() = var and + evar.getScope() = scope and + BaseFlow::reaches_exit(evar) + } - /** Helper for parameter_points_to */ - pragma[noinline] - private predicate default_parameter_points_to( - ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - PointsToInternal::importCtxPointsTo(def.getDefault(), value, origin) and - context_for_default_value(def, context) - } - - /** Helper for default_parameter_points_to */ - pragma[noinline] - private predicate context_for_default_value(ParameterDefinition def, PointsToContext context) { - context.isRuntime() - or - exists(PointsToContext caller, CallNode call, PythonFunctionObjectInternal func, int n | - context.fromCall(call, func, caller) and - func.getScope().getArg(n) = def.getParameter() and - not exists(call.getArg(n)) and - not exists(call.getArgByName(def.getVariable().getName())) and - not exists(call.getNode().getKwargs()) and - not exists(call.getNode().getStarargs()) - ) - } - - /** Helper for parameter_points_to */ - pragma[noinline] - private predicate special_parameter_points_to( - ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - /* Runtime: Just an unknown tuple (or dict for `**` args) */ - special_parameter_value(def, value) and - context.isRuntime() and - origin = def.getDefiningNode() - or - /* A tuple constructed from positional arguments for a `*` parameter. */ - def.isVarargs() and - exists(CallNode call, Function scope, PointsToContext caller, int offset, int length | - varargs_tuple(call, caller, scope, context, offset, length) and - value = TVarargsTuple(call, caller, offset, length) and - def.getScope() = scope - ) and - origin = def.getDefiningNode() - or - /* A `*` parameter with no surplus positional arguments; an empty tuple */ - def.isVarargs() and - exists(Function scope | - varargs_empty_tuple(scope, context) and - value = ObjectInternal::emptyTuple() and - def.getScope() = scope - ) and - origin = def.getDefiningNode() - } - - /** - * Holds if `call` in context `caller` calls into the function scope `scope` in context `callee` and - * that the number of position arguments (including expansion of `*` argument) exceeds the number of positional arguments by - * `length` and that the excess arguments start at `start`. + /** + * INTERNAL -- Use `FunctionObject.neverReturns()` instead. + * Whether function `func` never returns. Slightly conservative approximation, this predicate may be false + * for a function that can never return. + */ + cached + predicate neverReturns(Function f) { + /* + * A Python function never returns if it has no normal exits that are not dominated by a + * call to a function which itself never returns. */ - predicate varargs_tuple( - CallNode call, PointsToContext caller, Function scope, PointsToContext callee, int start, - int length - ) { - exists(int parameter_offset | - callsite_calls_function(call, caller, scope, callee, parameter_offset) and - start = scope.getPositionalParameterCount() - parameter_offset and - length = positional_argument_count(call, caller) - start and - length > 0 - ) - } - /** Holds if for function scope `func` in context `callee` the `*` parameter will hold the empty tuple. */ - predicate varargs_empty_tuple(Function func, PointsToContext callee) { - exists(CallNode call, PointsToContext caller, int parameter_offset | - callsite_calls_function(call, caller, func, callee, parameter_offset) and - func.getPositionalParameterCount() - parameter_offset >= - positional_argument_count(call, caller) - ) - } - - /** Helper predicate for special_parameter_points_to */ - private predicate special_parameter_value(ParameterDefinition p, ObjectInternal value) { - p.isVarargs() and value = TUnknownInstance(ObjectInternal::builtin("tuple")) - or - p.isKwargs() and value = TUnknownInstance(ObjectInternal::builtin("dict")) - } - - /** - * Holds if the `n`th argument in call `call` with context `caller` points-to `value` from `origin`, including values in tuples - * expanded by a `*` argument. For example, for the call `f('a', *(`x`,`y`))` the arguments are `('a', 'x', y')` - */ - predicate positional_argument_points_to( - CallNode call, int n, PointsToContext caller, ObjectInternal value, ControlFlowNode origin - ) { - PointsToInternal::pointsTo(call.getArg(n), caller, value, origin) - or - exists(SequenceObjectInternal arg, int pos | - pos = call.getNode().getPositionalArgumentCount() and - PointsToInternal::pointsTo(origin, caller, arg, _) and - value = arg.getItem(n - pos) and - origin = call.getStarArg() - ) - } - - /** Gets the number of positional arguments including values in tuples expanded by a `*` argument. */ - private int positional_argument_count(CallNode call, PointsToContext caller) { - result = call.getNode().getPositionalArgumentCount() and - not exists(call.getStarArg()) and - caller.appliesTo(call) - or - exists(SequenceObjectInternal arg, int pos | - pos = call.getNode().getPositionalArgumentCount() and - PointsToInternal::pointsTo(call.getStarArg(), caller, arg, _) and - result = pos + arg.length() - ) - } - - /** Holds if the parameter definition `def` points-to `value` from `origin` given the context `context` */ - predicate positional_parameter_points_to( - ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(CallNode call, int argument, PointsToContext caller, Function func, int offset | - positional_argument_points_to(call, argument, caller, value, origin) and - callsite_calls_function(call, caller, func, context, offset) and - def.getParameter() = func.getArg(argument + offset) - ) - } - - /** Holds if the named `argument` given the context `caller` is transferred to the parameter `param` with conntext `callee` by a call. */ - cached - predicate named_argument_transfer( - ControlFlowNode argument, PointsToContext caller, ParameterDefinition param, - PointsToContext callee - ) { - exists(CallNode call, Function func, int offset | - callsite_calls_function(call, caller, func, callee, offset) - | - exists(string name | - argument = call.getArgByName(name) and - param.getParameter() = func.getArgByName(name) - ) - ) - } - - /** - * Holds if the `call` with context `caller` calls the function `scope` in context `callee` - * and the offset from argument to parameter is `parameter_offset` - */ - cached - predicate callsite_calls_function( - CallNode call, PointsToContext caller, Function scope, PointsToContext callee, - int parameter_offset - ) { - exists(ObjectInternal func | - callWithContext(call, caller, func, callee) and - func.calleeAndOffset(scope, parameter_offset) - ) - } - - /** Model the transfer of values at scope-entry points. Transfer from `(pred_var, pred_context)` to `(succ_def, succ_context)`. */ - cached - predicate scope_entry_value_transfer( - EssaVariable pred_var, PointsToContext pred_context, ScopeEntryDefinition succ_def, - PointsToContext succ_context - ) { - scope_entry_value_transfer_from_earlier(pred_var, pred_context, succ_def, succ_context) - or - callsite_entry_value_transfer(pred_var, pred_context, succ_def, succ_context) - or - pred_context.isImport() and - pred_context = succ_context and - class_entry_value_transfer(pred_var, succ_def) - } - - /** - * Helper for `scope_entry_value_transfer`. Transfer of values from a temporally earlier scope to later scope. - * Earlier and later scopes are, for example, a module and functions in that module, or an __init__ method and another method. - */ - pragma[noinline] - private predicate scope_entry_value_transfer_from_earlier( - EssaVariable pred_var, PointsToContext pred_context, ScopeEntryDefinition succ_def, - PointsToContext succ_context - ) { - exists(Scope pred_scope, Scope succ_scope | - BaseFlow::scope_entry_value_transfer_from_earlier(pred_var, pred_scope, succ_def, succ_scope) and - succ_context.appliesToScope(succ_scope) - | - succ_context.isRuntime() and succ_context = pred_context - or - pred_context.isImport() and - pred_scope instanceof ImportTimeScope and - ( - succ_context.fromRuntime() - or - /* A call made at import time, but from another module. Assume this module has been fully imported. */ - succ_context.isCall() and - exists(CallNode call | - succ_context.fromCall(call, _) and call.getEnclosingModule() != pred_scope - ) - ) - or - /* - * If predecessor scope is main, then we assume that any global defined exactly once - * is available to all functions. Although not strictly true, this gives less surprising - * results in practice. - */ - - pred_context.isMain() and - pred_scope instanceof Module and - succ_context.fromRuntime() and - exists(Variable v | - v = pred_var.getSourceVariable() and - not strictcount(v.getAStore()) > 1 - ) - ) - or - exists(NonEscapingGlobalVariable var | - var = pred_var.getSourceVariable() and - var = succ_def.getSourceVariable() and - pred_var.getAUse() = succ_context.getRootCall() and - pred_context.isImport() and - succ_context.appliesToScope(succ_def.getScope()) - ) - } - - /** - * Helper for `scope_entry_value_transfer`. - * Transfer of values from the callsite to the callee, for enclosing variables, but not arguments/parameters. - */ - pragma[noinline] - private predicate callsite_entry_value_transfer( - EssaVariable caller_var, PointsToContext caller, ScopeEntryDefinition entry_def, - PointsToContext callee - ) { - exists(ControlFlowNode use, SsaSourceVariable var | - var_and_use(caller_var, use, var) and - entry_def.getSourceVariable() = var and - callsite_calls_function(use, caller, entry_def.getScope(), callee, _) - ) - } - - pragma[nomagic] - private predicate var_and_use(EssaVariable caller_var, ControlFlowNode use, SsaSourceVariable var) { - use = caller_var.getAUse() and - var = caller_var.getSourceVariable() - } - - /** Helper for `scope_entry_value_transfer`. */ - private predicate class_entry_value_transfer(EssaVariable pred_var, ScopeEntryDefinition succ_def) { - exists(ImportTimeScope scope, ControlFlowNode class_def | - class_def = pred_var.getAUse() and - scope.entryEdge(class_def, succ_def.getDefiningNode()) and - pred_var.getSourceVariable() = succ_def.getSourceVariable() - ) - } - - /** - * Points-to for a variable (possibly) redefined by a call: - * `var = ...; foo(); use(var)` - * Where var may be redefined in call to `foo` if `var` escapes (is global or non-local). - */ - pragma[noinline] - predicate callsite_points_to( - CallsiteRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - exists(SsaSourceVariable srcvar | srcvar = def.getSourceVariable() | - if srcvar instanceof EscapingAssignmentGlobalVariable - then - /* If global variable can be reassigned, we need to track it through calls */ - exists(EssaVariable var, Function func, PointsToContext callee | - callsite_calls_function(def.getCall(), context, func, callee, _) and - var_at_exit(srcvar, func, var) and - PointsToInternal::variablePointsTo(var, callee, value, origin) - ) - or - exists(ObjectInternal callable | - PointsToInternal::pointsTo(def.getCall().getFunction(), context, callable, _) and - exists(callable.getBuiltin()) and - PointsToInternal::variablePointsTo(def.getInput(), context, value, origin) - ) - else - /* Otherwise we can assume its value (but not those of its attributes or members) has not changed. */ - PointsToInternal::variablePointsTo(def.getInput(), context, value, origin) - ) - } - - /* Helper for computing ESSA variables at scope exit. */ - private predicate var_at_exit(Variable var, Scope scope, EssaVariable evar) { - not var instanceof LocalVariable and - evar.getSourceVariable() = var and - evar.getScope() = scope and - BaseFlow::reaches_exit(evar) - } - - /** - * INTERNAL -- Use `FunctionObject.neverReturns()` instead. - * Whether function `func` never returns. Slightly conservative approximation, this predicate may be false - * for a function that can never return. - */ - cached - predicate neverReturns(Function f) { - /* - * A Python function never returns if it has no normal exits that are not dominated by a - * call to a function which itself never returns. - */ - - forall(BasicBlock exit | exit = f.getANormalExit().getBasicBlock() | - exists(FunctionObject callee, BasicBlock call | - callee.getACall().getBasicBlock() = call and - callee.neverReturns() and - call.dominates(exit) - ) - ) - } + forall(BasicBlock exit | exit = f.getANormalExit().getBasicBlock() | + exists(FunctionObject callee, BasicBlock call | + callee.getACall().getBasicBlock() = call and + callee.neverReturns() and + call.dominates(exit) + ) + ) + } } /** Gets the `value, origin` that `f` would refer to if it has not been assigned some other value */ pragma[noinline] private predicate potential_builtin_points_to( - NameNode f, ObjectInternal value, ControlFlowNode origin + NameNode f, ObjectInternal value, ControlFlowNode origin ) { - f.isGlobal() and - f.isLoad() and - origin = f and - ( - value = ObjectInternal::builtin(f.getId()) - or - not exists(Builtin::builtin(f.getId())) and value = ObjectInternal::unknown() - ) + f.isGlobal() and + f.isLoad() and + origin = f and + ( + value = ObjectInternal::builtin(f.getId()) + or + not exists(Builtin::builtin(f.getId())) and value = ObjectInternal::unknown() + ) } module Expressions { - pragma[noinline] - private predicate attributeObjectPointsto( - AttrNode attr, PointsToContext context, string name, ControlFlowNode obj, - ObjectInternal objvalue - ) { - attr.isLoad() and - attr.getObject(name) = obj and - PointsToInternal::pointsTo(obj, context, objvalue, _) - } + pragma[noinline] + private predicate attributeObjectPointsto( + AttrNode attr, PointsToContext context, string name, ControlFlowNode obj, + ObjectInternal objvalue + ) { + attr.isLoad() and + attr.getObject(name) = obj and + PointsToInternal::pointsTo(obj, context, objvalue, _) + } - pragma[noinline] - predicate attributePointsTo( - AttrNode attr, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode obj, ObjectInternal objvalue - ) { - exists(string name | attributeObjectPointsto(attr, context, name, obj, objvalue) | - exists(CfgOrigin orig | - objvalue.attribute(name, value, orig) and - origin = orig.asCfgNodeOrHere(attr) - ) - or - objvalue.attributesUnknown() and - origin = attr and - value = ObjectInternal::unknown() - ) - } + pragma[noinline] + predicate attributePointsTo( + AttrNode attr, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode obj, ObjectInternal objvalue + ) { + exists(string name | attributeObjectPointsto(attr, context, name, obj, objvalue) | + exists(CfgOrigin orig | + objvalue.attribute(name, value, orig) and + origin = orig.asCfgNodeOrHere(attr) + ) + or + objvalue.attributesUnknown() and + origin = attr and + value = ObjectInternal::unknown() + ) + } - pragma[noinline] - predicate subscriptPointsTo( - SubscriptNode subscr, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode obj, ObjectInternal objvalue - ) { - exists(ControlFlowNode index | subscriptObjectAndIndex(subscr, context, obj, objvalue, index) | - objvalue.subscriptUnknown() and - value = ObjectInternal::unknown() - or - exists(int n | - PointsToInternal::pointsTo(index, context, TInt(n), _) and - value = objvalue.(SequenceObjectInternal).getItem(n) - ) - ) and - origin = subscr - } + pragma[noinline] + predicate subscriptPointsTo( + SubscriptNode subscr, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode obj, ObjectInternal objvalue + ) { + exists(ControlFlowNode index | subscriptObjectAndIndex(subscr, context, obj, objvalue, index) | + objvalue.subscriptUnknown() and + value = ObjectInternal::unknown() + or + exists(int n | + PointsToInternal::pointsTo(index, context, TInt(n), _) and + value = objvalue.(SequenceObjectInternal).getItem(n) + ) + ) and + origin = subscr + } - predicate subscriptPartsPointsTo( - SubscriptNode subscr, PointsToContext context, ObjectInternal objvalue, - ObjectInternal indexvalue - ) { - exists(ControlFlowNode index | - subscriptObjectAndIndex(subscr, context, _, objvalue, index) and - PointsToInternal::pointsTo(index, context, indexvalue, _) - ) - } + predicate subscriptPartsPointsTo( + SubscriptNode subscr, PointsToContext context, ObjectInternal objvalue, + ObjectInternal indexvalue + ) { + exists(ControlFlowNode index | + subscriptObjectAndIndex(subscr, context, _, objvalue, index) and + PointsToInternal::pointsTo(index, context, indexvalue, _) + ) + } - pragma[noinline] - private predicate subscriptObjectAndIndex( - SubscriptNode subscr, PointsToContext context, ControlFlowNode obj, ObjectInternal objvalue, - ControlFlowNode index - ) { - subscr.isLoad() and - obj = subscr.getObject() and - PointsToInternal::pointsTo(obj, context, objvalue, _) and - index = subscr.getIndex() - } + pragma[noinline] + private predicate subscriptObjectAndIndex( + SubscriptNode subscr, PointsToContext context, ControlFlowNode obj, ObjectInternal objvalue, + ControlFlowNode index + ) { + subscr.isLoad() and + obj = subscr.getObject() and + PointsToInternal::pointsTo(obj, context, objvalue, _) and + index = subscr.getIndex() + } - /** - * Tracking too many binary expressions is likely to kill performance, so just say anything other than addition or bitwise or is 'unknown'. - */ - pragma[noinline] - predicate binaryPointsTo( - BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode operand, ObjectInternal opvalue - ) { - origin = b and - operand = genericBinaryOperand(b) and - PointsToInternal::pointsTo(operand, context, opvalue, _) and - value = ObjectInternal::unknown() - } + /** + * Tracking too many binary expressions is likely to kill performance, so just say anything other than addition or bitwise or is 'unknown'. + */ + pragma[noinline] + predicate binaryPointsTo( + BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode operand, ObjectInternal opvalue + ) { + origin = b and + operand = genericBinaryOperand(b) and + PointsToInternal::pointsTo(operand, context, opvalue, _) and + value = ObjectInternal::unknown() + } - private ControlFlowNode genericBinaryOperand(BinaryExprNode b) { - exists(Operator op | - b.operands(result, op, _) - or - b.operands(_, op, result) - | - not op instanceof BitOr and - not op instanceof Add - ) - } + private ControlFlowNode genericBinaryOperand(BinaryExprNode b) { + exists(Operator op | + b.operands(result, op, _) + or + b.operands(_, op, result) + | + not op instanceof BitOr and + not op instanceof Add + ) + } - pragma[noinline] - predicate addPointsTo( - BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode operand, ObjectInternal opvalue - ) { - origin = b and - exists(Operator op | - b.operands(operand, op, _) - or - b.operands(_, op, operand) - | - op instanceof Add and - PointsToInternal::pointsTo(operand, context, opvalue, _) and - value = TUnknownInstance(opvalue.getClass()) - ) - } + pragma[noinline] + predicate addPointsTo( + BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode operand, ObjectInternal opvalue + ) { + origin = b and + exists(Operator op | + b.operands(operand, op, _) + or + b.operands(_, op, operand) + | + op instanceof Add and + PointsToInternal::pointsTo(operand, context, opvalue, _) and + value = TUnknownInstance(opvalue.getClass()) + ) + } - pragma[noinline] - predicate bitOrPointsTo( - BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode operand, ObjectInternal opvalue - ) { - origin = b and - exists(Operator op, ControlFlowNode other | - b.operands(operand, op, other) - or - b.operands(other, op, operand) - | - op instanceof BitOr and - exists(ObjectInternal obj, int i1, int i2 | - pointsToInt(operand, context, opvalue, i1) and - pointsToInt(other, context, obj, i2) and - value = TInt(i1.bitOr(i2)) - ) - ) - } + pragma[noinline] + predicate bitOrPointsTo( + BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode operand, ObjectInternal opvalue + ) { + origin = b and + exists(Operator op, ControlFlowNode other | + b.operands(operand, op, other) + or + b.operands(other, op, operand) + | + op instanceof BitOr and + exists(ObjectInternal obj, int i1, int i2 | + pointsToInt(operand, context, opvalue, i1) and + pointsToInt(other, context, obj, i2) and + value = TInt(i1.bitOr(i2)) + ) + ) + } - predicate pointsToInt(ControlFlowNode n, PointsToContext context, ObjectInternal obj, int value) { - PointsToInternal::pointsTo(n, context, obj, _) and - value = obj.intValue() - } + predicate pointsToInt(ControlFlowNode n, PointsToContext context, ObjectInternal obj, int value) { + PointsToInternal::pointsTo(n, context, obj, _) and + value = obj.intValue() + } - pragma[noinline] - predicate unaryPointsTo( - UnaryExprNode u, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode operand, ObjectInternal opvalue - ) { - exists(Unaryop op | - op = u.getNode().getOp() and - operand = u.getOperand() and - PointsToInternal::pointsTo(operand, context, opvalue, _) - | - op instanceof Not and value = ObjectInternal::bool(opvalue.booleanValue().booleanNot()) - or - op instanceof USub and value = ObjectInternal::fromInt(-opvalue.intValue()) - or - not op instanceof Not and opvalue = ObjectInternal::unknown() and value = opvalue - ) and - origin = u - } + pragma[noinline] + predicate unaryPointsTo( + UnaryExprNode u, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode operand, ObjectInternal opvalue + ) { + exists(Unaryop op | + op = u.getNode().getOp() and + operand = u.getOperand() and + PointsToInternal::pointsTo(operand, context, opvalue, _) + | + op instanceof Not and value = ObjectInternal::bool(opvalue.booleanValue().booleanNot()) + or + op instanceof USub and value = ObjectInternal::fromInt(-opvalue.intValue()) + or + not op instanceof Not and opvalue = ObjectInternal::unknown() and value = opvalue + ) and + origin = u + } - pragma[noinline] - predicate builtinCallPointsTo( - CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode arg, ObjectInternal argvalue - ) { - PointsToInternal::pointsTo(arg, context, argvalue, _) and - arg = call.getArg(0) and - exists(BuiltinFunctionObjectInternal callable | - PointsToInternal::pointsTo(call.getFunction(), context, callable, _) - | - callable != ObjectInternal::builtin("len") and - callable != ObjectInternal::builtin("callable") and - callable != ObjectInternal::builtin("isinstance") and - callable != ObjectInternal::builtin("issubclass") and - callable != ObjectInternal::builtin("hasattr") and - callable.isClass() = false and - value = ObjectInternal::unknown() - ) and - origin = call - } + pragma[noinline] + predicate builtinCallPointsTo( + CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode arg, ObjectInternal argvalue + ) { + PointsToInternal::pointsTo(arg, context, argvalue, _) and + arg = call.getArg(0) and + exists(BuiltinFunctionObjectInternal callable | + PointsToInternal::pointsTo(call.getFunction(), context, callable, _) + | + callable != ObjectInternal::builtin("len") and + callable != ObjectInternal::builtin("callable") and + callable != ObjectInternal::builtin("isinstance") and + callable != ObjectInternal::builtin("issubclass") and + callable != ObjectInternal::builtin("hasattr") and + callable.isClass() = false and + value = ObjectInternal::unknown() + ) and + origin = call + } - pragma[noinline] - predicate typeCallPointsTo( - CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode arg, ObjectInternal argvalue - ) { - type_call1(call, arg, context, argvalue) and - value = argvalue.getClass() and - origin = CfgOrigin::fromObject(value).asCfgNodeOrHere(call) - } + pragma[noinline] + predicate typeCallPointsTo( + CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode arg, ObjectInternal argvalue + ) { + type_call1(call, arg, context, argvalue) and + value = argvalue.getClass() and + origin = CfgOrigin::fromObject(value).asCfgNodeOrHere(call) + } - pragma[noinline] - private predicate lenCallPointsTo( - CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode arg, ObjectInternal argvalue - ) { - len_call(call, arg, context, argvalue) and - origin = call and - exists(int len | len = argvalue.length() | - value = TInt(len) and len >= 0 - or - len < 0 and value = TUnknownInstance(ObjectInternal::builtin("int")) - ) - } + pragma[noinline] + private predicate lenCallPointsTo( + CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode arg, ObjectInternal argvalue + ) { + len_call(call, arg, context, argvalue) and + origin = call and + exists(int len | len = argvalue.length() | + value = TInt(len) and len >= 0 + or + len < 0 and value = TUnknownInstance(ObjectInternal::builtin("int")) + ) + } - pragma[noinline] - private predicate getattrPointsTo( - CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode arg, ObjectInternal argvalue - ) { - exists(string name | getattr_call(call, arg, context, argvalue, name) | - argvalue.attributesUnknown() and value = ObjectInternal::unknown() and origin = call - or - exists(CfgOrigin valOrigin | - argvalue.attribute(name, value, valOrigin) and origin = valOrigin.asCfgNodeOrHere(call) - ) - ) - } + pragma[noinline] + private predicate getattrPointsTo( + CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode arg, ObjectInternal argvalue + ) { + exists(string name | getattr_call(call, arg, context, argvalue, name) | + argvalue.attributesUnknown() and value = ObjectInternal::unknown() and origin = call + or + exists(CfgOrigin valOrigin | + argvalue.attribute(name, value, valOrigin) and origin = valOrigin.asCfgNodeOrHere(call) + ) + ) + } - pragma[noinline] - predicate getattr_call( - CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, string name - ) { - exists(ControlFlowNode arg1 | - call_and_args_for_getattr(call, context, use, arg1) and - PointsToInternal::pointsTo(use, context, val, _) and - PointsToInternal::pointsToString(arg1, context, name) - ) - } + pragma[noinline] + predicate getattr_call( + CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, string name + ) { + exists(ControlFlowNode arg1 | + call_and_args_for_getattr(call, context, use, arg1) and + PointsToInternal::pointsTo(use, context, val, _) and + PointsToInternal::pointsToString(arg1, context, name) + ) + } - pragma[noinline] - private predicate call_and_args_for_getattr( - ControlFlowNode call, PointsToContext context, ControlFlowNode arg0, ControlFlowNode arg1 - ) { - exists(ControlFlowNode func | - call2(call, func, arg0, arg1) and - PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("getattr"), _) - ) - } + pragma[noinline] + private predicate call_and_args_for_getattr( + ControlFlowNode call, PointsToContext context, ControlFlowNode arg0, ControlFlowNode arg1 + ) { + exists(ControlFlowNode func | + call2(call, func, arg0, arg1) and + PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("getattr"), _) + ) + } - pragma[noinline] - predicate setattr_call( - CallNode call, PointsToContext context, ControlFlowNode obj, string name, ObjectInternal val, - ControlFlowNode origin - ) { - exists(ControlFlowNode arg1, ControlFlowNode arg2 | - call_and_args_for_setattr(call, context, obj, arg1, arg2) and - PointsToInternal::pointsTo(arg2, context, val, origin) and - PointsToInternal::pointsToString(arg1, context, name) - ) - } + pragma[noinline] + predicate setattr_call( + CallNode call, PointsToContext context, ControlFlowNode obj, string name, ObjectInternal val, + ControlFlowNode origin + ) { + exists(ControlFlowNode arg1, ControlFlowNode arg2 | + call_and_args_for_setattr(call, context, obj, arg1, arg2) and + PointsToInternal::pointsTo(arg2, context, val, origin) and + PointsToInternal::pointsToString(arg1, context, name) + ) + } - pragma[noinline] - private predicate call_and_args_for_setattr( - ControlFlowNode call, PointsToContext context, ControlFlowNode arg0, ControlFlowNode arg1, - ControlFlowNode arg2 - ) { - exists(ControlFlowNode func | - call3(call, func, arg0, arg1, arg2) and - PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("setattr"), _) - ) - } + pragma[noinline] + private predicate call_and_args_for_setattr( + ControlFlowNode call, PointsToContext context, ControlFlowNode arg0, ControlFlowNode arg1, + ControlFlowNode arg2 + ) { + exists(ControlFlowNode func | + call3(call, func, arg0, arg1, arg2) and + PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("setattr"), _) + ) + } - pragma[noinline] - private boolean containsComparisonEvaluatesTo( - CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue - ) { - exists(Cmpop op | - comp.operands(operand, op, _) or - comp.operands(_, op, operand) - | - (op instanceof In or op instanceof NotIn) and - PointsToInternal::pointsTo(operand, context, opvalue, _) - ) and - result = maybe() - } + pragma[noinline] + private boolean containsComparisonEvaluatesTo( + CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue + ) { + exists(Cmpop op | + comp.operands(operand, op, _) or + comp.operands(_, op, operand) + | + (op instanceof In or op instanceof NotIn) and + PointsToInternal::pointsTo(operand, context, opvalue, _) + ) and + result = maybe() + } - pragma[noinline] - private boolean equalityEvaluatesTo( - CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue - ) { - exists(ObjectInternal other, boolean sense | - equalityTest(comp, context, operand, opvalue, other, sense) - | - other = opvalue and result = sense - or - other != opvalue and result = sense.booleanNot() - or - opvalue.notTestableForEquality() and result = maybe() - or - other.notTestableForEquality() and result = maybe() - ) - } + pragma[noinline] + private boolean equalityEvaluatesTo( + CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue + ) { + exists(ObjectInternal other, boolean sense | + equalityTest(comp, context, operand, opvalue, other, sense) + | + other = opvalue and result = sense + or + other != opvalue and result = sense.booleanNot() + or + opvalue.notTestableForEquality() and result = maybe() + or + other.notTestableForEquality() and result = maybe() + ) + } - pragma[noinline] - private boolean comparesToUnknown( - CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue - ) { - (comp.operands(operand, _, _) or comp.operands(_, _, operand)) and - PointsToInternal::pointsTo(operand, context, opvalue, _) and - opvalue = ObjectInternal::unknown() and - result = maybe() - } + pragma[noinline] + private boolean comparesToUnknown( + CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue + ) { + (comp.operands(operand, _, _) or comp.operands(_, _, operand)) and + PointsToInternal::pointsTo(operand, context, opvalue, _) and + opvalue = ObjectInternal::unknown() and + result = maybe() + } - pragma[noinline] - private predicate equalityTest( - CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue, - ObjectInternal other, boolean sense - ) { - exists(ControlFlowNode r | - equality_test(comp, operand, sense, r) and - PointsToInternal::pointsTo(operand, context, opvalue, _) and - PointsToInternal::pointsTo(r, context, other, _) - ) - } + pragma[noinline] + private predicate equalityTest( + CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue, + ObjectInternal other, boolean sense + ) { + exists(ControlFlowNode r | + equality_test(comp, operand, sense, r) and + PointsToInternal::pointsTo(operand, context, opvalue, _) and + PointsToInternal::pointsTo(r, context, other, _) + ) + } - pragma[noinline] - private boolean inequalityEvaluatesTo( - CompareNode comp, PointsToContext context, ControlFlowNode use, ObjectInternal val - ) { - exists(boolean strict, boolean sense, ObjectInternal other | - inequalityTest(comp, context, use, val, other, strict, sense) - | - compare(val, other) = -1 and result = sense - or - compare(val, other) = 0 and result = strict.booleanNot() - or - compare(val, other) = 1 and result = sense.booleanNot() - or - val.notTestableForEquality() and result = maybe() - or - other.notTestableForEquality() and result = maybe() - ) - } + pragma[noinline] + private boolean inequalityEvaluatesTo( + CompareNode comp, PointsToContext context, ControlFlowNode use, ObjectInternal val + ) { + exists(boolean strict, boolean sense, ObjectInternal other | + inequalityTest(comp, context, use, val, other, strict, sense) + | + compare(val, other) = -1 and result = sense + or + compare(val, other) = 0 and result = strict.booleanNot() + or + compare(val, other) = 1 and result = sense.booleanNot() + or + val.notTestableForEquality() and result = maybe() + or + other.notTestableForEquality() and result = maybe() + ) + } - private int compare(ObjectInternal val, ObjectInternal other) { - inequalityTest(_, _, _, val, other, _, _) and - result = compare_unbound(val, other) - or - result = compare_sequence(val, other, 0) - } + private int compare(ObjectInternal val, ObjectInternal other) { + inequalityTest(_, _, _, val, other, _, _) and + result = compare_unbound(val, other) + or + result = compare_sequence(val, other, 0) + } - bindingset[val, other] - private int compare_unbound(ObjectInternal val, ObjectInternal other) { - val.intValue() < other.intValue() and result = -1 - or - val.intValue() > other.intValue() and result = 1 - or - val.intValue() = other.intValue() and result = 0 - or - val.strValue() < other.strValue() and result = -1 - or - val.strValue() > other.strValue() and result = 1 - or - val.strValue() = other.strValue() and result = 0 - } + bindingset[val, other] + private int compare_unbound(ObjectInternal val, ObjectInternal other) { + val.intValue() < other.intValue() and result = -1 + or + val.intValue() > other.intValue() and result = 1 + or + val.intValue() = other.intValue() and result = 0 + or + val.strValue() < other.strValue() and result = -1 + or + val.strValue() > other.strValue() and result = 1 + or + val.strValue() = other.strValue() and result = 0 + } - pragma[nomagic] - private int compare_sequence(SequenceObjectInternal val, SequenceObjectInternal other, int n) { - exists(int vlen, int olen | sequence_lengths_in_comparison(val, other, vlen, olen) | - n = vlen and olen > n and result = -1 - or - n = olen and vlen > n and result = 1 - or - n = olen and n = vlen and result = 0 - ) - or - result != 0 and result = compare_item(val, other, n) - or - compare_item(val, other, n) = 0 and result = compare_sequence(val, other, n + 1) - } + pragma[nomagic] + private int compare_sequence(SequenceObjectInternal val, SequenceObjectInternal other, int n) { + exists(int vlen, int olen | sequence_lengths_in_comparison(val, other, vlen, olen) | + n = vlen and olen > n and result = -1 + or + n = olen and vlen > n and result = 1 + or + n = olen and n = vlen and result = 0 + ) + or + result != 0 and result = compare_item(val, other, n) + or + compare_item(val, other, n) = 0 and result = compare_sequence(val, other, n + 1) + } - private predicate sequence_lengths_in_comparison( - SequenceObjectInternal val, SequenceObjectInternal other, int vlen, int olen - ) { - inequalityTest(_, _, _, val, other, _, _) and - vlen = val.length() and - olen = other.length() - } + private predicate sequence_lengths_in_comparison( + SequenceObjectInternal val, SequenceObjectInternal other, int vlen, int olen + ) { + inequalityTest(_, _, _, val, other, _, _) and + vlen = val.length() and + olen = other.length() + } - pragma[noinline] - private int compare_item(SequenceObjectInternal val, SequenceObjectInternal other, int n) { - inequalityTest(_, _, _, val, other, _, _) and - result = compare_unbound(val.getItem(n), other.getItem(n)) - } + pragma[noinline] + private int compare_item(SequenceObjectInternal val, SequenceObjectInternal other, int n) { + inequalityTest(_, _, _, val, other, _, _) and + result = compare_unbound(val.getItem(n), other.getItem(n)) + } - pragma[noinline] - private predicate inequalityTest( - CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue, - ObjectInternal other, boolean strict, boolean sense - ) { - exists(ControlFlowNode r | - inequality(comp, operand, r, strict) and sense = true - or - inequality(comp, r, operand, strict) and sense = false - | - PointsToInternal::pointsTo(operand, context, opvalue, _) and - PointsToInternal::pointsTo(r, context, other, _) - ) - } + pragma[noinline] + private predicate inequalityTest( + CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue, + ObjectInternal other, boolean strict, boolean sense + ) { + exists(ControlFlowNode r | + inequality(comp, operand, r, strict) and sense = true + or + inequality(comp, r, operand, strict) and sense = false + | + PointsToInternal::pointsTo(operand, context, opvalue, _) and + PointsToInternal::pointsTo(r, context, other, _) + ) + } - /** Helper for comparisons. */ - pragma[noinline] - private predicate inequality( - CompareNode cmp, ControlFlowNode lesser, ControlFlowNode greater, boolean strict - ) { - exists(Cmpop op | - cmp.operands(lesser, op, greater) and op.getSymbol() = "<" and strict = true - or - cmp.operands(lesser, op, greater) and op.getSymbol() = "<=" and strict = false - or - cmp.operands(greater, op, lesser) and op.getSymbol() = ">" and strict = true - or - cmp.operands(greater, op, lesser) and op.getSymbol() = ">=" and strict = false - ) - } + /** Helper for comparisons. */ + pragma[noinline] + private predicate inequality( + CompareNode cmp, ControlFlowNode lesser, ControlFlowNode greater, boolean strict + ) { + exists(Cmpop op | + cmp.operands(lesser, op, greater) and op.getSymbol() = "<" and strict = true + or + cmp.operands(lesser, op, greater) and op.getSymbol() = "<=" and strict = false + or + cmp.operands(greater, op, lesser) and op.getSymbol() = ">" and strict = true + or + cmp.operands(greater, op, lesser) and op.getSymbol() = ">=" and strict = false + ) + } - predicate pointsTo( - ControlFlowNode expr, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode subexpr, ObjectInternal subvalue - ) { - attributePointsTo(expr, context, value, origin, subexpr, subvalue) - or - subscriptPointsTo(expr, context, value, origin, subexpr, subvalue) - or - addPointsTo(expr, context, value, origin, subexpr, subvalue) - or - bitOrPointsTo(expr, context, value, origin, subexpr, subvalue) - or - binaryPointsTo(expr, context, value, origin, subexpr, subvalue) - or - unaryPointsTo(expr, context, value, origin, subexpr, subvalue) - or - builtinCallPointsTo(expr, context, value, origin, subexpr, subvalue) - or - lenCallPointsTo(expr, context, value, origin, subexpr, subvalue) - or - typeCallPointsTo(expr, context, value, origin, subexpr, subvalue) - or - getattrPointsTo(expr, context, value, origin, subexpr, subvalue) - or - value = ObjectInternal::bool(evaluatesTo(expr, context, subexpr, subvalue)) and origin = expr - } + predicate pointsTo( + ControlFlowNode expr, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode subexpr, ObjectInternal subvalue + ) { + attributePointsTo(expr, context, value, origin, subexpr, subvalue) + or + subscriptPointsTo(expr, context, value, origin, subexpr, subvalue) + or + addPointsTo(expr, context, value, origin, subexpr, subvalue) + or + bitOrPointsTo(expr, context, value, origin, subexpr, subvalue) + or + binaryPointsTo(expr, context, value, origin, subexpr, subvalue) + or + unaryPointsTo(expr, context, value, origin, subexpr, subvalue) + or + builtinCallPointsTo(expr, context, value, origin, subexpr, subvalue) + or + lenCallPointsTo(expr, context, value, origin, subexpr, subvalue) + or + typeCallPointsTo(expr, context, value, origin, subexpr, subvalue) + or + getattrPointsTo(expr, context, value, origin, subexpr, subvalue) + or + value = ObjectInternal::bool(evaluatesTo(expr, context, subexpr, subvalue)) and origin = expr + } - pragma[noinline] - boolean evaluatesTo( - ControlFlowNode expr, PointsToContext context, ControlFlowNode subexpr, ObjectInternal subvalue - ) { - result = equalityEvaluatesTo(expr, context, subexpr, subvalue) - or - result = inequalityEvaluatesTo(expr, context, subexpr, subvalue) - or - result = containsComparisonEvaluatesTo(expr, context, subexpr, subvalue) - or - result = comparesToUnknown(expr, context, subexpr, subvalue) - or - result = isinstanceEvaluatesTo(expr, context, subexpr, subvalue) - or - result = issubclassEvaluatesTo(expr, context, subexpr, subvalue) - or - result = callableEvaluatesTo(expr, context, subexpr, subvalue) - or - result = hasattrEvaluatesTo(expr, context, subexpr, subvalue) - } + pragma[noinline] + boolean evaluatesTo( + ControlFlowNode expr, PointsToContext context, ControlFlowNode subexpr, ObjectInternal subvalue + ) { + result = equalityEvaluatesTo(expr, context, subexpr, subvalue) + or + result = inequalityEvaluatesTo(expr, context, subexpr, subvalue) + or + result = containsComparisonEvaluatesTo(expr, context, subexpr, subvalue) + or + result = comparesToUnknown(expr, context, subexpr, subvalue) + or + result = isinstanceEvaluatesTo(expr, context, subexpr, subvalue) + or + result = issubclassEvaluatesTo(expr, context, subexpr, subvalue) + or + result = callableEvaluatesTo(expr, context, subexpr, subvalue) + or + result = hasattrEvaluatesTo(expr, context, subexpr, subvalue) + } - pragma[nomagic] - private boolean isinstanceEvaluatesTo( - CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val - ) { - exists(ObjectInternal cls | isinstance_call(call, use, context, val, cls) | - result = Types::improperSubclass(val.getClass(), cls) - or - val = ObjectInternal::unknown() and result = maybe() - or - cls = ObjectInternal::unknown() and result = maybe() - or - cls = ObjectInternal::unknownClass() and result = maybe() - ) - } + pragma[nomagic] + private boolean isinstanceEvaluatesTo( + CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val + ) { + exists(ObjectInternal cls | isinstance_call(call, use, context, val, cls) | + result = Types::improperSubclass(val.getClass(), cls) + or + val = ObjectInternal::unknown() and result = maybe() + or + cls = ObjectInternal::unknown() and result = maybe() + or + cls = ObjectInternal::unknownClass() and result = maybe() + ) + } - private predicate isinstance_call( - CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, - ObjectInternal cls - ) { - exists(ControlFlowNode func, ControlFlowNode arg1 | - call2(call, func, use, arg1) and - points_to_isinstance(func, context) and - PointsToInternal::pointsTo(use, context, val, _) and - PointsToInternal::pointsTo(arg1, context, cls, _) - ) - } + private predicate isinstance_call( + CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, + ObjectInternal cls + ) { + exists(ControlFlowNode func, ControlFlowNode arg1 | + call2(call, func, use, arg1) and + points_to_isinstance(func, context) and + PointsToInternal::pointsTo(use, context, val, _) and + PointsToInternal::pointsTo(arg1, context, cls, _) + ) + } - private predicate issubclass_call( - CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, - ObjectInternal cls - ) { - exists(ControlFlowNode func, ControlFlowNode arg1 | - call2(call, func, use, arg1) and - points_to_issubclass(func, context) and - PointsToInternal::pointsTo(use, context, val, _) and - PointsToInternal::pointsTo(arg1, context, cls, _) - ) - } + private predicate issubclass_call( + CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, + ObjectInternal cls + ) { + exists(ControlFlowNode func, ControlFlowNode arg1 | + call2(call, func, use, arg1) and + points_to_issubclass(func, context) and + PointsToInternal::pointsTo(use, context, val, _) and + PointsToInternal::pointsTo(arg1, context, cls, _) + ) + } - pragma[noinline] - private predicate points_to_isinstance(ControlFlowNode func, PointsToContext context) { - PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("isinstance"), _) - } + pragma[noinline] + private predicate points_to_isinstance(ControlFlowNode func, PointsToContext context) { + PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("isinstance"), _) + } - pragma[noinline] - private predicate points_to_issubclass(ControlFlowNode func, PointsToContext context) { - PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("issubclass"), _) - } + pragma[noinline] + private predicate points_to_issubclass(ControlFlowNode func, PointsToContext context) { + PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("issubclass"), _) + } - private predicate callable_call( - CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val - ) { - PointsToInternal::pointsTo(call.getFunction(), context, ObjectInternal::builtin("callable"), _) and - use = call.getArg(0) and - PointsToInternal::pointsTo(use, context, val, _) - } + private predicate callable_call( + CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val + ) { + PointsToInternal::pointsTo(call.getFunction(), context, ObjectInternal::builtin("callable"), _) and + use = call.getArg(0) and + PointsToInternal::pointsTo(use, context, val, _) + } - private predicate len_call( - CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val - ) { - PointsToInternal::pointsTo(call.getFunction(), context, ObjectInternal::builtin("len"), _) and - use = call.getArg(0) and - PointsToInternal::pointsTo(use, context, val, _) - } + private predicate len_call( + CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val + ) { + PointsToInternal::pointsTo(call.getFunction(), context, ObjectInternal::builtin("len"), _) and + use = call.getArg(0) and + PointsToInternal::pointsTo(use, context, val, _) + } - private predicate type_call1( - CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val - ) { - PointsToInternal::pointsTo(call.getFunction(), context, ObjectInternal::builtin("type"), _) and - use = call.getArg(0) and - not exists(call.getArg(1)) and - PointsToInternal::pointsTo(use, context, val, _) - } + private predicate type_call1( + CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val + ) { + PointsToInternal::pointsTo(call.getFunction(), context, ObjectInternal::builtin("type"), _) and + use = call.getArg(0) and + not exists(call.getArg(1)) and + PointsToInternal::pointsTo(use, context, val, _) + } - private predicate hasattr_call( - CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, string name - ) { - exists(ControlFlowNode arg1 | - call_to_hasattr(call, context, use, arg1) and - PointsToInternal::pointsTo(use, context, val, _) and - PointsToInternal::pointsToString(arg1, context, name) - ) - } + private predicate hasattr_call( + CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, string name + ) { + exists(ControlFlowNode arg1 | + call_to_hasattr(call, context, use, arg1) and + PointsToInternal::pointsTo(use, context, val, _) and + PointsToInternal::pointsToString(arg1, context, name) + ) + } - pragma[noinline] - private predicate call_to_hasattr( - ControlFlowNode call, PointsToContext context, ControlFlowNode arg0, ControlFlowNode arg1 - ) { - exists(ControlFlowNode func | - call2(call, func, arg0, arg1) and - PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("hasattr"), _) - ) - } + pragma[noinline] + private predicate call_to_hasattr( + ControlFlowNode call, PointsToContext context, ControlFlowNode arg0, ControlFlowNode arg1 + ) { + exists(ControlFlowNode func | + call2(call, func, arg0, arg1) and + PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("hasattr"), _) + ) + } - pragma[nomagic] - private boolean issubclassEvaluatesTo( - CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val - ) { - exists(ObjectInternal cls | issubclass_call(call, use, context, val, cls) | - result = Types::improperSubclass(val, cls) - or - val = ObjectInternal::unknownClass() and result = maybe() - or - val = ObjectInternal::unknown() and result = maybe() - or - cls = ObjectInternal::unknown() and result = maybe() - or - cls = ObjectInternal::unknownClass() and result = maybe() - ) - } + pragma[nomagic] + private boolean issubclassEvaluatesTo( + CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val + ) { + exists(ObjectInternal cls | issubclass_call(call, use, context, val, cls) | + result = Types::improperSubclass(val, cls) + or + val = ObjectInternal::unknownClass() and result = maybe() + or + val = ObjectInternal::unknown() and result = maybe() + or + cls = ObjectInternal::unknown() and result = maybe() + or + cls = ObjectInternal::unknownClass() and result = maybe() + ) + } - pragma[noinline] - private boolean callableEvaluatesTo( - CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val - ) { - callable_call(call, use, context, val) and - ( - val = ObjectInternal::unknown() and result = maybe() - or - val = ObjectInternal::unknownClass() and result = maybe() - or - result = Types::hasAttr(val.getClass(), "__call__") - ) - } + pragma[noinline] + private boolean callableEvaluatesTo( + CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val + ) { + callable_call(call, use, context, val) and + ( + val = ObjectInternal::unknown() and result = maybe() + or + val = ObjectInternal::unknownClass() and result = maybe() + or + result = Types::hasAttr(val.getClass(), "__call__") + ) + } - pragma[noinline] - private boolean hasattrEvaluatesTo( - CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val - ) { - exists(string name | hasattr_call(call, use, context, val, name) | - val = ObjectInternal::unknown() and result = maybe() - or - val = ObjectInternal::unknownClass() and result = maybe() - or - result = Types::hasAttr(val.getClass(), name) - ) - } + pragma[noinline] + private boolean hasattrEvaluatesTo( + CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val + ) { + exists(string name | hasattr_call(call, use, context, val, name) | + val = ObjectInternal::unknown() and result = maybe() + or + val = ObjectInternal::unknownClass() and result = maybe() + or + result = Types::hasAttr(val.getClass(), name) + ) + } - predicate requireSubClass(ObjectInternal sub, ObjectInternal sup) { - sup != ObjectInternal::unknownClass() and - sub != ObjectInternal::unknownClass() and - exists(ObjectInternal sup_or_tuple | - issubclass_call(_, _, _, sub, sup_or_tuple) and sub.isClass() = true - or - exists(ObjectInternal val | - isinstance_call(_, _, _, val, sup_or_tuple) and - sub = val.getClass() - ) - | - sup = sup_or_tuple - or - sup = sup_or_tuple.(TupleObjectInternal).getItem(_) - ) - } + predicate requireSubClass(ObjectInternal sub, ObjectInternal sup) { + sup != ObjectInternal::unknownClass() and + sub != ObjectInternal::unknownClass() and + exists(ObjectInternal sup_or_tuple | + issubclass_call(_, _, _, sub, sup_or_tuple) and sub.isClass() = true + or + exists(ObjectInternal val | + isinstance_call(_, _, _, val, sup_or_tuple) and + sub = val.getClass() + ) + | + sup = sup_or_tuple + or + sup = sup_or_tuple.(TupleObjectInternal).getItem(_) + ) + } - predicate requireHasAttr(ClassObjectInternal cls, string name) { - cls != ObjectInternal::unknownClass() and - exists(ObjectInternal val | val.getClass() = cls | - name = "__call__" and callable_call(_, _, _, val) - or - hasattr_call(_, _, _, val, name) - ) - } + predicate requireHasAttr(ClassObjectInternal cls, string name) { + cls != ObjectInternal::unknownClass() and + exists(ObjectInternal val | val.getClass() = cls | + name = "__call__" and callable_call(_, _, _, val) + or + hasattr_call(_, _, _, val, name) + ) + } } module Conditionals { - boolean testEvaluates( - ControlFlowNode expr, ControlFlowNode use, PointsToContext context, ObjectInternal value, - ControlFlowNode origin - ) { - pinode_test(expr, use) and - result = evaluates(expr, use, context, value, origin).booleanValue() - } + boolean testEvaluates( + ControlFlowNode expr, ControlFlowNode use, PointsToContext context, ObjectInternal value, + ControlFlowNode origin + ) { + pinode_test(expr, use) and + result = evaluates(expr, use, context, value, origin).booleanValue() + } - pragma[noinline] - ObjectInternal evaluates( - ControlFlowNode expr, ControlFlowNode use, PointsToContext context, ObjectInternal val, - ControlFlowNode origin - ) { - PointsToInternal::pointsTo(use, context, val, origin) and - pinode_test(_, use) and - expr = use and - result = val - or - exists(ControlFlowNode part, ObjectInternal partval | - pinode_test_part(expr, part) and - partval = evaluates(part, use, context, val, origin) and - Expressions::pointsTo(expr, context, result, _, part, partval) - ) - } + pragma[noinline] + ObjectInternal evaluates( + ControlFlowNode expr, ControlFlowNode use, PointsToContext context, ObjectInternal val, + ControlFlowNode origin + ) { + PointsToInternal::pointsTo(use, context, val, origin) and + pinode_test(_, use) and + expr = use and + result = val + or + exists(ControlFlowNode part, ObjectInternal partval | + pinode_test_part(expr, part) and + partval = evaluates(part, use, context, val, origin) and + Expressions::pointsTo(expr, context, result, _, part, partval) + ) + } - private predicate pinode_test(ControlFlowNode test, NameNode use) { - exists(PyEdgeRefinement pi | - pi.getInput().getASourceUse() = use and - pi.getTest() = test and - test.getAChild*() = use - ) - or - any(SingleSuccessorGuard ssg).useAndTest(use, test) - } + private predicate pinode_test(ControlFlowNode test, NameNode use) { + exists(PyEdgeRefinement pi | + pi.getInput().getASourceUse() = use and + pi.getTest() = test and + test.getAChild*() = use + ) + or + any(SingleSuccessorGuard ssg).useAndTest(use, test) + } - private predicate pinode_test_part(ControlFlowNode outer, ControlFlowNode inner) { - exists(ControlFlowNode test, NameNode use | - pinode_test(test, use) and - test.getAChild*() = outer and - outer.getAChild+() = inner and - inner.getAChild*() = use - ) - } + private predicate pinode_test_part(ControlFlowNode outer, ControlFlowNode inner) { + exists(ControlFlowNode test, NameNode use | + pinode_test(test, use) and + test.getAChild*() = outer and + outer.getAChild+() = inner and + inner.getAChild*() = use + ) + } } cached module Types { - cached - int base_count(ClassObjectInternal cls) { - cls = ObjectInternal::builtin("object") and result = 0 - or - exists(cls.getBuiltin()) and cls != ObjectInternal::builtin("object") and result = 1 - or - exists(Class pycls | pycls = cls.(PythonClassObjectInternal).getScope() | - result = strictcount(pycls.getABase()) - or - isNewStyle(cls) and not exists(pycls.getABase()) and result = 1 - or - isOldStyle(cls) and not exists(pycls.getABase()) and result = 0 - ) - } + cached + int base_count(ClassObjectInternal cls) { + cls = ObjectInternal::builtin("object") and result = 0 + or + exists(cls.getBuiltin()) and cls != ObjectInternal::builtin("object") and result = 1 + or + exists(Class pycls | pycls = cls.(PythonClassObjectInternal).getScope() | + result = strictcount(pycls.getABase()) + or + isNewStyle(cls) and not exists(pycls.getABase()) and result = 1 + or + isOldStyle(cls) and not exists(pycls.getABase()) and result = 0 + ) + } - cached - ObjectInternal getBase(ClassObjectInternal cls, int n) { - result.getBuiltin() = cls.getBuiltin().getBaseClass() and n = 0 + cached + ObjectInternal getBase(ClassObjectInternal cls, int n) { + result.getBuiltin() = cls.getBuiltin().getBaseClass() and n = 0 + or + exists(Class pycls | pycls = cls.(PythonClassObjectInternal).getScope() | + exists(ObjectInternal base | + PointsToInternal::pointsTo(pycls.getBase(n).getAFlowNode(), _, base, _) + | + result = base and base != ObjectInternal::unknown() or - exists(Class pycls | pycls = cls.(PythonClassObjectInternal).getScope() | - exists(ObjectInternal base | - PointsToInternal::pointsTo(pycls.getBase(n).getAFlowNode(), _, base, _) - | - result = base and base != ObjectInternal::unknown() - or - base = ObjectInternal::unknown() and result = ObjectInternal::unknownClass() - ) - or - not exists(pycls.getABase()) and - n = 0 and - isNewStyle(cls) and - result = ObjectInternal::builtin("object") - ) - or - cls = ObjectInternal::unknownClass() and - n = 0 and - result = ObjectInternal::builtin("object") - } + base = ObjectInternal::unknown() and result = ObjectInternal::unknownClass() + ) + or + not exists(pycls.getABase()) and + n = 0 and + isNewStyle(cls) and + result = ObjectInternal::builtin("object") + ) + or + cls = ObjectInternal::unknownClass() and + n = 0 and + result = ObjectInternal::builtin("object") + } - cached - predicate isOldStyle(ClassObjectInternal cls) { newStylePython2(cls, 0) = false } + cached + predicate isOldStyle(ClassObjectInternal cls) { newStylePython2(cls, 0) = false } - cached - predicate isNewStyle(ClassObjectInternal cls) { - major_version() = 3 - or - cls.isBuiltin() - or - newStylePython2(cls, 0) = true - } + cached + predicate isNewStyle(ClassObjectInternal cls) { + major_version() = 3 + or + cls.isBuiltin() + or + newStylePython2(cls, 0) = true + } - private boolean newStylePython2(ClassObjectInternal cls, int n) { - major_version() = 2 and - ( - hasDeclaredMetaclass(cls) = false and - exists(Class pycls | - pycls = cls.getClassDeclaration().getClass() and - n = count(pycls.getABase()) and - result = false - ) - or - exists(ClassObjectInternal base | base = getBase(cls, n) | - hasDeclaredMetaclass(cls) = false and - isOldStyle(base) and - result = newStylePython2(cls, n + 1) - or - isNewStyle(base) and result = true - ) - or - getMro(declaredMetaClass(cls)).contains(ObjectInternal::type()) and - n = 0 and - result = true - ) - } + private boolean newStylePython2(ClassObjectInternal cls, int n) { + major_version() = 2 and + ( + hasDeclaredMetaclass(cls) = false and + exists(Class pycls | + pycls = cls.getClassDeclaration().getClass() and + n = count(pycls.getABase()) and + result = false + ) + or + exists(ClassObjectInternal base | base = getBase(cls, n) | + hasDeclaredMetaclass(cls) = false and + isOldStyle(base) and + result = newStylePython2(cls, n + 1) + or + isNewStyle(base) and result = true + ) + or + getMro(declaredMetaClass(cls)).contains(ObjectInternal::type()) and + n = 0 and + result = true + ) + } - cached - ClassList getMro(ClassObjectInternal cls) { - isNewStyle(cls) and - result = Mro::newStyleMro(cls) - or - isOldStyle(cls) and - result = Mro::oldStyleMro(cls) - } + cached + ClassList getMro(ClassObjectInternal cls) { + isNewStyle(cls) and + result = Mro::newStyleMro(cls) + or + isOldStyle(cls) and + result = Mro::oldStyleMro(cls) + } - cached - predicate declaredAttribute( - ClassObjectInternal cls, string name, ObjectInternal value, CfgOrigin origin - ) { - value = ObjectInternal::fromBuiltin(cls.getBuiltin().getMember(name)) and - origin = CfgOrigin::unknown() - or - value != ObjectInternal::undefined() and - exists(EssaVariable var | - name = var.getName() and - var.getAUse() = cls.(PythonClassObjectInternal).getScope().getANormalExit() and - PointsToInternal::variablePointsTo(var, _, value, origin) - ) - } + cached + predicate declaredAttribute( + ClassObjectInternal cls, string name, ObjectInternal value, CfgOrigin origin + ) { + value = ObjectInternal::fromBuiltin(cls.getBuiltin().getMember(name)) and + origin = CfgOrigin::unknown() + or + value != ObjectInternal::undefined() and + exists(EssaVariable var | + name = var.getName() and + var.getAUse() = cls.(PythonClassObjectInternal).getScope().getANormalExit() and + PointsToInternal::variablePointsTo(var, _, value, origin) + ) + } - cached - ClassObjectInternal getMetaClass(PythonClassObjectInternal cls) { - result = declaredMetaClass(cls) - or - hasDeclaredMetaclass(cls) = false and result = getInheritedMetaclass(cls) - } + cached + ClassObjectInternal getMetaClass(PythonClassObjectInternal cls) { + result = declaredMetaClass(cls) + or + hasDeclaredMetaclass(cls) = false and result = getInheritedMetaclass(cls) + } - private ClassObjectInternal declaredMetaClass(PythonClassObjectInternal cls) { - exists(ObjectInternal obj | - PointsToInternal::variablePointsTo(metaclass_var(cls.getScope()), _, obj, _) - | - result = obj - or - obj = ObjectInternal::unknown() and result = ObjectInternal::unknownClass() - ) - or - exists(ControlFlowNode meta | - six_add_metaclass(_, _, cls, meta) and - PointsToInternal::pointsTo(meta, _, result, _) - ) - } + private ClassObjectInternal declaredMetaClass(PythonClassObjectInternal cls) { + exists(ObjectInternal obj | + PointsToInternal::variablePointsTo(metaclass_var(cls.getScope()), _, obj, _) + | + result = obj + or + obj = ObjectInternal::unknown() and result = ObjectInternal::unknownClass() + ) + or + exists(ControlFlowNode meta | + six_add_metaclass(_, _, cls, meta) and + PointsToInternal::pointsTo(meta, _, result, _) + ) + } - private boolean hasDeclaredMetaclass(PythonClassObjectInternal cls) { - result = has_six_add_metaclass(cls).booleanOr(has_metaclass_var_metaclass(cls)) - } + private boolean hasDeclaredMetaclass(PythonClassObjectInternal cls) { + result = has_six_add_metaclass(cls).booleanOr(has_metaclass_var_metaclass(cls)) + } - private ControlFlowNode decorator_call_callee(PythonClassObjectInternal cls) { - result = cls.getScope().getADecorator().getAFlowNode().(CallNode).getFunction() - } + private ControlFlowNode decorator_call_callee(PythonClassObjectInternal cls) { + result = cls.getScope().getADecorator().getAFlowNode().(CallNode).getFunction() + } - private boolean has_six_add_metaclass(PythonClassObjectInternal cls) { - exists(ControlFlowNode callee, ObjectInternal func | - callee = decorator_call_callee(cls) and - PointsToInternal::pointsTo(callee, _, func, _) - | - func = six_add_metaclass_function() and result = true - or - func != six_add_metaclass_function() and result = false - ) - or - not exists(Module m | m.getName() = "six") and result = false - or - exists(Class pycls | - pycls = cls.getScope() and - not exists(pycls.getADecorator()) and - result = false - ) - } + private boolean has_six_add_metaclass(PythonClassObjectInternal cls) { + exists(ControlFlowNode callee, ObjectInternal func | + callee = decorator_call_callee(cls) and + PointsToInternal::pointsTo(callee, _, func, _) + | + func = six_add_metaclass_function() and result = true + or + func != six_add_metaclass_function() and result = false + ) + or + not exists(Module m | m.getName() = "six") and result = false + or + exists(Class pycls | + pycls = cls.getScope() and + not exists(pycls.getADecorator()) and + result = false + ) + } - private boolean has_metaclass_var_metaclass(PythonClassObjectInternal cls) { - exists(ObjectInternal obj | - PointsToInternal::variablePointsTo(metaclass_var(cls.getScope()), _, obj, _) - | - obj = ObjectInternal::undefined() and result = false - or - obj != ObjectInternal::undefined() and result = true - ) - or - exists(Class pycls | - pycls = cls.getScope() and - not exists(metaclass_var(pycls)) and - result = false - ) - } + private boolean has_metaclass_var_metaclass(PythonClassObjectInternal cls) { + exists(ObjectInternal obj | + PointsToInternal::variablePointsTo(metaclass_var(cls.getScope()), _, obj, _) + | + obj = ObjectInternal::undefined() and result = false + or + obj != ObjectInternal::undefined() and result = true + ) + or + exists(Class pycls | + pycls = cls.getScope() and + not exists(metaclass_var(pycls)) and + result = false + ) + } - private EssaVariable metaclass_var(Class cls) { - result.getASourceUse() = cls.getMetaClass().getAFlowNode() - or - major_version() = 2 and - not exists(cls.getMetaClass()) and - result.getName() = "__metaclass__" and - cls.(ImportTimeScope).entryEdge(result.getAUse(), _) - } + private EssaVariable metaclass_var(Class cls) { + result.getASourceUse() = cls.getMetaClass().getAFlowNode() + or + major_version() = 2 and + not exists(cls.getMetaClass()) and + result.getName() = "__metaclass__" and + cls.(ImportTimeScope).entryEdge(result.getAUse(), _) + } - cached - predicate six_add_metaclass( - CallNode decorator_call, PointsToContext context, ClassObjectInternal decorated, - ControlFlowNode metaclass - ) { - exists(CallNode decorator | - PointsToInternal::pointsTo(decorator_call.getArg(0), context, decorated, _) and - decorator = decorator_call.getFunction() and - decorator.getArg(0) = metaclass - | - PointsToInternal::pointsTo(decorator.getFunction(), context, six_add_metaclass_function(), _) - or - exists(ModuleObjectInternal six | - six.getName() = "six" and - PointsToInternal::pointsTo(decorator.getFunction().(AttrNode).getObject("add_metaclass"), - context, six, _) - ) - ) - } + cached + predicate six_add_metaclass( + CallNode decorator_call, PointsToContext context, ClassObjectInternal decorated, + ControlFlowNode metaclass + ) { + exists(CallNode decorator | + PointsToInternal::pointsTo(decorator_call.getArg(0), context, decorated, _) and + decorator = decorator_call.getFunction() and + decorator.getArg(0) = metaclass + | + PointsToInternal::pointsTo(decorator.getFunction(), context, six_add_metaclass_function(), _) + or + exists(ModuleObjectInternal six | + six.getName() = "six" and + PointsToInternal::pointsTo(decorator.getFunction().(AttrNode).getObject("add_metaclass"), + context, six, _) + ) + ) + } - private ObjectInternal six_add_metaclass_function() { - exists(ModuleObjectInternal six | - six.getName() = "six" and - six.attribute("add_metaclass", result, _) - ) - } + private ObjectInternal six_add_metaclass_function() { + exists(ModuleObjectInternal six | + six.getName() = "six" and + six.attribute("add_metaclass", result, _) + ) + } - pragma[nomagic] - private ClassObjectInternal getInheritedMetaclass(ClassObjectInternal cls) { - result = getInheritedMetaclass(cls, 0) - or - // Best guess if base is not a known class - hasUnknownBase(cls) and result = ObjectInternal::unknownClass() - } + pragma[nomagic] + private ClassObjectInternal getInheritedMetaclass(ClassObjectInternal cls) { + result = getInheritedMetaclass(cls, 0) + or + // Best guess if base is not a known class + hasUnknownBase(cls) and result = ObjectInternal::unknownClass() + } - /* Helper for getInheritedMetaclass */ - private predicate hasUnknownBase(ClassObjectInternal cls) { - exists(ObjectInternal base | base = getBase(cls, _) | - base.isClass() = false - or - base = ObjectInternal::unknownClass() - ) - } + /* Helper for getInheritedMetaclass */ + private predicate hasUnknownBase(ClassObjectInternal cls) { + exists(ObjectInternal base | base = getBase(cls, _) | + base.isClass() = false + or + base = ObjectInternal::unknownClass() + ) + } - private ClassObjectInternal getInheritedMetaclass(ClassObjectInternal cls, int n) { - exists(Class c | - c = cls.(PythonClassObjectInternal).getScope() and - n = count(c.getABase()) and - n != 1 - | - result = ObjectInternal::type() and major_version() = 3 - or - result = ObjectInternal::classType() and major_version() = 2 - ) - or - base_count(cls) = 1 and - n = 0 and - result = getBase(cls, 0).getClass() - or - exists(ClassObjectInternal meta1, ClassObjectInternal meta2 | - base_count(cls) > 1 and - meta1 = getBase(cls, n).getClass() and - meta2 = getInheritedMetaclass(cls, n + 1) - | - /* Choose sub-class */ - improperSuperType(meta1) = meta2 and result = meta1 - or - improperSuperType(meta2) = meta1 and result = meta2 - or - meta2 = ObjectInternal::classType() and result = meta1 - or - /* Make sure we have a metaclass, even if base is unknown */ - meta1 = ObjectInternal::unknownClass() and result = ObjectInternal::builtin("type") - or - meta2 = ObjectInternal::unknownClass() and result = meta1 - ) - } + private ClassObjectInternal getInheritedMetaclass(ClassObjectInternal cls, int n) { + exists(Class c | + c = cls.(PythonClassObjectInternal).getScope() and + n = count(c.getABase()) and + n != 1 + | + result = ObjectInternal::type() and major_version() = 3 + or + result = ObjectInternal::classType() and major_version() = 2 + ) + or + base_count(cls) = 1 and + n = 0 and + result = getBase(cls, 0).getClass() + or + exists(ClassObjectInternal meta1, ClassObjectInternal meta2 | + base_count(cls) > 1 and + meta1 = getBase(cls, n).getClass() and + meta2 = getInheritedMetaclass(cls, n + 1) + | + /* Choose sub-class */ + improperSuperType(meta1) = meta2 and result = meta1 + or + improperSuperType(meta2) = meta1 and result = meta2 + or + meta2 = ObjectInternal::classType() and result = meta1 + or + /* Make sure we have a metaclass, even if base is unknown */ + meta1 = ObjectInternal::unknownClass() and result = ObjectInternal::builtin("type") + or + meta2 = ObjectInternal::unknownClass() and result = meta1 + ) + } - private ClassObjectInternal improperSuperType(ClassObjectInternal cls) { - result = cls - or - result = improperSuperType(getBase(cls, _)) - } + private ClassObjectInternal improperSuperType(ClassObjectInternal cls) { + result = cls + or + result = improperSuperType(getBase(cls, _)) + } - /* Holds if type inference failed to compute the full class hierarchy for this class for the reason given. */ - cached - predicate failedInference(ClassObjectInternal cls, string reason) { - exists(int priority | - failedInference(cls, reason, priority) and - priority = max(int p | failedInference(cls, _, p)) - ) - } + /* Holds if type inference failed to compute the full class hierarchy for this class for the reason given. */ + cached + predicate failedInference(ClassObjectInternal cls, string reason) { + exists(int priority | + failedInference(cls, reason, priority) and + priority = max(int p | failedInference(cls, _, p)) + ) + } - /* Holds if type inference failed to compute the full class hierarchy for this class for the reason given. */ - private predicate failedInference(ClassObjectInternal cls, string reason, int priority) { - strictcount(cls.(PythonClassObjectInternal).getScope().getADecorator()) > 1 and - reason = "Multiple decorators" and - priority = 0 - or - exists(cls.(PythonClassObjectInternal).getScope().getADecorator()) and - not six_add_metaclass(_, _, cls, _) and - reason = "Decorator not understood" and - priority = 1 - or - reason = "Missing base " + missingBase(cls) and priority = 6 - or - not exists(ObjectInternal meta | - meta = cls.getClass() and not meta = ObjectInternal::unknownClass() - ) and - reason = "Failed to infer metaclass" and - priority = 4 - or - exists(int i, ObjectInternal base1, ObjectInternal base2 | - base1 = getBase(cls, i) and - base2 = getBase(cls, i) and - base1 != base2 and - reason = "Multiple bases at position " + i - ) and - priority = 6 - or - duplicateBase(cls) and reason = "Duplicate bases classes" and priority = 6 - or - not exists(getMro(cls)) and reason = "Failed to compute MRO" and priority = 3 - or - exists(int i | - failedInference(getBase(cls, i), _, _) and - reason = "Failed inference for base class at position " + i - ) and - priority = 5 - } + /* Holds if type inference failed to compute the full class hierarchy for this class for the reason given. */ + private predicate failedInference(ClassObjectInternal cls, string reason, int priority) { + strictcount(cls.(PythonClassObjectInternal).getScope().getADecorator()) > 1 and + reason = "Multiple decorators" and + priority = 0 + or + exists(cls.(PythonClassObjectInternal).getScope().getADecorator()) and + not six_add_metaclass(_, _, cls, _) and + reason = "Decorator not understood" and + priority = 1 + or + reason = "Missing base " + missingBase(cls) and priority = 6 + or + not exists(ObjectInternal meta | + meta = cls.getClass() and not meta = ObjectInternal::unknownClass() + ) and + reason = "Failed to infer metaclass" and + priority = 4 + or + exists(int i, ObjectInternal base1, ObjectInternal base2 | + base1 = getBase(cls, i) and + base2 = getBase(cls, i) and + base1 != base2 and + reason = "Multiple bases at position " + i + ) and + priority = 6 + or + duplicateBase(cls) and reason = "Duplicate bases classes" and priority = 6 + or + not exists(getMro(cls)) and reason = "Failed to compute MRO" and priority = 3 + or + exists(int i | + failedInference(getBase(cls, i), _, _) and + reason = "Failed inference for base class at position " + i + ) and + priority = 5 + } - private int missingBase(ClassObjectInternal cls) { - exists(cls.(PythonClassObjectInternal).getScope().getBase(result)) and - not exists(ObjectInternal base | - base = getBase(cls, result) and not base = ObjectInternal::unknownClass() - ) - } + private int missingBase(ClassObjectInternal cls) { + exists(cls.(PythonClassObjectInternal).getScope().getBase(result)) and + not exists(ObjectInternal base | + base = getBase(cls, result) and not base = ObjectInternal::unknownClass() + ) + } - private predicate duplicateBase(ClassObjectInternal cls) { - exists(int i, int j, ClassObjectInternal dup | - dup = getBase(cls, i) and - dup != ObjectInternal::unknownClass() and - dup = getBase(cls, j) and - i != j - ) - } + private predicate duplicateBase(ClassObjectInternal cls) { + exists(int i, int j, ClassObjectInternal dup | + dup = getBase(cls, i) and + dup != ObjectInternal::unknownClass() and + dup = getBase(cls, j) and + i != j + ) + } - cached - boolean improperSubclass(ObjectInternal sub, ObjectInternal sup) { - sub = sup and result = true - or - result = true and mroContains(Types::getMro(sub), sup) - or - result = false and mroDoesnotContain(Types::getMro(sub), sup, 0) - or - result = tupleSubclass(sub, sup, 0) - } + cached + boolean improperSubclass(ObjectInternal sub, ObjectInternal sup) { + sub = sup and result = true + or + result = true and mroContains(Types::getMro(sub), sup) + or + result = false and mroDoesnotContain(Types::getMro(sub), sup, 0) + or + result = tupleSubclass(sub, sup, 0) + } - private boolean tupleSubclass(ObjectInternal cls, TupleObjectInternal tpl, int n) { - Expressions::requireSubClass(cls, tpl) and - ( - n = tpl.length() and result = false - or - result = improperSubclass(cls, tpl.getItem(n)).booleanOr(tupleSubclass(cls, tpl, n + 1)) - ) - } + private boolean tupleSubclass(ObjectInternal cls, TupleObjectInternal tpl, int n) { + Expressions::requireSubClass(cls, tpl) and + ( + n = tpl.length() and result = false + or + result = improperSubclass(cls, tpl.getItem(n)).booleanOr(tupleSubclass(cls, tpl, n + 1)) + ) + } - private predicate mroContains(ClassList mro, ClassObjectInternal sup) { - mro.contains(sup) - or - exists(ClassDecl item, ClassDecl sdecl | - item = mro.getAnItem().getClassDeclaration() and - sdecl = sup.getClassDeclaration() and - is_abstract_subclass(item, sdecl) - ) - } + private predicate mroContains(ClassList mro, ClassObjectInternal sup) { + mro.contains(sup) + or + exists(ClassDecl item, ClassDecl sdecl | + item = mro.getAnItem().getClassDeclaration() and + sdecl = sup.getClassDeclaration() and + is_abstract_subclass(item, sdecl) + ) + } - private predicate mroDoesnotContain(ClassList mro, ClassObjectInternal sup, int n) { - exists(ClassObjectInternal cls | - Expressions::requireSubClass(cls, sup) and - mro = getMro(cls) - ) and - ( - n = mro.length() - or - mroDoesnotContain(mro, sup, n + 1) and - mro.getItem(n) != sup and - exists(ClassDecl item, ClassDecl sdecl | - item = mro.getItem(n).getClassDeclaration() and - sdecl = sup.getClassDeclaration() and - not is_abstract_subclass(item, sdecl) - ) - ) - } + private predicate mroDoesnotContain(ClassList mro, ClassObjectInternal sup, int n) { + exists(ClassObjectInternal cls | + Expressions::requireSubClass(cls, sup) and + mro = getMro(cls) + ) and + ( + n = mro.length() + or + mroDoesnotContain(mro, sup, n + 1) and + mro.getItem(n) != sup and + exists(ClassDecl item, ClassDecl sdecl | + item = mro.getItem(n).getClassDeclaration() and + sdecl = sup.getClassDeclaration() and + not is_abstract_subclass(item, sdecl) + ) + ) + } - private predicate is_abstract_subclass(ClassDecl cls, ClassDecl sup) { - cls = Builtin::builtin("list") and sup.isAbstractBaseClass("Sequence") - or - cls = Builtin::builtin("set") and sup.isAbstractBaseClass("Set") - or - cls = Builtin::builtin("dict") and sup.isAbstractBaseClass("Mapping") - or - cls = Builtin::builtin("list") and sup.isAbstractBaseClass("Iterable") - or - cls = Builtin::builtin("set") and sup.isAbstractBaseClass("Iterable") - or - cls = Builtin::builtin("dict") and sup.isAbstractBaseClass("Iterable") - } + private predicate is_abstract_subclass(ClassDecl cls, ClassDecl sup) { + cls = Builtin::builtin("list") and sup.isAbstractBaseClass("Sequence") + or + cls = Builtin::builtin("set") and sup.isAbstractBaseClass("Set") + or + cls = Builtin::builtin("dict") and sup.isAbstractBaseClass("Mapping") + or + cls = Builtin::builtin("list") and sup.isAbstractBaseClass("Iterable") + or + cls = Builtin::builtin("set") and sup.isAbstractBaseClass("Iterable") + or + cls = Builtin::builtin("dict") and sup.isAbstractBaseClass("Iterable") + } - cached - boolean hasAttr(ObjectInternal cls, string name) { - result = mroHasAttr(Types::getMro(cls), name, 0) - } + cached + boolean hasAttr(ObjectInternal cls, string name) { + result = mroHasAttr(Types::getMro(cls), name, 0) + } - private boolean mroHasAttr(ClassList mro, string name, int n) { - exists(ClassObjectInternal cls | - Expressions::requireHasAttr(cls, name) and - mro = getMro(cls) - ) and - ( - n = mro.length() and result = false - or - exists(ClassDecl decl | decl = mro.getItem(n).getClassDeclaration() | - if decl.declaresAttribute(name) - then result = true - else result = mroHasAttr(mro, name, n + 1) - ) - ) - } + private boolean mroHasAttr(ClassList mro, string name, int n) { + exists(ClassObjectInternal cls | + Expressions::requireHasAttr(cls, name) and + mro = getMro(cls) + ) and + ( + n = mro.length() and result = false + or + exists(ClassDecl decl | decl = mro.getItem(n).getClassDeclaration() | + if decl.declaresAttribute(name) + then result = true + else result = mroHasAttr(mro, name, n + 1) + ) + ) + } } module AttributePointsTo { - pragma[noinline] - predicate pointsTo( - ControlFlowNode f, Context context, ObjectInternal value, ControlFlowNode origin - ) { - exists(EssaVariable var, string name, CfgOrigin orig | - getsVariableAttribute(f, var, name) and - variableAttributePointsTo(var, context, name, value, orig) and - origin = orig.asCfgNodeOrHere(f) - ) - } + pragma[noinline] + predicate pointsTo( + ControlFlowNode f, Context context, ObjectInternal value, ControlFlowNode origin + ) { + exists(EssaVariable var, string name, CfgOrigin orig | + getsVariableAttribute(f, var, name) and + variableAttributePointsTo(var, context, name, value, orig) and + origin = orig.asCfgNodeOrHere(f) + ) + } - pragma[noinline] - private predicate getsVariableAttribute(ControlFlowNode f, EssaVariable var, string name) { - Expressions::getattr_call(f, var.getASourceUse(), _, _, name) - or - f.isLoad() and var.getASourceUse() = f.(AttrNode).getObject(name) - } + pragma[noinline] + private predicate getsVariableAttribute(ControlFlowNode f, EssaVariable var, string name) { + Expressions::getattr_call(f, var.getASourceUse(), _, _, name) + or + f.isLoad() and var.getASourceUse() = f.(AttrNode).getObject(name) + } - pragma[nomagic] - predicate variableAttributePointsTo( - EssaVariable var, Context context, string name, ObjectInternal value, CfgOrigin origin - ) { - definitionAttributePointsTo(var.getDefinition(), context, name, value, origin) - or - exists(EssaVariable prev | - var.getDefinition().(PhiFunction).getShortCircuitInput() = prev and - variableAttributePointsTo(prev, context, name, value, origin) - ) - } + pragma[nomagic] + predicate variableAttributePointsTo( + EssaVariable var, Context context, string name, ObjectInternal value, CfgOrigin origin + ) { + definitionAttributePointsTo(var.getDefinition(), context, name, value, origin) + or + exists(EssaVariable prev | + var.getDefinition().(PhiFunction).getShortCircuitInput() = prev and + variableAttributePointsTo(prev, context, name, value, origin) + ) + } - predicate definitionAttributePointsTo( - EssaDefinition def, Context context, string name, ObjectInternal value, CfgOrigin origin - ) { - variableAttributePointsTo(def.(PhiFunction).getAnInput(), context, name, value, origin) - or - piNodeAttributePointsTo(def, context, name, value, origin) - or - refinementAttributePointsTo(def, context, name, value, origin) - or - selfParameterAttributePointsTo(def, context, name, value, origin) - or - selfMethodCallsitePointsTo(def, context, name, value, origin) - or - argumentRefinementPointsTo(def, context, name, value, origin) - } + predicate definitionAttributePointsTo( + EssaDefinition def, Context context, string name, ObjectInternal value, CfgOrigin origin + ) { + variableAttributePointsTo(def.(PhiFunction).getAnInput(), context, name, value, origin) + or + piNodeAttributePointsTo(def, context, name, value, origin) + or + refinementAttributePointsTo(def, context, name, value, origin) + or + selfParameterAttributePointsTo(def, context, name, value, origin) + or + selfMethodCallsitePointsTo(def, context, name, value, origin) + or + argumentRefinementPointsTo(def, context, name, value, origin) + } - pragma[noinline] - private predicate refinementAttributePointsTo( - EssaNodeRefinement def, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - attributeAssignmentAttributePointsTo(def, context, name, value, origin) - or - attributeDeleteAttributePointsTo(def, context, name, value, origin) - or - uniEdgedPhiAttributePointsTo(def, context, name, value, origin) - } + pragma[noinline] + private predicate refinementAttributePointsTo( + EssaNodeRefinement def, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + attributeAssignmentAttributePointsTo(def, context, name, value, origin) + or + attributeDeleteAttributePointsTo(def, context, name, value, origin) + or + uniEdgedPhiAttributePointsTo(def, context, name, value, origin) + } - /** Attribute deletions have no effect as far as value tracking is concerned. */ - pragma[noinline] - private predicate attributeAssignmentAttributePointsTo( - AttributeAssignment def, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - def.getName() != name and - variableAttributePointsTo(def.getInput(), context, name, value, origin) - or - def.getName() = name and - exists(ControlFlowNode cfgnode | - PointsToInternal::pointsTo(def.getValue(), context, value, cfgnode) and - origin = CfgOrigin::fromCfgNode(cfgnode) - ) - } + /** Attribute deletions have no effect as far as value tracking is concerned. */ + pragma[noinline] + private predicate attributeAssignmentAttributePointsTo( + AttributeAssignment def, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + def.getName() != name and + variableAttributePointsTo(def.getInput(), context, name, value, origin) + or + def.getName() = name and + exists(ControlFlowNode cfgnode | + PointsToInternal::pointsTo(def.getValue(), context, value, cfgnode) and + origin = CfgOrigin::fromCfgNode(cfgnode) + ) + } - /** Attribute deletions have no effect as far as value tracking is concerned. */ - pragma[noinline] - private predicate attributeDeleteAttributePointsTo( - EssaAttributeDeletion def, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - def.getName() != name and - variableAttributePointsTo(def.getInput(), context, name, value, origin) - } + /** Attribute deletions have no effect as far as value tracking is concerned. */ + pragma[noinline] + private predicate attributeDeleteAttributePointsTo( + EssaAttributeDeletion def, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + def.getName() != name and + variableAttributePointsTo(def.getInput(), context, name, value, origin) + } - private predicate uniEdgedPhiAttributePointsTo( - SingleSuccessorGuard unipi, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - variableAttributePointsTo(unipi.getInput(), context, name, value, origin) - } + private predicate uniEdgedPhiAttributePointsTo( + SingleSuccessorGuard unipi, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + variableAttributePointsTo(unipi.getInput(), context, name, value, origin) + } - private predicate piNodeAttributePointsTo( - PyEdgeRefinement pi, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - variableAttributePointsTo(pi.getInput(), context, name, value, origin) - } + private predicate piNodeAttributePointsTo( + PyEdgeRefinement pi, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + variableAttributePointsTo(pi.getInput(), context, name, value, origin) + } - private predicate selfParameterAttributePointsTo( - ParameterDefinition def, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - exists(SelfCallsiteRefinement call, Function func, PointsToContext caller | - InterProceduralPointsTo::selfMethodCall(call, caller, func, context) and - def.isSelf() and - def.getScope() = func and - variableAttributePointsTo(call.getInput(), caller, name, value, origin) - ) - } + private predicate selfParameterAttributePointsTo( + ParameterDefinition def, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + exists(SelfCallsiteRefinement call, Function func, PointsToContext caller | + InterProceduralPointsTo::selfMethodCall(call, caller, func, context) and + def.isSelf() and + def.getScope() = func and + variableAttributePointsTo(call.getInput(), caller, name, value, origin) + ) + } - /** Pass through for `self` for the implicit re-definition of `self` in `self.foo()`. */ - private predicate selfMethodCallsitePointsTo( - SelfCallsiteRefinement def, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - /* The value of self remains the same, only the attributes may change */ - exists(Function func, PointsToContext callee, EssaVariable exit_self | - InterProceduralPointsTo::selfMethodCall(def, context, func, callee) and - exit_self.getSourceVariable().(Variable).isSelf() and - exit_self.getScope() = func and - BaseFlow::reaches_exit(exit_self) and - variableAttributePointsTo(exit_self, callee, name, value, origin) - ) - } + /** Pass through for `self` for the implicit re-definition of `self` in `self.foo()`. */ + private predicate selfMethodCallsitePointsTo( + SelfCallsiteRefinement def, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + /* The value of self remains the same, only the attributes may change */ + exists(Function func, PointsToContext callee, EssaVariable exit_self | + InterProceduralPointsTo::selfMethodCall(def, context, func, callee) and + exit_self.getSourceVariable().(Variable).isSelf() and + exit_self.getScope() = func and + BaseFlow::reaches_exit(exit_self) and + variableAttributePointsTo(exit_self, callee, name, value, origin) + ) + } - private predicate argumentRefinementPointsTo( - ArgumentRefinement def, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - exists(ObjectInternal callable | - PointsToInternal::pointsTo(def.getCall().getFunction(), context, callable, _) and - callable != ObjectInternal::builtin("setattr") - ) and - variableAttributePointsTo(def.getInput(), context, name, value, origin) - or - exists(string othername | - Expressions::setattr_call(def.getCall(), context, def.getInput().getASourceUse(), othername, - _, _) and - not othername = name - ) and - variableAttributePointsTo(def.getInput(), context, name, value, origin) - or - exists(ControlFlowNode orig | - Expressions::setattr_call(def.getCall(), context, def.getInput().getASourceUse(), name, value, - orig) and - origin = CfgOrigin::fromCfgNode(orig) - ) - } + private predicate argumentRefinementPointsTo( + ArgumentRefinement def, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + exists(ObjectInternal callable | + PointsToInternal::pointsTo(def.getCall().getFunction(), context, callable, _) and + callable != ObjectInternal::builtin("setattr") + ) and + variableAttributePointsTo(def.getInput(), context, name, value, origin) + or + exists(string othername | + Expressions::setattr_call(def.getCall(), context, def.getInput().getASourceUse(), othername, + _, _) and + not othername = name + ) and + variableAttributePointsTo(def.getInput(), context, name, value, origin) + or + exists(ControlFlowNode orig | + Expressions::setattr_call(def.getCall(), context, def.getInput().getASourceUse(), name, value, + orig) and + origin = CfgOrigin::fromCfgNode(orig) + ) + } } cached module ModuleAttributes { - private EssaVariable varAtExit(Module mod, string name) { - result.getName() = name and result.getAUse() = mod.getANormalExit() - } + private EssaVariable varAtExit(Module mod, string name) { + result.getName() = name and result.getAUse() = mod.getANormalExit() + } - private EssaVariable moduleStateVariable(ControlFlowNode use) { - result.isMetaVariable() and result.getAUse() = use - } + private EssaVariable moduleStateVariable(ControlFlowNode use) { + result.isMetaVariable() and result.getAUse() = use + } - private EssaVariable moduleStateVarAtExit(Module mod) { - result = moduleStateVariable(mod.getANormalExit()) - } + private EssaVariable moduleStateVarAtExit(Module mod) { + result = moduleStateVariable(mod.getANormalExit()) + } - cached - predicate pointsToAtExit(Module mod, string name, ObjectInternal value, CfgOrigin origin) { - if exists(varAtExit(mod, name)) - then - PointsToInternal::variablePointsTo(varAtExit(mod, name), any(Context c | c.isImport()), value, - origin) - else attributePointsTo(moduleStateVarAtExit(mod), name, value, origin) - } + cached + predicate pointsToAtExit(Module mod, string name, ObjectInternal value, CfgOrigin origin) { + if exists(varAtExit(mod, name)) + then + PointsToInternal::variablePointsTo(varAtExit(mod, name), any(Context c | c.isImport()), value, + origin) + else attributePointsTo(moduleStateVarAtExit(mod), name, value, origin) + } - cached - predicate attributePointsTo(EssaVariable var, string name, ObjectInternal value, CfgOrigin origin) { - importStarPointsTo(var.getDefinition(), name, value, origin) - or - callsitePointsTo(var.getDefinition(), name, value, origin) - or - scopeEntryPointsTo(var.getDefinition(), name, value, origin) - or - phiPointsTo(var.getDefinition(), name, value, origin) - } + cached + predicate attributePointsTo(EssaVariable var, string name, ObjectInternal value, CfgOrigin origin) { + importStarPointsTo(var.getDefinition(), name, value, origin) + or + callsitePointsTo(var.getDefinition(), name, value, origin) + or + scopeEntryPointsTo(var.getDefinition(), name, value, origin) + or + phiPointsTo(var.getDefinition(), name, value, origin) + } - /** Holds if the phi-function `phi` refers to `(value, origin)` given the context `context`. */ - pragma[nomagic] - private predicate phiPointsTo(PhiFunction phi, string name, ObjectInternal value, CfgOrigin origin) { - exists(EssaVariable input | - PointsToInternal::ssa_phi_reachable_from_input(phi, any(Context c | c.isImport()), input) and - attributePointsTo(input, name, value, origin) - ) - } + /** Holds if the phi-function `phi` refers to `(value, origin)` given the context `context`. */ + pragma[nomagic] + private predicate phiPointsTo(PhiFunction phi, string name, ObjectInternal value, CfgOrigin origin) { + exists(EssaVariable input | + PointsToInternal::ssa_phi_reachable_from_input(phi, any(Context c | c.isImport()), input) and + attributePointsTo(input, name, value, origin) + ) + } - pragma[nomagic] - private predicate importStarPointsTo( - ImportStarRefinement def, string name, ObjectInternal value, CfgOrigin origin - ) { - def.getVariable().isMetaVariable() and - /* Attribute from imported module */ - exists(ModuleObjectInternal mod | - importStarDef(def, _, mod) and - /* Attribute from imported module */ - exists(CfgOrigin orig | - InterModulePointsTo::moduleExportsBoolean(mod, name) = true and - mod.attribute(name, value, orig) and - origin = orig.fix(def.getDefiningNode()) and - not exists(Variable v | v.getId() = name and v.getScope() = def.getScope()) - ) - ) - or - /* Retain value held before import */ - exists(ModuleObjectInternal mod, EssaVariable input | - importStarDef(def, input, mod) and - (InterModulePointsTo::moduleExportsBoolean(mod, name) = false or name.charAt(0) = "_") and - attributePointsTo(def.getInput(), name, value, origin) - ) - } + pragma[nomagic] + private predicate importStarPointsTo( + ImportStarRefinement def, string name, ObjectInternal value, CfgOrigin origin + ) { + def.getVariable().isMetaVariable() and + /* Attribute from imported module */ + exists(ModuleObjectInternal mod | + importStarDef(def, _, mod) and + /* Attribute from imported module */ + exists(CfgOrigin orig | + InterModulePointsTo::moduleExportsBoolean(mod, name) = true and + mod.attribute(name, value, orig) and + origin = orig.fix(def.getDefiningNode()) and + not exists(Variable v | v.getId() = name and v.getScope() = def.getScope()) + ) + ) + or + /* Retain value held before import */ + exists(ModuleObjectInternal mod, EssaVariable input | + importStarDef(def, input, mod) and + (InterModulePointsTo::moduleExportsBoolean(mod, name) = false or name.charAt(0) = "_") and + attributePointsTo(def.getInput(), name, value, origin) + ) + } - private predicate importStarDef( - ImportStarRefinement def, EssaVariable input, ModuleObjectInternal mod - ) { - exists(ImportStarNode imp | - def.getVariable().getName() = "$" and - imp = def.getDefiningNode() and - input = def.getInput() and - PointsToInternal::importCtxPointsTo(imp.getModule(), mod, _) - ) - } + private predicate importStarDef( + ImportStarRefinement def, EssaVariable input, ModuleObjectInternal mod + ) { + exists(ImportStarNode imp | + def.getVariable().getName() = "$" and + imp = def.getDefiningNode() and + input = def.getInput() and + PointsToInternal::importCtxPointsTo(imp.getModule(), mod, _) + ) + } - /** - * Points-to for a variable (possibly) redefined by a call: - * `var = ...; foo(); use(var)` - * Where var may be redefined in call to `foo` if `var` escapes (is global or non-local). - */ - pragma[noinline] - private predicate callsitePointsTo( - CallsiteRefinement def, string name, ObjectInternal value, CfgOrigin origin - ) { - def.getVariable().isMetaVariable() and - exists(EssaVariable var, Function func, PointsToContext callee | - InterProceduralPointsTo::callsite_calls_function(def.getCall(), _, func, callee, _) and - var = moduleStateVariable(func.getANormalExit()) and - attributePointsTo(var, name, value, origin) - ) - } + /** + * Points-to for a variable (possibly) redefined by a call: + * `var = ...; foo(); use(var)` + * Where var may be redefined in call to `foo` if `var` escapes (is global or non-local). + */ + pragma[noinline] + private predicate callsitePointsTo( + CallsiteRefinement def, string name, ObjectInternal value, CfgOrigin origin + ) { + def.getVariable().isMetaVariable() and + exists(EssaVariable var, Function func, PointsToContext callee | + InterProceduralPointsTo::callsite_calls_function(def.getCall(), _, func, callee, _) and + var = moduleStateVariable(func.getANormalExit()) and + attributePointsTo(var, name, value, origin) + ) + } - /** - * Holds if the attribute name of the implicit '$' variable refers to `value` at the start of the scope. - * Since it cannot refer to any actual value, it is set to "undefined" for sub module names. - */ - pragma[noinline] - private predicate scopeEntryPointsTo( - ScopeEntryDefinition def, string name, ObjectInternal value, CfgOrigin origin - ) { - def.getVariable().isMetaVariable() and - exists(Module m | - def.getScope() = m and - not exists(EssaVariable named | named.getName() = name and named.getScope() = m) and - value = ObjectInternal::undefined() and - origin = CfgOrigin::unknown() - | - m.isPackageInit() and exists(m.getPackage().getSubModule(name)) - or - not m.declaredInAll(_) and - exists(PythonModuleObjectInternal mod | - mod.getSourceModule() = m and - InterModulePointsTo::ofInterestInExports(mod, name) - ) - ) - } + /** + * Holds if the attribute name of the implicit '$' variable refers to `value` at the start of the scope. + * Since it cannot refer to any actual value, it is set to "undefined" for sub module names. + */ + pragma[noinline] + private predicate scopeEntryPointsTo( + ScopeEntryDefinition def, string name, ObjectInternal value, CfgOrigin origin + ) { + def.getVariable().isMetaVariable() and + exists(Module m | + def.getScope() = m and + not exists(EssaVariable named | named.getName() = name and named.getScope() = m) and + value = ObjectInternal::undefined() and + origin = CfgOrigin::unknown() + | + m.isPackageInit() and exists(m.getPackage().getSubModule(name)) + or + not m.declaredInAll(_) and + exists(PythonModuleObjectInternal mod | + mod.getSourceModule() = m and + InterModulePointsTo::ofInterestInExports(mod, name) + ) + ) + } } diff --git a/python/ql/src/semmle/python/pointsto/PointsToContext.qll b/python/ql/src/semmle/python/pointsto/PointsToContext.qll index 4be7f812a6b..8b4178c796f 100644 --- a/python/ql/src/semmle/python/pointsto/PointsToContext.qll +++ b/python/ql/src/semmle/python/pointsto/PointsToContext.qll @@ -9,67 +9,67 @@ private import semmle.python.objects.ObjectInternal */ private int given_cost() { - exists(string depth | - py_flags_versioned("context.cost", depth, _) and - result = depth.toInt() - ) + exists(string depth | + py_flags_versioned("context.cost", depth, _) and + result = depth.toInt() + ) } pragma[noinline] private int max_context_cost() { - not py_flags_versioned("context.cost", _, _) and result = 7 - or - result = max(int cost | cost = given_cost() | cost) + not py_flags_versioned("context.cost", _, _) and result = 7 + or + result = max(int cost | cost = given_cost() | cost) } private int syntactic_call_count(Scope s) { - exists(Function f | f = s and f.getName() != "__init__" | - result = - count(CallNode call | - call.getFunction().(NameNode).getId() = f.getName() - or - call.getFunction().(AttrNode).getName() = f.getName() - ) - ) - or - s.getName() = "__init__" and result = 1 - or - not s instanceof Function and result = 0 + exists(Function f | f = s and f.getName() != "__init__" | + result = + count(CallNode call | + call.getFunction().(NameNode).getId() = f.getName() + or + call.getFunction().(AttrNode).getName() = f.getName() + ) + ) + or + s.getName() = "__init__" and result = 1 + or + not s instanceof Function and result = 0 } private int incoming_call_cost(Scope s) { - /* - * Syntactic call count will often be a considerable overestimate - * of the actual number of calls, so we use the square root. - * Cost = log(sqrt(call-count)) - */ + /* + * Syntactic call count will often be a considerable overestimate + * of the actual number of calls, so we use the square root. + * Cost = log(sqrt(call-count)) + */ - result = ((syntactic_call_count(s) + 1).log(2) * 0.5).floor() + result = ((syntactic_call_count(s) + 1).log(2) * 0.5).floor() } private int context_cost(TPointsToContext ctx) { - ctx = TMainContext() and result = 0 - or - ctx = TRuntimeContext() and result = 0 - or - ctx = TImportContext() and result = 0 - or - ctx = TCallContext(_, _, result) + ctx = TMainContext() and result = 0 + or + ctx = TRuntimeContext() and result = 0 + or + ctx = TImportContext() and result = 0 + or + ctx = TCallContext(_, _, result) } private int call_cost(CallNode call) { - if call.getScope().inSource() then result = 2 else result = 3 + if call.getScope().inSource() then result = 2 else result = 3 } private int outgoing_calls(Scope s) { result = strictcount(CallNode call | call.getScope() = s) } predicate super_method_call(CallNode call) { - call.getFunction().(AttrNode).getObject().(CallNode).getFunction().(NameNode).getId() = "super" + call.getFunction().(AttrNode).getObject().(CallNode).getFunction().(NameNode).getId() = "super" } private int outgoing_call_cost(CallNode c) { - /* Cost = log(outgoing-call-count) */ - result = outgoing_calls(c.getScope()).log(2).floor() + /* Cost = log(outgoing-call-count) */ + result = outgoing_calls(c.getScope()).log(2).floor() } /** @@ -79,46 +79,46 @@ private int outgoing_call_cost(CallNode c) { * in the number of contexts while retaining good results. */ private int splay_cost(CallNode c) { - if super_method_call(c) - then result = 0 - else result = outgoing_call_cost(c) + incoming_call_cost(c.getScope()) + if super_method_call(c) + then result = 0 + else result = outgoing_call_cost(c) + incoming_call_cost(c.getScope()) } private predicate call_to_init_or_del(CallNode call) { - exists(string mname | mname = "__init__" or mname = "__del__" | - mname = call.getFunction().(AttrNode).getName() - ) + exists(string mname | mname = "__init__" or mname = "__del__" | + mname = call.getFunction().(AttrNode).getName() + ) } /** Total cost estimate */ private int total_call_cost(CallNode call) { - /* - * We want to always follow __init__ and __del__ calls as they tell us about object construction, - * but we need to be aware of cycles, so they must have a non-zero cost. - */ + /* + * We want to always follow __init__ and __del__ calls as they tell us about object construction, + * but we need to be aware of cycles, so they must have a non-zero cost. + */ - if call_to_init_or_del(call) then result = 1 else result = call_cost(call) + splay_cost(call) + if call_to_init_or_del(call) then result = 1 else result = call_cost(call) + splay_cost(call) } pragma[noinline] private int total_cost(CallNode call, PointsToContext ctx) { - ctx.appliesTo(call) and - result = total_call_cost(call) + context_cost(ctx) + ctx.appliesTo(call) and + result = total_call_cost(call) + context_cost(ctx) } cached private newtype TPointsToContext = - TMainContext() or - TRuntimeContext() or - TImportContext() or - TCallContext(ControlFlowNode call, PointsToContext outerContext, int cost) { - total_cost(call, outerContext) = cost and - cost <= max_context_cost() - } or - TObjectContext(SelfInstanceInternal object) + TMainContext() or + TRuntimeContext() or + TImportContext() or + TCallContext(ControlFlowNode call, PointsToContext outerContext, int cost) { + total_cost(call, outerContext) = cost and + cost <= max_context_cost() + } or + TObjectContext(SelfInstanceInternal object) module Context { - PointsToContext forObject(ObjectInternal object) { result = TObjectContext(object) } + PointsToContext forObject(ObjectInternal object) { result = TObjectContext(object) } } /** @@ -129,109 +129,109 @@ module Context { * * All other contexts are call contexts and consist of a pair of call-site and caller context. */ class PointsToContext extends TPointsToContext { - /** Gets a textual representation of this element. */ - cached - string toString() { - this = TMainContext() and result = "main" - or - this = TRuntimeContext() and result = "runtime" - or - this = TImportContext() and result = "import" - or - exists(CallNode callsite, PointsToContext outerContext | - this = TCallContext(callsite, outerContext, _) and - result = callsite.getLocation() + " from " + outerContext.toString() - ) - } + /** Gets a textual representation of this element. */ + cached + string toString() { + this = TMainContext() and result = "main" + or + this = TRuntimeContext() and result = "runtime" + or + this = TImportContext() and result = "import" + or + exists(CallNode callsite, PointsToContext outerContext | + this = TCallContext(callsite, outerContext, _) and + result = callsite.getLocation() + " from " + outerContext.toString() + ) + } - /** Holds if `call` is the call-site from which this context was entered and `outer` is the caller's context. */ - predicate fromCall(CallNode call, PointsToContext caller) { - caller.appliesTo(call) and - this = TCallContext(call, caller, _) - } + /** Holds if `call` is the call-site from which this context was entered and `outer` is the caller's context. */ + predicate fromCall(CallNode call, PointsToContext caller) { + caller.appliesTo(call) and + this = TCallContext(call, caller, _) + } - /** Holds if `call` is the call-site from which this context was entered and `caller` is the caller's context. */ - predicate fromCall(CallNode call, PythonFunctionObjectInternal callee, PointsToContext caller) { - call = callee.getACall(caller) and - this = TCallContext(call, caller, _) - } + /** Holds if `call` is the call-site from which this context was entered and `caller` is the caller's context. */ + predicate fromCall(CallNode call, PythonFunctionObjectInternal callee, PointsToContext caller) { + call = callee.getACall(caller) and + this = TCallContext(call, caller, _) + } - /** Gets the caller context for this callee context. */ - PointsToContext getOuter() { this = TCallContext(_, result, _) } + /** Gets the caller context for this callee context. */ + PointsToContext getOuter() { this = TCallContext(_, result, _) } - /** Holds if this context is relevant to the given scope. */ - predicate appliesToScope(Scope s) { - /* Scripts */ - this = TMainContext() and maybe_main(s) - or - /* Modules and classes evaluated at import */ - s instanceof ImportTimeScope and this = TImportContext() - or - this = TRuntimeContext() and executes_in_runtime_context(s) - or - /* Called functions, regardless of their name */ - exists( - PythonFunctionObjectInternal callable, ControlFlowNode call, TPointsToContext outerContext - | - call = callable.getACall(outerContext) and - this = TCallContext(call, outerContext, _) - | - s = callable.getScope() - ) - or - InterProceduralPointsTo::callsite_calls_function(_, _, s, this, _) - } + /** Holds if this context is relevant to the given scope. */ + predicate appliesToScope(Scope s) { + /* Scripts */ + this = TMainContext() and maybe_main(s) + or + /* Modules and classes evaluated at import */ + s instanceof ImportTimeScope and this = TImportContext() + or + this = TRuntimeContext() and executes_in_runtime_context(s) + or + /* Called functions, regardless of their name */ + exists( + PythonFunctionObjectInternal callable, ControlFlowNode call, TPointsToContext outerContext + | + call = callable.getACall(outerContext) and + this = TCallContext(call, outerContext, _) + | + s = callable.getScope() + ) + or + InterProceduralPointsTo::callsite_calls_function(_, _, s, this, _) + } - /** Holds if this context can apply to the CFG node `n`. */ - pragma[inline] - predicate appliesTo(ControlFlowNode n) { this.appliesToScope(n.getScope()) } + /** Holds if this context can apply to the CFG node `n`. */ + pragma[inline] + predicate appliesTo(ControlFlowNode n) { this.appliesToScope(n.getScope()) } - /** Holds if this context is a call context. */ - predicate isCall() { this = TCallContext(_, _, _) } + /** Holds if this context is a call context. */ + predicate isCall() { this = TCallContext(_, _, _) } - /** Holds if this is the "main" context. */ - predicate isMain() { this = TMainContext() } + /** Holds if this is the "main" context. */ + predicate isMain() { this = TMainContext() } - /** Holds if this is the "import" context. */ - predicate isImport() { this = TImportContext() } + /** Holds if this is the "import" context. */ + predicate isImport() { this = TImportContext() } - /** Holds if this is the "default" context. */ - predicate isRuntime() { this = TRuntimeContext() } + /** Holds if this is the "default" context. */ + predicate isRuntime() { this = TRuntimeContext() } - /** Holds if this context or one of its caller contexts is the default context. */ - predicate fromRuntime() { - this.isRuntime() - or - this.getOuter().fromRuntime() - } + /** Holds if this context or one of its caller contexts is the default context. */ + predicate fromRuntime() { + this.isRuntime() + or + this.getOuter().fromRuntime() + } - /** Gets the depth (number of calls) for this context. */ - int getDepth() { - not exists(this.getOuter()) and result = 0 - or - result = this.getOuter().getDepth() + 1 - } + /** Gets the depth (number of calls) for this context. */ + int getDepth() { + not exists(this.getOuter()) and result = 0 + or + result = this.getOuter().getDepth() + 1 + } - int getCost() { result = context_cost(this) } + int getCost() { result = context_cost(this) } - CallNode getCall() { this = TCallContext(result, _, _) } + CallNode getCall() { this = TCallContext(result, _, _) } - /** Holds if a call would be too expensive to create a new context for */ - pragma[nomagic] - predicate untrackableCall(CallNode call) { total_cost(call, this) > max_context_cost() } + /** Holds if a call would be too expensive to create a new context for */ + pragma[nomagic] + predicate untrackableCall(CallNode call) { total_cost(call, this) > max_context_cost() } - CallNode getRootCall() { - this = TCallContext(result, TImportContext(), _) - or - result = this.getOuter().getRootCall() - } + CallNode getRootCall() { + this = TCallContext(result, TImportContext(), _) + or + result = this.getOuter().getRootCall() + } - /** Gets a version of Python that this context includes */ - pragma[inline] - Version getAVersion() { - /* Currently contexts do not include any version information, but may do in the future */ - result = major_version() - } + /** Gets a version of Python that this context includes */ + pragma[inline] + Version getAVersion() { + /* Currently contexts do not include any version information, but may do in the future */ + result = major_version() + } } private predicate in_source(Scope s) { exists(s.getEnclosingModule().getFile().getRelativePath()) } @@ -242,15 +242,15 @@ private predicate in_source(Scope s) { exists(s.getEnclosingModule().getFile().g * all "public" functions and methods, including those invoked by the VM. */ predicate executes_in_runtime_context(Function f) { - /* "Public" scope, i.e. functions whose name starts not with an underscore, or special methods */ - (f.getName().charAt(0) != "_" or f.isSpecialMethod() or f.isInitMethod()) and - in_source(f) + /* "Public" scope, i.e. functions whose name starts not with an underscore, or special methods */ + (f.getName().charAt(0) != "_" or f.isSpecialMethod() or f.isInitMethod()) and + in_source(f) } private predicate maybe_main(Module m) { - exists(If i, Compare cmp, Name name, StrConst main | m.getAStmt() = i and i.getTest() = cmp | - cmp.compares(name, any(Eq eq), main) and - name.getId() = "__name__" and - main.getText() = "__main__" - ) + exists(If i, Compare cmp, Name name, StrConst main | m.getAStmt() = i and i.getTest() = cmp | + cmp.compares(name, any(Eq eq), main) and + name.getId() = "__name__" and + main.getText() = "__main__" + ) } diff --git a/python/ql/src/semmle/python/regex.qll b/python/ql/src/semmle/python/regex.qll index cc33b2b347c..aaaf13ed823 100644 --- a/python/ql/src/semmle/python/regex.qll +++ b/python/ql/src/semmle/python/regex.qll @@ -2,21 +2,21 @@ import python import semmle.python.objects.ObjectInternal private predicate re_module_function(string name, int flags) { - name = "compile" and flags = 1 - or - name = "search" and flags = 2 - or - name = "match" and flags = 2 - or - name = "split" and flags = 3 - or - name = "findall" and flags = 2 - or - name = "finditer" and flags = 2 - or - name = "sub" and flags = 4 - or - name = "subn" and flags = 4 + name = "compile" and flags = 1 + or + name = "search" and flags = 2 + or + name = "match" and flags = 2 + or + name = "split" and flags = 3 + or + name = "findall" and flags = 2 + or + name = "finditer" and flags = 2 + or + name = "sub" and flags = 4 + or + name = "subn" and flags = 4 } /** @@ -24,701 +24,699 @@ private predicate re_module_function(string name, int flags) { * If regex mode is not known, `mode` will be `"None"`. */ predicate used_as_regex(Expr s, string mode) { - (s instanceof Bytes or s instanceof Unicode) and - /* Call to re.xxx(regex, ... [mode]) */ - exists(CallNode call, string name | - call.getArg(0).refersTo(_, _, s.getAFlowNode()) and - call.getFunction().pointsTo(Module::named("re").attr(name)) and - not name = "escape" - | - mode = "None" - or - exists(Value obj | mode = mode_from_mode_object(obj) | - exists(int flags_arg | - re_module_function(name, flags_arg) and - call.getArg(flags_arg).pointsTo(obj) - ) - or - call.getArgByName("flags").pointsTo(obj) - ) + (s instanceof Bytes or s instanceof Unicode) and + /* Call to re.xxx(regex, ... [mode]) */ + exists(CallNode call, string name | + call.getArg(0).refersTo(_, _, s.getAFlowNode()) and + call.getFunction().pointsTo(Module::named("re").attr(name)) and + not name = "escape" + | + mode = "None" + or + exists(Value obj | mode = mode_from_mode_object(obj) | + exists(int flags_arg | + re_module_function(name, flags_arg) and + call.getArg(flags_arg).pointsTo(obj) + ) + or + call.getArgByName("flags").pointsTo(obj) ) + ) } string mode_from_mode_object(Value obj) { - ( - result = "DEBUG" or - result = "IGNORECASE" or - result = "LOCALE" or - result = "MULTILINE" or - result = "DOTALL" or - result = "UNICODE" or - result = "VERBOSE" - ) and - exists(int flag | - flag = Value::named("sre_constants.SRE_FLAG_" + result).(ObjectInternal).intValue() and - obj.(ObjectInternal).intValue().bitAnd(flag) = flag - ) + ( + result = "DEBUG" or + result = "IGNORECASE" or + result = "LOCALE" or + result = "MULTILINE" or + result = "DOTALL" or + result = "UNICODE" or + result = "VERBOSE" + ) and + exists(int flag | + flag = Value::named("sre_constants.SRE_FLAG_" + result).(ObjectInternal).intValue() and + obj.(ObjectInternal).intValue().bitAnd(flag) = flag + ) } /** A StrConst used as a regular expression */ abstract class RegexString extends Expr { - RegexString() { (this instanceof Bytes or this instanceof Unicode) } + RegexString() { (this instanceof Bytes or this instanceof Unicode) } - predicate char_set_start(int start, int end) { - this.nonEscapedCharAt(start) = "[" and - ( - this.getChar(start + 1) = "^" and end = start + 2 - or - not this.getChar(start + 1) = "^" and end = start + 1 - ) - } + predicate char_set_start(int start, int end) { + this.nonEscapedCharAt(start) = "[" and + ( + this.getChar(start + 1) = "^" and end = start + 2 + or + not this.getChar(start + 1) = "^" and end = start + 1 + ) + } - /** Whether there is a character class, between start (inclusive) and end (exclusive) */ - predicate charSet(int start, int end) { - exists(int inner_start, int inner_end | - this.char_set_start(start, inner_start) and - not this.char_set_start(_, start) - | - end = inner_end + 1 and - inner_end > inner_start and - this.nonEscapedCharAt(inner_end) = "]" and - not exists(int mid | this.nonEscapedCharAt(mid) = "]" | mid > inner_start and mid < inner_end) - ) - } + /** Whether there is a character class, between start (inclusive) and end (exclusive) */ + predicate charSet(int start, int end) { + exists(int inner_start, int inner_end | + this.char_set_start(start, inner_start) and + not this.char_set_start(_, start) + | + end = inner_end + 1 and + inner_end > inner_start and + this.nonEscapedCharAt(inner_end) = "]" and + not exists(int mid | this.nonEscapedCharAt(mid) = "]" | mid > inner_start and mid < inner_end) + ) + } - predicate escapingChar(int pos) { this.escaping(pos) = true } + predicate escapingChar(int pos) { this.escaping(pos) = true } - private boolean escaping(int pos) { - pos = -1 and result = false + private boolean escaping(int pos) { + pos = -1 and result = false + or + this.getChar(pos) = "\\" and result = this.escaping(pos - 1).booleanNot() + or + this.getChar(pos) != "\\" and result = false + } + + /** Gets the text of this regex */ + string getText() { + result = this.(Unicode).getS() + or + result = this.(Bytes).getS() + } + + string getChar(int i) { result = this.getText().charAt(i) } + + string nonEscapedCharAt(int i) { + result = this.getText().charAt(i) and + not this.escapingChar(i - 1) + } + + private predicate isOptionDivider(int i) { this.nonEscapedCharAt(i) = "|" } + + private predicate isGroupEnd(int i) { this.nonEscapedCharAt(i) = ")" } + + private predicate isGroupStart(int i) { this.nonEscapedCharAt(i) = "(" } + + predicate failedToParse(int i) { + exists(this.getChar(i)) and + not exists(int start, int end | + this.top_level(start, end) and + start <= i and + end > i + ) + } + + /** Named unicode characters, eg \N{degree sign} */ + private predicate escapedName(int start, int end) { + this.escapingChar(start) and + this.getChar(start + 1) = "N" and + this.getChar(start + 2) = "{" and + this.getChar(end - 1) = "}" and + end > start and + not exists(int i | start + 2 < i and i < end - 1 | this.getChar(i) = "}") + } + + private predicate escapedCharacter(int start, int end) { + this.escapingChar(start) and + not exists(this.getText().substring(start + 1, end + 1).toInt()) and + ( + // hex value \xhh + this.getChar(start + 1) = "x" and end = start + 4 + or + // octal value \ooo + end in [start + 2 .. start + 4] and + exists(this.getText().substring(start + 1, end).toInt()) + or + // 16-bit hex value \uhhhh + this.getChar(start + 1) = "u" and end = start + 6 + or + // 32-bit hex value \Uhhhhhhhh + this.getChar(start + 1) = "U" and end = start + 10 + or + escapedName(start, end) + or + // escape not handled above, update when adding a new case + not this.getChar(start + 1) in ["x", "u", "U", "N"] and + end = start + 2 + ) + } + + private predicate inCharSet(int index) { + exists(int x, int y | this.charSet(x, y) and index in [x + 1 .. y - 2]) + } + + /* + * 'simple' characters are any that don't alter the parsing of the regex. + */ + + private predicate simpleCharacter(int start, int end) { + end = start + 1 and + not this.charSet(start, _) and + not this.charSet(_, start + 1) and + exists(string c | c = this.getChar(start) | + exists(int x, int y, int z | + this.charSet(x, z) and + this.char_set_start(x, y) + | + start = y or - this.getChar(pos) = "\\" and result = this.escaping(pos - 1).booleanNot() + start = z - 2 or - this.getChar(pos) != "\\" and result = false - } + start > y and start < z - 2 and not c = "-" + ) + or + not this.inCharSet(start) and + not c = "(" and + not c = "[" and + not c = ")" and + not c = "|" and + not this.qualifier(start, _, _) + ) + } - /** Gets the text of this regex */ - string getText() { - result = this.(Unicode).getS() + predicate character(int start, int end) { + ( + this.simpleCharacter(start, end) and + not exists(int x, int y | this.escapedCharacter(x, y) and x <= start and y >= end) + or + this.escapedCharacter(start, end) + ) and + not exists(int x, int y | this.group_start(x, y) and x <= start and y >= end) + } + + predicate normalCharacter(int start, int end) { + this.character(start, end) and + not this.specialCharacter(start, end, _) + } + + predicate specialCharacter(int start, int end, string char) { + this.character(start, end) and + end = start + 1 and + char = this.getChar(start) and + (char = "$" or char = "^" or char = ".") and + not this.inCharSet(start) + } + + /** Whether the text in the range start,end is a group */ + predicate group(int start, int end) { + this.groupContents(start, end, _, _) + or + this.emptyGroup(start, end) + } + + /** Gets the number of the group in start,end */ + int getGroupNumber(int start, int end) { + this.group(start, end) and + result = + count(int i | this.group(i, _) and i < start and not this.non_capturing_group_start(i, _)) + 1 + } + + /** Gets the name, if it has one, of the group in start,end */ + string getGroupName(int start, int end) { + this.group(start, end) and + exists(int name_end | + this.named_group_start(start, name_end) and + result = this.getText().substring(start + 4, name_end - 1) + ) + } + + /** Whether the text in the range start, end is a group and can match the empty string. */ + predicate zeroWidthMatch(int start, int end) { + this.emptyGroup(start, end) + or + this.negativeAssertionGroup(start, end) + or + positiveLookaheadAssertionGroup(start, end) + or + this.positiveLookbehindAssertionGroup(start, end) + } + + private predicate emptyGroup(int start, int end) { + exists(int endm1 | end = endm1 + 1 | + this.group_start(start, endm1) and + this.isGroupEnd(endm1) + ) + } + + private predicate emptyMatchAtStartGroup(int start, int end) { + this.emptyGroup(start, end) + or + this.negativeAssertionGroup(start, end) + or + this.positiveLookaheadAssertionGroup(start, end) + } + + private predicate emptyMatchAtEndGroup(int start, int end) { + this.emptyGroup(start, end) + or + this.negativeAssertionGroup(start, end) + or + this.positiveLookbehindAssertionGroup(start, end) + } + + private predicate negativeAssertionGroup(int start, int end) { + exists(int in_start | + this.negative_lookahead_assertion_start(start, in_start) + or + this.negative_lookbehind_assertion_start(start, in_start) + | + this.groupContents(start, end, in_start, _) + ) + } + + private predicate positiveLookaheadAssertionGroup(int start, int end) { + exists(int in_start | this.lookahead_assertion_start(start, in_start) | + this.groupContents(start, end, in_start, _) + ) + } + + private predicate positiveLookbehindAssertionGroup(int start, int end) { + exists(int in_start | this.lookbehind_assertion_start(start, in_start) | + this.groupContents(start, end, in_start, _) + ) + } + + private predicate group_start(int start, int end) { + this.non_capturing_group_start(start, end) + or + this.flag_group_start(start, end, _) + or + this.named_group_start(start, end) + or + this.named_backreference_start(start, end) + or + this.lookahead_assertion_start(start, end) + or + this.negative_lookahead_assertion_start(start, end) + or + this.lookbehind_assertion_start(start, end) + or + this.negative_lookbehind_assertion_start(start, end) + or + this.comment_group_start(start, end) + or + this.simple_group_start(start, end) + } + + private predicate non_capturing_group_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = ":" and + end = start + 3 + } + + private predicate simple_group_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) != "?" and + end = start + 1 + } + + private predicate named_group_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "P" and + this.getChar(start + 3) = "<" and + not this.getChar(start + 4) = "=" and + not this.getChar(start + 4) = "!" and + exists(int name_end | + name_end = min(int i | i > start + 4 and this.getChar(i) = ">") and + end = name_end + 1 + ) + } + + private predicate named_backreference_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "P" and + this.getChar(start + 3) = "=" and + end = min(int i | i > start + 4 and this.getChar(i) = "?") + } + + private predicate flag_group_start(int start, int end, string c) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + end = start + 3 and + c = this.getChar(start + 2) and + ( + c = "i" or + c = "L" or + c = "m" or + c = "s" or + c = "u" or + c = "x" + ) + } + + /** + * Gets the mode of this regular expression string if + * it is defined by a prefix. + */ + string getModeFromPrefix() { + exists(string c | this.flag_group_start(_, _, c) | + c = "i" and result = "IGNORECASE" + or + c = "L" and result = "LOCALE" + or + c = "m" and result = "MULTILINE" + or + c = "s" and result = "DOTALL" + or + c = "u" and result = "UNICODE" + or + c = "x" and result = "VERBOSE" + ) + } + + private predicate lookahead_assertion_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "=" and + end = start + 3 + } + + private predicate negative_lookahead_assertion_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "!" and + end = start + 3 + } + + private predicate lookbehind_assertion_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "<" and + this.getChar(start + 3) = "=" and + end = start + 4 + } + + private predicate negative_lookbehind_assertion_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "<" and + this.getChar(start + 3) = "!" and + end = start + 4 + } + + private predicate comment_group_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "#" and + end = start + 3 + } + + predicate groupContents(int start, int end, int in_start, int in_end) { + this.group_start(start, in_start) and + end = in_end + 1 and + this.top_level(in_start, in_end) and + this.isGroupEnd(in_end) + } + + private predicate named_backreference(int start, int end, string name) { + this.named_backreference_start(start, start + 4) and + end = min(int i | i > start + 4 and this.getChar(i) = ")") + 1 and + name = this.getText().substring(start + 4, end - 2) + } + + private predicate numbered_backreference(int start, int end, int value) { + this.escapingChar(start) and + exists(string text, string svalue, int len | + end = start + len and + text = this.getText() and + len in [2 .. 3] + | + svalue = text.substring(start + 1, start + len) and + value = svalue.toInt() and + not exists(text.substring(start + 1, start + len + 1).toInt()) and + value != 0 + ) + } + + /** Whether the text in the range start,end is a back reference */ + predicate backreference(int start, int end) { + this.numbered_backreference(start, end, _) + or + this.named_backreference(start, end, _) + } + + /** Gets the number of the back reference in start,end */ + int getBackrefNumber(int start, int end) { this.numbered_backreference(start, end, result) } + + /** Gets the name, if it has one, of the back reference in start,end */ + string getBackrefName(int start, int end) { this.named_backreference(start, end, result) } + + private predicate baseItem(int start, int end) { + this.character(start, end) and + not exists(int x, int y | this.charSet(x, y) and x <= start and y >= end) + or + this.group(start, end) + or + this.charSet(start, end) + } + + private predicate qualifier(int start, int end, boolean maybe_empty) { + this.short_qualifier(start, end, maybe_empty) and not this.getChar(end) = "?" + or + exists(int short_end | this.short_qualifier(start, short_end, maybe_empty) | + if this.getChar(short_end) = "?" then end = short_end + 1 else end = short_end + ) + } + + private predicate short_qualifier(int start, int end, boolean maybe_empty) { + ( + this.getChar(start) = "+" and maybe_empty = false + or + this.getChar(start) = "*" and maybe_empty = true + or + this.getChar(start) = "?" and maybe_empty = true + ) and + end = start + 1 + or + exists(int endin | end = endin + 1 | + this.getChar(start) = "{" and + this.getChar(endin) = "}" and + end > start and + exists(string multiples | multiples = this.getText().substring(start + 1, endin) | + multiples.regexpMatch("0+") and maybe_empty = true or - result = this.(Bytes).getS() - } - - string getChar(int i) { result = this.getText().charAt(i) } - - string nonEscapedCharAt(int i) { - result = this.getText().charAt(i) and - not this.escapingChar(i - 1) - } - - private predicate isOptionDivider(int i) { this.nonEscapedCharAt(i) = "|" } - - private predicate isGroupEnd(int i) { this.nonEscapedCharAt(i) = ")" } - - private predicate isGroupStart(int i) { this.nonEscapedCharAt(i) = "(" } - - predicate failedToParse(int i) { - exists(this.getChar(i)) and - not exists(int start, int end | - this.top_level(start, end) and - start <= i and - end > i - ) - } - - /** Named unicode characters, eg \N{degree sign} */ - private predicate escapedName(int start, int end) { - this.escapingChar(start) and - this.getChar(start + 1) = "N" and - this.getChar(start + 2) = "{" and - this.getChar(end - 1) = "}" and - end > start and - not exists(int i | start + 2 < i and i < end - 1 | - this.getChar(i) = "}" - ) - } - - private predicate escapedCharacter(int start, int end) { - this.escapingChar(start) and - not exists(this.getText().substring(start + 1, end + 1).toInt()) and - ( - // hex value \xhh - this.getChar(start + 1) = "x" and end = start + 4 - or - // octal value \ooo - end in [start + 2 .. start + 4] and - exists(this.getText().substring(start + 1, end).toInt()) - or - // 16-bit hex value \uhhhh - this.getChar(start + 1) = "u" and end = start + 6 - or - // 32-bit hex value \Uhhhhhhhh - this.getChar(start + 1) = "U" and end = start + 10 - or - escapedName(start, end) - or - // escape not handled above, update when adding a new case - not this.getChar(start + 1) in ["x", "u", "U", "N"] and - end = start + 2 - ) - } - - private predicate inCharSet(int index) { - exists(int x, int y | this.charSet(x, y) and index in [x + 1 .. y - 2]) - } - - /* - * 'simple' characters are any that don't alter the parsing of the regex. - */ - - private predicate simpleCharacter(int start, int end) { - end = start + 1 and - not this.charSet(start, _) and - not this.charSet(_, start + 1) and - exists(string c | c = this.getChar(start) | - exists(int x, int y, int z | - this.charSet(x, z) and - this.char_set_start(x, y) - | - start = y - or - start = z - 2 - or - start > y and start < z - 2 and not c = "-" - ) - or - not this.inCharSet(start) and - not c = "(" and - not c = "[" and - not c = ")" and - not c = "|" and - not this.qualifier(start, _, _) - ) - } - - predicate character(int start, int end) { - ( - this.simpleCharacter(start, end) and - not exists(int x, int y | this.escapedCharacter(x, y) and x <= start and y >= end) - or - this.escapedCharacter(start, end) - ) and - not exists(int x, int y | this.group_start(x, y) and x <= start and y >= end) - } - - predicate normalCharacter(int start, int end) { - this.character(start, end) and - not this.specialCharacter(start, end, _) - } - - predicate specialCharacter(int start, int end, string char) { - this.character(start, end) and - end = start + 1 and - char = this.getChar(start) and - (char = "$" or char = "^" or char = ".") and - not this.inCharSet(start) - } - - /** Whether the text in the range start,end is a group */ - predicate group(int start, int end) { - this.groupContents(start, end, _, _) + multiples.regexpMatch("0*,[0-9]*") and maybe_empty = true or - this.emptyGroup(start, end) - } - - /** Gets the number of the group in start,end */ - int getGroupNumber(int start, int end) { - this.group(start, end) and - result = - count(int i | this.group(i, _) and i < start and not this.non_capturing_group_start(i, _)) + 1 - } - - /** Gets the name, if it has one, of the group in start,end */ - string getGroupName(int start, int end) { - this.group(start, end) and - exists(int name_end | - this.named_group_start(start, name_end) and - result = this.getText().substring(start + 4, name_end - 1) - ) - } - - /** Whether the text in the range start, end is a group and can match the empty string. */ - predicate zeroWidthMatch(int start, int end) { - this.emptyGroup(start, end) + multiples.regexpMatch("0*[1-9][0-9]*") and maybe_empty = false or - this.negativeAssertionGroup(start, end) - or - positiveLookaheadAssertionGroup(start, end) - or - this.positiveLookbehindAssertionGroup(start, end) - } + multiples.regexpMatch("0*[1-9][0-9]*,[0-9]*") and maybe_empty = false + ) and + not exists(int mid | + this.getChar(mid) = "}" and + mid > start and + mid < endin + ) + ) + } - private predicate emptyGroup(int start, int end) { - exists(int endm1 | end = endm1 + 1 | - this.group_start(start, endm1) and - this.isGroupEnd(endm1) - ) - } + /** + * Whether the text in the range start,end is a qualified item, where item is a character, + * a character set or a group. + */ + predicate qualifiedItem(int start, int end, boolean maybe_empty) { + this.qualifiedPart(start, _, end, maybe_empty) + } - private predicate emptyMatchAtStartGroup(int start, int end) { - this.emptyGroup(start, end) - or - this.negativeAssertionGroup(start, end) - or - this.positiveLookaheadAssertionGroup(start, end) - } + private predicate qualifiedPart(int start, int part_end, int end, boolean maybe_empty) { + this.baseItem(start, part_end) and + this.qualifier(part_end, end, maybe_empty) + } - private predicate emptyMatchAtEndGroup(int start, int end) { - this.emptyGroup(start, end) - or - this.negativeAssertionGroup(start, end) - or - this.positiveLookbehindAssertionGroup(start, end) - } + private predicate item(int start, int end) { + this.qualifiedItem(start, end, _) + or + this.baseItem(start, end) and not this.qualifier(end, _, _) + } - private predicate negativeAssertionGroup(int start, int end) { - exists(int in_start | - this.negative_lookahead_assertion_start(start, in_start) - or - this.negative_lookbehind_assertion_start(start, in_start) - | - this.groupContents(start, end, in_start, _) - ) - } + private predicate subsequence(int start, int end) { + ( + start = 0 or + this.group_start(_, start) or + this.isOptionDivider(start - 1) + ) and + this.item(start, end) + or + exists(int mid | + this.subsequence(start, mid) and + this.item(mid, end) + ) + } - private predicate positiveLookaheadAssertionGroup(int start, int end) { - exists(int in_start | this.lookahead_assertion_start(start, in_start) | - this.groupContents(start, end, in_start, _) - ) - } + /** + * Whether the text in the range start,end is a sequence of 1 or more items, where an item is a character, + * a character set or a group. + */ + predicate sequence(int start, int end) { + this.sequenceOrQualified(start, end) and + not this.qualifiedItem(start, end, _) + } - private predicate positiveLookbehindAssertionGroup(int start, int end) { - exists(int in_start | this.lookbehind_assertion_start(start, in_start) | - this.groupContents(start, end, in_start, _) - ) - } + private predicate sequenceOrQualified(int start, int end) { + this.subsequence(start, end) and + not this.item_start(end) + } - private predicate group_start(int start, int end) { - this.non_capturing_group_start(start, end) - or - this.flag_group_start(start, end, _) - or - this.named_group_start(start, end) - or - this.named_backreference_start(start, end) - or - this.lookahead_assertion_start(start, end) - or - this.negative_lookahead_assertion_start(start, end) - or - this.lookbehind_assertion_start(start, end) - or - this.negative_lookbehind_assertion_start(start, end) - or - this.comment_group_start(start, end) - or - this.simple_group_start(start, end) - } + private predicate item_start(int start) { + this.character(start, _) or + this.isGroupStart(start) or + this.charSet(start, _) + } - private predicate non_capturing_group_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = ":" and - end = start + 3 - } + private predicate item_end(int end) { + this.character(_, end) + or + exists(int endm1 | this.isGroupEnd(endm1) and end = endm1 + 1) + or + this.charSet(_, end) + or + this.qualifier(_, end, _) + } - private predicate simple_group_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) != "?" and - end = start + 1 - } + private predicate top_level(int start, int end) { + this.subalternation(start, end, _) and + not this.isOptionDivider(end) + } - private predicate named_group_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = "P" and - this.getChar(start + 3) = "<" and - not this.getChar(start + 4) = "=" and - not this.getChar(start + 4) = "!" and - exists(int name_end | - name_end = min(int i | i > start + 4 and this.getChar(i) = ">") and - end = name_end + 1 - ) - } + private predicate subalternation(int start, int end, int item_start) { + this.sequenceOrQualified(start, end) and + not this.isOptionDivider(start - 1) and + item_start = start + or + start = end and + not this.item_end(start) and + this.isOptionDivider(end) and + item_start = start + or + exists(int mid | + this.subalternation(start, mid, _) and + this.isOptionDivider(mid) and + item_start = mid + 1 + | + this.sequenceOrQualified(item_start, end) + or + not this.item_start(end) and end = item_start + ) + } - private predicate named_backreference_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = "P" and - this.getChar(start + 3) = "=" and - end = min(int i | i > start + 4 and this.getChar(i) = "?") - } + /** + * Whether the text in the range start,end is an alternation + */ + predicate alternation(int start, int end) { + this.top_level(start, end) and + exists(int less | this.subalternation(start, less, _) and less < end) + } - private predicate flag_group_start(int start, int end, string c) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - end = start + 3 and - c = this.getChar(start + 2) and - ( - c = "i" or - c = "L" or - c = "m" or - c = "s" or - c = "u" or - c = "x" - ) - } + /** + * Whether the text in the range start,end is an alternation and the text in part_start, part_end is one of the + * options in that alternation. + */ + predicate alternationOption(int start, int end, int part_start, int part_end) { + this.alternation(start, end) and + this.subalternation(start, part_end, part_start) + } - /** - * Gets the mode of this regular expression string if - * it is defined by a prefix. - */ - string getModeFromPrefix() { - exists(string c | this.flag_group_start(_, _, c) | - c = "i" and result = "IGNORECASE" - or - c = "L" and result = "LOCALE" - or - c = "m" and result = "MULTILINE" - or - c = "s" and result = "DOTALL" - or - c = "u" and result = "UNICODE" - or - c = "x" and result = "VERBOSE" - ) - } + /** A part of the regex that may match the start of the string. */ + private predicate firstPart(int start, int end) { + start = 0 and end = this.getText().length() + or + exists(int x | this.firstPart(x, end) | + this.emptyMatchAtStartGroup(x, start) or + this.qualifiedItem(x, start, true) or + this.specialCharacter(x, start, "^") + ) + or + exists(int y | this.firstPart(start, y) | + this.item(start, end) + or + this.qualifiedPart(start, end, y, _) + ) + or + exists(int x, int y | this.firstPart(x, y) | + this.groupContents(x, y, start, end) + or + this.alternationOption(x, y, start, end) + ) + } - private predicate lookahead_assertion_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = "=" and - end = start + 3 - } + /** A part of the regex that may match the end of the string. */ + private predicate lastPart(int start, int end) { + start = 0 and end = this.getText().length() + or + exists(int y | this.lastPart(start, y) | + this.emptyMatchAtEndGroup(end, y) + or + this.qualifiedItem(end, y, true) + or + this.specialCharacter(end, y, "$") + or + y = end + 2 and this.escapingChar(end) and this.getChar(end + 1) = "Z" + ) + or + exists(int x | + this.lastPart(x, end) and + this.item(start, end) + ) + or + exists(int y | this.lastPart(start, y) | this.qualifiedPart(start, end, y, _)) + or + exists(int x, int y | this.lastPart(x, y) | + this.groupContents(x, y, start, end) + or + this.alternationOption(x, y, start, end) + ) + } - private predicate negative_lookahead_assertion_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = "!" and - end = start + 3 - } + /** + * Whether the item at [start, end) is one of the first items + * to be matched. + */ + predicate firstItem(int start, int end) { + ( + this.character(start, end) + or + this.qualifiedItem(start, end, _) + or + this.charSet(start, end) + ) and + this.firstPart(start, end) + } - private predicate lookbehind_assertion_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = "<" and - this.getChar(start + 3) = "=" and - end = start + 4 - } - - private predicate negative_lookbehind_assertion_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = "<" and - this.getChar(start + 3) = "!" and - end = start + 4 - } - - private predicate comment_group_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = "#" and - end = start + 3 - } - - predicate groupContents(int start, int end, int in_start, int in_end) { - this.group_start(start, in_start) and - end = in_end + 1 and - this.top_level(in_start, in_end) and - this.isGroupEnd(in_end) - } - - private predicate named_backreference(int start, int end, string name) { - this.named_backreference_start(start, start + 4) and - end = min(int i | i > start + 4 and this.getChar(i) = ")") + 1 and - name = this.getText().substring(start + 4, end - 2) - } - - private predicate numbered_backreference(int start, int end, int value) { - this.escapingChar(start) and - exists(string text, string svalue, int len | - end = start + len and - text = this.getText() and - len in [2 .. 3] - | - svalue = text.substring(start + 1, start + len) and - value = svalue.toInt() and - not exists(text.substring(start + 1, start + len + 1).toInt()) and - value != 0 - ) - } - - /** Whether the text in the range start,end is a back reference */ - predicate backreference(int start, int end) { - this.numbered_backreference(start, end, _) - or - this.named_backreference(start, end, _) - } - - /** Gets the number of the back reference in start,end */ - int getBackrefNumber(int start, int end) { this.numbered_backreference(start, end, result) } - - /** Gets the name, if it has one, of the back reference in start,end */ - string getBackrefName(int start, int end) { this.named_backreference(start, end, result) } - - private predicate baseItem(int start, int end) { - this.character(start, end) and - not exists(int x, int y | this.charSet(x, y) and x <= start and y >= end) - or - this.group(start, end) - or - this.charSet(start, end) - } - - private predicate qualifier(int start, int end, boolean maybe_empty) { - this.short_qualifier(start, end, maybe_empty) and not this.getChar(end) = "?" - or - exists(int short_end | this.short_qualifier(start, short_end, maybe_empty) | - if this.getChar(short_end) = "?" then end = short_end + 1 else end = short_end - ) - } - - private predicate short_qualifier(int start, int end, boolean maybe_empty) { - ( - this.getChar(start) = "+" and maybe_empty = false - or - this.getChar(start) = "*" and maybe_empty = true - or - this.getChar(start) = "?" and maybe_empty = true - ) and - end = start + 1 - or - exists(int endin | end = endin + 1 | - this.getChar(start) = "{" and - this.getChar(endin) = "}" and - end > start and - exists(string multiples | multiples = this.getText().substring(start + 1, endin) | - multiples.regexpMatch("0+") and maybe_empty = true - or - multiples.regexpMatch("0*,[0-9]*") and maybe_empty = true - or - multiples.regexpMatch("0*[1-9][0-9]*") and maybe_empty = false - or - multiples.regexpMatch("0*[1-9][0-9]*,[0-9]*") and maybe_empty = false - ) and - not exists(int mid | - this.getChar(mid) = "}" and - mid > start and - mid < endin - ) - ) - } - - /** - * Whether the text in the range start,end is a qualified item, where item is a character, - * a character set or a group. - */ - predicate qualifiedItem(int start, int end, boolean maybe_empty) { - this.qualifiedPart(start, _, end, maybe_empty) - } - - private predicate qualifiedPart(int start, int part_end, int end, boolean maybe_empty) { - this.baseItem(start, part_end) and - this.qualifier(part_end, end, maybe_empty) - } - - private predicate item(int start, int end) { - this.qualifiedItem(start, end, _) - or - this.baseItem(start, end) and not this.qualifier(end, _, _) - } - - private predicate subsequence(int start, int end) { - ( - start = 0 or - this.group_start(_, start) or - this.isOptionDivider(start - 1) - ) and - this.item(start, end) - or - exists(int mid | - this.subsequence(start, mid) and - this.item(mid, end) - ) - } - - /** - * Whether the text in the range start,end is a sequence of 1 or more items, where an item is a character, - * a character set or a group. - */ - predicate sequence(int start, int end) { - this.sequenceOrQualified(start, end) and - not this.qualifiedItem(start, end, _) - } - - private predicate sequenceOrQualified(int start, int end) { - this.subsequence(start, end) and - not this.item_start(end) - } - - private predicate item_start(int start) { - this.character(start, _) or - this.isGroupStart(start) or - this.charSet(start, _) - } - - private predicate item_end(int end) { - this.character(_, end) - or - exists(int endm1 | this.isGroupEnd(endm1) and end = endm1 + 1) - or - this.charSet(_, end) - or - this.qualifier(_, end, _) - } - - private predicate top_level(int start, int end) { - this.subalternation(start, end, _) and - not this.isOptionDivider(end) - } - - private predicate subalternation(int start, int end, int item_start) { - this.sequenceOrQualified(start, end) and - not this.isOptionDivider(start - 1) and - item_start = start - or - start = end and - not this.item_end(start) and - this.isOptionDivider(end) and - item_start = start - or - exists(int mid | - this.subalternation(start, mid, _) and - this.isOptionDivider(mid) and - item_start = mid + 1 - | - this.sequenceOrQualified(item_start, end) - or - not this.item_start(end) and end = item_start - ) - } - - /** - * Whether the text in the range start,end is an alternation - */ - predicate alternation(int start, int end) { - this.top_level(start, end) and - exists(int less | this.subalternation(start, less, _) and less < end) - } - - /** - * Whether the text in the range start,end is an alternation and the text in part_start, part_end is one of the - * options in that alternation. - */ - predicate alternationOption(int start, int end, int part_start, int part_end) { - this.alternation(start, end) and - this.subalternation(start, part_end, part_start) - } - - /** A part of the regex that may match the start of the string. */ - private predicate firstPart(int start, int end) { - start = 0 and end = this.getText().length() - or - exists(int x | this.firstPart(x, end) | - this.emptyMatchAtStartGroup(x, start) or - this.qualifiedItem(x, start, true) or - this.specialCharacter(x, start, "^") - ) - or - exists(int y | this.firstPart(start, y) | - this.item(start, end) - or - this.qualifiedPart(start, end, y, _) - ) - or - exists(int x, int y | this.firstPart(x, y) | - this.groupContents(x, y, start, end) - or - this.alternationOption(x, y, start, end) - ) - } - - /** A part of the regex that may match the end of the string. */ - private predicate lastPart(int start, int end) { - start = 0 and end = this.getText().length() - or - exists(int y | this.lastPart(start, y) | - this.emptyMatchAtEndGroup(end, y) - or - this.qualifiedItem(end, y, true) - or - this.specialCharacter(end, y, "$") - or - y = end + 2 and this.escapingChar(end) and this.getChar(end + 1) = "Z" - ) - or - exists(int x | - this.lastPart(x, end) and - this.item(start, end) - ) - or - exists(int y | this.lastPart(start, y) | this.qualifiedPart(start, end, y, _)) - or - exists(int x, int y | this.lastPart(x, y) | - this.groupContents(x, y, start, end) - or - this.alternationOption(x, y, start, end) - ) - } - - /** - * Whether the item at [start, end) is one of the first items - * to be matched. - */ - predicate firstItem(int start, int end) { - ( - this.character(start, end) - or - this.qualifiedItem(start, end, _) - or - this.charSet(start, end) - ) and - this.firstPart(start, end) - } - - /** - * Whether the item at [start, end) is one of the last items - * to be matched. - */ - predicate lastItem(int start, int end) { - ( - this.character(start, end) - or - this.qualifiedItem(start, end, _) - or - this.charSet(start, end) - ) and - this.lastPart(start, end) - } + /** + * Whether the item at [start, end) is one of the last items + * to be matched. + */ + predicate lastItem(int start, int end) { + ( + this.character(start, end) + or + this.qualifiedItem(start, end, _) + or + this.charSet(start, end) + ) and + this.lastPart(start, end) + } } /** A StrConst used as a regular expression */ class Regex extends RegexString { - Regex() { used_as_regex(this, _) } + Regex() { used_as_regex(this, _) } - /** - * Gets a mode (if any) of this regular expression. Can be any of: - * DEBUG - * IGNORECASE - * LOCALE - * MULTILINE - * DOTALL - * UNICODE - * VERBOSE - */ - string getAMode() { - result != "None" and - used_as_regex(this, result) - or - result = this.getModeFromPrefix() - } + /** + * Gets a mode (if any) of this regular expression. Can be any of: + * DEBUG + * IGNORECASE + * LOCALE + * MULTILINE + * DOTALL + * UNICODE + * VERBOSE + */ + string getAMode() { + result != "None" and + used_as_regex(this, result) + or + result = this.getModeFromPrefix() + } } diff --git a/python/ql/src/semmle/python/security/ClearText.qll b/python/ql/src/semmle/python/security/ClearText.qll index a26e33218dd..8e964d19386 100644 --- a/python/ql/src/semmle/python/security/ClearText.qll +++ b/python/ql/src/semmle/python/security/ClearText.qll @@ -5,54 +5,54 @@ import semmle.python.dataflow.Files import semmle.python.web.Http module ClearTextStorage { - abstract class Sink extends TaintSink { - override predicate sinks(TaintKind kind) { kind instanceof SensitiveData } - } + abstract class Sink extends TaintSink { + override predicate sinks(TaintKind kind) { kind instanceof SensitiveData } + } - class CookieStorageSink extends Sink { - CookieStorageSink() { any(CookieSet cookie).getValue() = this } - } + class CookieStorageSink extends Sink { + CookieStorageSink() { any(CookieSet cookie).getValue() = this } + } - class FileStorageSink extends Sink { - FileStorageSink() { - exists(CallNode call, AttrNode meth, string name | - any(OpenFile fd).taints(meth.getObject(name)) and - call.getFunction() = meth and - call.getAnArg() = this - | - name = "write" - ) - } + class FileStorageSink extends Sink { + FileStorageSink() { + exists(CallNode call, AttrNode meth, string name | + any(OpenFile fd).taints(meth.getObject(name)) and + call.getFunction() = meth and + call.getAnArg() = this + | + name = "write" + ) } + } } module ClearTextLogging { - abstract class Sink extends TaintSink { - override predicate sinks(TaintKind kind) { kind instanceof SensitiveData } - } + abstract class Sink extends TaintSink { + override predicate sinks(TaintKind kind) { kind instanceof SensitiveData } + } - class PrintSink extends Sink { - PrintSink() { - exists(CallNode call | - call.getAnArg() = this and - call = Value::named("print").getACall() - ) - } + class PrintSink extends Sink { + PrintSink() { + exists(CallNode call | + call.getAnArg() = this and + call = Value::named("print").getACall() + ) } + } - class LoggingSink extends Sink { - LoggingSink() { - exists(CallNode call, AttrNode meth, string name | - call.getFunction() = meth and - meth.getObject(name).(NameNode).getId().matches("logg%") and - call.getAnArg() = this - | - name = "error" or - name = "warn" or - name = "warning" or - name = "debug" or - name = "info" - ) - } + class LoggingSink extends Sink { + LoggingSink() { + exists(CallNode call, AttrNode meth, string name | + call.getFunction() = meth and + meth.getObject(name).(NameNode).getId().matches("logg%") and + call.getAnArg() = this + | + name = "error" or + name = "warn" or + name = "warning" or + name = "debug" or + name = "info" + ) } + } } diff --git a/python/ql/src/semmle/python/security/Crypto.qll b/python/ql/src/semmle/python/security/Crypto.qll index 98ec8ecb2f1..65ec8f13a6e 100644 --- a/python/ql/src/semmle/python/security/Crypto.qll +++ b/python/ql/src/semmle/python/security/Crypto.qll @@ -4,136 +4,136 @@ private import semmle.python.security.SensitiveData private import semmle.crypto.Crypto as CryptoLib abstract class WeakCryptoSink extends TaintSink { - override predicate sinks(TaintKind taint) { taint instanceof SensitiveData } + override predicate sinks(TaintKind taint) { taint instanceof SensitiveData } } /** Modeling the 'pycrypto' package https://github.com/dlitz/pycrypto (latest release 2013) */ module Pycrypto { - ModuleValue cipher(string name) { result = Module::named("Crypto.Cipher").attr(name) } + ModuleValue cipher(string name) { result = Module::named("Crypto.Cipher").attr(name) } - class CipherInstance extends TaintKind { - string name; + class CipherInstance extends TaintKind { + string name; - CipherInstance() { - this = "Crypto.Cipher." + name and - exists(cipher(name)) - } - - string getName() { result = name } - - CryptoLib::CryptographicAlgorithm getAlgorithm() { result.getName() = name } - - predicate isWeak() { this.getAlgorithm().isWeak() } + CipherInstance() { + this = "Crypto.Cipher." + name and + exists(cipher(name)) } - class CipherInstanceSource extends TaintSource { - CipherInstance instance; + string getName() { result = name } - CipherInstanceSource() { - exists(AttrNode attr | - this.(CallNode).getFunction() = attr and - attr.getObject("new").pointsTo(cipher(instance.getName())) - ) - } + CryptoLib::CryptographicAlgorithm getAlgorithm() { result.getName() = name } - override string toString() { result = "Source of " + instance } + predicate isWeak() { this.getAlgorithm().isWeak() } + } - override predicate isSourceOf(TaintKind kind) { kind = instance } + class CipherInstanceSource extends TaintSource { + CipherInstance instance; + + CipherInstanceSource() { + exists(AttrNode attr | + this.(CallNode).getFunction() = attr and + attr.getObject("new").pointsTo(cipher(instance.getName())) + ) } - class PycryptoWeakCryptoSink extends WeakCryptoSink { - string name; + override string toString() { result = "Source of " + instance } - PycryptoWeakCryptoSink() { - exists(CallNode call, AttrNode method, CipherInstance cipher | - call.getAnArg() = this and - call.getFunction() = method and - cipher.taints(method.getObject("encrypt")) and - cipher.isWeak() and - cipher.getName() = name - ) - } + override predicate isSourceOf(TaintKind kind) { kind = instance } + } - override string toString() { result = "Use of weak crypto algorithm " + name } + class PycryptoWeakCryptoSink extends WeakCryptoSink { + string name; + + PycryptoWeakCryptoSink() { + exists(CallNode call, AttrNode method, CipherInstance cipher | + call.getAnArg() = this and + call.getFunction() = method and + cipher.taints(method.getObject("encrypt")) and + cipher.isWeak() and + cipher.getName() = name + ) } + + override string toString() { result = "Use of weak crypto algorithm " + name } + } } module Cryptography { - ModuleValue ciphers() { - result = Module::named("cryptography.hazmat.primitives.ciphers") and - result.isPackage() + ModuleValue ciphers() { + result = Module::named("cryptography.hazmat.primitives.ciphers") and + result.isPackage() + } + + class CipherClass extends ClassValue { + CipherClass() { ciphers().attr("Cipher") = this } + } + + class AlgorithmClass extends ClassValue { + AlgorithmClass() { ciphers().attr("algorithms").attr(_) = this } + + string getAlgorithmName() { result = this.declaredAttribute("name").(StringValue).getText() } + + predicate isWeak() { + exists(CryptoLib::CryptographicAlgorithm algo | + algo.getName() = this.getAlgorithmName() and + algo.isWeak() + ) + } + } + + class CipherInstance extends TaintKind { + AlgorithmClass cls; + + CipherInstance() { this = "cryptography.Cipher." + cls.getAlgorithmName() } + + AlgorithmClass getAlgorithm() { result = cls } + + predicate isWeak() { cls.isWeak() } + + override TaintKind getTaintOfMethodResult(string name) { + name = "encryptor" and + result.(Encryptor).getAlgorithm() = this.getAlgorithm() + } + } + + class CipherSource extends TaintSource { + CipherSource() { this.(CallNode).getFunction().pointsTo(any(CipherClass cls)) } + + override predicate isSourceOf(TaintKind kind) { + this.(CallNode).getArg(0).pointsTo().getClass() = kind.(CipherInstance).getAlgorithm() } - class CipherClass extends ClassValue { - CipherClass() { ciphers().attr("Cipher") = this } + override string toString() { result = "cryptography.Cipher.source" } + } + + class Encryptor extends TaintKind { + AlgorithmClass cls; + + Encryptor() { this = "cryptography.encryptor." + cls.getAlgorithmName() } + + AlgorithmClass getAlgorithm() { result = cls } + } + + class CryptographyWeakCryptoSink extends WeakCryptoSink { + CryptographyWeakCryptoSink() { + exists(CallNode call, AttrNode method, Encryptor encryptor | + call.getAnArg() = this and + call.getFunction() = method and + encryptor.taints(method.getObject("update")) and + encryptor.getAlgorithm().isWeak() + ) } - class AlgorithmClass extends ClassValue { - AlgorithmClass() { ciphers().attr("algorithms").attr(_) = this } - - string getAlgorithmName() { result = this.declaredAttribute("name").(StringValue).getText() } - - predicate isWeak() { - exists(CryptoLib::CryptographicAlgorithm algo | - algo.getName() = this.getAlgorithmName() and - algo.isWeak() - ) - } - } - - class CipherInstance extends TaintKind { - AlgorithmClass cls; - - CipherInstance() { this = "cryptography.Cipher." + cls.getAlgorithmName() } - - AlgorithmClass getAlgorithm() { result = cls } - - predicate isWeak() { cls.isWeak() } - - override TaintKind getTaintOfMethodResult(string name) { - name = "encryptor" and - result.(Encryptor).getAlgorithm() = this.getAlgorithm() - } - } - - class CipherSource extends TaintSource { - CipherSource() { this.(CallNode).getFunction().pointsTo(any(CipherClass cls)) } - - override predicate isSourceOf(TaintKind kind) { - this.(CallNode).getArg(0).pointsTo().getClass() = kind.(CipherInstance).getAlgorithm() - } - - override string toString() { result = "cryptography.Cipher.source" } - } - - class Encryptor extends TaintKind { - AlgorithmClass cls; - - Encryptor() { this = "cryptography.encryptor." + cls.getAlgorithmName() } - - AlgorithmClass getAlgorithm() { result = cls } - } - - class CryptographyWeakCryptoSink extends WeakCryptoSink { - CryptographyWeakCryptoSink() { - exists(CallNode call, AttrNode method, Encryptor encryptor | - call.getAnArg() = this and - call.getFunction() = method and - encryptor.taints(method.getObject("update")) and - encryptor.getAlgorithm().isWeak() - ) - } - - override string toString() { result = "Use of weak crypto algorithm" } - } + override string toString() { result = "Use of weak crypto algorithm" } + } } private class CipherConfig extends TaintTracking::Configuration { - CipherConfig() { this = "Crypto cipher config" } + CipherConfig() { this = "Crypto cipher config" } - override predicate isSource(TaintTracking::Source source) { - source instanceof Pycrypto::CipherInstanceSource - or - source instanceof Cryptography::CipherSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof Pycrypto::CipherInstanceSource + or + source instanceof Cryptography::CipherSource + } } diff --git a/python/ql/src/semmle/python/security/Exceptions.qll b/python/ql/src/semmle/python/security/Exceptions.qll index 4288761c565..25b0592c931 100644 --- a/python/ql/src/semmle/python/security/Exceptions.qll +++ b/python/ql/src/semmle/python/security/Exceptions.qll @@ -14,9 +14,9 @@ private Value traceback_function(string name) { result = Module::named("tracebac * message, arguments or parts of the exception traceback. */ class ExceptionInfo extends StringKind { - ExceptionInfo() { this = "exception.info" } + ExceptionInfo() { this = "exception.info" } - override string repr() { result = "exception info" } + override string repr() { result = "exception info" } } /** @@ -29,15 +29,15 @@ abstract class ErrorInfoSource extends TaintSource { } * This kind represents exceptions themselves. */ class ExceptionKind extends TaintKind { - ExceptionKind() { this = "exception.kind" } + ExceptionKind() { this = "exception.kind" } - override string repr() { result = "exception" } + override string repr() { result = "exception" } - override TaintKind getTaintOfAttribute(string name) { - name = "args" and result instanceof ExceptionInfoSequence - or - name = "message" and result instanceof ExceptionInfo - } + override TaintKind getTaintOfAttribute(string name) { + name = "args" and result instanceof ExceptionInfoSequence + or + name = "message" and result instanceof ExceptionInfo + } } /** @@ -45,18 +45,18 @@ class ExceptionKind extends TaintKind { * `except` statement. */ class ExceptionSource extends ErrorInfoSource { - ExceptionSource() { - exists(ClassValue cls | - cls.getASuperType() = ClassValue::baseException() and - this.(ControlFlowNode).pointsTo().getClass() = cls - ) - or - this = any(ExceptStmt s).getName().getAFlowNode() - } + ExceptionSource() { + exists(ClassValue cls | + cls.getASuperType() = ClassValue::baseException() and + this.(ControlFlowNode).pointsTo().getClass() = cls + ) + or + this = any(ExceptStmt s).getName().getAFlowNode() + } - override string toString() { result = "exception.source" } + override string toString() { result = "exception.source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionKind } } /** @@ -64,7 +64,7 @@ class ExceptionSource extends ErrorInfoSource { * for instance the contents of the `args` attribute, or the stack trace. */ class ExceptionInfoSequence extends SequenceKind { - ExceptionInfoSequence() { this.getItem() instanceof ExceptionInfo } + ExceptionInfoSequence() { this.getItem() instanceof ExceptionInfo } } /** @@ -72,23 +72,23 @@ class ExceptionInfoSequence extends SequenceKind { * sequences of exception information. */ class CallToTracebackFunction extends ErrorInfoSource { - CallToTracebackFunction() { - exists(string name | - name = "extract_tb" or - name = "extract_stack" or - name = "format_list" or - name = "format_exception_only" or - name = "format_exception" or - name = "format_tb" or - name = "format_stack" - | - this = traceback_function(name).getACall() - ) - } + CallToTracebackFunction() { + exists(string name | + name = "extract_tb" or + name = "extract_stack" or + name = "format_list" or + name = "format_exception_only" or + name = "format_exception" or + name = "format_tb" or + name = "format_stack" + | + this = traceback_function(name).getACall() + ) + } - override string toString() { result = "exception.info.sequence.source" } + override string toString() { result = "exception.info.sequence.source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfoSequence } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfoSequence } } /** @@ -96,9 +96,9 @@ class CallToTracebackFunction extends ErrorInfoSource { * string of information about an exception. */ class FormattedTracebackSource extends ErrorInfoSource { - FormattedTracebackSource() { this = traceback_function("format_exc").getACall() } + FormattedTracebackSource() { this = traceback_function("format_exc").getACall() } - override string toString() { result = "exception.info.source" } + override string toString() { result = "exception.info.source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfo } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfo } } diff --git a/python/ql/src/semmle/python/security/Paths.qll b/python/ql/src/semmle/python/security/Paths.qll index 3c7bc657948..8f7cba4b969 100644 --- a/python/ql/src/semmle/python/security/Paths.qll +++ b/python/ql/src/semmle/python/security/Paths.qll @@ -1,16 +1,16 @@ import semmle.python.dataflow.Implementation module TaintTrackingPaths { - predicate edge(TaintTrackingNode src, TaintTrackingNode dest, string label) { - exists(TaintTrackingNode source, TaintTrackingNode sink | - source.getConfiguration().hasFlowPath(source, sink) and - source.getASuccessor*() = src and - src.getASuccessor(label) = dest and - dest.getASuccessor*() = sink - ) - } + predicate edge(TaintTrackingNode src, TaintTrackingNode dest, string label) { + exists(TaintTrackingNode source, TaintTrackingNode sink | + source.getConfiguration().hasFlowPath(source, sink) and + source.getASuccessor*() = src and + src.getASuccessor(label) = dest and + dest.getASuccessor*() = sink + ) + } } query predicate edges(TaintTrackingNode fromnode, TaintTrackingNode tonode) { - TaintTrackingPaths::edge(fromnode, tonode, _) + TaintTrackingPaths::edge(fromnode, tonode, _) } diff --git a/python/ql/src/semmle/python/security/SensitiveData.qll b/python/ql/src/semmle/python/security/SensitiveData.qll index 18e52423d19..b616c960940 100644 --- a/python/ql/src/semmle/python/security/SensitiveData.qll +++ b/python/ql/src/semmle/python/security/SensitiveData.qll @@ -20,143 +20,143 @@ import semmle.python.web.HttpRequest * This is copied from the javascript library, but should be language independent. */ private module HeuristicNames { - /** - * Gets a regular expression that identifies strings that may indicate the presence of secret - * or trusted data. - */ - string maybeSecret() { result = "(?is).*((? 0)) - or - this = call.getArgByName(any(string s | not s = "task")) - ) - } + FabricExecuteExtension() { + call = Value::named("fabric.api.execute").getACall() and + ( + this = call.getArg(any(int i | i > 0)) + or + this = call.getArgByName(any(string s | not s = "task")) + ) + } - override ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { - tokind = fromkind and - exists(CallableValue func | - ( - call.getArg(0).pointsTo(func) - or - call.getArgByName("task").pointsTo(func) - ) and - exists(int i | - // execute(func, arg0, arg1) => func(arg0, arg1) - this = call.getArg(i) and - result = func.getParameter(i - 1) - ) - or - exists(string name | - this = call.getArgByName(name) and - result = func.getParameterByName(name) - ) - ) - } + override ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { + tokind = fromkind and + exists(CallableValue func | + ( + call.getArg(0).pointsTo(func) + or + call.getArgByName("task").pointsTo(func) + ) and + exists(int i | + // execute(func, arg0, arg1) => func(arg0, arg1) + this = call.getArg(i) and + result = func.getParameter(i - 1) + ) + or + exists(string name | + this = call.getArgByName(name) and + result = func.getParameterByName(name) + ) + ) + } } diff --git a/python/ql/src/semmle/python/security/injection/Deserialization.qll b/python/ql/src/semmle/python/security/injection/Deserialization.qll index 1f73ede22f2..029705cd807 100644 --- a/python/ql/src/semmle/python/security/injection/Deserialization.qll +++ b/python/ql/src/semmle/python/security/injection/Deserialization.qll @@ -3,6 +3,6 @@ import semmle.python.dataflow.TaintTracking /** `pickle.loads(untrusted)` vulnerability. */ abstract class DeserializationSink extends TaintSink { - bindingset[this] - DeserializationSink() { this = this } + bindingset[this] + DeserializationSink() { this = this } } diff --git a/python/ql/src/semmle/python/security/injection/Exec.qll b/python/ql/src/semmle/python/security/injection/Exec.qll index 462847e7d3e..b5008a94e3b 100644 --- a/python/ql/src/semmle/python/security/injection/Exec.qll +++ b/python/ql/src/semmle/python/security/injection/Exec.qll @@ -15,15 +15,15 @@ import semmle.python.security.strings.Untrusted * The `vuln` in `exec(vuln)` or similar. */ class StringEvaluationNode extends TaintSink { - override string toString() { result = "exec or eval" } + override string toString() { result = "exec or eval" } - StringEvaluationNode() { - exists(Exec exec | exec.getASubExpression().getAFlowNode() = this) - or - Value::named("exec").getACall().getAnArg() = this - or - Value::named("eval").getACall().getAnArg() = this - } + StringEvaluationNode() { + exists(Exec exec | exec.getASubExpression().getAFlowNode() = this) + or + Value::named("exec").getACall().getAnArg() = this + or + Value::named("eval").getACall().getAnArg() = this + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } diff --git a/python/ql/src/semmle/python/security/injection/Marshal.qll b/python/ql/src/semmle/python/security/injection/Marshal.qll index 7ae77e597a5..a77c7cd6278 100644 --- a/python/ql/src/semmle/python/security/injection/Marshal.qll +++ b/python/ql/src/semmle/python/security/injection/Marshal.qll @@ -18,14 +18,14 @@ private FunctionObject marshalLoads() { result = ModuleObject::named("marshal"). * The `vuln` in `marshal.loads(vuln)`. */ class UnmarshalingNode extends DeserializationSink { - override string toString() { result = "unmarshaling vulnerability" } + override string toString() { result = "unmarshaling vulnerability" } - UnmarshalingNode() { - exists(CallNode call | - marshalLoads().getACall() = call and - call.getAnArg() = this - ) - } + UnmarshalingNode() { + exists(CallNode call | + marshalLoads().getACall() = call and + call.getAnArg() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } diff --git a/python/ql/src/semmle/python/security/injection/Path.qll b/python/ql/src/semmle/python/security/injection/Path.qll index e871d11cf2b..ee470932749 100644 --- a/python/ql/src/semmle/python/security/injection/Path.qll +++ b/python/ql/src/semmle/python/security/injection/Path.qll @@ -7,52 +7,52 @@ import semmle.python.security.strings.Untrusted * NormalizedPath below handles that case. */ class PathSanitizer extends Sanitizer { - PathSanitizer() { this = "path.sanitizer" } + PathSanitizer() { this = "path.sanitizer" } - override predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { - taint instanceof ExternalStringKind and - abspath_call(node, _) - } + override predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { + taint instanceof ExternalStringKind and + abspath_call(node, _) + } } private FunctionObject abspath() { - exists(ModuleObject os_path | ModuleObject::named("os").attr("path") = os_path | - os_path.attr("abspath") = result - or - os_path.attr("normpath") = result - ) + exists(ModuleObject os_path | ModuleObject::named("os").attr("path") = os_path | + os_path.attr("abspath") = result + or + os_path.attr("normpath") = result + ) } /** A path that has been normalized, but not verified to be safe */ class NormalizedPath extends TaintKind { - NormalizedPath() { this = "normalized.path.injection" } + NormalizedPath() { this = "normalized.path.injection" } - override string repr() { result = "normalized path" } + override string repr() { result = "normalized path" } } private predicate abspath_call(CallNode call, ControlFlowNode arg) { - call.getFunction().refersTo(abspath()) and - arg = call.getArg(0) + call.getFunction().refersTo(abspath()) and + arg = call.getArg(0) } class AbsPath extends DataFlowExtension::DataFlowNode { - AbsPath() { abspath_call(_, this) } + AbsPath() { abspath_call(_, this) } - override ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { - abspath_call(result, this) and - tokind instanceof NormalizedPath and - fromkind instanceof ExternalStringKind - } + override ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { + abspath_call(result, this) and + tokind instanceof NormalizedPath and + fromkind instanceof ExternalStringKind + } } class NormalizedPathSanitizer extends Sanitizer { - NormalizedPathSanitizer() { this = "normalized.path.sanitizer" } + NormalizedPathSanitizer() { this = "normalized.path.sanitizer" } - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - taint instanceof NormalizedPath and - test.getTest().(CallNode).getFunction().(AttrNode).getName() = "startswith" and - test.getSense() = true - } + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + taint instanceof NormalizedPath and + test.getTest().(CallNode).getFunction().(AttrNode).getName() = "startswith" and + test.getSense() = true + } } /** @@ -60,22 +60,22 @@ class NormalizedPathSanitizer extends Sanitizer { * The `vuln` in `open(vuln)` and similar. */ class OpenNode extends TaintSink { - override string toString() { result = "argument to open()" } + override string toString() { result = "argument to open()" } - OpenNode() { - exists(CallNode call | - call = Value::named("open").getACall() and - ( - call.getArg(0) = this - or - call.getArgByName("file") = this - ) - ) - } - - override predicate sinks(TaintKind kind) { - kind instanceof ExternalStringKind + OpenNode() { + exists(CallNode call | + call = Value::named("open").getACall() and + ( + call.getArg(0) = this or - kind instanceof NormalizedPath - } + call.getArgByName("file") = this + ) + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof ExternalStringKind + or + kind instanceof NormalizedPath + } } diff --git a/python/ql/src/semmle/python/security/injection/Pickle.qll b/python/ql/src/semmle/python/security/injection/Pickle.qll index 1135587df50..f668c7011fe 100644 --- a/python/ql/src/semmle/python/security/injection/Pickle.qll +++ b/python/ql/src/semmle/python/security/injection/Pickle.qll @@ -12,25 +12,25 @@ import semmle.python.security.strings.Untrusted import semmle.python.security.injection.Deserialization private ModuleObject pickleModule() { - result.getName() = "pickle" - or - result.getName() = "cPickle" - or - result.getName() = "dill" + result.getName() = "pickle" + or + result.getName() = "cPickle" + or + result.getName() = "dill" } private FunctionObject pickleLoads() { result = pickleModule().attr("loads") } /** `pickle.loads(untrusted)` vulnerability. */ class UnpicklingNode extends DeserializationSink { - override string toString() { result = "unpickling untrusted data" } + override string toString() { result = "unpickling untrusted data" } - UnpicklingNode() { - exists(CallNode call | - pickleLoads().getACall() = call and - call.getAnArg() = this - ) - } + UnpicklingNode() { + exists(CallNode call | + pickleLoads().getACall() = call and + call.getAnArg() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } diff --git a/python/ql/src/semmle/python/security/injection/Sql.qll b/python/ql/src/semmle/python/security/injection/Sql.qll index 7fc9515c08b..5ded218fc9e 100644 --- a/python/ql/src/semmle/python/security/injection/Sql.qll +++ b/python/ql/src/semmle/python/security/injection/Sql.qll @@ -12,27 +12,27 @@ import semmle.python.security.strings.Untrusted import semmle.python.security.SQL private StringObject first_part(ControlFlowNode command) { - command.(BinaryExprNode).getOp() instanceof Add and - command.(BinaryExprNode).getLeft().refersTo(result) - or - exists(CallNode call, SequenceObject seq | call = command | - call = theStrType().lookupAttribute("join") and - call.getArg(0).refersTo(seq) and - seq.getInferredElement(0) = result - ) - or - command.(BinaryExprNode).getOp() instanceof Mod and - command.getNode().(StrConst).getLiteralObject() = result + command.(BinaryExprNode).getOp() instanceof Add and + command.(BinaryExprNode).getLeft().refersTo(result) + or + exists(CallNode call, SequenceObject seq | call = command | + call = theStrType().lookupAttribute("join") and + call.getArg(0).refersTo(seq) and + seq.getInferredElement(0) = result + ) + or + command.(BinaryExprNode).getOp() instanceof Mod and + command.getNode().(StrConst).getLiteralObject() = result } /** Holds if `command` appears to be a SQL command string of which `inject` is a part. */ predicate probable_sql_command(ControlFlowNode command, ControlFlowNode inject) { - exists(string prefix | - inject = command.getAChild*() and - first_part(command).getText().regexpMatch(" *" + prefix + ".*") - | - prefix = "CREATE" or prefix = "SELECT" - ) + exists(string prefix | + inject = command.getAChild*() and + first_part(command).getText().regexpMatch(" *" + prefix + ".*") + | + prefix = "CREATE" or prefix = "SELECT" + ) } /** @@ -40,10 +40,10 @@ predicate probable_sql_command(ControlFlowNode command, ControlFlowNode inject) * This will be overridden to provide specific kinds of DB cursor. */ abstract class DbCursor extends TaintKind { - bindingset[this] - DbCursor() { any() } + bindingset[this] + DbCursor() { any() } - string getExecuteMethodName() { result = "execute" } + string getExecuteMethodName() { result = "execute" } } /** @@ -51,11 +51,11 @@ abstract class DbCursor extends TaintKind { * vulnerable to malicious input. */ class SimpleSqlStringInjection extends SqlInjectionSink { - override string toString() { result = "simple SQL string injection" } + override string toString() { result = "simple SQL string injection" } - SimpleSqlStringInjection() { probable_sql_command(_, this) } + SimpleSqlStringInjection() { probable_sql_command(_, this) } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } /** @@ -69,15 +69,15 @@ abstract class DbConnectionSource extends TaintSource { } * The `vuln` in `db.connection.execute(vuln)` and similar. */ class DbConnectionExecuteArgument extends SqlInjectionSink { - override string toString() { result = "db.connection.execute" } + override string toString() { result = "db.connection.execute" } - DbConnectionExecuteArgument() { - exists(CallNode call, DbCursor cursor, string name | - cursor.taints(call.getFunction().(AttrNode).getObject(name)) and - cursor.getExecuteMethodName() = name and - call.getArg(0) = this - ) - } + DbConnectionExecuteArgument() { + exists(CallNode call, DbCursor cursor, string name | + cursor.taints(call.getFunction().(AttrNode).getObject(name)) and + cursor.getExecuteMethodName() = name and + call.getArg(0) = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } diff --git a/python/ql/src/semmle/python/security/injection/Xml.qll b/python/ql/src/semmle/python/security/injection/Xml.qll index 3a4e6ebc552..07241096b44 100644 --- a/python/ql/src/semmle/python/security/injection/Xml.qll +++ b/python/ql/src/semmle/python/security/injection/Xml.qll @@ -20,34 +20,34 @@ private ModuleObject xmlPullDomModule() { result.getName() = "xml.dom.pulldom" } private ModuleObject xmlSaxModule() { result.getName() = "xml.sax" } private class ExpatParser extends TaintKind { - ExpatParser() { this = "expat.parser" } + ExpatParser() { this = "expat.parser" } } private FunctionObject expatCreateParseFunction() { - result = ModuleObject::named("xml.parsers.expat").attr("ParserCreate") + result = ModuleObject::named("xml.parsers.expat").attr("ParserCreate") } private class ExpatCreateParser extends TaintSource { - ExpatCreateParser() { expatCreateParseFunction().getACall() = this } + ExpatCreateParser() { expatCreateParseFunction().getACall() = this } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExpatParser } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExpatParser } - string toString() { result = "expat.create.parser" } + string toString() { result = "expat.create.parser" } } private FunctionObject xmlFromString() { - result = xmlElementTreeModule().attr("fromstring") - or - result = xmlMiniDomModule().attr("parseString") - or - result = xmlPullDomModule().attr("parseString") - or - result = xmlSaxModule().attr("parseString") + result = xmlElementTreeModule().attr("fromstring") + or + result = xmlMiniDomModule().attr("parseString") + or + result = xmlPullDomModule().attr("parseString") + or + result = xmlSaxModule().attr("parseString") } /** A (potentially) malicious XML string. */ class ExternalXmlString extends ExternalStringKind { - ExternalXmlString() { this = "external xml encoded object" } + ExternalXmlString() { this = "external xml encoded object" } } /** @@ -55,14 +55,14 @@ class ExternalXmlString extends ExternalStringKind { * specially crafted XML string. */ class XmlLoadNode extends DeserializationSink { - override string toString() { result = "xml.load vulnerability" } + override string toString() { result = "xml.load vulnerability" } - XmlLoadNode() { - exists(CallNode call | call.getAnArg() = this | - xmlFromString().getACall() = call or - any(ExpatParser parser).taints(call.getFunction().(AttrNode).getObject("Parse")) - ) - } + XmlLoadNode() { + exists(CallNode call | call.getAnArg() = this | + xmlFromString().getACall() = call or + any(ExpatParser parser).taints(call.getFunction().(AttrNode).getObject("Parse")) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalXmlString } + override predicate sinks(TaintKind kind) { kind instanceof ExternalXmlString } } diff --git a/python/ql/src/semmle/python/security/injection/Yaml.qll b/python/ql/src/semmle/python/security/injection/Yaml.qll index 0799d9b9160..f8f92fff609 100644 --- a/python/ql/src/semmle/python/security/injection/Yaml.qll +++ b/python/ql/src/semmle/python/security/injection/Yaml.qll @@ -15,14 +15,14 @@ private FunctionObject yamlLoad() { result = ModuleObject::named("yaml").attr("l /** `yaml.load(untrusted)` vulnerability. */ class YamlLoadNode extends DeserializationSink { - override string toString() { result = "yaml.load vulnerability" } + override string toString() { result = "yaml.load vulnerability" } - YamlLoadNode() { - exists(CallNode call | - yamlLoad().getACall() = call and - call.getAnArg() = this - ) - } + YamlLoadNode() { + exists(CallNode call | + yamlLoad().getACall() = call and + call.getAnArg() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } diff --git a/python/ql/src/semmle/python/security/strings/Basic.qll b/python/ql/src/semmle/python/security/strings/Basic.qll index 9fb17b074a6..345f7d0b82b 100755 --- a/python/ql/src/semmle/python/security/strings/Basic.qll +++ b/python/ql/src/semmle/python/security/strings/Basic.qll @@ -4,107 +4,107 @@ import semmle.python.dataflow.TaintTracking /** An extensible kind of taint representing any kind of string. */ abstract class StringKind extends TaintKind { - bindingset[this] - StringKind() { this = this } + bindingset[this] + StringKind() { this = this } - override TaintKind getTaintOfMethodResult(string name) { - name in ["capitalize", "casefold", "center", "expandtabs", "format", "format_map", "ljust", - "lstrip", "lower", "replace", "rjust", "rstrip", "strip", "swapcase", "title", "upper", - "zfill", - /* encode/decode is technically not correct, but close enough */ - "encode", "decode"] and - result = this - or - name in ["partition", "rpartition", "rsplit", "split", "splitlines"] and - result.(SequenceKind).getItem() = this - } + override TaintKind getTaintOfMethodResult(string name) { + name in ["capitalize", "casefold", "center", "expandtabs", "format", "format_map", "ljust", + "lstrip", "lower", "replace", "rjust", "rstrip", "strip", "swapcase", "title", "upper", + "zfill", + /* encode/decode is technically not correct, but close enough */ + "encode", "decode"] and + result = this + or + name in ["partition", "rpartition", "rsplit", "split", "splitlines"] and + result.(SequenceKind).getItem() = this + } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - result = this and - ( - slice(fromnode, tonode) or - tonode.(BinaryExprNode).getAnOperand() = fromnode or - os_path_join(fromnode, tonode) or - str_format(fromnode, tonode) or - encode_decode(fromnode, tonode) or - to_str(fromnode, tonode) - ) - or - result = this and copy_call(fromnode, tonode) - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + result = this and + ( + slice(fromnode, tonode) or + tonode.(BinaryExprNode).getAnOperand() = fromnode or + os_path_join(fromnode, tonode) or + str_format(fromnode, tonode) or + encode_decode(fromnode, tonode) or + to_str(fromnode, tonode) + ) + or + result = this and copy_call(fromnode, tonode) + } - override ClassValue getType() { - result = Value::named("bytes") or - result = Value::named("str") or - result = Value::named("unicode") - } + override ClassValue getType() { + result = Value::named("bytes") or + result = Value::named("str") or + result = Value::named("unicode") + } } private class StringEqualitySanitizer extends Sanitizer { - StringEqualitySanitizer() { this = "string equality sanitizer" } + StringEqualitySanitizer() { this = "string equality sanitizer" } - /** The test `if untrusted == "KNOWN_VALUE":` sanitizes `untrusted` on its `true` edge. */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - taint instanceof StringKind and - exists(ControlFlowNode const, Cmpop op | const.getNode() instanceof StrConst | - ( - test.getTest().(CompareNode).operands(const, op, _) - or - test.getTest().(CompareNode).operands(_, op, const) - ) and - ( - op instanceof Eq and test.getSense() = true - or - op instanceof NotEq and test.getSense() = false - ) - ) - } + /** The test `if untrusted == "KNOWN_VALUE":` sanitizes `untrusted` on its `true` edge. */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + taint instanceof StringKind and + exists(ControlFlowNode const, Cmpop op | const.getNode() instanceof StrConst | + ( + test.getTest().(CompareNode).operands(const, op, _) + or + test.getTest().(CompareNode).operands(_, op, const) + ) and + ( + op instanceof Eq and test.getSense() = true + or + op instanceof NotEq and test.getSense() = false + ) + ) + } } /* tonode = ....format(fromnode) */ private predicate str_format(ControlFlowNode fromnode, CallNode tonode) { - tonode.getFunction().(AttrNode).getName() = "format" and - tonode.getAnArg() = fromnode + tonode.getFunction().(AttrNode).getName() = "format" and + tonode.getAnArg() = fromnode } /* tonode = codec.[en|de]code(fromnode)*/ private predicate encode_decode(ControlFlowNode fromnode, CallNode tonode) { - exists(FunctionObject func, string name | - not func.getFunction().isMethod() and - func.getACall() = tonode and - tonode.getAnArg() = fromnode and - func.getName() = name - | - name = "encode" or - name = "decode" or - name = "decodestring" - ) + exists(FunctionObject func, string name | + not func.getFunction().isMethod() and + func.getACall() = tonode and + tonode.getAnArg() = fromnode and + func.getName() = name + | + name = "encode" or + name = "decode" or + name = "decodestring" + ) } /* tonode = str(fromnode)*/ private predicate to_str(ControlFlowNode fromnode, CallNode tonode) { - tonode.getAnArg() = fromnode and - ( - tonode = ClassValue::bytes().getACall() - or - tonode = ClassValue::unicode().getACall() - ) + tonode.getAnArg() = fromnode and + ( + tonode = ClassValue::bytes().getACall() + or + tonode = ClassValue::unicode().getACall() + ) } /* tonode = fromnode[:] */ private predicate slice(ControlFlowNode fromnode, SubscriptNode tonode) { - exists(Slice all | - all = tonode.getIndex().getNode() and - not exists(all.getStart()) and - not exists(all.getStop()) and - tonode.getObject() = fromnode - ) + exists(Slice all | + all = tonode.getIndex().getNode() and + not exists(all.getStart()) and + not exists(all.getStop()) and + tonode.getObject() = fromnode + ) } /* tonode = os.path.join(..., fromnode, ...) */ private predicate os_path_join(ControlFlowNode fromnode, CallNode tonode) { - tonode = Value::named("os.path.join").getACall() and - tonode.getAnArg() = fromnode + tonode = Value::named("os.path.join").getACall() and + tonode.getAnArg() = fromnode } /** @@ -113,5 +113,5 @@ private predicate os_path_join(ControlFlowNode fromnode, CallNode tonode) { * DEPRECATED: Use `ExternalStringDictKind` instead. */ deprecated class StringDictKind extends DictKind { - StringDictKind() { this.getValue() instanceof StringKind } + StringDictKind() { this.getValue() instanceof StringKind } } diff --git a/python/ql/src/semmle/python/security/strings/Common.qll b/python/ql/src/semmle/python/security/strings/Common.qll index 404da271d78..bb0af8b8aec 100644 --- a/python/ql/src/semmle/python/security/strings/Common.qll +++ b/python/ql/src/semmle/python/security/strings/Common.qll @@ -2,13 +2,13 @@ import python /* A call that returns a copy (or similar) of the argument */ predicate copy_call(ControlFlowNode fromnode, CallNode tonode) { - tonode.getFunction().(AttrNode).getObject("copy") = fromnode - or - exists(ModuleValue copy, string name | name = "copy" or name = "deepcopy" | - copy.attr(name).(FunctionValue).getACall() = tonode and - tonode.getArg(0) = fromnode - ) - or - tonode.getFunction().pointsTo(Value::named("reversed")) and + tonode.getFunction().(AttrNode).getObject("copy") = fromnode + or + exists(ModuleValue copy, string name | name = "copy" or name = "deepcopy" | + copy.attr(name).(FunctionValue).getACall() = tonode and tonode.getArg(0) = fromnode + ) + or + tonode.getFunction().pointsTo(Value::named("reversed")) and + tonode.getArg(0) = fromnode } diff --git a/python/ql/src/semmle/python/security/strings/External.qll b/python/ql/src/semmle/python/security/strings/External.qll index 59c4a7373f0..76a74a66e9b 100644 --- a/python/ql/src/semmle/python/security/strings/External.qll +++ b/python/ql/src/semmle/python/security/strings/External.qll @@ -6,32 +6,32 @@ private import Common * An extensible kind of taint representing an externally controlled string. */ abstract class ExternalStringKind extends StringKind { - bindingset[this] - ExternalStringKind() { this = this } + bindingset[this] + ExternalStringKind() { this = this } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - result = StringKind.super.getTaintForFlowStep(fromnode, tonode) - or - tonode.(SequenceNode).getElement(_) = fromnode and - result.(ExternalStringSequenceKind).getItem() = this - or - json_load(fromnode, tonode) and result.(ExternalJsonKind).getValue() = this - or - tonode.(DictNode).getAValue() = fromnode and result.(ExternalStringDictKind).getValue() = this - or - urlsplit(fromnode, tonode) and result.(ExternalUrlSplitResult).getItem() = this - or - urlparse(fromnode, tonode) and result.(ExternalUrlParseResult).getItem() = this - or - parse_qs(fromnode, tonode) and result.(ExternalStringDictKind).getValue() = this - or - parse_qsl(fromnode, tonode) and result.(SequenceKind).getItem().(SequenceKind).getItem() = this - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + result = StringKind.super.getTaintForFlowStep(fromnode, tonode) + or + tonode.(SequenceNode).getElement(_) = fromnode and + result.(ExternalStringSequenceKind).getItem() = this + or + json_load(fromnode, tonode) and result.(ExternalJsonKind).getValue() = this + or + tonode.(DictNode).getAValue() = fromnode and result.(ExternalStringDictKind).getValue() = this + or + urlsplit(fromnode, tonode) and result.(ExternalUrlSplitResult).getItem() = this + or + urlparse(fromnode, tonode) and result.(ExternalUrlParseResult).getItem() = this + or + parse_qs(fromnode, tonode) and result.(ExternalStringDictKind).getValue() = this + or + parse_qsl(fromnode, tonode) and result.(SequenceKind).getItem().(SequenceKind).getItem() = this + } } /** A kind of "taint", representing a sequence, with a "taint" member */ class ExternalStringSequenceKind extends SequenceKind { - ExternalStringSequenceKind() { this.getItem() instanceof ExternalStringKind } + ExternalStringSequenceKind() { this.getItem() instanceof ExternalStringKind } } /** @@ -39,30 +39,30 @@ class ExternalStringSequenceKind extends SequenceKind { * This is typically a parsed JSON object. */ class ExternalJsonKind extends TaintKind { - ExternalJsonKind() { this = "json[" + any(ExternalStringKind key) + "]" } + ExternalJsonKind() { this = "json[" + any(ExternalStringKind key) + "]" } - /** Gets the taint kind for item in this sequence */ - TaintKind getValue() { - this = "json[" + result + "]" - or - result = this - } + /** Gets the taint kind for item in this sequence */ + TaintKind getValue() { + this = "json[" + result + "]" + or + result = this + } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - this.taints(fromnode) and - json_subscript_taint(tonode, fromnode, this, result) - or - result = this and copy_call(fromnode, tonode) - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + this.taints(fromnode) and + json_subscript_taint(tonode, fromnode, this, result) + or + result = this and copy_call(fromnode, tonode) + } - override TaintKind getTaintOfMethodResult(string name) { - name = "get" and result = this.getValue() - } + override TaintKind getTaintOfMethodResult(string name) { + name = "get" and result = this.getValue() + } } /** A kind of "taint", representing a dictionary mapping keys to tainted strings. */ class ExternalStringDictKind extends DictKind { - ExternalStringDictKind() { this.getValue() instanceof ExternalStringKind } + ExternalStringDictKind() { this.getValue() instanceof ExternalStringKind } } /** @@ -70,189 +70,189 @@ class ExternalStringDictKind extends DictKind { * tainted strings. */ class ExternalStringSequenceDictKind extends DictKind { - ExternalStringSequenceDictKind() { this.getValue() instanceof ExternalStringSequenceKind } + ExternalStringSequenceDictKind() { this.getValue() instanceof ExternalStringSequenceKind } } /** TaintKind for the result of `urlsplit(tainted_string)` */ class ExternalUrlSplitResult extends ExternalStringSequenceKind { - // https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlsplit - override TaintKind getTaintOfAttribute(string name) { - result = super.getTaintOfAttribute(name) - or - ( - // namedtuple field names - name = "scheme" or - name = "netloc" or - name = "path" or - name = "query" or - name = "fragment" or - // class methods - name = "username" or - name = "password" or - name = "hostname" - ) and - result instanceof ExternalStringKind - } + // https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlsplit + override TaintKind getTaintOfAttribute(string name) { + result = super.getTaintOfAttribute(name) + or + ( + // namedtuple field names + name = "scheme" or + name = "netloc" or + name = "path" or + name = "query" or + name = "fragment" or + // class methods + name = "username" or + name = "password" or + name = "hostname" + ) and + result instanceof ExternalStringKind + } - override TaintKind getTaintOfMethodResult(string name) { - result = super.getTaintOfMethodResult(name) - or - name = "geturl" and - result instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + result = super.getTaintOfMethodResult(name) + or + name = "geturl" and + result instanceof ExternalStringKind + } } /** TaintKind for the result of `urlparse(tainted_string)` */ class ExternalUrlParseResult extends ExternalStringSequenceKind { - // https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlparse - override TaintKind getTaintOfAttribute(string name) { - result = super.getTaintOfAttribute(name) - or - ( - // namedtuple field names - name = "scheme" or - name = "netloc" or - name = "path" or - name = "params" or - name = "query" or - name = "fragment" or - // class methods - name = "username" or - name = "password" or - name = "hostname" - ) and - result instanceof ExternalStringKind - } + // https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlparse + override TaintKind getTaintOfAttribute(string name) { + result = super.getTaintOfAttribute(name) + or + ( + // namedtuple field names + name = "scheme" or + name = "netloc" or + name = "path" or + name = "params" or + name = "query" or + name = "fragment" or + // class methods + name = "username" or + name = "password" or + name = "hostname" + ) and + result instanceof ExternalStringKind + } - override TaintKind getTaintOfMethodResult(string name) { - result = super.getTaintOfMethodResult(name) - or - name = "geturl" and - result instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + result = super.getTaintOfMethodResult(name) + or + name = "geturl" and + result instanceof ExternalStringKind + } } /* Helper for getTaintForStep() */ pragma[noinline] private predicate json_subscript_taint( - SubscriptNode sub, ControlFlowNode obj, ExternalJsonKind seq, TaintKind key + SubscriptNode sub, ControlFlowNode obj, ExternalJsonKind seq, TaintKind key ) { - sub.isLoad() and - sub.getObject() = obj and - key = seq.getValue() + sub.isLoad() and + sub.getObject() = obj and + key = seq.getValue() } private predicate json_load(ControlFlowNode fromnode, CallNode tonode) { - tonode = Value::named("json.loads").getACall() and - tonode.getArg(0) = fromnode + tonode = Value::named("json.loads").getACall() and + tonode.getArg(0) = fromnode } private predicate urlsplit(ControlFlowNode fromnode, CallNode tonode) { - // This could be implemented as `exists(FunctionValue` without the explicit six part, - // but then our tests will need to import +100 modules, so for now this slightly - // altered version gets to live on. - exists(Value urlsplit | - ( - urlsplit = Value::named("six.moves.urllib.parse.urlsplit") - or - // Python 2 - urlsplit = Value::named("urlparse.urlsplit") - or - // Python 3 - urlsplit = Value::named("urllib.parse.urlsplit") - ) and - tonode = urlsplit.getACall() and - tonode.getArg(0) = fromnode - ) + // This could be implemented as `exists(FunctionValue` without the explicit six part, + // but then our tests will need to import +100 modules, so for now this slightly + // altered version gets to live on. + exists(Value urlsplit | + ( + urlsplit = Value::named("six.moves.urllib.parse.urlsplit") + or + // Python 2 + urlsplit = Value::named("urlparse.urlsplit") + or + // Python 3 + urlsplit = Value::named("urllib.parse.urlsplit") + ) and + tonode = urlsplit.getACall() and + tonode.getArg(0) = fromnode + ) } private predicate urlparse(ControlFlowNode fromnode, CallNode tonode) { - // This could be implemented as `exists(FunctionValue` without the explicit six part, - // but then our tests will need to import +100 modules, so for now this slightly - // altered version gets to live on. - exists(Value urlparse | - ( - urlparse = Value::named("six.moves.urllib.parse.urlparse") - or - // Python 2 - urlparse = Value::named("urlparse.urlparse") - or - // Python 3 - urlparse = Value::named("urllib.parse.urlparse") - ) and - tonode = urlparse.getACall() and - tonode.getArg(0) = fromnode - ) + // This could be implemented as `exists(FunctionValue` without the explicit six part, + // but then our tests will need to import +100 modules, so for now this slightly + // altered version gets to live on. + exists(Value urlparse | + ( + urlparse = Value::named("six.moves.urllib.parse.urlparse") + or + // Python 2 + urlparse = Value::named("urlparse.urlparse") + or + // Python 3 + urlparse = Value::named("urllib.parse.urlparse") + ) and + tonode = urlparse.getACall() and + tonode.getArg(0) = fromnode + ) } private predicate parse_qs(ControlFlowNode fromnode, CallNode tonode) { - // This could be implemented as `exists(FunctionValue` without the explicit six part, - // but then our tests will need to import +100 modules, so for now this slightly - // altered version gets to live on. - exists(Value parse_qs | - ( - parse_qs = Value::named("six.moves.urllib.parse.parse_qs") - or - // Python 2 - parse_qs = Value::named("urlparse.parse_qs") - or - // Python 2 deprecated version of `urlparse.parse_qs` - parse_qs = Value::named("cgi.parse_qs") - or - // Python 3 - parse_qs = Value::named("urllib.parse.parse_qs") - ) and - tonode = parse_qs.getACall() and - ( - tonode.getArg(0) = fromnode - or - tonode.getArgByName("qs") = fromnode - ) + // This could be implemented as `exists(FunctionValue` without the explicit six part, + // but then our tests will need to import +100 modules, so for now this slightly + // altered version gets to live on. + exists(Value parse_qs | + ( + parse_qs = Value::named("six.moves.urllib.parse.parse_qs") + or + // Python 2 + parse_qs = Value::named("urlparse.parse_qs") + or + // Python 2 deprecated version of `urlparse.parse_qs` + parse_qs = Value::named("cgi.parse_qs") + or + // Python 3 + parse_qs = Value::named("urllib.parse.parse_qs") + ) and + tonode = parse_qs.getACall() and + ( + tonode.getArg(0) = fromnode + or + tonode.getArgByName("qs") = fromnode ) + ) } private predicate parse_qsl(ControlFlowNode fromnode, CallNode tonode) { - // This could be implemented as `exists(FunctionValue` without the explicit six part, - // but then our tests will need to import +100 modules, so for now this slightly - // altered version gets to live on. - exists(Value parse_qsl | - ( - parse_qsl = Value::named("six.moves.urllib.parse.parse_qsl") - or - // Python 2 - parse_qsl = Value::named("urlparse.parse_qsl") - or - // Python 2 deprecated version of `urlparse.parse_qsl` - parse_qsl = Value::named("cgi.parse_qsl") - or - // Python 3 - parse_qsl = Value::named("urllib.parse.parse_qsl") - ) and - tonode = parse_qsl.getACall() and - ( - tonode.getArg(0) = fromnode - or - tonode.getArgByName("qs") = fromnode - ) + // This could be implemented as `exists(FunctionValue` without the explicit six part, + // but then our tests will need to import +100 modules, so for now this slightly + // altered version gets to live on. + exists(Value parse_qsl | + ( + parse_qsl = Value::named("six.moves.urllib.parse.parse_qsl") + or + // Python 2 + parse_qsl = Value::named("urlparse.parse_qsl") + or + // Python 2 deprecated version of `urlparse.parse_qsl` + parse_qsl = Value::named("cgi.parse_qsl") + or + // Python 3 + parse_qsl = Value::named("urllib.parse.parse_qsl") + ) and + tonode = parse_qsl.getACall() and + ( + tonode.getArg(0) = fromnode + or + tonode.getArgByName("qs") = fromnode ) + ) } /** A kind of "taint", representing an open file-like object from an external source. */ class ExternalFileObject extends TaintKind { - ExternalStringKind valueKind; + ExternalStringKind valueKind; - ExternalFileObject() { this = "file[" + valueKind + "]" } + ExternalFileObject() { this = "file[" + valueKind + "]" } - /** Gets the taint kind for the contents of this file */ - TaintKind getValue() { result = valueKind } + /** Gets the taint kind for the contents of this file */ + TaintKind getValue() { result = valueKind } - override TaintKind getTaintOfMethodResult(string name) { - name in ["read", "readline"] and result = this.getValue() - or - name = "readlines" and result.(SequenceKind).getItem() = this.getValue() - } + override TaintKind getTaintOfMethodResult(string name) { + name in ["read", "readline"] and result = this.getValue() + or + name = "readlines" and result.(SequenceKind).getItem() = this.getValue() + } - override TaintKind getTaintForIteration() { result = this.getValue() } + override TaintKind getTaintForIteration() { result = this.getValue() } } /** @@ -267,65 +267,65 @@ class ExternalFileObject extends TaintKind { * - `if splitres[0] == "KNOWN_VALUE"` */ class UrlsplitUrlparseTempSanitizer extends Sanitizer { - // TODO: remove this once we have better support for named tuples - UrlsplitUrlparseTempSanitizer() { this = "UrlsplitUrlparseTempSanitizer" } + // TODO: remove this once we have better support for named tuples + UrlsplitUrlparseTempSanitizer() { this = "UrlsplitUrlparseTempSanitizer" } - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - ( - taint instanceof ExternalUrlSplitResult - or - taint instanceof ExternalUrlParseResult - ) and - exists(ControlFlowNode full_use | - full_use.(SubscriptNode).getObject() = test.getInput().getAUse() - or - full_use.(AttrNode).getObject() = test.getInput().getAUse() - | - clears_taint(full_use, test.getTest(), test.getSense()) - ) - } + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + ( + taint instanceof ExternalUrlSplitResult + or + taint instanceof ExternalUrlParseResult + ) and + exists(ControlFlowNode full_use | + full_use.(SubscriptNode).getObject() = test.getInput().getAUse() + or + full_use.(AttrNode).getObject() = test.getInput().getAUse() + | + clears_taint(full_use, test.getTest(), test.getSense()) + ) + } - private predicate clears_taint(ControlFlowNode tainted, ControlFlowNode test, boolean sense) { - test_equality_with_const(test, tainted, sense) + private predicate clears_taint(ControlFlowNode tainted, ControlFlowNode test, boolean sense) { + test_equality_with_const(test, tainted, sense) + or + test_in_const_seq(test, tainted, sense) + or + test.(UnaryExprNode).getNode().getOp() instanceof Not and + exists(ControlFlowNode nested_test | + nested_test = test.(UnaryExprNode).getOperand() and + clears_taint(tainted, nested_test, sense.booleanNot()) + ) + } + + /** holds for `== "KNOWN_VALUE"` on `true` edge, and `!= "KNOWN_VALUE"` on `false` edge */ + private predicate test_equality_with_const(CompareNode cmp, ControlFlowNode tainted, boolean sense) { + exists(ControlFlowNode const, Cmpop op | const.getNode() instanceof StrConst | + ( + cmp.operands(const, op, tainted) or - test_in_const_seq(test, tainted, sense) + cmp.operands(tainted, op, const) + ) and + ( + op instanceof Eq and sense = true or - test.(UnaryExprNode).getNode().getOp() instanceof Not and - exists(ControlFlowNode nested_test | - nested_test = test.(UnaryExprNode).getOperand() and - clears_taint(tainted, nested_test, sense.booleanNot()) - ) - } + op instanceof NotEq and sense = false + ) + ) + } - /** holds for `== "KNOWN_VALUE"` on `true` edge, and `!= "KNOWN_VALUE"` on `false` edge */ - private predicate test_equality_with_const(CompareNode cmp, ControlFlowNode tainted, boolean sense) { - exists(ControlFlowNode const, Cmpop op | const.getNode() instanceof StrConst | - ( - cmp.operands(const, op, tainted) - or - cmp.operands(tainted, op, const) - ) and - ( - op instanceof Eq and sense = true - or - op instanceof NotEq and sense = false - ) - ) - } - - /** holds for `in ["KNOWN_VALUE", ...]` on `true` edge, and `not in ["KNOWN_VALUE", ...]` on `false` edge */ - private predicate test_in_const_seq(CompareNode cmp, ControlFlowNode tainted, boolean sense) { - exists(SequenceNode const_seq, Cmpop op | - forall(ControlFlowNode elem | elem = const_seq.getAnElement() | - elem.getNode() instanceof StrConst - ) - | - cmp.operands(tainted, op, const_seq) and - ( - op instanceof In and sense = true - or - op instanceof NotIn and sense = false - ) - ) - } + /** holds for `in ["KNOWN_VALUE", ...]` on `true` edge, and `not in ["KNOWN_VALUE", ...]` on `false` edge */ + private predicate test_in_const_seq(CompareNode cmp, ControlFlowNode tainted, boolean sense) { + exists(SequenceNode const_seq, Cmpop op | + forall(ControlFlowNode elem | elem = const_seq.getAnElement() | + elem.getNode() instanceof StrConst + ) + | + cmp.operands(tainted, op, const_seq) and + ( + op instanceof In and sense = true + or + op instanceof NotIn and sense = false + ) + ) + } } diff --git a/python/ql/src/semmle/python/security/strings/Untrusted.qll b/python/ql/src/semmle/python/security/strings/Untrusted.qll index 6fa12668ada..da8c23e7bde 100644 --- a/python/ql/src/semmle/python/security/strings/Untrusted.qll +++ b/python/ql/src/semmle/python/security/strings/Untrusted.qll @@ -6,5 +6,5 @@ import External * This class is a simple sub-class of `ExternalStringKind`. */ class UntrustedStringKind extends ExternalStringKind { - UntrustedStringKind() { this = "externally controlled string" } + UntrustedStringKind() { this = "externally controlled string" } } diff --git a/python/ql/src/semmle/python/strings.qll b/python/ql/src/semmle/python/strings.qll index e366d191669..6c4b3ded1ce 100644 --- a/python/ql/src/semmle/python/strings.qll +++ b/python/ql/src/semmle/python/strings.qll @@ -1,11 +1,11 @@ import python predicate format_string(StrConst e) { - exists(BinaryExpr b | b.getOp() instanceof Mod and b.getLeft() = e) + exists(BinaryExpr b | b.getOp() instanceof Mod and b.getLeft() = e) } predicate mapping_format(StrConst e) { - conversion_specifier(e, _).regexpMatch("%\\([A-Z_a-z0-9]+\\).*") + conversion_specifier(e, _).regexpMatch("%\\([A-Z_a-z0-9]+\\).*") } /* @@ -18,36 +18,36 @@ predicate mapping_format(StrConst e) { */ private string conversion_specifier_string(StrConst e, int number, int position) { - exists(string s, string REGEX | s = e.getText() | - REGEX = "%(\\([^)]*\\))?[#0\\- +]*(\\*|[0-9]*)(\\.(\\*|[0-9]*))?(h|H|l|L)?[badiouxXeEfFgGcrs%]" and - result = s.regexpFind(REGEX, number, position) - ) + exists(string s, string REGEX | s = e.getText() | + REGEX = "%(\\([^)]*\\))?[#0\\- +]*(\\*|[0-9]*)(\\.(\\*|[0-9]*))?(h|H|l|L)?[badiouxXeEfFgGcrs%]" and + result = s.regexpFind(REGEX, number, position) + ) } private string conversion_specifier(StrConst e, int number) { - result = conversion_specifier_string(e, number, _) and result != "%%" + result = conversion_specifier_string(e, number, _) and result != "%%" } int illegal_conversion_specifier(StrConst e) { - format_string(e) and - "%" = e.getText().charAt(result) and - // not the start of a conversion specifier or the second % of a %% - not exists(conversion_specifier_string(e, _, result)) and - not exists(conversion_specifier_string(e, _, result - 1)) + format_string(e) and + "%" = e.getText().charAt(result) and + // not the start of a conversion specifier or the second % of a %% + not exists(conversion_specifier_string(e, _, result)) and + not exists(conversion_specifier_string(e, _, result - 1)) } /** Gets the number of format items in a format string */ int format_items(StrConst e) { - result = - count(int i | | conversion_specifier(e, i)) + - // a conversion specifier uses an extra item for each * - count(int i, int j | conversion_specifier(e, i).charAt(j) = "*") + result = + count(int i | | conversion_specifier(e, i)) + + // a conversion specifier uses an extra item for each * + count(int i, int j | conversion_specifier(e, i).charAt(j) = "*") } private string str(Expr e) { - result = e.(Num).getN() - or - result = "'" + e.(StrConst).getText() + "'" + result = e.(Num).getN() + or + result = "'" + e.(StrConst).getText() + "'" } /** Gets a string representation of an expression more suited for embedding in message strings than .toString() */ diff --git a/python/ql/src/semmle/python/templates/PyxlTags.qll b/python/ql/src/semmle/python/templates/PyxlTags.qll index 07f4aa44e03..f0e663cdad0 100644 --- a/python/ql/src/semmle/python/templates/PyxlTags.qll +++ b/python/ql/src/semmle/python/templates/PyxlTags.qll @@ -4,68 +4,68 @@ import python * A Tag in Pyxl (which gets converted to a call in Python). */ class PyxlTag extends Call { - PyxlTag() { pyxl_tag(this, _) } + PyxlTag() { pyxl_tag(this, _) } - string getPyxlTagName() { pyxl_tag(this, result) } + string getPyxlTagName() { pyxl_tag(this, result) } - /** Gets the pyxl or Python node that is enclosed by this one in the pyxl source */ - Expr getEnclosedNode() { none() } + /** Gets the pyxl or Python node that is enclosed by this one in the pyxl source */ + Expr getEnclosedNode() { none() } - /** Gets the Python code (if any) that is contained in this pyxl node */ - Expr getEnclosedPythonCode() { - result = this.getEnclosedNode() and not result instanceof PyxlTag - or - result = this.getEnclosedNode().(PyxlTag).getEnclosedPythonCode() - } + /** Gets the Python code (if any) that is contained in this pyxl node */ + Expr getEnclosedPythonCode() { + result = this.getEnclosedNode() and not result instanceof PyxlTag + or + result = this.getEnclosedNode().(PyxlTag).getEnclosedPythonCode() + } } private predicate pyxl_tag(Call c, string name) { - exists(Attribute tag, Name html | - tag = c.getFunc() and - html = tag.getObject() and - name = tag.getName() and - html.getId() = "html" - ) + exists(Attribute tag, Name html | + tag = c.getFunc() and + html = tag.getObject() and + name = tag.getName() and + html.getId() = "html" + ) } class PyxlHtmlTag extends PyxlTag { - PyxlHtmlTag() { this.getPyxlTagName().prefix(2) = "x_" } + PyxlHtmlTag() { this.getPyxlTagName().prefix(2) = "x_" } - string getTagName() { result = this.getPyxlTagName().suffix(2) } + string getTagName() { result = this.getPyxlTagName().suffix(2) } - /** Html tags get transformed into a call. This node is the callee function and the enclosed node is an argument. */ - override Expr getEnclosedNode() { - exists(Call c | - c.getFunc() = this and - result = c.getAnArg() - ) - } + /** Html tags get transformed into a call. This node is the callee function and the enclosed node is an argument. */ + override Expr getEnclosedNode() { + exists(Call c | + c.getFunc() = this and + result = c.getAnArg() + ) + } } class PyxlIfTag extends PyxlTag { - PyxlIfTag() { this.getPyxlTagName() = "_push_condition" } + PyxlIfTag() { this.getPyxlTagName() = "_push_condition" } - override Expr getEnclosedNode() { result = this.getAnArg() } + override Expr getEnclosedNode() { result = this.getAnArg() } } class PyxlEndIfTag extends PyxlTag { - PyxlEndIfTag() { this.getPyxlTagName() = "_leave_if" } + PyxlEndIfTag() { this.getPyxlTagName() = "_leave_if" } - override Expr getEnclosedNode() { result = this.getAnArg() } + override Expr getEnclosedNode() { result = this.getAnArg() } } class PyxlRawHtml extends PyxlTag { - PyxlRawHtml() { this.getPyxlTagName() = "rawhtml" } + PyxlRawHtml() { this.getPyxlTagName() = "rawhtml" } - /** The text for this raw html, if it is simple text. */ - string getText() { - exists(Unicode text | - text = this.getValue() and - result = text.getS() - ) - } + /** The text for this raw html, if it is simple text. */ + string getText() { + exists(Unicode text | + text = this.getValue() and + result = text.getS() + ) + } - Expr getValue() { result = this.getArg(0) } + Expr getValue() { result = this.getArg(0) } - override Expr getEnclosedNode() { result = this.getAnArg() } + override Expr getEnclosedNode() { result = this.getAnArg() } } diff --git a/python/ql/src/semmle/python/templates/Templates.qll b/python/ql/src/semmle/python/templates/Templates.qll index 00aacd232a0..fa33aa33a7f 100644 --- a/python/ql/src/semmle/python/templates/Templates.qll +++ b/python/ql/src/semmle/python/templates/Templates.qll @@ -3,13 +3,13 @@ import python abstract class Template extends Module { } class SpitfireTemplate extends Template { - SpitfireTemplate() { this.getKind() = "Spitfire template" } + SpitfireTemplate() { this.getKind() = "Spitfire template" } } class PyxlModule extends Template { - PyxlModule() { - exists(Comment c | c.getLocation().getFile() = this.getFile() | - c.getText().regexpMatch("# *coding.*pyxl.*") - ) - } + PyxlModule() { + exists(Comment c | c.getLocation().getFile() = this.getFile() | + c.getText().regexpMatch("# *coding.*pyxl.*") + ) + } } diff --git a/python/ql/src/semmle/python/types/Builtins.qll b/python/ql/src/semmle/python/types/Builtins.qll index 062d552a887..b5a03686bdd 100644 --- a/python/ql/src/semmle/python/types/Builtins.qll +++ b/python/ql/src/semmle/python/types/Builtins.qll @@ -1,126 +1,126 @@ 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, _) - ) - } + 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, _) + ) + } - /** Gets a textual representation of this element. */ - 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 - ) - } + /** Gets a textual representation of this element. */ + 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 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 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 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()) - } + 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() - } + predicate inheritsFromType() { + this = Builtin::special("type") + or + this.getBaseClass().inheritsFromType() + } - string getName() { if this.isStr() then result = "str" else py_cobjectnames(this, result) } + string getName() { if this.isStr() then result = "str" else py_cobjectnames(this, result) } - private predicate isStr() { - major_version() = 2 and this = Builtin::special("bytes") - or - major_version() = 3 and this = Builtin::special("unicode") - } + private predicate isStr() { + major_version() = 2 and this = Builtin::special("bytes") + or + major_version() = 3 and this = Builtin::special("unicode") + } - predicate isClass() { - py_cobjecttypes(_, this) - or - this = Builtin::unknownType() - or - exists(Builtin meta | meta.inheritsFromType() and py_cobjecttypes(this, meta)) - } + 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 isFunction() { + this.getClass() = Builtin::special("BuiltinFunctionType") and + exists(Builtin mod | + mod.isModule() and + mod.getMember(_) = this + ) + } - predicate isModule() { this.getClass() = Builtin::special("ModuleType") } + predicate isModule() { this.getClass() = Builtin::special("ModuleType") } - predicate isMethod() { - this.getClass() = Builtin::special("MethodDescriptorType") - or - this.getClass().getName() = "wrapper_descriptor" - } + predicate isMethod() { + this.getClass() = Builtin::special("MethodDescriptorType") + or + this.getClass().getName() = "wrapper_descriptor" + } - int intValue() { - ( - this.getClass() = Builtin::special("int") or - this.getClass() = Builtin::special("long") - ) and - result = this.getName().toInt() - } + int intValue() { + ( + this.getClass() = Builtin::special("int") or + this.getClass() = Builtin::special("long") + ) and + result = this.getName().toInt() + } - float floatValue() { - this.getClass() = Builtin::special("float") and - result = this.getName().toFloat() - } + float floatValue() { + this.getClass() = Builtin::special("float") and + result = this.getName().toFloat() + } - string strValue() { - ( - this.getClass() = Builtin::special("unicode") or - this.getClass() = Builtin::special("bytes") - ) and - exists(string quoted_string | - quoted_string = this.getName() and - result = quoted_string.regexpCapture("[bu]'([\\s\\S]*)'", 1) - ) - } + string strValue() { + ( + this.getClass() = Builtin::special("unicode") or + this.getClass() = Builtin::special("bytes") + ) and + exists(string quoted_string | + quoted_string = this.getName() and + result = quoted_string.regexpCapture("[bu]'([\\s\\S]*)'", 1) + ) + } } 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 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 builtin(string name) { result = builtinModule().getMember(name) } - Builtin special(string name) { py_special_objects(result, name) } + Builtin special(string name) { py_special_objects(result, name) } - Builtin unknown() { py_special_objects(result, "_1") } + Builtin unknown() { py_special_objects(result, "_1") } - Builtin unknownType() { py_special_objects(result, "_semmle_unknown_type") } + Builtin unknownType() { py_special_objects(result, "_semmle_unknown_type") } } diff --git a/python/ql/src/semmle/python/types/ClassObject.qll b/python/ql/src/semmle/python/types/ClassObject.qll index d48a9bddf22..8bcfb2ff92d 100644 --- a/python/ql/src/semmle/python/types/ClassObject.qll +++ b/python/ql/src/semmle/python/types/ClassObject.qll @@ -21,339 +21,339 @@ private import semmle.python.objects.ObjectInternal * running program. */ class ClassObject extends Object { - private ClassObjectInternal theClass() { - result.getOrigin() = this - or - result.getBuiltin() = this - } + private ClassObjectInternal theClass() { + result.getOrigin() = this + or + result.getBuiltin() = this + } - ClassObject() { - this.getOrigin() instanceof ClassExpr or - this.asBuiltin().isClass() - } + ClassObject() { + this.getOrigin() instanceof ClassExpr or + this.asBuiltin().isClass() + } - /** Gets the short (unqualified) name of this class */ - string getName() { result = theClass().getName() } + /** Gets the short (unqualified) name of this class */ + string getName() { result = theClass().getName() } - /** - * Gets the qualified name for this class. - * Should return the same name as the `__qualname__` attribute on classes in Python 3. + /** + * Gets the qualified name for this class. + * Should return the same name as the `__qualname__` attribute on classes in Python 3. + */ + string getQualifiedName() { + result = theClass().getBuiltin().getName() + or + result = theClass().(PythonClassObjectInternal).getScope().getQualifiedName() + } + + /** Gets the nth base class of this class */ + Object getBaseType(int n) { result = Types::getBase(theClass(), n).getSource() } + + /** Gets a base class of this class */ + Object getABaseType() { result = this.getBaseType(_) } + + /** Whether this class has a base class */ + predicate hasABase() { exists(Types::getBase(theClass(), _)) } + + /** Gets a super class of this class (includes transitive super classes) */ + ClassObject getASuperType() { + result = Types::getMro(theClass()).getTail().getAnItem().getSource() + } + + /** Gets a super class of this class (includes transitive super classes) or this class */ + ClassObject getAnImproperSuperType() { result = this.getABaseType*() } + + /** + * Whether this class is a new style class. + * A new style class is one that implicitly or explicitly inherits from `object`. + */ + predicate isNewStyle() { Types::isNewStyle(theClass()) } + + /** + * Whether this class is an old style class. + * An old style class is one that does not inherit from `object`. + */ + predicate isOldStyle() { Types::isOldStyle(theClass()) } + + /** + * Whether this class is a legal exception class. + * What constitutes a legal exception class differs between major versions + */ + predicate isLegalExceptionType() { + not this.isNewStyle() + or + this.getAnImproperSuperType() = theBaseExceptionType() + or + major_version() = 2 and this = theTupleType() + } + + /** Gets the scope associated with this class, if it is not a builtin class */ + Class getPyClass() { result.getClassObject() = this } + + /** Returns an attribute declared on this class (not on a super-class) */ + Object declaredAttribute(string name) { + exists(ObjectInternal val | + Types::declaredAttribute(theClass(), name, val, _) and + result = val.getSource() + ) + } + + /** Returns an attribute declared on this class (not on a super-class) */ + predicate declaresAttribute(string name) { + theClass().getClassDeclaration().declaresAttribute(name) + } + + /** + * Returns an attribute as it would be when looked up at runtime on this class. + * Will include attributes of super-classes + */ + Object lookupAttribute(string name) { + exists(ObjectInternal val | + theClass().lookup(name, val, _) and + result = val.getSource() + ) + } + + ClassList getMro() { result = Types::getMro(theClass()) } + + /** Looks up an attribute by searching this class' MRO starting at `start` */ + Object lookupMro(ClassObject start, string name) { + exists(ClassObjectInternal other, ClassObjectInternal decl, ObjectInternal val | + other.getSource() = start and + decl = Types::getMro(theClass()).startingAt(other).findDeclaringClass(name) and + Types::declaredAttribute(decl, name, val, _) and + result = val.getSource() + ) + } + + /** Whether the named attribute refers to the object and origin */ + predicate attributeRefersTo(string name, Object obj, ControlFlowNode origin) { + this.attributeRefersTo(name, obj, _, origin) + } + + /** Whether the named attribute refers to the object, class and origin */ + predicate attributeRefersTo(string name, Object obj, ClassObject cls, ControlFlowNode origin) { + exists(ObjectInternal val, CfgOrigin valorig | + theClass().lookup(name, val, valorig) and + obj = val.getSource() and + cls = val.getClass().getSource() and + origin = valorig.toCfgNode() + ) + } + + /** Whether this class has a attribute named `name`, either declared or inherited. */ + predicate hasAttribute(string name) { theClass().hasAttribute(name) } + + /** + * Whether it is impossible to know all the attributes of this class. Usually because it is + * impossible to calculate the full class hierarchy or because some attribute is too dynamic. + */ + predicate unknowableAttributes() { + /* True for a class with undeterminable superclasses, unanalysable metaclasses, or other confusions */ + this.failedInference() + or + this.getMetaClass().failedInference() + or + exists(Object base | base = this.getABaseType() | + base.(ClassObject).unknowableAttributes() + or + not base instanceof ClassObject + ) + } + + /** Gets the metaclass for this class */ + ClassObject getMetaClass() { + result = theClass().getClass().getSource() and + not this.failedInference() + } + + /* Whether this class is abstract. */ + predicate isAbstract() { + this.getMetaClass() = theAbcMetaClassObject() + or + exists(FunctionObject f, Raise r, Name ex | + f = this.lookupAttribute(_) and + r.getScope() = f.getFunction() + | + (r.getException() = ex or r.getException().(Call).getFunc() = ex) and + (ex.getId() = "NotImplementedError" or ex.getId() = "NotImplemented") + ) + } + + ControlFlowNode declaredMetaClass() { result = this.getPyClass().getMetaClass().getAFlowNode() } + + /** Has type inference failed to compute the full class hierarchy for this class for the reason given. */ + predicate failedInference(string reason) { Types::failedInference(theClass(), reason) } + + /** Has type inference failed to compute the full class hierarchy for this class */ + predicate failedInference() { this.failedInference(_) } + + /** + * Gets an object which is the sole instance of this class, if this class is probably a singleton. + * Note the 'probable' in the name; there is no guarantee that this class is in fact a singleton. + * It is guaranteed that getProbableSingletonInstance() returns at most one Object for each ClassObject. + */ + Object getProbableSingletonInstance() { + exists(ControlFlowNode use, Expr origin | use.refersTo(result, this, origin.getAFlowNode()) | + this.hasStaticallyUniqueInstance() and + /* Ensure that original expression will be executed only one. */ + origin.getScope() instanceof ImportTimeScope and + not exists(Expr outer | outer.getASubExpression+() = origin) + ) + or + this = theNoneType() and result = theNoneObject() + } + + /** This class is only instantiated at one place in the code */ + private predicate hasStaticallyUniqueInstance() { + strictcount(SpecificInstanceInternal inst | inst.getClass() = theClass()) = 1 + } + + ImportTimeScope getImportTimeScope() { result = this.getPyClass() } + + override string toString() { + this.isC() and result = "builtin-class " + this.getName() and not this = theUnknownType() + or + not this.isC() and result = "class " + this.getName() + } + + /* Method Resolution Order */ + /** Returns the next class in the MRO of 'this' after 'sup' */ + ClassObject nextInMro(ClassObject sup) { + exists(ClassObjectInternal other | + other.getSource() = sup and + result = Types::getMro(theClass()).startingAt(other).getTail().getHead().getSource() + ) and + not this.failedInference() + } + + /** + * Gets the MRO for this class. ClassObject `sup` occurs at `index` in the list of classes. + * `this` has an index of `1`, the next class in the MRO has an index of `2`, and so on. + */ + ClassObject getMroItem(int index) { result = this.getMro().getItem(index).getSource() } + + /** Holds if this class has duplicate base classes */ + predicate hasDuplicateBases() { + exists(ClassObject base, int i, int j | + i != j and base = this.getBaseType(i) and base = this.getBaseType(j) + ) + } + + /** Holds if this class is an iterable. */ + predicate isIterable() { this.hasAttribute("__iter__") or this.hasAttribute("__getitem__") } + + /** Holds if this class is an iterator. */ + predicate isIterator() { + this.hasAttribute("__iter__") and + ( + major_version() = 3 and this.hasAttribute("__next__") + or + /* + * Because 'next' is a common method name we need to check that an __iter__ + * method actually returns this class. This is not needed for Py3 as the + * '__next__' method exists to define a class as an iterator. + */ + + major_version() = 2 and + this.hasAttribute("next") and + exists(ClassObject other, FunctionObject iter | other.declaredAttribute("__iter__") = iter | + iter.getAnInferredReturnType() = this + ) + ) + or + /* This will be redundant when we have C class information */ + this = theGeneratorType() + } + + /** + * Holds if this class is an improper subclass of the other class. + * True if this is a sub-class of other or this is the same class as other. + * + * Equivalent to the Python builtin function issubclass(). + */ + predicate isSubclassOf(ClassObject other) { this = other or this.getASuperType() = other } + + /** Synonymous with isContainer(), retained for backwards compatibility. */ + predicate isCollection() { this.isContainer() } + + /** Holds if this class is a container(). That is, does it have a __getitem__ method. */ + predicate isContainer() { exists(this.lookupAttribute("__getitem__")) } + + /** Holds if this class is a mapping. */ + predicate isMapping() { + exists(this.lookupAttribute("__getitem__")) and + not this.isSequence() + } + + /** Holds if this class is probably a sequence. */ + predicate isSequence() { + /* + * To determine whether something is a sequence or a mapping is not entirely clear, + * so we need to guess a bit. */ - string getQualifiedName() { - result = theClass().getBuiltin().getName() - or - result = theClass().(PythonClassObjectInternal).getScope().getQualifiedName() - } - /** Gets the nth base class of this class */ - Object getBaseType(int n) { result = Types::getBase(theClass(), n).getSource() } + this.getAnImproperSuperType() = theTupleType() + or + this.getAnImproperSuperType() = theListType() + or + this.getAnImproperSuperType() = theRangeType() + or + this.getAnImproperSuperType() = theBytesType() + or + this.getAnImproperSuperType() = theUnicodeType() + or + /* Does this inherit from abc.Sequence? */ + this.getASuperType().getName() = "Sequence" + or + /* Does it have an index or __reversed__ method? */ + this.isContainer() and + ( + this.hasAttribute("index") or + this.hasAttribute("__reversed__") + ) + } - /** Gets a base class of this class */ - Object getABaseType() { result = this.getBaseType(_) } + predicate isCallable() { this.hasAttribute("__call__") } - /** Whether this class has a base class */ - predicate hasABase() { exists(Types::getBase(theClass(), _)) } + predicate isContextManager() { this.hasAttribute("__enter__") and this.hasAttribute("__exit__") } - /** Gets a super class of this class (includes transitive super classes) */ - ClassObject getASuperType() { - result = Types::getMro(theClass()).getTail().getAnItem().getSource() - } + predicate assignedInInit(string name) { + exists(FunctionObject init | init = this.lookupAttribute("__init__") | + attribute_assigned_in_method(init, name) + ) + } - /** Gets a super class of this class (includes transitive super classes) or this class */ - ClassObject getAnImproperSuperType() { result = this.getABaseType*() } + /** Holds if this class is unhashable */ + predicate unhashable() { + this.lookupAttribute("__hash__") = theNoneObject() + or + this.lookupAttribute("__hash__").(FunctionObject).neverReturns() + } - /** - * Whether this class is a new style class. - * A new style class is one that implicitly or explicitly inherits from `object`. - */ - predicate isNewStyle() { Types::isNewStyle(theClass()) } + /** Holds if this class is a descriptor */ + predicate isDescriptorType() { this.hasAttribute("__get__") } - /** - * Whether this class is an old style class. - * An old style class is one that does not inherit from `object`. - */ - predicate isOldStyle() { Types::isOldStyle(theClass()) } + /** Holds if this class is an overriding descriptor */ + predicate isOverridingDescriptorType() { + this.hasAttribute("__get__") and this.hasAttribute("__set__") + } - /** - * Whether this class is a legal exception class. - * What constitutes a legal exception class differs between major versions - */ - predicate isLegalExceptionType() { - not this.isNewStyle() - or - this.getAnImproperSuperType() = theBaseExceptionType() - or - major_version() = 2 and this = theTupleType() - } + FunctionObject getAMethodCalledFromInit() { + exists(FunctionObject init | + init = this.lookupAttribute("__init__") and + init.getACallee*() = result + ) + } - /** Gets the scope associated with this class, if it is not a builtin class */ - Class getPyClass() { result.getClassObject() = this } + override boolean booleanValue() { result = true } - /** Returns an attribute declared on this class (not on a super-class) */ - Object declaredAttribute(string name) { - exists(ObjectInternal val | - Types::declaredAttribute(theClass(), name, val, _) and - result = val.getSource() - ) - } + /** + * Gets a call to this class. Note that the call may not create a new instance of + * this class, as that depends on the `__new__` method of this class. + */ + CallNode getACall() { result.getFunction().refersTo(this) } - /** Returns an attribute declared on this class (not on a super-class) */ - predicate declaresAttribute(string name) { - theClass().getClassDeclaration().declaresAttribute(name) - } - - /** - * Returns an attribute as it would be when looked up at runtime on this class. - * Will include attributes of super-classes - */ - Object lookupAttribute(string name) { - exists(ObjectInternal val | - theClass().lookup(name, val, _) and - result = val.getSource() - ) - } - - ClassList getMro() { result = Types::getMro(theClass()) } - - /** Looks up an attribute by searching this class' MRO starting at `start` */ - Object lookupMro(ClassObject start, string name) { - exists(ClassObjectInternal other, ClassObjectInternal decl, ObjectInternal val | - other.getSource() = start and - decl = Types::getMro(theClass()).startingAt(other).findDeclaringClass(name) and - Types::declaredAttribute(decl, name, val, _) and - result = val.getSource() - ) - } - - /** Whether the named attribute refers to the object and origin */ - predicate attributeRefersTo(string name, Object obj, ControlFlowNode origin) { - this.attributeRefersTo(name, obj, _, origin) - } - - /** Whether the named attribute refers to the object, class and origin */ - predicate attributeRefersTo(string name, Object obj, ClassObject cls, ControlFlowNode origin) { - exists(ObjectInternal val, CfgOrigin valorig | - theClass().lookup(name, val, valorig) and - obj = val.getSource() and - cls = val.getClass().getSource() and - origin = valorig.toCfgNode() - ) - } - - /** Whether this class has a attribute named `name`, either declared or inherited. */ - predicate hasAttribute(string name) { theClass().hasAttribute(name) } - - /** - * Whether it is impossible to know all the attributes of this class. Usually because it is - * impossible to calculate the full class hierarchy or because some attribute is too dynamic. - */ - predicate unknowableAttributes() { - /* True for a class with undeterminable superclasses, unanalysable metaclasses, or other confusions */ - this.failedInference() - or - this.getMetaClass().failedInference() - or - exists(Object base | base = this.getABaseType() | - base.(ClassObject).unknowableAttributes() - or - not base instanceof ClassObject - ) - } - - /** Gets the metaclass for this class */ - ClassObject getMetaClass() { - result = theClass().getClass().getSource() and - not this.failedInference() - } - - /* Whether this class is abstract. */ - predicate isAbstract() { - this.getMetaClass() = theAbcMetaClassObject() - or - exists(FunctionObject f, Raise r, Name ex | - f = this.lookupAttribute(_) and - r.getScope() = f.getFunction() - | - (r.getException() = ex or r.getException().(Call).getFunc() = ex) and - (ex.getId() = "NotImplementedError" or ex.getId() = "NotImplemented") - ) - } - - ControlFlowNode declaredMetaClass() { result = this.getPyClass().getMetaClass().getAFlowNode() } - - /** Has type inference failed to compute the full class hierarchy for this class for the reason given. */ - predicate failedInference(string reason) { Types::failedInference(theClass(), reason) } - - /** Has type inference failed to compute the full class hierarchy for this class */ - predicate failedInference() { this.failedInference(_) } - - /** - * Gets an object which is the sole instance of this class, if this class is probably a singleton. - * Note the 'probable' in the name; there is no guarantee that this class is in fact a singleton. - * It is guaranteed that getProbableSingletonInstance() returns at most one Object for each ClassObject. - */ - Object getProbableSingletonInstance() { - exists(ControlFlowNode use, Expr origin | use.refersTo(result, this, origin.getAFlowNode()) | - this.hasStaticallyUniqueInstance() and - /* Ensure that original expression will be executed only one. */ - origin.getScope() instanceof ImportTimeScope and - not exists(Expr outer | outer.getASubExpression+() = origin) - ) - or - this = theNoneType() and result = theNoneObject() - } - - /** This class is only instantiated at one place in the code */ - private predicate hasStaticallyUniqueInstance() { - strictcount(SpecificInstanceInternal inst | inst.getClass() = theClass()) = 1 - } - - ImportTimeScope getImportTimeScope() { result = this.getPyClass() } - - override string toString() { - this.isC() and result = "builtin-class " + this.getName() and not this = theUnknownType() - or - not this.isC() and result = "class " + this.getName() - } - - /* Method Resolution Order */ - /** Returns the next class in the MRO of 'this' after 'sup' */ - ClassObject nextInMro(ClassObject sup) { - exists(ClassObjectInternal other | - other.getSource() = sup and - result = Types::getMro(theClass()).startingAt(other).getTail().getHead().getSource() - ) and - not this.failedInference() - } - - /** - * Gets the MRO for this class. ClassObject `sup` occurs at `index` in the list of classes. - * `this` has an index of `1`, the next class in the MRO has an index of `2`, and so on. - */ - ClassObject getMroItem(int index) { result = this.getMro().getItem(index).getSource() } - - /** Holds if this class has duplicate base classes */ - predicate hasDuplicateBases() { - exists(ClassObject base, int i, int j | - i != j and base = this.getBaseType(i) and base = this.getBaseType(j) - ) - } - - /** Holds if this class is an iterable. */ - predicate isIterable() { this.hasAttribute("__iter__") or this.hasAttribute("__getitem__") } - - /** Holds if this class is an iterator. */ - predicate isIterator() { - this.hasAttribute("__iter__") and - ( - major_version() = 3 and this.hasAttribute("__next__") - or - /* - * Because 'next' is a common method name we need to check that an __iter__ - * method actually returns this class. This is not needed for Py3 as the - * '__next__' method exists to define a class as an iterator. - */ - - major_version() = 2 and - this.hasAttribute("next") and - exists(ClassObject other, FunctionObject iter | other.declaredAttribute("__iter__") = iter | - iter.getAnInferredReturnType() = this - ) - ) - or - /* This will be redundant when we have C class information */ - this = theGeneratorType() - } - - /** - * Holds if this class is an improper subclass of the other class. - * True if this is a sub-class of other or this is the same class as other. - * - * Equivalent to the Python builtin function issubclass(). - */ - predicate isSubclassOf(ClassObject other) { this = other or this.getASuperType() = other } - - /** Synonymous with isContainer(), retained for backwards compatibility. */ - predicate isCollection() { this.isContainer() } - - /** Holds if this class is a container(). That is, does it have a __getitem__ method. */ - predicate isContainer() { exists(this.lookupAttribute("__getitem__")) } - - /** Holds if this class is a mapping. */ - predicate isMapping() { - exists(this.lookupAttribute("__getitem__")) and - not this.isSequence() - } - - /** Holds if this class is probably a sequence. */ - predicate isSequence() { - /* - * To determine whether something is a sequence or a mapping is not entirely clear, - * so we need to guess a bit. - */ - - this.getAnImproperSuperType() = theTupleType() - or - this.getAnImproperSuperType() = theListType() - or - this.getAnImproperSuperType() = theRangeType() - or - this.getAnImproperSuperType() = theBytesType() - or - this.getAnImproperSuperType() = theUnicodeType() - or - /* Does this inherit from abc.Sequence? */ - this.getASuperType().getName() = "Sequence" - or - /* Does it have an index or __reversed__ method? */ - this.isContainer() and - ( - this.hasAttribute("index") or - this.hasAttribute("__reversed__") - ) - } - - predicate isCallable() { this.hasAttribute("__call__") } - - predicate isContextManager() { this.hasAttribute("__enter__") and this.hasAttribute("__exit__") } - - predicate assignedInInit(string name) { - exists(FunctionObject init | init = this.lookupAttribute("__init__") | - attribute_assigned_in_method(init, name) - ) - } - - /** Holds if this class is unhashable */ - predicate unhashable() { - this.lookupAttribute("__hash__") = theNoneObject() - or - this.lookupAttribute("__hash__").(FunctionObject).neverReturns() - } - - /** Holds if this class is a descriptor */ - predicate isDescriptorType() { this.hasAttribute("__get__") } - - /** Holds if this class is an overriding descriptor */ - predicate isOverridingDescriptorType() { - this.hasAttribute("__get__") and this.hasAttribute("__set__") - } - - FunctionObject getAMethodCalledFromInit() { - exists(FunctionObject init | - init = this.lookupAttribute("__init__") and - init.getACallee*() = result - ) - } - - override boolean booleanValue() { result = true } - - /** - * Gets a call to this class. Note that the call may not create a new instance of - * this class, as that depends on the `__new__` method of this class. - */ - CallNode getACall() { result.getFunction().refersTo(this) } - - override predicate notClass() { none() } + override predicate notClass() { none() } } /** @@ -361,17 +361,17 @@ class ClassObject extends Object { * Python 2 and the 'unicode' class for Python 3 */ ClassObject theStrType() { - if major_version() = 2 then result = theBytesType() else result = theUnicodeType() + if major_version() = 2 then result = theBytesType() else result = theUnicodeType() } private Module theAbcModule() { result.getName() = "abc" } ClassObject theAbcMetaClassObject() { - /* Avoid using points-to and thus negative recursion */ - exists(Class abcmeta | result.getPyClass() = abcmeta | - abcmeta.getName() = "ABCMeta" and - abcmeta.getScope() = theAbcModule() - ) + /* Avoid using points-to and thus negative recursion */ + exists(Class abcmeta | result.getPyClass() = abcmeta | + abcmeta.getName() = "ABCMeta" and + abcmeta.getScope() = theAbcModule() + ) } /* Common builtin classes */ @@ -422,9 +422,9 @@ ClassObject theUnicodeType() { result.asBuiltin() = Builtin::special("unicode") /** The builtin class '(x)range' */ ClassObject theRangeType() { - result = Object::builtin("xrange") - or - major_version() = 3 and result = Object::builtin("range") + result = Object::builtin("xrange") + or + major_version() = 3 and result = Object::builtin("range") } /** The builtin class for bytes. str in Python2, bytes in Python3 */ @@ -441,7 +441,7 @@ ClassObject theBaseExceptionType() { result.asBuiltin() = Builtin::special("Base /** The class of builtin-functions */ ClassObject theBuiltinFunctionType() { - result.asBuiltin() = Builtin::special("BuiltinFunctionType") + result.asBuiltin() = Builtin::special("BuiltinFunctionType") } /** The class of Python functions */ @@ -474,19 +474,19 @@ ClassObject theBoundMethodType() { result.asBuiltin() = Builtin::special("Method /** The builtin class of builtin properties */ ClassObject theGetSetDescriptorType() { - result.asBuiltin() = Builtin::special("GetSetDescriptorType") + result.asBuiltin() = Builtin::special("GetSetDescriptorType") } /** The method descriptor class */ ClassObject theMethodDescriptorType() { - result.asBuiltin() = Builtin::special("MethodDescriptorType") + result.asBuiltin() = Builtin::special("MethodDescriptorType") } /** The class of builtin properties */ ClassObject theBuiltinPropertyType() { - /* This is CPython specific */ - result.isC() and - result.getName() = "getset_descriptor" + /* This is CPython specific */ + result.isC() and + result.getName() = "getset_descriptor" } /** The builtin class 'IOError' */ diff --git a/python/ql/src/semmle/python/types/Descriptors.qll b/python/ql/src/semmle/python/types/Descriptors.qll index f190f9a6434..6a5743c444a 100644 --- a/python/ql/src/semmle/python/types/Descriptors.qll +++ b/python/ql/src/semmle/python/types/Descriptors.qll @@ -3,26 +3,26 @@ private import semmle.python.objects.ObjectInternal /** A class method object. Either a decorated function or an explicit call to classmethod(f) */ class ClassMethodObject extends Object { - ClassMethodObject() { any(ClassMethodObjectInternal cm).getOrigin() = this } + ClassMethodObject() { any(ClassMethodObjectInternal cm).getOrigin() = this } - FunctionObject getFunction() { - exists(ClassMethodObjectInternal cm | - cm.getOrigin() = this and - result = cm.getFunction().getSource() - ) - } + FunctionObject getFunction() { + exists(ClassMethodObjectInternal cm | + cm.getOrigin() = this and + result = cm.getFunction().getSource() + ) + } - CallNode getACall() { result = this.getFunction().getACall() } + CallNode getACall() { result = this.getFunction().getACall() } } /** A static method object. Either a decorated function or an explicit call to staticmethod(f) */ class StaticMethodObject extends Object { - StaticMethodObject() { any(StaticMethodObjectInternal sm).getOrigin() = this } + StaticMethodObject() { any(StaticMethodObjectInternal sm).getOrigin() = this } - FunctionObject getFunction() { - exists(StaticMethodObjectInternal sm | - sm.getOrigin() = this and - result = sm.getFunction().getSource() - ) - } + FunctionObject getFunction() { + exists(StaticMethodObjectInternal sm | + sm.getOrigin() = this and + result = sm.getFunction().getSource() + ) + } } diff --git a/python/ql/src/semmle/python/types/Exceptions.qll b/python/ql/src/semmle/python/types/Exceptions.qll index 7fe1b274664..7383b44e742 100644 --- a/python/ql/src/semmle/python/types/Exceptions.qll +++ b/python/ql/src/semmle/python/types/Exceptions.qll @@ -15,432 +15,432 @@ import python /** Subset of ControlFlowNodes which might raise an exception */ class RaisingNode extends ControlFlowNode { - RaisingNode() { - exists(this.getAnExceptionalSuccessor()) - or - this.isExceptionalExit(_) - } + RaisingNode() { + exists(this.getAnExceptionalSuccessor()) + or + this.isExceptionalExit(_) + } - /** Gets the CFG node for the exception, if and only if this RaisingNode is an explicit raise */ - ControlFlowNode getExceptionNode() { - exists(Raise r | - r = this.getNode() and - result.getNode() = r.getRaised() and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the CFG node for the exception, if and only if this RaisingNode is an explicit raise */ + ControlFlowNode getExceptionNode() { + exists(Raise r | + r = this.getNode() and + result.getNode() = r.getRaised() and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - private predicate quits() { this.(CallNode).getFunction().refersTo(Object::quitter(_)) } + private predicate quits() { this.(CallNode).getFunction().refersTo(Object::quitter(_)) } - /** - * Gets the type of an exception that may be raised - * at this control flow node - */ - ClassObject getARaisedType_objectapi() { - result = this.localRaisedType_objectapi() - or - exists(FunctionObject func | this = func.getACall() | result = func.getARaisedType()) - or - result = systemExitRaise_objectapi() - } + /** + * Gets the type of an exception that may be raised + * at this control flow node + */ + ClassObject getARaisedType_objectapi() { + result = this.localRaisedType_objectapi() + or + exists(FunctionObject func | this = func.getACall() | result = func.getARaisedType()) + or + result = systemExitRaise_objectapi() + } - /** - * Gets the type of an exception that may be raised - * at this control flow node - */ - ClassValue getARaisedType() { - result = this.localRaisedType() - or - exists(FunctionValue func | this = func.getACall() | result = func.getARaisedType()) - or - result = systemExitRaise() - } + /** + * Gets the type of an exception that may be raised + * at this control flow node + */ + ClassValue getARaisedType() { + result = this.localRaisedType() + or + exists(FunctionValue func | this = func.getACall() | result = func.getARaisedType()) + or + result = systemExitRaise() + } - pragma[noinline] - private ClassObject systemExitRaise_objectapi() { - this.quits() and result = Object::builtin("SystemExit") - } + pragma[noinline] + private ClassObject systemExitRaise_objectapi() { + this.quits() and result = Object::builtin("SystemExit") + } - pragma[noinline] - private ClassValue systemExitRaise() { this.quits() and result = ClassValue::systemExit() } + pragma[noinline] + private ClassValue systemExitRaise() { this.quits() and result = ClassValue::systemExit() } - pragma[noinline, nomagic] - private ClassObject localRaisedType_objectapi() { - result.isSubclassOf(theBaseExceptionType()) and - ( - exists(ControlFlowNode ex | - ex = this.getExceptionNode() and - (ex.refersTo(result) or ex.refersTo(_, result, _)) - ) - or - this.getNode() instanceof ImportExpr and result = Object::builtin("ImportError") - or - this.getNode() instanceof Print and result = theIOErrorType() - or - exists(ExceptFlowNode except | - except = this.getAnExceptionalSuccessor() and - except.handles_objectapi(result) and - result = this.innateException_objectapi() - ) - or - not exists(ExceptFlowNode except | except = this.getAnExceptionalSuccessor()) and - sequence_or_mapping(this) and - result = theLookupErrorType() - or - this.read_write_call() and result = theIOErrorType() - ) - } + pragma[noinline, nomagic] + private ClassObject localRaisedType_objectapi() { + result.isSubclassOf(theBaseExceptionType()) and + ( + exists(ControlFlowNode ex | + ex = this.getExceptionNode() and + (ex.refersTo(result) or ex.refersTo(_, result, _)) + ) + or + this.getNode() instanceof ImportExpr and result = Object::builtin("ImportError") + or + this.getNode() instanceof Print and result = theIOErrorType() + or + exists(ExceptFlowNode except | + except = this.getAnExceptionalSuccessor() and + except.handles_objectapi(result) and + result = this.innateException_objectapi() + ) + or + not exists(ExceptFlowNode except | except = this.getAnExceptionalSuccessor()) and + sequence_or_mapping(this) and + result = theLookupErrorType() + or + this.read_write_call() and result = theIOErrorType() + ) + } - pragma[noinline, nomagic] - private ClassValue localRaisedType() { - result.getASuperType() = ClassValue::baseException() and - ( - exists(ControlFlowNode ex | - ex = this.getExceptionNode() and - (ex.pointsTo(result) or ex.pointsTo().getClass() = result) - ) - or - this.getNode() instanceof ImportExpr and result = ClassValue::importError() - or - this.getNode() instanceof Print and result = ClassValue::ioError() - or - exists(ExceptFlowNode except | - except = this.getAnExceptionalSuccessor() and - except.handles(result) and - result = this.innateException() - ) - or - not exists(ExceptFlowNode except | except = this.getAnExceptionalSuccessor()) and - sequence_or_mapping(this) and - result = ClassValue::lookupError() - or - this.read_write_call() and result = ClassValue::ioError() - ) - } + pragma[noinline, nomagic] + private ClassValue localRaisedType() { + result.getASuperType() = ClassValue::baseException() and + ( + exists(ControlFlowNode ex | + ex = this.getExceptionNode() and + (ex.pointsTo(result) or ex.pointsTo().getClass() = result) + ) + or + this.getNode() instanceof ImportExpr and result = ClassValue::importError() + or + this.getNode() instanceof Print and result = ClassValue::ioError() + or + exists(ExceptFlowNode except | + except = this.getAnExceptionalSuccessor() and + except.handles(result) and + result = this.innateException() + ) + or + not exists(ExceptFlowNode except | except = this.getAnExceptionalSuccessor()) and + sequence_or_mapping(this) and + result = ClassValue::lookupError() + or + this.read_write_call() and result = ClassValue::ioError() + ) + } - /** Holds if this is an innate exception (AttributeError, NameError, IndexError, or KeyError). */ - pragma[noinline] - ClassObject innateException_objectapi() { - this.getNode() instanceof Attribute and result = theAttributeErrorType() - or - this.getNode() instanceof Name and result = theNameErrorType() - or - this.getNode() instanceof Subscript and result = theIndexErrorType() - or - this.getNode() instanceof Subscript and result = theKeyErrorType() - } + /** Holds if this is an innate exception (AttributeError, NameError, IndexError, or KeyError). */ + pragma[noinline] + ClassObject innateException_objectapi() { + this.getNode() instanceof Attribute and result = theAttributeErrorType() + or + this.getNode() instanceof Name and result = theNameErrorType() + or + this.getNode() instanceof Subscript and result = theIndexErrorType() + or + this.getNode() instanceof Subscript and result = theKeyErrorType() + } - /** Holds if this is an innate exception (AttributeError, NameError, IndexError, or KeyError). */ - pragma[noinline] - ClassValue innateException() { - this.getNode() instanceof Attribute and result = ClassValue::attributeError() - or - this.getNode() instanceof Name and result = ClassValue::nameError() - or - this.getNode() instanceof Subscript and result = ClassValue::indexError() - or - this.getNode() instanceof Subscript and result = ClassValue::keyError() - } + /** Holds if this is an innate exception (AttributeError, NameError, IndexError, or KeyError). */ + pragma[noinline] + ClassValue innateException() { + this.getNode() instanceof Attribute and result = ClassValue::attributeError() + or + this.getNode() instanceof Name and result = ClassValue::nameError() + or + this.getNode() instanceof Subscript and result = ClassValue::indexError() + or + this.getNode() instanceof Subscript and result = ClassValue::keyError() + } - /** - * Whether this control flow node raises an exception, - * but the type of the exception it raises cannot be inferred. - */ - predicate raisesUnknownType() { - /* read/write calls are assumed to raise IOError (OSError for Py3) */ - not this.read_write_call() and - ( - /* Call to an unknown object */ - this.getNode() instanceof Call and - not exists(FunctionObject func | this = func.getACall()) and - not exists(ClassObject known | this.(CallNode).getFunction().refersTo(known)) - or - this.getNode() instanceof Exec - or - /* Call to a function raising an unknown type */ - exists(FunctionObject func | this = func.getACall() | func.raisesUnknownType()) - ) - } + /** + * Whether this control flow node raises an exception, + * but the type of the exception it raises cannot be inferred. + */ + predicate raisesUnknownType() { + /* read/write calls are assumed to raise IOError (OSError for Py3) */ + not this.read_write_call() and + ( + /* Call to an unknown object */ + this.getNode() instanceof Call and + not exists(FunctionObject func | this = func.getACall()) and + not exists(ClassObject known | this.(CallNode).getFunction().refersTo(known)) + or + this.getNode() instanceof Exec + or + /* Call to a function raising an unknown type */ + exists(FunctionObject func | this = func.getACall() | func.raisesUnknownType()) + ) + } - private predicate read_write_call() { - exists(string mname | mname = this.(CallNode).getFunction().(AttrNode).getName() | - mname = "read" or mname = "write" - ) - } + private predicate read_write_call() { + exists(string mname | mname = this.(CallNode).getFunction().(AttrNode).getName() | + mname = "read" or mname = "write" + ) + } - /** Whether (as inferred by type inference) it is highly unlikely (or impossible) for control to flow from this to succ. */ - predicate unlikelySuccessor(ControlFlowNode succ) { - succ = this.getAnExceptionalSuccessor() and - not this.viableExceptionEdge_objectapi(succ, _) and - not this.raisesUnknownType() - or - exists(FunctionObject func | - func.getACall() = this and - func.neverReturns() and - succ = this.getASuccessor() and - not succ = this.getAnExceptionalSuccessor() and - // If result is yielded then func is likely to be some form of coroutine. - not succ.getNode() instanceof Yield - ) - or - this.quits() and - succ = this.getASuccessor() and - not succ = this.getAnExceptionalSuccessor() - } + /** Whether (as inferred by type inference) it is highly unlikely (or impossible) for control to flow from this to succ. */ + predicate unlikelySuccessor(ControlFlowNode succ) { + succ = this.getAnExceptionalSuccessor() and + not this.viableExceptionEdge_objectapi(succ, _) and + not this.raisesUnknownType() + or + exists(FunctionObject func | + func.getACall() = this and + func.neverReturns() and + succ = this.getASuccessor() and + not succ = this.getAnExceptionalSuccessor() and + // If result is yielded then func is likely to be some form of coroutine. + not succ.getNode() instanceof Yield + ) + or + this.quits() and + succ = this.getASuccessor() and + not succ = this.getAnExceptionalSuccessor() + } - /** Whether it is considered plausible that 'raised' can be raised across the edge this-succ */ - predicate viableExceptionEdge_objectapi(ControlFlowNode succ, ClassObject raised) { - raised.isLegalExceptionType() and - raised = this.getARaisedType_objectapi() and - succ = this.getAnExceptionalSuccessor() and - ( - /* An 'except' that handles raised and there is no more previous handler */ - succ.(ExceptFlowNode).handles_objectapi(raised) and - not exists(ExceptFlowNode other, StmtList s, int i, int j | - not other = succ and - other.handles_objectapi(raised) and - s.getItem(i) = succ.getNode() and - s.getItem(j) = other.getNode() - | - j < i - ) - or - /* Any successor that is not an 'except', provided that 'raised' is not handled by a different successor. */ - not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles_objectapi(raised) and - not succ instanceof ExceptFlowNode - ) - } + /** Whether it is considered plausible that 'raised' can be raised across the edge this-succ */ + predicate viableExceptionEdge_objectapi(ControlFlowNode succ, ClassObject raised) { + raised.isLegalExceptionType() and + raised = this.getARaisedType_objectapi() and + succ = this.getAnExceptionalSuccessor() and + ( + /* An 'except' that handles raised and there is no more previous handler */ + succ.(ExceptFlowNode).handles_objectapi(raised) and + not exists(ExceptFlowNode other, StmtList s, int i, int j | + not other = succ and + other.handles_objectapi(raised) and + s.getItem(i) = succ.getNode() and + s.getItem(j) = other.getNode() + | + j < i + ) + or + /* Any successor that is not an 'except', provided that 'raised' is not handled by a different successor. */ + not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles_objectapi(raised) and + not succ instanceof ExceptFlowNode + ) + } - /** Whether it is considered plausible that 'raised' can be raised across the edge this-succ */ - predicate viableExceptionEdge(ControlFlowNode succ, ClassValue raised) { - raised.isLegalExceptionType() and - raised = this.getARaisedType() and - succ = this.getAnExceptionalSuccessor() and - ( - /* An 'except' that handles raised and there is no more previous handler */ - succ.(ExceptFlowNode).handles(raised) and - not exists(ExceptFlowNode other, StmtList s, int i, int j | - not other = succ and - other.handles(raised) and - s.getItem(i) = succ.getNode() and - s.getItem(j) = other.getNode() - | - j < i - ) - or - /* Any successor that is not an 'except', provided that 'raised' is not handled by a different successor. */ - not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles(raised) and - not succ instanceof ExceptFlowNode - ) - } + /** Whether it is considered plausible that 'raised' can be raised across the edge this-succ */ + predicate viableExceptionEdge(ControlFlowNode succ, ClassValue raised) { + raised.isLegalExceptionType() and + raised = this.getARaisedType() and + succ = this.getAnExceptionalSuccessor() and + ( + /* An 'except' that handles raised and there is no more previous handler */ + succ.(ExceptFlowNode).handles(raised) and + not exists(ExceptFlowNode other, StmtList s, int i, int j | + not other = succ and + other.handles(raised) and + s.getItem(i) = succ.getNode() and + s.getItem(j) = other.getNode() + | + j < i + ) + or + /* Any successor that is not an 'except', provided that 'raised' is not handled by a different successor. */ + not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles(raised) and + not succ instanceof ExceptFlowNode + ) + } - /** - * Whether this exceptional exit is viable. That is, is it - * plausible that the scope `s` can be exited with exception `raised` - * at this point. - */ - predicate viableExceptionalExit_objectapi(Scope s, ClassObject raised) { - raised.isLegalExceptionType() and - raised = this.getARaisedType_objectapi() and - this.isExceptionalExit(s) and - not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles_objectapi(raised) - } + /** + * Whether this exceptional exit is viable. That is, is it + * plausible that the scope `s` can be exited with exception `raised` + * at this point. + */ + predicate viableExceptionalExit_objectapi(Scope s, ClassObject raised) { + raised.isLegalExceptionType() and + raised = this.getARaisedType_objectapi() and + this.isExceptionalExit(s) and + not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles_objectapi(raised) + } - /** - * Whether this exceptional exit is viable. That is, is it - * plausible that the scope `s` can be exited with exception `raised` - * at this point. - */ - predicate viableExceptionalExit(Scope s, ClassValue raised) { - raised.isLegalExceptionType() and - raised = this.getARaisedType() and - this.isExceptionalExit(s) and - not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles(raised) - } + /** + * Whether this exceptional exit is viable. That is, is it + * plausible that the scope `s` can be exited with exception `raised` + * at this point. + */ + predicate viableExceptionalExit(Scope s, ClassValue raised) { + raised.isLegalExceptionType() and + raised = this.getARaisedType() and + this.isExceptionalExit(s) and + not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles(raised) + } } /** Is this a sequence or mapping subscript x[i]? */ private predicate sequence_or_mapping(RaisingNode r) { r.getNode() instanceof Subscript } private predicate current_exception_objectapi(ClassObject ex, BasicBlock b) { - exists(RaisingNode r | - r.viableExceptionEdge_objectapi(b.getNode(0), ex) and not b.getNode(0) instanceof ExceptFlowNode - ) - or - exists(BasicBlock prev | - current_exception_objectapi(ex, prev) and - exists(ControlFlowNode pred, ControlFlowNode succ | - pred = prev.getLastNode() and succ = b.getNode(0) - | - pred.getASuccessor() = succ and - ( - /* Normal control flow */ - not pred.getAnExceptionalSuccessor() = succ - or - /* Re-raise the current exception, propagating to the successor */ - pred instanceof ReraisingNode - ) - ) + exists(RaisingNode r | + r.viableExceptionEdge_objectapi(b.getNode(0), ex) and not b.getNode(0) instanceof ExceptFlowNode + ) + or + exists(BasicBlock prev | + current_exception_objectapi(ex, prev) and + exists(ControlFlowNode pred, ControlFlowNode succ | + pred = prev.getLastNode() and succ = b.getNode(0) + | + pred.getASuccessor() = succ and + ( + /* Normal control flow */ + not pred.getAnExceptionalSuccessor() = succ + or + /* Re-raise the current exception, propagating to the successor */ + pred instanceof ReraisingNode + ) ) + ) } private predicate current_exception(ClassValue ex, BasicBlock b) { - exists(RaisingNode r | - r.viableExceptionEdge(b.getNode(0), ex) and not b.getNode(0) instanceof ExceptFlowNode - ) - or - exists(BasicBlock prev | - current_exception(ex, prev) and - exists(ControlFlowNode pred, ControlFlowNode succ | - pred = prev.getLastNode() and succ = b.getNode(0) - | - pred.getASuccessor() = succ and - ( - /* Normal control flow */ - not pred.getAnExceptionalSuccessor() = succ - or - /* Re-raise the current exception, propagating to the successor */ - pred instanceof ReraisingNode - ) - ) + exists(RaisingNode r | + r.viableExceptionEdge(b.getNode(0), ex) and not b.getNode(0) instanceof ExceptFlowNode + ) + or + exists(BasicBlock prev | + current_exception(ex, prev) and + exists(ControlFlowNode pred, ControlFlowNode succ | + pred = prev.getLastNode() and succ = b.getNode(0) + | + pred.getASuccessor() = succ and + ( + /* Normal control flow */ + not pred.getAnExceptionalSuccessor() = succ + or + /* Re-raise the current exception, propagating to the successor */ + pred instanceof ReraisingNode + ) ) + ) } private predicate unknown_current_exception(BasicBlock b) { - exists(RaisingNode r | - r.raisesUnknownType() and - r.getAnExceptionalSuccessor() = b.getNode(0) and - not b.getNode(0) instanceof ExceptFlowNode - ) - or - exists(BasicBlock prev | - unknown_current_exception(prev) and - exists(ControlFlowNode pred, ControlFlowNode succ | - pred = prev.getLastNode() and succ = b.getNode(0) - | - pred.getASuccessor() = succ and - (not pred.getAnExceptionalSuccessor() = succ or pred instanceof ReraisingNode) - ) + exists(RaisingNode r | + r.raisesUnknownType() and + r.getAnExceptionalSuccessor() = b.getNode(0) and + not b.getNode(0) instanceof ExceptFlowNode + ) + or + exists(BasicBlock prev | + unknown_current_exception(prev) and + exists(ControlFlowNode pred, ControlFlowNode succ | + pred = prev.getLastNode() and succ = b.getNode(0) + | + pred.getASuccessor() = succ and + (not pred.getAnExceptionalSuccessor() = succ or pred instanceof ReraisingNode) ) + ) } /** INTERNAL -- Use FunctionObject.getARaisedType() instead */ predicate scope_raises_objectapi(ClassObject ex, Scope s) { - exists(BasicBlock b | - current_exception_objectapi(ex, b) and - b.getLastNode().isExceptionalExit(s) - | - b.getLastNode() instanceof ReraisingNode - ) - or - exists(RaisingNode r | r.viableExceptionalExit_objectapi(s, ex)) + exists(BasicBlock b | + current_exception_objectapi(ex, b) and + b.getLastNode().isExceptionalExit(s) + | + b.getLastNode() instanceof ReraisingNode + ) + or + exists(RaisingNode r | r.viableExceptionalExit_objectapi(s, ex)) } /** INTERNAL -- Use FunctionObject.getARaisedType() instead */ predicate scope_raises(ClassValue ex, Scope s) { - exists(BasicBlock b | - current_exception(ex, b) and - b.getLastNode().isExceptionalExit(s) - | - b.getLastNode() instanceof ReraisingNode - ) - or - exists(RaisingNode r | r.viableExceptionalExit(s, ex)) + exists(BasicBlock b | + current_exception(ex, b) and + b.getLastNode().isExceptionalExit(s) + | + b.getLastNode() instanceof ReraisingNode + ) + or + exists(RaisingNode r | r.viableExceptionalExit(s, ex)) } /** INTERNAL -- Use FunctionObject.raisesUnknownType() instead */ predicate scope_raises_unknown(Scope s) { - exists(BasicBlock b | - b.getLastNode() instanceof ReraisingNode and - b.getLastNode().isExceptionalExit(s) - | - unknown_current_exception(b) - ) - or - exists(RaisingNode r | - r.raisesUnknownType() and - r.isExceptionalExit(s) - ) + exists(BasicBlock b | + b.getLastNode() instanceof ReraisingNode and + b.getLastNode().isExceptionalExit(s) + | + unknown_current_exception(b) + ) + or + exists(RaisingNode r | + r.raisesUnknownType() and + r.isExceptionalExit(s) + ) } /** ControlFlowNode for an 'except' statement. */ class ExceptFlowNode extends ControlFlowNode { - ExceptFlowNode() { this.getNode() instanceof ExceptStmt } + ExceptFlowNode() { this.getNode() instanceof ExceptStmt } - ControlFlowNode getType() { - exists(ExceptStmt ex | - this.getBasicBlock().dominates(result.getBasicBlock()) and - ex = this.getNode() and - result = ex.getType().getAFlowNode() - ) - } + ControlFlowNode getType() { + exists(ExceptStmt ex | + this.getBasicBlock().dominates(result.getBasicBlock()) and + ex = this.getNode() and + result = ex.getType().getAFlowNode() + ) + } - ControlFlowNode getName() { - exists(ExceptStmt ex | - this.getBasicBlock().dominates(result.getBasicBlock()) and - ex = this.getNode() and - result = ex.getName().getAFlowNode() - ) - } + ControlFlowNode getName() { + exists(ExceptStmt ex | + this.getBasicBlock().dominates(result.getBasicBlock()) and + ex = this.getNode() and + result = ex.getName().getAFlowNode() + ) + } - private predicate handledObject_objectapi(Object obj, ClassObject cls, ControlFlowNode origin) { - this.getType().refersTo(obj, cls, origin) - or - exists(Object tup | this.handledObject_objectapi(tup, theTupleType(), _) | - element_from_tuple_objectapi(tup).refersTo(obj, cls, origin) - ) - } + private predicate handledObject_objectapi(Object obj, ClassObject cls, ControlFlowNode origin) { + this.getType().refersTo(obj, cls, origin) + or + exists(Object tup | this.handledObject_objectapi(tup, theTupleType(), _) | + element_from_tuple_objectapi(tup).refersTo(obj, cls, origin) + ) + } - private predicate handledObject(Value val, ClassValue cls, ControlFlowNode origin) { - val.getClass() = cls and - ( - this.getType().pointsTo(val, origin) - or - exists(TupleValue tup | this.handledObject(tup, ClassValue::tuple(), _) | - val = tup.getItem(_) and origin = val.getOrigin() - ) - ) - } + private predicate handledObject(Value val, ClassValue cls, ControlFlowNode origin) { + val.getClass() = cls and + ( + this.getType().pointsTo(val, origin) + or + exists(TupleValue tup | this.handledObject(tup, ClassValue::tuple(), _) | + val = tup.getItem(_) and origin = val.getOrigin() + ) + ) + } - /** Gets the inferred type(s) that are handled by this node, splitting tuples if possible. */ - pragma[noinline] - predicate handledException_objectapi(Object obj, ClassObject cls, ControlFlowNode origin) { - this.handledObject_objectapi(obj, cls, origin) and not cls = theTupleType() - or - not exists(this.getNode().(ExceptStmt).getType()) and - obj = theBaseExceptionType() and - cls = theTypeType() and - origin = this - } + /** Gets the inferred type(s) that are handled by this node, splitting tuples if possible. */ + pragma[noinline] + predicate handledException_objectapi(Object obj, ClassObject cls, ControlFlowNode origin) { + this.handledObject_objectapi(obj, cls, origin) and not cls = theTupleType() + or + not exists(this.getNode().(ExceptStmt).getType()) and + obj = theBaseExceptionType() and + cls = theTypeType() and + origin = this + } - /** Gets the inferred type(s) that are handled by this node, splitting tuples if possible. */ - pragma[noinline] - predicate handledException(Value val, ClassValue cls, ControlFlowNode origin) { - this.handledObject(val, cls, origin) and not cls = ClassValue::tuple() - or - not exists(this.getNode().(ExceptStmt).getType()) and - val = ClassValue::baseException() and - cls = ClassValue::type() and - origin = this - } + /** Gets the inferred type(s) that are handled by this node, splitting tuples if possible. */ + pragma[noinline] + predicate handledException(Value val, ClassValue cls, ControlFlowNode origin) { + this.handledObject(val, cls, origin) and not cls = ClassValue::tuple() + or + not exists(this.getNode().(ExceptStmt).getType()) and + val = ClassValue::baseException() and + cls = ClassValue::type() and + origin = this + } - /** Whether this `except` handles `cls` */ - predicate handles_objectapi(ClassObject cls) { - exists(ClassObject handled | this.handledException_objectapi(handled, _, _) | - cls.getAnImproperSuperType() = handled - ) - } + /** Whether this `except` handles `cls` */ + predicate handles_objectapi(ClassObject cls) { + exists(ClassObject handled | this.handledException_objectapi(handled, _, _) | + cls.getAnImproperSuperType() = handled + ) + } - /** Whether this `except` handles `cls` */ - predicate handles(ClassValue cls) { - exists(ClassValue handled | this.handledException(handled, _, _) | - cls.getASuperType() = handled - ) - } + /** Whether this `except` handles `cls` */ + predicate handles(ClassValue cls) { + exists(ClassValue handled | this.handledException(handled, _, _) | + cls.getASuperType() = handled + ) + } } private ControlFlowNode element_from_tuple_objectapi(Object tuple) { - exists(Tuple t | t = tuple.getOrigin() and result = t.getAnElt().getAFlowNode()) + exists(Tuple t | t = tuple.getOrigin() and result = t.getAnElt().getAFlowNode()) } /** @@ -448,35 +448,35 @@ private ControlFlowNode element_from_tuple_objectapi(Object tuple) { * that reraises the current exception. */ class ReraisingNode extends RaisingNode { - ReraisingNode() { - not this.getNode() instanceof Raise and - in_finally(this) and - forall(ControlFlowNode succ | succ = this.getASuccessor() | - succ = this.getAnExceptionalSuccessor() - ) - } + ReraisingNode() { + not this.getNode() instanceof Raise and + in_finally(this) and + forall(ControlFlowNode succ | succ = this.getASuccessor() | + succ = this.getAnExceptionalSuccessor() + ) + } - /** Gets a class that may be raised by this node */ - override ClassObject getARaisedType_objectapi() { - exists(BasicBlock b | - current_exception_objectapi(result, b) and - b.getNode(_) = this - ) - } + /** Gets a class that may be raised by this node */ + override ClassObject getARaisedType_objectapi() { + exists(BasicBlock b | + current_exception_objectapi(result, b) and + b.getNode(_) = this + ) + } - /** Gets a class that may be raised by this node */ - override ClassValue getARaisedType() { - exists(BasicBlock b | - current_exception(result, b) and - b.getNode(_) = this - ) - } + /** Gets a class that may be raised by this node */ + override ClassValue getARaisedType() { + exists(BasicBlock b | + current_exception(result, b) and + b.getNode(_) = this + ) + } } private predicate in_finally(ControlFlowNode n) { - exists(Stmt f | exists(Try t | f = t.getAFinalstmt()) | - f = n.getNode() - or - f.containsInScope(n.getNode()) - ) + exists(Stmt f | exists(Try t | f = t.getAFinalstmt()) | + f = n.getNode() + or + f.containsInScope(n.getNode()) + ) } diff --git a/python/ql/src/semmle/python/types/Extensions.qll b/python/ql/src/semmle/python/types/Extensions.qll index 19e05875826..9c067ed7e4a 100644 --- a/python/ql/src/semmle/python/types/Extensions.qll +++ b/python/ql/src/semmle/python/types/Extensions.qll @@ -19,9 +19,9 @@ private import semmle.python.web.HttpConstants import semmle.python.objects.ObjectInternal abstract class PointsToExtension extends @py_flow_node { - string toString() { result = "PointsToExtension with missing toString" } + string toString() { result = "PointsToExtension with missing toString" } - abstract predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin); + abstract predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin); } /* Legacy API */ @@ -32,160 +32,160 @@ abstract class PointsToExtension extends @py_flow_node { /** DEPRECATED -- Use PointsToExtension instead */ abstract deprecated class CustomPointsToFact extends @py_flow_node { - string toString() { result = "CustomPointsToFact with missing toString" } + string toString() { result = "CustomPointsToFact with missing toString" } - abstract predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin); + abstract predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin); } /** DEPRECATED -- Use PointsToExtension instead */ deprecated class FinalCustomPointsToFact = CustomPointsToFact; abstract deprecated class CustomPointsToOriginFact extends CustomPointsToFact { - abstract predicate pointsTo(Object value, ClassObject cls); + abstract predicate pointsTo(Object value, ClassObject cls); - override predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin) { - this.pointsTo(value, cls) and origin = this and context.appliesTo(this) - } + override predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin) { + this.pointsTo(value, cls) and origin = this and context.appliesTo(this) + } } /* Custom points-to fact with inferred class */ abstract deprecated class CustomPointsToObjectFact extends CustomPointsToFact { - abstract predicate pointsTo(Object value); + abstract predicate pointsTo(Object value); - override predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin) { - this.pointsTo(value) and cls = simple_types(value) and origin = this and context.appliesTo(this) - } + override predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin) { + this.pointsTo(value) and cls = simple_types(value) and origin = this and context.appliesTo(this) + } } /** DEPRECATED -- Unsupported; do not use */ abstract deprecated class CustomPointsToAttribute extends Object { - abstract predicate attributePointsTo( - string name, Object value, ClassObject cls, ControlFlowNode origin - ); + abstract predicate attributePointsTo( + string name, Object value, ClassObject cls, ControlFlowNode origin + ); } /* An example */ /** Any variable iterating over range or xrange must be an integer */ class RangeIterationVariableFact extends PointsToExtension { - RangeIterationVariableFact() { - exists(For f, ControlFlowNode iterable | - iterable.getBasicBlock().dominates(this.(ControlFlowNode).getBasicBlock()) and - f.getIter().getAFlowNode() = iterable and - f.getTarget().getAFlowNode() = this and - exists(ObjectInternal range | - PointsTo::pointsTo(iterable, _, range, _) and - range.getClass() = ObjectInternal::builtin("range") - ) - ) - } + RangeIterationVariableFact() { + exists(For f, ControlFlowNode iterable | + iterable.getBasicBlock().dominates(this.(ControlFlowNode).getBasicBlock()) and + f.getIter().getAFlowNode() = iterable and + f.getTarget().getAFlowNode() = this and + exists(ObjectInternal range | + PointsTo::pointsTo(iterable, _, range, _) and + range.getClass() = ObjectInternal::builtin("range") + ) + ) + } - override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { - value = TUnknownInstance(ObjectInternal::builtin("int")) and - origin = this and - context.appliesTo(this) - } + override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { + value = TUnknownInstance(ObjectInternal::builtin("int")) and + origin = this and + context.appliesTo(this) + } } /* bottle module route constants */ class BottleRoutePointToExtension extends PointsToExtension { - string name; + string name; - BottleRoutePointToExtension() { - exists(DefinitionNode defn | - defn.getScope().(Module).getName() = "bottle" and - this = defn.getValue() and - name = defn.(NameNode).getId() - | - name = "route" or - name = httpVerbLower() - ) - } + BottleRoutePointToExtension() { + exists(DefinitionNode defn | + defn.getScope().(Module).getName() = "bottle" and + this = defn.getValue() and + name = defn.(NameNode).getId() + | + name = "route" or + name = httpVerbLower() + ) + } - override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { - context.isImport() and - exists(CfgOrigin orig | - Module::named("bottle").attr("Bottle").(ClassObjectInternal).attribute(name, value, orig) and - origin = orig.asCfgNodeOrHere(this) - ) - } + override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { + context.isImport() and + exists(CfgOrigin orig | + Module::named("bottle").attr("Bottle").(ClassObjectInternal).attribute(name, value, orig) and + origin = orig.asCfgNodeOrHere(this) + ) + } } /* Python 3.6+ regex module constants */ string short_flag(string flag) { - ( - flag = "ASCII" or - flag = "IGNORECASE" or - flag = "LOCALE" or - flag = "UNICODE" or - flag = "MULTILINE" or - flag = "TEMPLATE" - ) and - result = flag.prefix(1) - or - flag = "DOTALL" and result = "S" - or - flag = "VERBOSE" and result = "X" + ( + flag = "ASCII" or + flag = "IGNORECASE" or + flag = "LOCALE" or + flag = "UNICODE" or + flag = "MULTILINE" or + flag = "TEMPLATE" + ) and + result = flag.prefix(1) + or + flag = "DOTALL" and result = "S" + or + flag = "VERBOSE" and result = "X" } class ReModulePointToExtension extends PointsToExtension { - string name; + string name; - ReModulePointToExtension() { - exists(ModuleObjectInternal re | - re.getName() = "re" and - PointsTo::pointsTo(this.(AttrNode).getObject(name), _, re, _) - ) - } + ReModulePointToExtension() { + exists(ModuleObjectInternal re | + re.getName() = "re" and + PointsTo::pointsTo(this.(AttrNode).getObject(name), _, re, _) + ) + } - override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { - exists(ModuleObjectInternal sre_constants, CfgOrigin orig, string flag | - (name = flag or name = short_flag(flag)) and - sre_constants.getName() = "sre_constants" and - sre_constants.attribute("SRE_FLAG_" + flag, value, orig) and - origin = orig.asCfgNodeOrHere(this) - ) and - pointsTo_helper(context) - } + override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { + exists(ModuleObjectInternal sre_constants, CfgOrigin orig, string flag | + (name = flag or name = short_flag(flag)) and + sre_constants.getName() = "sre_constants" and + sre_constants.attribute("SRE_FLAG_" + flag, value, orig) and + origin = orig.asCfgNodeOrHere(this) + ) and + pointsTo_helper(context) + } - pragma[noinline] - private predicate pointsTo_helper(Context context) { context.appliesTo(this) } + pragma[noinline] + private predicate pointsTo_helper(Context context) { context.appliesTo(this) } } deprecated private class BackwardCompatiblePointToExtension extends PointsToExtension { - BackwardCompatiblePointToExtension() { this instanceof CustomPointsToFact } + BackwardCompatiblePointToExtension() { this instanceof CustomPointsToFact } - override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { - exists(Object obj, ClassObject cls | - this.(CustomPointsToFact).pointsTo(context, obj, cls, origin) - | - value.getBuiltin() = obj - or - obj instanceof ControlFlowNode and - exists(ClassObjectInternal c | - c.getSource() = cls and - value = TUnknownInstance(c) - ) - ) - or - exists(ObjectInternal owner, string name | - PointsTo::pointsTo(this.(AttrNode).getObject(name), context, owner, _) and - additionalAttribute(owner, name, value, origin) - ) - } + override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { + exists(Object obj, ClassObject cls | + this.(CustomPointsToFact).pointsTo(context, obj, cls, origin) + | + value.getBuiltin() = obj + or + obj instanceof ControlFlowNode and + exists(ClassObjectInternal c | + c.getSource() = cls and + value = TUnknownInstance(c) + ) + ) + or + exists(ObjectInternal owner, string name | + PointsTo::pointsTo(this.(AttrNode).getObject(name), context, owner, _) and + additionalAttribute(owner, name, value, origin) + ) + } } deprecated private predicate additionalAttribute( - ObjectInternal owner, string name, ObjectInternal value, ControlFlowNode origin + ObjectInternal owner, string name, ObjectInternal value, ControlFlowNode origin ) { - exists(Object obj, ClassObject cls | - owner.getSource().(CustomPointsToAttribute).attributePointsTo(name, obj, cls, origin) - | - value.getBuiltin() = obj - or - obj instanceof ControlFlowNode and - exists(ClassObjectInternal c | - c.getSource() = cls and - value = TUnknownInstance(c) - ) + exists(Object obj, ClassObject cls | + owner.getSource().(CustomPointsToAttribute).attributePointsTo(name, obj, cls, origin) + | + value.getBuiltin() = obj + or + obj instanceof ControlFlowNode and + exists(ClassObjectInternal c | + c.getSource() = cls and + value = TUnknownInstance(c) ) + ) } diff --git a/python/ql/src/semmle/python/types/FunctionObject.qll b/python/ql/src/semmle/python/types/FunctionObject.qll index 5d3a81363db..c293a43d675 100644 --- a/python/ql/src/semmle/python/types/FunctionObject.qll +++ b/python/ql/src/semmle/python/types/FunctionObject.qll @@ -9,306 +9,306 @@ private import semmle.python.types.Builtins /** A function object, whether written in Python or builtin */ abstract class FunctionObject extends Object { - CallableValue theCallable() { result.(ObjectInternal).getSource() = this } + CallableValue theCallable() { result.(ObjectInternal).getSource() = this } - predicate isOverridingMethod() { exists(Object f | this.overrides(f)) } + predicate isOverridingMethod() { exists(Object f | this.overrides(f)) } - predicate isOverriddenMethod() { exists(Object f | f.overrides(this)) } + predicate isOverriddenMethod() { exists(Object f | f.overrides(this)) } - Function getFunction() { result = this.getOrigin().(CallableExpr).getInnerScope() } + Function getFunction() { result = this.getOrigin().(CallableExpr).getInnerScope() } - /** This function always returns None, meaning that its return value should be disregarded */ - abstract predicate isProcedure(); + /** This function always returns None, meaning that its return value should be disregarded */ + abstract predicate isProcedure(); - /** Gets the name of this function */ - abstract string getName(); + /** Gets the name of this function */ + abstract string getName(); - /** Gets a class that may be raised by this function */ - abstract ClassObject getARaisedType(); + /** Gets a class that may be raised by this function */ + abstract ClassObject getARaisedType(); - /** Whether this function raises an exception, the class of which cannot be inferred */ - abstract predicate raisesUnknownType(); + /** Whether this function raises an exception, the class of which cannot be inferred */ + abstract predicate raisesUnknownType(); - /** Use descriptiveString() instead. */ - deprecated string prettyString() { result = this.descriptiveString() } + /** Use descriptiveString() instead. */ + deprecated string prettyString() { result = this.descriptiveString() } - /** Gets a longer, more descriptive version of toString() */ - abstract string descriptiveString(); + /** Gets a longer, more descriptive version of toString() */ + abstract string descriptiveString(); - /** Gets a call-site from where this function is called as a function */ - CallNode getAFunctionCall() { result.getFunction().inferredValue() = theCallable() } + /** Gets a call-site from where this function is called as a function */ + CallNode getAFunctionCall() { result.getFunction().inferredValue() = theCallable() } - /** Gets a call-site from where this function is called as a method */ - CallNode getAMethodCall() { - exists(BoundMethodObjectInternal bm | - result.getFunction().inferredValue() = bm and - bm.getFunction() = theCallable() - ) - } + /** Gets a call-site from where this function is called as a method */ + CallNode getAMethodCall() { + exists(BoundMethodObjectInternal bm | + result.getFunction().inferredValue() = bm and + bm.getFunction() = theCallable() + ) + } - /** Gets a call-site from where this function is called */ - ControlFlowNode getACall() { result = theCallable().getACall() } + /** Gets a call-site from where this function is called */ + ControlFlowNode getACall() { result = theCallable().getACall() } - /** Gets a call-site from where this function is called, given the `context` */ - ControlFlowNode getACall(Context caller_context) { - result = theCallable().getACall(caller_context) - } + /** Gets a call-site from where this function is called, given the `context` */ + ControlFlowNode getACall(Context caller_context) { + result = theCallable().getACall(caller_context) + } - /** - * Gets the `ControlFlowNode` that will be passed as the nth argument to `this` when called at `call`. - * This predicate will correctly handle `x.y()`, treating `x` as the zeroth argument. - */ - ControlFlowNode getArgumentForCall(CallNode call, int n) { - result = theCallable().getArgumentForCall(call, n) - } + /** + * Gets the `ControlFlowNode` that will be passed as the nth argument to `this` when called at `call`. + * This predicate will correctly handle `x.y()`, treating `x` as the zeroth argument. + */ + ControlFlowNode getArgumentForCall(CallNode call, int n) { + result = theCallable().getArgumentForCall(call, n) + } - /** - * Gets the `ControlFlowNode` that will be passed as the named argument to `this` when called at `call`. - * This predicate will correctly handle `x.y()`, treating `x` as the self argument. - */ - ControlFlowNode getNamedArgumentForCall(CallNode call, string name) { - result = theCallable().getNamedArgumentForCall(call, name) - } + /** + * Gets the `ControlFlowNode` that will be passed as the named argument to `this` when called at `call`. + * This predicate will correctly handle `x.y()`, treating `x` as the self argument. + */ + ControlFlowNode getNamedArgumentForCall(CallNode call, string name) { + result = theCallable().getNamedArgumentForCall(call, name) + } - /** Whether this function never returns. This is an approximation. */ - predicate neverReturns() { theCallable().neverReturns() } + /** Whether this function never returns. This is an approximation. */ + predicate neverReturns() { theCallable().neverReturns() } - /** - * Whether this is a "normal" method, that is, it is exists as a class attribute - * which is not wrapped and not the __new__ method. - */ - predicate isNormalMethod() { - exists(ClassObject cls, string name | - cls.declaredAttribute(name) = this and - name != "__new__" and - not this.getOrigin() instanceof Lambda - ) - } + /** + * Whether this is a "normal" method, that is, it is exists as a class attribute + * which is not wrapped and not the __new__ method. + */ + predicate isNormalMethod() { + exists(ClassObject cls, string name | + cls.declaredAttribute(name) = this and + name != "__new__" and + not this.getOrigin() instanceof Lambda + ) + } - /** Gets the minimum number of parameters that can be correctly passed to this function */ - abstract int minParameters(); + /** Gets the minimum number of parameters that can be correctly passed to this function */ + abstract int minParameters(); - /** Gets the maximum number of parameters that can be correctly passed to this function */ - abstract int maxParameters(); + /** Gets the maximum number of parameters that can be correctly passed to this function */ + abstract int maxParameters(); - /** Gets a function that this function (directly) calls */ - FunctionObject getACallee() { - exists(ControlFlowNode node | - node.getScope() = this.getFunction() and - result.getACall() = node - ) - } + /** Gets a function that this function (directly) calls */ + FunctionObject getACallee() { + exists(ControlFlowNode node | + node.getScope() = this.getFunction() and + result.getACall() = node + ) + } - /** - * Gets the qualified name for this function object. - * Should return the same name as the `__qualname__` attribute on functions in Python 3. - */ - abstract string getQualifiedName(); + /** + * Gets the qualified name for this function object. + * Should return the same name as the `__qualname__` attribute on functions in Python 3. + */ + abstract string getQualifiedName(); - /** Whether `name` is a legal argument name for this function */ - bindingset[name] - predicate isLegalArgumentName(string name) { - this.getFunction().getAnArg().asName().getId() = name - or - this.getFunction().getAKeywordOnlyArg().getId() = name - or - this.getFunction().hasKwArg() - } + /** Whether `name` is a legal argument name for this function */ + bindingset[name] + predicate isLegalArgumentName(string name) { + this.getFunction().getAnArg().asName().getId() = name + or + this.getFunction().getAKeywordOnlyArg().getId() = name + or + this.getFunction().hasKwArg() + } - /** Gets a class that this function may return */ - ClassObject getAnInferredReturnType() { result = this.(BuiltinCallable).getAReturnType() } + /** Gets a class that this function may return */ + ClassObject getAnInferredReturnType() { result = this.(BuiltinCallable).getAReturnType() } - predicate isAbstract() { this.getARaisedType() = theNotImplementedErrorType() } + predicate isAbstract() { this.getARaisedType() = theNotImplementedErrorType() } } class PyFunctionObject extends FunctionObject { - PyFunctionObject() { any(PythonFunctionObjectInternal f).getOrigin() = this } + PyFunctionObject() { any(PythonFunctionObjectInternal f).getOrigin() = this } - override string toString() { result = "Function " + this.getName() } + override string toString() { result = "Function " + this.getName() } - override string getName() { - result = this.getOrigin().(FunctionExpr).getName() - or - this.getOrigin() instanceof Lambda and result = "lambda" - } + override string getName() { + result = this.getOrigin().(FunctionExpr).getName() + or + this.getOrigin() instanceof Lambda and result = "lambda" + } - /** Whether this function is a procedure, that is, it has no explicit return statement and is not a generator function */ - override predicate isProcedure() { this.getFunction().isProcedure() } + /** Whether this function is a procedure, that is, it has no explicit return statement and is not a generator function */ + override predicate isProcedure() { this.getFunction().isProcedure() } - override ClassObject getARaisedType() { scope_raises_objectapi(result, this.getFunction()) } + override ClassObject getARaisedType() { scope_raises_objectapi(result, this.getFunction()) } - override predicate raisesUnknownType() { scope_raises_unknown(this.getFunction()) } + override predicate raisesUnknownType() { scope_raises_unknown(this.getFunction()) } - /** Gets a control flow node corresponding to the value of a return statement */ - ControlFlowNode getAReturnedNode() { result = this.getFunction().getAReturnValueFlowNode() } + /** Gets a control flow node corresponding to the value of a return statement */ + ControlFlowNode getAReturnedNode() { result = this.getFunction().getAReturnValueFlowNode() } - override string descriptiveString() { - if this.getFunction().isMethod() - then - exists(Class cls | this.getFunction().getScope() = cls | - result = "method " + this.getQualifiedName() - ) - else result = "function " + this.getQualifiedName() - } + override string descriptiveString() { + if this.getFunction().isMethod() + then + exists(Class cls | this.getFunction().getScope() = cls | + result = "method " + this.getQualifiedName() + ) + else result = "function " + this.getQualifiedName() + } - override int minParameters() { - exists(Function f | - f = this.getFunction() and - result = count(f.getAnArg()) - count(f.getDefinition().getArgs().getADefault()) - ) - } + override int minParameters() { + exists(Function f | + f = this.getFunction() and + result = count(f.getAnArg()) - count(f.getDefinition().getArgs().getADefault()) + ) + } - override int maxParameters() { - exists(Function f | - f = this.getFunction() and - if exists(f.getVararg()) - then result = 2147483647 // INT_MAX - else result = count(f.getAnArg()) - ) - } + override int maxParameters() { + exists(Function f | + f = this.getFunction() and + if exists(f.getVararg()) + then result = 2147483647 // INT_MAX + else result = count(f.getAnArg()) + ) + } - override string getQualifiedName() { result = this.getFunction().getQualifiedName() } + override string getQualifiedName() { result = this.getFunction().getQualifiedName() } - predicate unconditionallyReturnsParameter(int n) { - exists(SsaVariable pvar | - exists(Parameter p | p = this.getFunction().getArg(n) | - p.asName().getAFlowNode() = pvar.getDefinition() - ) and - exists(NameNode rval | - rval = pvar.getAUse() and - exists(Return r | r.getValue() = rval.getNode()) and - rval.strictlyDominates(rval.getScope().getANormalExit()) - ) - ) - } + predicate unconditionallyReturnsParameter(int n) { + exists(SsaVariable pvar | + exists(Parameter p | p = this.getFunction().getArg(n) | + p.asName().getAFlowNode() = pvar.getDefinition() + ) and + exists(NameNode rval | + rval = pvar.getAUse() and + exists(Return r | r.getValue() = rval.getNode()) and + rval.strictlyDominates(rval.getScope().getANormalExit()) + ) + ) + } - /** Factored out to help join ordering */ - private predicate implicitlyReturns(Object none_, ClassObject noneType) { - noneType = theNoneType() and - not this.getFunction().isGenerator() and - none_ = theNoneObject() and - ( - not exists(this.getAReturnedNode()) and exists(this.getFunction().getANormalExit()) - or - exists(Return ret | ret.getScope() = this.getFunction() and not exists(ret.getValue())) - ) - } + /** Factored out to help join ordering */ + private predicate implicitlyReturns(Object none_, ClassObject noneType) { + noneType = theNoneType() and + not this.getFunction().isGenerator() and + none_ = theNoneObject() and + ( + not exists(this.getAReturnedNode()) and exists(this.getFunction().getANormalExit()) + or + exists(Return ret | ret.getScope() = this.getFunction() and not exists(ret.getValue())) + ) + } - /** Gets a class that this function may return */ - override ClassObject getAnInferredReturnType() { - this.getFunction().isGenerator() and result = theGeneratorType() - or - not this.neverReturns() and - not this.getFunction().isGenerator() and - ( - this.(PyFunctionObject).getAReturnedNode().refersTo(_, result, _) - or - this.implicitlyReturns(_, result) - ) - } + /** Gets a class that this function may return */ + override ClassObject getAnInferredReturnType() { + this.getFunction().isGenerator() and result = theGeneratorType() + or + not this.neverReturns() and + not this.getFunction().isGenerator() and + ( + this.(PyFunctionObject).getAReturnedNode().refersTo(_, result, _) + or + this.implicitlyReturns(_, result) + ) + } - ParameterDefinition getParameter(int n) { - result.getDefiningNode().getNode() = this.getFunction().getArg(n) - } + ParameterDefinition getParameter(int n) { + result.getDefiningNode().getNode() = this.getFunction().getArg(n) + } } abstract class BuiltinCallable extends FunctionObject { - abstract ClassObject getAReturnType(); + abstract ClassObject getAReturnType(); - override predicate isProcedure() { - forex(ClassObject rt | rt = this.getAReturnType() | rt = theNoneType()) - } + override predicate isProcedure() { + forex(ClassObject rt | rt = this.getAReturnType() | rt = theNoneType()) + } - abstract override string getQualifiedName(); + abstract override string getQualifiedName(); - override ControlFlowNode getArgumentForCall(CallNode call, int n) { - call = this.getACall() and result = call.getArg(n) - } + override ControlFlowNode getArgumentForCall(CallNode call, int n) { + call = this.getACall() and result = call.getArg(n) + } } class BuiltinMethodObject extends BuiltinCallable { - BuiltinMethodObject() { any(BuiltinMethodObjectInternal m).getBuiltin() = this } + BuiltinMethodObject() { any(BuiltinMethodObjectInternal m).getBuiltin() = this } - override string getQualifiedName() { - exists(ClassObject cls | cls.asBuiltin().getMember(_) = this.asBuiltin() | - result = cls.getName() + "." + this.getName() - ) - or - not exists(ClassObject cls | cls.asBuiltin().getMember(_) = this.asBuiltin()) and - result = this.getName() - } + override string getQualifiedName() { + exists(ClassObject cls | cls.asBuiltin().getMember(_) = this.asBuiltin() | + result = cls.getName() + "." + this.getName() + ) + or + not exists(ClassObject cls | cls.asBuiltin().getMember(_) = this.asBuiltin()) and + result = this.getName() + } - override string descriptiveString() { result = "builtin-method " + this.getQualifiedName() } + override string descriptiveString() { result = "builtin-method " + this.getQualifiedName() } - override string getName() { result = this.asBuiltin().getName() } + override string getName() { result = this.asBuiltin().getName() } - override string toString() { result = "Builtin-method " + this.getName() } + override string toString() { result = "Builtin-method " + this.getName() } - override ClassObject getARaisedType() { - /* Information is unavailable for C code in general */ - none() - } + override ClassObject getARaisedType() { + /* Information is unavailable for C code in general */ + none() + } - override predicate raisesUnknownType() { - /* Information is unavailable for C code in general */ - any() - } + override predicate raisesUnknownType() { + /* Information is unavailable for C code in general */ + any() + } - override int minParameters() { none() } + override int minParameters() { none() } - override int maxParameters() { none() } + override int maxParameters() { none() } - override ClassObject getAReturnType() { ext_rettype(this.asBuiltin(), result.asBuiltin()) } + override ClassObject getAReturnType() { ext_rettype(this.asBuiltin(), result.asBuiltin()) } } class BuiltinFunctionObject extends BuiltinCallable { - BuiltinFunctionObject() { any(BuiltinFunctionObjectInternal f).getBuiltin() = this } + BuiltinFunctionObject() { any(BuiltinFunctionObjectInternal f).getBuiltin() = this } - override string getName() { result = this.asBuiltin().getName() } + override string getName() { result = this.asBuiltin().getName() } - override string getQualifiedName() { result = this.getName() } + override string getQualifiedName() { result = this.getName() } - override string toString() { result = "Builtin-function " + this.getName() } + override string toString() { result = "Builtin-function " + this.getName() } - override string descriptiveString() { result = "builtin-function " + this.getName() } + override string descriptiveString() { result = "builtin-function " + this.getName() } - override ClassObject getARaisedType() { - /* Information is unavailable for C code in general */ - none() - } + override ClassObject getARaisedType() { + /* Information is unavailable for C code in general */ + none() + } - override predicate raisesUnknownType() { - /* Information is unavailable for C code in general */ - any() - } + override predicate raisesUnknownType() { + /* Information is unavailable for C code in general */ + any() + } - override ClassObject getAReturnType() { - /* - * Enumerate the types of a few builtin functions, that the CPython analysis misses. - */ + override ClassObject getAReturnType() { + /* + * Enumerate the types of a few builtin functions, that the CPython analysis misses. + */ - this = Object::builtin("hex") and result = theStrType() - or - this = Object::builtin("oct") and result = theStrType() - or - this = Object::builtin("intern") and result = theStrType() - or - /* Fix a few minor inaccuracies in the CPython analysis */ - ext_rettype(this.asBuiltin(), result.asBuiltin()) and - not ( - this = Object::builtin("__import__") and result = theNoneType() - or - this = Object::builtin("compile") and result = theNoneType() - or - this = Object::builtin("sum") - or - this = Object::builtin("filter") - ) - } + this = Object::builtin("hex") and result = theStrType() + or + this = Object::builtin("oct") and result = theStrType() + or + this = Object::builtin("intern") and result = theStrType() + or + /* Fix a few minor inaccuracies in the CPython analysis */ + ext_rettype(this.asBuiltin(), result.asBuiltin()) and + not ( + this = Object::builtin("__import__") and result = theNoneType() + or + this = Object::builtin("compile") and result = theNoneType() + or + this = Object::builtin("sum") + or + this = Object::builtin("filter") + ) + } - override int minParameters() { none() } + override int minParameters() { none() } - override int maxParameters() { none() } + override int maxParameters() { none() } } /** DEPRECATED -- Use `Object::builtin("apply")` instead. */ diff --git a/python/ql/src/semmle/python/types/ImportTime.qll b/python/ql/src/semmle/python/types/ImportTime.qll index a35ed9d122a..a520f84dcb7 100644 --- a/python/ql/src/semmle/python/types/ImportTime.qll +++ b/python/ql/src/semmle/python/types/ImportTime.qll @@ -7,28 +7,28 @@ import python * This is an artificial approximation, which is necessary for static analysis. */ class ImportTimeScope extends Scope { - ImportTimeScope() { not this.getEnclosingScope*() instanceof Function } + ImportTimeScope() { not this.getEnclosingScope*() instanceof Function } - /** - * Whether this scope explicitly defines 'name'. - * Does not cover implicit definitions be import * - */ - pragma[nomagic] - predicate definesName(string name) { - exists(SsaVariable var | name = var.getId() and var.getAUse() = this.getANormalExit()) - } + /** + * Whether this scope explicitly defines 'name'. + * Does not cover implicit definitions be import * + */ + pragma[nomagic] + predicate definesName(string name) { + exists(SsaVariable var | name = var.getId() and var.getAUse() = this.getANormalExit()) + } - /** Holds if the control flow passes from `outer` to `inner` when this scope starts executing */ - predicate entryEdge(ControlFlowNode outer, ControlFlowNode inner) { - inner = this.getEntryNode() and - outer.getNode().(ClassExpr).getInnerScope() = this - } + /** Holds if the control flow passes from `outer` to `inner` when this scope starts executing */ + predicate entryEdge(ControlFlowNode outer, ControlFlowNode inner) { + inner = this.getEntryNode() and + outer.getNode().(ClassExpr).getInnerScope() = this + } - /** Gets the global variable that is used during lookup, should `var` be undefined. */ - GlobalVariable getOuterVariable(LocalVariable var) { - this instanceof Class and - var.getScope() = this and - result.getScope() = this.getEnclosingModule() and - var.getId() = result.getId() - } + /** Gets the global variable that is used during lookup, should `var` be undefined. */ + GlobalVariable getOuterVariable(LocalVariable var) { + this instanceof Class and + var.getScope() = this and + result.getScope() = this.getEnclosingModule() and + var.getId() = result.getId() + } } diff --git a/python/ql/src/semmle/python/types/ModuleKind.qll b/python/ql/src/semmle/python/types/ModuleKind.qll index 1509bac24e2..edb582b3627 100644 --- a/python/ql/src/semmle/python/types/ModuleKind.qll +++ b/python/ql/src/semmle/python/types/ModuleKind.qll @@ -1,34 +1,34 @@ import python private predicate is_normal_module(ModuleObject m) { - m instanceof BuiltinModuleObject - or - m instanceof PackageObject - or - exists(ImportingStmt i | m.importedAs(i.getAnImportedModuleName())) - or - m.getName().matches("%\\_\\_init\\_\\_") + m instanceof BuiltinModuleObject + or + m instanceof PackageObject + or + exists(ImportingStmt i | m.importedAs(i.getAnImportedModuleName())) + or + m.getName().matches("%\\_\\_init\\_\\_") } private predicate is_script(ModuleObject m) { - not is_normal_module(m) and - ( - m.getModule().getFile().getExtension() != ".py" - or - exists(If i, Name name, StrConst main, Cmpop op | - i.getScope() = m.getModule() and - op instanceof Eq and - i.getTest().(Compare).compares(name, op, main) and - name.getId() = "__name__" and - main.getText() = "__main__" - ) + not is_normal_module(m) and + ( + m.getModule().getFile().getExtension() != ".py" + or + exists(If i, Name name, StrConst main, Cmpop op | + i.getScope() = m.getModule() and + op instanceof Eq and + i.getTest().(Compare).compares(name, op, main) and + name.getId() = "__name__" and + main.getText() = "__main__" ) + ) } private predicate is_plugin(ModuleObject m) { - // This needs refining but is sufficient for our present needs. - not is_normal_module(m) and - not is_script(m) + // This needs refining but is sufficient for our present needs. + not is_normal_module(m) and + not is_script(m) } /** @@ -36,9 +36,9 @@ private predicate is_plugin(ModuleObject m) { * "module", "script" or "plugin" */ string getKindForModule(ModuleObject m) { - is_normal_module(m) and result = "module" - or - is_script(m) and result = "script" - or - is_plugin(m) and result = "plugin" + is_normal_module(m) and result = "module" + or + is_script(m) and result = "script" + or + is_plugin(m) and result = "plugin" } diff --git a/python/ql/src/semmle/python/types/ModuleObject.qll b/python/ql/src/semmle/python/types/ModuleObject.qll index 644d4e60244..ea62c57fff0 100644 --- a/python/ql/src/semmle/python/types/ModuleObject.qll +++ b/python/ql/src/semmle/python/types/ModuleObject.qll @@ -4,145 +4,145 @@ private import semmle.python.objects.ObjectInternal private import semmle.python.types.ModuleKind abstract class ModuleObject extends Object { - ModuleValue theModule() { - result.(PythonModuleObjectInternal).getSourceModule() = this.getModule() - or - result.(PackageObjectInternal).getFolder() = this.(PackageObject).getPath() - or - result.(BuiltinModuleObjectInternal).getBuiltin() = this - } + ModuleValue theModule() { + result.(PythonModuleObjectInternal).getSourceModule() = this.getModule() + or + result.(PackageObjectInternal).getFolder() = this.(PackageObject).getPath() + or + result.(BuiltinModuleObjectInternal).getBuiltin() = this + } - /** Gets the scope corresponding to this module, if this is a Python module */ - Module getModule() { none() } + /** Gets the scope corresponding to this module, if this is a Python module */ + Module getModule() { none() } - /** Gets the source scope corresponding to this module, if this is a Python module */ - Module getSourceModule() { none() } + /** Gets the source scope corresponding to this module, if this is a Python module */ + Module getSourceModule() { none() } - Container getPath() { none() } + Container getPath() { none() } - /** Gets the name of this scope */ - abstract string getName(); + /** Gets the name of this scope */ + abstract string getName(); - override string toString() { - result = "Module " + this.getName() - or - not exists(this.getName()) and - result = this.getModule().toString() - } + override string toString() { + result = "Module " + this.getName() + or + not exists(this.getName()) and + result = this.getModule().toString() + } - /** - * Gets the named attribute of this module. Using attributeRefersTo() instead - * may provide better results for presentation. - */ - Object getAttribute(string name) { this.attributeRefersTo(name, result, _) } + /** + * Gets the named attribute of this module. Using attributeRefersTo() instead + * may provide better results for presentation. + */ + Object getAttribute(string name) { this.attributeRefersTo(name, result, _) } - /** - * Gets the named attribute of this module. - * Synonym for `getAttribute(name)` - */ - pragma[inline] - final Object attr(string name) { result = this.getAttribute(name) } + /** + * Gets the named attribute of this module. + * Synonym for `getAttribute(name)` + */ + pragma[inline] + final Object attr(string name) { result = this.getAttribute(name) } - predicate hasAttribute(string name) { theModule().hasAttribute(name) } + predicate hasAttribute(string name) { theModule().hasAttribute(name) } - predicate attributeRefersTo(string name, Object obj, ControlFlowNode origin) { - exists(ObjectInternal val, CfgOrigin valorig | - theModule().(ModuleObjectInternal).attribute(name, val, valorig) and - obj = val.getSource() and - origin = valorig.toCfgNode() - ) - } + predicate attributeRefersTo(string name, Object obj, ControlFlowNode origin) { + exists(ObjectInternal val, CfgOrigin valorig | + theModule().(ModuleObjectInternal).attribute(name, val, valorig) and + obj = val.getSource() and + origin = valorig.toCfgNode() + ) + } - predicate attributeRefersTo(string name, Object obj, ClassObject cls, ControlFlowNode origin) { - exists(ObjectInternal val, CfgOrigin valorig | - theModule().(ModuleObjectInternal).attribute(name, val, valorig) and - obj = val.getSource() and - cls = val.getClass().getSource() and - origin = valorig.toCfgNode() - ) - } + predicate attributeRefersTo(string name, Object obj, ClassObject cls, ControlFlowNode origin) { + exists(ObjectInternal val, CfgOrigin valorig | + theModule().(ModuleObjectInternal).attribute(name, val, valorig) and + obj = val.getSource() and + cls = val.getClass().getSource() and + origin = valorig.toCfgNode() + ) + } - /** Gets the package for this module. */ - PackageObject getPackage() { - this.getName().matches("%.%") and - result.getName() = this.getName().regexpReplaceAll("\\.[^.]*$", "") - } + /** Gets the package for this module. */ + PackageObject getPackage() { + this.getName().matches("%.%") and + result.getName() = this.getName().regexpReplaceAll("\\.[^.]*$", "") + } - /** - * Whether this module "exports" `name`. That is, whether using `import *` on this module - * will result in `name` being added to the namespace. - */ - predicate exports(string name) { theModule().exports(name) } + /** + * Whether this module "exports" `name`. That is, whether using `import *` on this module + * will result in `name` being added to the namespace. + */ + predicate exports(string name) { theModule().exports(name) } - /** - * Whether the complete set of names "exported" by this module can be accurately determined - * - * DEPRECATED: Use ModuleValue::hasCompleteExportInfo instead - */ - abstract deprecated predicate exportsComplete(); + /** + * Whether the complete set of names "exported" by this module can be accurately determined + * + * DEPRECATED: Use ModuleValue::hasCompleteExportInfo instead + */ + abstract deprecated predicate exportsComplete(); - /** Gets the short name of the module. For example the short name of module x.y.z is 'z' */ - string getShortName() { - result = this.getName().suffix(this.getPackage().getName().length() + 1) - or - result = this.getName() and not exists(this.getPackage()) - } + /** Gets the short name of the module. For example the short name of module x.y.z is 'z' */ + string getShortName() { + result = this.getName().suffix(this.getPackage().getName().length() + 1) + or + result = this.getName() and not exists(this.getPackage()) + } - /** - * Whether this module is imported by 'import name'. For example on a linux system, - * the module 'posixpath' is imported as 'os.path' or as 'posixpath' - */ - predicate importedAs(string name) { PointsToInternal::module_imported_as(theModule(), name) } + /** + * Whether this module is imported by 'import name'. For example on a linux system, + * the module 'posixpath' is imported as 'os.path' or as 'posixpath' + */ + predicate importedAs(string name) { PointsToInternal::module_imported_as(theModule(), name) } - ModuleObject getAnImportedModule() { - result.importedAs(this.getModule().getAnImportedModuleName()) - } + ModuleObject getAnImportedModule() { + result.importedAs(this.getModule().getAnImportedModuleName()) + } - /** - * Gets the kind for this module. Will be one of - * "module", "script" or "plugin". - */ - string getKind() { result = getKindForModule(this) } + /** + * Gets the kind for this module. Will be one of + * "module", "script" or "plugin". + */ + string getKind() { result = getKindForModule(this) } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } } class BuiltinModuleObject extends ModuleObject { - BuiltinModuleObject() { this.asBuiltin().getClass() = theModuleType().asBuiltin() } + BuiltinModuleObject() { this.asBuiltin().getClass() = theModuleType().asBuiltin() } - override string getName() { result = this.asBuiltin().getName() } + override string getName() { result = this.asBuiltin().getName() } - override Object getAttribute(string name) { - result.asBuiltin() = this.asBuiltin().getMember(name) - } + override Object getAttribute(string name) { + result.asBuiltin() = this.asBuiltin().getMember(name) + } - override predicate hasAttribute(string name) { exists(this.asBuiltin().getMember(name)) } + override predicate hasAttribute(string name) { exists(this.asBuiltin().getMember(name)) } - deprecated override predicate exportsComplete() { any() } + deprecated override predicate exportsComplete() { any() } } class PythonModuleObject extends ModuleObject { - PythonModuleObject() { exists(Module m | m.getEntryNode() = this | not m.isPackage()) } + PythonModuleObject() { exists(Module m | m.getEntryNode() = this | not m.isPackage()) } - override string getName() { result = this.getModule().getName() } + override string getName() { result = this.getModule().getName() } - override Module getModule() { result = this.getOrigin() } + override Module getModule() { result = this.getOrigin() } - override Module getSourceModule() { result = this.getOrigin() } + override Module getSourceModule() { result = this.getOrigin() } - override Container getPath() { result = this.getModule().getFile() } + override Container getPath() { result = this.getModule().getFile() } - deprecated override predicate exportsComplete() { - exists(Module m | m = this.getModule() | - not exists(Call modify, Attribute attr, GlobalVariable all | - modify.getScope() = m and - modify.getFunc() = attr and - all.getId() = "__all__" - | - attr.getObject().(Name).uses(all) - ) - ) - } + deprecated override predicate exportsComplete() { + exists(Module m | m = this.getModule() | + not exists(Call modify, Attribute attr, GlobalVariable all | + modify.getScope() = m and + modify.getFunc() = attr and + all.getId() = "__all__" + | + attr.getObject().(Name).uses(all) + ) + ) + } } /** @@ -153,74 +153,74 @@ 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) { - result.asBuiltin().getClass() = theStrType().asBuiltin() and - exists(string repr | - repr = result.asBuiltin().getName() and - repr.charAt(1) = "'" - | - /* Strip quotes off repr */ - text = repr.substring(2, repr.length() - 1) - ) + result.asBuiltin().getClass() = theStrType().asBuiltin() and + exists(string repr | + repr = result.asBuiltin().getName() and + repr.charAt(1) = "'" + | + /* Strip quotes off repr */ + text = repr.substring(2, repr.length() - 1) + ) } class PackageObject extends ModuleObject { - PackageObject() { exists(Module p | p.getEntryNode() = this | p.isPackage()) } + PackageObject() { exists(Module p | p.getEntryNode() = this | p.isPackage()) } - override string getName() { result = this.getModule().getName() } + override string getName() { result = this.getModule().getName() } - override Module getModule() { result = this.getOrigin() } + override Module getModule() { result = this.getOrigin() } - override Module getSourceModule() { result = this.getModule().getInitModule() } + override Module getSourceModule() { result = this.getModule().getInitModule() } - override Container getPath() { result = this.getModule().getPath() } + override Container getPath() { result = this.getModule().getPath() } - ModuleObject submodule(string name) { - result.getPackage() = this and - name = result.getShortName() - } + ModuleObject submodule(string name) { + result.getPackage() = this and + name = result.getShortName() + } - override Object getAttribute(string name) { - exists(ObjectInternal val | - theModule().(PackageObjectInternal).attribute(name, val, _) and - result = val.getSource() - ) - } + override Object getAttribute(string name) { + exists(ObjectInternal val | + theModule().(PackageObjectInternal).attribute(name, val, _) and + result = val.getSource() + ) + } - PythonModuleObject getInitModule() { result.getModule() = this.getModule().getInitModule() } + PythonModuleObject getInitModule() { result.getModule() = this.getModule().getInitModule() } - /** Holds if this package has no `__init__.py` file. */ - predicate hasNoInitModule() { - not exists(Module m | - m.isPackageInit() and - m.getFile().getParent() = this.getPath() - ) - } + /** Holds if this package has no `__init__.py` file. */ + predicate hasNoInitModule() { + not exists(Module m | + m.isPackageInit() and + m.getFile().getParent() = this.getPath() + ) + } - deprecated override predicate exportsComplete() { - not exists(this.getInitModule()) - or - this.getInitModule().exportsComplete() - } + deprecated override predicate exportsComplete() { + not exists(this.getInitModule()) + or + this.getInitModule().exportsComplete() + } - override predicate hasAttribute(string name) { - exists(this.submodule(name)) - or - this.getInitModule().hasAttribute(name) - } + override predicate hasAttribute(string name) { + exists(this.submodule(name)) + or + this.getInitModule().hasAttribute(name) + } - Location getLocation() { none() } + Location getLocation() { none() } - override predicate hasLocationInfo(string path, int bl, int bc, int el, int ec) { - path = this.getPath().getName() and - bl = 0 and - bc = 0 and - el = 0 and - ec = 0 - } + override predicate hasLocationInfo(string path, int bl, int bc, int el, int ec) { + path = this.getPath().getName() and + bl = 0 and + bc = 0 and + el = 0 and + ec = 0 + } } /** Utility module for predicates relevant to the `ModuleObject` class. */ module ModuleObject { - /** Gets a `ModuleObject` called `name`, if it exists. */ - ModuleObject named(string name) { result.getName() = name } + /** Gets a `ModuleObject` called `name`, if it exists. */ + ModuleObject named(string name) { result.getName() = name } } diff --git a/python/ql/src/semmle/python/types/Object.qll b/python/ql/src/semmle/python/types/Object.qll index 0bfc7dd0059..7f6bc9f0e36 100644 --- a/python/ql/src/semmle/python/types/Object.qll +++ b/python/ql/src/semmle/python/types/Object.qll @@ -5,12 +5,12 @@ private import semmle.python.types.Builtins cached private 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 IntegerLiteral and - not obj.(ControlFlowNode).getNode() instanceof StrConst - or - obj instanceof Builtin + /* 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 IntegerLiteral and + not obj.(ControlFlowNode).getNode() instanceof StrConst + or + obj instanceof Builtin } /** @@ -30,186 +30,191 @@ private predicate is_an_object(@py_object obj) { * there is a one-to-one relation. */ class Object extends @py_object { - Object() { is_an_object(this) } + Object() { is_an_object(this) } - /** - * Gets an inferred type for this object, without using inter-procedural analysis. - * WARNING: The lack of context makes this less accurate than f.refersTo(this, result, _) - * for a control flow node 'f' - */ - ClassObject getAnInferredType() { - exists(ControlFlowNode somewhere | somewhere.refersTo(this, result, _)) - or - this.asBuiltin().getClass() = result.asBuiltin() and not this = unknownValue() - or - this = unknownValue() and result = theUnknownType() - } + /** + * Gets an inferred type for this object, without using inter-procedural analysis. + * WARNING: The lack of context makes this less accurate than f.refersTo(this, result, _) + * for a control flow node 'f' + */ + ClassObject getAnInferredType() { + exists(ControlFlowNode somewhere | somewhere.refersTo(this, result, _)) + or + this.asBuiltin().getClass() = result.asBuiltin() and not this = unknownValue() + or + this = unknownValue() and result = theUnknownType() + } - /** - * Whether this is 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() { exists(this.asBuiltin()) } + /** + * Whether this is 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() { exists(this.asBuiltin()) } - /** Retained for backwards compatibility. See Object.isBuiltin() */ - predicate isC() { this.isBuiltin() } + /** Retained for backwards compatibility. See Object.isBuiltin() */ + predicate isC() { this.isBuiltin() } - /** - * Gets the point in the source code from which this object "originates". - * - * WARNING: The lack of context makes this less accurate than f.refersTo(this, _, result) - * for a control flow node 'f'. - */ - AstNode getOrigin() { py_flow_bb_node(this, result, _, _) } + /** + * Gets the point in the source code from which this object "originates". + * + * WARNING: The lack of context makes this less accurate than f.refersTo(this, _, result) + * for a control flow node 'f'. + */ + AstNode getOrigin() { py_flow_bb_node(this, result, _, _) } - private predicate hasOrigin() { py_flow_bb_node(this, _, _, _) } + private predicate hasOrigin() { py_flow_bb_node(this, _, _, _) } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { this.hasOrigin() and this.getOrigin().getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - or - not this.hasOrigin() and - filepath = ":Compiled Code" and - startline = 0 and - startcolumn = 0 and - endline = 0 and - endcolumn = 0 - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.hasOrigin() and + this + .getOrigin() + .getLocation() + .hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + or + not this.hasOrigin() and + filepath = ":Compiled Code" and + startline = 0 and + startcolumn = 0 and + endline = 0 and + endcolumn = 0 + } - /** INTERNAL -- Do not use */ - Builtin asBuiltin() { result = this } + /** INTERNAL -- Do not use */ + Builtin asBuiltin() { result = this } - /** Gets a textual representation of this element. */ - string toString() { - not this = undefinedVariable() and - not this = unknownValue() and - exists(ClassObject type | type.asBuiltin() = this.asBuiltin().getClass() | - result = type.getName() + " " + this.asBuiltin().getName() - ) - or - result = this.getOrigin().toString() - } + /** Gets a textual representation of this element. */ + string toString() { + not this = undefinedVariable() and + not this = unknownValue() and + exists(ClassObject type | type.asBuiltin() = this.asBuiltin().getClass() | + result = type.getName() + " " + this.asBuiltin().getName() + ) + or + result = this.getOrigin().toString() + } - /** - * Gets the class of this object for simple cases, namely constants, functions, - * comprehensions and built-in objects. - * - * This exists primarily for internal use. Use getAnInferredType() instead. - */ - cached - ClassObject simpleClass() { - result = comprehension(this.getOrigin()) - or - result = collection_literal(this.getOrigin()) - or - result = string_literal(this.getOrigin()) - or - this.getOrigin() instanceof CallableExpr and result = thePyFunctionType() - or - this.getOrigin() instanceof Module and result = theModuleType() - or - result.(Object).asBuiltin() = this.asBuiltin().getClass() - } + /** + * Gets the class of this object for simple cases, namely constants, functions, + * comprehensions and built-in objects. + * + * This exists primarily for internal use. Use getAnInferredType() instead. + */ + cached + ClassObject simpleClass() { + result = comprehension(this.getOrigin()) + or + result = collection_literal(this.getOrigin()) + or + result = string_literal(this.getOrigin()) + or + this.getOrigin() instanceof CallableExpr and result = thePyFunctionType() + or + this.getOrigin() instanceof Module and result = theModuleType() + or + result.(Object).asBuiltin() = this.asBuiltin().getClass() + } - private ClassObject declaringClass(string name) { result.declaredAttribute(name) = this } + private ClassObject declaringClass(string name) { result.declaredAttribute(name) = this } - /** - * Whether this overrides o. In this context, "overrides" means that this object - * is a named attribute of a some class C and `o` is a named attribute of another - * class S, both attributes having the same name, and S is a super class of C. - */ - predicate overrides(Object o) { - exists(string name | declaringClass(name).getASuperType() = o.declaringClass(name)) - } + /** + * Whether this overrides o. In this context, "overrides" means that this object + * is a named attribute of a some class C and `o` is a named attribute of another + * class S, both attributes having the same name, and S is a super class of C. + */ + predicate overrides(Object o) { + exists(string name | declaringClass(name).getASuperType() = o.declaringClass(name)) + } - private boolean booleanFromValue() { - exists(ObjectInternal obj | obj.getSource() = this | result = obj.booleanValue()) - } + private boolean booleanFromValue() { + exists(ObjectInternal obj | obj.getSource() = this | result = obj.booleanValue()) + } - /** - * The Boolean value of this object if it always evaluates to true or false. - * For example: - * false for None, true for 7 and no result for int(x) - */ - boolean booleanValue() { - result = this.booleanFromValue() and - not this.maybe() - } + /** + * The Boolean value of this object if it always evaluates to true or false. + * For example: + * false for None, true for 7 and no result for int(x) + */ + boolean booleanValue() { + result = this.booleanFromValue() and + not this.maybe() + } - final predicate maybe() { - booleanFromValue() = true and - booleanFromValue() = false - } + final predicate maybe() { + booleanFromValue() = true and + booleanFromValue() = false + } - predicate notClass() { any() } + predicate notClass() { any() } - /** - * Holds if this object can be referred to by `longName` - * For example, the modules `dict` in the `sys` module - * has the long name `sys.modules` and the name `os.path.join` - * will refer to the path joining function even though it might - * be declared in the `posix` or `nt` modules. - * Long names can have no more than three dots after the module name. - */ - cached - predicate hasLongName(string longName) { - this = findByName0(longName) - or - this = findByName1(longName) - or - this = findByName2(longName) - or - this = findByName3(longName) - or - exists(ClassMethodObject cm | - cm.hasLongName(longName) and - cm.getFunction() = this - ) - or - exists(StaticMethodObject cm | - cm.hasLongName(longName) and - cm.getFunction() = this - ) - } + /** + * Holds if this object can be referred to by `longName` + * For example, the modules `dict` in the `sys` module + * has the long name `sys.modules` and the name `os.path.join` + * will refer to the path joining function even though it might + * be declared in the `posix` or `nt` modules. + * Long names can have no more than three dots after the module name. + */ + cached + predicate hasLongName(string longName) { + this = findByName0(longName) + or + this = findByName1(longName) + or + this = findByName2(longName) + or + this = findByName3(longName) + or + exists(ClassMethodObject cm | + cm.hasLongName(longName) and + cm.getFunction() = this + ) + or + exists(StaticMethodObject cm | + cm.hasLongName(longName) and + cm.getFunction() = this + ) + } } private Object findByName0(string longName) { result.(ModuleObject).getName() = longName } private Object findByName1(string longName) { - exists(string owner, string attrname | longName = owner + "." + attrname | - result = findByName0(owner).(ModuleObject).attr(attrname) - or - result = findByName0(owner).(ClassObject).lookupAttribute(attrname) - ) and - not result = findByName0(_) + exists(string owner, string attrname | longName = owner + "." + attrname | + result = findByName0(owner).(ModuleObject).attr(attrname) + or + result = findByName0(owner).(ClassObject).lookupAttribute(attrname) + ) and + not result = findByName0(_) } private Object findByName2(string longName) { - exists(string owner, string attrname | longName = owner + "." + attrname | - result = findByName1(owner).(ModuleObject).attr(attrname) - or - result = findByName1(owner).(ClassObject).lookupAttribute(attrname) - ) and - not result = findByName0(_) and - not result = findByName1(_) + exists(string owner, string attrname | longName = owner + "." + attrname | + result = findByName1(owner).(ModuleObject).attr(attrname) + or + result = findByName1(owner).(ClassObject).lookupAttribute(attrname) + ) and + not result = findByName0(_) and + not result = findByName1(_) } private Object findByName3(string longName) { - exists(string owner, string attrname | longName = owner + "." + attrname | - result = findByName2(owner).(ModuleObject).attr(attrname) - or - result = findByName2(owner).(ClassObject).lookupAttribute(attrname) - ) and - not result = findByName0(_) and - not result = findByName1(_) and - not result = findByName2(_) + exists(string owner, string attrname | longName = owner + "." + attrname | + result = findByName2(owner).(ModuleObject).attr(attrname) + or + result = findByName2(owner).(ClassObject).lookupAttribute(attrname) + ) and + not result = findByName0(_) and + not result = findByName1(_) and + not result = findByName2(_) } /** @@ -218,50 +223,50 @@ private Object findByName3(string longName) { * or in a builtin module as a value. */ class NumericObject extends Object { - NumericObject() { - this.asBuiltin().getClass() = theIntType().asBuiltin() or - this.asBuiltin().getClass() = theLongType().asBuiltin() or - this.asBuiltin().getClass() = theFloatType().asBuiltin() - } + NumericObject() { + this.asBuiltin().getClass() = theIntType().asBuiltin() or + this.asBuiltin().getClass() = theLongType().asBuiltin() or + this.asBuiltin().getClass() = theFloatType().asBuiltin() + } - /** - * Gets the Boolean value that this object - * would evaluate to in a Boolean context, - * such as `bool(x)` or `if x: ...` - */ - override boolean booleanValue() { - this.intValue() != 0 and result = true - or - this.intValue() = 0 and result = false - or - this.floatValue() != 0 and result = true - or - this.floatValue() = 0 and result = false - } + /** + * Gets the Boolean value that this object + * would evaluate to in a Boolean context, + * such as `bool(x)` or `if x: ...` + */ + override boolean booleanValue() { + this.intValue() != 0 and result = true + or + this.intValue() = 0 and result = false + or + this.floatValue() != 0 and result = true + or + this.floatValue() = 0 and result = false + } - /** Gets the value of this object if it is a constant integer and it fits in a QL int */ - int intValue() { - ( - this.asBuiltin().getClass() = theIntType().asBuiltin() or - this.asBuiltin().getClass() = theLongType().asBuiltin() - ) and - result = this.asBuiltin().getName().toInt() - } + /** Gets the value of this object if it is a constant integer and it fits in a QL int */ + int intValue() { + ( + this.asBuiltin().getClass() = theIntType().asBuiltin() or + this.asBuiltin().getClass() = theLongType().asBuiltin() + ) and + result = this.asBuiltin().getName().toInt() + } - /** Gets the value of this object if it is a constant float */ - float floatValue() { - this.asBuiltin().getClass() = theFloatType().asBuiltin() and - result = this.asBuiltin().getName().toFloat() - } + /** Gets the value of this object if it is a constant float */ + float floatValue() { + this.asBuiltin().getClass() = theFloatType().asBuiltin() and + result = this.asBuiltin().getName().toFloat() + } - /** Gets the string representation of this object, equivalent to calling repr() in Python */ - string repr() { - exists(string s | s = this.asBuiltin().getName() | - if this.asBuiltin().getClass() = theLongType().asBuiltin() - then result = s + "L" - else result = s - ) - } + /** Gets the string representation of this object, equivalent to calling repr() in Python */ + string repr() { + exists(string s | s = this.asBuiltin().getName() | + if this.asBuiltin().getClass() = theLongType().asBuiltin() + then result = s + "L" + else result = s + ) + } } /** @@ -270,28 +275,28 @@ class NumericObject extends Object { * or in a builtin module as a value. */ class StringObject extends Object { - StringObject() { - this.asBuiltin().getClass() = theUnicodeType().asBuiltin() or - this.asBuiltin().getClass() = theBytesType().asBuiltin() - } + StringObject() { + this.asBuiltin().getClass() = theUnicodeType().asBuiltin() or + this.asBuiltin().getClass() = theBytesType().asBuiltin() + } - /** Whether this string is composed entirely of ascii encodable characters */ - predicate isAscii() { this.getText().regexpMatch("^\\p{ASCII}*$") } + /** Whether this string is composed entirely of ascii encodable characters */ + predicate isAscii() { this.getText().regexpMatch("^\\p{ASCII}*$") } - override boolean booleanValue() { - this.getText() = "" and result = false - or - this.getText() != "" and result = true - } + override boolean booleanValue() { + this.getText() = "" and result = false + or + this.getText() != "" and result = true + } - /** Gets the text for this string */ - cached - string getText() { - exists(string quoted_string | - quoted_string = this.asBuiltin().getName() and - result = quoted_string.regexpCapture("[bu]'([\\s\\S]*)'", 1) - ) - } + /** Gets the text for this string */ + cached + string getText() { + exists(string quoted_string | + quoted_string = this.asBuiltin().getName() and + result = quoted_string.regexpCapture("[bu]'([\\s\\S]*)'", 1) + ) + } } /** @@ -300,58 +305,58 @@ class StringObject extends Object { * or in a builtin module as a value. */ abstract class SequenceObject extends Object { - /** Gets the length of this sequence */ - int getLength() { - result = strictcount(this.getBuiltinElement(_)) - or - result = strictcount(this.getSourceElement(_)) - } + /** Gets the length of this sequence */ + int getLength() { + result = strictcount(this.getBuiltinElement(_)) + or + result = strictcount(this.getSourceElement(_)) + } - /** Gets the nth item of this builtin sequence */ - Object getBuiltinElement(int n) { result.asBuiltin() = this.asBuiltin().getItem(n) } + /** Gets the nth item of this builtin sequence */ + Object getBuiltinElement(int n) { result.asBuiltin() = this.asBuiltin().getItem(n) } - /** Gets the nth source element of this sequence */ - ControlFlowNode getSourceElement(int n) { result = this.(SequenceNode).getElement(n) } + /** Gets the nth source element of this sequence */ + ControlFlowNode getSourceElement(int n) { result = this.(SequenceNode).getElement(n) } - Object getInferredElement(int n) { - result = this.getBuiltinElement(n) - or - this.getSourceElement(n).refersTo(result) - } + Object getInferredElement(int n) { + result = this.getBuiltinElement(n) + or + this.getSourceElement(n).refersTo(result) + } } class TupleObject extends SequenceObject { - TupleObject() { - this.asBuiltin().getClass() = theTupleType().asBuiltin() - or - this instanceof TupleNode - or - exists(Function func | func.getVararg().getAFlowNode() = this) - } + TupleObject() { + this.asBuiltin().getClass() = theTupleType().asBuiltin() + or + this instanceof TupleNode + or + exists(Function func | func.getVararg().getAFlowNode() = this) + } } module TupleObject { - TupleObject empty() { - exists(Builtin empty | - empty = result.asBuiltin() and - empty.getClass() = theTupleType().asBuiltin() and - not exists(empty.getItem(_)) - ) - } + TupleObject empty() { + exists(Builtin empty | + empty = result.asBuiltin() and + empty.getClass() = theTupleType().asBuiltin() and + not exists(empty.getItem(_)) + ) + } } class NonEmptyTupleObject extends TupleObject { - NonEmptyTupleObject() { exists(Function func | func.getVararg().getAFlowNode() = this) } + NonEmptyTupleObject() { exists(Function func | func.getVararg().getAFlowNode() = this) } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } } class ListObject extends SequenceObject { - ListObject() { - this.asBuiltin().getClass() = theListType().asBuiltin() - or - this instanceof ListNode - } + ListObject() { + this.asBuiltin().getClass() = theListType().asBuiltin() + or + this instanceof ListNode + } } /** The `builtin` module */ @@ -394,62 +399,62 @@ deprecated Object theNotImplementedObject() { result = Object::builtin("NotImple deprecated Object theEmptyTupleObject() { result = TupleObject::empty() } module Object { - Object builtin(string name) { result.asBuiltin() = Builtin::builtin(name) } + Object builtin(string name) { result.asBuiltin() = Builtin::builtin(name) } - /** The named quitter object (quit or exit) in the builtin namespace */ - Object quitter(string name) { - (name = "quit" or name = "exit") and - result = builtin(name) - } + /** The named quitter object (quit or exit) in the builtin namespace */ + Object quitter(string name) { + (name = "quit" or name = "exit") and + result = builtin(name) + } - /** The builtin object `NotImplemented`. Not be confused with `NotImplementedError`. */ - Object notImplemented() { result = builtin("NotImplemented") } + /** The builtin object `NotImplemented`. Not be confused with `NotImplementedError`. */ + Object notImplemented() { result = builtin("NotImplemented") } } private ClassObject comprehension(Expr e) { - e instanceof ListComp and result = theListType() - or - e instanceof SetComp and result = theSetType() - or - e instanceof DictComp and result = theDictType() - or - e instanceof GeneratorExp and result = theGeneratorType() + e instanceof ListComp and result = theListType() + or + e instanceof SetComp and result = theSetType() + or + e instanceof DictComp and result = theDictType() + or + e instanceof GeneratorExp and result = theGeneratorType() } private ClassObject collection_literal(Expr e) { - e instanceof List and result = theListType() - or - e instanceof Set and result = theSetType() - or - e instanceof Dict and result = theDictType() - or - e instanceof Tuple and result = theTupleType() + e instanceof List and result = theListType() + or + e instanceof Set and result = theSetType() + or + e instanceof Dict and result = theDictType() + or + e instanceof Tuple and result = theTupleType() } private ClassObject string_literal(Expr e) { - e instanceof Bytes and result = theBytesType() - or - e instanceof Unicode and result = theUnicodeType() + e instanceof Bytes and result = theBytesType() + or + e instanceof Unicode and result = theUnicodeType() } Object theUnknownType() { result.asBuiltin() = Builtin::unknownType() } /* For backwards compatibility */ class SuperBoundMethod extends Object { - string name; + string name; - SuperBoundMethod() { - this.(AttrNode).getObject(name).inferredValue().getClass() = Value::named("super") - } + SuperBoundMethod() { + this.(AttrNode).getObject(name).inferredValue().getClass() = Value::named("super") + } - override string toString() { result = "super()." + name } + override string toString() { result = "super()." + name } - Object getFunction(string fname) { - fname = name and - exists(SuperInstance sup, BoundMethodObjectInternal m | - sup = this.(AttrNode).getObject(name).inferredValue() and - sup.attribute(name, m, _) and - result = m.getFunction().getSource() - ) - } + Object getFunction(string fname) { + fname = name and + exists(SuperInstance sup, BoundMethodObjectInternal m | + sup = this.(AttrNode).getObject(name).inferredValue() and + sup.attribute(name, m, _) and + result = m.getFunction().getSource() + ) + } } diff --git a/python/ql/src/semmle/python/types/Properties.qll b/python/ql/src/semmle/python/types/Properties.qll index 207562c63c6..09bd08b6c15 100644 --- a/python/ql/src/semmle/python/types/Properties.qll +++ b/python/ql/src/semmle/python/types/Properties.qll @@ -9,100 +9,100 @@ import python * Also any instances of types.GetSetDescriptorType (which are equivalent, but implemented in C) */ abstract class PropertyObject extends Object { - PropertyObject() { - property_getter(this, _) - or - this.asBuiltin().getClass() = theBuiltinPropertyType().asBuiltin() - } + PropertyObject() { + property_getter(this, _) + or + this.asBuiltin().getClass() = theBuiltinPropertyType().asBuiltin() + } - /** Gets the name of this property */ - abstract string getName(); + /** Gets the name of this property */ + abstract string getName(); - /** Gets the getter of this property */ - abstract Object getGetter(); + /** Gets the getter of this property */ + abstract Object getGetter(); - /** Gets the setter of this property */ - abstract Object getSetter(); + /** Gets the setter of this property */ + abstract Object getSetter(); - /** Gets the deleter of this property */ - abstract Object getDeleter(); + /** Gets the deleter of this property */ + abstract Object getDeleter(); - override string toString() { result = "Property " + this.getName() } + override string toString() { result = "Property " + this.getName() } - /** Whether this property is read-only. */ - predicate isReadOnly() { not exists(this.getSetter()) } + /** Whether this property is read-only. */ + predicate isReadOnly() { not exists(this.getSetter()) } - /** - * Gets an inferred type of this property. - * That is the type returned by its getter function, - * not the type of the property object which is types.PropertyType. - */ - abstract ClassObject getInferredPropertyType(); + /** + * Gets an inferred type of this property. + * That is the type returned by its getter function, + * not the type of the property object which is types.PropertyType. + */ + abstract ClassObject getInferredPropertyType(); } class PythonPropertyObject extends PropertyObject { - PythonPropertyObject() { property_getter(this, _) } + PythonPropertyObject() { property_getter(this, _) } - override string getName() { result = this.getGetter().getName() } + override string getName() { result = this.getGetter().getName() } - /** Gets the getter function of this property */ - override FunctionObject getGetter() { property_getter(this, result) } + /** Gets the getter function of this property */ + override FunctionObject getGetter() { property_getter(this, result) } - override ClassObject getInferredPropertyType() { - result = this.getGetter().getAnInferredReturnType() - } + override ClassObject getInferredPropertyType() { + result = this.getGetter().getAnInferredReturnType() + } - /** Gets the setter function of this property */ - override FunctionObject getSetter() { property_setter(this, result) } + /** Gets the setter function of this property */ + override FunctionObject getSetter() { property_setter(this, result) } - /** Gets the deleter function of this property */ - override FunctionObject getDeleter() { property_deleter(this, result) } + /** Gets the deleter function of this property */ + override FunctionObject getDeleter() { property_deleter(this, result) } } class BuiltinPropertyObject extends PropertyObject { - BuiltinPropertyObject() { this.asBuiltin().getClass() = theBuiltinPropertyType().asBuiltin() } + BuiltinPropertyObject() { this.asBuiltin().getClass() = theBuiltinPropertyType().asBuiltin() } - override string getName() { result = this.asBuiltin().getName() } + override string getName() { result = this.asBuiltin().getName() } - /** Gets the getter method wrapper of this property */ - override Object getGetter() { result.asBuiltin() = this.asBuiltin().getMember("__get__") } + /** Gets the getter method wrapper of this property */ + override Object getGetter() { result.asBuiltin() = this.asBuiltin().getMember("__get__") } - override ClassObject getInferredPropertyType() { none() } + override ClassObject getInferredPropertyType() { none() } - /** Gets the setter method wrapper of this property */ - override Object getSetter() { result.asBuiltin() = this.asBuiltin().getMember("__set__") } + /** Gets the setter method wrapper of this property */ + override Object getSetter() { result.asBuiltin() = this.asBuiltin().getMember("__set__") } - /** Gets the deleter method wrapper of this property */ - override Object getDeleter() { result.asBuiltin() = this.asBuiltin().getMember("__delete__") } + /** Gets the deleter method wrapper of this property */ + override Object getDeleter() { result.asBuiltin() = this.asBuiltin().getMember("__delete__") } } private predicate property_getter(CallNode decorated, FunctionObject getter) { - decorated.getFunction().refersTo(thePropertyType()) and - decorated.getArg(0).refersTo(getter) + decorated.getFunction().refersTo(thePropertyType()) and + decorated.getArg(0).refersTo(getter) } private predicate property_setter(CallNode decorated, FunctionObject setter) { - property_getter(decorated, _) and - exists(CallNode setter_call, AttrNode prop_setter | - prop_setter.getObject("setter").refersTo(decorated.(Object)) - | - setter_call.getArg(0).refersTo(setter) and - setter_call.getFunction() = prop_setter - ) - or - decorated.getFunction().refersTo(thePropertyType()) and - decorated.getArg(1).refersTo(setter) + property_getter(decorated, _) and + exists(CallNode setter_call, AttrNode prop_setter | + prop_setter.getObject("setter").refersTo(decorated.(Object)) + | + setter_call.getArg(0).refersTo(setter) and + setter_call.getFunction() = prop_setter + ) + or + decorated.getFunction().refersTo(thePropertyType()) and + decorated.getArg(1).refersTo(setter) } private predicate property_deleter(CallNode decorated, FunctionObject deleter) { - property_getter(decorated, _) and - exists(CallNode deleter_call, AttrNode prop_deleter | - prop_deleter.getObject("deleter").refersTo(decorated.(Object)) - | - deleter_call.getArg(0).refersTo(deleter) and - deleter_call.getFunction() = prop_deleter - ) - or - decorated.getFunction().refersTo(thePropertyType()) and - decorated.getArg(2).refersTo(deleter) + property_getter(decorated, _) and + exists(CallNode deleter_call, AttrNode prop_deleter | + prop_deleter.getObject("deleter").refersTo(decorated.(Object)) + | + deleter_call.getArg(0).refersTo(deleter) and + deleter_call.getFunction() = prop_deleter + ) + or + decorated.getFunction().refersTo(thePropertyType()) and + decorated.getArg(2).refersTo(deleter) } diff --git a/python/ql/src/semmle/python/types/Version.qll b/python/ql/src/semmle/python/types/Version.qll index 26ab46e970a..ee34387ba8c 100644 --- a/python/ql/src/semmle/python/types/Version.qll +++ b/python/ql/src/semmle/python/types/Version.qll @@ -5,12 +5,12 @@ import python * Currently only 2.7 or 3.x but may include different sets of versions in the future. */ class Version extends int { - Version() { this = 2 or this = 3 } + Version() { this = 2 or this = 3 } - /** Holds if this version (or set of versions) includes the version `major`.`minor` */ - predicate includes(int major, int minor) { - this = 2 and major = 2 and minor = 7 - or - this = 3 and major = 3 and minor in [4 .. 8] - } + /** Holds if this version (or set of versions) includes the version `major`.`minor` */ + predicate includes(int major, int minor) { + this = 2 and major = 2 and minor = 7 + or + this = 3 and major = 3 and minor in [4 .. 8] + } } diff --git a/python/ql/src/semmle/python/values/StringAttributes.qll b/python/ql/src/semmle/python/values/StringAttributes.qll index 1209313eaf1..a7a8ef00f00 100644 --- a/python/ql/src/semmle/python/values/StringAttributes.qll +++ b/python/ql/src/semmle/python/values/StringAttributes.qll @@ -1,82 +1,82 @@ import python predicate string_attribute_all(ControlFlowNode n, string attr) { - (n.getNode() instanceof Unicode or n.getNode() instanceof Bytes) and - attr = "const" - or - exists(Object s | - n.refersTo(s, theBytesType(), _) and - attr = "bytes" and - // We are only interested in bytes if they may cause an exception if - // implicitly converted to unicode. ASCII is safe. - not s.(StringObject).isAscii() - ) + (n.getNode() instanceof Unicode or n.getNode() instanceof Bytes) and + attr = "const" + or + exists(Object s | + n.refersTo(s, theBytesType(), _) and + attr = "bytes" and + // We are only interested in bytes if they may cause an exception if + // implicitly converted to unicode. ASCII is safe. + not s.(StringObject).isAscii() + ) } predicate tracked_object(ControlFlowNode obj, string attr) { - tracked_object_all(obj, attr) - or - tracked_object_any(obj, attr) + tracked_object_all(obj, attr) + or + tracked_object_any(obj, attr) } predicate open_file(Object obj) { obj.(CallNode).getFunction().refersTo(Object::builtin("open")) } predicate string_attribute_any(ControlFlowNode n, string attr) { - attr = "user-input" and - exists(Object input | n.(CallNode).getFunction().refersTo(input) | - if major_version() = 2 - then input = Object::builtin("raw_input") - else input = Object::builtin("input") - ) - or - attr = "file-input" and - exists(Object fd | n.(CallNode).getFunction().(AttrNode).getObject("read").refersTo(fd) | - open_file(fd) - ) - or - n.refersTo(_, theUnicodeType(), _) and attr = "unicode" + attr = "user-input" and + exists(Object input | n.(CallNode).getFunction().refersTo(input) | + if major_version() = 2 + then input = Object::builtin("raw_input") + else input = Object::builtin("input") + ) + or + attr = "file-input" and + exists(Object fd | n.(CallNode).getFunction().(AttrNode).getObject("read").refersTo(fd) | + open_file(fd) + ) + or + n.refersTo(_, theUnicodeType(), _) and attr = "unicode" } predicate tracked_object_any(ControlFlowNode obj, string attr) { - string_attribute_any(obj, attr) - or - exists(ControlFlowNode other | tracking_step(other, obj) | tracked_object_any(other, attr)) + string_attribute_any(obj, attr) + or + exists(ControlFlowNode other | tracking_step(other, obj) | tracked_object_any(other, attr)) } predicate tracked_object_all(ControlFlowNode obj, string attr) { - string_attribute_all(obj, attr) - or - forex(ControlFlowNode other | tracking_step(other, obj) | tracked_object_all(other, attr)) + string_attribute_all(obj, attr) + or + forex(ControlFlowNode other | tracking_step(other, obj) | tracked_object_all(other, attr)) } predicate tracked_call_step(ControlFlowNode ret, ControlFlowNode call) { - exists(FunctionObject func, Return r | - func.getACall() = call and - func.getFunction() = r.getScope() and - r.getValue() = ret.getNode() - ) + exists(FunctionObject func, Return r | + func.getACall() = call and + func.getFunction() = r.getScope() and + r.getValue() = ret.getNode() + ) } ControlFlowNode sequence_for_iterator(ControlFlowNode f) { - exists(For for | f.getNode() = for.getTarget() | - result.getNode() = for.getIter() and - result.getBasicBlock().dominates(f.getBasicBlock()) - ) + exists(For for | f.getNode() = for.getTarget() | + result.getNode() = for.getIter() and + result.getBasicBlock().dominates(f.getBasicBlock()) + ) } pragma[noinline] private predicate tracking_step(ControlFlowNode src, ControlFlowNode dest) { - src = dest.(BinaryExprNode).getAnOperand() - or - src = dest.(UnaryExprNode).getOperand() - or - src = sequence_for_iterator(dest) - or - src = dest.(AttrNode).getObject() - or - src = dest.(SubscriptNode).getObject() - or - tracked_call_step(src, dest) - or - dest.refersTo(src.(Object)) + src = dest.(BinaryExprNode).getAnOperand() + or + src = dest.(UnaryExprNode).getOperand() + or + src = sequence_for_iterator(dest) + or + src = dest.(AttrNode).getObject() + or + src = dest.(SubscriptNode).getObject() + or + tracked_call_step(src, dest) + or + dest.refersTo(src.(Object)) } diff --git a/python/ql/src/semmle/python/web/Http.qll b/python/ql/src/semmle/python/web/Http.qll index f8724554fc2..527a050d814 100644 --- a/python/ql/src/semmle/python/web/Http.qll +++ b/python/ql/src/semmle/python/web/Http.qll @@ -11,32 +11,32 @@ abstract class HttpRequestTaintSource extends TaintSource { } * As specified in PEP 3333. https://www.python.org/dev/peps/pep-3333/#environ-variables */ class WsgiEnvironment extends TaintKind { - WsgiEnvironment() { this = "wsgi.environment" } + WsgiEnvironment() { this = "wsgi.environment" } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - result = this and Implementation::copyCall(fromnode, tonode) - or - result = this and - tonode.(CallNode).getFunction().pointsTo(ClassValue::dict()) and - tonode.(CallNode).getArg(0) = fromnode - or - exists(Value key, string text | - tonode.(CallNode).getFunction().(AttrNode).getObject("get") = fromnode and - tonode.(CallNode).getArg(0).pointsTo(key) - or - tonode.(SubscriptNode).getObject() = fromnode and - tonode.isLoad() and - tonode.(SubscriptNode).getIndex().pointsTo(key) - | - key = Value::forString(text) and - result instanceof ExternalStringKind and - ( - text = "QUERY_STRING" or - text = "PATH_INFO" or - text.prefix(5) = "HTTP_" - ) - ) - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + result = this and Implementation::copyCall(fromnode, tonode) + or + result = this and + tonode.(CallNode).getFunction().pointsTo(ClassValue::dict()) and + tonode.(CallNode).getArg(0) = fromnode + or + exists(Value key, string text | + tonode.(CallNode).getFunction().(AttrNode).getObject("get") = fromnode and + tonode.(CallNode).getArg(0).pointsTo(key) + or + tonode.(SubscriptNode).getObject() = fromnode and + tonode.isLoad() and + tonode.(SubscriptNode).getIndex().pointsTo(key) + | + key = Value::forString(text) and + result instanceof ExternalStringKind and + ( + text = "QUERY_STRING" or + text = "PATH_INFO" or + text.prefix(5) = "HTTP_" + ) + ) + } } /** @@ -44,31 +44,31 @@ class WsgiEnvironment extends TaintKind { * typically an instance of `http.cookies.Morsel` */ class UntrustedMorsel extends TaintKind { - UntrustedMorsel() { this = "http.Morsel" } + UntrustedMorsel() { this = "http.Morsel" } - override TaintKind getTaintOfAttribute(string name) { - result instanceof ExternalStringKind and - name = "value" - } + override TaintKind getTaintOfAttribute(string name) { + result instanceof ExternalStringKind and + name = "value" + } } /** A standard cookie object from a HTTP request, typically an instance of `http.cookies.SimpleCookie` */ class UntrustedCookie extends TaintKind { - UntrustedCookie() { this = "http.Cookie" } + UntrustedCookie() { this = "http.Cookie" } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - tonode.(SubscriptNode).getObject() = fromnode and - result instanceof UntrustedMorsel - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + tonode.(SubscriptNode).getObject() = fromnode and + result instanceof UntrustedMorsel + } } abstract class CookieOperation extends @py_flow_node { - /** Gets a textual representation of this element. */ - abstract string toString(); + /** Gets a textual representation of this element. */ + abstract string toString(); - abstract ControlFlowNode getKey(); + abstract ControlFlowNode getKey(); - abstract ControlFlowNode getValue(); + abstract ControlFlowNode getValue(); } abstract class CookieGet extends CookieOperation { } @@ -77,43 +77,43 @@ abstract class CookieSet extends CookieOperation { } /** Generic taint sink in a http response */ abstract class HttpResponseTaintSink extends TaintSink { - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } abstract class HttpRedirectTaintSink extends TaintSink { - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } module Client { - // TODO: user-input in other than URL: - // - `data`, `json` for `requests.post` - // - `body` for `HTTPConnection.request` - // - headers? - // TODO: Add more library support - // - urllib3 https://github.com/urllib3/urllib3 - // - httpx https://github.com/encode/httpx + // TODO: user-input in other than URL: + // - `data`, `json` for `requests.post` + // - `body` for `HTTPConnection.request` + // - headers? + // TODO: Add more library support + // - urllib3 https://github.com/urllib3/urllib3 + // - httpx https://github.com/encode/httpx + /** + * An outgoing http request + * + * For example: + * conn = HTTPConnection('example.com') + * conn.request('GET', '/path') + */ + abstract class HttpRequest extends ControlFlowNode { /** - * An outgoing http request + * Get any ControlFlowNode that is used to construct the final URL. * - * For example: - * conn = HTTPConnection('example.com') - * conn.request('GET', '/path') + * In the HTTPConnection example, there is a result for both `'example.com'` and for `'/path'`. */ - abstract class HttpRequest extends ControlFlowNode { - /** - * Get any ControlFlowNode that is used to construct the final URL. - * - * In the HTTPConnection example, there is a result for both `'example.com'` and for `'/path'`. - */ - abstract ControlFlowNode getAUrlPart(); + abstract ControlFlowNode getAUrlPart(); - abstract string getMethodUpper(); - } + abstract string getMethodUpper(); + } - /** Taint sink for the URL-part of an outgoing http request */ - class HttpRequestUrlTaintSink extends TaintSink { - HttpRequestUrlTaintSink() { this = any(HttpRequest r).getAUrlPart() } + /** Taint sink for the URL-part of an outgoing http request */ + class HttpRequestUrlTaintSink extends TaintSink { + HttpRequestUrlTaintSink() { this = any(HttpRequest r).getAUrlPart() } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } - } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + } } diff --git a/python/ql/src/semmle/python/web/HttpConstants.qll b/python/ql/src/semmle/python/web/HttpConstants.qll index 41f3905b887..5d39d517fc9 100644 --- a/python/ql/src/semmle/python/web/HttpConstants.qll +++ b/python/ql/src/semmle/python/web/HttpConstants.qll @@ -1,12 +1,12 @@ /** Gets an http verb */ string httpVerb() { - result = "GET" or - result = "POST" or - result = "PUT" or - result = "PATCH" or - result = "DELETE" or - result = "OPTIONS" or - result = "HEAD" + result = "GET" or + result = "POST" or + result = "PUT" or + result = "PATCH" or + result = "DELETE" or + result = "OPTIONS" or + result = "HEAD" } /** Gets an http verb, in lower case */ diff --git a/python/ql/src/semmle/python/web/bottle/General.qll b/python/ql/src/semmle/python/web/bottle/General.qll index d368a1f27b5..99aacf0948d 100644 --- a/python/ql/src/semmle/python/web/bottle/General.qll +++ b/python/ql/src/semmle/python/web/bottle/General.qll @@ -13,34 +13,34 @@ ClassValue theBottleClass() { result = theBottleModule().attr("Bottle") } * by decorating `func` with `app.route(route)` or `route(route)` */ predicate bottle_route(CallNode route_call, ControlFlowNode route, Function func) { - exists(CallNode decorator_call, string name | - route_call.getFunction().(AttrNode).getObject(name).pointsTo().getClass() = theBottleClass() or - route_call.getFunction().pointsTo(theBottleModule().attr(name)) - | - (name = "route" or name = httpVerbLower()) and - decorator_call.getFunction() = route_call and - route_call.getArg(0) = route and - decorator_call.getArg(0).getNode().(FunctionExpr).getInnerScope() = func - ) + exists(CallNode decorator_call, string name | + route_call.getFunction().(AttrNode).getObject(name).pointsTo().getClass() = theBottleClass() or + route_call.getFunction().pointsTo(theBottleModule().attr(name)) + | + (name = "route" or name = httpVerbLower()) and + decorator_call.getFunction() = route_call and + route_call.getArg(0) = route and + decorator_call.getArg(0).getNode().(FunctionExpr).getInnerScope() = func + ) } class BottleRoute extends ControlFlowNode { - BottleRoute() { bottle_route(this, _, _) } + BottleRoute() { bottle_route(this, _, _) } - string getUrl() { - exists(StrConst url | - bottle_route(this, url.getAFlowNode(), _) and - result = url.getText() - ) - } + string getUrl() { + exists(StrConst url | + bottle_route(this, url.getAFlowNode(), _) and + result = url.getText() + ) + } - Function getFunction() { bottle_route(this, _, result) } + Function getFunction() { bottle_route(this, _, result) } - Parameter getANamedArgument() { - exists(string name, Function func | - func = this.getFunction() and - func.getArgByName(name) = result and - this.getUrl().matches("%<" + name + ">%") - ) - } + Parameter getANamedArgument() { + exists(string name, Function func | + func = this.getFunction() and + func.getArgByName(name) = result and + this.getUrl().matches("%<" + name + ">%") + ) + } } diff --git a/python/ql/src/semmle/python/web/bottle/Redirect.qll b/python/ql/src/semmle/python/web/bottle/Redirect.qll index be4c552fea2..714468d6b45 100644 --- a/python/ql/src/semmle/python/web/bottle/Redirect.qll +++ b/python/ql/src/semmle/python/web/bottle/Redirect.qll @@ -15,14 +15,14 @@ FunctionValue bottle_redirect() { result = theBottleModule().attr("redirect") } * Represents an argument to the `bottle.redirect` function. */ class BottleRedirect extends TaintSink { - override string toString() { result = "bottle.redirect" } + override string toString() { result = "bottle.redirect" } - BottleRedirect() { - exists(CallNode call | - bottle_redirect().getACall() = call and - this = call.getAnArg() - ) - } + BottleRedirect() { + exists(CallNode call | + bottle_redirect().getACall() = call and + this = call.getAnArg() + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } } diff --git a/python/ql/src/semmle/python/web/bottle/Request.qll b/python/ql/src/semmle/python/web/bottle/Request.qll index 91f04dde16d..67b5b78bfdf 100644 --- a/python/ql/src/semmle/python/web/bottle/Request.qll +++ b/python/ql/src/semmle/python/web/bottle/Request.qll @@ -7,61 +7,61 @@ import semmle.python.web.bottle.General private Value theBottleRequestObject() { result = theBottleModule().attr("request") } class BottleRequestKind extends TaintKind { - BottleRequestKind() { this = "bottle.request" } + BottleRequestKind() { this = "bottle.request" } - override TaintKind getTaintOfAttribute(string name) { - result instanceof BottleFormsDict and - (name = "cookies" or name = "query" or name = "form") - or - result instanceof ExternalStringKind and - (name = "query_string" or name = "url_args") - or - result.(DictKind).getValue() instanceof FileUpload and - name = "files" - } + override TaintKind getTaintOfAttribute(string name) { + result instanceof BottleFormsDict and + (name = "cookies" or name = "query" or name = "form") + or + result instanceof ExternalStringKind and + (name = "query_string" or name = "url_args") + or + result.(DictKind).getValue() instanceof FileUpload and + name = "files" + } } private class RequestSource extends HttpRequestTaintSource { - RequestSource() { this.(ControlFlowNode).pointsTo(theBottleRequestObject()) } + RequestSource() { this.(ControlFlowNode).pointsTo(theBottleRequestObject()) } - override predicate isSourceOf(TaintKind kind) { kind instanceof BottleRequestKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof BottleRequestKind } } class BottleFormsDict extends TaintKind { - BottleFormsDict() { this = "bottle.FormsDict" } + BottleFormsDict() { this = "bottle.FormsDict" } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - /* Cannot use `getTaintOfAttribute(name)` as it wouldn't bind `name` */ - exists(string name | - fromnode = tonode.(AttrNode).getObject(name) and - result instanceof ExternalStringKind - | - name != "get" and name != "getunicode" and name != "getall" - ) - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + /* Cannot use `getTaintOfAttribute(name)` as it wouldn't bind `name` */ + exists(string name | + fromnode = tonode.(AttrNode).getObject(name) and + result instanceof ExternalStringKind + | + name != "get" and name != "getunicode" and name != "getall" + ) + } - override TaintKind getTaintOfMethodResult(string name) { - (name = "get" or name = "getunicode") and - result instanceof ExternalStringKind - or - name = "getall" and result.(SequenceKind).getItem() instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + (name = "get" or name = "getunicode") and + result instanceof ExternalStringKind + or + name = "getall" and result.(SequenceKind).getItem() instanceof ExternalStringKind + } } class FileUpload extends TaintKind { - FileUpload() { this = "bottle.FileUpload" } + FileUpload() { this = "bottle.FileUpload" } - override TaintKind getTaintOfAttribute(string name) { - name = "filename" and result instanceof ExternalStringKind - or - name = "raw_filename" and result instanceof ExternalStringKind - or - name = "file" and result instanceof UntrustedFile - } + override TaintKind getTaintOfAttribute(string name) { + name = "filename" and result instanceof ExternalStringKind + or + name = "raw_filename" and result instanceof ExternalStringKind + or + name = "file" and result instanceof UntrustedFile + } } class UntrustedFile extends TaintKind { - UntrustedFile() { this = "Untrusted file" } + UntrustedFile() { this = "Untrusted file" } } // @@ -70,11 +70,11 @@ class UntrustedFile extends TaintKind { // /** Parameter to a bottle request handler function */ class BottleRequestParameter extends HttpRequestTaintSource { - BottleRequestParameter() { - exists(BottleRoute route | route.getANamedArgument() = this.(ControlFlowNode).getNode()) - } + BottleRequestParameter() { + exists(BottleRoute route | route.getANamedArgument() = this.(ControlFlowNode).getNode()) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "bottle handler function argument" } + override string toString() { result = "bottle handler function argument" } } diff --git a/python/ql/src/semmle/python/web/bottle/Response.qll b/python/ql/src/semmle/python/web/bottle/Response.qll index dede231c27d..285e83c8685 100644 --- a/python/ql/src/semmle/python/web/bottle/Response.qll +++ b/python/ql/src/semmle/python/web/bottle/Response.qll @@ -10,43 +10,43 @@ import semmle.python.web.bottle.General * track the flow of response objects. */ class BottleResponse extends TaintKind { - BottleResponse() { this = "bottle.response" } + BottleResponse() { this = "bottle.response" } } private Value theBottleResponseObject() { result = theBottleModule().attr("response") } class BottleResponseBodyAssignment extends HttpResponseTaintSink { - BottleResponseBodyAssignment() { - exists(DefinitionNode lhs | - lhs.getValue() = this and - lhs.(AttrNode).getObject("body").pointsTo(theBottleResponseObject()) - ) - } + BottleResponseBodyAssignment() { + exists(DefinitionNode lhs | + lhs.getValue() = this and + lhs.(AttrNode).getObject("body").pointsTo(theBottleResponseObject()) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } } class BottleHandlerFunctionResult extends HttpResponseTaintSink { - BottleHandlerFunctionResult() { - exists(BottleRoute route, Return ret | - ret.getScope() = route.getFunction() and - ret.getValue().getAFlowNode() = this - ) - } + BottleHandlerFunctionResult() { + exists(BottleRoute route, Return ret | + ret.getScope() = route.getFunction() and + ret.getValue().getAFlowNode() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } - override string toString() { result = "bottle handler function result" } + override string toString() { result = "bottle handler function result" } } class BottleCookieSet extends CookieSet, CallNode { - BottleCookieSet() { - any(BottleResponse r).taints(this.getFunction().(AttrNode).getObject("set_cookie")) - } + BottleCookieSet() { + any(BottleResponse r).taints(this.getFunction().(AttrNode).getObject("set_cookie")) + } - override string toString() { result = CallNode.super.toString() } + override string toString() { result = CallNode.super.toString() } - override ControlFlowNode getKey() { result = this.getArg(0) } + override ControlFlowNode getKey() { result = this.getArg(0) } - override ControlFlowNode getValue() { result = this.getArg(1) } + override ControlFlowNode getValue() { result = this.getArg(1) } } diff --git a/python/ql/src/semmle/python/web/cherrypy/General.qll b/python/ql/src/semmle/python/web/cherrypy/General.qll index 5a8984d98d3..718c1486bc4 100644 --- a/python/ql/src/semmle/python/web/cherrypy/General.qll +++ b/python/ql/src/semmle/python/web/cherrypy/General.qll @@ -2,43 +2,43 @@ import python import semmle.python.web.Http module CherryPy { - FunctionValue expose() { result = Value::named("cherrypy.expose") } + FunctionValue expose() { result = Value::named("cherrypy.expose") } } class CherryPyExposedFunction extends Function { - CherryPyExposedFunction() { - this.getADecorator().pointsTo(CherryPy::expose()) - or - this.getADecorator().(Call).getFunc().pointsTo(CherryPy::expose()) - } + CherryPyExposedFunction() { + this.getADecorator().pointsTo(CherryPy::expose()) + or + this.getADecorator().(Call).getFunc().pointsTo(CherryPy::expose()) + } } class CherryPyRoute extends CallNode { - CherryPyRoute() { - /* cherrypy.quickstart(root, script_name, config) */ - Value::named("cherrypy.quickstart").(FunctionValue).getACall() = this - or - /* cherrypy.tree.mount(root, script_name, config) */ - this.getFunction().(AttrNode).getObject("mount").pointsTo(Value::named("cherrypy.tree")) - } + CherryPyRoute() { + /* cherrypy.quickstart(root, script_name, config) */ + Value::named("cherrypy.quickstart").(FunctionValue).getACall() = this + or + /* cherrypy.tree.mount(root, script_name, config) */ + this.getFunction().(AttrNode).getObject("mount").pointsTo(Value::named("cherrypy.tree")) + } - ClassValue getAppClass() { - this.getArg(0).pointsTo().getClass() = result - or - this.getArgByName("root").pointsTo().getClass() = result - } + ClassValue getAppClass() { + this.getArg(0).pointsTo().getClass() = result + or + this.getArgByName("root").pointsTo().getClass() = result + } - string getPath() { - exists(Value path | path = Value::forString(result) | - this.getArg(1).pointsTo(path) - or - this.getArgByName("script_name").pointsTo(path) - ) - } + string getPath() { + exists(Value path | path = Value::forString(result) | + this.getArg(1).pointsTo(path) + or + this.getArgByName("script_name").pointsTo(path) + ) + } - ClassValue getConfig() { - this.getArg(2).pointsTo().getClass() = result - or - this.getArgByName("config").pointsTo().getClass() = result - } + ClassValue getConfig() { + this.getArg(2).pointsTo().getClass() = result + or + this.getArgByName("config").pointsTo().getClass() = result + } } diff --git a/python/ql/src/semmle/python/web/cherrypy/Request.qll b/python/ql/src/semmle/python/web/cherrypy/Request.qll index 309d51f5539..094474b8915 100644 --- a/python/ql/src/semmle/python/web/cherrypy/Request.qll +++ b/python/ql/src/semmle/python/web/cherrypy/Request.qll @@ -6,41 +6,41 @@ import semmle.python.web.cherrypy.General /** The cherrypy.request local-proxy object */ class CherryPyRequest extends TaintKind { - CherryPyRequest() { this = "cherrypy.request" } + CherryPyRequest() { this = "cherrypy.request" } - override TaintKind getTaintOfAttribute(string name) { - name = "params" and result instanceof ExternalStringDictKind - or - name = "cookie" and result instanceof UntrustedCookie - } + override TaintKind getTaintOfAttribute(string name) { + name = "params" and result instanceof ExternalStringDictKind + or + name = "cookie" and result instanceof UntrustedCookie + } - override TaintKind getTaintOfMethodResult(string name) { - ( - name = "getHeader" or - name = "getCookie" or - name = "getUser" or - name = "getPassword" - ) and - result instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + ( + name = "getHeader" or + name = "getCookie" or + name = "getUser" or + name = "getPassword" + ) and + result instanceof ExternalStringKind + } } class CherryPyExposedFunctionParameter extends HttpRequestTaintSource { - CherryPyExposedFunctionParameter() { - exists(Parameter p | - p = any(CherryPyExposedFunction f).getAnArg() and - not p.isSelf() and - p.asName().getAFlowNode() = this - ) - } + CherryPyExposedFunctionParameter() { + exists(Parameter p | + p = any(CherryPyExposedFunction f).getAnArg() and + not p.isSelf() and + p.asName().getAFlowNode() = this + ) + } - override string toString() { result = "CherryPy handler function parameter" } + override string toString() { result = "CherryPy handler function parameter" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } } class CherryPyRequestSource extends HttpRequestTaintSource { - CherryPyRequestSource() { this.(ControlFlowNode).pointsTo(Value::named("cherrypy.request")) } + CherryPyRequestSource() { this.(ControlFlowNode).pointsTo(Value::named("cherrypy.request")) } - override predicate isSourceOf(TaintKind kind) { kind instanceof CherryPyRequest } + override predicate isSourceOf(TaintKind kind) { kind instanceof CherryPyRequest } } diff --git a/python/ql/src/semmle/python/web/cherrypy/Response.qll b/python/ql/src/semmle/python/web/cherrypy/Response.qll index 3ed1d0d9b57..6905244ca95 100644 --- a/python/ql/src/semmle/python/web/cherrypy/Response.qll +++ b/python/ql/src/semmle/python/web/cherrypy/Response.qll @@ -5,14 +5,14 @@ import semmle.python.web.Http import semmle.python.web.cherrypy.General class CherryPyExposedFunctionResult extends HttpResponseTaintSink { - CherryPyExposedFunctionResult() { - exists(Return ret | - ret.getScope() instanceof CherryPyExposedFunction and - ret.getValue().getAFlowNode() = this - ) - } + CherryPyExposedFunctionResult() { + exists(Return ret | + ret.getScope() instanceof CherryPyExposedFunction and + ret.getValue().getAFlowNode() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } - override string toString() { result = "cherrypy handler function result" } + override string toString() { result = "cherrypy handler function result" } } diff --git a/python/ql/src/semmle/python/web/client/Requests.qll b/python/ql/src/semmle/python/web/client/Requests.qll index 6899c651fa6..a65ea3229d5 100644 --- a/python/ql/src/semmle/python/web/client/Requests.qll +++ b/python/ql/src/semmle/python/web/client/Requests.qll @@ -7,16 +7,16 @@ import python private import semmle.python.web.Http class RequestsHttpRequest extends Client::HttpRequest, CallNode { - CallableValue func; - string method; + CallableValue func; + string method; - RequestsHttpRequest() { - method = httpVerbLower() and - func = Module::named("requests").attr(method) and - this = func.getACall() - } + RequestsHttpRequest() { + method = httpVerbLower() and + func = Module::named("requests").attr(method) and + this = func.getACall() + } - override ControlFlowNode getAUrlPart() { result = func.getNamedArgumentForCall(this, "url") } + override ControlFlowNode getAUrlPart() { result = func.getNamedArgumentForCall(this, "url") } - override string getMethodUpper() { result = method.toUpperCase() } + override string getMethodUpper() { result = method.toUpperCase() } } diff --git a/python/ql/src/semmle/python/web/client/StdLib.qll b/python/ql/src/semmle/python/web/client/StdLib.qll index 9ee089a47f5..459c2db8ec0 100644 --- a/python/ql/src/semmle/python/web/client/StdLib.qll +++ b/python/ql/src/semmle/python/web/client/StdLib.qll @@ -2,54 +2,54 @@ import python private import semmle.python.web.Http ClassValue httpConnectionClass() { - // Python 2 - result = Value::named("httplib.HTTPConnection") - or - result = Value::named("httplib.HTTPSConnection") - or - // Python 3 - result = Value::named("http.client.HTTPConnection") - or - result = Value::named("http.client.HTTPSConnection") - or - // six - result = Value::named("six.moves.http_client.HTTPConnection") - or - result = Value::named("six.moves.http_client.HTTPSConnection") + // Python 2 + result = Value::named("httplib.HTTPConnection") + or + result = Value::named("httplib.HTTPSConnection") + or + // Python 3 + result = Value::named("http.client.HTTPConnection") + or + result = Value::named("http.client.HTTPSConnection") + or + // six + result = Value::named("six.moves.http_client.HTTPConnection") + or + result = Value::named("six.moves.http_client.HTTPSConnection") } class HttpConnectionHttpRequest extends Client::HttpRequest, CallNode { - CallNode constructor_call; - CallableValue func; + CallNode constructor_call; + CallableValue func; - HttpConnectionHttpRequest() { - exists(ClassValue cls, AttrNode call_origin, Value constructor_call_value | - cls = httpConnectionClass() and - func = cls.lookup("request") and - this = func.getACall() and - // since you can do `r = conn.request; r('GET', path)`, we need to find the origin - this.getFunction().pointsTo(_, _, call_origin) and - // Since HTTPSConnection is a subtype of HTTPConnection, up until this point, `cls` could be either class, - // because `HTTPSConnection.request == HTTPConnection.request`. To avoid generating 2 results, we filter - // on the actual class used as the constructor - call_origin.getObject().pointsTo(_, constructor_call_value, constructor_call) and - cls = constructor_call_value.getClass() and - constructor_call = cls.getACall() - ) - } + HttpConnectionHttpRequest() { + exists(ClassValue cls, AttrNode call_origin, Value constructor_call_value | + cls = httpConnectionClass() and + func = cls.lookup("request") and + this = func.getACall() and + // since you can do `r = conn.request; r('GET', path)`, we need to find the origin + this.getFunction().pointsTo(_, _, call_origin) and + // Since HTTPSConnection is a subtype of HTTPConnection, up until this point, `cls` could be either class, + // because `HTTPSConnection.request == HTTPConnection.request`. To avoid generating 2 results, we filter + // on the actual class used as the constructor + call_origin.getObject().pointsTo(_, constructor_call_value, constructor_call) and + cls = constructor_call_value.getClass() and + constructor_call = cls.getACall() + ) + } - override ControlFlowNode getAUrlPart() { - result = func.getNamedArgumentForCall(this, "url") - or - result = constructor_call.getArg(0) - or - result = constructor_call.getArgByName("host") - } + override ControlFlowNode getAUrlPart() { + result = func.getNamedArgumentForCall(this, "url") + or + result = constructor_call.getArg(0) + or + result = constructor_call.getArgByName("host") + } - override string getMethodUpper() { - exists(string method | - result = method.toUpperCase() and - func.getNamedArgumentForCall(this, "method").pointsTo(Value::forString(method)) - ) - } + override string getMethodUpper() { + exists(string method | + result = method.toUpperCase() and + func.getNamedArgumentForCall(this, "method").pointsTo(Value::forString(method)) + ) + } } diff --git a/python/ql/src/semmle/python/web/django/Db.qll b/python/ql/src/semmle/python/web/django/Db.qll index 00a36f5ba76..1dbb52fd13d 100644 --- a/python/ql/src/semmle/python/web/django/Db.qll +++ b/python/ql/src/semmle/python/web/django/Db.qll @@ -5,7 +5,7 @@ import semmle.python.security.injection.Sql * A taint kind representing a django cursor object. */ class DjangoDbCursor extends DbCursor { - DjangoDbCursor() { this = "django.db.connection.cursor" } + DjangoDbCursor() { this = "django.db.connection.cursor" } } private Value theDjangoConnectionObject() { result = Value::named("django.db.connection") } @@ -14,16 +14,16 @@ private Value theDjangoConnectionObject() { result = Value::named("django.db.con * A kind of taint source representing sources of django cursor objects. */ class DjangoDbCursorSource extends DbConnectionSource { - DjangoDbCursorSource() { - exists(AttrNode cursor | - this.(CallNode).getFunction() = cursor and - cursor.getObject("cursor").pointsTo(theDjangoConnectionObject()) - ) - } + DjangoDbCursorSource() { + exists(AttrNode cursor | + this.(CallNode).getFunction() = cursor and + cursor.getObject("cursor").pointsTo(theDjangoConnectionObject()) + ) + } - override string toString() { result = "django.db.connection.cursor" } + override string toString() { result = "django.db.connection.cursor" } - override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoDbCursor } + override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoDbCursor } } ClassValue theDjangoRawSqlClass() { result = Value::named("django.db.models.expressions.RawSQL") } @@ -33,14 +33,14 @@ ClassValue theDjangoRawSqlClass() { result = Value::named("django.db.models.expr * allows arbitrary SQL statements to be executed, which is a security risk. */ class DjangoRawSqlSink extends SqlInjectionSink { - DjangoRawSqlSink() { - exists(CallNode call | - call = theDjangoRawSqlClass().getACall() and - this = call.getArg(0) - ) - } + DjangoRawSqlSink() { + exists(CallNode call | + call = theDjangoRawSqlClass().getACall() and + this = call.getArg(0) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "django.db.models.expressions.RawSQL(sink,...)" } + override string toString() { result = "django.db.models.expressions.RawSQL(sink,...)" } } diff --git a/python/ql/src/semmle/python/web/django/General.qll b/python/ql/src/semmle/python/web/django/General.qll index 8d707a3a6e6..0b511cbfbcf 100644 --- a/python/ql/src/semmle/python/web/django/General.qll +++ b/python/ql/src/semmle/python/web/django/General.qll @@ -6,19 +6,19 @@ import semmle.python.web.Http // a FunctionValue, so we can't use `FunctionValue.getArgumentForCall` // https://github.com/django/django/blob/master/django/urls/conf.py#L76 abstract class DjangoRoute extends CallNode { - DjangoViewHandler getViewHandler() { - result = view_handler_from_view_arg(this.getArg(1)) - or - result = view_handler_from_view_arg(this.getArgByName("view")) - } + DjangoViewHandler getViewHandler() { + result = view_handler_from_view_arg(this.getArg(1)) + or + result = view_handler_from_view_arg(this.getArgByName("view")) + } - abstract string getANamedArgument(); + abstract string getANamedArgument(); - /** - * Get the number of positional arguments that will be passed to the view. - * Will only return a result if there are no named arguments. - */ - abstract int getNumPositionalArguments(); + /** + * Get the number of positional arguments that will be passed to the view. + * Will only return a result if there are no named arguments. + */ + abstract int getNumPositionalArguments(); } /** @@ -27,8 +27,8 @@ abstract class DjangoRoute extends CallNode { * https://docs.djangoproject.com/en/3.0/topics/http/views/ */ class DjangoViewHandler extends PythonFunctionValue { - /** Gets the index of the 'request' argument */ - int getRequestArgIndex() { result = 0 } + /** Gets the index of the 'request' argument */ + int getRequestArgIndex() { result = 0 } } /** @@ -37,20 +37,20 @@ class DjangoViewHandler extends PythonFunctionValue { * https://docs.djangoproject.com/en/3.0/topics/class-based-views/ */ private class DjangoViewClass extends ClassValue { - DjangoViewClass() { - Value::named("django.views.generic.View") = this.getASuperType() - or - Value::named("django.views.View") = this.getASuperType() - } + DjangoViewClass() { + Value::named("django.views.generic.View") = this.getASuperType() + or + Value::named("django.views.View") = this.getASuperType() + } } class DjangoClassBasedViewHandler extends DjangoViewHandler { - DjangoClassBasedViewHandler() { exists(DjangoViewClass cls | cls.lookup(httpVerbLower()) = this) } + DjangoClassBasedViewHandler() { exists(DjangoViewClass cls | cls.lookup(httpVerbLower()) = this) } - override int getRequestArgIndex() { - // due to `self` being the first parameter - result = 1 - } + override int getRequestArgIndex() { + // due to `self` being the first parameter + result = 1 + } } /** @@ -58,79 +58,79 @@ class DjangoClassBasedViewHandler extends DjangoViewHandler { * django route. That is, this methods handles Class-based Views and its `as_view()` function. */ private DjangoViewHandler view_handler_from_view_arg(ControlFlowNode view_arg) { - // Function-based view - result = view_arg.pointsTo() - or - // Class-based view - exists(ClassValue cls | - cls = view_arg.(CallNode).getFunction().(AttrNode).getObject("as_view").pointsTo() and - result = cls.lookup(httpVerbLower()) - ) + // Function-based view + result = view_arg.pointsTo() + or + // Class-based view + exists(ClassValue cls | + cls = view_arg.(CallNode).getFunction().(AttrNode).getObject("as_view").pointsTo() and + result = cls.lookup(httpVerbLower()) + ) } // We need this "dummy" class, since otherwise the regex argument would not be considered // a regex (RegexString is abstract) class DjangoRouteRegex extends RegexString { - DjangoRouteRegex() { exists(DjangoRegexRoute route | route.getRouteArg() = this.getAFlowNode()) } + DjangoRouteRegex() { exists(DjangoRegexRoute route | route.getRouteArg() = this.getAFlowNode()) } } class DjangoRegexRoute extends DjangoRoute { - ControlFlowNode route; + ControlFlowNode route; - DjangoRegexRoute() { - exists(FunctionValue route_maker | - // Django 1.x: https://docs.djangoproject.com/en/1.11/ref/urls/#django.conf.urls.url - Value::named("django.conf.urls.url") = route_maker and - route_maker.getArgumentForCall(this, 0) = route - ) - or - // Django 2.x and 3.x: https://docs.djangoproject.com/en/3.0/ref/urls/#re-path - this = Value::named("django.urls.re_path").getACall() and - ( - route = this.getArg(0) - or - route = this.getArgByName("route") - ) - } + DjangoRegexRoute() { + exists(FunctionValue route_maker | + // Django 1.x: https://docs.djangoproject.com/en/1.11/ref/urls/#django.conf.urls.url + Value::named("django.conf.urls.url") = route_maker and + route_maker.getArgumentForCall(this, 0) = route + ) + or + // Django 2.x and 3.x: https://docs.djangoproject.com/en/3.0/ref/urls/#re-path + this = Value::named("django.urls.re_path").getACall() and + ( + route = this.getArg(0) + or + route = this.getArgByName("route") + ) + } - ControlFlowNode getRouteArg() { result = route } + ControlFlowNode getRouteArg() { result = route } - override string getANamedArgument() { - exists(DjangoRouteRegex regex | regex.getAFlowNode() = route | - result = regex.getGroupName(_, _) - ) - } + override string getANamedArgument() { + exists(DjangoRouteRegex regex | regex.getAFlowNode() = route | + result = regex.getGroupName(_, _) + ) + } - override int getNumPositionalArguments() { - not exists(this.getANamedArgument()) and - exists(DjangoRouteRegex regex | regex.getAFlowNode() = route | - result = count(regex.getGroupNumber(_, _)) - ) - } + override int getNumPositionalArguments() { + not exists(this.getANamedArgument()) and + exists(DjangoRouteRegex regex | regex.getAFlowNode() = route | + result = count(regex.getGroupNumber(_, _)) + ) + } } class DjangoPathRoute extends DjangoRoute { - ControlFlowNode route; + ControlFlowNode route; - DjangoPathRoute() { - // Django 2.x and 3.x: https://docs.djangoproject.com/en/3.0/ref/urls/#path - this = Value::named("django.urls.path").getACall() and - ( - route = this.getArg(0) - or - route = this.getArgByName("route") - ) - } + DjangoPathRoute() { + // Django 2.x and 3.x: https://docs.djangoproject.com/en/3.0/ref/urls/#path + this = Value::named("django.urls.path").getACall() and + ( + route = this.getArg(0) + or + route = this.getArgByName("route") + ) + } - override string getANamedArgument() { - // regexp taken from django: - // https://github.com/django/django/blob/7d1bf29977bb368d7c28e7c6eb146db3b3009ae7/django/urls/resolvers.py#L199 - exists(StrConst route_str, string match | - route_str = route.getNode() and - match = route_str.getText().regexpFind("<(?:(?[^>:]+):)?(?\\w+)>", _, _) and - result = match.regexpCapture("<(?:(?[^>:]+):)?(?\\w+)>", 2) - ) - } + override string getANamedArgument() { + // regexp taken from django: + // https://github.com/django/django/blob/7d1bf29977bb368d7c28e7c6eb146db3b3009ae7/django/urls/resolvers.py#L199 + exists(StrConst route_str, string match | + route_str = route.getNode() and + match = route_str.getText().regexpFind("<(?:(?[^>:]+):)?(?\\w+)>", _, _) and + result = match.regexpCapture("<(?:(?[^>:]+):)?(?\\w+)>", 2) + ) + } - override int getNumPositionalArguments() { none() } + override int getNumPositionalArguments() { none() } } diff --git a/python/ql/src/semmle/python/web/django/Model.qll b/python/ql/src/semmle/python/web/django/Model.qll index f8a61bda10e..d48fe6e04f9 100644 --- a/python/ql/src/semmle/python/web/django/Model.qll +++ b/python/ql/src/semmle/python/web/django/Model.qll @@ -6,52 +6,52 @@ import semmle.python.security.injection.Sql /** A django model class */ class DjangoModel extends ClassValue { - DjangoModel() { Value::named("django.db.models.Model") = this.getASuperType() } + DjangoModel() { Value::named("django.db.models.Model") = this.getASuperType() } } /** A "taint" for django database tables */ class DjangoDbTableObjects extends TaintKind { - DjangoDbTableObjects() { this = "django.db.models.Model.objects" } + DjangoDbTableObjects() { this = "django.db.models.Model.objects" } - override TaintKind getTaintOfMethodResult(string name) { - result = this and - ( - name = "filter" or - name = "exclude" or - name = "annotate" or - name = "order_by" or - name = "reverse" or - name = "distinct" or - name = "values" or - name = "values_list" or - name = "dates" or - name = "datetimes" or - name = "none" or - name = "all" or - name = "union" or - name = "intersection" or - name = "difference" or - name = "select_related" or - name = "prefetch_related" or - name = "extra" or - name = "defer" or - name = "only" or - name = "using" or - name = "select_for_update" or - name = "raw" - ) - } + override TaintKind getTaintOfMethodResult(string name) { + result = this and + ( + name = "filter" or + name = "exclude" or + name = "annotate" or + name = "order_by" or + name = "reverse" or + name = "distinct" or + name = "values" or + name = "values_list" or + name = "dates" or + name = "datetimes" or + name = "none" or + name = "all" or + name = "union" or + name = "intersection" or + name = "difference" or + name = "select_related" or + name = "prefetch_related" or + name = "extra" or + name = "defer" or + name = "only" or + name = "using" or + name = "select_for_update" or + name = "raw" + ) + } } /** Django model objects, which are sources of django database table "taint" */ class DjangoModelObjects extends TaintSource { - DjangoModelObjects() { - this.(AttrNode).isLoad() and this.(AttrNode).getObject("objects").pointsTo(any(DjangoModel m)) - } + DjangoModelObjects() { + this.(AttrNode).isLoad() and this.(AttrNode).getObject("objects").pointsTo(any(DjangoModel m)) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoDbTableObjects } + override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoDbTableObjects } - override string toString() { result = "django.db.models.Model.objects" } + override string toString() { result = "django.db.models.Model.objects" } } /** @@ -59,16 +59,16 @@ class DjangoModelObjects extends TaintSource { * to be sent to the database, which is a security risk. */ class DjangoModelRawCall extends SqlInjectionSink { - DjangoModelRawCall() { - exists(CallNode raw_call, ControlFlowNode queryset | this = raw_call.getArg(0) | - raw_call.getFunction().(AttrNode).getObject("raw") = queryset and - any(DjangoDbTableObjects objs).taints(queryset) - ) - } + DjangoModelRawCall() { + exists(CallNode raw_call, ControlFlowNode queryset | this = raw_call.getArg(0) | + raw_call.getFunction().(AttrNode).getObject("raw") = queryset and + any(DjangoDbTableObjects objs).taints(queryset) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "django.models.QuerySet.raw(sink,...)" } + override string toString() { result = "django.models.QuerySet.raw(sink,...)" } } /** @@ -76,14 +76,14 @@ class DjangoModelRawCall extends SqlInjectionSink { * to be sent to the database, which is a security risk. */ class DjangoModelExtraCall extends SqlInjectionSink { - DjangoModelExtraCall() { - exists(CallNode extra_call, ControlFlowNode queryset | this = extra_call.getArg(0) | - extra_call.getFunction().(AttrNode).getObject("extra") = queryset and - any(DjangoDbTableObjects objs).taints(queryset) - ) - } + DjangoModelExtraCall() { + exists(CallNode extra_call, ControlFlowNode queryset | this = extra_call.getArg(0) | + extra_call.getFunction().(AttrNode).getObject("extra") = queryset and + any(DjangoDbTableObjects objs).taints(queryset) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "django.models.QuerySet.extra(sink,...)" } + override string toString() { result = "django.models.QuerySet.extra(sink,...)" } } diff --git a/python/ql/src/semmle/python/web/django/Redirect.qll b/python/ql/src/semmle/python/web/django/Redirect.qll index d6afbcce7e7..3b0b1f2b50b 100644 --- a/python/ql/src/semmle/python/web/django/Redirect.qll +++ b/python/ql/src/semmle/python/web/django/Redirect.qll @@ -14,11 +14,11 @@ private import semmle.python.web.Http * The URL argument for a call to the `django.shortcuts.redirect` function. */ class DjangoShortcutsRedirectSink extends HttpRedirectTaintSink { - override string toString() { result = "DjangoShortcutsRedirectSink" } + override string toString() { result = "DjangoShortcutsRedirectSink" } - DjangoShortcutsRedirectSink() { - this = Value::named("django.shortcuts.redirect").(FunctionValue).getArgumentForCall(_, 0) - } + DjangoShortcutsRedirectSink() { + this = Value::named("django.shortcuts.redirect").(FunctionValue).getArgumentForCall(_, 0) + } } /** DEPRECATED: Use `DjangoShortcutsRedirectSink` instead. */ @@ -28,13 +28,13 @@ deprecated class DjangoRedirect = DjangoShortcutsRedirectSink; * The URL argument when instantiating a Django Redirect Response. */ class DjangoRedirectResponseSink extends HttpRedirectTaintSink { - DjangoRedirectResponseSink() { - exists(CallNode call | call = any(DjangoRedirectResponseClass cls).getACall() | - this = call.getArg(0) - or - this = call.getArgByName("redirect_to") - ) - } + DjangoRedirectResponseSink() { + exists(CallNode call | call = any(DjangoRedirectResponseClass cls).getACall() | + this = call.getArg(0) + or + this = call.getArgByName("redirect_to") + ) + } - override string toString() { result = "DjangoRedirectResponseSink" } + override string toString() { result = "DjangoRedirectResponseSink" } } diff --git a/python/ql/src/semmle/python/web/django/Request.qll b/python/ql/src/semmle/python/web/django/Request.qll index 503264c2817..291d61b184b 100644 --- a/python/ql/src/semmle/python/web/django/Request.qll +++ b/python/ql/src/semmle/python/web/django/Request.qll @@ -5,74 +5,74 @@ import semmle.python.web.django.General /** A django.request.HttpRequest object */ class DjangoRequest extends TaintKind { - DjangoRequest() { this = "django.request.HttpRequest" } + DjangoRequest() { this = "django.request.HttpRequest" } - override TaintKind getTaintOfAttribute(string name) { - (name = "GET" or name = "POST") and - result instanceof DjangoQueryDict - } + override TaintKind getTaintOfAttribute(string name) { + (name = "GET" or name = "POST") and + result instanceof DjangoQueryDict + } - override TaintKind getTaintOfMethodResult(string name) { - (name = "body" or name = "path") and - result instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + (name = "body" or name = "path") and + result instanceof ExternalStringKind + } } /* Helper for getTaintForStep() */ pragma[noinline] private predicate subscript_taint(SubscriptNode sub, ControlFlowNode obj, TaintKind kind) { - sub.getObject() = obj and - kind instanceof ExternalStringKind + sub.getObject() = obj and + kind instanceof ExternalStringKind } /** A django.request.QueryDict object */ class DjangoQueryDict extends TaintKind { - DjangoQueryDict() { this = "django.http.request.QueryDict" } + DjangoQueryDict() { this = "django.http.request.QueryDict" } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - this.taints(fromnode) and - subscript_taint(tonode, fromnode, result) - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + this.taints(fromnode) and + subscript_taint(tonode, fromnode, result) + } - override TaintKind getTaintOfMethodResult(string name) { - name = "get" and result instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + name = "get" and result instanceof ExternalStringKind + } } /** A Django request parameter */ class DjangoRequestSource extends HttpRequestTaintSource { - DjangoRequestSource() { - exists(DjangoRoute route, DjangoViewHandler view, int request_arg_index | - route.getViewHandler() = view and - request_arg_index = view.getRequestArgIndex() and - this = view.getScope().getArg(request_arg_index).asName().getAFlowNode() - ) - } + DjangoRequestSource() { + exists(DjangoRoute route, DjangoViewHandler view, int request_arg_index | + route.getViewHandler() = view and + request_arg_index = view.getRequestArgIndex() and + this = view.getScope().getArg(request_arg_index).asName().getAFlowNode() + ) + } - override string toString() { result = "Django request source" } + override string toString() { result = "Django request source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoRequest } + override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoRequest } } /** An argument specified in a url routing table */ class DjangoRequestParameter extends HttpRequestTaintSource { - DjangoRequestParameter() { - exists(DjangoRoute route, Function f, DjangoViewHandler view, int request_arg_index | - route.getViewHandler() = view and - request_arg_index = view.getRequestArgIndex() and - f = view.getScope() - | - this.(ControlFlowNode).getNode() = f.getArgByName(route.getANamedArgument()) - or - exists(int i | i >= 0 | - i < route.getNumPositionalArguments() and - // +1 because first argument is always the request - this.(ControlFlowNode).getNode() = f.getArg(request_arg_index + 1 + i) - ) - ) - } + DjangoRequestParameter() { + exists(DjangoRoute route, Function f, DjangoViewHandler view, int request_arg_index | + route.getViewHandler() = view and + request_arg_index = view.getRequestArgIndex() and + f = view.getScope() + | + this.(ControlFlowNode).getNode() = f.getArgByName(route.getANamedArgument()) + or + exists(int i | i >= 0 | + i < route.getNumPositionalArguments() and + // +1 because first argument is always the request + this.(ControlFlowNode).getNode() = f.getArg(request_arg_index + 1 + i) + ) + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "django.http.request.parameter" } + override string toString() { result = "django.http.request.parameter" } } diff --git a/python/ql/src/semmle/python/web/django/Response.qll b/python/ql/src/semmle/python/web/django/Response.qll index 649a503cc4b..ea1fe234693 100644 --- a/python/ql/src/semmle/python/web/django/Response.qll +++ b/python/ql/src/semmle/python/web/django/Response.qll @@ -16,74 +16,74 @@ deprecated class DjangoResponse = DjangoResponseKind; /** INTERNAL class used for tracking a django response object. */ private class DjangoResponseKind extends TaintKind { - DjangoResponseKind() { this = "django.response.HttpResponse" } + DjangoResponseKind() { this = "django.response.HttpResponse" } } /** INTERNAL taint-source used for tracking a django response object. */ private class DjangoResponseSource extends TaintSource { - DjangoResponseSource() { exists(DjangoContentResponseClass cls | cls.getACall() = this) } + DjangoResponseSource() { exists(DjangoContentResponseClass cls | cls.getACall() = this) } - override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoResponseKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoResponseKind } - override string toString() { result = "django.http.response.HttpResponse" } + override string toString() { result = "django.http.response.HttpResponse" } } /** A write to a django response, which is vulnerable to external data (xss) */ class DjangoResponseWrite extends HttpResponseTaintSink { - DjangoResponseWrite() { - exists(AttrNode meth, CallNode call | - call.getFunction() = meth and - any(DjangoResponseKind response).taints(meth.getObject("write")) and - this = call.getArg(0) - ) - } + DjangoResponseWrite() { + exists(AttrNode meth, CallNode call | + call.getFunction() = meth and + any(DjangoResponseKind response).taints(meth.getObject("write")) and + this = call.getArg(0) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } - override string toString() { result = "django.Response.write(...)" } + override string toString() { result = "django.Response.write(...)" } } /** * An argument to initialization of a django response. */ class DjangoResponseContent extends HttpResponseTaintSink { - DjangoContentResponseClass cls; - CallNode call; + DjangoContentResponseClass cls; + CallNode call; - DjangoResponseContent() { - call = cls.getACall() and - this = cls.getContentArg(call) - } + DjangoResponseContent() { + call = cls.getACall() and + this = cls.getContentArg(call) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } - override string toString() { result = "django.Response(...)" } + override string toString() { result = "django.Response(...)" } } /** * An argument to initialization of a django response, which is vulnerable to external data (XSS). */ class DjangoResponseContentXSSVulnerable extends DjangoResponseContent { - override DjangoXSSVulnerableResponseClass cls; + override DjangoXSSVulnerableResponseClass cls; - DjangoResponseContentXSSVulnerable() { - not exists(cls.getContentTypeArg(call)) - or - exists(StringValue s | - cls.getContentTypeArg(call).pointsTo(s) and - s.getText().matches("text/html%") - ) - } + DjangoResponseContentXSSVulnerable() { + not exists(cls.getContentTypeArg(call)) + or + exists(StringValue s | + cls.getContentTypeArg(call).pointsTo(s) and + s.getText().matches("text/html%") + ) + } } class DjangoCookieSet extends CookieSet, CallNode { - DjangoCookieSet() { - any(DjangoResponseKind r).taints(this.getFunction().(AttrNode).getObject("set_cookie")) - } + DjangoCookieSet() { + any(DjangoResponseKind r).taints(this.getFunction().(AttrNode).getObject("set_cookie")) + } - override string toString() { result = CallNode.super.toString() } + override string toString() { result = CallNode.super.toString() } - override ControlFlowNode getKey() { result = this.getArg(0) } + override ControlFlowNode getKey() { result = this.getArg(0) } - override ControlFlowNode getValue() { result = this.getArg(1) } + override ControlFlowNode getValue() { result = this.getArg(1) } } diff --git a/python/ql/src/semmle/python/web/django/Shared.qll b/python/ql/src/semmle/python/web/django/Shared.qll index ea856ddf65a..d6b49e22a6f 100644 --- a/python/ql/src/semmle/python/web/django/Shared.qll +++ b/python/ql/src/semmle/python/web/django/Shared.qll @@ -5,26 +5,26 @@ deprecated FunctionValue redirect() { result = Value::named("django.shortcuts.re /** DEPRECATED: Use `DjangoRedirectResponseClass` instead. */ deprecated ClassValue theDjangoHttpRedirectClass() { - // version 1.x - result = Value::named("django.http.response.HttpResponseRedirectBase") - or - // version 2.x - result = Value::named("django.http.HttpResponseRedirectBase") + // version 1.x + result = Value::named("django.http.response.HttpResponseRedirectBase") + or + // version 2.x + result = Value::named("django.http.HttpResponseRedirectBase") } /** A class that is a Django Redirect Response (subclass of `django.http.HttpResponseRedirectBase`). */ class DjangoRedirectResponseClass extends ClassValue { - DjangoRedirectResponseClass() { - exists(ClassValue redirect_base | - // version 1.x - redirect_base = Value::named("django.http.response.HttpResponseRedirectBase") - or - // version 2.x and 3.x - redirect_base = Value::named("django.http.HttpResponseRedirectBase") - | - this.getASuperType() = redirect_base - ) - } + DjangoRedirectResponseClass() { + exists(ClassValue redirect_base | + // version 1.x + redirect_base = Value::named("django.http.response.HttpResponseRedirectBase") + or + // version 2.x and 3.x + redirect_base = Value::named("django.http.HttpResponseRedirectBase") + | + this.getASuperType() = redirect_base + ) + } } /** @@ -32,53 +32,53 @@ class DjangoRedirectResponseClass extends ClassValue { * A subclass of `django.http.HttpResponse` that is not a `DjangoRedirectResponseClass`. */ class DjangoContentResponseClass extends ClassValue { - ClassValue base; + ClassValue base; - DjangoContentResponseClass() { - ( - // version 1.x - base = Value::named("django.http.response.HttpResponse") - or - // version 2.x and 3.x - // https://docs.djangoproject.com/en/2.2/ref/request-response/#httpresponse-objects - base = Value::named("django.http.HttpResponse") - ) and - this.getASuperType() = base - } + DjangoContentResponseClass() { + ( + // version 1.x + base = Value::named("django.http.response.HttpResponse") + or + // version 2.x and 3.x + // https://docs.djangoproject.com/en/2.2/ref/request-response/#httpresponse-objects + base = Value::named("django.http.HttpResponse") + ) and + this.getASuperType() = base + } - // The reason these two methods are defined in this class (and not in the Sink - // definition that uses this class), is that if we were to add support for - // `django.http.response.HttpResponseNotAllowed` it would make much more sense to add - // the custom logic in this class (or subclass), than to handle all of it in the sink - // definition. - /** Gets the `content` argument of a `call` to the constructor */ - ControlFlowNode getContentArg(CallNode call) { none() } + // The reason these two methods are defined in this class (and not in the Sink + // definition that uses this class), is that if we were to add support for + // `django.http.response.HttpResponseNotAllowed` it would make much more sense to add + // the custom logic in this class (or subclass), than to handle all of it in the sink + // definition. + /** Gets the `content` argument of a `call` to the constructor */ + ControlFlowNode getContentArg(CallNode call) { none() } - /** Gets the `content_type` argument of a `call` to the constructor */ - ControlFlowNode getContentTypeArg(CallNode call) { none() } + /** Gets the `content_type` argument of a `call` to the constructor */ + ControlFlowNode getContentTypeArg(CallNode call) { none() } } /** A class that is a Django Response, and is vulnerable to XSS. */ class DjangoXSSVulnerableResponseClass extends DjangoContentResponseClass { - DjangoXSSVulnerableResponseClass() { - // We want to avoid FPs on subclasses that are not exposed to XSS, for example `JsonResponse`. - // The easiest way is to disregard any subclass that has a special `__init__` method. - // It's not guaranteed to remove all FPs, or not to generate FNs, but compared to our - // previous implementation that would treat 0-th argument to _any_ subclass as a sink, - // this gets us much closer to reality. - this.lookup("__init__") = base.lookup("__init__") and - not this instanceof DjangoRedirectResponseClass - } + DjangoXSSVulnerableResponseClass() { + // We want to avoid FPs on subclasses that are not exposed to XSS, for example `JsonResponse`. + // The easiest way is to disregard any subclass that has a special `__init__` method. + // It's not guaranteed to remove all FPs, or not to generate FNs, but compared to our + // previous implementation that would treat 0-th argument to _any_ subclass as a sink, + // this gets us much closer to reality. + this.lookup("__init__") = base.lookup("__init__") and + not this instanceof DjangoRedirectResponseClass + } - override ControlFlowNode getContentArg(CallNode call) { - result = call.getArg(0) - or - result = call.getArgByName("content") - } + override ControlFlowNode getContentArg(CallNode call) { + result = call.getArg(0) + or + result = call.getArgByName("content") + } - override ControlFlowNode getContentTypeArg(CallNode call) { - result = call.getArg(1) - or - result = call.getArgByName("content_type") - } + override ControlFlowNode getContentTypeArg(CallNode call) { + result = call.getArg(1) + or + result = call.getArgByName("content_type") + } } diff --git a/python/ql/src/semmle/python/web/falcon/General.qll b/python/ql/src/semmle/python/web/falcon/General.qll index bee0ad746f6..b08b71cfbd7 100644 --- a/python/ql/src/semmle/python/web/falcon/General.qll +++ b/python/ql/src/semmle/python/web/falcon/General.qll @@ -6,39 +6,39 @@ ClassValue theFalconAPIClass() { result = Value::named("falcon.API") } /** Holds if `route` is routed to `resource` */ private predicate api_route(CallNode route_call, ControlFlowNode route, ClassValue resource) { - route_call.getFunction().(AttrNode).getObject("add_route").pointsTo().getClass() = - theFalconAPIClass() and - route_call.getArg(0) = route and - route_call.getArg(1).pointsTo().getClass() = resource + route_call.getFunction().(AttrNode).getObject("add_route").pointsTo().getClass() = + theFalconAPIClass() and + route_call.getArg(0) = route and + route_call.getArg(1).pointsTo().getClass() = resource } private predicate route(FalconRoute route, Function target, string funcname) { - route.getResourceClass().lookup("on_" + funcname).(FunctionValue).getScope() = target + route.getResourceClass().lookup("on_" + funcname).(FunctionValue).getScope() = target } class FalconRoute extends ControlFlowNode { - FalconRoute() { api_route(this, _, _) } + FalconRoute() { api_route(this, _, _) } - string getUrl() { - exists(StrConst url | - api_route(this, url.getAFlowNode(), _) and - result = url.getText() - ) - } + string getUrl() { + exists(StrConst url | + api_route(this, url.getAFlowNode(), _) and + result = url.getText() + ) + } - ClassValue getResourceClass() { api_route(this, _, result) } + ClassValue getResourceClass() { api_route(this, _, result) } - FalconHandlerFunction getHandlerFunction(string method) { route(this, result, method) } + FalconHandlerFunction getHandlerFunction(string method) { route(this, result, method) } } class FalconHandlerFunction extends Function { - FalconHandlerFunction() { route(_, this, _) } + FalconHandlerFunction() { route(_, this, _) } - private string methodName() { route(_, this, result) } + private string methodName() { route(_, this, result) } - string getMethod() { result = this.methodName().toUpperCase() } + string getMethod() { result = this.methodName().toUpperCase() } - Parameter getRequest() { result = this.getArg(1) } + Parameter getRequest() { result = this.getArg(1) } - Parameter getResponse() { result = this.getArg(2) } + Parameter getResponse() { result = this.getArg(2) } } diff --git a/python/ql/src/semmle/python/web/falcon/Request.qll b/python/ql/src/semmle/python/web/falcon/Request.qll index 66707b01d0c..4b6ceb93fb6 100644 --- a/python/ql/src/semmle/python/web/falcon/Request.qll +++ b/python/ql/src/semmle/python/web/falcon/Request.qll @@ -6,39 +6,39 @@ import semmle.python.security.strings.External /** https://falcon.readthedocs.io/en/stable/api/request_and_response.html */ class FalconRequest extends TaintKind { - FalconRequest() { this = "falcon.request" } + FalconRequest() { this = "falcon.request" } - override TaintKind getTaintOfAttribute(string name) { - name = "env" and result instanceof WsgiEnvironment - or - result instanceof ExternalStringKind and - ( - name = "uri" or - name = "url" or - name = "forwarded_uri" or - name = "relative_uri" or - name = "query_string" - ) - or - result instanceof ExternalStringDictKind and - (name = "cookies" or name = "params") - or - name = "stream" and result instanceof ExternalFileObject - } + override TaintKind getTaintOfAttribute(string name) { + name = "env" and result instanceof WsgiEnvironment + or + result instanceof ExternalStringKind and + ( + name = "uri" or + name = "url" or + name = "forwarded_uri" or + name = "relative_uri" or + name = "query_string" + ) + or + result instanceof ExternalStringDictKind and + (name = "cookies" or name = "params") + or + name = "stream" and result instanceof ExternalFileObject + } - override TaintKind getTaintOfMethodResult(string name) { - name = "get_param" and result instanceof ExternalStringKind - or - name = "get_param_as_json" and result instanceof ExternalJsonKind - or - name = "get_param_as_list" and result instanceof ExternalStringSequenceKind - } + override TaintKind getTaintOfMethodResult(string name) { + name = "get_param" and result instanceof ExternalStringKind + or + name = "get_param_as_json" and result instanceof ExternalJsonKind + or + name = "get_param_as_list" and result instanceof ExternalStringSequenceKind + } } class FalconRequestParameter extends HttpRequestTaintSource { - FalconRequestParameter() { - exists(FalconHandlerFunction f | f.getRequest() = this.(ControlFlowNode).getNode()) - } + FalconRequestParameter() { + exists(FalconHandlerFunction f | f.getRequest() = this.(ControlFlowNode).getNode()) + } - override predicate isSourceOf(TaintKind k) { k instanceof FalconRequest } + override predicate isSourceOf(TaintKind k) { k instanceof FalconRequest } } diff --git a/python/ql/src/semmle/python/web/falcon/Response.qll b/python/ql/src/semmle/python/web/falcon/Response.qll index c66a6315ce5..3ea44fafcd3 100644 --- a/python/ql/src/semmle/python/web/falcon/Response.qll +++ b/python/ql/src/semmle/python/web/falcon/Response.qll @@ -6,24 +6,24 @@ import semmle.python.security.strings.External /** https://falcon.readthedocs.io/en/stable/api/request_and_response.html */ class FalconResponse extends TaintKind { - FalconResponse() { this = "falcon.response" } + FalconResponse() { this = "falcon.response" } } /** Only used internally to track the response parameter */ private class FalconResponseParameter extends TaintSource { - FalconResponseParameter() { - exists(FalconHandlerFunction f | f.getResponse() = this.(ControlFlowNode).getNode()) - } + FalconResponseParameter() { + exists(FalconHandlerFunction f | f.getResponse() = this.(ControlFlowNode).getNode()) + } - override predicate isSourceOf(TaintKind k) { k instanceof FalconResponse } + override predicate isSourceOf(TaintKind k) { k instanceof FalconResponse } } class FalconResponseBodySink extends HttpResponseTaintSink { - FalconResponseBodySink() { - exists(AttrNode attr | any(FalconResponse f).taints(attr.getObject("body")) | - attr.(DefinitionNode).getValue() = this - ) - } + FalconResponseBodySink() { + exists(AttrNode attr | any(FalconResponse f).taints(attr.getObject("body")) | + attr.(DefinitionNode).getValue() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } } diff --git a/python/ql/src/semmle/python/web/flask/General.qll b/python/ql/src/semmle/python/web/flask/General.qll index 71eb57fb07b..d5d7e30ec47 100644 --- a/python/ql/src/semmle/python/web/flask/General.qll +++ b/python/ql/src/semmle/python/web/flask/General.qll @@ -15,36 +15,36 @@ ClassValue theFlaskReponseClass() { result = Value::named("flask.Response") } * by decorating `func` with `app.route(route)` */ predicate app_route(ControlFlowNode route, Function func) { - exists(CallNode route_call, CallNode decorator_call | - route_call.getFunction().(AttrNode).getObject("route").pointsTo().getClass() = theFlaskClass() and - decorator_call.getFunction() = route_call and - route_call.getArg(0) = route and - decorator_call.getArg(0).getNode().(FunctionExpr).getInnerScope() = func - ) + exists(CallNode route_call, CallNode decorator_call | + route_call.getFunction().(AttrNode).getObject("route").pointsTo().getClass() = theFlaskClass() and + decorator_call.getFunction() = route_call and + route_call.getArg(0) = route and + decorator_call.getArg(0).getNode().(FunctionExpr).getInnerScope() = func + ) } /* Helper for add_url_rule */ private predicate add_url_rule_call(ControlFlowNode regex, ControlFlowNode callable) { - exists(CallNode call | - call.getFunction().(AttrNode).getObject("add_url_rule").pointsTo().getClass() = theFlaskClass() and - regex = call.getArg(0) - | - callable = call.getArg(2) or - callable = call.getArgByName("view_func") - ) + exists(CallNode call | + call.getFunction().(AttrNode).getObject("add_url_rule").pointsTo().getClass() = theFlaskClass() and + regex = call.getArg(0) + | + callable = call.getArg(2) or + callable = call.getArgByName("view_func") + ) } /** Holds if urls matching `regex` are routed to `func` */ predicate add_url_rule(ControlFlowNode regex, Function func) { - exists(ControlFlowNode callable | add_url_rule_call(regex, callable) | - exists(PythonFunctionValue f | f.getScope() = func and callable.pointsTo(f)) - or - /* MethodView.as_view() */ - exists(MethodViewClass view_cls | view_cls.asTaint().taints(callable) | - func = view_cls.lookup(httpVerbLower()).(FunctionValue).getScope() - ) - /* TODO: -- Handle Views that aren't MethodViews */ + exists(ControlFlowNode callable | add_url_rule_call(regex, callable) | + exists(PythonFunctionValue f | f.getScope() = func and callable.pointsTo(f)) + or + /* MethodView.as_view() */ + exists(MethodViewClass view_cls | view_cls.asTaint().taints(callable) | + func = view_cls.lookup(httpVerbLower()).(FunctionValue).getScope() ) + /* TODO: -- Handle Views that aren't MethodViews */ + ) } /** @@ -52,53 +52,53 @@ predicate add_url_rule(ControlFlowNode regex, Function func) { * any of flask's routing mechanisms. */ predicate flask_routing(ControlFlowNode regex, Function func) { - app_route(regex, func) - or - add_url_rule(regex, func) + app_route(regex, func) + or + add_url_rule(regex, func) } /** A class that extends flask.views.MethodView */ private class MethodViewClass extends ClassValue { - MethodViewClass() { this.getASuperType() = theFlaskMethodViewClass() } + MethodViewClass() { this.getASuperType() = theFlaskMethodViewClass() } - /* As we are restricted to strings for taint kinds, we need to map these classes to strings. */ - string taintString() { result = "flask/" + this.getQualifiedName() + ".as.view" } + /* As we are restricted to strings for taint kinds, we need to map these classes to strings. */ + string taintString() { result = "flask/" + this.getQualifiedName() + ".as.view" } - /* As we are restricted to strings for taint kinds, we need to map these classes to strings. */ - TaintKind asTaint() { result = this.taintString() } + /* As we are restricted to strings for taint kinds, we need to map these classes to strings. */ + TaintKind asTaint() { result = this.taintString() } } private class MethodViewTaint extends TaintKind { - MethodViewTaint() { any(MethodViewClass cls).taintString() = this } + MethodViewTaint() { any(MethodViewClass cls).taintString() = this } } /** A source of method view "taint"s. */ private class AsView extends TaintSource { - AsView() { - exists(ClassValue view_class | - view_class.getASuperType() = theFlaskMethodViewClass() and - this.(CallNode).getFunction().(AttrNode).getObject("as_view").pointsTo(view_class) - ) - } + AsView() { + exists(ClassValue view_class | + view_class.getASuperType() = theFlaskMethodViewClass() and + this.(CallNode).getFunction().(AttrNode).getObject("as_view").pointsTo(view_class) + ) + } - override string toString() { result = "flask.MethodView.as_view()" } + override string toString() { result = "flask.MethodView.as_view()" } - override predicate isSourceOf(TaintKind kind) { - exists(MethodViewClass view_class | - kind = view_class.asTaint() and - this.(CallNode).getFunction().(AttrNode).getObject("as_view").pointsTo(view_class) - ) - } + override predicate isSourceOf(TaintKind kind) { + exists(MethodViewClass view_class | + kind = view_class.asTaint() and + this.(CallNode).getFunction().(AttrNode).getObject("as_view").pointsTo(view_class) + ) + } } class FlaskCookieSet extends CookieSet, CallNode { - FlaskCookieSet() { - any(FlaskResponseTaintKind t).taints(this.getFunction().(AttrNode).getObject("set_cookie")) - } + FlaskCookieSet() { + any(FlaskResponseTaintKind t).taints(this.getFunction().(AttrNode).getObject("set_cookie")) + } - override string toString() { result = CallNode.super.toString() } + override string toString() { result = CallNode.super.toString() } - override ControlFlowNode getKey() { result = this.getArg(0) } + override ControlFlowNode getKey() { result = this.getArg(0) } - override ControlFlowNode getValue() { result = this.getArg(1) } + override ControlFlowNode getValue() { result = this.getArg(1) } } diff --git a/python/ql/src/semmle/python/web/flask/Redirect.qll b/python/ql/src/semmle/python/web/flask/Redirect.qll index 4c4e289c605..caba59ec2c1 100644 --- a/python/ql/src/semmle/python/web/flask/Redirect.qll +++ b/python/ql/src/semmle/python/web/flask/Redirect.qll @@ -15,12 +15,12 @@ FunctionValue flask_redirect() { result = Value::named("flask.redirect") } * Represents an argument to the `flask.redirect` function. */ class FlaskRedirect extends HttpRedirectTaintSink { - override string toString() { result = "flask.redirect" } + override string toString() { result = "flask.redirect" } - FlaskRedirect() { - exists(CallNode call | - flask_redirect().getACall() = call and - this = call.getAnArg() - ) - } + FlaskRedirect() { + exists(CallNode call | + flask_redirect().getACall() = call and + this = call.getAnArg() + ) + } } diff --git a/python/ql/src/semmle/python/web/flask/Request.qll b/python/ql/src/semmle/python/web/flask/Request.qll index 5548e409c32..ceae5e7a2c6 100644 --- a/python/ql/src/semmle/python/web/flask/Request.qll +++ b/python/ql/src/semmle/python/web/flask/Request.qll @@ -7,52 +7,52 @@ private Value theFlaskRequestObject() { result = Value::named("flask.request") } /** Holds if `attr` is an access of attribute `name` of the flask request object */ private predicate flask_request_attr(AttrNode attr, string name) { - attr.isLoad() and - attr.getObject(name).pointsTo(theFlaskRequestObject()) + attr.isLoad() and + attr.getObject(name).pointsTo(theFlaskRequestObject()) } /** Source of external data from a flask request */ class FlaskRequestData extends HttpRequestTaintSource { - FlaskRequestData() { - not this instanceof FlaskRequestArgs and - exists(string name | flask_request_attr(this, name) | - name = "path" or - name = "full_path" or - name = "base_url" or - name = "url" - ) - } + FlaskRequestData() { + not this instanceof FlaskRequestArgs and + exists(string name | flask_request_attr(this, name) | + name = "path" or + name = "full_path" or + name = "base_url" or + name = "url" + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "flask.request" } + override string toString() { result = "flask.request" } } /** Source of dictionary whose values are externally controlled */ class FlaskRequestArgs extends HttpRequestTaintSource { - FlaskRequestArgs() { - exists(string attr | flask_request_attr(this, attr) | - attr = "args" or - attr = "form" or - attr = "values" or - attr = "files" or - attr = "headers" or - attr = "json" - ) - } + FlaskRequestArgs() { + exists(string attr | flask_request_attr(this, attr) | + attr = "args" or + attr = "form" or + attr = "values" or + attr = "files" or + attr = "headers" or + attr = "json" + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - override string toString() { result = "flask.request.args" } + override string toString() { result = "flask.request.args" } } /** Source of dictionary whose values are externally controlled */ class FlaskRequestJson extends HttpRequestTaintSource { - FlaskRequestJson() { flask_request_attr(this, "json") } + FlaskRequestJson() { flask_request_attr(this, "json") } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalJsonKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalJsonKind } - override string toString() { result = "flask.request.json" } + override string toString() { result = "flask.request.json" } } /** @@ -66,23 +66,23 @@ class FlaskRequestJson extends HttpRequestTaintSource { * ``` */ class FlaskRoutedParameter extends HttpRequestTaintSource { - FlaskRoutedParameter() { - exists(string name, Function func, StrConst url_pattern | - this.(ControlFlowNode).getNode() = func.getArgByName(name) and - flask_routing(url_pattern.getAFlowNode(), func) and - exists(string match | - match = url_pattern.getS().regexpFind(werkzeug_rule_re(), _, _) and - name = match.regexpCapture(werkzeug_rule_re(), 4) - ) - ) - } + FlaskRoutedParameter() { + exists(string name, Function func, StrConst url_pattern | + this.(ControlFlowNode).getNode() = func.getArgByName(name) and + flask_routing(url_pattern.getAFlowNode(), func) and + exists(string match | + match = url_pattern.getS().regexpFind(werkzeug_rule_re(), _, _) and + name = match.regexpCapture(werkzeug_rule_re(), 4) + ) + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } } private string werkzeug_rule_re() { - // since flask uses werkzeug internally, we are using its routing rules from - // https://github.com/pallets/werkzeug/blob/4dc8d6ab840d4b78cbd5789cef91b01e3bde01d5/src/werkzeug/routing.py#L138-L151 - result = - "(?[^<]*)<(?:(?[a-zA-Z_][a-zA-Z0-9_]*)(?:\\((?.*?)\\))?\\:)?(?[a-zA-Z_][a-zA-Z0-9_]*)>" + // since flask uses werkzeug internally, we are using its routing rules from + // https://github.com/pallets/werkzeug/blob/4dc8d6ab840d4b78cbd5789cef91b01e3bde01d5/src/werkzeug/routing.py#L138-L151 + result = + "(?[^<]*)<(?:(?[a-zA-Z_][a-zA-Z0-9_]*)(?:\\((?.*?)\\))?\\:)?(?[a-zA-Z_][a-zA-Z0-9_]*)>" } diff --git a/python/ql/src/semmle/python/web/flask/Response.qll b/python/ql/src/semmle/python/web/flask/Response.qll index e070f19b1f6..e8166175580 100644 --- a/python/ql/src/semmle/python/web/flask/Response.qll +++ b/python/ql/src/semmle/python/web/flask/Response.qll @@ -8,48 +8,48 @@ import semmle.python.web.flask.General * http response malice. */ class FlaskRoutedResponse extends HttpResponseTaintSink { - FlaskRoutedResponse() { - exists(PythonFunctionValue response | - flask_routing(_, response.getScope()) and - this = response.getAReturnedNode() - ) - } + FlaskRoutedResponse() { + exists(PythonFunctionValue response | + flask_routing(_, response.getScope()) and + this = response.getAReturnedNode() + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } - override string toString() { result = "flask.routed.response" } + override string toString() { result = "flask.routed.response" } } class FlaskResponseArgument extends HttpResponseTaintSink { - FlaskResponseArgument() { - exists(CallNode call | - ( - call.getFunction().pointsTo(theFlaskReponseClass()) - or - call.getFunction().pointsTo(Value::named("flask.make_response")) - ) and - call.getArg(0) = this - ) - } + FlaskResponseArgument() { + exists(CallNode call | + ( + call.getFunction().pointsTo(theFlaskReponseClass()) + or + call.getFunction().pointsTo(Value::named("flask.make_response")) + ) and + call.getArg(0) = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } - override string toString() { result = "flask.response.argument" } + override string toString() { result = "flask.response.argument" } } class FlaskResponseTaintKind extends TaintKind { - FlaskResponseTaintKind() { this = "flask.Response" } + FlaskResponseTaintKind() { this = "flask.Response" } } class FlaskResponseConfiguration extends TaintTracking::Configuration { - FlaskResponseConfiguration() { this = "Flask response configuration" } + FlaskResponseConfiguration() { this = "Flask response configuration" } - override predicate isSource(DataFlow::Node node, TaintKind kind) { - kind instanceof FlaskResponseTaintKind and - ( - node.asCfgNode().(CallNode).getFunction().pointsTo(theFlaskReponseClass()) - or - node.asCfgNode().(CallNode).getFunction().pointsTo(Value::named("flask.make_response")) - ) - } + override predicate isSource(DataFlow::Node node, TaintKind kind) { + kind instanceof FlaskResponseTaintKind and + ( + node.asCfgNode().(CallNode).getFunction().pointsTo(theFlaskReponseClass()) + or + node.asCfgNode().(CallNode).getFunction().pointsTo(Value::named("flask.make_response")) + ) + } } diff --git a/python/ql/src/semmle/python/web/pyramid/Redirect.qll b/python/ql/src/semmle/python/web/pyramid/Redirect.qll index 2ab68b40621..85301256736 100644 --- a/python/ql/src/semmle/python/web/pyramid/Redirect.qll +++ b/python/ql/src/semmle/python/web/pyramid/Redirect.qll @@ -10,24 +10,24 @@ import semmle.python.security.strings.Basic import semmle.python.web.Http private ClassValue redirectClass() { - exists(ModuleValue ex | ex.getName() = "pyramid.httpexceptions" | - ex.attr("HTTPFound") = result - or - ex.attr("HTTPTemporaryRedirect") = result - ) + exists(ModuleValue ex | ex.getName() = "pyramid.httpexceptions" | + ex.attr("HTTPFound") = result + or + ex.attr("HTTPTemporaryRedirect") = result + ) } /** * Represents an argument to the `tornado.redirect` function. */ class PyramidRedirect extends HttpRedirectTaintSink { - override string toString() { result = "pyramid.redirect" } + override string toString() { result = "pyramid.redirect" } - PyramidRedirect() { - exists(CallNode call | call.getFunction().pointsTo(redirectClass()) | - call.getArg(0) = this - or - call.getArgByName("location") = this - ) - } + PyramidRedirect() { + exists(CallNode call | call.getFunction().pointsTo(redirectClass()) | + call.getArg(0) = this + or + call.getArgByName("location") = this + ) + } } diff --git a/python/ql/src/semmle/python/web/pyramid/Request.qll b/python/ql/src/semmle/python/web/pyramid/Request.qll index f3422b682d6..19b1e1af25e 100644 --- a/python/ql/src/semmle/python/web/pyramid/Request.qll +++ b/python/ql/src/semmle/python/web/pyramid/Request.qll @@ -5,21 +5,21 @@ private import semmle.python.web.webob.Request private import semmle.python.web.pyramid.View class PyramidRequest extends BaseWebobRequest { - PyramidRequest() { this = "pyramid.request" } + PyramidRequest() { this = "pyramid.request" } - override ClassValue getType() { result = Value::named("pyramid.request.Request") } + override ClassValue getType() { result = Value::named("pyramid.request.Request") } } /** Source of pyramid request objects */ class PyramidViewArgument extends HttpRequestTaintSource { - PyramidViewArgument() { - exists(Function view_func | - is_pyramid_view_function(view_func) and - this.(ControlFlowNode).getNode() = view_func.getArg(0) - ) - } + PyramidViewArgument() { + exists(Function view_func | + is_pyramid_view_function(view_func) and + this.(ControlFlowNode).getNode() = view_func.getArg(0) + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof PyramidRequest } + override predicate isSourceOf(TaintKind kind) { kind instanceof PyramidRequest } - override string toString() { result = "pyramid.view.argument" } + override string toString() { result = "pyramid.view.argument" } } diff --git a/python/ql/src/semmle/python/web/pyramid/Response.qll b/python/ql/src/semmle/python/web/pyramid/Response.qll index c51a437350d..f29832f2d06 100644 --- a/python/ql/src/semmle/python/web/pyramid/Response.qll +++ b/python/ql/src/semmle/python/web/pyramid/Response.qll @@ -10,29 +10,29 @@ private import semmle.python.web.Http * http response malice. */ class PyramidRoutedResponse extends HttpResponseTaintSink { - PyramidRoutedResponse() { - exists(PythonFunctionValue view | - is_pyramid_view_function(view.getScope()) and - this = view.getAReturnedNode() - ) - } + PyramidRoutedResponse() { + exists(PythonFunctionValue view | + is_pyramid_view_function(view.getScope()) and + this = view.getAReturnedNode() + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } - override string toString() { result = "pyramid.routed.response" } + override string toString() { result = "pyramid.routed.response" } } class PyramidCookieSet extends CookieSet, CallNode { - PyramidCookieSet() { - exists(ControlFlowNode f | - f = this.getFunction().(AttrNode).getObject("set_cookie") and - f.pointsTo().getClass() = Value::named("pyramid.response.Response") - ) - } + PyramidCookieSet() { + exists(ControlFlowNode f | + f = this.getFunction().(AttrNode).getObject("set_cookie") and + f.pointsTo().getClass() = Value::named("pyramid.response.Response") + ) + } - override string toString() { result = CallNode.super.toString() } + override string toString() { result = CallNode.super.toString() } - override ControlFlowNode getKey() { result = this.getArg(0) } + override ControlFlowNode getKey() { result = this.getArg(0) } - override ControlFlowNode getValue() { result = this.getArg(1) } + override ControlFlowNode getValue() { result = this.getArg(1) } } diff --git a/python/ql/src/semmle/python/web/pyramid/View.qll b/python/ql/src/semmle/python/web/pyramid/View.qll index 3bf49de87c3..b4e0dc770fc 100644 --- a/python/ql/src/semmle/python/web/pyramid/View.qll +++ b/python/ql/src/semmle/python/web/pyramid/View.qll @@ -5,5 +5,5 @@ ModuleValue thePyramidViewModule() { result.getName() = "pyramid.view" } Value thePyramidViewConfig() { result = thePyramidViewModule().attr("view_config") } predicate is_pyramid_view_function(Function func) { - func.getADecorator().pointsTo().getClass() = thePyramidViewConfig() + func.getADecorator().pointsTo().getClass() = thePyramidViewConfig() } diff --git a/python/ql/src/semmle/python/web/stdlib/Request.qll b/python/ql/src/semmle/python/web/stdlib/Request.qll index 459a5091389..c1095d811ce 100644 --- a/python/ql/src/semmle/python/web/stdlib/Request.qll +++ b/python/ql/src/semmle/python/web/stdlib/Request.qll @@ -3,122 +3,124 @@ * Specifically, we model `HttpRequestTaintSource`s from instances of `BaseHTTPRequestHandler` * (or subclasses) and form parsing using `cgi.FieldStorage`. */ + import python import semmle.python.dataflow.TaintTracking import semmle.python.web.Http /** Source of BaseHTTPRequestHandler instances. */ class StdLibRequestSource extends HttpRequestTaintSource { - StdLibRequestSource() { - exists(ClassValue cls | - cls.getABaseType+() = Value::named("BaseHTTPServer.BaseHTTPRequestHandler") - or - cls.getABaseType+() = Value::named("http.server.BaseHTTPRequestHandler") - | - this.(ControlFlowNode).pointsTo().getClass() = cls - ) - } + StdLibRequestSource() { + exists(ClassValue cls | + cls.getABaseType+() = Value::named("BaseHTTPServer.BaseHTTPRequestHandler") + or + cls.getABaseType+() = Value::named("http.server.BaseHTTPRequestHandler") + | + this.(ControlFlowNode).pointsTo().getClass() = cls + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof BaseHTTPRequestHandlerKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof BaseHTTPRequestHandlerKind } } /** TaintKind for an instance of BaseHTTPRequestHandler. */ class BaseHTTPRequestHandlerKind extends TaintKind { - BaseHTTPRequestHandlerKind() { this = "BaseHTTPRequestHandlerKind" } + BaseHTTPRequestHandlerKind() { this = "BaseHTTPRequestHandlerKind" } - override TaintKind getTaintOfAttribute(string name) { - name in ["requestline", "path"] and - result instanceof ExternalStringKind - or - name = "headers" and - result instanceof HTTPMessageKind - or - name = "rfile" and - result instanceof ExternalFileObject - } + override TaintKind getTaintOfAttribute(string name) { + name in ["requestline", "path"] and + result instanceof ExternalStringKind + or + name = "headers" and + result instanceof HTTPMessageKind + or + name = "rfile" and + result instanceof ExternalFileObject + } } /** TaintKind for headers (instance of HTTPMessage). */ class HTTPMessageKind extends ExternalStringDictKind { - override TaintKind getTaintOfMethodResult(string name) { - result = super.getTaintOfMethodResult(name) - or - name = "get_all" and - result.(SequenceKind).getItem() = this.getValue() - or - name in ["as_bytes", "as_string"] and - result instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + result = super.getTaintOfMethodResult(name) + or + name = "get_all" and + result.(SequenceKind).getItem() = this.getValue() + or + name in ["as_bytes", "as_string"] and + result instanceof ExternalStringKind + } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - result = super.getTaintForFlowStep(fromnode, tonode) - or - exists(ClassValue cls | cls = ClassValue::unicode() or cls = ClassValue::bytes() | - tonode = cls.getACall() and - tonode.(CallNode).getArg(0) = fromnode and - result instanceof ExternalStringKind - ) - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + result = super.getTaintForFlowStep(fromnode, tonode) + or + exists(ClassValue cls | cls = ClassValue::unicode() or cls = ClassValue::bytes() | + tonode = cls.getACall() and + tonode.(CallNode).getArg(0) = fromnode and + result instanceof ExternalStringKind + ) + } } /** Source of parsed HTTP forms (by using the `cgi` module). */ class CgiFieldStorageSource extends HttpRequestTaintSource { - CgiFieldStorageSource() { this = Value::named("cgi.FieldStorage").getACall() } + CgiFieldStorageSource() { this = Value::named("cgi.FieldStorage").getACall() } - override predicate isSourceOf(TaintKind kind) { kind instanceof CgiFieldStorageFormKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof CgiFieldStorageFormKind } } /** TaintKind for a parsed HTTP form. */ class CgiFieldStorageFormKind extends TaintKind { - /* - * There is a slight difference between how we model form/fields and how it is handled by the code. - * In the code - * ``` - * form = cgi.FieldStorage() - * field = form['myfield'] - * ``` - * both `form` and `field` have the type `cgi.FieldStorage`. This allows the code to represent - * nested forms as `form['nested_form']['myfield']`. However, since HTML forms can't be nested - * we ignore that detail since it allows for a more clean modeling. - */ - CgiFieldStorageFormKind() { this = "CgiFieldStorageFormKind" } + /* + * There is a slight difference between how we model form/fields and how it is handled by the code. + * In the code + * ``` + * form = cgi.FieldStorage() + * field = form['myfield'] + * ``` + * both `form` and `field` have the type `cgi.FieldStorage`. This allows the code to represent + * nested forms as `form['nested_form']['myfield']`. However, since HTML forms can't be nested + * we ignore that detail since it allows for a more clean modeling. + */ - override TaintKind getTaintOfAttribute(string name) { - name = "value" and result.(SequenceKind).getItem() instanceof CgiFieldStorageFieldKind - } + CgiFieldStorageFormKind() { this = "CgiFieldStorageFormKind" } - override TaintKind getTaintOfMethodResult(string name) { - name = "getvalue" and - ( - result instanceof ExternalStringKind - or - result.(SequenceKind).getItem() instanceof ExternalStringKind - ) - or - name = "getfirst" and - result instanceof ExternalStringKind - or - name = "getlist" and - result.(SequenceKind).getItem() instanceof ExternalStringKind - } + override TaintKind getTaintOfAttribute(string name) { + name = "value" and result.(SequenceKind).getItem() instanceof CgiFieldStorageFieldKind + } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - tonode.(SubscriptNode).getObject() = fromnode and - ( - result instanceof CgiFieldStorageFieldKind - or - result.(SequenceKind).getItem() instanceof CgiFieldStorageFieldKind - ) - } + override TaintKind getTaintOfMethodResult(string name) { + name = "getvalue" and + ( + result instanceof ExternalStringKind + or + result.(SequenceKind).getItem() instanceof ExternalStringKind + ) + or + name = "getfirst" and + result instanceof ExternalStringKind + or + name = "getlist" and + result.(SequenceKind).getItem() instanceof ExternalStringKind + } + + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + tonode.(SubscriptNode).getObject() = fromnode and + ( + result instanceof CgiFieldStorageFieldKind + or + result.(SequenceKind).getItem() instanceof CgiFieldStorageFieldKind + ) + } } /** TaintKind for the field of a parsed HTTP form. */ class CgiFieldStorageFieldKind extends TaintKind { - CgiFieldStorageFieldKind() { this = "CgiFieldStorageFieldKind" } + CgiFieldStorageFieldKind() { this = "CgiFieldStorageFieldKind" } - override TaintKind getTaintOfAttribute(string name) { - name in ["filename", "value"] and result instanceof ExternalStringKind - or - name = "file" and result instanceof ExternalFileObject - } + override TaintKind getTaintOfAttribute(string name) { + name in ["filename", "value"] and result instanceof ExternalStringKind + or + name = "file" and result instanceof ExternalFileObject + } } diff --git a/python/ql/src/semmle/python/web/stdlib/Response.qll b/python/ql/src/semmle/python/web/stdlib/Response.qll index 58949e0a6d9..784690dea5a 100644 --- a/python/ql/src/semmle/python/web/stdlib/Response.qll +++ b/python/ql/src/semmle/python/web/stdlib/Response.qll @@ -7,37 +7,37 @@ import semmle.python.dataflow.TaintTracking import semmle.python.web.Http private predicate is_wfile(AttrNode wfile) { - exists(ClassValue cls | - // Python 2 - cls.getABaseType+() = Value::named("BaseHTTPServer.BaseHTTPRequestHandler") - or - // Python 3 - cls.getABaseType+() = Value::named("http.server.BaseHTTPRequestHandler") - | - wfile.getObject("wfile").pointsTo().getClass() = cls - ) + exists(ClassValue cls | + // Python 2 + cls.getABaseType+() = Value::named("BaseHTTPServer.BaseHTTPRequestHandler") + or + // Python 3 + cls.getABaseType+() = Value::named("http.server.BaseHTTPRequestHandler") + | + wfile.getObject("wfile").pointsTo().getClass() = cls + ) } /** Sink for `h.wfile.write` where `h` is an instance of BaseHTTPRequestHandler. */ class StdLibWFileWriteSink extends HttpResponseTaintSink { - StdLibWFileWriteSink() { - exists(CallNode call | - is_wfile(call.getFunction().(AttrNode).getObject("write")) and - call.getArg(0) = this - ) - } + StdLibWFileWriteSink() { + exists(CallNode call | + is_wfile(call.getFunction().(AttrNode).getObject("write")) and + call.getArg(0) = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } /** Sink for `h.wfile.writelines` where `h` is an instance of BaseHTTPRequestHandler. */ class StdLibWFileWritelinesSink extends HttpResponseTaintSink { - StdLibWFileWritelinesSink() { - exists(CallNode call | - is_wfile(call.getFunction().(AttrNode).getObject("writelines")) and - call.getArg(0) = this - ) - } + StdLibWFileWritelinesSink() { + exists(CallNode call | + is_wfile(call.getFunction().(AttrNode).getObject("writelines")) and + call.getArg(0) = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringSequenceKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringSequenceKind } } diff --git a/python/ql/src/semmle/python/web/tornado/Redirect.qll b/python/ql/src/semmle/python/web/tornado/Redirect.qll index f846f113816..93875c948ce 100644 --- a/python/ql/src/semmle/python/web/tornado/Redirect.qll +++ b/python/ql/src/semmle/python/web/tornado/Redirect.qll @@ -14,15 +14,15 @@ import Tornado * Represents an argument to the `tornado.redirect` function. */ class TornadoHttpRequestHandlerRedirect extends HttpRedirectTaintSink { - override string toString() { result = "tornado.HttpRequestHandler.redirect" } + override string toString() { result = "tornado.HttpRequestHandler.redirect" } - TornadoHttpRequestHandlerRedirect() { - exists(CallNode call, ControlFlowNode node | - node = call.getFunction().(AttrNode).getObject("redirect") and - isTornadoRequestHandlerInstance(node) and - this = call.getArg(0) - ) - } + TornadoHttpRequestHandlerRedirect() { + exists(CallNode call, ControlFlowNode node | + node = call.getFunction().(AttrNode).getObject("redirect") and + isTornadoRequestHandlerInstance(node) and + this = call.getArg(0) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } } diff --git a/python/ql/src/semmle/python/web/tornado/Request.qll b/python/ql/src/semmle/python/web/tornado/Request.qll index cfb7bfa7b04..1dde3379117 100644 --- a/python/ql/src/semmle/python/web/tornado/Request.qll +++ b/python/ql/src/semmle/python/web/tornado/Request.qll @@ -5,68 +5,68 @@ import Tornado /** A tornado.request.HttpRequest object */ class TornadoRequest extends TaintKind { - TornadoRequest() { this = "tornado.request.HttpRequest" } + TornadoRequest() { this = "tornado.request.HttpRequest" } - override TaintKind getTaintOfAttribute(string name) { - result instanceof ExternalStringDictKind and - ( - name = "headers" or - name = "cookies" - ) - or - result instanceof ExternalStringKind and - ( - name = "uri" or - name = "query" or - name = "body" - ) - or - result instanceof ExternalStringSequenceDictKind and - ( - name = "arguments" or - name = "query_arguments" or - name = "body_arguments" - ) - } + override TaintKind getTaintOfAttribute(string name) { + result instanceof ExternalStringDictKind and + ( + name = "headers" or + name = "cookies" + ) + or + result instanceof ExternalStringKind and + ( + name = "uri" or + name = "query" or + name = "body" + ) + or + result instanceof ExternalStringSequenceDictKind and + ( + name = "arguments" or + name = "query_arguments" or + name = "body_arguments" + ) + } } class TornadoRequestSource extends HttpRequestTaintSource { - TornadoRequestSource() { isTornadoRequestHandlerInstance(this.(AttrNode).getObject("request")) } + TornadoRequestSource() { isTornadoRequestHandlerInstance(this.(AttrNode).getObject("request")) } - override string toString() { result = "Tornado request source" } + override string toString() { result = "Tornado request source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof TornadoRequest } + override predicate isSourceOf(TaintKind kind) { kind instanceof TornadoRequest } } class TornadoExternalInputSource extends HttpRequestTaintSource { - TornadoExternalInputSource() { - exists(string name | - name = "get_argument" or - name = "get_query_argument" or - name = "get_body_argument" or - name = "decode_argument" - | - this = callToNamedTornadoRequestHandlerMethod(name) - ) - } + TornadoExternalInputSource() { + exists(string name | + name = "get_argument" or + name = "get_query_argument" or + name = "get_body_argument" or + name = "decode_argument" + | + this = callToNamedTornadoRequestHandlerMethod(name) + ) + } - override string toString() { result = "Tornado request method" } + override string toString() { result = "Tornado request method" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } } class TornadoExternalInputListSource extends HttpRequestTaintSource { - TornadoExternalInputListSource() { - exists(string name | - name = "get_arguments" or - name = "get_query_arguments" or - name = "get_body_arguments" - | - this = callToNamedTornadoRequestHandlerMethod(name) - ) - } + TornadoExternalInputListSource() { + exists(string name | + name = "get_arguments" or + name = "get_query_arguments" or + name = "get_body_arguments" + | + this = callToNamedTornadoRequestHandlerMethod(name) + ) + } - override string toString() { result = "Tornado request method" } + override string toString() { result = "Tornado request method" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } } diff --git a/python/ql/src/semmle/python/web/tornado/Response.qll b/python/ql/src/semmle/python/web/tornado/Response.qll index b9213ac8446..8ef3762ab2d 100644 --- a/python/ql/src/semmle/python/web/tornado/Response.qll +++ b/python/ql/src/semmle/python/web/tornado/Response.qll @@ -5,43 +5,43 @@ private import semmle.python.web.Http import Tornado class TornadoConnection extends TaintKind { - TornadoConnection() { this = "tornado.http.connection" } + TornadoConnection() { this = "tornado.http.connection" } } class TornadoConnectionSource extends TaintSource { - TornadoConnectionSource() { - isTornadoRequestHandlerInstance(this.(AttrNode).getObject("connection")) - } + TornadoConnectionSource() { + isTornadoRequestHandlerInstance(this.(AttrNode).getObject("connection")) + } - override string toString() { result = "Tornado http connection source" } + override string toString() { result = "Tornado http connection source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof TornadoConnection } + override predicate isSourceOf(TaintKind kind) { kind instanceof TornadoConnection } } class TornadoConnectionWrite extends HttpResponseTaintSink { - override string toString() { result = "tornado.connection.write" } + override string toString() { result = "tornado.connection.write" } - TornadoConnectionWrite() { - exists(CallNode call, ControlFlowNode conn | - conn = call.getFunction().(AttrNode).getObject("write") and - this = call.getAnArg() and - exists(TornadoConnection tc | tc.taints(conn)) - ) - } + TornadoConnectionWrite() { + exists(CallNode call, ControlFlowNode conn | + conn = call.getFunction().(AttrNode).getObject("write") and + this = call.getAnArg() and + exists(TornadoConnection tc | tc.taints(conn)) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } } class TornadoHttpRequestHandlerWrite extends HttpResponseTaintSink { - override string toString() { result = "tornado.HttpRequestHandler.write" } + override string toString() { result = "tornado.HttpRequestHandler.write" } - TornadoHttpRequestHandlerWrite() { - exists(CallNode call, ControlFlowNode node | - node = call.getFunction().(AttrNode).getObject("write") and - this = call.getAnArg() and - isTornadoRequestHandlerInstance(node) - ) - } + TornadoHttpRequestHandlerWrite() { + exists(CallNode call, ControlFlowNode node | + node = call.getFunction().(AttrNode).getObject("write") and + this = call.getAnArg() and + isTornadoRequestHandlerInstance(node) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } } diff --git a/python/ql/src/semmle/python/web/tornado/Tornado.qll b/python/ql/src/semmle/python/web/tornado/Tornado.qll index d9f6ab823b9..40523b15261 100644 --- a/python/ql/src/semmle/python/web/tornado/Tornado.qll +++ b/python/ql/src/semmle/python/web/tornado/Tornado.qll @@ -3,11 +3,11 @@ import semmle.python.dataflow.TaintTracking import semmle.python.web.Http private ClassValue theTornadoRequestHandlerClass() { - result = Value::named("tornado.web.RequestHandler") + result = Value::named("tornado.web.RequestHandler") } ClassValue aTornadoRequestHandlerClass() { - result.getABaseType+() = theTornadoRequestHandlerClass() + result.getABaseType+() = theTornadoRequestHandlerClass() } /** @@ -15,36 +15,36 @@ ClassValue aTornadoRequestHandlerClass() { * `RequestHandler` class. */ predicate isTornadoRequestHandlerInstance(ControlFlowNode node) { - node.pointsTo().getClass() = aTornadoRequestHandlerClass() - or - /* - * In some cases, the points-to analysis won't capture all instances we care - * about. For these, we use the following syntactic check. First, that - * `node` appears inside a method of a subclass of - * `tornado.web.RequestHandler`: - */ + node.pointsTo().getClass() = aTornadoRequestHandlerClass() + or + /* + * In some cases, the points-to analysis won't capture all instances we care + * about. For these, we use the following syntactic check. First, that + * `node` appears inside a method of a subclass of + * `tornado.web.RequestHandler`: + */ - node.getScope().getEnclosingScope() = aTornadoRequestHandlerClass().getScope() and - /* Secondly, that `node` refers to the `self` argument: */ - node.isLoad() and - node.(NameNode).isSelf() + node.getScope().getEnclosingScope() = aTornadoRequestHandlerClass().getScope() and + /* Secondly, that `node` refers to the `self` argument: */ + node.isLoad() and + node.(NameNode).isSelf() } CallNode callToNamedTornadoRequestHandlerMethod(string name) { - isTornadoRequestHandlerInstance(result.getFunction().(AttrNode).getObject(name)) + isTornadoRequestHandlerInstance(result.getFunction().(AttrNode).getObject(name)) } class TornadoCookieSet extends CookieSet, CallNode { - TornadoCookieSet() { - exists(ControlFlowNode f | - f = this.getFunction().(AttrNode).getObject("set_cookie") and - isTornadoRequestHandlerInstance(f) - ) - } + TornadoCookieSet() { + exists(ControlFlowNode f | + f = this.getFunction().(AttrNode).getObject("set_cookie") and + isTornadoRequestHandlerInstance(f) + ) + } - override string toString() { result = CallNode.super.toString() } + override string toString() { result = CallNode.super.toString() } - override ControlFlowNode getKey() { result = this.getArg(0) } + override ControlFlowNode getKey() { result = this.getArg(0) } - override ControlFlowNode getValue() { result = this.getArg(1) } + override ControlFlowNode getValue() { result = this.getArg(1) } } diff --git a/python/ql/src/semmle/python/web/turbogears/Request.qll b/python/ql/src/semmle/python/web/turbogears/Request.qll index 19d9be06c52..806d85bafc5 100644 --- a/python/ql/src/semmle/python/web/turbogears/Request.qll +++ b/python/ql/src/semmle/python/web/turbogears/Request.qll @@ -4,23 +4,23 @@ import semmle.python.web.Http import TurboGears private class ValidatedMethodParameter extends Parameter { - ValidatedMethodParameter() { - exists(string name, TurboGearsControllerMethod method | - method.getArgByName(name) = this and - method.getValidationDict().getItem(_).(KeyValuePair).getKey().(StrConst).getText() = name - ) - } + ValidatedMethodParameter() { + exists(string name, TurboGearsControllerMethod method | + method.getArgByName(name) = this and + method.getValidationDict().getItem(_).(KeyValuePair).getKey().(StrConst).getText() = name + ) + } } class UnvalidatedControllerMethodParameter extends HttpRequestTaintSource { - UnvalidatedControllerMethodParameter() { - exists(Parameter p | - any(TurboGearsControllerMethod m | not m.getName() = "onerror").getAnArg() = p and - not p instanceof ValidatedMethodParameter and - not p.isSelf() and - p.(Name).getAFlowNode() = this - ) - } + UnvalidatedControllerMethodParameter() { + exists(Parameter p | + any(TurboGearsControllerMethod m | not m.getName() = "onerror").getAnArg() = p and + not p instanceof ValidatedMethodParameter and + not p.isSelf() and + p.(Name).getAFlowNode() = this + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } } diff --git a/python/ql/src/semmle/python/web/turbogears/Response.qll b/python/ql/src/semmle/python/web/turbogears/Response.qll index b6345d3755a..a095057e7d2 100644 --- a/python/ql/src/semmle/python/web/turbogears/Response.qll +++ b/python/ql/src/semmle/python/web/turbogears/Response.qll @@ -5,27 +5,27 @@ import semmle.python.web.Http import TurboGears class ControllerMethodReturnValue extends HttpResponseTaintSink { - override string toString() { result = "TurboGears ControllerMethodReturnValue" } + override string toString() { result = "TurboGears ControllerMethodReturnValue" } - ControllerMethodReturnValue() { - exists(TurboGearsControllerMethod m | - m.getAReturnValueFlowNode() = this and - not m.isTemplated() - ) - } + ControllerMethodReturnValue() { + exists(TurboGearsControllerMethod m | + m.getAReturnValueFlowNode() = this and + not m.isTemplated() + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } } class ControllerMethodTemplatedReturnValue extends HttpResponseTaintSink { - override string toString() { result = "TurboGears ControllerMethodTemplatedReturnValue" } + override string toString() { result = "TurboGears ControllerMethodTemplatedReturnValue" } - ControllerMethodTemplatedReturnValue() { - exists(TurboGearsControllerMethod m | - m.getAReturnValueFlowNode() = this and - m.isTemplated() - ) - } + ControllerMethodTemplatedReturnValue() { + exists(TurboGearsControllerMethod m | + m.getAReturnValueFlowNode() = this and + m.isTemplated() + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringDictKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringDictKind } } diff --git a/python/ql/src/semmle/python/web/turbogears/TurboGears.qll b/python/ql/src/semmle/python/web/turbogears/TurboGears.qll index 547a6c0e505..0c53dcc17bc 100644 --- a/python/ql/src/semmle/python/web/turbogears/TurboGears.qll +++ b/python/ql/src/semmle/python/web/turbogears/TurboGears.qll @@ -6,28 +6,28 @@ private ClassValue theTurboGearsControllerClass() { result = Value::named("tg.TG ClassValue aTurboGearsControllerClass() { result.getABaseType+() = theTurboGearsControllerClass() } class TurboGearsControllerMethod extends Function { - ControlFlowNode decorator; + ControlFlowNode decorator; - TurboGearsControllerMethod() { - aTurboGearsControllerClass().getScope() = this.getScope() and - decorator = this.getADecorator().getAFlowNode() and - /* Is decorated with @expose() or @expose(path) */ - ( - decorator.(CallNode).getFunction().(NameNode).getId() = "expose" - or - decorator.pointsTo().getClass() = Value::named("tg.expose") - ) - } + TurboGearsControllerMethod() { + aTurboGearsControllerClass().getScope() = this.getScope() and + decorator = this.getADecorator().getAFlowNode() and + /* Is decorated with @expose() or @expose(path) */ + ( + decorator.(CallNode).getFunction().(NameNode).getId() = "expose" + or + decorator.pointsTo().getClass() = Value::named("tg.expose") + ) + } - private ControlFlowNode templateName() { result = decorator.(CallNode).getArg(0) } + private ControlFlowNode templateName() { result = decorator.(CallNode).getArg(0) } - predicate isTemplated() { exists(templateName()) } + predicate isTemplated() { exists(templateName()) } - Dict getValidationDict() { - exists(Call call, Value dict | - call = this.getADecorator() and - call.getFunc().(Name).getId() = "validate" and - call.getArg(0).pointsTo(dict, result) - ) - } + Dict getValidationDict() { + exists(Call call, Value dict | + call = this.getADecorator() and + call.getFunc().(Name).getId() = "validate" and + call.getArg(0).pointsTo(dict, result) + ) + } } diff --git a/python/ql/src/semmle/python/web/twisted/Request.qll b/python/ql/src/semmle/python/web/twisted/Request.qll index 0be6fc78f2c..e164de585d1 100644 --- a/python/ql/src/semmle/python/web/twisted/Request.qll +++ b/python/ql/src/semmle/python/web/twisted/Request.qll @@ -5,31 +5,31 @@ import Twisted /** A twisted.web.http.Request object */ class TwistedRequest extends TaintKind { - TwistedRequest() { this = "twisted.request.http.Request" } + TwistedRequest() { this = "twisted.request.http.Request" } - override TaintKind getTaintOfAttribute(string name) { - result instanceof ExternalStringSequenceDictKind and - name = "args" - or - result instanceof ExternalStringKind and - name = "uri" - } + override TaintKind getTaintOfAttribute(string name) { + result instanceof ExternalStringSequenceDictKind and + name = "args" + or + result instanceof ExternalStringKind and + name = "uri" + } - override TaintKind getTaintOfMethodResult(string name) { - ( - name = "getHeader" or - name = "getCookie" or - name = "getUser" or - name = "getPassword" - ) and - result instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + ( + name = "getHeader" or + name = "getCookie" or + name = "getUser" or + name = "getPassword" + ) and + result instanceof ExternalStringKind + } } class TwistedRequestSource extends HttpRequestTaintSource { - TwistedRequestSource() { isTwistedRequestInstance(this) } + TwistedRequestSource() { isTwistedRequestInstance(this) } - override string toString() { result = "Twisted request source" } + override string toString() { result = "Twisted request source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof TwistedRequest } + override predicate isSourceOf(TaintKind kind) { kind instanceof TwistedRequest } } diff --git a/python/ql/src/semmle/python/web/twisted/Response.qll b/python/ql/src/semmle/python/web/twisted/Response.qll index be32ba08188..4817ec762d6 100644 --- a/python/ql/src/semmle/python/web/twisted/Response.qll +++ b/python/ql/src/semmle/python/web/twisted/Response.qll @@ -6,18 +6,18 @@ import Twisted import Request class TwistedResponse extends HttpResponseTaintSink { - TwistedResponse() { - exists(PythonFunctionValue func, string name | - isKnownRequestHandlerMethodName(name) and - name = func.getName() and - func = getTwistedRequestHandlerMethod(name) and - this = func.getAReturnedNode() - ) - } + TwistedResponse() { + exists(PythonFunctionValue func, string name | + isKnownRequestHandlerMethodName(name) and + name = func.getName() and + func = getTwistedRequestHandlerMethod(name) and + this = func.getAReturnedNode() + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "Twisted response" } + override string toString() { result = "Twisted response" } } /** @@ -26,20 +26,20 @@ class TwistedResponse extends HttpResponseTaintSink { * request. */ class TwistedRequestSetter extends HttpResponseTaintSink { - TwistedRequestSetter() { - exists(CallNode call, ControlFlowNode node, string name | - ( - name = "setHeader" or - name = "addCookie" or - name = "write" - ) and - any(TwistedRequest t).taints(node) and - node = call.getFunction().(AttrNode).getObject(name) and - this = call.getAnArg() - ) - } + TwistedRequestSetter() { + exists(CallNode call, ControlFlowNode node, string name | + ( + name = "setHeader" or + name = "addCookie" or + name = "write" + ) and + any(TwistedRequest t).taints(node) and + node = call.getFunction().(AttrNode).getObject(name) and + this = call.getAnArg() + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "Twisted request setter" } + override string toString() { result = "Twisted request setter" } } diff --git a/python/ql/src/semmle/python/web/twisted/Twisted.qll b/python/ql/src/semmle/python/web/twisted/Twisted.qll index 9ecd12b9620..98cb60e60dd 100644 --- a/python/ql/src/semmle/python/web/twisted/Twisted.qll +++ b/python/ql/src/semmle/python/web/twisted/Twisted.qll @@ -2,23 +2,23 @@ import python import semmle.python.dataflow.TaintTracking private ClassValue theTwistedHttpRequestClass() { - result = Value::named("twisted.web.http.Request") + result = Value::named("twisted.web.http.Request") } private ClassValue theTwistedHttpResourceClass() { - result = Value::named("twisted.web.resource.Resource") + result = Value::named("twisted.web.resource.Resource") } ClassValue aTwistedRequestHandlerClass() { result.getABaseType+() = theTwistedHttpResourceClass() } FunctionValue getTwistedRequestHandlerMethod(string name) { - result = aTwistedRequestHandlerClass().declaredAttribute(name) + result = aTwistedRequestHandlerClass().declaredAttribute(name) } bindingset[name] predicate isKnownRequestHandlerMethodName(string name) { - name = "render" or - name.matches("render_%") + name = "render" or + name.matches("render_%") } /** @@ -26,25 +26,25 @@ predicate isKnownRequestHandlerMethodName(string name) { * `Request` class. */ predicate isTwistedRequestInstance(NameNode node) { - node.pointsTo().getClass() = theTwistedHttpRequestClass() - or - /* - * In points-to analysis cannot infer that a given object is an instance of - * the `twisted.web.http.Request` class, we also include any parameter - * called `request` that appears inside a subclass of a request handler - * class, and the appropriate arguments of known request handler methods. - */ + node.pointsTo().getClass() = theTwistedHttpRequestClass() + or + /* + * In points-to analysis cannot infer that a given object is an instance of + * the `twisted.web.http.Request` class, we also include any parameter + * called `request` that appears inside a subclass of a request handler + * class, and the appropriate arguments of known request handler methods. + */ - exists(Function func | - func = node.getScope() and - func.getEnclosingScope() = aTwistedRequestHandlerClass().getScope() - | - /* Any parameter called `request` */ - node.getId() = "request" and - node.isParameter() - or - /* Any request parameter of a known request handler method */ - isKnownRequestHandlerMethodName(func.getName()) and - node.getNode() = func.getArg(1) - ) + exists(Function func | + func = node.getScope() and + func.getEnclosingScope() = aTwistedRequestHandlerClass().getScope() + | + /* Any parameter called `request` */ + node.getId() = "request" and + node.isParameter() + or + /* Any request parameter of a known request handler method */ + isKnownRequestHandlerMethodName(func.getName()) and + node.getNode() = func.getArg(1) + ) } diff --git a/python/ql/src/semmle/python/web/webob/Request.qll b/python/ql/src/semmle/python/web/webob/Request.qll index 4d6e98bb2e9..6aafa730ca1 100644 --- a/python/ql/src/semmle/python/web/webob/Request.qll +++ b/python/ql/src/semmle/python/web/webob/Request.qll @@ -3,36 +3,36 @@ import semmle.python.dataflow.TaintTracking import semmle.python.web.Http abstract class BaseWebobRequest extends TaintKind { - bindingset[this] - BaseWebobRequest() { any() } + bindingset[this] + BaseWebobRequest() { any() } - override TaintKind getTaintOfAttribute(string name) { - result instanceof ExternalStringDictKind and - ( - name = "GET" or - name = "POST" or - name = "headers" - ) - or - result instanceof ExternalStringKind and - name = "body" - } + override TaintKind getTaintOfAttribute(string name) { + result instanceof ExternalStringDictKind and + ( + name = "GET" or + name = "POST" or + name = "headers" + ) + or + result instanceof ExternalStringKind and + name = "body" + } - override TaintKind getTaintOfMethodResult(string name) { - result = this and - ( - name = "copy" or - name = "copy_get" or - name = "copy_body" - ) - or - result instanceof ExternalStringKind and - name = "as_bytes" - } + override TaintKind getTaintOfMethodResult(string name) { + result = this and + ( + name = "copy" or + name = "copy_get" or + name = "copy_body" + ) + or + result instanceof ExternalStringKind and + name = "as_bytes" + } } class WebobRequest extends BaseWebobRequest { - WebobRequest() { this = "webob.Request" } + WebobRequest() { this = "webob.Request" } - override ClassValue getType() { result = Value::named("webob.request.Request") } + override ClassValue getType() { result = Value::named("webob.request.Request") } } diff --git a/python/ql/test/2/library-tests/ControlFlow/Exceptions/Likely.ql b/python/ql/test/2/library-tests/ControlFlow/Exceptions/Likely.ql index 80831a9ca54..f8e4a4b7dac 100644 --- a/python/ql/test/2/library-tests/ControlFlow/Exceptions/Likely.ql +++ b/python/ql/test/2/library-tests/ControlFlow/Exceptions/Likely.ql @@ -2,6 +2,6 @@ import python from ControlFlowNode r, ControlFlowNode s where - s = r.getAnExceptionalSuccessor() and - not r.(RaisingNode).unlikelySuccessor(s) + s = r.getAnExceptionalSuccessor() and + not r.(RaisingNode).unlikelySuccessor(s) select r.getLocation().getStartLine(), r.toString(), s.getLocation().getStartLine(), s.toString() diff --git a/python/ql/test/2/library-tests/PointsTo/class_properties/ClassValues.ql b/python/ql/test/2/library-tests/PointsTo/class_properties/ClassValues.ql index 8594fc33ae2..3281b8d26e6 100644 --- a/python/ql/test/2/library-tests/PointsTo/class_properties/ClassValues.ql +++ b/python/ql/test/2/library-tests/PointsTo/class_properties/ClassValues.ql @@ -2,19 +2,19 @@ import python from ClassValue cls, string res where - exists(CallNode call | - call.getFunction().(NameNode).getId() = "test" and - call.getAnArg().pointsTo(cls) - ) and - ( - cls.isSequence() and - cls.isMapping() and - res = "IS BOTH. SHOULD NOT HAPPEN. THEY ARE MUTUALLY EXCLUSIVE." - or - cls.isSequence() and not cls.isMapping() and res = "sequence" - or - not cls.isSequence() and cls.isMapping() and res = "mapping" - or - not cls.isSequence() and not cls.isMapping() and res = "neither sequence nor mapping" - ) + exists(CallNode call | + call.getFunction().(NameNode).getId() = "test" and + call.getAnArg().pointsTo(cls) + ) and + ( + cls.isSequence() and + cls.isMapping() and + res = "IS BOTH. SHOULD NOT HAPPEN. THEY ARE MUTUALLY EXCLUSIVE." + or + cls.isSequence() and not cls.isMapping() and res = "sequence" + or + not cls.isSequence() and cls.isMapping() and res = "mapping" + or + not cls.isSequence() and not cls.isMapping() and res = "neither sequence nor mapping" + ) select res, cls.toString() diff --git a/python/ql/test/2/library-tests/PointsTo/import_time/Pruned.ql b/python/ql/test/2/library-tests/PointsTo/import_time/Pruned.ql index 94a1db9b83d..e77849860c7 100644 --- a/python/ql/test/2/library-tests/PointsTo/import_time/Pruned.ql +++ b/python/ql/test/2/library-tests/PointsTo/import_time/Pruned.ql @@ -4,9 +4,9 @@ import semmle.python.pointsto.PointsToContext from ControlFlowNode f, Location l, Context c where - not PointsToInternal::reachableBlock(f.getBasicBlock(), c) and - c.isImport() and - (f.getNode() instanceof FunctionExpr or f.getNode() instanceof ClassExpr) and - l = f.getLocation() and - l.getFile().getShortName() = "test.py" + not PointsToInternal::reachableBlock(f.getBasicBlock(), c) and + c.isImport() and + (f.getNode() instanceof FunctionExpr or f.getNode() instanceof ClassExpr) and + l = f.getLocation() and + l.getFile().getShortName() = "test.py" select l.getStartLine() diff --git a/python/ql/test/2/library-tests/PointsTo/imports/Runtime.ql b/python/ql/test/2/library-tests/PointsTo/imports/Runtime.ql index 44a35b27b27..7a46cc8cad1 100644 --- a/python/ql/test/2/library-tests/PointsTo/imports/Runtime.ql +++ b/python/ql/test/2/library-tests/PointsTo/imports/Runtime.ql @@ -2,9 +2,9 @@ import python from int line, ControlFlowNode f, Object o, ControlFlowNode orig where - not f.getLocation().getFile().inStdlib() and - f.refersTo(o, orig) and - line = f.getLocation().getStartLine() and - line != 0 and - not o instanceof NumericObject // Omit sys.hexversion as it will change between machines + not f.getLocation().getFile().inStdlib() and + f.refersTo(o, orig) and + line = f.getLocation().getStartLine() and + line != 0 and + not o instanceof NumericObject // Omit sys.hexversion as it will change between machines select f.getLocation().getFile().getShortName(), line, f.toString(), o.toString(), orig.toString() diff --git a/python/ql/test/2/library-tests/PointsTo/origin_uniqueness/Origin.ql b/python/ql/test/2/library-tests/PointsTo/origin_uniqueness/Origin.ql index 6cd800ac399..4c7a4fff358 100644 --- a/python/ql/test/2/library-tests/PointsTo/origin_uniqueness/Origin.ql +++ b/python/ql/test/2/library-tests/PointsTo/origin_uniqueness/Origin.ql @@ -4,8 +4,8 @@ string short_loc(Location l) { result = l.getFile().getShortName() + ":" + l.get from ControlFlowNode use, Object obj, ControlFlowNode orig, int line where - use.refersTo(obj, orig) and - use.getLocation().getFile().getShortName() = "test.py" and - line = use.getLocation().getStartLine() and - not line = 0 + use.refersTo(obj, orig) and + use.getLocation().getFile().getShortName() = "test.py" and + line = use.getLocation().getStartLine() and + not line = 0 select line, use.toString(), obj.toString(), short_loc(orig.getLocation()) diff --git a/python/ql/test/2/library-tests/classes/attr/class_attr.ql b/python/ql/test/2/library-tests/classes/attr/class_attr.ql index 3b7bf8b3ba0..197ab1a1e5e 100644 --- a/python/ql/test/2/library-tests/classes/attr/class_attr.ql +++ b/python/ql/test/2/library-tests/classes/attr/class_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name, Object obj where - cls.hasLocationInfo(_, line, _, _, _) and - obj = cls.lookupAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + obj = cls.lookupAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name, obj.toString() diff --git a/python/ql/test/2/library-tests/classes/attr/class_has_attr.ql b/python/ql/test/2/library-tests/classes/attr/class_has_attr.ql index 2f16aa4ca97..be8272d1bd6 100644 --- a/python/ql/test/2/library-tests/classes/attr/class_has_attr.ql +++ b/python/ql/test/2/library-tests/classes/attr/class_has_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name where - cls.hasLocationInfo(_, line, _, _, _) and - cls.hasAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + cls.hasAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name diff --git a/python/ql/test/2/library-tests/classes/attr/list_attr.ql b/python/ql/test/2/library-tests/classes/attr/list_attr.ql index aad2d9489c3..c38694d5883 100644 --- a/python/ql/test/2/library-tests/classes/attr/list_attr.ql +++ b/python/ql/test/2/library-tests/classes/attr/list_attr.ql @@ -8,9 +8,9 @@ import python from ClassObject cls, string name, Object what where - ( - cls.getName() = "list" or - cls.getASuperType().getName() = "list" - ) and - cls.lookupAttribute(name) = what + ( + cls.getName() = "list" or + cls.getASuperType().getName() = "list" + ) and + cls.lookupAttribute(name) = what select cls.toString(), name, what.toString() diff --git a/python/ql/test/2/library-tests/classes/mro/C3.ql b/python/ql/test/2/library-tests/classes/mro/C3.ql index c4b0dd896d6..6807f223f91 100644 --- a/python/ql/test/2/library-tests/classes/mro/C3.ql +++ b/python/ql/test/2/library-tests/classes/mro/C3.ql @@ -4,7 +4,7 @@ import semmle.python.pointsto.PointsTo import semmle.python.objects.ObjectInternal ClassList mro(ClassObjectInternal cls) { - if Types::isNewStyle(cls) then result = Mro::newStyleMro(cls) else result = Mro::oldStyleMro(cls) + if Types::isNewStyle(cls) then result = Mro::newStyleMro(cls) else result = Mro::oldStyleMro(cls) } from ClassObjectInternal cls diff --git a/python/ql/test/2/library-tests/classes/mro/mro.ql b/python/ql/test/2/library-tests/classes/mro/mro.ql index 122d31c4a9b..0c4cf077adb 100644 --- a/python/ql/test/2/library-tests/classes/mro/mro.ql +++ b/python/ql/test/2/library-tests/classes/mro/mro.ql @@ -2,6 +2,6 @@ import python from ClassObject cls, ClassObject l, ClassObject r where - not cls.isC() and - r = cls.nextInMro(l) + not cls.isC() and + r = cls.nextInMro(l) select cls.toString(), l.toString(), r.toString() diff --git a/python/ql/test/2/library-tests/comprehensions/ConsistencyCheck.ql b/python/ql/test/2/library-tests/comprehensions/ConsistencyCheck.ql index 475505620f4..2f5191fb547 100644 --- a/python/ql/test/2/library-tests/comprehensions/ConsistencyCheck.ql +++ b/python/ql/test/2/library-tests/comprehensions/ConsistencyCheck.ql @@ -5,5 +5,5 @@ import python select count(Comprehension c | - count(c.toString()) != 1 or count(c.getLocation()) != 1 or not exists(c.getAFlowNode()) - ) + count(c.toString()) != 1 or count(c.getLocation()) != 1 or not exists(c.getAFlowNode()) + ) diff --git a/python/ql/test/2/library-tests/locations/general/AllLocations.ql b/python/ql/test/2/library-tests/locations/general/AllLocations.ql index 9e6fcb00a05..e3a84325418 100644 --- a/python/ql/test/2/library-tests/locations/general/AllLocations.ql +++ b/python/ql/test/2/library-tests/locations/general/AllLocations.ql @@ -9,7 +9,7 @@ import python from string classname where - exists(AstNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) - or - exists(ControlFlowNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) + exists(AstNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) + or + exists(ControlFlowNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) select classname diff --git a/python/ql/test/2/library-tests/modules/general/import_test.ql b/python/ql/test/2/library-tests/modules/general/import_test.ql index 94f8c1447ca..cbd8cafb2f7 100644 --- a/python/ql/test/2/library-tests/modules/general/import_test.ql +++ b/python/ql/test/2/library-tests/modules/general/import_test.ql @@ -2,8 +2,8 @@ import python from ImportExpr ie, string m, string t, string r where - m = ie.getImportedModuleName() and - (if ie.isTop() then t = "top" else t = "bottom") and - (if ie.isRelative() then r = "relative" else r = "absolute") + m = ie.getImportedModuleName() and + (if ie.isTop() then t = "top" else t = "bottom") and + (if ie.isRelative() then r = "relative" else r = "absolute") select ie.getScope().toString(), ie.getLocation().getStartLine(), ie.toString(), ie.getLevel(), t, - r, m + r, m diff --git a/python/ql/test/2/library-tests/objects/Literals.ql b/python/ql/test/2/library-tests/objects/Literals.ql index ad6e1181cfd..a7f10b358ff 100644 --- a/python/ql/test/2/library-tests/objects/Literals.ql +++ b/python/ql/test/2/library-tests/objects/Literals.ql @@ -2,9 +2,9 @@ import python string repr(Expr e) { - result = e.(Num).getN() or - result = e.(Bytes).getS() or - result = e.(Unicode).getS() + result = e.(Num).getN() or + result = e.(Bytes).getS() or + result = e.(Unicode).getS() } from ImmutableLiteral l diff --git a/python/ql/test/2/library-tests/six/pointsto.ql b/python/ql/test/2/library-tests/six/pointsto.ql index d44761b1b12..cca7eeede10 100644 --- a/python/ql/test/2/library-tests/six/pointsto.ql +++ b/python/ql/test/2/library-tests/six/pointsto.ql @@ -1,9 +1,9 @@ import python string longname(Expr e) { - result = e.(Name).getId() - or - exists(Attribute a | a = e | result = longname(a.getObject()) + "." + a.getName()) + result = e.(Name).getId() + or + exists(Attribute a | a = e | result = longname(a.getObject()) + "." + a.getName()) } from Expr e, Value v diff --git a/python/ql/test/2/library-tests/types/classes/new_style.ql b/python/ql/test/2/library-tests/types/classes/new_style.ql index a0cd38b9e62..2af40565329 100644 --- a/python/ql/test/2/library-tests/types/classes/new_style.ql +++ b/python/ql/test/2/library-tests/types/classes/new_style.ql @@ -2,7 +2,7 @@ import python from ClassObject cls, string style where - not cls.isC() and - not cls.failedInference() and - (if cls.isNewStyle() then style = "new" else style = "old") + not cls.isC() and + not cls.failedInference() and + (if cls.isNewStyle() then style = "new" else style = "old") select cls.toString(), style diff --git a/python/ql/test/2/library-tests/types/exceptions/Raises.ql b/python/ql/test/2/library-tests/types/exceptions/Raises.ql index aa477f718a2..2415c707967 100644 --- a/python/ql/test/2/library-tests/types/exceptions/Raises.ql +++ b/python/ql/test/2/library-tests/types/exceptions/Raises.ql @@ -2,11 +2,11 @@ import python from PyFunctionObject f, string type where - type = f.getARaisedType().toString() - or - type = "Unknown" and f.raisesUnknownType() - or - not exists(f.getARaisedType()) and - not f.raisesUnknownType() and - type = "None" + type = f.getARaisedType().toString() + or + type = "Unknown" and f.raisesUnknownType() + or + not exists(f.getARaisedType()) and + not f.raisesUnknownType() and + type = "None" select f.toString(), type diff --git a/python/ql/test/2/library-tests/types/properties/BuiltinProperties.ql b/python/ql/test/2/library-tests/types/properties/BuiltinProperties.ql index 6ff0563e787..24766db9f2e 100644 --- a/python/ql/test/2/library-tests/types/properties/BuiltinProperties.ql +++ b/python/ql/test/2/library-tests/types/properties/BuiltinProperties.ql @@ -2,7 +2,7 @@ import python from ClassObject cls, string name, BuiltinPropertyObject p where - cls.declaredAttribute(name) = p and - (cls = theObjectType() or cls = theListType() or cls = theTypeType()) + cls.declaredAttribute(name) = p and + (cls = theObjectType() or cls = theListType() or cls = theTypeType()) select cls.toString(), name, p.toString(), p.getGetter().toString(), p.getSetter().toString(), - p.getDeleter().toString() + p.getDeleter().toString() diff --git a/python/ql/test/3/library-tests/ControlFlow/Exceptions/Likely.ql b/python/ql/test/3/library-tests/ControlFlow/Exceptions/Likely.ql index 80831a9ca54..f8e4a4b7dac 100644 --- a/python/ql/test/3/library-tests/ControlFlow/Exceptions/Likely.ql +++ b/python/ql/test/3/library-tests/ControlFlow/Exceptions/Likely.ql @@ -2,6 +2,6 @@ import python from ControlFlowNode r, ControlFlowNode s where - s = r.getAnExceptionalSuccessor() and - not r.(RaisingNode).unlikelySuccessor(s) + s = r.getAnExceptionalSuccessor() and + not r.(RaisingNode).unlikelySuccessor(s) select r.getLocation().getStartLine(), r.toString(), s.getLocation().getStartLine(), s.toString() diff --git a/python/ql/test/3/library-tests/PointsTo/attributes/TestWithType.ql b/python/ql/test/3/library-tests/PointsTo/attributes/TestWithType.ql index 0c6149c38b1..2b4b8a8c70c 100644 --- a/python/ql/test/3/library-tests/PointsTo/attributes/TestWithType.ql +++ b/python/ql/test/3/library-tests/PointsTo/attributes/TestWithType.ql @@ -3,4 +3,4 @@ import python from ControlFlowNode f, Object o, ClassObject c, ControlFlowNode x where f.refersTo(o, c, x) select f.getLocation().getStartLine(), f.toString(), o.toString(), c.toString(), - x.getLocation().getStartLine() + x.getLocation().getStartLine() diff --git a/python/ql/test/3/library-tests/PointsTo/class_properties/ClassValues.ql b/python/ql/test/3/library-tests/PointsTo/class_properties/ClassValues.ql index 8594fc33ae2..3281b8d26e6 100644 --- a/python/ql/test/3/library-tests/PointsTo/class_properties/ClassValues.ql +++ b/python/ql/test/3/library-tests/PointsTo/class_properties/ClassValues.ql @@ -2,19 +2,19 @@ import python from ClassValue cls, string res where - exists(CallNode call | - call.getFunction().(NameNode).getId() = "test" and - call.getAnArg().pointsTo(cls) - ) and - ( - cls.isSequence() and - cls.isMapping() and - res = "IS BOTH. SHOULD NOT HAPPEN. THEY ARE MUTUALLY EXCLUSIVE." - or - cls.isSequence() and not cls.isMapping() and res = "sequence" - or - not cls.isSequence() and cls.isMapping() and res = "mapping" - or - not cls.isSequence() and not cls.isMapping() and res = "neither sequence nor mapping" - ) + exists(CallNode call | + call.getFunction().(NameNode).getId() = "test" and + call.getAnArg().pointsTo(cls) + ) and + ( + cls.isSequence() and + cls.isMapping() and + res = "IS BOTH. SHOULD NOT HAPPEN. THEY ARE MUTUALLY EXCLUSIVE." + or + cls.isSequence() and not cls.isMapping() and res = "sequence" + or + not cls.isSequence() and cls.isMapping() and res = "mapping" + or + not cls.isSequence() and not cls.isMapping() and res = "neither sequence nor mapping" + ) select res, cls.toString() diff --git a/python/ql/test/3/library-tests/PointsTo/consts/BooleanConstants.ql b/python/ql/test/3/library-tests/PointsTo/consts/BooleanConstants.ql index 6215714a25e..4299e11d660 100644 --- a/python/ql/test/3/library-tests/PointsTo/consts/BooleanConstants.ql +++ b/python/ql/test/3/library-tests/PointsTo/consts/BooleanConstants.ql @@ -3,7 +3,7 @@ import semmle.python.pointsto.PointsTo from ControlFlowNode f, Context c, boolean b where - exists(Object obj | PointsTo::points_to(f, c, obj, _, _) and obj.booleanValue() = b) and - not exists(Object obj | PointsTo::points_to(f, c, obj, _, _) and not obj.booleanValue() = b) + exists(Object obj | PointsTo::points_to(f, c, obj, _, _) and obj.booleanValue() = b) and + not exists(Object obj | PointsTo::points_to(f, c, obj, _, _) and not obj.booleanValue() = b) select f.getLocation().getFile().getShortName(), f.getLocation().getStartLine(), f.toString(), - c.toString(), b + c.toString(), b diff --git a/python/ql/test/3/library-tests/PointsTo/import_time/Pruned.ql b/python/ql/test/3/library-tests/PointsTo/import_time/Pruned.ql index d07dc65c34f..de3b4a282c2 100644 --- a/python/ql/test/3/library-tests/PointsTo/import_time/Pruned.ql +++ b/python/ql/test/3/library-tests/PointsTo/import_time/Pruned.ql @@ -3,7 +3,7 @@ import semmle.python.pointsto.PointsTo from ControlFlowNode f, Location l where - not PointsToInternal::reachableBlock(f.getBasicBlock(), _) and - l = f.getLocation() and - l.getFile().getShortName() = "test.py" + not PointsToInternal::reachableBlock(f.getBasicBlock(), _) and + l = f.getLocation() and + l.getFile().getShortName() = "test.py" select l.getStartLine() diff --git a/python/ql/test/3/library-tests/PointsTo/regressions/subprocess-assert/ClassValue.ql b/python/ql/test/3/library-tests/PointsTo/regressions/subprocess-assert/ClassValue.ql index e21a864b8bb..bc666b4f206 100644 --- a/python/ql/test/3/library-tests/PointsTo/regressions/subprocess-assert/ClassValue.ql +++ b/python/ql/test/3/library-tests/PointsTo/regressions/subprocess-assert/ClassValue.ql @@ -3,9 +3,9 @@ import python // as used in semmle.python.filters.Tests from ClassValue c, string base where - c.getScope().getLocation().getFile().getShortName().matches("mwe%.py") and - c.getName() = "MyTest" and - if exists(c.getABaseType()) - then base = c.getABaseType().toString() - else base = "" + c.getScope().getLocation().getFile().getShortName().matches("mwe%.py") and + c.getName() = "MyTest" and + if exists(c.getABaseType()) + then base = c.getABaseType().toString() + else base = "" select c, base diff --git a/python/ql/test/3/library-tests/PointsTo/typehints/Values.ql b/python/ql/test/3/library-tests/PointsTo/typehints/Values.ql index 8716d38f086..192468a2248 100644 --- a/python/ql/test/3/library-tests/PointsTo/typehints/Values.ql +++ b/python/ql/test/3/library-tests/PointsTo/typehints/Values.ql @@ -2,6 +2,6 @@ import python from ControlFlowNode f, Context ctx, Value v, ControlFlowNode origin where - f.pointsTo(ctx, v, origin) and - f.getLocation().getFile().getBaseName() = "test.py" + f.pointsTo(ctx, v, origin) and + f.getLocation().getFile().getBaseName() = "test.py" select f.getLocation(), f.toString(), ctx, v diff --git a/python/ql/test/3/library-tests/classes/attr/class_attr.ql b/python/ql/test/3/library-tests/classes/attr/class_attr.ql index 3b7bf8b3ba0..197ab1a1e5e 100644 --- a/python/ql/test/3/library-tests/classes/attr/class_attr.ql +++ b/python/ql/test/3/library-tests/classes/attr/class_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name, Object obj where - cls.hasLocationInfo(_, line, _, _, _) and - obj = cls.lookupAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + obj = cls.lookupAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name, obj.toString() diff --git a/python/ql/test/3/library-tests/classes/attr/class_has_attr.ql b/python/ql/test/3/library-tests/classes/attr/class_has_attr.ql index 2f16aa4ca97..be8272d1bd6 100644 --- a/python/ql/test/3/library-tests/classes/attr/class_has_attr.ql +++ b/python/ql/test/3/library-tests/classes/attr/class_has_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name where - cls.hasLocationInfo(_, line, _, _, _) and - cls.hasAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + cls.hasAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name diff --git a/python/ql/test/3/library-tests/classes/mro/mro.ql b/python/ql/test/3/library-tests/classes/mro/mro.ql index 2c710a18eeb..576d6a75afd 100644 --- a/python/ql/test/3/library-tests/classes/mro/mro.ql +++ b/python/ql/test/3/library-tests/classes/mro/mro.ql @@ -8,6 +8,6 @@ import python from ClassObject cls, ClassObject l, ClassObject r where - not cls.isC() and - r = cls.nextInMro(l) + not cls.isC() and + r = cls.nextInMro(l) select cls.toString(), l.toString(), r.toString() diff --git a/python/ql/test/3/library-tests/classes/mro/mro_index.ql b/python/ql/test/3/library-tests/classes/mro/mro_index.ql index 641667e28f1..da40776044e 100644 --- a/python/ql/test/3/library-tests/classes/mro/mro_index.ql +++ b/python/ql/test/3/library-tests/classes/mro/mro_index.ql @@ -8,6 +8,6 @@ import python from ClassObject cls, ClassObject sup, int index where - sup = cls.getMroItem(index) and - not cls.isC() + sup = cls.getMroItem(index) and + not cls.isC() select cls.toString(), index, sup.toString() diff --git a/python/ql/test/3/library-tests/locations/general/AllLocations.ql b/python/ql/test/3/library-tests/locations/general/AllLocations.ql index 9e6fcb00a05..e3a84325418 100644 --- a/python/ql/test/3/library-tests/locations/general/AllLocations.ql +++ b/python/ql/test/3/library-tests/locations/general/AllLocations.ql @@ -9,7 +9,7 @@ import python from string classname where - exists(AstNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) - or - exists(ControlFlowNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) + exists(AstNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) + or + exists(ControlFlowNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) select classname diff --git a/python/ql/test/3/library-tests/modules/general/import_test.ql b/python/ql/test/3/library-tests/modules/general/import_test.ql index 94f8c1447ca..cbd8cafb2f7 100644 --- a/python/ql/test/3/library-tests/modules/general/import_test.ql +++ b/python/ql/test/3/library-tests/modules/general/import_test.ql @@ -2,8 +2,8 @@ import python from ImportExpr ie, string m, string t, string r where - m = ie.getImportedModuleName() and - (if ie.isTop() then t = "top" else t = "bottom") and - (if ie.isRelative() then r = "relative" else r = "absolute") + m = ie.getImportedModuleName() and + (if ie.isTop() then t = "top" else t = "bottom") and + (if ie.isRelative() then r = "relative" else r = "absolute") select ie.getScope().toString(), ie.getLocation().getStartLine(), ie.toString(), ie.getLevel(), t, - r, m + r, m diff --git a/python/ql/test/3/library-tests/parameters/Special.ql b/python/ql/test/3/library-tests/parameters/Special.ql index 4987599bc72..e26e0797ff6 100644 --- a/python/ql/test/3/library-tests/parameters/Special.ql +++ b/python/ql/test/3/library-tests/parameters/Special.ql @@ -2,9 +2,9 @@ import python from Parameter p, string type where - p.isKwargs() and type = "kwargs" - or - p.isVarargs() and type = "varargs" - or - not p.isKwargs() and not p.isVarargs() and type = "normal" + p.isKwargs() and type = "kwargs" + or + p.isVarargs() and type = "varargs" + or + not p.isKwargs() and not p.isVarargs() and type = "normal" select p.getName(), type diff --git a/python/ql/test/3/library-tests/six/pointsto.ql b/python/ql/test/3/library-tests/six/pointsto.ql index d44761b1b12..cca7eeede10 100644 --- a/python/ql/test/3/library-tests/six/pointsto.ql +++ b/python/ql/test/3/library-tests/six/pointsto.ql @@ -1,9 +1,9 @@ import python string longname(Expr e) { - result = e.(Name).getId() - or - exists(Attribute a | a = e | result = longname(a.getObject()) + "." + a.getName()) + result = e.(Name).getId() + or + exists(Attribute a | a = e | result = longname(a.getObject()) + "." + a.getName()) } from Expr e, Value v diff --git a/python/ql/test/3/library-tests/taint/unpacking/Taint.qll b/python/ql/test/3/library-tests/taint/unpacking/Taint.qll index 21e16aabac5..010b9738c5c 100644 --- a/python/ql/test/3/library-tests/taint/unpacking/Taint.qll +++ b/python/ql/test/3/library-tests/taint/unpacking/Taint.qll @@ -3,25 +3,25 @@ import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } + SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "taint source" } + override string toString() { result = "taint source" } } class ListSource extends TaintSource { - ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } + ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } - override string toString() { result = "list taint source" } + override string toString() { result = "list taint source" } } class DictSource extends TaintSource { - DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } + DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - override string toString() { result = "dict taint source" } + override string toString() { result = "dict taint source" } } diff --git a/python/ql/test/3/library-tests/taint/unpacking/TestTaint.ql b/python/ql/test/3/library-tests/taint/unpacking/TestTaint.ql index fb1d102aa7a..47883578516 100644 --- a/python/ql/test/3/library-tests/taint/unpacking/TestTaint.ql +++ b/python/ql/test/3/library-tests/taint/unpacking/TestTaint.ql @@ -4,16 +4,16 @@ import Taint from Call call, Expr arg, string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - call.getFunc().(Name).getId() = "test" and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "NO TAINT" - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) + call.getLocation().getFile().getShortName() = "test.py" and + call.getFunc().(Name).getId() = "test" and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "NO TAINT" + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) + ) select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/3/library-tests/types/exceptions/Raises.ql b/python/ql/test/3/library-tests/types/exceptions/Raises.ql index aa477f718a2..2415c707967 100644 --- a/python/ql/test/3/library-tests/types/exceptions/Raises.ql +++ b/python/ql/test/3/library-tests/types/exceptions/Raises.ql @@ -2,11 +2,11 @@ import python from PyFunctionObject f, string type where - type = f.getARaisedType().toString() - or - type = "Unknown" and f.raisesUnknownType() - or - not exists(f.getARaisedType()) and - not f.raisesUnknownType() and - type = "None" + type = f.getARaisedType().toString() + or + type = "Unknown" and f.raisesUnknownType() + or + not exists(f.getARaisedType()) and + not f.raisesUnknownType() and + type = "None" select f.toString(), type diff --git a/python/ql/test/3/library-tests/types/exceptions/Viable.ql b/python/ql/test/3/library-tests/types/exceptions/Viable.ql index ed388e2faf2..bf00a0675d6 100644 --- a/python/ql/test/3/library-tests/types/exceptions/Viable.ql +++ b/python/ql/test/3/library-tests/types/exceptions/Viable.ql @@ -3,4 +3,4 @@ import python from RaisingNode r, ControlFlowNode n, ClassObject ex where r.viableExceptionEdge_objectapi(n, ex) select r.getLocation().getStartLine(), n.getLocation().getStartLine(), r.getNode().toString(), - n.getNode().toString(), ex.toString() + n.getNode().toString(), ex.toString() diff --git a/python/ql/test/3/library-tests/types/namespaces/NameSpace.ql b/python/ql/test/3/library-tests/types/namespaces/NameSpace.ql index 4a35fae7e8b..38241e8fabc 100644 --- a/python/ql/test/3/library-tests/types/namespaces/NameSpace.ql +++ b/python/ql/test/3/library-tests/types/namespaces/NameSpace.ql @@ -9,16 +9,16 @@ import python from Scope s, string name, Object val where - name != "__name__" and - ( - exists(ModuleObject m | - m.getModule() = s and - m.attributeRefersTo(name, val, _) - ) - or - exists(ClassObject cls | - cls.getPyClass() = s and - cls.declaredAttribute(name) = val - ) + name != "__name__" and + ( + exists(ModuleObject m | + m.getModule() = s and + m.attributeRefersTo(name, val, _) ) + or + exists(ClassObject cls | + cls.getPyClass() = s and + cls.declaredAttribute(name) = val + ) + ) select s.toString(), name, val.toString() diff --git a/python/ql/test/3/library-tests/types/properties/BuiltinProperties.ql b/python/ql/test/3/library-tests/types/properties/BuiltinProperties.ql index 6ff0563e787..24766db9f2e 100644 --- a/python/ql/test/3/library-tests/types/properties/BuiltinProperties.ql +++ b/python/ql/test/3/library-tests/types/properties/BuiltinProperties.ql @@ -2,7 +2,7 @@ import python from ClassObject cls, string name, BuiltinPropertyObject p where - cls.declaredAttribute(name) = p and - (cls = theObjectType() or cls = theListType() or cls = theTypeType()) + cls.declaredAttribute(name) = p and + (cls = theObjectType() or cls = theListType() or cls = theTypeType()) select cls.toString(), name, p.toString(), p.getGetter().toString(), p.getSetter().toString(), - p.getDeleter().toString() + p.getDeleter().toString() diff --git a/python/ql/test/experimental/dataflow/basic/callGraph.ql b/python/ql/test/experimental/dataflow/basic/callGraph.ql index 0d0a0279891..53747b31739 100644 --- a/python/ql/test/experimental/dataflow/basic/callGraph.ql +++ b/python/ql/test/experimental/dataflow/basic/callGraph.ql @@ -1,9 +1,5 @@ import callGraphConfig -from - DataFlow::Node source, - DataFlow::Node sink -where - exists(CallGraphConfig cfg | cfg.hasFlow(source, sink)) -select - source, sink +from DataFlow::Node source, DataFlow::Node sink +where exists(CallGraphConfig cfg | cfg.hasFlow(source, sink)) +select source, sink diff --git a/python/ql/test/experimental/dataflow/basic/callGraphConfig.qll b/python/ql/test/experimental/dataflow/basic/callGraphConfig.qll index 9866ac1cdbe..241b7b9478c 100644 --- a/python/ql/test/experimental/dataflow/basic/callGraphConfig.qll +++ b/python/ql/test/experimental/dataflow/basic/callGraphConfig.qll @@ -1,7 +1,7 @@ import experimental.dataflow.DataFlow /** - * A configuration to find the call graph edges. + * A configuration to find the call graph edges. */ class CallGraphConfig extends DataFlow::Configuration { CallGraphConfig() { this = "CallGraphConfig" } diff --git a/python/ql/test/experimental/dataflow/basic/callGraphSinks.ql b/python/ql/test/experimental/dataflow/basic/callGraphSinks.ql index ef635f9afa6..020ea245cfd 100644 --- a/python/ql/test/experimental/dataflow/basic/callGraphSinks.ql +++ b/python/ql/test/experimental/dataflow/basic/callGraphSinks.ql @@ -2,4 +2,4 @@ import callGraphConfig from DataFlow::Node sink where exists(CallGraphConfig cfg | cfg.isSink(sink)) -select sink \ No newline at end of file +select sink diff --git a/python/ql/test/experimental/dataflow/basic/callGraphSources.ql b/python/ql/test/experimental/dataflow/basic/callGraphSources.ql index de58e5a2269..a6bd5538866 100644 --- a/python/ql/test/experimental/dataflow/basic/callGraphSources.ql +++ b/python/ql/test/experimental/dataflow/basic/callGraphSources.ql @@ -2,4 +2,4 @@ import callGraphConfig from DataFlow::Node source where exists(CallGraphConfig cfg | cfg.isSource(source)) -select source \ No newline at end of file +select source diff --git a/python/ql/test/experimental/dataflow/basic/global.ql b/python/ql/test/experimental/dataflow/basic/global.ql index 6f57575819a..ba9a302b05b 100644 --- a/python/ql/test/experimental/dataflow/basic/global.ql +++ b/python/ql/test/experimental/dataflow/basic/global.ql @@ -1,10 +1,7 @@ import allFlowsConfig -from - DataFlow::Node source, - DataFlow::Node sink +from DataFlow::Node source, DataFlow::Node sink where - source != sink and + source != sink and exists(AllFlowsConfig cfg | cfg.hasFlow(source, sink)) -select - source, sink +select source, sink diff --git a/python/ql/test/experimental/dataflow/basic/globalStep.ql b/python/ql/test/experimental/dataflow/basic/globalStep.ql index f583a9f1a81..18014b2cc5f 100644 --- a/python/ql/test/experimental/dataflow/basic/globalStep.ql +++ b/python/ql/test/experimental/dataflow/basic/globalStep.ql @@ -1,9 +1,5 @@ import allFlowsConfig -from - DataFlow::PathNode fromNode, - DataFlow::PathNode toNode -where - toNode = fromNode.getASuccessor() -select - fromNode, toNode +from DataFlow::PathNode fromNode, DataFlow::PathNode toNode +where toNode = fromNode.getASuccessor() +select fromNode, toNode diff --git a/python/ql/test/experimental/dataflow/basic/local.ql b/python/ql/test/experimental/dataflow/basic/local.ql index 40aa5c403e1..a4f7519483f 100644 --- a/python/ql/test/experimental/dataflow/basic/local.ql +++ b/python/ql/test/experimental/dataflow/basic/local.ql @@ -1,9 +1,5 @@ import experimental.dataflow.DataFlow -from - DataFlow::Node fromNode, - DataFlow::Node toNode -where - DataFlow::localFlow(fromNode, toNode) -select - fromNode, toNode +from DataFlow::Node fromNode, DataFlow::Node toNode +where DataFlow::localFlow(fromNode, toNode) +select fromNode, toNode diff --git a/python/ql/test/experimental/dataflow/basic/localStep.ql b/python/ql/test/experimental/dataflow/basic/localStep.ql index b8a9941b99b..ee57b07f0c7 100644 --- a/python/ql/test/experimental/dataflow/basic/localStep.ql +++ b/python/ql/test/experimental/dataflow/basic/localStep.ql @@ -1,9 +1,5 @@ import experimental.dataflow.DataFlow -from - DataFlow::Node fromNode, - DataFlow::Node toNode -where - DataFlow::localFlowStep(fromNode, toNode) -select - fromNode, toNode +from DataFlow::Node fromNode, DataFlow::Node toNode +where DataFlow::localFlowStep(fromNode, toNode) +select fromNode, toNode diff --git a/python/ql/test/experimental/dataflow/basic/maximalFlows.ql b/python/ql/test/experimental/dataflow/basic/maximalFlows.ql index 6e183dc393b..ddd673954b9 100644 --- a/python/ql/test/experimental/dataflow/basic/maximalFlows.ql +++ b/python/ql/test/experimental/dataflow/basic/maximalFlows.ql @@ -1,10 +1,7 @@ import maximalFlowsConfig -from - DataFlow::Node source, - DataFlow::Node sink +from DataFlow::Node source, DataFlow::Node sink where - source != sink and + source != sink and exists(MaximalFlowsConfig cfg | cfg.hasFlow(source, sink)) -select - source, sink +select source, sink diff --git a/python/ql/test/experimental/dataflow/basic/maximalFlowsConfig.qll b/python/ql/test/experimental/dataflow/basic/maximalFlowsConfig.qll index 9db8ec67e94..de2d22d7d52 100644 --- a/python/ql/test/experimental/dataflow/basic/maximalFlowsConfig.qll +++ b/python/ql/test/experimental/dataflow/basic/maximalFlowsConfig.qll @@ -11,9 +11,7 @@ class MaximalFlowsConfig extends DataFlow::Configuration { node instanceof DataFlow::ParameterNode or node instanceof DataFlow::EssaNode and - not exists(DataFlow::EssaNode pred | - DataFlow::localFlowStep(pred, node) - ) + not exists(DataFlow::EssaNode pred | DataFlow::localFlowStep(pred, node)) } override predicate isSink(DataFlow::Node node) { diff --git a/python/ql/test/experimental/dataflow/basic/sinks.ql b/python/ql/test/experimental/dataflow/basic/sinks.ql index 9b4534b9870..8560bb99d3d 100644 --- a/python/ql/test/experimental/dataflow/basic/sinks.ql +++ b/python/ql/test/experimental/dataflow/basic/sinks.ql @@ -2,4 +2,4 @@ import allFlowsConfig from DataFlow::Node sink where exists(AllFlowsConfig cfg | cfg.isSink(sink)) -select sink \ No newline at end of file +select sink diff --git a/python/ql/test/experimental/dataflow/basic/sources.ql b/python/ql/test/experimental/dataflow/basic/sources.ql index f47fa31d62e..d079d4db596 100644 --- a/python/ql/test/experimental/dataflow/basic/sources.ql +++ b/python/ql/test/experimental/dataflow/basic/sources.ql @@ -2,4 +2,4 @@ import allFlowsConfig from DataFlow::Node source where exists(AllFlowsConfig cfg | cfg.isSource(source)) -select source \ No newline at end of file +select source diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.ql b/python/ql/test/experimental/dataflow/coverage/dataflow.ql index 2d6e71ea679..65a13d57233 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.ql +++ b/python/ql/test/experimental/dataflow/coverage/dataflow.ql @@ -1,9 +1,5 @@ import experimental.dataflow.testConfig -from - DataFlow::Node source, - DataFlow::Node sink -where - exists(TestConfiguration cfg | cfg.hasFlow(source, sink)) -select - source, sink +from DataFlow::Node source, DataFlow::Node sink +where exists(TestConfiguration cfg | cfg.hasFlow(source, sink)) +select source, sink diff --git a/python/ql/test/experimental/dataflow/regression/dataflow.ql b/python/ql/test/experimental/dataflow/regression/dataflow.ql index 066a42b2c1c..a5a933bdc71 100644 --- a/python/ql/test/experimental/dataflow/regression/dataflow.ql +++ b/python/ql/test/experimental/dataflow/regression/dataflow.ql @@ -7,10 +7,6 @@ import experimental.dataflow.testConfig -from - DataFlow::Node source, - DataFlow::Node sink -where - exists(TestConfiguration cfg | cfg.hasFlow(source, sink)) -select - source, sink +from DataFlow::Node source, DataFlow::Node sink +where exists(TestConfiguration cfg | cfg.hasFlow(source, sink)) +select source, sink diff --git a/python/ql/test/experimental/dataflow/testConfig.qll b/python/ql/test/experimental/dataflow/testConfig.qll index 8d89991d8c2..178f2f4f229 100644 --- a/python/ql/test/experimental/dataflow/testConfig.qll +++ b/python/ql/test/experimental/dataflow/testConfig.qll @@ -9,9 +9,9 @@ * SINK(s) * ``` * `SOURCE` will be a source and the second occurance of `s` will be a sink. - * + * * In order to test literals, alternative sources are defined for each type: - * + * * for | use * ---------- * string | `"source"` @@ -25,7 +25,7 @@ import experimental.dataflow.DataFlow class TestConfiguration extends DataFlow::Configuration { TestConfiguration() { this = "TestConfiguration" } - override predicate isSource(DataFlow::Node node) { + override predicate isSource(DataFlow::Node node) { node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "SOURCE" or node.(DataFlow::CfgNode).getNode().getNode().(StrConst).getS() = "source" @@ -36,7 +36,7 @@ class TestConfiguration extends DataFlow::Configuration { // No support for complex numbers } - override predicate isSink(DataFlow::Node node) { + override predicate isSink(DataFlow::Node node) { exists(CallNode call | call.getFunction().(NameNode).getId() in ["SINK", "SINK_F"] and node.(DataFlow::CfgNode).getNode() = call.getAnArg() diff --git a/python/ql/test/library-tests/ControlFlow/PointsToSupport/UseFromDefinition.ql b/python/ql/test/library-tests/ControlFlow/PointsToSupport/UseFromDefinition.ql index 54e7ed36333..8b52244478f 100644 --- a/python/ql/test/library-tests/ControlFlow/PointsToSupport/UseFromDefinition.ql +++ b/python/ql/test/library-tests/ControlFlow/PointsToSupport/UseFromDefinition.ql @@ -2,14 +2,14 @@ import python /*Find any Definition, assigned value pairs that 'valueForDefinition' misses */ Expr assignedValue(Name n) { - exists(Assign a | a.getATarget() = n and result = a.getValue()) - or - exists(Alias a | a.getAsname() = n and result = a.getValue()) + exists(Assign a | a.getATarget() = n and result = a.getValue()) + or + exists(Alias a | a.getAsname() = n and result = a.getValue()) } from Name def, DefinitionNode d where - d = def.getAFlowNode() and - exists(assignedValue(def)) and - not d.getValue().getNode() = assignedValue(def) + d = def.getAFlowNode() and + exists(assignedValue(def)) and + not d.getValue().getNode() = assignedValue(def) select def.toString(), assignedValue(def) diff --git a/python/ql/test/library-tests/ControlFlow/augassign/AugAssignFlow.ql b/python/ql/test/library-tests/ControlFlow/augassign/AugAssignFlow.ql index a4b98183c27..ce4f454ab3b 100644 --- a/python/ql/test/library-tests/ControlFlow/augassign/AugAssignFlow.ql +++ b/python/ql/test/library-tests/ControlFlow/augassign/AugAssignFlow.ql @@ -4,7 +4,7 @@ int lineof(ControlFlowNode f) { result = f.getNode().getLocation().getStartLine( from ControlFlowNode defn, ControlFlowNode use where - defn.getNode() = use.getNode() and - defn.isStore() and - use.isLoad() + defn.getNode() = use.getNode() and + defn.isStore() and + use.isLoad() select defn.toString(), use.toString(), lineof(defn) diff --git a/python/ql/test/library-tests/ControlFlow/augassign/Kind.ql b/python/ql/test/library-tests/ControlFlow/augassign/Kind.ql index c97f8446345..979a2395941 100644 --- a/python/ql/test/library-tests/ControlFlow/augassign/Kind.ql +++ b/python/ql/test/library-tests/ControlFlow/augassign/Kind.ql @@ -1,15 +1,15 @@ import python string kind(ControlFlowNode f) { - if f.isAugLoad() - then result = "aug load" + if f.isAugLoad() + then result = "aug load" + else ( + if f.isAugStore() + then result = "aug store" else ( - if f.isAugStore() - then result = "aug store" - else ( - if f.isLoad() then result = "load" else (f.isStore() and result = "store") - ) + if f.isLoad() then result = "load" else (f.isStore() and result = "store") ) + ) } from ControlFlowNode cfg diff --git a/python/ql/test/library-tests/ControlFlow/comparison/Compare.ql b/python/ql/test/library-tests/ControlFlow/comparison/Compare.ql index 13e4736e6d9..a14e31c420a 100644 --- a/python/ql/test/library-tests/ControlFlow/comparison/Compare.ql +++ b/python/ql/test/library-tests/ControlFlow/comparison/Compare.ql @@ -9,10 +9,10 @@ import python from CompareNode c, NameNode l, NameNode r, Cmpop op, int line, Variable vl, Variable vr where - c.operands(l, op, r) and - line = c.getLocation().getStartLine() and - line = l.getLocation().getStartLine() and - line = r.getLocation().getStartLine() and - l.uses(vl) and - r.uses(vr) + c.operands(l, op, r) and + line = c.getLocation().getStartLine() and + line = l.getLocation().getStartLine() and + line = r.getLocation().getStartLine() and + l.uses(vl) and + r.uses(vr) select line, c.toString(), vl.getId(), vr.getId(), op.getSymbol() diff --git a/python/ql/test/library-tests/ControlFlow/delete/test.ql b/python/ql/test/library-tests/ControlFlow/delete/test.ql index 2aaa45ea719..ba6ea81fb40 100644 --- a/python/ql/test/library-tests/ControlFlow/delete/test.ql +++ b/python/ql/test/library-tests/ControlFlow/delete/test.ql @@ -3,4 +3,4 @@ import python from ControlFlowNode p, ControlFlowNode s where p.getASuccessor() = s select p.getLocation().getStartLine().toString(), p.toString(), s.getLocation().getStartLine(), - s.toString() + s.toString() diff --git a/python/ql/test/library-tests/ControlFlow/dominators/DominatesConsistency.ql b/python/ql/test/library-tests/ControlFlow/dominators/DominatesConsistency.ql index d39328e44c7..680d27e5cd1 100644 --- a/python/ql/test/library-tests/ControlFlow/dominators/DominatesConsistency.ql +++ b/python/ql/test/library-tests/ControlFlow/dominators/DominatesConsistency.ql @@ -1,8 +1,8 @@ import python select count(BasicBlock b1, BasicBlock b2 | - b1 = b2.getImmediateDominator+() and not b1.strictlyDominates(b2) - ), - count(BasicBlock b1, BasicBlock b2 | - not b1 = b2.getImmediateDominator+() and b1.strictlyDominates(b2) - ) + b1 = b2.getImmediateDominator+() and not b1.strictlyDominates(b2) + ), + count(BasicBlock b1, BasicBlock b2 | + not b1 = b2.getImmediateDominator+() and b1.strictlyDominates(b2) + ) diff --git a/python/ql/test/library-tests/ControlFlow/dominators/idom.ql b/python/ql/test/library-tests/ControlFlow/dominators/idom.ql index cd948b6ff10..44c7da924aa 100644 --- a/python/ql/test/library-tests/ControlFlow/dominators/idom.ql +++ b/python/ql/test/library-tests/ControlFlow/dominators/idom.ql @@ -10,6 +10,6 @@ import python /* This query should *never* produce a result */ from ControlFlowNode f where - not exists(f.getImmediateDominator()) and - not f.getNode() instanceof Scope + not exists(f.getImmediateDominator()) and + not f.getNode() instanceof Scope select f diff --git a/python/ql/test/library-tests/ControlFlow/general/ImmediateDominatorCheck.ql b/python/ql/test/library-tests/ControlFlow/general/ImmediateDominatorCheck.ql index 66758604be2..917e112b290 100644 --- a/python/ql/test/library-tests/ControlFlow/general/ImmediateDominatorCheck.ql +++ b/python/ql/test/library-tests/ControlFlow/general/ImmediateDominatorCheck.ql @@ -1,19 +1,19 @@ import python predicate can_reach_from_entry_without_passing(ControlFlowNode target, ControlFlowNode pass) { - target != pass and - target.getScope() = pass.getScope() and - ( - target.isEntryNode() - or - exists(ControlFlowNode pre | - target.getAPredecessor() = pre and can_reach_from_entry_without_passing(pre, pass) - ) + target != pass and + target.getScope() = pass.getScope() and + ( + target.isEntryNode() + or + exists(ControlFlowNode pre | + target.getAPredecessor() = pre and can_reach_from_entry_without_passing(pre, pass) ) + ) } from ControlFlowNode node, ControlFlowNode dom where - dom = node.getImmediateDominator() and - can_reach_from_entry_without_passing(node, dom) + dom = node.getImmediateDominator() and + can_reach_from_entry_without_passing(node, dom) select node.toString(), dom.toString() diff --git a/python/ql/test/library-tests/ControlFlow/general/Lines.ql b/python/ql/test/library-tests/ControlFlow/general/Lines.ql index dabbe2bbf58..ca6e7715538 100644 --- a/python/ql/test/library-tests/ControlFlow/general/Lines.ql +++ b/python/ql/test/library-tests/ControlFlow/general/Lines.ql @@ -2,7 +2,7 @@ import python from Scope s, int n where - exists(Function f | f = s | n = f.getMetrics().getNumberOfLines()) - or - exists(Module m | m = s | n = m.getMetrics().getNumberOfLines()) + exists(Function f | f = s | n = f.getMetrics().getNumberOfLines()) + or + exists(Module m | m = s | n = m.getMetrics().getNumberOfLines()) select s.toString(), n diff --git a/python/ql/test/library-tests/ControlFlow/general/Reaches.ql b/python/ql/test/library-tests/ControlFlow/general/Reaches.ql index 3412e6a99bd..2fecec98f1b 100644 --- a/python/ql/test/library-tests/ControlFlow/general/Reaches.ql +++ b/python/ql/test/library-tests/ControlFlow/general/Reaches.ql @@ -1,10 +1,10 @@ import python predicate reaches_exit(Name u) { - u.uses(_) and - exists(ControlFlowNode f, BasicBlock b | f.getNode() = u and f.getBasicBlock() = b | - b.reachesExit() - ) + u.uses(_) and + exists(ControlFlowNode f, BasicBlock b | f.getNode() = u and f.getBasicBlock() = b | + b.reachesExit() + ) } from Name u diff --git a/python/ql/test/library-tests/ControlFlow/raising_stmts/RaisingFlow.ql b/python/ql/test/library-tests/ControlFlow/raising_stmts/RaisingFlow.ql index 35e43acaa12..afe95f4c799 100644 --- a/python/ql/test/library-tests/ControlFlow/raising_stmts/RaisingFlow.ql +++ b/python/ql/test/library-tests/ControlFlow/raising_stmts/RaisingFlow.ql @@ -7,9 +7,9 @@ import python from ControlFlowNode p, ControlFlowNode s, string kind where - p.getASuccessor() = s and - (if s = p.getAnExceptionalSuccessor() then kind = "exception" else kind = " normal ") and - not p.getNode() instanceof Scope and - not s.getNode() instanceof Scope + p.getASuccessor() = s and + (if s = p.getAnExceptionalSuccessor() then kind = "exception" else kind = " normal ") and + not p.getNode() instanceof Scope and + not s.getNode() instanceof Scope select p.getNode().getLocation().getStartLine(), p.toString(), kind, - s.getNode().getLocation().getStartLine(), s + s.getNode().getLocation().getStartLine(), s diff --git a/python/ql/test/library-tests/ControlFlow/splitting/NodeCount.ql b/python/ql/test/library-tests/ControlFlow/splitting/NodeCount.ql index c743952f2b1..c51707a65ff 100644 --- a/python/ql/test/library-tests/ControlFlow/splitting/NodeCount.ql +++ b/python/ql/test/library-tests/ControlFlow/splitting/NodeCount.ql @@ -2,10 +2,10 @@ import python from AstNode a, Scope s where - not a instanceof Import and - not a instanceof If and - not a instanceof AssignStmt and - not a instanceof ExprStmt and - a.getScope() = s and - s instanceof Function + not a instanceof Import and + not a instanceof If and + not a instanceof AssignStmt and + not a instanceof ExprStmt and + a.getScope() = s and + s instanceof Function select a.getLocation().getStartLine(), s.getName(), a, count(a.getAFlowNode()) diff --git a/python/ql/test/library-tests/ControlFlow/splitting/SuccessorCount.ql b/python/ql/test/library-tests/ControlFlow/splitting/SuccessorCount.ql index 0941d2f2024..2d7e21b2c93 100644 --- a/python/ql/test/library-tests/ControlFlow/splitting/SuccessorCount.ql +++ b/python/ql/test/library-tests/ControlFlow/splitting/SuccessorCount.ql @@ -2,7 +2,7 @@ import python from ControlFlowNode p, Scope s where - p.getScope() = s and - (exists(p.getATrueSuccessor()) or exists(p.getAFalseSuccessor())) and - s instanceof Function + p.getScope() = s and + (exists(p.getATrueSuccessor()) or exists(p.getAFalseSuccessor())) and + s instanceof Function select p.getLocation().getStartLine(), s.getName(), p, strictcount(p.getASuccessor()) diff --git a/python/ql/test/library-tests/ControlFlow/ssa/defns/test.ql b/python/ql/test/library-tests/ControlFlow/ssa/defns/test.ql index d4cff3d6122..b07837f8746 100644 --- a/python/ql/test/library-tests/ControlFlow/ssa/defns/test.ql +++ b/python/ql/test/library-tests/ControlFlow/ssa/defns/test.ql @@ -3,4 +3,4 @@ import python from SsaVariable var, SsaVariable def where def = var.getAnUltimateDefinition() select var.getLocation().getFile().getShortName(), var.toString(), var.getLocation().getStartLine(), - def, def.getLocation().getStartLine() + def, def.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/ControlFlow/ssa/deletions/test.ql b/python/ql/test/library-tests/ControlFlow/ssa/deletions/test.ql index feafac5a6c7..90bd66be7b8 100644 --- a/python/ql/test/library-tests/ControlFlow/ssa/deletions/test.ql +++ b/python/ql/test/library-tests/ControlFlow/ssa/deletions/test.ql @@ -2,12 +2,12 @@ import python from SsaVariable v, string kind, ControlFlowNode use, int line where - use = v.getAUse() and - ( - kind = "delete" and v.getDefinition().isDelete() - or - kind = "other " and not v.getDefinition().isDelete() - ) and - line = use.getLocation().getStartLine() and - line != 0 + use = v.getAUse() and + ( + kind = "delete" and v.getDefinition().isDelete() + or + kind = "other " and not v.getDefinition().isDelete() + ) and + line = use.getLocation().getStartLine() and + line != 0 select line, use.toString(), v.getId(), kind diff --git a/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/phi_input_test.ql b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/phi_input_test.ql index fb2c8f20da8..b03da6a851d 100644 --- a/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/phi_input_test.ql +++ b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/phi_input_test.ql @@ -3,4 +3,4 @@ import python from SsaVariable var, SsaVariable arg, BasicBlock pred where pred = var.getPredecessorBlockForPhiArgument(arg) select var.getLocation().getFile().getShortName(), var.toString(), var.getLocation().getStartLine(), - arg, arg.getLocation().getStartLine(), pred.getLastNode().getLocation().getStartLine() + arg, arg.getLocation().getStartLine(), pred.getLastNode().getLocation().getStartLine() diff --git a/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test.ql b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test.ql index a8aef8cc72d..9466ac97061 100644 --- a/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test.ql +++ b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test.ql @@ -3,4 +3,4 @@ import python from SsaVariable var, SsaVariable arg where arg = var.getAPhiInput() select var.getLocation().getFile().getShortName(), var.toString(), var.getLocation().getStartLine(), - arg, arg.getLocation().getStartLine() + arg, arg.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/ControlFlow/ssa/uses/test.ql b/python/ql/test/library-tests/ControlFlow/ssa/uses/test.ql index e120b6e1657..31128317e45 100644 --- a/python/ql/test/library-tests/ControlFlow/ssa/uses/test.ql +++ b/python/ql/test/library-tests/ControlFlow/ssa/uses/test.ql @@ -3,4 +3,4 @@ import python from ControlFlowNode use, SsaVariable def where def.getAUse() = use select use.getLocation().getFile().getShortName(), use.toString(), use.getLocation().getStartLine(), - def.toString(), def.getLocation().getStartLine() + def.toString(), def.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/ControlFlow/successors/Successors.ql b/python/ql/test/library-tests/ControlFlow/successors/Successors.ql index 802ed60962b..4b4c2aa7c30 100644 --- a/python/ql/test/library-tests/ControlFlow/successors/Successors.ql +++ b/python/ql/test/library-tests/ControlFlow/successors/Successors.ql @@ -3,15 +3,15 @@ import semmle.python.TestUtils from ControlFlowNode p, ControlFlowNode s, string what where - s = p.getAFalseSuccessor() and what = "false" - or - s = p.getATrueSuccessor() and what = "true" - or - s = p.getAnExceptionalSuccessor() and what = "exceptional" - or - s = p.getANormalSuccessor() and what = "normal" - or - // Add fake edges for node that raise out of scope - p.isExceptionalExit(_) and s = p.getScope().getEntryNode() and what = "exit" + s = p.getAFalseSuccessor() and what = "false" + or + s = p.getATrueSuccessor() and what = "true" + or + s = p.getAnExceptionalSuccessor() and what = "exceptional" + or + s = p.getANormalSuccessor() and what = "normal" + or + // Add fake edges for node that raise out of scope + p.isExceptionalExit(_) and s = p.getScope().getEntryNode() and what = "exit" select compact_location(p.getNode()), p.getNode().toString(), compact_location(s.getNode()), - s.getNode().toString(), what + s.getNode().toString(), what diff --git a/python/ql/test/library-tests/ControlFlow/truefalse/ExceptionalSuccessors.ql b/python/ql/test/library-tests/ControlFlow/truefalse/ExceptionalSuccessors.ql index 352b1d2890d..fa4b918559e 100644 --- a/python/ql/test/library-tests/ControlFlow/truefalse/ExceptionalSuccessors.ql +++ b/python/ql/test/library-tests/ControlFlow/truefalse/ExceptionalSuccessors.ql @@ -9,8 +9,8 @@ import python from ControlFlowNode p, ControlFlowNode s where - s = p.getAnExceptionalSuccessor() - or - // Add fake edges for node that raise out of scope - p.isExceptionalExit(_) and s = p.getScope().getEntryNode() + s = p.getAnExceptionalSuccessor() + or + // Add fake edges for node that raise out of scope + p.isExceptionalExit(_) and s = p.getScope().getEntryNode() select p.getLocation().getFile().getShortName(), p.getLocation().getStartLine(), p, s.toString() diff --git a/python/ql/test/library-tests/ControlFlow/truefalse/TrueFalseSuccessors.ql b/python/ql/test/library-tests/ControlFlow/truefalse/TrueFalseSuccessors.ql index 1dedb90ea49..66e621a0d96 100644 --- a/python/ql/test/library-tests/ControlFlow/truefalse/TrueFalseSuccessors.ql +++ b/python/ql/test/library-tests/ControlFlow/truefalse/TrueFalseSuccessors.ql @@ -9,8 +9,8 @@ import python from ControlFlowNode p, ControlFlowNode s, string which where - s = p.getAFalseSuccessor() and which = "False" - or - s = p.getATrueSuccessor() and which = "True" + s = p.getAFalseSuccessor() and which = "False" + or + s = p.getATrueSuccessor() and which = "True" select p.getLocation().getFile().getShortName(), p.getLocation().getStartLine(), p, s.toString(), - which + which diff --git a/python/ql/test/library-tests/ControlFlow/try/test_ssa.ql b/python/ql/test/library-tests/ControlFlow/try/test_ssa.ql index 8ac7583b627..4d75a7728f2 100644 --- a/python/ql/test/library-tests/ControlFlow/try/test_ssa.ql +++ b/python/ql/test/library-tests/ControlFlow/try/test_ssa.ql @@ -3,4 +3,4 @@ import python from SsaVariable var, ControlFlowNode use where use = var.getAUse() select var.getLocation().getFile().getShortName(), var.toString(), var.getLocation().getStartLine(), - use.toString(), use.getLocation().getStartLine() + use.toString(), use.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/DuplicateCode/Duplicate.ql b/python/ql/test/library-tests/DuplicateCode/Duplicate.ql index c680d481398..a8f04593ea3 100644 --- a/python/ql/test/library-tests/DuplicateCode/Duplicate.ql +++ b/python/ql/test/library-tests/DuplicateCode/Duplicate.ql @@ -9,15 +9,15 @@ import python import external.CodeDuplication predicate lexically_sorted(DuplicateBlock dup1, DuplicateBlock dup2) { - dup1.sourceFile().getAbsolutePath() < dup2.sourceFile().getAbsolutePath() - or - dup1.sourceFile().getAbsolutePath() = dup2.sourceFile().getAbsolutePath() and - dup1.sourceStartLine() < dup2.sourceStartLine() + dup1.sourceFile().getAbsolutePath() < dup2.sourceFile().getAbsolutePath() + or + dup1.sourceFile().getAbsolutePath() = dup2.sourceFile().getAbsolutePath() and + dup1.sourceStartLine() < dup2.sourceStartLine() } from DuplicateBlock dup1, DuplicateBlock dup2 where - dup1.getEquivalenceClass() = dup2.getEquivalenceClass() and - lexically_sorted(dup1, dup2) + dup1.getEquivalenceClass() = dup2.getEquivalenceClass() and + lexically_sorted(dup1, dup2) select dup1.toString(), dup2.toString(), dup1.sourceFile().getShortName(), dup1.sourceStartLine(), - dup1.sourceEndLine() + dup1.sourceEndLine() diff --git a/python/ql/test/library-tests/DuplicateCode/DuplicateStatements.ql b/python/ql/test/library-tests/DuplicateCode/DuplicateStatements.ql index 17904ea65cd..ca297eb3718 100644 --- a/python/ql/test/library-tests/DuplicateCode/DuplicateStatements.ql +++ b/python/ql/test/library-tests/DuplicateCode/DuplicateStatements.ql @@ -9,18 +9,18 @@ import python import external.CodeDuplication predicate mostlyDuplicateFunction(Function f) { - exists(int covered, int total, Function other, int percent | - duplicateStatements(f, other, covered, total) and - covered != total and - total > 5 and - covered * 100 / total = percent and - percent > 80 and - not exists(Scope s | s = f.getScope*() | duplicateScopes(s, _, _, _)) - ) + exists(int covered, int total, Function other, int percent | + duplicateStatements(f, other, covered, total) and + covered != total and + total > 5 and + covered * 100 / total = percent and + percent > 80 and + not exists(Scope s | s = f.getScope*() | duplicateScopes(s, _, _, _)) + ) } from Stmt s where - mostlyDuplicateFunction(s.getScope()) and - not duplicateStatement(s.getScope(), _, s, _) + mostlyDuplicateFunction(s.getScope()) and + not duplicateStatement(s.getScope(), _, s, _) select s.toString(), s.getLocation().toString() diff --git a/python/ql/test/library-tests/DuplicateCode/Similar.ql b/python/ql/test/library-tests/DuplicateCode/Similar.ql index 528908336d8..3f9a99c8ecf 100644 --- a/python/ql/test/library-tests/DuplicateCode/Similar.ql +++ b/python/ql/test/library-tests/DuplicateCode/Similar.ql @@ -9,14 +9,14 @@ import python import external.CodeDuplication predicate lexically_sorted(SimilarBlock dup1, SimilarBlock dup2) { - dup1.sourceFile().getAbsolutePath() < dup2.sourceFile().getAbsolutePath() - or - dup1.sourceFile().getAbsolutePath() = dup2.sourceFile().getAbsolutePath() and - dup1.sourceStartLine() < dup2.sourceStartLine() + dup1.sourceFile().getAbsolutePath() < dup2.sourceFile().getAbsolutePath() + or + dup1.sourceFile().getAbsolutePath() = dup2.sourceFile().getAbsolutePath() and + dup1.sourceStartLine() < dup2.sourceStartLine() } from SimilarBlock dup1, SimilarBlock dup2 where - dup1.getEquivalenceClass() = dup2.getEquivalenceClass() and - lexically_sorted(dup1, dup2) + dup1.getEquivalenceClass() = dup2.getEquivalenceClass() and + lexically_sorted(dup1, dup2) select dup1, dup2, dup1.sourceFile().getShortName(), dup1.sourceStartLine(), dup1.sourceEndLine() diff --git a/python/ql/test/library-tests/PointsTo/api/ClassValue.ql b/python/ql/test/library-tests/PointsTo/api/ClassValue.ql index a71380b7603..230cc487ea4 100644 --- a/python/ql/test/library-tests/PointsTo/api/ClassValue.ql +++ b/python/ql/test/library-tests/PointsTo/api/ClassValue.ql @@ -2,13 +2,13 @@ import python from ClassValue cls, string description where - cls = ClassValue::bool() and description = "bool" - or - cls = ClassValue::int_() and description = "int" - or - cls = ClassValue::float_() and description = "float" - or - cls = ClassValue::classmethod() and description = "classmethod" - or - cls = ClassValue::bool().getMro().getItem(2) and description = "object" + cls = ClassValue::bool() and description = "bool" + or + cls = ClassValue::int_() and description = "int" + or + cls = ClassValue::float_() and description = "float" + or + cls = ClassValue::classmethod() and description = "classmethod" + or + cls = ClassValue::bool().getMro().getItem(2) and description = "object" select cls, description diff --git a/python/ql/test/library-tests/PointsTo/api/Constants.ql b/python/ql/test/library-tests/PointsTo/api/Constants.ql index 39763e6fc24..e44e52b4b9f 100644 --- a/python/ql/test/library-tests/PointsTo/api/Constants.ql +++ b/python/ql/test/library-tests/PointsTo/api/Constants.ql @@ -2,15 +2,15 @@ import python from string txt, Value val where - exists(string s | - txt = "u'" + s + "'" and val = Value::forUnicode(s) - or - txt = "b'" + s + "'" and val = Value::forBytes(s) - | - s = "a" or s = "b" or s = "c" or s = "d" - ) + exists(string s | + txt = "u'" + s + "'" and val = Value::forUnicode(s) or - exists(int i | txt = i.toString() and val = Value::forInt(i) | - i in [1 .. 10] or i in [1000 .. 1010] - ) + txt = "b'" + s + "'" and val = Value::forBytes(s) + | + s = "a" or s = "b" or s = "c" or s = "d" + ) + or + exists(int i | txt = i.toString() and val = Value::forInt(i) | + i in [1 .. 10] or i in [1000 .. 1010] + ) select txt, val diff --git a/python/ql/test/library-tests/PointsTo/api/QualifedNames.ql b/python/ql/test/library-tests/PointsTo/api/QualifedNames.ql index 226b2520521..daafad0492b 100644 --- a/python/ql/test/library-tests/PointsTo/api/QualifedNames.ql +++ b/python/ql/test/library-tests/PointsTo/api/QualifedNames.ql @@ -2,14 +2,14 @@ import python from FunctionValue v, string name where - name = v.getQualifiedName() and - ( - v = Value::named("len") - or - v instanceof PythonFunctionValue - or - v = Value::named("sys.exit") - or - v = Value::named("list").(ClassValue).lookup("append") - ) + name = v.getQualifiedName() and + ( + v = Value::named("len") + or + v instanceof PythonFunctionValue + or + v = Value::named("sys.exit") + or + v = Value::named("list").(ClassValue).lookup("append") + ) select v, name diff --git a/python/ql/test/library-tests/PointsTo/api/Value.ql b/python/ql/test/library-tests/PointsTo/api/Value.ql index 23d78317764..5af0d1061e7 100644 --- a/python/ql/test/library-tests/PointsTo/api/Value.ql +++ b/python/ql/test/library-tests/PointsTo/api/Value.ql @@ -2,12 +2,12 @@ import python from Value val, string name where - val = Value::named(name) and - ( - name = "bool" or - name = "sys" or - name = "sys.argv" or - name = "ValueError" or - name = "slice" - ) + val = Value::named(name) and + ( + name = "bool" or + name = "sys" or + name = "sys.argv" or + name = "ValueError" or + name = "slice" + ) select val, name diff --git a/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.ql b/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.ql index de13f0504e8..7c76b9e0ce5 100644 --- a/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.ql +++ b/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.ql @@ -2,4 +2,4 @@ import python from CallNode call, CallableValue callable, int i select call.getLocation().getStartLine(), call.toString(), callable.toString(), i, - callable.getArgumentForCall(call, i).toString() + callable.getArgumentForCall(call, i).toString() diff --git a/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.ql b/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.ql index c531a9ab57a..4cb3fbdf335 100644 --- a/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.ql +++ b/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.ql @@ -2,4 +2,4 @@ import python from CallNode call, CallableValue callable, string name select call.getLocation().getStartLine(), call.toString(), callable.toString(), name, - callable.getNamedArgumentForCall(call, name).toString() + callable.getNamedArgumentForCall(call, name).toString() diff --git a/python/ql/test/library-tests/PointsTo/comparisons/PointsTo.ql b/python/ql/test/library-tests/PointsTo/comparisons/PointsTo.ql index 958306c53e6..00d2b448c78 100644 --- a/python/ql/test/library-tests/PointsTo/comparisons/PointsTo.ql +++ b/python/ql/test/library-tests/PointsTo/comparisons/PointsTo.ql @@ -3,7 +3,7 @@ import semmle.python.objects.ObjectAPI from int line, ControlFlowNode f, Value v where - any(ExprStmt s).getValue() = f.getNode() and - line = f.getLocation().getStartLine() and - f.pointsTo(v) + any(ExprStmt s).getValue() = f.getNode() and + line = f.getLocation().getStartLine() and + f.pointsTo(v) select line, v diff --git a/python/ql/test/library-tests/PointsTo/customise/test.ql b/python/ql/test/library-tests/PointsTo/customise/test.ql index 8aea8b05b18..c2fceb95225 100644 --- a/python/ql/test/library-tests/PointsTo/customise/test.ql +++ b/python/ql/test/library-tests/PointsTo/customise/test.ql @@ -7,29 +7,29 @@ import semmle.python.types.Extensions */ class HasTypeFact extends CustomPointsToOriginFact { - HasTypeFact() { - exists(FunctionObject func, string name | - func.getACall() = this and - name = func.getName() and - name.prefix("has_type_".length()) = "has_type_" - ) - } + HasTypeFact() { + exists(FunctionObject func, string name | + func.getACall() = this and + name = func.getName() and + name.prefix("has_type_".length()) = "has_type_" + ) + } - override predicate pointsTo(Object value, ClassObject cls) { - exists(FunctionObject func, string name | - func.getACall() = this and - name = func.getName() and - name.prefix("has_type_".length()) = "has_type_" - | - cls.getName() = name.suffix("has_type_".length()) - ) and - value = this - } + override predicate pointsTo(Object value, ClassObject cls) { + exists(FunctionObject func, string name | + func.getACall() = this and + name = func.getName() and + name.prefix("has_type_".length()) = "has_type_" + | + cls.getName() = name.suffix("has_type_".length()) + ) and + value = this + } } from int line, ControlFlowNode f, Object o, ClassObject c where - f.getLocation().getStartLine() = line and - exists(Comment ct | ct.getLocation().getStartLine() < line) and - f.refersTo(o, c, _) + f.getLocation().getStartLine() = line and + exists(Comment ct | ct.getLocation().getStartLine() < line) and + f.refersTo(o, c, _) select line, f.toString(), o.toString(), c.toString() diff --git a/python/ql/test/library-tests/PointsTo/decorators/Test.ql b/python/ql/test/library-tests/PointsTo/decorators/Test.ql index 3aff12a3a1f..b5175845070 100644 --- a/python/ql/test/library-tests/PointsTo/decorators/Test.ql +++ b/python/ql/test/library-tests/PointsTo/decorators/Test.ql @@ -4,7 +4,7 @@ import python // version to version, just the end result. from NameNode f, Object o, ControlFlowNode x, int line where - f.refersTo(o, x) and - f.getLocation().getFile().getBaseName() = "test.py" and - line = f.getLocation().getStartLine() + f.refersTo(o, x) and + f.getLocation().getFile().getBaseName() = "test.py" and + line = f.getLocation().getStartLine() select line, f.toString(), o.toString(), x.getLocation().toString() diff --git a/python/ql/test/library-tests/PointsTo/decorators/Values.ql b/python/ql/test/library-tests/PointsTo/decorators/Values.ql index 712cc025786..fc7a08db20b 100644 --- a/python/ql/test/library-tests/PointsTo/decorators/Values.ql +++ b/python/ql/test/library-tests/PointsTo/decorators/Values.ql @@ -4,6 +4,6 @@ import semmle.python.objects.ObjectInternal from NameNode f, Context ctx, ObjectInternal v where - f.getLocation().getFile().getBaseName() = "test.py" and - PointsTo::pointsTo(f, ctx, v, _) + f.getLocation().getFile().getBaseName() = "test.py" and + PointsTo::pointsTo(f, ctx, v, _) select f, ctx, v diff --git a/python/ql/test/library-tests/PointsTo/extensions/Extend.ql b/python/ql/test/library-tests/PointsTo/extensions/Extend.ql index 14082905ce4..078b925a1df 100644 --- a/python/ql/test/library-tests/PointsTo/extensions/Extend.ql +++ b/python/ql/test/library-tests/PointsTo/extensions/Extend.ql @@ -3,55 +3,55 @@ import semmle.python.pointsto.PointsTo private import semmle.python.types.Extensions class CfgExtension extends CustomPointsToOriginFact { - CfgExtension() { - this.(NameNode).getId() = "one" - or - this.(NameNode).getId() = "two" - } + CfgExtension() { + this.(NameNode).getId() = "one" + or + this.(NameNode).getId() = "two" + } - override predicate pointsTo(Object value, ClassObject cls) { - cls = theIntType() and - ( - this.(NameNode).getId() = "one" and value.(NumericObject).intValue() = 1 - or - this.(NameNode).getId() = "two" and value.(NumericObject).intValue() = 2 - ) - } + override predicate pointsTo(Object value, ClassObject cls) { + cls = theIntType() and + ( + this.(NameNode).getId() = "one" and value.(NumericObject).intValue() = 1 + or + this.(NameNode).getId() = "two" and value.(NumericObject).intValue() = 2 + ) + } } class AttributeExtension extends CustomPointsToAttribute { - AttributeExtension() { this = this } + AttributeExtension() { this = this } - override predicate attributePointsTo( - string name, Object value, ClassObject cls, ControlFlowNode origin - ) { - cls = theIntType() and - origin = any(Module m).getEntryNode() and - ( - name = "three" and value.(NumericObject).intValue() = 3 - or - name = "four" and value.(NumericObject).intValue() = 4 - ) - } + override predicate attributePointsTo( + string name, Object value, ClassObject cls, ControlFlowNode origin + ) { + cls = theIntType() and + origin = any(Module m).getEntryNode() and + ( + name = "three" and value.(NumericObject).intValue() = 3 + or + name = "four" and value.(NumericObject).intValue() = 4 + ) + } } class NoClassExtension extends CustomPointsToObjectFact { - NoClassExtension() { this = this } + NoClassExtension() { this = this } - override predicate pointsTo(Object value) { - this.(NameNode).getId() = "five" and value.(NumericObject).intValue() = 5 - or - this.(NameNode).getId() = "six" and value.(NumericObject).intValue() = 6 - } + override predicate pointsTo(Object value) { + this.(NameNode).getId() = "five" and value.(NumericObject).intValue() = 5 + or + this.(NameNode).getId() = "six" and value.(NumericObject).intValue() = 6 + } } /* Check that we can use old API without causing non-monotonic recursion */ class RecurseIntoOldPointsTo extends CustomPointsToOriginFact { - RecurseIntoOldPointsTo() { PointsTo::points_to(this, _, unknownValue(), _, _) } + RecurseIntoOldPointsTo() { PointsTo::points_to(this, _, unknownValue(), _, _) } - override predicate pointsTo(Object value, ClassObject cls) { - value = unknownValue() and cls = theUnknownType() - } + override predicate pointsTo(Object value, ClassObject cls) { + value = unknownValue() and cls = theUnknownType() + } } from ControlFlowNode f, Object o diff --git a/python/ql/test/library-tests/PointsTo/functions/Calls.ql b/python/ql/test/library-tests/PointsTo/functions/Calls.ql index 2833c2e60be..5bc29b5aaf3 100644 --- a/python/ql/test/library-tests/PointsTo/functions/Calls.ql +++ b/python/ql/test/library-tests/PointsTo/functions/Calls.ql @@ -2,10 +2,10 @@ import python from CallNode call, FunctionObject func, string kind where - ( - func.getAMethodCall() = call and kind = "method" - or - func.getAFunctionCall() = call and kind = "function" - ) and - call.getLocation().getFile().getShortName().matches("odasa%") + ( + func.getAMethodCall() = call and kind = "method" + or + func.getAFunctionCall() = call and kind = "function" + ) and + call.getLocation().getFile().getShortName().matches("odasa%") select call.getLocation().getStartLine(), call.toString(), func.toString(), kind diff --git a/python/ql/test/library-tests/PointsTo/functions/test.ql b/python/ql/test/library-tests/PointsTo/functions/test.ql index f520f6b2254..f85e95f5fe4 100644 --- a/python/ql/test/library-tests/PointsTo/functions/test.ql +++ b/python/ql/test/library-tests/PointsTo/functions/test.ql @@ -2,6 +2,6 @@ import python from Call c, FunctionObject f where - c.getFunc().(Attribute).getObject().(Name).getId() = "self" and - f.getACall().getNode() = c + c.getFunc().(Attribute).getObject().(Name).getId() = "self" and + f.getACall().getNode() = c select c.getLocation().getStartLine(), f.toString() diff --git a/python/ql/test/library-tests/PointsTo/general/GlobalPointsTo.ql b/python/ql/test/library-tests/PointsTo/general/GlobalPointsTo.ql index a7b9403a18d..8caa54ccc23 100644 --- a/python/ql/test/library-tests/PointsTo/general/GlobalPointsTo.ql +++ b/python/ql/test/library-tests/PointsTo/general/GlobalPointsTo.ql @@ -3,7 +3,7 @@ import interesting from int line, ControlFlowNode f, Object o, ImportTimeScope n where - of_interest(f, line) and - f.refersTo(o) and - f.getScope() = n + of_interest(f, line) and + f.refersTo(o) and + f.getScope() = n select n.toString(), line, f.toString(), o.toString() diff --git a/python/ql/test/library-tests/PointsTo/general/LocalPointsTo.ql b/python/ql/test/library-tests/PointsTo/general/LocalPointsTo.ql index 342a329746e..aee2cb11bf4 100644 --- a/python/ql/test/library-tests/PointsTo/general/LocalPointsTo.ql +++ b/python/ql/test/library-tests/PointsTo/general/LocalPointsTo.ql @@ -11,6 +11,6 @@ import Util from int line, ControlFlowNode f, Object o where - of_interest(f, line) and - f.refersTo(o) + of_interest(f, line) and + f.refersTo(o) select line, f.toString(), repr(o) diff --git a/python/ql/test/library-tests/PointsTo/general/LocalPointsToType.ql b/python/ql/test/library-tests/PointsTo/general/LocalPointsToType.ql index c80de106c3d..fe14e61e01b 100644 --- a/python/ql/test/library-tests/PointsTo/general/LocalPointsToType.ql +++ b/python/ql/test/library-tests/PointsTo/general/LocalPointsToType.ql @@ -4,6 +4,6 @@ import Util from int line, ControlFlowNode f, Object o, ClassObject cls where - of_interest(f, line) and - f.refersTo(o, cls, _) + of_interest(f, line) and + f.refersTo(o, cls, _) select line, f.toString(), repr(o), repr(cls) diff --git a/python/ql/test/library-tests/PointsTo/general/Util.qll b/python/ql/test/library-tests/PointsTo/general/Util.qll index f75ed24f4f0..1c26d7d830b 100644 --- a/python/ql/test/library-tests/PointsTo/general/Util.qll +++ b/python/ql/test/library-tests/PointsTo/general/Util.qll @@ -1,10 +1,10 @@ import python string repr(Object o) { - not o instanceof StringObject and not o = theBoundMethodType() and result = o.toString() - or - /* Work around differing names in 2/3 */ - result = "'" + o.(StringObject).getText() + "'" - or - o = theBoundMethodType() and result = "builtin-class method" + not o instanceof StringObject and not o = theBoundMethodType() and result = o.toString() + or + /* Work around differing names in 2/3 */ + result = "'" + o.(StringObject).getText() + "'" + or + o = theBoundMethodType() and result = "builtin-class method" } diff --git a/python/ql/test/library-tests/PointsTo/general/interesting.qll b/python/ql/test/library-tests/PointsTo/general/interesting.qll index f7c3e9d682d..728738d65bc 100644 --- a/python/ql/test/library-tests/PointsTo/general/interesting.qll +++ b/python/ql/test/library-tests/PointsTo/general/interesting.qll @@ -1,13 +1,13 @@ import python predicate of_interest(ControlFlowNode n, int line) { - exists(Location l, File f | l = n.getLocation() | - line = l.getStartLine() and - f = l.getFile() and - f.getName().matches("%test.py%") and - exists(Comment c | - c.getLocation().getStartLine() < line and - c.getLocation().getFile() = f - ) + exists(Location l, File f | l = n.getLocation() | + line = l.getStartLine() and + f = l.getFile() and + f.getName().matches("%test.py%") and + exists(Comment c | + c.getLocation().getStartLine() < line and + c.getLocation().getFile() = f ) + ) } diff --git a/python/ql/test/library-tests/PointsTo/global/Global.ql b/python/ql/test/library-tests/PointsTo/global/Global.ql index d9b8a246d11..d3ab7bc0269 100644 --- a/python/ql/test/library-tests/PointsTo/global/Global.ql +++ b/python/ql/test/library-tests/PointsTo/global/Global.ql @@ -6,6 +6,6 @@ import semmle.python.objects.ObjectInternal from ControlFlowNode f, PointsToContext ctx, Value obj, ControlFlowNode orig where - exists(ExprStmt s | s.getValue().getAFlowNode() = f) and - PointsTo::pointsTo(f, ctx, obj, orig) + exists(ExprStmt s | s.getValue().getAFlowNode() = f) and + PointsTo::pointsTo(f, ctx, obj, orig) select ctx, f, obj.toString(), orig diff --git a/python/ql/test/library-tests/PointsTo/guarded/PointsTo.ql b/python/ql/test/library-tests/PointsTo/guarded/PointsTo.ql index b545f6e6a18..db4710786ac 100644 --- a/python/ql/test/library-tests/PointsTo/guarded/PointsTo.ql +++ b/python/ql/test/library-tests/PointsTo/guarded/PointsTo.ql @@ -2,7 +2,7 @@ import python from ControlFlowNode f, Object o, ControlFlowNode x where - f.refersTo(o, x) and - exists(CallNode call | call.getFunction().getNode().(Name).getId() = "use" and call.getArg(0) = f) + f.refersTo(o, x) and + exists(CallNode call | call.getFunction().getNode().(Name).getId() = "use" and call.getArg(0) = f) select f.getLocation().getFile().getShortName(), f.getLocation().getStartLine(), f.toString(), - o.toString(), x.getLocation().getStartLine() + o.toString(), x.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/guarded/PointsToWithType.ql b/python/ql/test/library-tests/PointsTo/guarded/PointsToWithType.ql index 2bf6b6b62a9..1c294e64282 100644 --- a/python/ql/test/library-tests/PointsTo/guarded/PointsToWithType.ql +++ b/python/ql/test/library-tests/PointsTo/guarded/PointsToWithType.ql @@ -2,7 +2,7 @@ import python from ControlFlowNode f, Object o, ClassObject c, ControlFlowNode x where - f.refersTo(o, c, x) and - exists(CallNode call | call.getFunction().getNode().(Name).getId() = "use" and call.getArg(0) = f) + f.refersTo(o, c, x) and + exists(CallNode call | call.getFunction().getNode().(Name).getId() = "use" and call.getArg(0) = f) select f.getLocation().getFile().getShortName(), f.getLocation().getStartLine(), f.toString(), - o.toString(), c.toString(), x.getLocation().getStartLine() + o.toString(), c.toString(), x.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/imports/Runtime.ql b/python/ql/test/library-tests/PointsTo/imports/Runtime.ql index 4d917aa5af6..f694bc64cf0 100644 --- a/python/ql/test/library-tests/PointsTo/imports/Runtime.ql +++ b/python/ql/test/library-tests/PointsTo/imports/Runtime.ql @@ -2,8 +2,8 @@ import python from int line, ControlFlowNode f, Object o, ControlFlowNode orig where - not f.getLocation().getFile().inStdlib() and - f.refersTo(o, orig) and - line = f.getLocation().getStartLine() and - line != 0 + not f.getLocation().getFile().inStdlib() and + f.refersTo(o, orig) and + line = f.getLocation().getStartLine() and + line != 0 select f.getLocation().getFile().getShortName(), line, f.toString(), o.toString(), orig.toString() diff --git a/python/ql/test/library-tests/PointsTo/imports/RuntimeWithType.ql b/python/ql/test/library-tests/PointsTo/imports/RuntimeWithType.ql index 7e915d04573..99a5f7b8163 100644 --- a/python/ql/test/library-tests/PointsTo/imports/RuntimeWithType.ql +++ b/python/ql/test/library-tests/PointsTo/imports/RuntimeWithType.ql @@ -2,9 +2,9 @@ import python from int line, ControlFlowNode f, Object o, ClassObject cls, ControlFlowNode orig where - not f.getLocation().getFile().inStdlib() and - f.refersTo(o, cls, orig) and - line = f.getLocation().getStartLine() and - line != 0 + not f.getLocation().getFile().inStdlib() and + f.refersTo(o, cls, orig) and + line = f.getLocation().getStartLine() and + line != 0 select f.getLocation().getFile().getShortName(), line, f.toString(), o.toString(), cls.toString(), - orig.toString() + orig.toString() diff --git a/python/ql/test/library-tests/PointsTo/indexing/Test.ql b/python/ql/test/library-tests/PointsTo/indexing/Test.ql index 825cb1cf3be..694e4d8e98e 100644 --- a/python/ql/test/library-tests/PointsTo/indexing/Test.ql +++ b/python/ql/test/library-tests/PointsTo/indexing/Test.ql @@ -2,6 +2,6 @@ import python from ControlFlowNode f, Object o, ControlFlowNode x where - f.refersTo(o, x) and - f.getLocation().getFile().getBaseName() = "test.py" + f.refersTo(o, x) and + f.getLocation().getFile().getBaseName() = "test.py" select f.getLocation().getStartLine(), f.toString(), o.toString(), x.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/indexing/TestWithType.ql b/python/ql/test/library-tests/PointsTo/indexing/TestWithType.ql index e11999a75de..9f16abc2de0 100644 --- a/python/ql/test/library-tests/PointsTo/indexing/TestWithType.ql +++ b/python/ql/test/library-tests/PointsTo/indexing/TestWithType.ql @@ -2,7 +2,7 @@ import python from ControlFlowNode f, Object o, ClassObject c, ControlFlowNode x where - f.refersTo(o, c, x) and - f.getLocation().getFile().getBaseName() = "test.py" + f.refersTo(o, c, x) and + f.getLocation().getFile().getBaseName() = "test.py" select f.getLocation().getStartLine(), f.toString(), o.toString(), c.toString(), - x.getLocation().getStartLine() + x.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/inheritance/BaseTypes.ql b/python/ql/test/library-tests/PointsTo/inheritance/BaseTypes.ql index c4677b7df51..f1201eba0dc 100644 --- a/python/ql/test/library-tests/PointsTo/inheritance/BaseTypes.ql +++ b/python/ql/test/library-tests/PointsTo/inheritance/BaseTypes.ql @@ -2,6 +2,6 @@ import python from ClassObject cls, ClassObject base, int n where - not cls.isBuiltin() and - base = cls.getBaseType(n) + not cls.isBuiltin() and + base = cls.getBaseType(n) select cls.toString(), n, base.toString() diff --git a/python/ql/test/library-tests/PointsTo/inheritance/MetaClass.ql b/python/ql/test/library-tests/PointsTo/inheritance/MetaClass.ql index 3768116ff11..0ca90782119 100644 --- a/python/ql/test/library-tests/PointsTo/inheritance/MetaClass.ql +++ b/python/ql/test/library-tests/PointsTo/inheritance/MetaClass.ql @@ -2,6 +2,6 @@ import python from ClassObject cls, ClassObject meta where - not cls.isBuiltin() and - meta = cls.getMetaClass() + not cls.isBuiltin() and + meta = cls.getMetaClass() select cls.toString(), meta.toString() diff --git a/python/ql/test/library-tests/PointsTo/inheritance/Mro.ql b/python/ql/test/library-tests/PointsTo/inheritance/Mro.ql index 7fdd431c216..2627eac8e38 100644 --- a/python/ql/test/library-tests/PointsTo/inheritance/Mro.ql +++ b/python/ql/test/library-tests/PointsTo/inheritance/Mro.ql @@ -4,7 +4,7 @@ private import semmle.python.pointsto.PointsTo /** Make unknown type visible */ class UnknownType extends UnknownClassInternal { - override string toString() { result = "*UNKNOWN TYPE" } + override string toString() { result = "*UNKNOWN TYPE" } } from ClassObjectInternal c diff --git a/python/ql/test/library-tests/PointsTo/inheritance/SuperTypes.ql b/python/ql/test/library-tests/PointsTo/inheritance/SuperTypes.ql index 7810c607787..338ea118ac1 100644 --- a/python/ql/test/library-tests/PointsTo/inheritance/SuperTypes.ql +++ b/python/ql/test/library-tests/PointsTo/inheritance/SuperTypes.ql @@ -2,6 +2,6 @@ import python from ClassObject cls, ClassObject sup where - not cls.isBuiltin() and - sup = cls.getASuperType() + not cls.isBuiltin() and + sup = cls.getASuperType() select cls.toString(), sup.toString() diff --git a/python/ql/test/library-tests/PointsTo/local/LocalPointsTo.ql b/python/ql/test/library-tests/PointsTo/local/LocalPointsTo.ql index 996b8597d5e..c81bd0ed3de 100644 --- a/python/ql/test/library-tests/PointsTo/local/LocalPointsTo.ql +++ b/python/ql/test/library-tests/PointsTo/local/LocalPointsTo.ql @@ -4,6 +4,6 @@ import semmle.python.objects.ObjectInternal from ControlFlowNode f, ObjectInternal obj, ControlFlowNode orig where - exists(ExprStmt s | s.getValue().getAFlowNode() = f) and - PointsTo::pointsTo(f, _, obj, orig) + exists(ExprStmt s | s.getValue().getAFlowNode() = f) and + PointsTo::pointsTo(f, _, obj, orig) select f, obj.toString(), orig diff --git a/python/ql/test/library-tests/PointsTo/lookup/Lookup.ql b/python/ql/test/library-tests/PointsTo/lookup/Lookup.ql index 67aff9597c2..b984c682742 100644 --- a/python/ql/test/library-tests/PointsTo/lookup/Lookup.ql +++ b/python/ql/test/library-tests/PointsTo/lookup/Lookup.ql @@ -2,12 +2,12 @@ import python from string l, NameNode n where - n.getLocation().getFile().getShortName() = "test.py" and - ( - n.isGlobal() and l = "global" - or - n.isLocal() and l = "local" - or - n.isNonLocal() and l = "non-local" - ) + n.getLocation().getFile().getShortName() = "test.py" and + ( + n.isGlobal() and l = "global" + or + n.isLocal() and l = "local" + or + n.isNonLocal() and l = "non-local" + ) select n.getLocation().getStartLine(), n.getId(), l diff --git a/python/ql/test/library-tests/PointsTo/metaclass/Failed.ql b/python/ql/test/library-tests/PointsTo/metaclass/Failed.ql index d9cb2f019a6..a5d2afd4241 100644 --- a/python/ql/test/library-tests/PointsTo/metaclass/Failed.ql +++ b/python/ql/test/library-tests/PointsTo/metaclass/Failed.ql @@ -2,6 +2,6 @@ import python from ClassObject cls, string reason where - cls.getPyClass().getEnclosingModule().getName() = "test" and - cls.failedInference(reason) + cls.getPyClass().getEnclosingModule().getName() = "test" and + cls.failedInference(reason) select cls, reason diff --git a/python/ql/test/library-tests/PointsTo/metaclass/Mro.ql b/python/ql/test/library-tests/PointsTo/metaclass/Mro.ql index 5a10701ef83..0f48b7a1034 100644 --- a/python/ql/test/library-tests/PointsTo/metaclass/Mro.ql +++ b/python/ql/test/library-tests/PointsTo/metaclass/Mro.ql @@ -4,7 +4,7 @@ private import semmle.python.pointsto.PointsTo /** Make unknown type visible */ class UnknownType extends UnknownClassInternal { - override string toString() { result = "*UNKNOWN TYPE" } + override string toString() { result = "*UNKNOWN TYPE" } } from PythonClassObjectInternal cls diff --git a/python/ql/test/library-tests/PointsTo/metaclass/Style.ql b/python/ql/test/library-tests/PointsTo/metaclass/Style.ql index 29feef64ec1..f29ba3a8b7c 100644 --- a/python/ql/test/library-tests/PointsTo/metaclass/Style.ql +++ b/python/ql/test/library-tests/PointsTo/metaclass/Style.ql @@ -2,10 +2,10 @@ import python from ClassObject cls, string style where - cls.getPyClass().getEnclosingModule().getName() = "test" and - ( - cls.isNewStyle() and style = "new" - or - cls.isOldStyle() and style = "old" - ) + cls.getPyClass().getEnclosingModule().getName() = "test" and + ( + cls.isNewStyle() and style = "new" + or + cls.isOldStyle() and style = "old" + ) select cls, style diff --git a/python/ql/test/library-tests/PointsTo/metaclass/test.ql b/python/ql/test/library-tests/PointsTo/metaclass/test.ql index 17b90483315..a0d47fa9717 100644 --- a/python/ql/test/library-tests/PointsTo/metaclass/test.ql +++ b/python/ql/test/library-tests/PointsTo/metaclass/test.ql @@ -3,7 +3,7 @@ private import semmle.python.objects.ObjectInternal /** Make unknown type visible */ class UnknownType extends UnknownClassInternal { - override string toString() { result = "*UNKNOWN TYPE" } + override string toString() { result = "*UNKNOWN TYPE" } } from ClassObject cls diff --git a/python/ql/test/library-tests/PointsTo/new/ClassMethod.ql b/python/ql/test/library-tests/PointsTo/new/ClassMethod.ql index 5ad6fabd380..5810cb22d8e 100644 --- a/python/ql/test/library-tests/PointsTo/new/ClassMethod.ql +++ b/python/ql/test/library-tests/PointsTo/new/ClassMethod.ql @@ -5,4 +5,4 @@ import Util from ClassMethodObject cm, CallNode call where call = cm.getACall() select locate(call.getLocation(), "lp"), cm.getFunction().toString(), - cm.(ControlFlowNode).getLocation().toString() + cm.(ControlFlowNode).getLocation().toString() diff --git a/python/ql/test/library-tests/PointsTo/new/Consistency.ql b/python/ql/test/library-tests/PointsTo/new/Consistency.ql index fe6cd9a861c..282b96fc541 100644 --- a/python/ql/test/library-tests/PointsTo/new/Consistency.ql +++ b/python/ql/test/library-tests/PointsTo/new/Consistency.ql @@ -3,142 +3,142 @@ import semmle.python.pointsto.PointsTo import semmle.python.objects.ObjectInternal predicate ssa_consistency(string clsname, string problem, string what) { + /* Exactly one definition of each SSA variable */ + exists(EssaVariable var | clsname = var.getAQlClass() | /* Exactly one definition of each SSA variable */ - exists(EssaVariable var | clsname = var.getAQlClass() | - /* Exactly one definition of each SSA variable */ - count(var.getDefinition()) != 1 and - problem = " has " + count(var.getDefinition()) + " definitions." and - what = "SSA variable " + var.getSourceVariable().getName() - or - /* Backing variable */ - not exists(var.getSourceVariable()) and - problem = "An SSA variable has no backing variable." and - what = "An SSA variable" - or - count(var.getSourceVariable()) != 1 and - problem = - var.getSourceVariable().getName() + " has " + count(var.getSourceVariable()) + - " backing variables." and - what = "SSA variable " + var.getSourceVariable().getName() - ) + count(var.getDefinition()) != 1 and + problem = " has " + count(var.getDefinition()) + " definitions." and + what = "SSA variable " + var.getSourceVariable().getName() or - /* Exactly one location */ - exists(EssaDefinition def | - clsname = def.getAQlClass() and - what = - "SSA Definition " + def.getSourceVariable().getName() + " in " + - def.getSourceVariable().(Variable).getScope().getName() and - count(def.getLocation()) != 1 and - problem = " has " + count(def.getLocation()) + " locations" - ) + /* Backing variable */ + not exists(var.getSourceVariable()) and + problem = "An SSA variable has no backing variable." and + what = "An SSA variable" or - /* Must have a source variable */ - exists(EssaDefinition def | - clsname = def.getAQlClass() and - not exists(def.getSourceVariable()) and - what = " at " + def.getLocation() and - problem = "has not source variable" - ) - or - /* Variables must have exactly one representation */ - exists(EssaVariable var | - clsname = var.getAQlClass() and - what = - "SSA variable " + var.getSourceVariable().getName() + " defined at " + - var.getDefinition().getLocation() and - count(var.getRepresentation()) != 1 and - problem = " has " + count(var.getRepresentation()) + " representations" - ) - or - /* Definitions must have exactly one representation */ - exists(EssaDefinition def | - clsname = def.getAQlClass() and - what = "SSA definition " + def.getSourceVariable().getName() + " at " + def.getLocation() and - count(def.getRepresentation()) != 1 and - problem = - " has " + count(def.getRepresentation()) + " representations: " + def.getRepresentation() - ) - or - /* Refinements must have exactly one input */ - exists(EssaNodeRefinement ref | - clsname = ref.getAQlClass() and - what = "Refinement " + ref.getSourceVariable().getName() + " at " + ref.getLocation() and - count(ref.getInput()) != 1 and - problem = " has " + count(ref.getInput()) + " inputs: " + ref.getInput().getRepresentation() - ) - or - /* - * Ideally filter nodes should have exactly one input, but it is not a big deal - * if we prune away the input, leaving it with none. - */ + count(var.getSourceVariable()) != 1 and + problem = + var.getSourceVariable().getName() + " has " + count(var.getSourceVariable()) + + " backing variables." and + what = "SSA variable " + var.getSourceVariable().getName() + ) + or + /* Exactly one location */ + exists(EssaDefinition def | + clsname = def.getAQlClass() and + what = + "SSA Definition " + def.getSourceVariable().getName() + " in " + + def.getSourceVariable().(Variable).getScope().getName() and + count(def.getLocation()) != 1 and + problem = " has " + count(def.getLocation()) + " locations" + ) + or + /* Must have a source variable */ + exists(EssaDefinition def | + clsname = def.getAQlClass() and + not exists(def.getSourceVariable()) and + what = " at " + def.getLocation() and + problem = "has not source variable" + ) + or + /* Variables must have exactly one representation */ + exists(EssaVariable var | + clsname = var.getAQlClass() and + what = + "SSA variable " + var.getSourceVariable().getName() + " defined at " + + var.getDefinition().getLocation() and + count(var.getRepresentation()) != 1 and + problem = " has " + count(var.getRepresentation()) + " representations" + ) + or + /* Definitions must have exactly one representation */ + exists(EssaDefinition def | + clsname = def.getAQlClass() and + what = "SSA definition " + def.getSourceVariable().getName() + " at " + def.getLocation() and + count(def.getRepresentation()) != 1 and + problem = + " has " + count(def.getRepresentation()) + " representations: " + def.getRepresentation() + ) + or + /* Refinements must have exactly one input */ + exists(EssaNodeRefinement ref | + clsname = ref.getAQlClass() and + what = "Refinement " + ref.getSourceVariable().getName() + " at " + ref.getLocation() and + count(ref.getInput()) != 1 and + problem = " has " + count(ref.getInput()) + " inputs: " + ref.getInput().getRepresentation() + ) + or + /* + * Ideally filter nodes should have exactly one input, but it is not a big deal + * if we prune away the input, leaving it with none. + */ - exists(EssaEdgeRefinement def | - clsname = def.getAQlClass() and - what = def.getSourceVariable().getName() + " at " + def.getLocation() - | - count(def.getInput()) > 1 and problem = " has " + count(def.getInput()) + " inputs." - ) + exists(EssaEdgeRefinement def | + clsname = def.getAQlClass() and + what = def.getSourceVariable().getName() + " at " + def.getLocation() + | + count(def.getInput()) > 1 and problem = " has " + count(def.getInput()) + " inputs." + ) + or + /* Each use has only one reaching SSA variable */ + exists(ControlFlowNode use, SsaSourceVariable v, int c | + c = strictcount(EssaVariable s | s.getAUse() = use and s.getSourceVariable() = v) and + clsname = use.getAQlClass() and + c != 1 and + what = use + " at " + use.getLocation() and + problem = " has " + c + " SSA variables reaching." + ) + or + /* Python-specific subclasses of EssaDefinitions should be disjoint and complete */ + exists(EssaDefinition def | + clsname = def.getAQlClass() and + what = def.getVariable().getName() + " at " + def.getLocation() and + problem = "has non-disjoint subclasses" + | + strictcount(def.getAQlClass()) > 2 or - /* Each use has only one reaching SSA variable */ - exists(ControlFlowNode use, SsaSourceVariable v, int c | - c = strictcount(EssaVariable s | s.getAUse() = use and s.getSourceVariable() = v) and - clsname = use.getAQlClass() and - c != 1 and - what = use + " at " + use.getLocation() and - problem = " has " + c + " SSA variables reaching." - ) + /* OK if method call and argument overlap: `x.foo(x)` */ + strictcount(def.getAQlClass()) > 1 and + not clsname = "ArgumentRefinement" and + not clsname = "SelfCallsiteRefinement" + ) + or + exists(EssaDefinition def | + clsname = def.getAQlClass() and + clsname.prefix(4) = "Essa" and + what = " at " + def.getLocation() and + problem = "not covered by Python-specific subclass." + ) + or + // All modules should have __name__ + exists(Module m | + what = " at " + m.getLocation() and + clsname = "Module" + | + not exists(m.getName()) and + problem = "does not have a name" or - /* Python-specific subclasses of EssaDefinitions should be disjoint and complete */ - exists(EssaDefinition def | - clsname = def.getAQlClass() and - what = def.getVariable().getName() + " at " + def.getLocation() and - problem = "has non-disjoint subclasses" - | - strictcount(def.getAQlClass()) > 2 - or - /* OK if method call and argument overlap: `x.foo(x)` */ - strictcount(def.getAQlClass()) > 1 and - not clsname = "ArgumentRefinement" and - not clsname = "SelfCallsiteRefinement" - ) + not m.isPackage() and + not exists(Variable v | v.getId() = "__name__" and v.getScope() = m) and + problem = "does not have a __name__ variable" or - exists(EssaDefinition def | - clsname = def.getAQlClass() and - clsname.prefix(4) = "Essa" and - what = " at " + def.getLocation() and - problem = "not covered by Python-specific subclass." - ) - or - // All modules should have __name__ - exists(Module m | - what = " at " + m.getLocation() and - clsname = "Module" - | - not exists(m.getName()) and - problem = "does not have a name" - or - not m.isPackage() and - not exists(Variable v | v.getId() = "__name__" and v.getScope() = m) and - problem = "does not have a __name__ variable" - or - not m.isPackage() and - not exists(EssaNodeDefinition def | - def.getDefiningNode().getScope() = m and - def.getVariable().getName() = "__name__" - ) and - problem = "does not have an ImplicitModuleNameDefinition" - ) + not m.isPackage() and + not exists(EssaNodeDefinition def | + def.getDefiningNode().getScope() = m and + def.getVariable().getName() = "__name__" + ) and + problem = "does not have an ImplicitModuleNameDefinition" + ) } predicate undefined_consistency(string clsname, string problem, string what) { - /* Variables may be undefined, but values cannot be */ - exists(ControlFlowNode f | - PointsToInternal::pointsTo(f, _, ObjectInternal::undefined(), _) and - clsname = f.getAQlClass() and - not clsname = "AnyNode" and - problem = " points-to an undefined variable" and - what = f.toString() - ) + /* Variables may be undefined, but values cannot be */ + exists(ControlFlowNode f | + PointsToInternal::pointsTo(f, _, ObjectInternal::undefined(), _) and + clsname = f.getAQlClass() and + not clsname = "AnyNode" and + problem = " points-to an undefined variable" and + what = f.toString() + ) } from string clsname, string problem, string what diff --git a/python/ql/test/library-tests/PointsTo/new/Dataflow.ql b/python/ql/test/library-tests/PointsTo/new/Dataflow.ql index 47a12acee53..d4dd0878942 100755 --- a/python/ql/test/library-tests/PointsTo/new/Dataflow.ql +++ b/python/ql/test/library-tests/PointsTo/new/Dataflow.ql @@ -4,4 +4,4 @@ import Util from EssaVariable v, EssaDefinition def where def = v.getDefinition() and not v.getSourceVariable() instanceof SpecialSsaSourceVariable select locate(def.getLocation(), "abdefghijknrs_"), - v.getRepresentation() + " = " + def.getRepresentation() + v.getRepresentation() + " = " + def.getRepresentation() diff --git a/python/ql/test/library-tests/PointsTo/new/Live.ql b/python/ql/test/library-tests/PointsTo/new/Live.ql index 4bcb7da27e6..daa641c6624 100644 --- a/python/ql/test/library-tests/PointsTo/new/Live.ql +++ b/python/ql/test/library-tests/PointsTo/new/Live.ql @@ -4,7 +4,7 @@ import Util from Variable var, BasicBlock b, ControlFlowNode loc, string end where - Liveness::liveAtEntry(var, b) and end = "entry" and loc = b.getNode(0) - or - Liveness::liveAtExit(var, b) and end = "exit" and loc = b.getLastNode() + Liveness::liveAtEntry(var, b) and end = "entry" and loc = b.getNode(0) + or + Liveness::liveAtExit(var, b) and end = "exit" and loc = b.getLastNode() select var, locate(loc.getLocation(), "b"), end diff --git a/python/ql/test/library-tests/PointsTo/new/NameSpace.ql b/python/ql/test/library-tests/PointsTo/new/NameSpace.ql index 18fd5e9e37c..5d099f78b61 100644 --- a/python/ql/test/library-tests/PointsTo/new/NameSpace.ql +++ b/python/ql/test/library-tests/PointsTo/new/NameSpace.ql @@ -3,16 +3,16 @@ import Util from Scope s, string name, Object val where - name != "__name__" and - ( - exists(ModuleObject m | - m.getModule() = s and - m.attributeRefersTo(name, val, _) - ) - or - exists(ClassObject cls | - cls.getPyClass() = s and - cls.declaredAttribute(name) = val - ) + name != "__name__" and + ( + exists(ModuleObject m | + m.getModule() = s and + m.attributeRefersTo(name, val, _) ) + or + exists(ClassObject cls | + cls.getPyClass() = s and + cls.declaredAttribute(name) = val + ) + ) select locate(s.getLocation(), "abcdghijklopqrs"), s.toString(), name, repr(val) diff --git a/python/ql/test/library-tests/PointsTo/new/PointsToMissing.ql b/python/ql/test/library-tests/PointsTo/new/PointsToMissing.ql index 044d33c2887..cac31b95572 100644 --- a/python/ql/test/library-tests/PointsTo/new/PointsToMissing.ql +++ b/python/ql/test/library-tests/PointsTo/new/PointsToMissing.ql @@ -5,21 +5,21 @@ import semmle.python.objects.ObjectInternal /* This test should return _no_ results. */ predicate relevant_node(ControlFlowNode n) { - exists(CallNode c | - c.getFunction().(NameNode).getId() = "check" and - n = c.getAnArg() - ) - or - exists(Comment c, string filepath, int bl | - n.getNode().getScope().getLocation().hasLocationInfo(filepath, bl, _, _, _) and - c.getLocation().hasLocationInfo(filepath, bl, _, _, _) and - c.getText().matches("%check") and - not n.(NameNode).isStore() - ) + exists(CallNode c | + c.getFunction().(NameNode).getId() = "check" and + n = c.getAnArg() + ) + or + exists(Comment c, string filepath, int bl | + n.getNode().getScope().getLocation().hasLocationInfo(filepath, bl, _, _, _) and + c.getLocation().hasLocationInfo(filepath, bl, _, _, _) and + c.getText().matches("%check") and + not n.(NameNode).isStore() + ) } from ControlFlowNode f where - relevant_node(f) and - not PointsTo::pointsTo(f, _, _, _) + relevant_node(f) and + not PointsTo::pointsTo(f, _, _, _) select locate(f.getLocation(), "abchlr"), f.toString() diff --git a/python/ql/test/library-tests/PointsTo/new/PointsToWithContext.ql b/python/ql/test/library-tests/PointsTo/new/PointsToWithContext.ql index 56a58642f1c..dbed1a9a7f6 100755 --- a/python/ql/test/library-tests/PointsTo/new/PointsToWithContext.ql +++ b/python/ql/test/library-tests/PointsTo/new/PointsToWithContext.ql @@ -6,4 +6,4 @@ import semmle.python.pointsto.PointsToContext from ControlFlowNode f, Object o, ClassObject c, ControlFlowNode x, PointsToContext ctx where PointsTo::points_to(f, ctx, o, c, x) select locate(f.getLocation(), "abeghijklmnpqrstu"), f.toString(), repr(o), repr(c), - x.getLocation().getStartLine(), ctx + x.getLocation().getStartLine(), ctx diff --git a/python/ql/test/library-tests/PointsTo/new/PointsToWithType.ql b/python/ql/test/library-tests/PointsTo/new/PointsToWithType.ql index ed04a0b3dc3..5747ce18fd5 100644 --- a/python/ql/test/library-tests/PointsTo/new/PointsToWithType.ql +++ b/python/ql/test/library-tests/PointsTo/new/PointsToWithType.ql @@ -5,4 +5,4 @@ import semmle.python.pointsto.PointsTo from ControlFlowNode f, Object o, ClassObject c, ControlFlowNode x where PointsTo::points_to(f, _, o, c, x) select locate(f.getLocation(), "abdeghijkls"), f.toString(), repr(o), repr(c), - x.getLocation().getStartLine() + x.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/new/Precedes.ql b/python/ql/test/library-tests/PointsTo/new/Precedes.ql index bda245eca6d..d14b206b5a8 100644 --- a/python/ql/test/library-tests/PointsTo/new/Precedes.ql +++ b/python/ql/test/library-tests/PointsTo/new/Precedes.ql @@ -4,4 +4,4 @@ import Util from Scope pre, Scope post where pre.precedes(post) select locate(pre.getLocation(), "q"), pre.toString(), locate(post.getLocation(), "q"), - post.toString() + post.toString() diff --git a/python/ql/test/library-tests/PointsTo/new/SSA.ql b/python/ql/test/library-tests/PointsTo/new/SSA.ql index 6c154f57e57..b8f2c4cf54b 100644 --- a/python/ql/test/library-tests/PointsTo/new/SSA.ql +++ b/python/ql/test/library-tests/PointsTo/new/SSA.ql @@ -5,8 +5,8 @@ import Util from EssaVariable v, EssaDefinition def, Object o, ClassObject cls where - def = v.getDefinition() and - not v.getSourceVariable() instanceof SpecialSsaSourceVariable and - PointsTo::ssa_variable_points_to(v, _, o, cls, _) + def = v.getDefinition() and + not v.getSourceVariable() instanceof SpecialSsaSourceVariable and + PointsTo::ssa_variable_points_to(v, _, o, cls, _) select locate(def.getLocation(), "abcdegjqmns_"), - v.getRepresentation() + " = " + def.getRepresentation(), repr(o), repr(cls) + v.getRepresentation() + " = " + def.getRepresentation(), repr(o), repr(cls) diff --git a/python/ql/test/library-tests/PointsTo/new/SourceNodeDefinitions.ql b/python/ql/test/library-tests/PointsTo/new/SourceNodeDefinitions.ql index a1547da65c6..77f6ab0923b 100644 --- a/python/ql/test/library-tests/PointsTo/new/SourceNodeDefinitions.ql +++ b/python/ql/test/library-tests/PointsTo/new/SourceNodeDefinitions.ql @@ -4,10 +4,10 @@ import Util from SsaSourceVariable var, ControlFlowNode defn, string kind where - not var instanceof SpecialSsaSourceVariable and - ( - var.hasDefiningNode(defn) and kind = "definition" - or - var.hasRefinement(_, defn) and kind = "refinement" - ) + not var instanceof SpecialSsaSourceVariable and + ( + var.hasDefiningNode(defn) and kind = "definition" + or + var.hasRefinement(_, defn) and kind = "refinement" + ) select locate(defn.getLocation(), "ab"), var.(Variable), defn.toString(), kind diff --git a/python/ql/test/library-tests/PointsTo/new/SsaAttr.ql b/python/ql/test/library-tests/PointsTo/new/SsaAttr.ql index dc71ac5df65..67d85c2e3bd 100644 --- a/python/ql/test/library-tests/PointsTo/new/SsaAttr.ql +++ b/python/ql/test/library-tests/PointsTo/new/SsaAttr.ql @@ -5,7 +5,7 @@ import Util from EssaVariable var, string name, ObjectInternal o, Context ctx where - AttributePointsTo::variableAttributePointsTo(var, ctx, name, o, _) and - not var.getSourceVariable() instanceof SpecialSsaSourceVariable + AttributePointsTo::variableAttributePointsTo(var, ctx, name, o, _) and + not var.getSourceVariable() instanceof SpecialSsaSourceVariable select locate(var.getDefinition().getLocation(), "abdfgikm"), var.getRepresentation(), name, - var.getDefinition().getRepresentation(), o, ctx + var.getDefinition().getRepresentation(), o, ctx diff --git a/python/ql/test/library-tests/PointsTo/new/TestEvaluate.ql b/python/ql/test/library-tests/PointsTo/new/TestEvaluate.ql index 2367df63b63..d473e4e804a 100644 --- a/python/ql/test/library-tests/PointsTo/new/TestEvaluate.ql +++ b/python/ql/test/library-tests/PointsTo/new/TestEvaluate.ql @@ -4,16 +4,16 @@ import semmle.python.pointsto.PointsToContext import Util from - ControlFlowNode test, ControlFlowNode use, ObjectInternal val, boolean eval, PointsToContext ctx, - ControlFlowNode origin, string what + ControlFlowNode test, ControlFlowNode use, ObjectInternal val, boolean eval, PointsToContext ctx, + ControlFlowNode origin, string what where - not use instanceof NameConstantNode and - not use.getNode() instanceof ImmutableLiteral and - eval = Conditionals::testEvaluates(test, use, ctx, val, origin) and - ( - what = val.getSource().(Object).toString() - or - not exists(val.getSource()) and what = origin.getNode().toString() - ) + not use instanceof NameConstantNode and + not use.getNode() instanceof ImmutableLiteral and + eval = Conditionals::testEvaluates(test, use, ctx, val, origin) and + ( + what = val.getSource().(Object).toString() + or + not exists(val.getSource()) and what = origin.getNode().toString() + ) select locate(test.getLocation(), "bc"), test.getNode().toString(), eval.toString(), - use.getNode().toString(), what + use.getNode().toString(), what diff --git a/python/ql/test/library-tests/PointsTo/new/Util.qll b/python/ql/test/library-tests/PointsTo/new/Util.qll index f59a1a8dae1..b83ad89d1c8 100644 --- a/python/ql/test/library-tests/PointsTo/new/Util.qll +++ b/python/ql/test/library-tests/PointsTo/new/Util.qll @@ -3,47 +3,47 @@ import semmle.python.objects.ObjectInternal bindingset[which] string locate(Location l, string which) { - exists(string file, int line | - file = l.getFile().getShortName() and - line = l.getStartLine() and - file.charAt(0) = which.charAt(_) and - file.charAt(1) = "_" and - result = file + ":" + line - ) + exists(string file, int line | + file = l.getFile().getShortName() and + line = l.getStartLine() and + file.charAt(0) = which.charAt(_) and + file.charAt(1) = "_" and + result = file + ":" + line + ) } string repr(Object o) { - /* - * Do not show `unknownValue()` to keep noise levels down. - * To show it add: - * `o = unknownValue() and result = "*UNKNOWN VALUE*"` - */ + /* + * Do not show `unknownValue()` to keep noise levels down. + * To show it add: + * `o = unknownValue() and result = "*UNKNOWN VALUE*"` + */ - not o instanceof StringObject and - not o = undefinedVariable() and - not o = theUnknownType() and - not o = theBoundMethodType() and - result = o.toString() - or - o = undefinedVariable() and result = "*UNDEFINED*" - or - o = theUnknownType() and result = "*UNKNOWN TYPE*" - or - /* Work around differing names in 2/3 */ - result = "'" + o.(StringObject).getText() + "'" - or - o = theBoundMethodType() and result = "builtin-class method" + not o instanceof StringObject and + not o = undefinedVariable() and + not o = theUnknownType() and + not o = theBoundMethodType() and + result = o.toString() + or + o = undefinedVariable() and result = "*UNDEFINED*" + or + o = theUnknownType() and result = "*UNKNOWN TYPE*" + or + /* Work around differing names in 2/3 */ + result = "'" + o.(StringObject).getText() + "'" + or + o = theBoundMethodType() and result = "builtin-class method" } predicate long_tuple(Value v) { v.(TupleObjectInternal).length() > 3 } string vrepr(Value v) { - /* Work around differing names in 2/3 */ - not v = ObjectInternal::boundMethod() and - not long_tuple(v) and - result = v.toString() - or - v = ObjectInternal::boundMethod() and result = "builtin-class method" - or - long_tuple(v) and result = "(..., ...)" + /* Work around differing names in 2/3 */ + not v = ObjectInternal::boundMethod() and + not long_tuple(v) and + result = v.toString() + or + v = ObjectInternal::boundMethod() and result = "builtin-class method" + or + long_tuple(v) and result = "(..., ...)" } diff --git a/python/ql/test/library-tests/PointsTo/new/Values.ql b/python/ql/test/library-tests/PointsTo/new/Values.ql index 754fcfede54..668e7a6b265 100644 --- a/python/ql/test/library-tests/PointsTo/new/Values.ql +++ b/python/ql/test/library-tests/PointsTo/new/Values.ql @@ -4,4 +4,4 @@ import Util from ControlFlowNode f, Context ctx, Value v, ControlFlowNode origin where f.pointsTo(ctx, v, origin) select locate(f.getLocation(), "abeghijklmnpqrstu"), f.toString(), ctx, vrepr(v), - vrepr(v.getClass()) + vrepr(v.getClass()) diff --git a/python/ql/test/library-tests/PointsTo/new/VarUses.ql b/python/ql/test/library-tests/PointsTo/new/VarUses.ql index 56c1ca637a1..58de54d7a3d 100644 --- a/python/ql/test/library-tests/PointsTo/new/VarUses.ql +++ b/python/ql/test/library-tests/PointsTo/new/VarUses.ql @@ -4,6 +4,6 @@ import Util from SsaSourceVariable var, ControlFlowNode use where - (use = var.getAUse() or var.hasRefinement(use, _)) and - not var instanceof SpecialSsaSourceVariable + (use = var.getAUse() or var.hasRefinement(use, _)) and + not var instanceof SpecialSsaSourceVariable select locate(use.getLocation(), "abd"), var.getName(), use.toString() diff --git a/python/ql/test/library-tests/PointsTo/properties/Values.ql b/python/ql/test/library-tests/PointsTo/properties/Values.ql index 597a54cb641..23416efc0eb 100644 --- a/python/ql/test/library-tests/PointsTo/properties/Values.ql +++ b/python/ql/test/library-tests/PointsTo/properties/Values.ql @@ -2,10 +2,10 @@ import python import semmle.python.objects.ObjectInternal string vrepr(Value v) { - /* Work around differing names in 2/3 */ - not v = ObjectInternal::boundMethod() and result = v.toString() - or - v = ObjectInternal::boundMethod() and result = "builtin-class method" + /* Work around differing names in 2/3 */ + not v = ObjectInternal::boundMethod() and result = v.toString() + or + v = ObjectInternal::boundMethod() and result = "builtin-class method" } from ControlFlowNode f, Context ctx, Value v, ControlFlowNode origin diff --git a/python/ql/test/library-tests/PointsTo/regressions/missing/if-urlsplit-access/Test.ql b/python/ql/test/library-tests/PointsTo/regressions/missing/if-urlsplit-access/Test.ql index c9e7d4caf3e..db02e9c4f08 100644 --- a/python/ql/test/library-tests/PointsTo/regressions/missing/if-urlsplit-access/Test.ql +++ b/python/ql/test/library-tests/PointsTo/regressions/missing/if-urlsplit-access/Test.ql @@ -2,9 +2,9 @@ import python from ControlFlowNode arg, CallNode call, string debug where - call.getAnArg() = arg and - call.getFunction().(NameNode).getId() = "check" and - if exists(arg.pointsTo()) - then debug = arg.pointsTo().toString() - else debug = "" + call.getAnArg() = arg and + call.getFunction().(NameNode).getId() = "check" and + if exists(arg.pointsTo()) + then debug = arg.pointsTo().toString() + else debug = "" select arg, debug diff --git a/python/ql/test/library-tests/PointsTo/regressions/missing/re-compile/Test.ql b/python/ql/test/library-tests/PointsTo/regressions/missing/re-compile/Test.ql index c9e7d4caf3e..db02e9c4f08 100644 --- a/python/ql/test/library-tests/PointsTo/regressions/missing/re-compile/Test.ql +++ b/python/ql/test/library-tests/PointsTo/regressions/missing/re-compile/Test.ql @@ -2,9 +2,9 @@ import python from ControlFlowNode arg, CallNode call, string debug where - call.getAnArg() = arg and - call.getFunction().(NameNode).getId() = "check" and - if exists(arg.pointsTo()) - then debug = arg.pointsTo().toString() - else debug = "" + call.getAnArg() = arg and + call.getFunction().(NameNode).getId() = "check" and + if exists(arg.pointsTo()) + then debug = arg.pointsTo().toString() + else debug = "" select arg, debug diff --git a/python/ql/test/library-tests/PointsTo/regressions/missing/uncalled-function/Test.ql b/python/ql/test/library-tests/PointsTo/regressions/missing/uncalled-function/Test.ql index c9e7d4caf3e..db02e9c4f08 100644 --- a/python/ql/test/library-tests/PointsTo/regressions/missing/uncalled-function/Test.ql +++ b/python/ql/test/library-tests/PointsTo/regressions/missing/uncalled-function/Test.ql @@ -2,9 +2,9 @@ import python from ControlFlowNode arg, CallNode call, string debug where - call.getAnArg() = arg and - call.getFunction().(NameNode).getId() = "check" and - if exists(arg.pointsTo()) - then debug = arg.pointsTo().toString() - else debug = "" + call.getAnArg() = arg and + call.getFunction().(NameNode).getId() = "check" and + if exists(arg.pointsTo()) + then debug = arg.pointsTo().toString() + else debug = "" select arg, debug diff --git a/python/ql/test/library-tests/PointsTo/regressions/wrong/classmethod/Test.ql b/python/ql/test/library-tests/PointsTo/regressions/wrong/classmethod/Test.ql index dd894ad5cea..700e0dd72a5 100644 --- a/python/ql/test/library-tests/PointsTo/regressions/wrong/classmethod/Test.ql +++ b/python/ql/test/library-tests/PointsTo/regressions/wrong/classmethod/Test.ql @@ -2,9 +2,9 @@ import python from NameNode name, CallNode call, string debug where - call.getAnArg() = name and - call.getFunction().(NameNode).getId() = "check" and - if exists(name.pointsTo()) - then debug = name.pointsTo().toString() - else debug = "" + call.getAnArg() = name and + call.getFunction().(NameNode).getId() = "check" and + if exists(name.pointsTo()) + then debug = name.pointsTo().toString() + else debug = "" select name, debug diff --git a/python/ql/test/library-tests/PointsTo/subclass/TestEvaluate.ql b/python/ql/test/library-tests/PointsTo/subclass/TestEvaluate.ql index 0f197edeb0a..b726c5885b3 100644 --- a/python/ql/test/library-tests/PointsTo/subclass/TestEvaluate.ql +++ b/python/ql/test/library-tests/PointsTo/subclass/TestEvaluate.ql @@ -4,9 +4,9 @@ import semmle.python.objects.ObjectInternal import semmle.python.pointsto.PointsToContext from - ControlFlowNode test, ControlFlowNode use, ObjectInternal val, boolean eval, PointsToContext ctx + ControlFlowNode test, ControlFlowNode use, ObjectInternal val, boolean eval, PointsToContext ctx where - PointsTo::pointsTo(use, ctx, val, _) and - eval = Conditionals::testEvaluates(test, use, ctx, val, _) + PointsTo::pointsTo(use, ctx, val, _) and + eval = Conditionals::testEvaluates(test, use, ctx, val, _) select test.getLocation().getStartLine(), test.getNode().toString(), eval.toString(), - use.getNode().toString(), val.toString() + use.getNode().toString(), val.toString() diff --git a/python/ql/test/library-tests/PointsTo/super/SuperMethodCall.ql b/python/ql/test/library-tests/PointsTo/super/SuperMethodCall.ql index 6245b56f711..86ad5bee155 100644 --- a/python/ql/test/library-tests/PointsTo/super/SuperMethodCall.ql +++ b/python/ql/test/library-tests/PointsTo/super/SuperMethodCall.ql @@ -5,7 +5,7 @@ import semmle.python.objects.ObjectInternal from CallNode call, SuperInstance sup, BoundMethodObjectInternal bm where - call.getFunction().inferredValue() = bm and - call.getFunction().(AttrNode).getObject().inferredValue() = sup + call.getFunction().inferredValue() = bm and + call.getFunction().(AttrNode).getObject().inferredValue() = sup select call.getLocation().getStartLine(), call.toString(), - bm.getFunction().getSource().(FunctionObject).getQualifiedName() + bm.getFunction().getSource().(FunctionObject).getQualifiedName() diff --git a/python/ql/test/library-tests/attributes/SelfAttribute.ql b/python/ql/test/library-tests/attributes/SelfAttribute.ql index 7ac995d2061..b99843fee79 100644 --- a/python/ql/test/library-tests/attributes/SelfAttribute.ql +++ b/python/ql/test/library-tests/attributes/SelfAttribute.ql @@ -3,7 +3,7 @@ import semmle.python.SelfAttribute from SelfAttributeRead sa, int line, string g, string l where - line = sa.getLocation().getStartLine() and - (if sa.guardedByHasattr() then g = "guarded" else g = "") and - if sa.locallyDefined() then l = "defined" else l = "" + line = sa.getLocation().getStartLine() and + (if sa.guardedByHasattr() then g = "guarded" else g = "") and + if sa.locallyDefined() then l = "defined" else l = "" select line, sa.getName(), g + l diff --git a/python/ql/test/library-tests/classes/abstract/Abstract.ql b/python/ql/test/library-tests/classes/abstract/Abstract.ql index 6773bb22785..bd2f98034cb 100644 --- a/python/ql/test/library-tests/classes/abstract/Abstract.ql +++ b/python/ql/test/library-tests/classes/abstract/Abstract.ql @@ -2,6 +2,6 @@ import python from ClassObject cls, string abstract where - not cls.isBuiltin() and - if cls.isAbstract() then abstract = "yes" else abstract = "no" + not cls.isBuiltin() and + if cls.isAbstract() then abstract = "yes" else abstract = "no" select cls.toString(), abstract diff --git a/python/ql/test/library-tests/classes/attr/class_attr.ql b/python/ql/test/library-tests/classes/attr/class_attr.ql index 3b7bf8b3ba0..197ab1a1e5e 100644 --- a/python/ql/test/library-tests/classes/attr/class_attr.ql +++ b/python/ql/test/library-tests/classes/attr/class_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name, Object obj where - cls.hasLocationInfo(_, line, _, _, _) and - obj = cls.lookupAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + obj = cls.lookupAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name, obj.toString() diff --git a/python/ql/test/library-tests/classes/attr/class_defined_attr.ql b/python/ql/test/library-tests/classes/attr/class_defined_attr.ql index ec798dcf190..d7583e689c4 100644 --- a/python/ql/test/library-tests/classes/attr/class_defined_attr.ql +++ b/python/ql/test/library-tests/classes/attr/class_defined_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name, Object obj where - cls.hasLocationInfo(_, line, _, _, _) and - obj = cls.declaredAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + obj = cls.declaredAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name, obj.toString() diff --git a/python/ql/test/library-tests/classes/attr/class_defines_attr.ql b/python/ql/test/library-tests/classes/attr/class_defines_attr.ql index 858d3e49e20..6b266a0d40f 100644 --- a/python/ql/test/library-tests/classes/attr/class_defines_attr.ql +++ b/python/ql/test/library-tests/classes/attr/class_defines_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name where - cls.hasLocationInfo(_, line, _, _, _) and - cls.declaresAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + cls.declaresAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name diff --git a/python/ql/test/library-tests/classes/attr/class_has_attr.ql b/python/ql/test/library-tests/classes/attr/class_has_attr.ql index 2f16aa4ca97..be8272d1bd6 100644 --- a/python/ql/test/library-tests/classes/attr/class_has_attr.ql +++ b/python/ql/test/library-tests/classes/attr/class_has_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name where - cls.hasLocationInfo(_, line, _, _, _) and - cls.hasAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + cls.hasAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name diff --git a/python/ql/test/library-tests/classes/attr/hash.ql b/python/ql/test/library-tests/classes/attr/hash.ql index a8ccf6c9d6b..19ac8933f69 100644 --- a/python/ql/test/library-tests/classes/attr/hash.ql +++ b/python/ql/test/library-tests/classes/attr/hash.ql @@ -8,9 +8,9 @@ import python from ClassObject cls, int line, Object obj where - cls.hasLocationInfo(_, line, _, _, _) and - obj = cls.lookupAttribute("__hash__") and - not cls.isC() and - not obj = theObjectType().lookupAttribute("__hash__") and - not obj = theTypeType().lookupAttribute("__hash__") + cls.hasLocationInfo(_, line, _, _, _) and + obj = cls.lookupAttribute("__hash__") and + not cls.isC() and + not obj = theObjectType().lookupAttribute("__hash__") and + not obj = theTypeType().lookupAttribute("__hash__") select line, cls.toString(), obj.toString() diff --git a/python/ql/test/library-tests/comments/length.ql b/python/ql/test/library-tests/comments/length.ql index 0a15328c35e..460b79c5531 100644 --- a/python/ql/test/library-tests/comments/length.ql +++ b/python/ql/test/library-tests/comments/length.ql @@ -3,6 +3,6 @@ import Lexical.CommentedOutCode from CommentBlock block, int line, boolean code where - block.hasLocationInfo(_, line, _, _, _) and - if block instanceof CommentedOutCodeBlock then code = true else code = false + block.hasLocationInfo(_, line, _, _, _) and + if block instanceof CommentedOutCodeBlock then code = true else code = false select line, block.length(), code diff --git a/python/ql/test/library-tests/comparisons/Compare2.ql b/python/ql/test/library-tests/comparisons/Compare2.ql index ade279c9efd..c8e05d39e22 100644 --- a/python/ql/test/library-tests/comparisons/Compare2.ql +++ b/python/ql/test/library-tests/comparisons/Compare2.ql @@ -3,10 +3,10 @@ import semmle.python.Comparisons from Comparison c, NameNode l, CompareOp op, NameNode r, float k, string add where - c.tests(l, op, r, k) and - ( - k < 0 and add = "" - or - k >= 0 and add = "+" - ) + c.tests(l, op, r, k) and + ( + k < 0 and add = "" + or + k >= 0 and add = "+" + ) select c.getLocation().getStartLine(), l.getId() + " " + op.repr() + " " + r.getId() + add + k diff --git a/python/ql/test/library-tests/comparisons/CompareControls.ql b/python/ql/test/library-tests/comparisons/CompareControls.ql index b803e40dfed..9da8b566d4e 100644 --- a/python/ql/test/library-tests/comparisons/CompareControls.ql +++ b/python/ql/test/library-tests/comparisons/CompareControls.ql @@ -4,4 +4,4 @@ import semmle.python.Comparisons from ComparisonControlBlock comp, SsaVariable v, CompareOp op, float k, BasicBlock b where comp.controls(v.getAUse(), op, k, b) select comp.getTest().getLocation().getStartLine(), v.getId() + " " + op.repr() + " " + k, - b.getNode(0).getLocation().getStartLine() + b.getNode(0).getLocation().getStartLine() diff --git a/python/ql/test/library-tests/dependencies/Dependencies.ql b/python/ql/test/library-tests/dependencies/Dependencies.ql index cab84c4417b..12378a567d2 100644 --- a/python/ql/test/library-tests/dependencies/Dependencies.ql +++ b/python/ql/test/library-tests/dependencies/Dependencies.ql @@ -4,4 +4,4 @@ import semmle.python.dependencies.Dependencies from DependencyKind dk, AstNode src, Object target where dk.isADependency(src, target) select dk.toString(), src.getLocation().getFile().getShortName(), src.getLocation().getStartLine(), - src.toString(), target.toString() + src.toString(), target.toString() diff --git a/python/ql/test/library-tests/descriptors/Descriptors.ql b/python/ql/test/library-tests/descriptors/Descriptors.ql index dd97b623f7f..e577d47b421 100644 --- a/python/ql/test/library-tests/descriptors/Descriptors.ql +++ b/python/ql/test/library-tests/descriptors/Descriptors.ql @@ -2,8 +2,8 @@ import python from ClassObject cls, string kind where - cls.isDescriptorType() and - /* Exclude bound-method as its name differs between 2 and 3 */ - not cls = theBoundMethodType() and - (if cls.isOverridingDescriptorType() then kind = "overriding" else kind = "non-overriding") + cls.isDescriptorType() and + /* Exclude bound-method as its name differs between 2 and 3 */ + not cls = theBoundMethodType() and + (if cls.isOverridingDescriptorType() then kind = "overriding" else kind = "non-overriding") select cls.toString(), kind diff --git a/python/ql/test/library-tests/descriptors/Methods.ql b/python/ql/test/library-tests/descriptors/Methods.ql index 4a2ec39d70c..b112bfd1d4b 100644 --- a/python/ql/test/library-tests/descriptors/Methods.ql +++ b/python/ql/test/library-tests/descriptors/Methods.ql @@ -5,7 +5,7 @@ int lineof(Object o) { result = o.getOrigin().getLocation().getStartLine() } from Object m, FunctionObject f where - m.(ClassMethodObject).getFunction() = f - or - m.(StaticMethodObject).getFunction() = f + m.(ClassMethodObject).getFunction() = f + or + m.(StaticMethodObject).getFunction() = f select lineof(m), m.toString(), lineof(f), f.toString() diff --git a/python/ql/test/library-tests/descriptors/Properties.ql b/python/ql/test/library-tests/descriptors/Properties.ql index ed36fb4e5bc..598c6d36c3b 100644 --- a/python/ql/test/library-tests/descriptors/Properties.ql +++ b/python/ql/test/library-tests/descriptors/Properties.ql @@ -3,9 +3,9 @@ import semmle.python.types.Descriptors from PropertyValue p, string method_name, FunctionValue method where - method_name = "getter" and method = p.getGetter() - or - method_name = "setter" and method = p.getSetter() - or - method_name = "deleter" and method = p.getDeleter() + method_name = "getter" and method = p.getGetter() + or + method_name = "setter" and method = p.getSetter() + or + method_name = "deleter" and method = p.getDeleter() select method, method_name, p diff --git a/python/ql/test/library-tests/encoding/CheckEncoding.ql b/python/ql/test/library-tests/encoding/CheckEncoding.ql index 60fc167e293..1e7fc8f5cfe 100644 --- a/python/ql/test/library-tests/encoding/CheckEncoding.ql +++ b/python/ql/test/library-tests/encoding/CheckEncoding.ql @@ -2,7 +2,7 @@ import python from File f, string encoding where - encoding = f.getSpecifiedEncoding() - or - not exists(f.getSpecifiedEncoding()) and encoding = "none" + encoding = f.getSpecifiedEncoding() + or + not exists(f.getSpecifiedEncoding()) and encoding = "none" select f.getAbsolutePath(), encoding diff --git a/python/ql/test/library-tests/examples/custom-sanitizer/Taint.qll b/python/ql/test/library-tests/examples/custom-sanitizer/Taint.qll index 64cbacae2a6..343fc976503 100644 --- a/python/ql/test/library-tests/examples/custom-sanitizer/Taint.qll +++ b/python/ql/test/library-tests/examples/custom-sanitizer/Taint.qll @@ -3,38 +3,38 @@ import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } + SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "taint source" } + override string toString() { result = "taint source" } } class MySimpleSanitizer extends Sanitizer { - MySimpleSanitizer() { this = "MySimpleSanitizer" } + MySimpleSanitizer() { this = "MySimpleSanitizer" } - /** - * The test `if is_safe(arg):` sanitizes `arg` on its `true` edge. - * - * Can't handle `if not is_safe(arg):` :\ that's why it's called MySimpleSanitizer - */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - taint instanceof ExternalStringKind and - exists(CallNode call | test.getTest() = call and test.getSense() = true | - call = Value::named("test.is_safe").getACall() and - test.getInput().getAUse() = call.getAnArg() - ) - } + /** + * The test `if is_safe(arg):` sanitizes `arg` on its `true` edge. + * + * Can't handle `if not is_safe(arg):` :\ that's why it's called MySimpleSanitizer + */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + taint instanceof ExternalStringKind and + exists(CallNode call | test.getTest() = call and test.getSense() = true | + call = Value::named("test.is_safe").getACall() and + test.getInput().getAUse() = call.getAnArg() + ) + } } class MySanitizerHandlingNot extends Sanitizer { - MySanitizerHandlingNot() { this = "MySanitizerHandlingNot" } + MySanitizerHandlingNot() { this = "MySanitizerHandlingNot" } - /** The test `if is_safe(arg):` sanitizes `arg` on its `true` edge. */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - taint instanceof ExternalStringKind and - clears_taint_on_true(test.getTest(), test.getSense(), test) - } + /** The test `if is_safe(arg):` sanitizes `arg` on its `true` edge. */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + taint instanceof ExternalStringKind and + clears_taint_on_true(test.getTest(), test.getSense(), test) + } } /** @@ -47,30 +47,30 @@ class MySanitizerHandlingNot extends Sanitizer { * this predicate, since the tuple where `test = c` and `sense = true` would hold. */ private predicate clears_taint_on_true( - ControlFlowNode test, boolean sense, PyEdgeRefinement edge_refinement + ControlFlowNode test, boolean sense, PyEdgeRefinement edge_refinement ) { - edge_refinement.getTest().getNode().(Expr).getASubExpression*() = test.getNode() and - ( - test = Value::named("test.is_safe").getACall() and - edge_refinement.getInput().getAUse() = test.(CallNode).getAnArg() and - sense = true - or - test.(UnaryExprNode).getNode().getOp() instanceof Not and - exists(ControlFlowNode nested_test | - nested_test = test.(UnaryExprNode).getOperand() and - clears_taint_on_true(nested_test, sense.booleanNot(), edge_refinement) - ) + edge_refinement.getTest().getNode().(Expr).getASubExpression*() = test.getNode() and + ( + test = Value::named("test.is_safe").getACall() and + edge_refinement.getInput().getAUse() = test.(CallNode).getAnArg() and + sense = true + or + test.(UnaryExprNode).getNode().getOp() instanceof Not and + exists(ControlFlowNode nested_test | + nested_test = test.(UnaryExprNode).getOperand() and + clears_taint_on_true(nested_test, sense.booleanNot(), edge_refinement) ) + ) } class TestConfig extends TaintTracking::Configuration { - TestConfig() { this = "TestConfig" } + TestConfig() { this = "TestConfig" } - override predicate isSanitizer(Sanitizer sanitizer) { - sanitizer instanceof MySanitizerHandlingNot - } + override predicate isSanitizer(Sanitizer sanitizer) { + sanitizer instanceof MySanitizerHandlingNot + } - override predicate isSource(TaintTracking::Source source) { source instanceof SimpleSource } + override predicate isSource(TaintTracking::Source source) { source instanceof SimpleSource } - override predicate isSink(TaintTracking::Sink sink) { none() } + override predicate isSink(TaintTracking::Sink sink) { none() } } diff --git a/python/ql/test/library-tests/examples/custom-sanitizer/TestTaint.ql b/python/ql/test/library-tests/examples/custom-sanitizer/TestTaint.ql index 571672cb312..431e96e4d5d 100644 --- a/python/ql/test/library-tests/examples/custom-sanitizer/TestTaint.ql +++ b/python/ql/test/library-tests/examples/custom-sanitizer/TestTaint.ql @@ -3,29 +3,29 @@ import semmle.python.dataflow.TaintTracking import Taint from - Call call, Expr arg, boolean expected_taint, boolean has_taint, string test_res, - string taint_string + Call call, Expr arg, boolean expected_taint, boolean has_taint, string test_res, + string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - ( - call.getFunc().(Name).getId() = "ensure_tainted" and - expected_taint = true - or - call.getFunc().(Name).getId() = "ensure_not_tainted" and - expected_taint = false + call.getLocation().getFile().getShortName() = "test.py" and + ( + call.getFunc().(Name).getId() = "ensure_tainted" and + expected_taint = true + or + call.getFunc().(Name).getId() = "ensure_not_tainted" and + expected_taint = false + ) and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "" and + has_taint = false + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "" and - has_taint = false - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) and - has_taint = true - ) and - if expected_taint = has_taint then test_res = "ok" else test_res = "failure" + has_taint = true + ) and + if expected_taint = has_taint then test_res = "ok" else test_res = "failure" // if expected_taint = has_taint then test_res = "✓" else test_res = "✕" select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string, test_res + taint_string, test_res diff --git a/python/ql/test/library-tests/exceptions/Legal.ql b/python/ql/test/library-tests/exceptions/Legal.ql index eb27a82d614..bfe47717d23 100644 --- a/python/ql/test/library-tests/exceptions/Legal.ql +++ b/python/ql/test/library-tests/exceptions/Legal.ql @@ -2,9 +2,9 @@ import python from ClassObject cls, string legal where - not cls.isC() and cls.isLegalExceptionType() and legal = "yes" and not cls.failedInference() - or - not cls.isC() and not cls.isLegalExceptionType() and legal = "no" and not cls.failedInference() - or - not cls.isC() and cls.failedInference(legal) + not cls.isC() and cls.isLegalExceptionType() and legal = "yes" and not cls.failedInference() + or + not cls.isC() and not cls.isLegalExceptionType() and legal = "no" and not cls.failedInference() + or + not cls.isC() and cls.failedInference(legal) select cls.toString(), legal diff --git a/python/ql/test/library-tests/exprs/ast/AstParent.ql b/python/ql/test/library-tests/exprs/ast/AstParent.ql index f472a6f6e5b..fb783070e92 100644 --- a/python/ql/test/library-tests/exprs/ast/AstParent.ql +++ b/python/ql/test/library-tests/exprs/ast/AstParent.ql @@ -1,4 +1,4 @@ import python select count(AstNode c | not exists(c.getParentNode()) and not c instanceof Module) + - count(AstNode c | strictcount(c.getParentNode()) > 1) + count(AstNode c | strictcount(c.getParentNode()) > 1) diff --git a/python/ql/test/library-tests/filters/generated/Filter.ql b/python/ql/test/library-tests/filters/generated/Filter.ql index 389440ffd3a..fa6d342710b 100644 --- a/python/ql/test/library-tests/filters/generated/Filter.ql +++ b/python/ql/test/library-tests/filters/generated/Filter.ql @@ -3,7 +3,7 @@ import semmle.python.filters.GeneratedCode from GeneratedFile f, string tool where - tool = f.getTool() - or - not exists(f.getTool()) and tool = "none" + tool = f.getTool() + or + not exists(f.getTool()) and tool = "none" select f.toString(), tool diff --git a/python/ql/test/library-tests/formatting/FormatArguments.ql b/python/ql/test/library-tests/formatting/FormatArguments.ql index f2cc38f7e8c..0c3f007131c 100644 --- a/python/ql/test/library-tests/formatting/FormatArguments.ql +++ b/python/ql/test/library-tests/formatting/FormatArguments.ql @@ -3,7 +3,7 @@ import Expressions.Formatting.AdvancedFormatting from AdvancedFormatString a, string name, int start, int end where - name = "'" + a.getFieldName(start, end) + "'" - or - name = a.getFieldNumber(start, end).toString() + name = "'" + a.getFieldName(start, end) + "'" + or + name = a.getFieldNumber(start, end).toString() select a.getLocation().getStartLine(), a.getText(), start, end, name diff --git a/python/ql/test/library-tests/jump_to_defn/Consistency.ql b/python/ql/test/library-tests/jump_to_defn/Consistency.ql index ba274e0aa21..3e49e8b0e39 100644 --- a/python/ql/test/library-tests/jump_to_defn/Consistency.ql +++ b/python/ql/test/library-tests/jump_to_defn/Consistency.ql @@ -3,17 +3,17 @@ import analysis.DefinitionTracking import analysis.CrossProjectDefinitions predicate local_problem(Definition defn, string issue, string repr) { - not exists(defn.toString()) and issue = "no toString()" and repr = "a local definition" - or - not exists(defn.getAstNode()) and issue = "no getAstNode()" and repr = defn.toString() - or - not exists(defn.getLocation()) and issue = "no getLocation()" and repr = defn.toString() - or - count(defn.getLocation()) > 1 and issue = "more than one getLocation()" and repr = defn.toString() + not exists(defn.toString()) and issue = "no toString()" and repr = "a local definition" + or + not exists(defn.getAstNode()) and issue = "no getAstNode()" and repr = defn.toString() + or + not exists(defn.getLocation()) and issue = "no getLocation()" and repr = defn.toString() + or + count(defn.getLocation()) > 1 and issue = "more than one getLocation()" and repr = defn.toString() } predicate remote_problem(Symbol s, string issue, string repr) { - not exists(s.toString()) and issue = "no toString()" and repr = "a symbol" + not exists(s.toString()) and issue = "no toString()" and repr = "a symbol" } from string issue, string repr diff --git a/python/ql/test/library-tests/jump_to_defn/Remote.ql b/python/ql/test/library-tests/jump_to_defn/Remote.ql index 7602e5839d3..120fbab6f11 100644 --- a/python/ql/test/library-tests/jump_to_defn/Remote.ql +++ b/python/ql/test/library-tests/jump_to_defn/Remote.ql @@ -4,7 +4,7 @@ import analysis.CrossProjectDefinitions from Definition defn, Symbol s where - s.find() = defn.getAstNode() and - // Exclude dunder names as these vary from version to version. - not s.toString().regexpMatch(".+__") + s.find() = defn.getAstNode() and + // Exclude dunder names as these vary from version to version. + not s.toString().regexpMatch(".+__") select s.toString() diff --git a/python/ql/test/library-tests/jump_to_defn/test.ql b/python/ql/test/library-tests/jump_to_defn/test.ql index 0f952578997..ae29e7b3027 100644 --- a/python/ql/test/library-tests/jump_to_defn/test.ql +++ b/python/ql/test/library-tests/jump_to_defn/test.ql @@ -7,6 +7,6 @@ import analysis.DefinitionTracking from Expr use, Definition defn where - defn = getADefinition(use) and - use.getEnclosingModule().getName() = "test" + defn = getADefinition(use) and + use.getEnclosingModule().getName() = "test" select use.getLocation().toString(), use.toString(), defn.toString() diff --git a/python/ql/test/library-tests/locations/implicit_concatenation/part_locations.ql b/python/ql/test/library-tests/locations/implicit_concatenation/part_locations.ql index aac64976f75..07e339bc0f1 100644 --- a/python/ql/test/library-tests/locations/implicit_concatenation/part_locations.ql +++ b/python/ql/test/library-tests/locations/implicit_concatenation/part_locations.ql @@ -1,9 +1,9 @@ import python class ImplicitConcat extends StrConst { - ImplicitConcat() { exists(this.getAnImplicitlyConcatenatedPart()) } + ImplicitConcat() { exists(this.getAnImplicitlyConcatenatedPart()) } } from StringPart s select s.getLocation().getStartLine(), s.getText(), s.getLocation().getStartColumn(), - s.getLocation().getEndColumn() + s.getLocation().getEndColumn() diff --git a/python/ql/test/library-tests/locations/implicit_concatenation/parts.ql b/python/ql/test/library-tests/locations/implicit_concatenation/parts.ql index 49fe354b6ee..0637e72df6e 100644 --- a/python/ql/test/library-tests/locations/implicit_concatenation/parts.ql +++ b/python/ql/test/library-tests/locations/implicit_concatenation/parts.ql @@ -1,7 +1,7 @@ import python class ImplicitConcat extends StrConst { - ImplicitConcat() { exists(this.getAnImplicitlyConcatenatedPart()) } + ImplicitConcat() { exists(this.getAnImplicitlyConcatenatedPart()) } } from StrConst s, StringPart part, int n diff --git a/python/ql/test/library-tests/locations/implicit_concatenation/test.ql b/python/ql/test/library-tests/locations/implicit_concatenation/test.ql index 09ba3dcd1c4..ca595f53833 100644 --- a/python/ql/test/library-tests/locations/implicit_concatenation/test.ql +++ b/python/ql/test/library-tests/locations/implicit_concatenation/test.ql @@ -1,13 +1,13 @@ import python class ImplicitConcat extends StrConst { - ImplicitConcat() { exists(this.getAnImplicitlyConcatenatedPart()) } + ImplicitConcat() { exists(this.getAnImplicitlyConcatenatedPart()) } } from StrConst s, boolean isConcat where - s instanceof ImplicitConcat and isConcat = true - or - not s instanceof ImplicitConcat and isConcat = false + s instanceof ImplicitConcat and isConcat = true + or + not s instanceof ImplicitConcat and isConcat = false select s.getLocation().getStartLine(), s.getText(), isConcat, s.getText().length(), - s.getLocation().getStartColumn(), s.getLocation().getEndColumn() + s.getLocation().getStartColumn(), s.getLocation().getEndColumn() diff --git a/python/ql/test/library-tests/locations/negative_numbers/negative.ql b/python/ql/test/library-tests/locations/negative_numbers/negative.ql index 0fe2cdcc2bc..f36bb716167 100644 --- a/python/ql/test/library-tests/locations/negative_numbers/negative.ql +++ b/python/ql/test/library-tests/locations/negative_numbers/negative.ql @@ -2,6 +2,6 @@ import python from Expr e, int bl, int bc, int el, int ec, string p where - e.getLocation().hasLocationInfo(_, bl, bc, el, ec) and - if e.isParenthesized() then p = "()" else p = "" + e.getLocation().hasLocationInfo(_, bl, bc, el, ec) and + if e.isParenthesized() then p = "()" else p = "" select e.toString(), bl, bc, el, ec, p diff --git a/python/ql/test/library-tests/modules/usage/ModuleUsage.ql b/python/ql/test/library-tests/modules/usage/ModuleUsage.ql index 3ff70adb69d..5f776e2374e 100644 --- a/python/ql/test/library-tests/modules/usage/ModuleUsage.ql +++ b/python/ql/test/library-tests/modules/usage/ModuleUsage.ql @@ -2,15 +2,15 @@ import python from ModuleValue mv, string usage where - // builtin module has different name in Python 2 and 3 - not mv = Module::builtinModule() and - ( - mv.isUsedAsModule() and usage = "isUsedAsModule" - or - mv.isUsedAsScript() and usage = "isUsedAsScript" - or - not mv.isUsedAsModule() and - not mv.isUsedAsScript() and - usage = "" - ) + // builtin module has different name in Python 2 and 3 + not mv = Module::builtinModule() and + ( + mv.isUsedAsModule() and usage = "isUsedAsModule" + or + mv.isUsedAsScript() and usage = "isUsedAsScript" + or + not mv.isUsedAsModule() and + not mv.isUsedAsScript() and + usage = "" + ) select mv, usage diff --git a/python/ql/test/library-tests/objects/Literals.ql b/python/ql/test/library-tests/objects/Literals.ql index ad6e1181cfd..a7f10b358ff 100644 --- a/python/ql/test/library-tests/objects/Literals.ql +++ b/python/ql/test/library-tests/objects/Literals.ql @@ -2,9 +2,9 @@ import python string repr(Expr e) { - result = e.(Num).getN() or - result = e.(Bytes).getS() or - result = e.(Unicode).getS() + result = e.(Num).getN() or + result = e.(Bytes).getS() or + result = e.(Unicode).getS() } from ImmutableLiteral l diff --git a/python/ql/test/library-tests/objects/Name.ql b/python/ql/test/library-tests/objects/Name.ql index c20358b9062..900af0cf2e3 100644 --- a/python/ql/test/library-tests/objects/Name.ql +++ b/python/ql/test/library-tests/objects/Name.ql @@ -2,20 +2,20 @@ import python from Object o, string name where - o.hasLongName(name) and - ( - name = "sys.modules" - or - name = "test.n" - or - name = "test.l" - or - name = "test.d" - or - name = "test.C.meth" - or - name = "test.C.cmeth" - or - name = "test.C.smeth" - ) + o.hasLongName(name) and + ( + name = "sys.modules" + or + name = "test.n" + or + name = "test.l" + or + name = "test.d" + or + name = "test.C.meth" + or + name = "test.C.cmeth" + or + name = "test.C.smeth" + ) select name, o.toString() diff --git a/python/ql/test/library-tests/overrides/FunctionOverrides.ql b/python/ql/test/library-tests/overrides/FunctionOverrides.ql index 1493223aa8e..c719006665d 100644 --- a/python/ql/test/library-tests/overrides/FunctionOverrides.ql +++ b/python/ql/test/library-tests/overrides/FunctionOverrides.ql @@ -2,6 +2,6 @@ import python from PythonFunctionValue f, string overriding, string overridden where - (if f.isOverridingMethod() then overriding = "overriding" else overriding = "not overriding") and - (if f.isOverriddenMethod() then overridden = "overridden" else overridden = "not overridden") + (if f.isOverridingMethod() then overriding = "overriding" else overriding = "not overriding") and + (if f.isOverriddenMethod() then overridden = "overridden" else overridden = "not overridden") select f, overriding, overridden diff --git a/python/ql/test/library-tests/parameters/Special.ql b/python/ql/test/library-tests/parameters/Special.ql index 4987599bc72..e26e0797ff6 100644 --- a/python/ql/test/library-tests/parameters/Special.ql +++ b/python/ql/test/library-tests/parameters/Special.ql @@ -2,9 +2,9 @@ import python from Parameter p, string type where - p.isKwargs() and type = "kwargs" - or - p.isVarargs() and type = "varargs" - or - not p.isKwargs() and not p.isVarargs() and type = "normal" + p.isKwargs() and type = "kwargs" + or + p.isVarargs() and type = "varargs" + or + not p.isKwargs() and not p.isVarargs() and type = "normal" select p.getName(), type diff --git a/python/ql/test/library-tests/regex/Alternation.ql b/python/ql/test/library-tests/regex/Alternation.ql index 79622fae32e..b369f822d4a 100644 --- a/python/ql/test/library-tests/regex/Alternation.ql +++ b/python/ql/test/library-tests/regex/Alternation.ql @@ -4,4 +4,4 @@ import semmle.python.regex from Regex r, int start, int end, int part_start, int part_end where r.alternationOption(start, end, part_start, part_end) select r.getText(), start, end, r.getText().substring(start, end), part_start, part_end, - r.getText().substring(part_start, part_end) + r.getText().substring(part_start, part_end) diff --git a/python/ql/test/library-tests/regex/FirstLast.ql b/python/ql/test/library-tests/regex/FirstLast.ql index 7a57eb51382..5bca6fdf542 100644 --- a/python/ql/test/library-tests/regex/FirstLast.ql +++ b/python/ql/test/library-tests/regex/FirstLast.ql @@ -2,9 +2,9 @@ import python import semmle.python.regex predicate part(Regex r, int start, int end, string kind) { - r.lastItem(start, end) and kind = "last" - or - r.firstItem(start, end) and kind = "first" + r.lastItem(start, end) and kind = "last" + or + r.firstItem(start, end) and kind = "first" } from Regex r, int start, int end, string kind diff --git a/python/ql/test/library-tests/regex/GroupContents.ql b/python/ql/test/library-tests/regex/GroupContents.ql index 28ad5749c0a..4fd7d9d229e 100644 --- a/python/ql/test/library-tests/regex/GroupContents.ql +++ b/python/ql/test/library-tests/regex/GroupContents.ql @@ -4,4 +4,4 @@ import semmle.python.regex from Regex r, int start, int end, int part_start, int part_end where r.groupContents(start, end, part_start, part_end) select r.getText(), start, end, r.getText().substring(start, end), part_start, part_end, - r.getText().substring(part_start, part_end) + r.getText().substring(part_start, part_end) diff --git a/python/ql/test/library-tests/regex/Regex.ql b/python/ql/test/library-tests/regex/Regex.ql index 708ad82804d..ab320bb744e 100644 --- a/python/ql/test/library-tests/regex/Regex.ql +++ b/python/ql/test/library-tests/regex/Regex.ql @@ -2,21 +2,21 @@ import python import semmle.python.regex predicate part(Regex r, int start, int end, string kind) { - r.alternation(start, end) and kind = "choice" - or - r.normalCharacter(start, end) and kind = "char" - or - r.specialCharacter(start, end, kind) - or - r.sequence(start, end) and kind = "sequence" - or - r.charSet(start, end) and kind = "char-set" - or - r.zeroWidthMatch(start, end) and kind = "empty group" - or - r.group(start, end) and not r.zeroWidthMatch(start, end) and kind = "non-empty group" - or - r.qualifiedItem(start, end, _) and kind = "qualified" + r.alternation(start, end) and kind = "choice" + or + r.normalCharacter(start, end) and kind = "char" + or + r.specialCharacter(start, end, kind) + or + r.sequence(start, end) and kind = "sequence" + or + r.charSet(start, end) and kind = "char-set" + or + r.zeroWidthMatch(start, end) and kind = "empty group" + or + r.group(start, end) and not r.zeroWidthMatch(start, end) and kind = "non-empty group" + or + r.qualifiedItem(start, end, _) and kind = "qualified" } from Regex r, int start, int end, string kind diff --git a/python/ql/test/library-tests/security/fabric-v1-execute/Taint.qll b/python/ql/test/library-tests/security/fabric-v1-execute/Taint.qll index 666b963b686..7d1a812be92 100644 --- a/python/ql/test/library-tests/security/fabric-v1-execute/Taint.qll +++ b/python/ql/test/library-tests/security/fabric-v1-execute/Taint.qll @@ -4,21 +4,21 @@ import semmle.python.security.strings.Untrusted import semmle.python.security.injection.Command class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } + SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "taint source" } + override string toString() { result = "taint source" } } class FabricExecuteTestConfiguration extends TaintTracking::Configuration { - FabricExecuteTestConfiguration() { this = "FabricExecuteTestConfiguration" } + FabricExecuteTestConfiguration() { this = "FabricExecuteTestConfiguration" } - override predicate isSource(TaintTracking::Source source) { source instanceof SimpleSource } + override predicate isSource(TaintTracking::Source source) { source instanceof SimpleSource } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof CommandSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof CommandSink } - override predicate isExtension(TaintTracking::Extension extension) { - extension instanceof FabricExecuteExtension - } + override predicate isExtension(TaintTracking::Extension extension) { + extension instanceof FabricExecuteExtension + } } diff --git a/python/ql/test/library-tests/security/fabric-v1-execute/TestTaint.ql b/python/ql/test/library-tests/security/fabric-v1-execute/TestTaint.ql index bdc2f60cbe9..7b9c6025c9d 100644 --- a/python/ql/test/library-tests/security/fabric-v1-execute/TestTaint.ql +++ b/python/ql/test/library-tests/security/fabric-v1-execute/TestTaint.ql @@ -2,33 +2,32 @@ import python import semmle.python.security.TaintTracking import semmle.python.web.HttpRequest import semmle.python.security.strings.Untrusted - import Taint from - Call call, Expr arg, boolean expected_taint, boolean has_taint, string test_res, - string taint_string + Call call, Expr arg, boolean expected_taint, boolean has_taint, string test_res, + string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - ( - call.getFunc().(Name).getId() = "ensure_tainted" and - expected_taint = true - or - call.getFunc().(Name).getId() = "ensure_not_tainted" and - expected_taint = false + call.getLocation().getFile().getShortName() = "test.py" and + ( + call.getFunc().(Name).getId() = "ensure_tainted" and + expected_taint = true + or + call.getFunc().(Name).getId() = "ensure_not_tainted" and + expected_taint = false + ) and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "" and + has_taint = false + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "" and - has_taint = false - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) and - has_taint = true - ) and - if expected_taint = has_taint then test_res = "ok " else test_res = "fail" + has_taint = true + ) and + if expected_taint = has_taint then test_res = "ok " else test_res = "fail" // if expected_taint = has_taint then test_res = "✓" else test_res = "✕" select arg.getLocation().toString(), test_res, call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/library-tests/state_tracking/Lib.qll b/python/ql/test/library-tests/state_tracking/Lib.qll index e784b7e198b..dc24e2bf0b7 100644 --- a/python/ql/test/library-tests/state_tracking/Lib.qll +++ b/python/ql/test/library-tests/state_tracking/Lib.qll @@ -4,15 +4,15 @@ import semmle.python.dataflow.StateTracking predicate callTo(CallNode call, string name) { call.getFunction().(NameNode).getId() = name } class Initialized extends TrackableState { - Initialized() { this = "initialized" } + Initialized() { this = "initialized" } - override predicate startsAt(ControlFlowNode f) { callTo(f, "initialize") } + override predicate startsAt(ControlFlowNode f) { callTo(f, "initialize") } } class Frobnicated extends TrackableState { - Frobnicated() { this = "frobnicated" } + Frobnicated() { this = "frobnicated" } - override predicate startsAt(ControlFlowNode f) { callTo(f, "frobnicate") } + override predicate startsAt(ControlFlowNode f) { callTo(f, "frobnicate") } - override predicate endsAt(ControlFlowNode f) { callTo(f, "defrobnicate") } + override predicate endsAt(ControlFlowNode f) { callTo(f, "defrobnicate") } } diff --git a/python/ql/test/library-tests/state_tracking/Test.ql b/python/ql/test/library-tests/state_tracking/Test.ql index cfdfa7c77aa..a0a12e8615d 100644 --- a/python/ql/test/library-tests/state_tracking/Test.ql +++ b/python/ql/test/library-tests/state_tracking/Test.ql @@ -3,10 +3,10 @@ import Lib from ControlFlowNode f, TrackableState state, Context ctx, boolean sense where - f.getLocation().getStartLine() >= 20 and - ( - state.appliesTo(f, ctx) and sense = true - or - state.mayNotApplyTo(f, ctx) and sense = false - ) + f.getLocation().getStartLine() >= 20 and + ( + state.appliesTo(f, ctx) and sense = true + or + state.mayNotApplyTo(f, ctx) and sense = false + ) select f.getLocation().toString(), f, ctx, state, sense diff --git a/python/ql/test/library-tests/state_tracking/Violations.ql b/python/ql/test/library-tests/state_tracking/Violations.ql index db70e7d3368..c4228141e29 100644 --- a/python/ql/test/library-tests/state_tracking/Violations.ql +++ b/python/ql/test/library-tests/state_tracking/Violations.ql @@ -3,10 +3,10 @@ import Lib from ControlFlowNode f, TrackableState state where - ( - callTo(f, "exacerbate") and state = "frobnicated" - or - callTo(f, "frobnicate") and state = "initialized" - ) and - state.mayNotApplyTo(f) + ( + callTo(f, "exacerbate") and state = "frobnicated" + or + callTo(f, "frobnicate") and state = "initialized" + ) and + state.mayNotApplyTo(f) select f.getLocation().toString(), f.toString(), state.toString() diff --git a/python/ql/test/library-tests/stmts/general/AstParent.ql b/python/ql/test/library-tests/stmts/general/AstParent.ql index 85e0f4947fa..a54c8c669e2 100644 --- a/python/ql/test/library-tests/stmts/general/AstParent.ql +++ b/python/ql/test/library-tests/stmts/general/AstParent.ql @@ -2,4 +2,4 @@ import python /* The result of this query should always be 0, *regardless* of the database. */ select count(AstNode c | not exists(c.getParentNode()) and not c instanceof Module) + - count(AstNode c | strictcount(c.getParentNode()) > 1) + count(AstNode c | strictcount(c.getParentNode()) > 1) diff --git a/python/ql/test/library-tests/stmts/general/SubExpressions.ql b/python/ql/test/library-tests/stmts/general/SubExpressions.ql index e3b5eed1ced..352feba7a0c 100644 --- a/python/ql/test/library-tests/stmts/general/SubExpressions.ql +++ b/python/ql/test/library-tests/stmts/general/SubExpressions.ql @@ -2,4 +2,4 @@ import python from Stmt s select s.toString(), s.getASubExpression().toString(), - s.getASubExpression().getASubExpression*().toString(), s.getLocation().getStartLine() + s.getASubExpression().getASubExpression*().toString(), s.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/stmts/raise_stmt/AST.ql b/python/ql/test/library-tests/stmts/raise_stmt/AST.ql index 62719f1179f..7dee8499025 100644 --- a/python/ql/test/library-tests/stmts/raise_stmt/AST.ql +++ b/python/ql/test/library-tests/stmts/raise_stmt/AST.ql @@ -3,4 +3,4 @@ import python from AstNode parent, AstNode child where child.getParentNode() = parent select parent.getLocation().getStartLine(), parent.toString(), child.getLocation().getStartLine(), - child.toString() + child.toString() diff --git a/python/ql/test/library-tests/stmts/try_stmt/AST.ql b/python/ql/test/library-tests/stmts/try_stmt/AST.ql index 62719f1179f..7dee8499025 100644 --- a/python/ql/test/library-tests/stmts/try_stmt/AST.ql +++ b/python/ql/test/library-tests/stmts/try_stmt/AST.ql @@ -3,4 +3,4 @@ import python from AstNode parent, AstNode child where child.getParentNode() = parent select parent.getLocation().getStartLine(), parent.toString(), child.getLocation().getStartLine(), - child.toString() + child.toString() diff --git a/python/ql/test/library-tests/stmts/with_stmt/AST.ql b/python/ql/test/library-tests/stmts/with_stmt/AST.ql index 62719f1179f..7dee8499025 100644 --- a/python/ql/test/library-tests/stmts/with_stmt/AST.ql +++ b/python/ql/test/library-tests/stmts/with_stmt/AST.ql @@ -3,4 +3,4 @@ import python from AstNode parent, AstNode child where child.getParentNode() = parent select parent.getLocation().getStartLine(), parent.toString(), child.getLocation().getStartLine(), - child.toString() + child.toString() diff --git a/python/ql/test/library-tests/taint/collections/Taint.qll b/python/ql/test/library-tests/taint/collections/Taint.qll index 21e16aabac5..010b9738c5c 100644 --- a/python/ql/test/library-tests/taint/collections/Taint.qll +++ b/python/ql/test/library-tests/taint/collections/Taint.qll @@ -3,25 +3,25 @@ import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } + SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "taint source" } + override string toString() { result = "taint source" } } class ListSource extends TaintSource { - ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } + ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } - override string toString() { result = "list taint source" } + override string toString() { result = "list taint source" } } class DictSource extends TaintSource { - DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } + DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - override string toString() { result = "dict taint source" } + override string toString() { result = "dict taint source" } } diff --git a/python/ql/test/library-tests/taint/collections/TestStep.ql b/python/ql/test/library-tests/taint/collections/TestStep.ql index 7e42b878e74..177edce3498 100644 --- a/python/ql/test/library-tests/taint/collections/TestStep.ql +++ b/python/ql/test/library-tests/taint/collections/TestStep.ql @@ -4,8 +4,8 @@ import Taint from TaintedNode n, TaintedNode s where - n.getLocation().getFile().getShortName() = "test.py" and - s.getLocation().getFile().getShortName() = "test.py" and - s = n.getASuccessor() + n.getLocation().getFile().getShortName() = "test.py" and + s.getLocation().getFile().getShortName() = "test.py" and + s = n.getASuccessor() select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getAstNode(), n.getContext(), - " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), s.getAstNode(), s.getContext() + " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), s.getAstNode(), s.getContext() diff --git a/python/ql/test/library-tests/taint/collections/TestTaint.ql b/python/ql/test/library-tests/taint/collections/TestTaint.ql index fb1d102aa7a..47883578516 100644 --- a/python/ql/test/library-tests/taint/collections/TestTaint.ql +++ b/python/ql/test/library-tests/taint/collections/TestTaint.ql @@ -4,16 +4,16 @@ import Taint from Call call, Expr arg, string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - call.getFunc().(Name).getId() = "test" and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "NO TAINT" - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) + call.getLocation().getFile().getShortName() = "test.py" and + call.getFunc().(Name).getId() = "test" and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "NO TAINT" + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) + ) select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/library-tests/taint/config/RockPaperScissors.ql b/python/ql/test/library-tests/taint/config/RockPaperScissors.ql index abcc862f418..8d6170351f1 100644 --- a/python/ql/test/library-tests/taint/config/RockPaperScissors.ql +++ b/python/ql/test/library-tests/taint/config/RockPaperScissors.ql @@ -10,4 +10,4 @@ import semmle.python.security.Paths from RockPaperScissorConfig config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "$@ loses to $@.", src.getNode(), src.getTaintKind().toString(), - sink.getNode(), sink.getTaintKind().toString() + sink.getNode(), sink.getTaintKind().toString() diff --git a/python/ql/test/library-tests/taint/config/Simple.ql b/python/ql/test/library-tests/taint/config/Simple.ql index b3593354f5e..9a87a67c9f1 100644 --- a/python/ql/test/library-tests/taint/config/Simple.ql +++ b/python/ql/test/library-tests/taint/config/Simple.ql @@ -10,4 +10,4 @@ import semmle.python.security.Paths from SimpleConfig config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "$@ flows to $@.", src.getNode(), src.getTaintKind().toString(), - sink.getNode(), sink.getTaintKind().toString() + sink.getNode(), sink.getTaintKind().toString() diff --git a/python/ql/test/library-tests/taint/config/TaintLib.qll b/python/ql/test/library-tests/taint/config/TaintLib.qll index 52e7c71858b..35eebe8ffa6 100644 --- a/python/ql/test/library-tests/taint/config/TaintLib.qll +++ b/python/ql/test/library-tests/taint/config/TaintLib.qll @@ -2,247 +2,247 @@ import python import semmle.python.dataflow.TaintTracking class SimpleTest extends TaintKind { - SimpleTest() { this = "simple.test" } + SimpleTest() { this = "simple.test" } } abstract class TestConfig extends TaintTracking::Configuration { - bindingset[this] - TestConfig() { any() } + bindingset[this] + TestConfig() { any() } } class SimpleConfig extends TestConfig { - SimpleConfig() { this = "Simple config" } + SimpleConfig() { this = "Simple config" } - override predicate isSource(DataFlow::Node node, TaintKind kind) { - node.asCfgNode().(NameNode).getId() = "SOURCE" and - kind instanceof SimpleTest - } + override predicate isSource(DataFlow::Node node, TaintKind kind) { + node.asCfgNode().(NameNode).getId() = "SOURCE" and + kind instanceof SimpleTest + } - override predicate isSink(DataFlow::Node node, TaintKind kind) { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "SINK" and - node.asCfgNode() = call.getAnArg() - ) and - kind instanceof SimpleTest - } + override predicate isSink(DataFlow::Node node, TaintKind kind) { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "SINK" and + node.asCfgNode() = call.getAnArg() + ) and + kind instanceof SimpleTest + } - override predicate isBarrier(DataFlow::Node node, TaintKind kind) { - node.asCfgNode().(CallNode).getFunction().(NameNode).getId() = "SANITIZE" and - kind instanceof SimpleTest - } + override predicate isBarrier(DataFlow::Node node, TaintKind kind) { + node.asCfgNode().(CallNode).getFunction().(NameNode).getId() = "SANITIZE" and + kind instanceof SimpleTest + } } class BasicCustomTaint extends TaintKind { - BasicCustomTaint() { this = "basic.custom" } + BasicCustomTaint() { this = "basic.custom" } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - tonode.(CallNode).getAnArg() = fromnode and - tonode.(CallNode).getFunction().(NameNode).getId() = "TAINT_FROM_ARG" and - result = this - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + tonode.(CallNode).getAnArg() = fromnode and + tonode.(CallNode).getFunction().(NameNode).getId() = "TAINT_FROM_ARG" and + result = this + } } class BasicCustomConfig extends TestConfig { - BasicCustomConfig() { this = "Basic custom config" } + BasicCustomConfig() { this = "Basic custom config" } - override predicate isSource(DataFlow::Node node, TaintKind kind) { - node.asCfgNode().(NameNode).getId() = "CUSTOM_SOURCE" and - kind instanceof SimpleTest - } + override predicate isSource(DataFlow::Node node, TaintKind kind) { + node.asCfgNode().(NameNode).getId() = "CUSTOM_SOURCE" and + kind instanceof SimpleTest + } - override predicate isSink(DataFlow::Node node, TaintKind kind) { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "CUSTOM_SINK" and - node.asCfgNode() = call.getAnArg() - ) and - kind instanceof SimpleTest - } + override predicate isSink(DataFlow::Node node, TaintKind kind) { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "CUSTOM_SINK" and + node.asCfgNode() = call.getAnArg() + ) and + kind instanceof SimpleTest + } } class Rock extends TaintKind { - Rock() { this = "rock" } + Rock() { this = "rock" } - override TaintKind getTaintOfMethodResult(string name) { - name = "prev" and result instanceof Scissors - } + override TaintKind getTaintOfMethodResult(string name) { + name = "prev" and result instanceof Scissors + } } class Paper extends TaintKind { - Paper() { this = "paper" } + Paper() { this = "paper" } - override TaintKind getTaintOfMethodResult(string name) { - name = "prev" and result instanceof Rock - } + override TaintKind getTaintOfMethodResult(string name) { + name = "prev" and result instanceof Rock + } } class Scissors extends TaintKind { - Scissors() { this = "scissors" } + Scissors() { this = "scissors" } - override TaintKind getTaintOfMethodResult(string name) { - name = "prev" and result instanceof Paper - } + override TaintKind getTaintOfMethodResult(string name) { + name = "prev" and result instanceof Paper + } } class RockPaperScissorConfig extends TestConfig { - RockPaperScissorConfig() { this = "Rock-paper-scissors config" } + RockPaperScissorConfig() { this = "Rock-paper-scissors config" } - override predicate isSource(DataFlow::Node node, TaintKind kind) { - exists(string name | - node.asCfgNode().(NameNode).getId() = name and - kind = name.toLowerCase() - | - name = "ROCK" or name = "PAPER" or name = "SCISSORS" - ) - } + override predicate isSource(DataFlow::Node node, TaintKind kind) { + exists(string name | + node.asCfgNode().(NameNode).getId() = name and + kind = name.toLowerCase() + | + name = "ROCK" or name = "PAPER" or name = "SCISSORS" + ) + } - override predicate isSink(DataFlow::Node node, TaintKind kind) { - exists(string name | function_param(name, node) | - name = "paper" and kind = "rock" - or - name = "rock" and kind = "scissors" - or - name = "scissors" and kind = "paper" - ) - } + override predicate isSink(DataFlow::Node node, TaintKind kind) { + exists(string name | function_param(name, node) | + name = "paper" and kind = "rock" + or + name = "rock" and kind = "scissors" + or + name = "scissors" and kind = "paper" + ) + } } private predicate function_param(string funcname, DataFlow::Node arg) { - exists(FunctionObject f | - f.getName() = funcname and - arg.asCfgNode() = f.getArgumentForCall(_, _) - ) + exists(FunctionObject f | + f.getName() = funcname and + arg.asCfgNode() = f.getArgumentForCall(_, _) + ) } class TaintCarrier extends TaintKind { - TaintCarrier() { this = "explicit.carrier" } + TaintCarrier() { this = "explicit.carrier" } - override TaintKind getTaintOfMethodResult(string name) { - name = "get_taint" and result instanceof SimpleTest - } + override TaintKind getTaintOfMethodResult(string name) { + name = "get_taint" and result instanceof SimpleTest + } } class TaintCarrierConfig extends TestConfig { - TaintCarrierConfig() { this = "Taint carrier config" } + TaintCarrierConfig() { this = "Taint carrier config" } - override predicate isSource(DataFlow::Node node, TaintKind kind) { - node.asCfgNode().(NameNode).getId() = "TAINT_CARRIER_SOURCE" and - kind instanceof TaintCarrier - } + override predicate isSource(DataFlow::Node node, TaintKind kind) { + node.asCfgNode().(NameNode).getId() = "TAINT_CARRIER_SOURCE" and + kind instanceof TaintCarrier + } - override predicate isSink(DataFlow::Node node, TaintKind kind) { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "SINK" and - node.asCfgNode() = call.getAnArg() - ) and - kind instanceof SimpleTest - } + override predicate isSink(DataFlow::Node node, TaintKind kind) { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "SINK" and + node.asCfgNode() = call.getAnArg() + ) and + kind instanceof SimpleTest + } - override predicate isBarrier(DataFlow::Node node, TaintKind kind) { - node.asCfgNode().(CallNode).getFunction().(NameNode).getId() = "SANITIZE" and - kind instanceof SimpleTest - } + override predicate isBarrier(DataFlow::Node node, TaintKind kind) { + node.asCfgNode().(CallNode).getFunction().(NameNode).getId() = "SANITIZE" and + kind instanceof SimpleTest + } } /* Some more realistic examples */ abstract class UserInput extends TaintKind { - bindingset[this] - UserInput() { any() } + bindingset[this] + UserInput() { any() } } class UserInputSource extends TaintSource { - UserInputSource() { this.(CallNode).getFunction().(NameNode).getId() = "user_input" } + UserInputSource() { this.(CallNode).getFunction().(NameNode).getId() = "user_input" } - override predicate isSourceOf(TaintKind kind) { kind instanceof UserInput } + override predicate isSourceOf(TaintKind kind) { kind instanceof UserInput } - override string toString() { result = "user.input.source" } + override string toString() { result = "user.input.source" } } class SqlInjectionTaint extends UserInput { - SqlInjectionTaint() { this = "SQL injection" } + SqlInjectionTaint() { this = "SQL injection" } } class CommandInjectionTaint extends UserInput { - CommandInjectionTaint() { this = "Command injection" } + CommandInjectionTaint() { this = "Command injection" } } class SqlSanitizer extends Sanitizer { - SqlSanitizer() { this = "SQL sanitizer" } + SqlSanitizer() { this = "SQL sanitizer" } - /** Holds if `test` shows value to be untainted with `taint` */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - exists(FunctionObject f, CallNode call | - f.getName() = "isEscapedSql" and - test.getTest() = call and - call.getAnArg() = test.getSourceVariable().getAUse() and - f.getACall() = call and - test.getSense() = true - ) and - taint instanceof SqlInjectionTaint - } + /** Holds if `test` shows value to be untainted with `taint` */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + exists(FunctionObject f, CallNode call | + f.getName() = "isEscapedSql" and + test.getTest() = call and + call.getAnArg() = test.getSourceVariable().getAUse() and + f.getACall() = call and + test.getSense() = true + ) and + taint instanceof SqlInjectionTaint + } } class CommandSanitizer extends Sanitizer { - CommandSanitizer() { this = "Command sanitizer" } + CommandSanitizer() { this = "Command sanitizer" } - /** Holds if `test` shows value to be untainted with `taint` */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - exists(FunctionObject f | - f.getName() = "isValidCommand" and - f.getACall().(CallNode).getAnArg() = test.getSourceVariable().getAUse() and - test.getSense() = true - ) and - taint instanceof CommandInjectionTaint - } + /** Holds if `test` shows value to be untainted with `taint` */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + exists(FunctionObject f | + f.getName() = "isValidCommand" and + f.getACall().(CallNode).getAnArg() = test.getSourceVariable().getAUse() and + test.getSense() = true + ) and + taint instanceof CommandInjectionTaint + } } class SqlQuery extends TaintSink { - SqlQuery() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "sql_query" and - call.getAnArg() = this - ) - } + SqlQuery() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "sql_query" and + call.getAnArg() = this + ) + } - override string toString() { result = "SQL query" } + override string toString() { result = "SQL query" } - override predicate sinks(TaintKind taint) { taint instanceof SqlInjectionTaint } + override predicate sinks(TaintKind taint) { taint instanceof SqlInjectionTaint } } class OsCommand extends TaintSink { - OsCommand() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "os_command" and - call.getAnArg() = this - ) - } + OsCommand() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "os_command" and + call.getAnArg() = this + ) + } - override string toString() { result = "OS command" } + override string toString() { result = "OS command" } - override predicate sinks(TaintKind taint) { taint instanceof CommandInjectionTaint } + override predicate sinks(TaintKind taint) { taint instanceof CommandInjectionTaint } } class Falsey extends TaintKind { - Falsey() { this = "falsey" } + Falsey() { this = "falsey" } - override boolean booleanValue() { result = false } + override boolean booleanValue() { result = false } } class FalseySource extends TaintSource { - FalseySource() { this.(NameNode).getId() = "FALSEY" } + FalseySource() { this.(NameNode).getId() = "FALSEY" } - override predicate isSourceOf(TaintKind kind) { kind instanceof Falsey } + override predicate isSourceOf(TaintKind kind) { kind instanceof Falsey } - override string toString() { result = "falsey.source" } + override string toString() { result = "falsey.source" } } class TaintIterable extends TaintKind { - TaintIterable() { this = "iterable.simple" } + TaintIterable() { this = "iterable.simple" } - override TaintKind getTaintForIteration() { result instanceof SimpleTest } + override TaintKind getTaintForIteration() { result instanceof SimpleTest } } class TaintIterableSource extends TaintSource { - TaintIterableSource() { this.(NameNode).getId() = "ITERABLE_SOURCE" } + TaintIterableSource() { this.(NameNode).getId() = "ITERABLE_SOURCE" } - override predicate isSourceOf(TaintKind kind) { kind instanceof TaintIterable } + override predicate isSourceOf(TaintKind kind) { kind instanceof TaintIterable } } diff --git a/python/ql/test/library-tests/taint/config/TaintedArgument.ql b/python/ql/test/library-tests/taint/config/TaintedArgument.ql index 0663fce65e1..b8753b3fe00 100644 --- a/python/ql/test/library-tests/taint/config/TaintedArgument.ql +++ b/python/ql/test/library-tests/taint/config/TaintedArgument.ql @@ -4,9 +4,9 @@ import TaintLib import semmle.python.dataflow.Implementation from - TaintTrackingImplementation config, TaintTrackingNode src, CallNode call, - TaintTrackingContext caller, CallableValue pyfunc, int arg, AttributePath path, TaintKind kind + TaintTrackingImplementation config, TaintTrackingNode src, CallNode call, + TaintTrackingContext caller, CallableValue pyfunc, int arg, AttributePath path, TaintKind kind where - config instanceof TestConfig and - config.callWithTaintedArgument(src, call, caller, pyfunc, arg, path, kind) + config instanceof TestConfig and + config.callWithTaintedArgument(src, call, caller, pyfunc, arg, path, kind) select config, src, call, caller, pyfunc, arg, path, kind diff --git a/python/ql/test/library-tests/taint/config/TestNode.ql b/python/ql/test/library-tests/taint/config/TestNode.ql index 688002f3eb0..d45943ddaaa 100644 --- a/python/ql/test/library-tests/taint/config/TestNode.ql +++ b/python/ql/test/library-tests/taint/config/TestNode.ql @@ -6,4 +6,4 @@ import TaintLib from TaintTrackingNode n where n.getConfiguration() instanceof TestConfig select n.getLocation().toString(), n.getTaintKind(), n.getNode().toString(), n.getPath().toString(), - n.getContext().toString() + n.getContext().toString() diff --git a/python/ql/test/library-tests/taint/config/TestSource.ql b/python/ql/test/library-tests/taint/config/TestSource.ql index 45c5dd3ac57..6698d7cb8dc 100644 --- a/python/ql/test/library-tests/taint/config/TestSource.ql +++ b/python/ql/test/library-tests/taint/config/TestSource.ql @@ -5,4 +5,4 @@ import TaintLib from TestConfig config, DataFlow::Node source, TaintKind kind where config.isSource(source, kind) select config, source.getLocation().toString(), source.getLocation().getStartLine(), - source.toString(), kind + source.toString(), kind diff --git a/python/ql/test/library-tests/taint/config/TestStep.ql b/python/ql/test/library-tests/taint/config/TestStep.ql index 2773321d300..c03d5c1ba10 100644 --- a/python/ql/test/library-tests/taint/config/TestStep.ql +++ b/python/ql/test/library-tests/taint/config/TestStep.ql @@ -6,5 +6,5 @@ import semmle.python.dataflow.Implementation from TaintTrackingNode n, TaintTrackingNode s, TestConfig config where s = n.getASuccessor() and config = n.getConfiguration() select config + ":", n.getTaintKind(), n.getLocation().toString(), n.getNode().toString(), - n.getContext(), " --> ", s.getTaintKind(), s.getLocation().toString(), s.getNode().toString(), - s.getContext() + n.getContext(), " --> ", s.getTaintKind(), s.getLocation().toString(), s.getNode().toString(), + s.getContext() diff --git a/python/ql/test/library-tests/taint/dataflow/Config.qll b/python/ql/test/library-tests/taint/dataflow/Config.qll index 34a36dd9e1f..a02d2e0227c 100644 --- a/python/ql/test/library-tests/taint/dataflow/Config.qll +++ b/python/ql/test/library-tests/taint/dataflow/Config.qll @@ -2,14 +2,14 @@ import python import semmle.python.dataflow.DataFlow class TestConfiguration extends DataFlow::Configuration { - TestConfiguration() { this = "Test configuration" } + TestConfiguration() { this = "Test configuration" } - override predicate isSource(ControlFlowNode source) { source.(NameNode).getId() = "SOURCE" } + override predicate isSource(ControlFlowNode source) { source.(NameNode).getId() = "SOURCE" } - override predicate isSink(ControlFlowNode sink) { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "SINK" and - sink = call.getAnArg() - ) - } + override predicate isSink(ControlFlowNode sink) { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "SINK" and + sink = call.getAnArg() + ) + } } diff --git a/python/ql/test/library-tests/taint/dataflow/TestNode.ql b/python/ql/test/library-tests/taint/dataflow/TestNode.ql index 3498d5546da..93b25572a52 100644 --- a/python/ql/test/library-tests/taint/dataflow/TestNode.ql +++ b/python/ql/test/library-tests/taint/dataflow/TestNode.ql @@ -3,4 +3,4 @@ import Config from TaintedNode n select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getNode().getNode().toString(), - n.getContext() + n.getContext() diff --git a/python/ql/test/library-tests/taint/example/DilbertConfig.qll b/python/ql/test/library-tests/taint/example/DilbertConfig.qll index c54ea8060a3..c27c3dfd103 100644 --- a/python/ql/test/library-tests/taint/example/DilbertConfig.qll +++ b/python/ql/test/library-tests/taint/example/DilbertConfig.qll @@ -10,41 +10,41 @@ import semmle.python.dataflow.Configuration /* First of all we set up some TaintKinds */ class Engineer extends TaintKind { - Engineer() { this = "Wally" or this = "Dilbert" } + Engineer() { this = "Wally" or this = "Dilbert" } } class Wally extends Engineer { - Wally() { this = "Wally" } + Wally() { this = "Wally" } } /** Then the configuration */ class DilbertConfig extends TaintTracking::Configuration { - DilbertConfig() { this = "Dilbert config" } + DilbertConfig() { this = "Dilbert config" } - override predicate isSource(DataFlow::Node node, TaintKind kind) { - node.asAstNode().(Name).getId() = "ENGINEER" and kind instanceof Engineer - } + override predicate isSource(DataFlow::Node node, TaintKind kind) { + node.asAstNode().(Name).getId() = "ENGINEER" and kind instanceof Engineer + } - override predicate isSink(DataFlow::Node node, TaintKind kind) { - /* Engineers hate meetings */ - function_param("meeting", node) and kind instanceof Engineer - } + override predicate isSink(DataFlow::Node node, TaintKind kind) { + /* Engineers hate meetings */ + function_param("meeting", node) and kind instanceof Engineer + } - override predicate isBarrier(DataFlow::Node node, TaintKind kind) { - /* There is no way that Wally is working through lunch */ - function_param("lunch", node) and kind instanceof Wally - } + override predicate isBarrier(DataFlow::Node node, TaintKind kind) { + /* There is no way that Wally is working through lunch */ + function_param("lunch", node) and kind instanceof Wally + } - override predicate isBarrier(DataFlow::Node node) { - /* Even the conscientious stop work if the building is on fire */ - function_param("fire", node) - } + override predicate isBarrier(DataFlow::Node node) { + /* Even the conscientious stop work if the building is on fire */ + function_param("fire", node) + } } /** Helper predicate looking for `funcname(..., arg, ...)` */ private predicate function_param(string funcname, DataFlow::Node arg) { - exists(Call call | - call.getFunc().(Name).getId() = funcname and - arg.asAstNode() = call.getAnArg() - ) + exists(Call call | + call.getFunc().(Name).getId() = funcname and + arg.asAstNode() = call.getAnArg() + ) } diff --git a/python/ql/test/library-tests/taint/example/Edges.ql b/python/ql/test/library-tests/taint/example/Edges.ql index 063f4883316..022ae760a55 100644 --- a/python/ql/test/library-tests/taint/example/Edges.ql +++ b/python/ql/test/library-tests/taint/example/Edges.ql @@ -4,34 +4,34 @@ import semmle.python.dataflow.Implementation import DilbertConfig string shortString(TaintTrackingNode n) { - if n.getContext().isTop() - then - result = - n.getLocation().getStartLine() + ": " + n.getNode().toString() + n.getPath().extension() + - " = " + n.getTaintKind() - else - result = - n.getLocation().getStartLine() + ": " + n.getNode().toString() + n.getPath().extension() + - " = " + n.getTaintKind() + " (" + n.getContext().toString() + ")" + if n.getContext().isTop() + then + result = + n.getLocation().getStartLine() + ": " + n.getNode().toString() + n.getPath().extension() + + " = " + n.getTaintKind() + else + result = + n.getLocation().getStartLine() + ": " + n.getNode().toString() + n.getPath().extension() + + " = " + n.getTaintKind() + " (" + n.getContext().toString() + ")" } bindingset[s, len] string ljust(string s, int len) { - result = - s + - " " - .prefix(len - s.length()) + result = + s + + " " + .prefix(len - s.length()) } bindingset[s, len] string format(string s, int len) { - exists(string label | - s = "" and label = "[dataflow]" - or - s != "" and label = s - | - result = ljust(label, len) - ) + exists(string label | + s = "" and label = "[dataflow]" + or + s != "" and label = s + | + result = ljust(label, len) + ) } from TaintTrackingNode p, TaintTrackingNode s, string label diff --git a/python/ql/test/library-tests/taint/example/ExampleConfig.ql b/python/ql/test/library-tests/taint/example/ExampleConfig.ql index e3809c7a024..e406fc73e21 100644 --- a/python/ql/test/library-tests/taint/example/ExampleConfig.ql +++ b/python/ql/test/library-tests/taint/example/ExampleConfig.ql @@ -12,4 +12,4 @@ import semmle.python.security.Paths from DilbertConfig config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "$@ goes to a $@.", src.getNode(), src.getTaintKind().toString(), - sink.getNode(), "meeting" + sink.getNode(), "meeting" diff --git a/python/ql/test/library-tests/taint/example/Nodes.ql b/python/ql/test/library-tests/taint/example/Nodes.ql index c7544767bba..4cdeb13c303 100644 --- a/python/ql/test/library-tests/taint/example/Nodes.ql +++ b/python/ql/test/library-tests/taint/example/Nodes.ql @@ -6,4 +6,4 @@ import DilbertConfig from TaintTrackingNode n where n.getConfiguration() instanceof DilbertConfig select n.getLocation().toString(), n.getNode().toString(), n.getPath().toString(), - n.getContext().toString(), n.getTaintKind() + n.getContext().toString(), n.getTaintKind() diff --git a/python/ql/test/library-tests/taint/exception_traceback/TestSource.ql b/python/ql/test/library-tests/taint/exception_traceback/TestSource.ql index d66d80dae40..d892afe9999 100644 --- a/python/ql/test/library-tests/taint/exception_traceback/TestSource.ql +++ b/python/ql/test/library-tests/taint/exception_traceback/TestSource.ql @@ -4,6 +4,6 @@ import semmle.python.web.HttpResponse from TaintSource src, TaintKind kind where - src.isSourceOf(kind) and - not src.getLocation().getFile().inStdlib() + src.isSourceOf(kind) and + not src.getLocation().getFile().inStdlib() select src.getLocation().toString(), src.(ControlFlowNode).getNode().toString(), kind diff --git a/python/ql/test/library-tests/taint/exception_traceback/TestStep.ql b/python/ql/test/library-tests/taint/exception_traceback/TestStep.ql index 6d10a7c5ed3..b1ab12d0f25 100644 --- a/python/ql/test/library-tests/taint/exception_traceback/TestStep.ql +++ b/python/ql/test/library-tests/taint/exception_traceback/TestStep.ql @@ -4,9 +4,9 @@ import semmle.python.web.HttpResponse from TaintedNode n, TaintedNode s where - s = n.getASuccessor() and - not n.getLocation().getFile().inStdlib() and - not s.getLocation().getFile().inStdlib() + s = n.getASuccessor() and + not n.getLocation().getFile().inStdlib() and + not s.getLocation().getFile().inStdlib() select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getNode().toString(), - n.getContext(), " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), - s.getNode().toString(), s.getContext() + n.getContext(), " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), + s.getNode().toString(), s.getContext() diff --git a/python/ql/test/library-tests/taint/extensions/ExtensionsLib.qll b/python/ql/test/library-tests/taint/extensions/ExtensionsLib.qll index 19e369412ac..08ba0ce6e40 100644 --- a/python/ql/test/library-tests/taint/extensions/ExtensionsLib.qll +++ b/python/ql/test/library-tests/taint/extensions/ExtensionsLib.qll @@ -2,72 +2,72 @@ import python import semmle.python.dataflow.TaintTracking class SimpleTest extends TaintKind { - SimpleTest() { this = "simple.test" } + SimpleTest() { this = "simple.test" } } class SimpleSink extends TaintSink { - override string toString() { result = "Simple sink" } + override string toString() { result = "Simple sink" } - SimpleSink() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "SINK" and - this = call.getAnArg() - ) - } + SimpleSink() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "SINK" and + this = call.getAnArg() + ) + } - override predicate sinks(TaintKind taint) { taint instanceof SimpleTest } + override predicate sinks(TaintKind taint) { taint instanceof SimpleTest } } class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "SOURCE" } + SimpleSource() { this.(NameNode).getId() = "SOURCE" } - override predicate isSourceOf(TaintKind kind) { kind instanceof SimpleTest } + override predicate isSourceOf(TaintKind kind) { kind instanceof SimpleTest } - override string toString() { result = "simple.source" } + override string toString() { result = "simple.source" } } predicate visit_call(CallNode call, FunctionObject func) { - exists(AttrNode attr, ClassObject cls, string name | - name.prefix(6) = "visit_" and - func = cls.lookupAttribute(name) and - attr.getObject("visit").refersTo(_, cls, _) and - attr = call.getFunction() - ) + exists(AttrNode attr, ClassObject cls, string name | + name.prefix(6) = "visit_" and + func = cls.lookupAttribute(name) and + attr.getObject("visit").refersTo(_, cls, _) and + attr = call.getFunction() + ) } /* Test call extensions by tracking taint through visitor methods */ class TestCallReturnExtension extends DataFlowExtension::DataFlowNode { - TestCallReturnExtension() { - exists(PyFunctionObject func | - visit_call(_, func) and - this = func.getAReturnedNode() - ) - } + TestCallReturnExtension() { + exists(PyFunctionObject func | + visit_call(_, func) and + this = func.getAReturnedNode() + ) + } - override ControlFlowNode getAReturnSuccessorNode(CallNode call) { - exists(PyFunctionObject func | - visit_call(call, func) and - this = func.getAReturnedNode() and - result = call - ) - } + override ControlFlowNode getAReturnSuccessorNode(CallNode call) { + exists(PyFunctionObject func | + visit_call(call, func) and + this = func.getAReturnedNode() and + result = call + ) + } } class TestCallParameterExtension extends DataFlowExtension::DataFlowNode { - TestCallParameterExtension() { - exists(PyFunctionObject func, CallNode call | - visit_call(call, func) and - this = call.getAnArg() - ) - } + TestCallParameterExtension() { + exists(PyFunctionObject func, CallNode call | + visit_call(call, func) and + this = call.getAnArg() + ) + } - override ControlFlowNode getACalleeSuccessorNode(CallNode call) { - exists(PyFunctionObject func | - visit_call(call, func) and - exists(int n | - this = call.getArg(n) and - result.getNode() = func.getFunction().getArg(n + 1) - ) - ) - } + override ControlFlowNode getACalleeSuccessorNode(CallNode call) { + exists(PyFunctionObject func | + visit_call(call, func) and + exists(int n | + this = call.getArg(n) and + result.getNode() = func.getFunction().getArg(n + 1) + ) + ) + } } diff --git a/python/ql/test/library-tests/taint/extensions/TestNode.ql b/python/ql/test/library-tests/taint/extensions/TestNode.ql index 2fa17776be3..b884345b69a 100644 --- a/python/ql/test/library-tests/taint/extensions/TestNode.ql +++ b/python/ql/test/library-tests/taint/extensions/TestNode.ql @@ -3,4 +3,4 @@ import ExtensionsLib from TaintedNode n select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getNode().getNode().toString(), - n.getContext() + n.getContext() diff --git a/python/ql/test/library-tests/taint/extensions/TestStep.ql b/python/ql/test/library-tests/taint/extensions/TestStep.ql index 9005aba858e..9defeb85b41 100644 --- a/python/ql/test/library-tests/taint/extensions/TestStep.ql +++ b/python/ql/test/library-tests/taint/extensions/TestStep.ql @@ -4,5 +4,5 @@ import ExtensionsLib from TaintedNode n, TaintedNode s where s = n.getASuccessor() select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getNode().getNode().toString(), - n.getContext(), " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), - s.getNode().getNode().toString(), s.getContext() + n.getContext(), " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), + s.getNode().getNode().toString(), s.getContext() diff --git a/python/ql/test/library-tests/taint/flowpath_regression/Config.qll b/python/ql/test/library-tests/taint/flowpath_regression/Config.qll index 446365b2d12..ae9d0e3c332 100644 --- a/python/ql/test/library-tests/taint/flowpath_regression/Config.qll +++ b/python/ql/test/library-tests/taint/flowpath_regression/Config.qll @@ -3,43 +3,43 @@ import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class FooSource extends TaintSource { - FooSource() { this.(CallNode).getFunction().(NameNode).getId() = "foo_source" } + FooSource() { this.(CallNode).getFunction().(NameNode).getId() = "foo_source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof UntrustedStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof UntrustedStringKind } - override string toString() { result = "FooSource" } + override string toString() { result = "FooSource" } } class FooSink extends TaintSink { - FooSink() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "foo_sink" and - call.getAnArg() = this - ) - } + FooSink() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "foo_sink" and + call.getAnArg() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof UntrustedStringKind } + override predicate sinks(TaintKind kind) { kind instanceof UntrustedStringKind } - override string toString() { result = "FooSink" } + override string toString() { result = "FooSink" } } class FooConfig extends TaintTracking::Configuration { - FooConfig() { this = "FooConfig" } + FooConfig() { this = "FooConfig" } - override predicate isSource(TaintTracking::Source source) { source instanceof FooSource } + override predicate isSource(TaintTracking::Source source) { source instanceof FooSource } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof FooSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof FooSink } } class BarSink extends TaintSink { - BarSink() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "bar_sink" and - call.getAnArg() = this - ) - } + BarSink() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "bar_sink" and + call.getAnArg() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof UntrustedStringKind } + override predicate sinks(TaintKind kind) { kind instanceof UntrustedStringKind } - override string toString() { result = "BarSink" } + override string toString() { result = "BarSink" } } diff --git a/python/ql/test/library-tests/taint/general/Contexts.ql b/python/ql/test/library-tests/taint/general/Contexts.ql index 6eee5f449b2..509c8c938f0 100644 --- a/python/ql/test/library-tests/taint/general/Contexts.ql +++ b/python/ql/test/library-tests/taint/general/Contexts.ql @@ -4,6 +4,6 @@ import TaintLib from CallContext context, Scope s where - exists(CallContext caller | caller.getCallee(_) = context) and - context.appliesToScope(s) + exists(CallContext caller | caller.getCallee(_) = context) and + context.appliesToScope(s) select s.getLocation().toString(), context, s.toString() diff --git a/python/ql/test/library-tests/taint/general/ParamSource.ql b/python/ql/test/library-tests/taint/general/ParamSource.ql index 192de466882..7ac2b7699ef 100644 --- a/python/ql/test/library-tests/taint/general/ParamSource.ql +++ b/python/ql/test/library-tests/taint/general/ParamSource.ql @@ -4,36 +4,36 @@ import semmle.python.dataflow.TaintTracking import semmle.python.security.injection.Command class TestKind extends TaintKind { - TestKind() { this = "test" } + TestKind() { this = "test" } } class CustomSource extends TaintSource { - CustomSource() { - exists(Parameter p | - p.asName().getId() = "arg" and - this.(ControlFlowNode).getNode() = p - ) - } + CustomSource() { + exists(Parameter p | + p.asName().getId() = "arg" and + this.(ControlFlowNode).getNode() = p + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof TestKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof TestKind } - override string toString() { result = "Source of untrusted input" } + override string toString() { result = "Source of untrusted input" } } class SimpleSink extends TaintSink { - override string toString() { result = "Simple sink" } + override string toString() { result = "Simple sink" } - SimpleSink() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "SINK" and - this = call.getAnArg() - ) - } + SimpleSink() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "SINK" and + this = call.getAnArg() + ) + } - override predicate sinks(TaintKind taint) { taint instanceof TestKind } + override predicate sinks(TaintKind taint) { taint instanceof TestKind } } from TaintSource src, TaintSink sink, TaintKind srckind, TaintKind sinkkind where src.flowsToSink(srckind, sink) and sink.sinks(sinkkind) select srckind, src.getLocation().toString(), sink.getLocation().getStartLine(), - sink.(ControlFlowNode).getNode().toString(), sinkkind + sink.(ControlFlowNode).getNode().toString(), sinkkind diff --git a/python/ql/test/library-tests/taint/general/TaintConsistency.ql b/python/ql/test/library-tests/taint/general/TaintConsistency.ql index d07828e5947..8cef7c378a4 100644 --- a/python/ql/test/library-tests/taint/general/TaintConsistency.ql +++ b/python/ql/test/library-tests/taint/general/TaintConsistency.ql @@ -4,26 +4,26 @@ import semmle.python.dataflow.Implementation import TaintLib from - TaintKind taint, TaintTrackingContext c, DataFlow::Node n, string what, - TaintTrackingImplementation impl + TaintKind taint, TaintTrackingContext c, DataFlow::Node n, string what, + TaintTrackingImplementation impl where - not exists(TaintedNode t | t.getTaintKind() = taint and t.getNode() = n and t.getContext() = c) and - ( - impl.flowStep(_, n, c, _, taint, _) and what = "missing node at end of step" - or - impl.flowSource(n, c, _, taint) and what = "missing node for source" - ) + not exists(TaintedNode t | t.getTaintKind() = taint and t.getNode() = n and t.getContext() = c) and + ( + impl.flowStep(_, n, c, _, taint, _) and what = "missing node at end of step" or - exists(TaintedNode t | t.getTaintKind() = taint and t.getNode() = n and t.getContext() = c | - not impl.flowStep(_, n, c, _, taint, _) and - not impl.flowSource(n, c, _, taint) and - what = "TaintedNode with no reason" - or - impl.flowStep(t, n, c, _, taint, _) and what = "step ends where it starts" - or - impl.flowStep(t, _, _, _, _, _) and - not impl.flowStep(_, n, c, _, taint, _) and - not impl.flowSource(n, c, _, taint) and - what = "No predecessor and not a source" - ) + impl.flowSource(n, c, _, taint) and what = "missing node for source" + ) + or + exists(TaintedNode t | t.getTaintKind() = taint and t.getNode() = n and t.getContext() = c | + not impl.flowStep(_, n, c, _, taint, _) and + not impl.flowSource(n, c, _, taint) and + what = "TaintedNode with no reason" + or + impl.flowStep(t, n, c, _, taint, _) and what = "step ends where it starts" + or + impl.flowStep(t, _, _, _, _, _) and + not impl.flowStep(_, n, c, _, taint, _) and + not impl.flowSource(n, c, _, taint) and + what = "No predecessor and not a source" + ) select n.getLocation(), taint, c, n.toString(), what diff --git a/python/ql/test/library-tests/taint/general/TaintLib.qll b/python/ql/test/library-tests/taint/general/TaintLib.qll index d0e8b9902ec..af3799c3b95 100644 --- a/python/ql/test/library-tests/taint/general/TaintLib.qll +++ b/python/ql/test/library-tests/taint/general/TaintLib.qll @@ -2,279 +2,279 @@ import python import semmle.python.dataflow.TaintTracking class SimpleTest extends TaintKind { - SimpleTest() { this = "simple.test" } + SimpleTest() { this = "simple.test" } } class SimpleSink extends TaintSink { - override string toString() { result = "Simple sink" } + override string toString() { result = "Simple sink" } - SimpleSink() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "SINK" and - this = call.getAnArg() - ) - } + SimpleSink() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "SINK" and + this = call.getAnArg() + ) + } - override predicate sinks(TaintKind taint) { taint instanceof SimpleTest } + override predicate sinks(TaintKind taint) { taint instanceof SimpleTest } } class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "SOURCE" } + SimpleSource() { this.(NameNode).getId() = "SOURCE" } - override predicate isSourceOf(TaintKind kind) { kind instanceof SimpleTest } + override predicate isSourceOf(TaintKind kind) { kind instanceof SimpleTest } - override string toString() { result = "simple.source" } + override string toString() { result = "simple.source" } } class SimpleSanitizer extends Sanitizer { - SimpleSanitizer() { this = "Simple sanitizer" } + SimpleSanitizer() { this = "Simple sanitizer" } - override predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { - node.(CallNode).getFunction().(NameNode).getId() = "SANITIZE" and - taint instanceof SimpleTest - } + override predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { + node.(CallNode).getFunction().(NameNode).getId() = "SANITIZE" and + taint instanceof SimpleTest + } - override predicate sanitizingDefinition(TaintKind taint, EssaDefinition def) { - exists(CallNode call | - def.(ArgumentRefinement).getInput().getAUse() = call.getAnArg() and - call.getFunction().(NameNode).getId() = "SANITIZE" - ) and - taint instanceof SimpleTest - } + override predicate sanitizingDefinition(TaintKind taint, EssaDefinition def) { + exists(CallNode call | + def.(ArgumentRefinement).getInput().getAUse() = call.getAnArg() and + call.getFunction().(NameNode).getId() = "SANITIZE" + ) and + taint instanceof SimpleTest + } } class BasicCustomTaint extends TaintKind { - BasicCustomTaint() { this = "basic.custom" } + BasicCustomTaint() { this = "basic.custom" } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - tonode.(CallNode).getAnArg() = fromnode and - tonode.(CallNode).getFunction().(NameNode).getId() = "TAINT_FROM_ARG" and - result = this - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + tonode.(CallNode).getAnArg() = fromnode and + tonode.(CallNode).getFunction().(NameNode).getId() = "TAINT_FROM_ARG" and + result = this + } } class BasicCustomSink extends TaintSink { - override string toString() { result = "Basic custom sink" } + override string toString() { result = "Basic custom sink" } - BasicCustomSink() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "CUSTOM_SINK" and - this = call.getAnArg() - ) - } + BasicCustomSink() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "CUSTOM_SINK" and + this = call.getAnArg() + ) + } - override predicate sinks(TaintKind taint) { taint instanceof BasicCustomTaint } + override predicate sinks(TaintKind taint) { taint instanceof BasicCustomTaint } } class BasicCustomSource extends TaintSource { - BasicCustomSource() { this.(NameNode).getId() = "CUSTOM_SOURCE" } + BasicCustomSource() { this.(NameNode).getId() = "CUSTOM_SOURCE" } - override predicate isSourceOf(TaintKind kind) { kind instanceof BasicCustomTaint } + override predicate isSourceOf(TaintKind kind) { kind instanceof BasicCustomTaint } - override string toString() { result = "basic.custom.source" } + override string toString() { result = "basic.custom.source" } } class Rock extends TaintKind { - Rock() { this = "rock" } + Rock() { this = "rock" } - override TaintKind getTaintOfMethodResult(string name) { - name = "prev" and result instanceof Scissors - } + override TaintKind getTaintOfMethodResult(string name) { + name = "prev" and result instanceof Scissors + } - predicate isSink(ControlFlowNode sink) { - exists(CallNode call | - call.getArg(0) = sink and - call.getFunction().(NameNode).getId() = "paper" - ) - } + predicate isSink(ControlFlowNode sink) { + exists(CallNode call | + call.getArg(0) = sink and + call.getFunction().(NameNode).getId() = "paper" + ) + } } class Paper extends TaintKind { - Paper() { this = "paper" } + Paper() { this = "paper" } - override TaintKind getTaintOfMethodResult(string name) { - name = "prev" and result instanceof Rock - } + override TaintKind getTaintOfMethodResult(string name) { + name = "prev" and result instanceof Rock + } - predicate isSink(ControlFlowNode sink) { - exists(CallNode call | - call.getArg(0) = sink and - call.getFunction().(NameNode).getId() = "scissors" - ) - } + predicate isSink(ControlFlowNode sink) { + exists(CallNode call | + call.getArg(0) = sink and + call.getFunction().(NameNode).getId() = "scissors" + ) + } } class Scissors extends TaintKind { - Scissors() { this = "scissors" } + Scissors() { this = "scissors" } - override TaintKind getTaintOfMethodResult(string name) { - name = "prev" and result instanceof Paper - } + override TaintKind getTaintOfMethodResult(string name) { + name = "prev" and result instanceof Paper + } - predicate isSink(ControlFlowNode sink) { - exists(CallNode call | - call.getArg(0) = sink and - call.getFunction().(NameNode).getId() = "rock" - ) - } + predicate isSink(ControlFlowNode sink) { + exists(CallNode call | + call.getArg(0) = sink and + call.getFunction().(NameNode).getId() = "rock" + ) + } } class RockPaperScissorSource extends TaintSource { - RockPaperScissorSource() { - exists(string name | this.(NameNode).getId() = name | - name = "ROCK" or name = "PAPER" or name = "SCISSORS" - ) - } + RockPaperScissorSource() { + exists(string name | this.(NameNode).getId() = name | + name = "ROCK" or name = "PAPER" or name = "SCISSORS" + ) + } - override predicate isSourceOf(TaintKind kind) { kind = this.(NameNode).getId().toLowerCase() } + override predicate isSourceOf(TaintKind kind) { kind = this.(NameNode).getId().toLowerCase() } - override string toString() { result = "rock.paper.scissors.source" } + override string toString() { result = "rock.paper.scissors.source" } } private predicate function_param(string funcname, ControlFlowNode arg) { - exists(FunctionObject f | - f.getName() = funcname and - arg = f.getArgumentForCall(_, _) - ) + exists(FunctionObject f | + f.getName() = funcname and + arg = f.getArgumentForCall(_, _) + ) } class RockPaperScissorSink extends TaintSink { - RockPaperScissorSink() { - exists(string name | function_param(name, this) | - name = "rock" or name = "paper" or name = "scissors" - ) - } + RockPaperScissorSink() { + exists(string name | function_param(name, this) | + name = "rock" or name = "paper" or name = "scissors" + ) + } - override predicate sinks(TaintKind taint) { - exists(string name | function_param(name, this) | - name = "paper" and taint = "rock" - or - name = "rock" and taint = "scissors" - or - name = "scissors" and taint = "paper" - ) - } + override predicate sinks(TaintKind taint) { + exists(string name | function_param(name, this) | + name = "paper" and taint = "rock" + or + name = "rock" and taint = "scissors" + or + name = "scissors" and taint = "paper" + ) + } - override string toString() { result = "rock.paper.scissors.sink" } + override string toString() { result = "rock.paper.scissors.sink" } } class TaintCarrier extends TaintKind { - TaintCarrier() { this = "explicit.carrier" } + TaintCarrier() { this = "explicit.carrier" } - override TaintKind getTaintOfMethodResult(string name) { - name = "get_taint" and result instanceof SimpleTest - } + override TaintKind getTaintOfMethodResult(string name) { + name = "get_taint" and result instanceof SimpleTest + } } /* There is no sink for `TaintCarrier`. It is not "dangerous" in itself; it merely holds a `SimpleTest`. */ class TaintCarrierSource extends TaintSource { - TaintCarrierSource() { this.(NameNode).getId() = "TAINT_CARRIER_SOURCE" } + TaintCarrierSource() { this.(NameNode).getId() = "TAINT_CARRIER_SOURCE" } - override predicate isSourceOf(TaintKind kind) { kind instanceof TaintCarrier } + override predicate isSourceOf(TaintKind kind) { kind instanceof TaintCarrier } - override string toString() { result = "taint.carrier.source" } + override string toString() { result = "taint.carrier.source" } } /* Some more realistic examples */ abstract class UserInput extends TaintKind { - bindingset[this] - UserInput() { any() } + bindingset[this] + UserInput() { any() } } class UserInputSource extends TaintSource { - UserInputSource() { this.(CallNode).getFunction().(NameNode).getId() = "user_input" } + UserInputSource() { this.(CallNode).getFunction().(NameNode).getId() = "user_input" } - override predicate isSourceOf(TaintKind kind) { kind instanceof UserInput } + override predicate isSourceOf(TaintKind kind) { kind instanceof UserInput } - override string toString() { result = "user.input.source" } + override string toString() { result = "user.input.source" } } class SqlInjectionTaint extends UserInput { - SqlInjectionTaint() { this = "SQL injection" } + SqlInjectionTaint() { this = "SQL injection" } } class CommandInjectionTaint extends UserInput { - CommandInjectionTaint() { this = "Command injection" } + CommandInjectionTaint() { this = "Command injection" } } class SqlSanitizer extends Sanitizer { - SqlSanitizer() { this = "SQL sanitizer" } + SqlSanitizer() { this = "SQL sanitizer" } - /** Holds if `test` shows value to be untainted with `taint` */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - exists(FunctionObject f, CallNode call | - f.getName() = "isEscapedSql" and - test.getTest() = call and - call.getAnArg() = test.getSourceVariable().getAUse() and - f.getACall() = call and - test.getSense() = true - ) and - taint instanceof SqlInjectionTaint - } + /** Holds if `test` shows value to be untainted with `taint` */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + exists(FunctionObject f, CallNode call | + f.getName() = "isEscapedSql" and + test.getTest() = call and + call.getAnArg() = test.getSourceVariable().getAUse() and + f.getACall() = call and + test.getSense() = true + ) and + taint instanceof SqlInjectionTaint + } } class CommandSanitizer extends Sanitizer { - CommandSanitizer() { this = "Command sanitizer" } + CommandSanitizer() { this = "Command sanitizer" } - /** Holds if `test` shows value to be untainted with `taint` */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - exists(FunctionObject f | - f.getName() = "isValidCommand" and - f.getACall().(CallNode).getAnArg() = test.getSourceVariable().getAUse() and - test.getSense() = true - ) and - taint instanceof CommandInjectionTaint - } + /** Holds if `test` shows value to be untainted with `taint` */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + exists(FunctionObject f | + f.getName() = "isValidCommand" and + f.getACall().(CallNode).getAnArg() = test.getSourceVariable().getAUse() and + test.getSense() = true + ) and + taint instanceof CommandInjectionTaint + } } class SqlQuery extends TaintSink { - SqlQuery() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "sql_query" and - call.getAnArg() = this - ) - } + SqlQuery() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "sql_query" and + call.getAnArg() = this + ) + } - override string toString() { result = "SQL query" } + override string toString() { result = "SQL query" } - override predicate sinks(TaintKind taint) { taint instanceof SqlInjectionTaint } + override predicate sinks(TaintKind taint) { taint instanceof SqlInjectionTaint } } class OsCommand extends TaintSink { - OsCommand() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "os_command" and - call.getAnArg() = this - ) - } + OsCommand() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "os_command" and + call.getAnArg() = this + ) + } - override string toString() { result = "OS command" } + override string toString() { result = "OS command" } - override predicate sinks(TaintKind taint) { taint instanceof CommandInjectionTaint } + override predicate sinks(TaintKind taint) { taint instanceof CommandInjectionTaint } } class Falsey extends TaintKind { - Falsey() { this = "falsey" } + Falsey() { this = "falsey" } - override boolean booleanValue() { result = false } + override boolean booleanValue() { result = false } } class FalseySource extends TaintSource { - FalseySource() { this.(NameNode).getId() = "FALSEY" } + FalseySource() { this.(NameNode).getId() = "FALSEY" } - override predicate isSourceOf(TaintKind kind) { kind instanceof Falsey } + override predicate isSourceOf(TaintKind kind) { kind instanceof Falsey } - override string toString() { result = "falsey.source" } + override string toString() { result = "falsey.source" } } class TaintIterable extends TaintKind { - TaintIterable() { this = "iterable.simple" } + TaintIterable() { this = "iterable.simple" } - override TaintKind getTaintForIteration() { result instanceof SimpleTest } + override TaintKind getTaintForIteration() { result instanceof SimpleTest } } class TaintIterableSource extends TaintSource { - TaintIterableSource() { this.(NameNode).getId() = "ITERABLE_SOURCE" } + TaintIterableSource() { this.(NameNode).getId() = "ITERABLE_SOURCE" } - override predicate isSourceOf(TaintKind kind) { kind instanceof TaintIterable } + override predicate isSourceOf(TaintKind kind) { kind instanceof TaintIterable } } diff --git a/python/ql/test/library-tests/taint/general/TestDefn.ql b/python/ql/test/library-tests/taint/general/TestDefn.ql index e2791bf2e72..242ab7018a8 100644 --- a/python/ql/test/library-tests/taint/general/TestDefn.ql +++ b/python/ql/test/library-tests/taint/general/TestDefn.ql @@ -4,4 +4,4 @@ import TaintLib from EssaNodeDefinition defn, TaintedNode n where n.getNode().asVariable() = defn.getVariable() select defn.getLocation().toString(), defn.getRepresentation(), n.getLocation().toString(), - "Taint " + n.toString(), defn.getDefiningNode().getNode().toString() + "Taint " + n.toString(), defn.getDefiningNode().getNode().toString() diff --git a/python/ql/test/library-tests/taint/general/TestSink.ql b/python/ql/test/library-tests/taint/general/TestSink.ql index 2405ee3af06..0ad7df6a560 100644 --- a/python/ql/test/library-tests/taint/general/TestSink.ql +++ b/python/ql/test/library-tests/taint/general/TestSink.ql @@ -5,4 +5,4 @@ import TaintLib from TaintSource src, TaintSink sink, TaintKind srckind, TaintKind sinkkind where src.flowsToSink(srckind, sink) and sink.sinks(sinkkind) select srckind, src.getLocation().toString(), sink.getLocation().getStartLine(), - sink.(ControlFlowNode).getNode().toString(), sinkkind + sink.(ControlFlowNode).getNode().toString(), sinkkind diff --git a/python/ql/test/library-tests/taint/general/TestStep.ql b/python/ql/test/library-tests/taint/general/TestStep.ql index 5274cd0af44..82ca707013e 100644 --- a/python/ql/test/library-tests/taint/general/TestStep.ql +++ b/python/ql/test/library-tests/taint/general/TestStep.ql @@ -5,4 +5,4 @@ import TaintLib from TaintedNode n, TaintedNode s where s = n.getASuccessor() select n.toString(), n.getLocation().toString(), n.getNode().toString(), n.getContext(), "-->", - s.toString(), s.getLocation().toString(), s.getNode().toString(), s.getContext() + s.toString(), s.getLocation().toString(), s.getNode().toString(), s.getContext() diff --git a/python/ql/test/library-tests/taint/general/TestTaint.ql b/python/ql/test/library-tests/taint/general/TestTaint.ql index 7c513d7b52c..79c350d9aa2 100644 --- a/python/ql/test/library-tests/taint/general/TestTaint.ql +++ b/python/ql/test/library-tests/taint/general/TestTaint.ql @@ -4,16 +4,16 @@ import TaintLib from Call call, Expr arg, string taint_string where - call.getLocation().getFile().getShortName() = "assignment.py" and - call.getFunc().(Name).getId() = "test" and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "NO TAINT" - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) + call.getLocation().getFile().getShortName() = "assignment.py" and + call.getFunc().(Name).getId() = "test" and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "NO TAINT" + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) + ) select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/library-tests/taint/general/TestVar.ql b/python/ql/test/library-tests/taint/general/TestVar.ql index 991d3cdbfa4..28fd4fa74ac 100644 --- a/python/ql/test/library-tests/taint/general/TestVar.ql +++ b/python/ql/test/library-tests/taint/general/TestVar.ql @@ -4,4 +4,4 @@ import TaintLib from EssaVariable var, TaintedNode n where n.getNode().asVariable() = var select var.getDefinition().getLocation().toString(), var.getRepresentation(), - n.getLocation().toString(), "Taint " + n.toString() + n.getLocation().toString(), "Taint " + n.toString() diff --git a/python/ql/test/library-tests/taint/namedtuple/Taint.qll b/python/ql/test/library-tests/taint/namedtuple/Taint.qll index bb40491c202..0dc3c71ec84 100644 --- a/python/ql/test/library-tests/taint/namedtuple/Taint.qll +++ b/python/ql/test/library-tests/taint/namedtuple/Taint.qll @@ -3,43 +3,43 @@ import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } + SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "taint source" } + override string toString() { result = "taint source" } } class ListSource extends TaintSource { - ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } + ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } - override string toString() { result = "list taint source" } + override string toString() { result = "list taint source" } } class DictSource extends TaintSource { - DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } + DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - override string toString() { result = "dict taint source" } + override string toString() { result = "dict taint source" } } class TestConfig extends TaintTracking::Configuration { - TestConfig() { this = "TestConfig" } + TestConfig() { this = "TestConfig" } - override predicate isSanitizer(Sanitizer sanitizer) { - sanitizer instanceof UrlsplitUrlparseTempSanitizer - } + override predicate isSanitizer(Sanitizer sanitizer) { + sanitizer instanceof UrlsplitUrlparseTempSanitizer + } - override predicate isSource(TaintTracking::Source source) { - source instanceof SimpleSource - or - source instanceof ListSource - or - source instanceof DictSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof SimpleSource + or + source instanceof ListSource + or + source instanceof DictSource + } - override predicate isSink(TaintTracking::Sink sink) { none() } + override predicate isSink(TaintTracking::Sink sink) { none() } } diff --git a/python/ql/test/library-tests/taint/namedtuple/TestTaint.ql b/python/ql/test/library-tests/taint/namedtuple/TestTaint.ql index fb1d102aa7a..47883578516 100644 --- a/python/ql/test/library-tests/taint/namedtuple/TestTaint.ql +++ b/python/ql/test/library-tests/taint/namedtuple/TestTaint.ql @@ -4,16 +4,16 @@ import Taint from Call call, Expr arg, string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - call.getFunc().(Name).getId() = "test" and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "NO TAINT" - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) + call.getLocation().getFile().getShortName() = "test.py" and + call.getFunc().(Name).getId() = "test" and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "NO TAINT" + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) + ) select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/library-tests/taint/strings/Taint.qll b/python/ql/test/library-tests/taint/strings/Taint.qll index 3840df662ef..3368a1c4f70 100644 --- a/python/ql/test/library-tests/taint/strings/Taint.qll +++ b/python/ql/test/library-tests/taint/strings/Taint.qll @@ -4,41 +4,41 @@ import semmle.python.security.strings.Untrusted import semmle.python.security.Exceptions class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } + SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "taint source" } + override string toString() { result = "taint source" } } class ListSource extends TaintSource { - ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } + ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } - override string toString() { result = "list taint source" } + override string toString() { result = "list taint source" } } class DictSource extends TaintSource { - DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } + DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - override string toString() { result = "dict taint source" } + override string toString() { result = "dict taint source" } } class ExceptionInfoSource extends TaintSource { - ExceptionInfoSource() { this.(NameNode).getId() = "TAINTED_EXCEPTION_INFO" } + ExceptionInfoSource() { this.(NameNode).getId() = "TAINTED_EXCEPTION_INFO" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfo } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfo } - override string toString() { result = "Exception info source" } + override string toString() { result = "Exception info source" } } class ExternalFileObjectSource extends TaintSource { - ExternalFileObjectSource() { this.(NameNode).getId() = "TAINTED_FILE" } + ExternalFileObjectSource() { this.(NameNode).getId() = "TAINTED_FILE" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalFileObject } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalFileObject } - override string toString() { result = "Tainted file source" } + override string toString() { result = "Tainted file source" } } diff --git a/python/ql/test/library-tests/taint/strings/TestStep.ql b/python/ql/test/library-tests/taint/strings/TestStep.ql index 7e42b878e74..177edce3498 100644 --- a/python/ql/test/library-tests/taint/strings/TestStep.ql +++ b/python/ql/test/library-tests/taint/strings/TestStep.ql @@ -4,8 +4,8 @@ import Taint from TaintedNode n, TaintedNode s where - n.getLocation().getFile().getShortName() = "test.py" and - s.getLocation().getFile().getShortName() = "test.py" and - s = n.getASuccessor() + n.getLocation().getFile().getShortName() = "test.py" and + s.getLocation().getFile().getShortName() = "test.py" and + s = n.getASuccessor() select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getAstNode(), n.getContext(), - " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), s.getAstNode(), s.getContext() + " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), s.getAstNode(), s.getContext() diff --git a/python/ql/test/library-tests/taint/strings/TestTaint.ql b/python/ql/test/library-tests/taint/strings/TestTaint.ql index fb1d102aa7a..47883578516 100644 --- a/python/ql/test/library-tests/taint/strings/TestTaint.ql +++ b/python/ql/test/library-tests/taint/strings/TestTaint.ql @@ -4,16 +4,16 @@ import Taint from Call call, Expr arg, string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - call.getFunc().(Name).getId() = "test" and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "NO TAINT" - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) + call.getLocation().getFile().getShortName() = "test.py" and + call.getFunc().(Name).getId() = "test" and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "NO TAINT" + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) + ) select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/library-tests/taint/unpacking/Taint.qll b/python/ql/test/library-tests/taint/unpacking/Taint.qll index 21e16aabac5..010b9738c5c 100644 --- a/python/ql/test/library-tests/taint/unpacking/Taint.qll +++ b/python/ql/test/library-tests/taint/unpacking/Taint.qll @@ -3,25 +3,25 @@ import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } + SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "taint source" } + override string toString() { result = "taint source" } } class ListSource extends TaintSource { - ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } + ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } - override string toString() { result = "list taint source" } + override string toString() { result = "list taint source" } } class DictSource extends TaintSource { - DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } + DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - override string toString() { result = "dict taint source" } + override string toString() { result = "dict taint source" } } diff --git a/python/ql/test/library-tests/taint/unpacking/TestStep.ql b/python/ql/test/library-tests/taint/unpacking/TestStep.ql index 7e42b878e74..177edce3498 100644 --- a/python/ql/test/library-tests/taint/unpacking/TestStep.ql +++ b/python/ql/test/library-tests/taint/unpacking/TestStep.ql @@ -4,8 +4,8 @@ import Taint from TaintedNode n, TaintedNode s where - n.getLocation().getFile().getShortName() = "test.py" and - s.getLocation().getFile().getShortName() = "test.py" and - s = n.getASuccessor() + n.getLocation().getFile().getShortName() = "test.py" and + s.getLocation().getFile().getShortName() = "test.py" and + s = n.getASuccessor() select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getAstNode(), n.getContext(), - " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), s.getAstNode(), s.getContext() + " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), s.getAstNode(), s.getContext() diff --git a/python/ql/test/library-tests/taint/unpacking/TestTaint.ql b/python/ql/test/library-tests/taint/unpacking/TestTaint.ql index fb1d102aa7a..47883578516 100644 --- a/python/ql/test/library-tests/taint/unpacking/TestTaint.ql +++ b/python/ql/test/library-tests/taint/unpacking/TestTaint.ql @@ -4,16 +4,16 @@ import Taint from Call call, Expr arg, string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - call.getFunc().(Name).getId() = "test" and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "NO TAINT" - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) + call.getLocation().getFile().getShortName() = "test.py" and + call.getFunc().(Name).getId() = "test" and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "NO TAINT" + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) + ) select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/library-tests/thrift/Function.ql b/python/ql/test/library-tests/thrift/Function.ql index 2161fd8ec8a..59411777280 100644 --- a/python/ql/test/library-tests/thrift/Function.ql +++ b/python/ql/test/library-tests/thrift/Function.ql @@ -2,9 +2,9 @@ import external.Thrift from ThriftFunction t, string n, ThriftElement x where - exists(int i | x = t.getArgument(i) and n = i.toString()) - or - x = t.getAThrows() and n = "throws" - or - x = t.getReturnType() and n = "returns" + exists(int i | x = t.getArgument(i) and n = i.toString()) + or + x = t.getAThrows() and n = "throws" + or + x = t.getReturnType() and n = "returns" select t, n, x diff --git a/python/ql/test/library-tests/types/attributes/Test.ql b/python/ql/test/library-tests/types/attributes/Test.ql index a012b0d3a15..9e066a6414b 100644 --- a/python/ql/test/library-tests/types/attributes/Test.ql +++ b/python/ql/test/library-tests/types/attributes/Test.ql @@ -3,4 +3,4 @@ import python from ClassObject cls, ClassObject start, string name, Object val where not name.substring(0, 2) = "__" and val = cls.lookupMro(start, name) select cls.getOrigin().getLocation().getStartLine(), cls.toString(), start.toString(), name, - val.toString(), val.getOrigin().getLocation().getStartLine() + val.toString(), val.getOrigin().getLocation().getStartLine() diff --git a/python/ql/test/library-tests/types/classattr/ClassAttribute.ql b/python/ql/test/library-tests/types/classattr/ClassAttribute.ql index 6e023dcada5..d0633a36e3a 100644 --- a/python/ql/test/library-tests/types/classattr/ClassAttribute.ql +++ b/python/ql/test/library-tests/types/classattr/ClassAttribute.ql @@ -8,11 +8,11 @@ import python from ClassObject cls, string name, string kind where - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") and - ( - cls.hasAttribute(name) and kind = "has" - or - cls.declaresAttribute(name) and kind = "declares" - ) + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") and + ( + cls.hasAttribute(name) and kind = "has" + or + cls.declaresAttribute(name) and kind = "declares" + ) select cls.toString(), kind, name diff --git a/python/ql/test/library-tests/types/classattr/ClassMember.ql b/python/ql/test/library-tests/types/classattr/ClassMember.ql index 1357deb0da9..d1e136a5108 100644 --- a/python/ql/test/library-tests/types/classattr/ClassMember.ql +++ b/python/ql/test/library-tests/types/classattr/ClassMember.ql @@ -8,11 +8,11 @@ import python from ClassObject cls, string name, string kind, Object o where - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") and - ( - o = cls.lookupAttribute(name) and kind = "has" - or - o = cls.declaredAttribute(name) and kind = "declares" - ) + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") and + ( + o = cls.lookupAttribute(name) and kind = "has" + or + o = cls.declaredAttribute(name) and kind = "declares" + ) select cls.toString(), kind, name, o.toString() diff --git a/python/ql/test/library-tests/types/classattr/SpecialAttribute.ql b/python/ql/test/library-tests/types/classattr/SpecialAttribute.ql index 26e9ad08c26..dc21b250c1e 100644 --- a/python/ql/test/library-tests/types/classattr/SpecialAttribute.ql +++ b/python/ql/test/library-tests/types/classattr/SpecialAttribute.ql @@ -2,12 +2,12 @@ import python from ClassObject cls, string name, string kind, Object o where - not cls.isC() and - name.matches("\\_\\_%\\_\\_") and - not o = theObjectType().lookupAttribute(name) and - ( - o = cls.lookupAttribute(name) and kind = "has" - or - o = cls.declaredAttribute(name) and kind = "declares" - ) + not cls.isC() and + name.matches("\\_\\_%\\_\\_") and + not o = theObjectType().lookupAttribute(name) and + ( + o = cls.lookupAttribute(name) and kind = "has" + or + o = cls.declaredAttribute(name) and kind = "declares" + ) select cls.toString(), kind, name, o.toString() diff --git a/python/ql/test/library-tests/types/exceptions/Impossible.ql b/python/ql/test/library-tests/types/exceptions/Impossible.ql index 69c658edba6..787ba8898f9 100644 --- a/python/ql/test/library-tests/types/exceptions/Impossible.ql +++ b/python/ql/test/library-tests/types/exceptions/Impossible.ql @@ -2,18 +2,18 @@ import python from RaisingNode r, ControlFlowNode n, string kind where - r.unlikelySuccessor(n) and - ( - r.getATrueSuccessor() = n and kind = "true" - or - r.getAFalseSuccessor() = n and kind = "false" - or - r.getAnExceptionalSuccessor() = n and kind = "exceptional" - or - not r.getATrueSuccessor() = n and - not r.getAFalseSuccessor() = n and - not r.getAnExceptionalSuccessor() = n and - kind = "normal" - ) + r.unlikelySuccessor(n) and + ( + r.getATrueSuccessor() = n and kind = "true" + or + r.getAFalseSuccessor() = n and kind = "false" + or + r.getAnExceptionalSuccessor() = n and kind = "exceptional" + or + not r.getATrueSuccessor() = n and + not r.getAFalseSuccessor() = n and + not r.getAnExceptionalSuccessor() = n and + kind = "normal" + ) select r.getLocation().getStartLine(), n.getLocation().getStartLine(), r.getNode().toString(), - n.getNode().toString(), kind + n.getNode().toString(), kind diff --git a/python/ql/test/library-tests/types/exceptions/LineRaises.ql b/python/ql/test/library-tests/types/exceptions/LineRaises.ql index 933eb8d59bb..de8b834e520 100644 --- a/python/ql/test/library-tests/types/exceptions/LineRaises.ql +++ b/python/ql/test/library-tests/types/exceptions/LineRaises.ql @@ -2,11 +2,11 @@ import python from RaisingNode r, string type where - type = r.getARaisedType().toString() - or - type = "Unknown" and r.raisesUnknownType() - or - not exists(r.getARaisedType()) and - not r.raisesUnknownType() and - type = "None" + type = r.getARaisedType().toString() + or + type = "Unknown" and r.raisesUnknownType() + or + not exists(r.getARaisedType()) and + not r.raisesUnknownType() and + type = "None" select r.getNode().getLocation().getStartLine(), type diff --git a/python/ql/test/library-tests/types/exceptions/Raises.ql b/python/ql/test/library-tests/types/exceptions/Raises.ql index aa477f718a2..2415c707967 100644 --- a/python/ql/test/library-tests/types/exceptions/Raises.ql +++ b/python/ql/test/library-tests/types/exceptions/Raises.ql @@ -2,11 +2,11 @@ import python from PyFunctionObject f, string type where - type = f.getARaisedType().toString() - or - type = "Unknown" and f.raisesUnknownType() - or - not exists(f.getARaisedType()) and - not f.raisesUnknownType() and - type = "None" + type = f.getARaisedType().toString() + or + type = "Unknown" and f.raisesUnknownType() + or + not exists(f.getARaisedType()) and + not f.raisesUnknownType() and + type = "None" select f.toString(), type diff --git a/python/ql/test/library-tests/types/exceptions/Viable.ql b/python/ql/test/library-tests/types/exceptions/Viable.ql index ed388e2faf2..bf00a0675d6 100644 --- a/python/ql/test/library-tests/types/exceptions/Viable.ql +++ b/python/ql/test/library-tests/types/exceptions/Viable.ql @@ -3,4 +3,4 @@ import python from RaisingNode r, ControlFlowNode n, ClassObject ex where r.viableExceptionEdge_objectapi(n, ex) select r.getLocation().getStartLine(), n.getLocation().getStartLine(), r.getNode().toString(), - n.getNode().toString(), ex.toString() + n.getNode().toString(), ex.toString() diff --git a/python/ql/test/library-tests/variables/scopes/free.ql b/python/ql/test/library-tests/variables/scopes/free.ql index 65789e76a52..cd5e0325de0 100644 --- a/python/ql/test/library-tests/variables/scopes/free.ql +++ b/python/ql/test/library-tests/variables/scopes/free.ql @@ -2,7 +2,7 @@ import python from LocalVariable v, Scope inner where - v.escapes() and - inner = v.getAnAccess().getScope() and - inner != v.getScope() + v.escapes() and + inner = v.getAnAccess().getScope() and + inner != v.getScope() select v.toString(), v.getScope().toString(), inner.toString() diff --git a/python/ql/test/library-tests/variables/scopes/locals.ql b/python/ql/test/library-tests/variables/scopes/locals.ql index 62814925fe9..35f29176653 100644 --- a/python/ql/test/library-tests/variables/scopes/locals.ql +++ b/python/ql/test/library-tests/variables/scopes/locals.ql @@ -2,7 +2,7 @@ import python from LocalVariable l, string kind where - l instanceof FastLocalVariable and kind = "fast" - or - l instanceof NameLocalVariable and kind = "name" + l instanceof FastLocalVariable and kind = "fast" + or + l instanceof NameLocalVariable and kind = "name" select l, l.getScope(), kind diff --git a/python/ql/test/library-tests/variables/scopes/lookup.ql b/python/ql/test/library-tests/variables/scopes/lookup.ql index 248cd62b911..84dfaac48b5 100644 --- a/python/ql/test/library-tests/variables/scopes/lookup.ql +++ b/python/ql/test/library-tests/variables/scopes/lookup.ql @@ -2,17 +2,17 @@ import python from NameNode n, string l where - n.isLoad() and - ( - n.isGlobal() and l = "global" - or - n.isLocal() and l = "local" - or - n.isNonLocal() and l = "non-local" - or - not n.isGlobal() and - not n.isLocal() and - not n.isNonLocal() and - l = "none" - ) + n.isLoad() and + ( + n.isGlobal() and l = "global" + or + n.isLocal() and l = "local" + or + n.isNonLocal() and l = "non-local" + or + not n.isGlobal() and + not n.isLocal() and + not n.isNonLocal() and + l = "none" + ) select n.getLocation().getStartLine(), n.toString(), l diff --git a/python/ql/test/library-tests/web/stdlib/HttpSources.ql b/python/ql/test/library-tests/web/stdlib/HttpSources.ql index 3b91a26d14f..f4e9a1b48d3 100644 --- a/python/ql/test/library-tests/web/stdlib/HttpSources.ql +++ b/python/ql/test/library-tests/web/stdlib/HttpSources.ql @@ -4,6 +4,6 @@ import semmle.python.security.strings.Untrusted from HttpRequestTaintSource source, TaintKind kind where - source.isSourceOf(kind) and - source.getLocation().getFile().getShortName() != "cgi.py" + source.isSourceOf(kind) and + source.getLocation().getFile().getShortName() != "cgi.py" select source.(ControlFlowNode).getNode(), kind diff --git a/python/ql/test/library-tests/web/stdlib/TestTaint.ql b/python/ql/test/library-tests/web/stdlib/TestTaint.ql index 1ac84c3d290..cd2b08ef235 100644 --- a/python/ql/test/library-tests/web/stdlib/TestTaint.ql +++ b/python/ql/test/library-tests/web/stdlib/TestTaint.ql @@ -4,29 +4,29 @@ import semmle.python.web.HttpRequest import semmle.python.security.strings.Untrusted from - Call call, Expr arg, boolean expected_taint, boolean has_taint, string test_res, - string taint_string + Call call, Expr arg, boolean expected_taint, boolean has_taint, string test_res, + string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - ( - call.getFunc().(Name).getId() = "ensure_tainted" and - expected_taint = true - or - call.getFunc().(Name).getId() = "ensure_not_tainted" and - expected_taint = false + call.getLocation().getFile().getShortName() = "test.py" and + ( + call.getFunc().(Name).getId() = "ensure_tainted" and + expected_taint = true + or + call.getFunc().(Name).getId() = "ensure_not_tainted" and + expected_taint = false + ) and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "" and + has_taint = false + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "" and - has_taint = false - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) and - has_taint = true - ) and - if expected_taint = has_taint then test_res = "ok " else test_res = "fail" + has_taint = true + ) and + if expected_taint = has_taint then test_res = "ok " else test_res = "fail" // if expected_taint = has_taint then test_res = "✓" else test_res = "✕" select arg.getLocation().toString(), test_res, call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/library-tests/web/zope/Test.ql b/python/ql/test/library-tests/web/zope/Test.ql index e694883237b..ca705ac292e 100644 --- a/python/ql/test/library-tests/web/zope/Test.ql +++ b/python/ql/test/library-tests/web/zope/Test.ql @@ -3,8 +3,8 @@ import semmle.python.TestUtils from ControlFlowNode f, Value v, ControlFlowNode x where - exists(ExprStmt s | s.getValue().getAFlowNode() = f) and - f.pointsTo(v, x) and - f.getLocation().getFile().getBaseName() = "test.py" + exists(ExprStmt s | s.getValue().getAFlowNode() = f) and + f.pointsTo(v, x) and + f.getLocation().getFile().getBaseName() = "test.py" select f.getLocation().getStartLine(), f.toString(), v.toString(), - remove_library_prefix(x.getLocation()) + remove_library_prefix(x.getLocation()) diff --git a/python/ql/test/query-tests/Metrics/ratios/CodeRatio.ql b/python/ql/test/query-tests/Metrics/ratios/CodeRatio.ql index 8525edcb8b8..745568f2405 100644 --- a/python/ql/test/query-tests/Metrics/ratios/CodeRatio.ql +++ b/python/ql/test/query-tests/Metrics/ratios/CodeRatio.ql @@ -3,4 +3,4 @@ import python from Module m, ModuleMetrics mm where mm = m.getMetrics() and mm.getNumberOfLines() > 0 select m, 100.0 * (mm.getNumberOfLinesOfCode().(float) / mm.getNumberOfLines().(float)) as ratio - order by ratio desc + order by ratio desc diff --git a/python/ql/test/query-tests/Resources/Dataflow.ql b/python/ql/test/query-tests/Resources/Dataflow.ql index 4e2cf15b50d..fad31d80ec1 100644 --- a/python/ql/test/query-tests/Resources/Dataflow.ql +++ b/python/ql/test/query-tests/Resources/Dataflow.ql @@ -3,12 +3,12 @@ import Resources.FileOpen from EssaVariable v, EssaDefinition def, string open, string exit where - def = v.getDefinition() and - v.getSourceVariable().getName().charAt(0) = "f" and - ( - var_is_open(v, _) and open = "open" - or - not var_is_open(v, _) and open = "closed" - ) and - if BaseFlow::reaches_exit(v) then exit = "exit" else exit = "" + def = v.getDefinition() and + v.getSourceVariable().getName().charAt(0) = "f" and + ( + var_is_open(v, _) and open = "open" + or + not var_is_open(v, _) and open = "closed" + ) and + if BaseFlow::reaches_exit(v) then exit = "exit" else exit = "" select v.getRepresentation() + " = " + v.getDefinition().getRepresentation(), open, exit diff --git a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql index 1c5aa49030e..e6d27a1fda8 100644 --- a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql +++ b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql @@ -1,82 +1,82 @@ class Location extends @location { - /** Gets the start line of this location */ - int getStartLine() { - locations_default(this, _, result, _, _, _) or - locations_ast(this, _, result, _, _, _) - } + /** Gets the start line of this location */ + int getStartLine() { + locations_default(this, _, result, _, _, _) or + locations_ast(this, _, result, _, _, _) + } - /** Gets the start column of this location */ - int getStartColumn() { - locations_default(this, _, _, result, _, _) or - locations_ast(this, _, _, result, _, _) - } + /** Gets the start column of this location */ + int getStartColumn() { + locations_default(this, _, _, result, _, _) or + locations_ast(this, _, _, result, _, _) + } - string toString() { result = "" + ":" + this.getStartLine().toString() } + string toString() { result = "" + ":" + this.getStartLine().toString() } } class Expr_ extends @py_expr { - string toString() { result = "Expr" } + string toString() { result = "Expr" } - Location getLocation() { py_locations(result, this) } + Location getLocation() { py_locations(result, this) } } class ExprParent_ extends @py_expr_parent { - string toString() { result = "ExprParent" } + string toString() { result = "ExprParent" } } class ExprList_ extends @py_expr_list { - /** Gets the nth item of this expression list */ - Expr_ getItem(int index) { py_exprs(result, _, this, index) } + /** Gets the nth item of this expression list */ + Expr_ getItem(int index) { py_exprs(result, _, this, index) } - string toString() { result = "ExprList" } + string toString() { result = "ExprList" } } class Parameter_ extends @py_parameter { - string toString() { result = "Parameter" } + string toString() { result = "Parameter" } - Location getLocation() { result = this.(Expr_).getLocation() } + Location getLocation() { result = this.(Expr_).getLocation() } } class ParameterList extends @py_parameter_list { - /** Gets the nth parameter */ - Parameter_ getItem(int index) { - /* Item can be a Name or a Tuple, both of which are expressions */ - py_exprs(result, _, this, index) - } + /** Gets the nth parameter */ + Parameter_ getItem(int index) { + /* Item can be a Name or a Tuple, both of which are expressions */ + py_exprs(result, _, this, index) + } - string toString() { result = "ParameterList" } + string toString() { result = "ParameterList" } } class Arguments_ extends @py_arguments { - /** Gets the keyword-only default values of this parameters definition. */ - ExprList_ getKwDefaults() { py_expr_lists(result, this, 0) } + /** Gets the keyword-only default values of this parameters definition. */ + ExprList_ getKwDefaults() { py_expr_lists(result, this, 0) } - /** Gets the nth keyword-only default value of this parameters definition. */ - Expr_ getKwDefault(int index) { result = this.getKwDefaults().getItem(index) } + /** Gets the nth keyword-only default value of this parameters definition. */ + Expr_ getKwDefault(int index) { result = this.getKwDefaults().getItem(index) } - /** Gets the default values of this parameters definition. */ - ExprList_ getDefaults() { py_expr_lists(result, this, 1) } + /** Gets the default values of this parameters definition. */ + ExprList_ getDefaults() { py_expr_lists(result, this, 1) } - /** Gets the nth default value of this parameters definition. */ - Expr_ getDefault(int index) { result = this.getDefaults().getItem(index) } + /** Gets the nth default value of this parameters definition. */ + Expr_ getDefault(int index) { result = this.getDefaults().getItem(index) } - string toString() { result = "Arguments" } + string toString() { result = "Arguments" } } class Function_ extends @py_Function { - /** Gets the positional parameter list of this function. */ - ParameterList getArgs() { py_parameter_lists(result, this) } + /** Gets the positional parameter list of this function. */ + ParameterList getArgs() { py_parameter_lists(result, this) } - /** Gets the nth positional parameter of this function. */ - Parameter_ getArg(int index) { result = this.getArgs().getItem(index) } + /** Gets the nth positional parameter of this function. */ + Parameter_ getArg(int index) { result = this.getArgs().getItem(index) } - /** Gets the keyword-only parameter list of this function. */ - ExprList_ getKwonlyargs() { py_expr_lists(result, this, 3) } + /** Gets the keyword-only parameter list of this function. */ + ExprList_ getKwonlyargs() { py_expr_lists(result, this, 3) } - /** Gets the nth keyword-only parameter of this function. */ - Expr_ getKwonlyarg(int index) { result = this.getKwonlyargs().getItem(index) } + /** Gets the nth keyword-only parameter of this function. */ + Expr_ getKwonlyarg(int index) { result = this.getKwonlyargs().getItem(index) } - string toString() { result = "Function" } + string toString() { result = "Function" } } /** @@ -85,77 +85,77 @@ class Function_ extends @py_Function { * hierarchy slightly different (that's why it's called Adjusted) */ abstract class CallableExprAdjusted extends Expr_ { - /** - * Gets The default values and annotations (type-hints) for the arguments of this callable. - * - * This predicate is called getArgs(), rather than getParameters() for compatibility with Python's AST module. - */ - abstract Arguments_ getArgs(); + /** + * Gets The default values and annotations (type-hints) for the arguments of this callable. + * + * This predicate is called getArgs(), rather than getParameters() for compatibility with Python's AST module. + */ + abstract Arguments_ getArgs(); - /** Gets the function scope of this code expression. */ - abstract Function_ getInnerScope(); + /** Gets the function scope of this code expression. */ + abstract Function_ getInnerScope(); } class Lambda_ extends @py_Lambda, CallableExprAdjusted, Expr_ { - /** Gets the arguments of this lambda expression. */ - override Arguments_ getArgs() { py_arguments(result, this) } + /** Gets the arguments of this lambda expression. */ + override Arguments_ getArgs() { py_arguments(result, this) } - /** Gets the function scope of this lambda expression. */ - override Function_ getInnerScope() { py_Functions(result, this) } + /** Gets the function scope of this lambda expression. */ + override Function_ getInnerScope() { py_Functions(result, this) } - override string toString() { result = "Lambda" } + override string toString() { result = "Lambda" } } class FunctionExpr_ extends @py_FunctionExpr, CallableExprAdjusted, Expr_ { - /** Gets the parameters of this function definition. */ - override Arguments_ getArgs() { py_arguments(result, this) } + /** Gets the parameters of this function definition. */ + override Arguments_ getArgs() { py_arguments(result, this) } - /** Gets the function scope of this function definition. */ - override Function_ getInnerScope() { py_Functions(result, this) } + /** Gets the function scope of this function definition. */ + override Function_ getInnerScope() { py_Functions(result, this) } - override string toString() { result = "FunctionExpr" } + override string toString() { result = "FunctionExpr" } } - /* * This upgrade changes the *layout* of the default values for parameters, by * making `Argument.getKwDefault(i)` return the default value for keyword-only parameter `i` * (instead of the i'th default for a keyword-only parameter). `Argument.getDefault` is * changed in the same manner to keep consistency. */ + from Expr_ expr, int kind, ExprParent_ parent, int oldidx, int newidx where - py_exprs(expr, kind, parent, oldidx) and - ( - // expr is not a parameter default - not exists(Arguments_ args | args.getDefault(oldidx) = expr) and - not exists(Arguments_ args | args.getKwDefault(oldidx) = expr) and - newidx = oldidx - or - // expr is a default for a normal parameter - exists(Arguments_ args, CallableExprAdjusted callable | - callable.getArgs() = args and - args.getDefault(oldidx) = expr and - newidx = oldidx + count(callable.getInnerScope().getArg(_)) - count(args.getDefault(_)) - ) - or - // expr is a default for a keyword-only parameter. - // before this upgrade, we would not always attach the default value to the correct keyword-only parameter, - // to fix this, we calculate the new index based on the source location of the default value (a default value - // must belong to the parameter that was defined immediately before the default value). - exists(Arguments_ args, CallableExprAdjusted callable | - callable.getArgs() = args and - args.getKwDefault(oldidx) = expr and - newidx = - // the last parameter to be defined before this default value - max(int i | - exists(Parameter_ param | param = callable.getInnerScope().getKwonlyarg(i) | - param.getLocation().getStartLine() < expr.getLocation().getStartLine() - or - param.getLocation().getStartLine() = expr.getLocation().getStartLine() and - param.getLocation().getStartColumn() < expr.getLocation().getStartColumn() - ) - ) + py_exprs(expr, kind, parent, oldidx) and + ( + // expr is not a parameter default + not exists(Arguments_ args | args.getDefault(oldidx) = expr) and + not exists(Arguments_ args | args.getKwDefault(oldidx) = expr) and + newidx = oldidx + or + // expr is a default for a normal parameter + exists(Arguments_ args, CallableExprAdjusted callable | + callable.getArgs() = args and + args.getDefault(oldidx) = expr and + newidx = oldidx + count(callable.getInnerScope().getArg(_)) - count(args.getDefault(_)) + ) + or + // expr is a default for a keyword-only parameter. + // before this upgrade, we would not always attach the default value to the correct keyword-only parameter, + // to fix this, we calculate the new index based on the source location of the default value (a default value + // must belong to the parameter that was defined immediately before the default value). + exists(Arguments_ args, CallableExprAdjusted callable | + callable.getArgs() = args and + args.getKwDefault(oldidx) = expr and + newidx = + // the last parameter to be defined before this default value + max(int i | + exists(Parameter_ param | param = callable.getInnerScope().getKwonlyarg(i) | + param.getLocation().getStartLine() < expr.getLocation().getStartLine() + or + param.getLocation().getStartLine() = expr.getLocation().getStartLine() and + param.getLocation().getStartColumn() < expr.getLocation().getStartColumn() + ) ) ) + ) select expr, kind, parent, newidx From 45eccb25216a07f9290199fe3cabafbfaef30bc2 Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Tue, 7 Jul 2020 17:01:17 +0200 Subject: [PATCH 34/40] Python: Fix test failures. --- python/ql/test/library-tests/PointsTo/new/SSA.expected | 2 +- python/ql/test/library-tests/taint/extensions/TestStep.expected | 2 +- python/ql/test/library-tests/taint/general/Contexts.expected | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/python/ql/test/library-tests/PointsTo/new/SSA.expected b/python/ql/test/library-tests/PointsTo/new/SSA.expected index fb0b2156ecc..56ec51dfcd5 100644 --- a/python/ql/test/library-tests/PointsTo/new/SSA.expected +++ b/python/ql/test/library-tests/PointsTo/new/SSA.expected @@ -1,4 +1,4 @@ -WARNING: Predicate ssa_variable_points_to has been deprecated and may be removed in future (SSA.ql:10,5-37) +WARNING: Predicate ssa_variable_points_to has been deprecated and may be removed in future (SSA.ql:10,3-35) | __init__.py:0 | __name___0 = ScopeEntryDefinition | 'code' | builtin-class str | | __init__.py:0 | __name___0 = ScopeEntryDefinition | 'code.package' | builtin-class str | | __init__.py:0 | __name___0 = ScopeEntryDefinition | 'code.test_package' | builtin-class str | diff --git a/python/ql/test/library-tests/taint/extensions/TestStep.expected b/python/ql/test/library-tests/taint/extensions/TestStep.expected index c5915950d98..15863a8db4e 100644 --- a/python/ql/test/library-tests/taint/extensions/TestStep.expected +++ b/python/ql/test/library-tests/taint/extensions/TestStep.expected @@ -1,5 +1,5 @@ WARNING: Predicate getNode has been deprecated and may be removed in future (TestStep.ql:6,77-84) -WARNING: Predicate getNode has been deprecated and may be removed in future (TestStep.ql:8,17-24) +WARNING: Predicate getNode has been deprecated and may be removed in future (TestStep.ql:8,15-22) | Taint simple.test | visitor.py:10 | arg | p2 = simple.test | --> | Taint simple.test | visitor.py:13 | arg | p2 = simple.test | | Taint simple.test | visitor.py:18 | arg | | --> | Taint simple.test | visitor.py:19 | arg | | | Taint simple.test | visitor.py:19 | arg | | --> | Taint simple.test | visitor.py:26 | Attribute() | | diff --git a/python/ql/test/library-tests/taint/general/Contexts.expected b/python/ql/test/library-tests/taint/general/Contexts.expected index cfce11fbaa9..82be163c39c 100644 --- a/python/ql/test/library-tests/taint/general/Contexts.expected +++ b/python/ql/test/library-tests/taint/general/Contexts.expected @@ -1,5 +1,5 @@ WARNING: Type CallContext has been deprecated and may be removed in future (Contexts.ql:5,6-17) -WARNING: Type CallContext has been deprecated and may be removed in future (Contexts.ql:7,12-23) +WARNING: Type CallContext has been deprecated and may be removed in future (Contexts.ql:7,10-21) | assignment.py:1 | p0 = simple.test | Function test | | assignment.py:1 | p1 = simple.test | Function test | | assignment.py:1 | p2 = simple.test | Function test | From 583f7f914e09bf0ee565cd1cfcaae5df2fe97e23 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Tue, 7 Jul 2020 17:22:30 +0200 Subject: [PATCH 35/40] Drop taint tracking for Arrays.{setAll, parallelSetAll, parallelPrefix} --- .../src/semmle/code/java/dataflow/internal/ContainerFlow.qll | 2 +- .../local-additional-taint/localAdditionalTaintStep.expected | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll index 8625bad0089..a7127b48356 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll @@ -204,7 +204,7 @@ private predicate taintPreservingArgToArg(Method method, int input, int output) or method.getDeclaringType().hasQualifiedName("java.util", "Arrays") and ( - method.hasName(["fill", "parallelPrefix", "parallelSetAll", "setAll"]) and + method.hasName("fill") and output = 0 and input = method.getNumberOfParameters() - 1 ) diff --git a/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected b/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected index 392a9312097..b4ca4db0716 100644 --- a/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected +++ b/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected @@ -12,16 +12,12 @@ | ArraysTest.java:14:19:14:24 | source | ArraysTest.java:14:3:14:25 | toString(...) | | ArraysTest.java:15:23:15:29 | "value" | ArraysTest.java:15:15:15:20 | source [post update] | | ArraysTest.java:16:30:16:35 | "data" | ArraysTest.java:16:15:16:20 | source [post update] | -| ArraysTest.java:17:33:17:47 | ...->... | ArraysTest.java:17:25:17:30 | source [post update] | | ArraysTest.java:17:43:17:43 | x | ArraysTest.java:17:43:17:47 | ... + ... | | ArraysTest.java:17:47:17:47 | y | ArraysTest.java:17:43:17:47 | ... + ... | -| ArraysTest.java:18:40:18:54 | ...->... | ArraysTest.java:18:25:18:30 | source [post update] | | ArraysTest.java:18:50:18:50 | x | ArraysTest.java:18:50:18:54 | ... + ... | | ArraysTest.java:18:54:18:54 | y | ArraysTest.java:18:50:18:54 | ... + ... | -| ArraysTest.java:19:33:19:56 | ...->... | ArraysTest.java:19:25:19:30 | source [post update] | | ArraysTest.java:19:38:19:44 | Integer | ArraysTest.java:19:38:19:56 | toString(...) | | ArraysTest.java:19:55:19:55 | x | ArraysTest.java:19:38:19:56 | toString(...) | -| ArraysTest.java:20:25:20:48 | ...->... | ArraysTest.java:20:17:20:22 | source [post update] | | ArraysTest.java:20:30:20:36 | Integer | ArraysTest.java:20:30:20:48 | toString(...) | | ArraysTest.java:20:47:20:47 | x | ArraysTest.java:20:30:20:48 | toString(...) | | CollectionsTest.java:8:28:8:32 | "one" | CollectionsTest.java:8:3:8:33 | new ..[] { .. } | From 940fec5669e345fd9de3005cdd463c8ed717d84e Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Tue, 7 Jul 2020 17:26:49 +0200 Subject: [PATCH 36/40] Drop taint tracking for Arrays.{deepToString,toString} --- .../ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll | 2 +- .../local-additional-taint/localAdditionalTaintStep.expected | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll index a7127b48356..8e2305faeb4 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll @@ -183,7 +183,7 @@ private predicate taintPreservingArgumentToMethod(Method method, int arg) { or method.getDeclaringType().hasQualifiedName("java.util", "Arrays") and ( - method.hasName(["copyOf", "copyOfRange", "deepToString", "spliterator", "stream", "toString"]) and + method.hasName(["copyOf", "copyOfRange", "spliterator", "stream"]) and arg = 0 ) } diff --git a/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected b/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected index b4ca4db0716..4de5be40177 100644 --- a/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected +++ b/java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected @@ -6,10 +6,8 @@ | ArraysTest.java:8:24:8:30 | "three" | ArraysTest.java:8:3:8:31 | new ..[] { .. } | | ArraysTest.java:9:17:9:22 | source | ArraysTest.java:9:3:9:27 | copyOf(...) | | ArraysTest.java:10:22:10:27 | source | ArraysTest.java:10:3:10:35 | copyOfRange(...) | -| ArraysTest.java:11:23:11:28 | source | ArraysTest.java:11:3:11:29 | deepToString(...) | | ArraysTest.java:12:22:12:27 | source | ArraysTest.java:12:3:12:28 | spliterator(...) | | ArraysTest.java:13:17:13:22 | source | ArraysTest.java:13:3:13:23 | stream(...) | -| ArraysTest.java:14:19:14:24 | source | ArraysTest.java:14:3:14:25 | toString(...) | | ArraysTest.java:15:23:15:29 | "value" | ArraysTest.java:15:15:15:20 | source [post update] | | ArraysTest.java:16:30:16:35 | "data" | ArraysTest.java:16:15:16:20 | source [post update] | | ArraysTest.java:17:43:17:43 | x | ArraysTest.java:17:43:17:47 | ... + ... | From 7306f58e574666dbc5b954598051c2ffbc6489ea Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 7 Jul 2020 19:44:43 +0200 Subject: [PATCH 37/40] Python: Fix experimental tests --- python/ql/src/experimental/CWE-643/xpath.ql | 1 + .../semmle/python/security/injection/Xpath.qll | 4 ++-- python/ql/test/experimental/CWE-091/Xslt.qlref | 2 +- python/ql/test/experimental/CWE-643/xpath.qlref | 2 +- python/ql/test/experimental/CWE-643/xpathSinks.expected | 8 ++++---- python/ql/test/experimental/CWE-643/xpathSinks.ql | 1 + 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/python/ql/src/experimental/CWE-643/xpath.ql b/python/ql/src/experimental/CWE-643/xpath.ql index fbdf57d4f1a..15720c408ee 100644 --- a/python/ql/src/experimental/CWE-643/xpath.ql +++ b/python/ql/src/experimental/CWE-643/xpath.ql @@ -12,6 +12,7 @@ import python import semmle.python.security.Paths +import semmle.python.security.strings.Untrusted /* Sources */ import semmle.python.web.HttpRequest /* Sinks */ diff --git a/python/ql/src/experimental/semmle/python/security/injection/Xpath.qll b/python/ql/src/experimental/semmle/python/security/injection/Xpath.qll index 01a3e6de38d..fa5c7647f1f 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/Xpath.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/Xpath.qll @@ -22,14 +22,14 @@ module XpathInjection { abstract class XpathInjectionSink extends TaintSink { } /** - * A Sink representing an argument to the `etree.Xpath` call. + * A Sink representing an argument to the `etree.XPath` call. * * from lxml import etree * root = etree.XML("") * find_text = etree.XPath("`sink`") */ private class EtreeXpathArgument extends XpathInjectionSink { - override string toString() { result = "lxml.etree.Xpath" } + override string toString() { result = "lxml.etree.XPath" } EtreeXpathArgument() { exists(CallNode call | call.getFunction().(AttrNode).getObject("XPath").pointsTo(etree()) | diff --git a/python/ql/test/experimental/CWE-091/Xslt.qlref b/python/ql/test/experimental/CWE-091/Xslt.qlref index 32605307db8..27123a448a7 100644 --- a/python/ql/test/experimental/CWE-091/Xslt.qlref +++ b/python/ql/test/experimental/CWE-091/Xslt.qlref @@ -1 +1 @@ -experimental/CWE-643/Xslt.ql +experimental/CWE-091/Xslt.ql diff --git a/python/ql/test/experimental/CWE-643/xpath.qlref b/python/ql/test/experimental/CWE-643/xpath.qlref index 61dcb500e5e..e569931999c 100644 --- a/python/ql/test/experimental/CWE-643/xpath.qlref +++ b/python/ql/test/experimental/CWE-643/xpath.qlref @@ -1 +1 @@ -experimental/CWE-643/xpath.ql \ No newline at end of file +experimental/CWE-643/xpath.ql diff --git a/python/ql/test/experimental/CWE-643/xpathSinks.expected b/python/ql/test/experimental/CWE-643/xpathSinks.expected index c5d2000ab52..c3bfec2fcaf 100644 --- a/python/ql/test/experimental/CWE-643/xpathSinks.expected +++ b/python/ql/test/experimental/CWE-643/xpathSinks.expected @@ -1,12 +1,12 @@ | xpath.py:8:20:8:29 | lxml.etree.parse.xpath | externally controlled string | -| xpath.py:13:29:13:38 | lxml.etree.Xpath | externally controlled string | -| xpath.py:19:29:19:38 | lxml.etree.Xpath | externally controlled string | +| xpath.py:13:29:13:38 | lxml.etree.XPath | externally controlled string | +| xpath.py:19:29:19:38 | lxml.etree.XPath | externally controlled string | | xpath.py:25:38:25:46 | lxml.etree.ETXpath | externally controlled string | | xpath.py:32:29:32:34 | libxml2.parseFile.xpathEval | externally controlled string | | xpathBad.py:13:20:13:43 | lxml.etree.parse.xpath | externally controlled string | | xpathFlow.py:14:20:14:29 | lxml.etree.parse.xpath | externally controlled string | -| xpathFlow.py:23:29:23:38 | lxml.etree.Xpath | externally controlled string | -| xpathFlow.py:32:29:32:38 | lxml.etree.Xpath | externally controlled string | +| xpathFlow.py:23:29:23:38 | lxml.etree.XPath | externally controlled string | +| xpathFlow.py:32:29:32:38 | lxml.etree.XPath | externally controlled string | | xpathFlow.py:41:31:41:40 | lxml.etree.ETXpath | externally controlled string | | xpathFlow.py:49:29:49:38 | libxml2.parseFile.xpathEval | externally controlled string | | xpathGood.py:13:20:13:37 | lxml.etree.parse.xpath | externally controlled string | diff --git a/python/ql/test/experimental/CWE-643/xpathSinks.ql b/python/ql/test/experimental/CWE-643/xpathSinks.ql index 8a96e90035c..a9e5aaae427 100644 --- a/python/ql/test/experimental/CWE-643/xpathSinks.ql +++ b/python/ql/test/experimental/CWE-643/xpathSinks.ql @@ -1,5 +1,6 @@ import python import experimental.semmle.python.security.injection.Xpath +import semmle.python.security.strings.Untrusted from XpathInjection::XpathInjectionSink sink, TaintKind kind where sink.sinks(kind) From 00a61816c08590c9524a760e5466ac738a01b3bc Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Tue, 7 Jul 2020 22:37:58 +0200 Subject: [PATCH 38/40] Improve VariableAssign.getSource documentation --- java/ql/src/semmle/code/java/Expr.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/semmle/code/java/Expr.qll b/java/ql/src/semmle/code/java/Expr.qll index 12a232bc4b0..63fc61bf1aa 100755 --- a/java/ql/src/semmle/code/java/Expr.qll +++ b/java/ql/src/semmle/code/java/Expr.qll @@ -1243,7 +1243,7 @@ class VariableAssign extends VariableUpdate { } /** - * Gets the source of this assignment, if any. + * Gets the source (right-hand side) of this assignment, if any. * * An initialization in a `CatchClause` or `EnhancedForStmt` is implicit and * does not have a source. From 40b9d34ab91c1c75ef4af98bee2d6244d6f2a639 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 8 Jul 2020 09:57:48 +0200 Subject: [PATCH 39/40] Java: Consolidate springframework-5.2.3 stubs --- .../experimental/query-tests/security/CWE-016/options | 2 +- .../experimental/query-tests/security/CWE-074/options | 2 +- .../experimental/query-tests/security/CWE-917/options | 2 +- .../springframework/web/bind/annotation/RequestParam.java | 8 -------- .../org/springframework/beans/factory/BeanFactory.java | 0 .../beans/factory/HierarchicalBeanFactory.java | 0 .../springframework/beans/factory/InitializingBean.java | 0 .../beans/factory/ListableBeanFactory.java | 0 .../autoconfigure/security/servlet/EndpointRequest.java | 0 .../servlet/ApplicationContextRequestMatcher.java | 0 .../org/springframework/context/ApplicationContext.java | 0 .../context/ApplicationEventPublisher.java | 0 .../org/springframework/context/MessageSource.java | 0 .../org/springframework/core/env/EnvironmentCapable.java | 0 .../org/springframework/core/io/ResourceLoader.java | 0 .../core/io/support/ResourcePatternResolver.java | 0 .../org/springframework/jndi/JndiTemplate.java | 0 .../org/springframework/security/config/Customizer.java | 0 .../annotation/AbstractConfiguredSecurityBuilder.java | 0 .../config/annotation/AbstractSecurityBuilder.java | 0 .../security/config/annotation/SecurityBuilder.java | 0 .../security/config/annotation/SecurityConfigurer.java | 0 .../config/annotation/SecurityConfigurerAdapter.java | 0 .../annotation/web/AbstractRequestMatcherRegistry.java | 0 .../config/annotation/web/HttpSecurityBuilder.java | 0 .../config/annotation/web/builders/HttpSecurity.java | 0 .../AbstractConfigAttributeRequestMatcherRegistry.java | 0 .../web/configurers/AbstractHttpConfigurer.java | 0 .../web/configurers/AbstractInterceptUrlConfigurer.java | 0 .../configurers/ExpressionUrlAuthorizationConfigurer.java | 0 .../security/web/DefaultSecurityFilterChain.java | 0 .../springframework/security/web/SecurityFilterChain.java | 0 .../security/web/util/matcher/RequestMatcher.java | 0 .../web/context/WebApplicationContext.java | 0 34 files changed, 3 insertions(+), 11 deletions(-) delete mode 100644 java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/web/bind/annotation/RequestParam.java rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/beans/factory/BeanFactory.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/beans/factory/HierarchicalBeanFactory.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/beans/factory/InitializingBean.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/beans/factory/ListableBeanFactory.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/boot/security/servlet/ApplicationContextRequestMatcher.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/context/ApplicationContext.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/context/ApplicationEventPublisher.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/context/MessageSource.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/core/env/EnvironmentCapable.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/core/io/ResourceLoader.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/core/io/support/ResourcePatternResolver.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/jndi/JndiTemplate.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/Customizer.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/AbstractSecurityBuilder.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityBuilder.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityConfigurer.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityConfigurerAdapter.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/HttpSecurityBuilder.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/builders/HttpSecurity.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractConfigAttributeRequestMatcherRegistry.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractHttpConfigurer.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractInterceptUrlConfigurer.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/web/DefaultSecurityFilterChain.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/web/SecurityFilterChain.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/security/web/util/matcher/RequestMatcher.java (100%) rename java/ql/test/{experimental => }/stubs/springframework-5.2.3/org/springframework/web/context/WebApplicationContext.java (100%) diff --git a/java/ql/test/experimental/query-tests/security/CWE-016/options b/java/ql/test/experimental/query-tests/security/CWE-016/options index aeef8fc5abc..3ebc054c664 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-016/options +++ b/java/ql/test/experimental/query-tests/security/CWE-016/options @@ -1 +1 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/springframework-5.2.3 +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3 diff --git a/java/ql/test/experimental/query-tests/security/CWE-074/options b/java/ql/test/experimental/query-tests/security/CWE-074/options index b9529aa93ce..fe27cd3bf3c 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-074/options +++ b/java/ql/test/experimental/query-tests/security/CWE-074/options @@ -1 +1 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/springframework-5.2.3:${testdir}/../../../stubs/shiro-core-1.5.2:${testdir}/../../../stubs/spring-ldap-2.3.2 \ No newline at end of file +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../stubs/shiro-core-1.5.2:${testdir}/../../../stubs/spring-ldap-2.3.2 diff --git a/java/ql/test/experimental/query-tests/security/CWE-917/options b/java/ql/test/experimental/query-tests/security/CWE-917/options index c5767a074fa..ef63b56d84e 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-917/options +++ b/java/ql/test/experimental/query-tests/security/CWE-917/options @@ -1 +1 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/springframework-5.2.3:${testdir}/../../../stubs/ognl-3.2.14:${testdir}/../../../stubs/struts2-core-2.5.22 +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../stubs/ognl-3.2.14:${testdir}/../../../stubs/struts2-core-2.5.22 diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/web/bind/annotation/RequestParam.java b/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/web/bind/annotation/RequestParam.java deleted file mode 100644 index 5ae52ad123f..00000000000 --- a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/web/bind/annotation/RequestParam.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.springframework.web.bind.annotation; - -import java.lang.annotation.*; - -@Target(value=ElementType.PARAMETER) -@Retention(value=RetentionPolicy.RUNTIME) -@Documented -public @interface RequestParam { } diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/beans/factory/BeanFactory.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/factory/BeanFactory.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/beans/factory/BeanFactory.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/factory/BeanFactory.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/beans/factory/HierarchicalBeanFactory.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/factory/HierarchicalBeanFactory.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/beans/factory/HierarchicalBeanFactory.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/factory/HierarchicalBeanFactory.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/beans/factory/InitializingBean.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/factory/InitializingBean.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/beans/factory/InitializingBean.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/factory/InitializingBean.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/beans/factory/ListableBeanFactory.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/factory/ListableBeanFactory.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/beans/factory/ListableBeanFactory.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/factory/ListableBeanFactory.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/boot/security/servlet/ApplicationContextRequestMatcher.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/boot/security/servlet/ApplicationContextRequestMatcher.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/boot/security/servlet/ApplicationContextRequestMatcher.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/boot/security/servlet/ApplicationContextRequestMatcher.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/context/ApplicationContext.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/context/ApplicationContext.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/context/ApplicationContext.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/context/ApplicationContext.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/context/ApplicationEventPublisher.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/context/ApplicationEventPublisher.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/context/ApplicationEventPublisher.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/context/ApplicationEventPublisher.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/context/MessageSource.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/context/MessageSource.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/context/MessageSource.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/context/MessageSource.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/core/env/EnvironmentCapable.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/core/env/EnvironmentCapable.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/core/env/EnvironmentCapable.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/core/env/EnvironmentCapable.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/core/io/ResourceLoader.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/core/io/ResourceLoader.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/core/io/ResourceLoader.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/core/io/ResourceLoader.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/core/io/support/ResourcePatternResolver.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/core/io/support/ResourcePatternResolver.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/core/io/support/ResourcePatternResolver.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/core/io/support/ResourcePatternResolver.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/jndi/JndiTemplate.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/jndi/JndiTemplate.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/jndi/JndiTemplate.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/jndi/JndiTemplate.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/Customizer.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/Customizer.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/Customizer.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/Customizer.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/AbstractSecurityBuilder.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/AbstractSecurityBuilder.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/AbstractSecurityBuilder.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/AbstractSecurityBuilder.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityBuilder.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityBuilder.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityBuilder.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityBuilder.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityConfigurer.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityConfigurer.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityConfigurer.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityConfigurer.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityConfigurerAdapter.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityConfigurerAdapter.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityConfigurerAdapter.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/SecurityConfigurerAdapter.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/HttpSecurityBuilder.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/HttpSecurityBuilder.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/HttpSecurityBuilder.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/HttpSecurityBuilder.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/builders/HttpSecurity.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/builders/HttpSecurity.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/builders/HttpSecurity.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/builders/HttpSecurity.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractConfigAttributeRequestMatcherRegistry.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractConfigAttributeRequestMatcherRegistry.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractConfigAttributeRequestMatcherRegistry.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractConfigAttributeRequestMatcherRegistry.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractHttpConfigurer.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractHttpConfigurer.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractHttpConfigurer.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractHttpConfigurer.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractInterceptUrlConfigurer.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractInterceptUrlConfigurer.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractInterceptUrlConfigurer.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/AbstractInterceptUrlConfigurer.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/web/DefaultSecurityFilterChain.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/web/DefaultSecurityFilterChain.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/web/DefaultSecurityFilterChain.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/web/DefaultSecurityFilterChain.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/web/SecurityFilterChain.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/web/SecurityFilterChain.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/web/SecurityFilterChain.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/web/SecurityFilterChain.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/web/util/matcher/RequestMatcher.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/security/web/util/matcher/RequestMatcher.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/security/web/util/matcher/RequestMatcher.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/security/web/util/matcher/RequestMatcher.java diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/web/context/WebApplicationContext.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/context/WebApplicationContext.java similarity index 100% rename from java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/web/context/WebApplicationContext.java rename to java/ql/test/stubs/springframework-5.2.3/org/springframework/web/context/WebApplicationContext.java From 6eac8e82a38e6d8ac29d9ba349646da6a6279e34 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 8 Jul 2020 10:08:44 +0200 Subject: [PATCH 40/40] Java: Consolidate spring-ldap-2.3.2 stubs. --- .../query-tests/security/CWE-074/options | 2 +- .../ldap/core/ContextMapper.java | 4 - .../ldap/core/DirContextOperations.java | 4 - .../ldap/core/LdapTemplate.java | 76 ------------------- .../core/NameClassPairCallbackHandler.java | 3 - .../ldap/filter/EqualsFilter.java | 5 -- .../springframework/ldap/filter/Filter.java | 4 - .../ldap/filter/HardcodedFilter.java | 7 -- .../ldap/query/ConditionCriteria.java | 5 -- .../ldap/query/ContainerCriteria.java | 4 - .../springframework/ldap/query/LdapQuery.java | 4 - .../ldap/query/LdapQueryBuilder.java | 14 ---- .../ldap/support/LdapEncoder.java | 5 -- .../ldap/support/LdapNameBuilder.java | 12 --- .../ldap/support/LdapUtils.java | 7 -- .../ldap/core/AttributesMapper.java | 0 .../ldap/core/DirContextProcessor.java | 0 .../ldap/core/LdapOperations.java | 0 .../ldap/core/LdapTemplate.java | 50 +++++++++++- 19 files changed, 50 insertions(+), 156 deletions(-) delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/ContextMapper.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextOperations.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapTemplate.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/NameClassPairCallbackHandler.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/EqualsFilter.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/Filter.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/HardcodedFilter.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/ConditionCriteria.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/ContainerCriteria.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/LdapQuery.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/LdapQueryBuilder.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapEncoder.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapNameBuilder.java delete mode 100644 java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapUtils.java rename java/ql/test/{experimental => }/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/AttributesMapper.java (100%) rename java/ql/test/{experimental => }/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextProcessor.java (100%) rename java/ql/test/{experimental => }/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapOperations.java (100%) diff --git a/java/ql/test/experimental/query-tests/security/CWE-074/options b/java/ql/test/experimental/query-tests/security/CWE-074/options index fe27cd3bf3c..6ce8be3e7f5 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-074/options +++ b/java/ql/test/experimental/query-tests/security/CWE-074/options @@ -1 +1 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../stubs/shiro-core-1.5.2:${testdir}/../../../stubs/spring-ldap-2.3.2 +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../stubs/shiro-core-1.5.2:${testdir}/../../../../stubs/spring-ldap-2.3.2 diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/ContextMapper.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/ContextMapper.java deleted file mode 100644 index 951015b637e..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/ContextMapper.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.springframework.ldap.core; - -public interface ContextMapper { -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextOperations.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextOperations.java deleted file mode 100644 index 682de892a42..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextOperations.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.springframework.ldap.core; - -public interface DirContextOperations { -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapTemplate.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapTemplate.java deleted file mode 100644 index 29bee2191d2..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapTemplate.java +++ /dev/null @@ -1,76 +0,0 @@ -package org.springframework.ldap.core; - -import org.springframework.beans.factory.InitializingBean; - -import java.util.*; - -import javax.naming.Name; -import javax.naming.directory.SearchControls; - -import org.springframework.ldap.filter.Filter; - -import org.springframework.ldap.query.LdapQuery; - -public class LdapTemplate implements LdapOperations, InitializingBean { - public void authenticate(LdapQuery query, String password) { } - - public boolean authenticate(Name base, String filter, String password) { return true; } - - public List find(Name base, Filter filter, SearchControls searchControls, final Class clazz) { return null; } - - public List find(LdapQuery query, Class clazz) { return null; } - - public T findOne(LdapQuery query, Class clazz) { return null; } - - public void search(String base, String filter, int searchScope, boolean returningObjFlag, NameClassPairCallbackHandler handler) { } - - public void search(final String base, final String filter, final SearchControls controls, NameClassPairCallbackHandler handler) {} - - public void search(final String base, final String filter, final SearchControls controls, NameClassPairCallbackHandler handler, DirContextProcessor processor) {} - - public void search(String base, String filter, NameClassPairCallbackHandler handler) {} - - public List search(String base, String filter, int searchScope, String[] attrs, AttributesMapper mapper) { return null; } - - public List search(String base, String filter, int searchScope, AttributesMapper mapper) { return null; } - - public List search(String base, String filter, AttributesMapper mapper) { return null; } - - public List search(String base, String filter, int searchScope, String[] attrs, ContextMapper mapper) { return null; } - - public List search(String base, String filter, int searchScope, ContextMapper mapper) { return null; } - - public List search(String base, String filter, ContextMapper mapper) { return null; } - - public List search(String base, String filter, SearchControls controls, ContextMapper mapper) { return null; } - - public List search(String base, String filter, SearchControls controls, AttributesMapper mapper) { return null; } - - public List search(String base, String filter, SearchControls controls, AttributesMapper mapper, DirContextProcessor processor) { return null; } - - public List search(String base, String filter, SearchControls controls, ContextMapper mapper, DirContextProcessor processor) { return null; } - - public DirContextOperations searchForContext(LdapQuery query) { return null; } - - public T searchForObject(Name base, String filter, ContextMapper mapper) { return null; } - - public T searchForObject(String base, String filter, ContextMapper mapper) { return null; } - - public T searchForObject(String base, String filter, SearchControls searchControls, ContextMapper mapper) { return null; } - - public Object lookup(final String dn) { return new Object(); } - - public DirContextOperations lookupContext(String dn) { return null; } - - public T findByDn(Name dn, final Class clazz) { return null; } - - public void rename(final Name oldDn, final Name newDn) {} - - public List list(final Name base) { return null; } - - public List listBindings(final Name base) { return null; } - - public void unbind(final String dn) {} - - public void unbind(final String dn, boolean recursive) {} -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/NameClassPairCallbackHandler.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/NameClassPairCallbackHandler.java deleted file mode 100644 index 250e6da0237..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/NameClassPairCallbackHandler.java +++ /dev/null @@ -1,3 +0,0 @@ -package org.springframework.ldap.core; - -public interface NameClassPairCallbackHandler { } diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/EqualsFilter.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/EqualsFilter.java deleted file mode 100644 index a5cbbd2a674..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/EqualsFilter.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.springframework.ldap.filter; - -public class EqualsFilter implements Filter { - public EqualsFilter(String attribute, String value) { } -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/Filter.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/Filter.java deleted file mode 100644 index b24091e6de0..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/Filter.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.springframework.ldap.filter; - -public interface Filter { -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/HardcodedFilter.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/HardcodedFilter.java deleted file mode 100644 index bc43dddc6f8..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/filter/HardcodedFilter.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.springframework.ldap.filter; - -public class HardcodedFilter implements Filter { - public HardcodedFilter(String filter) { } - public StringBuffer encode(StringBuffer buff) { return buff; } - public String toString() { return ""; } -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/ConditionCriteria.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/ConditionCriteria.java deleted file mode 100644 index 80cf59b6040..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/ConditionCriteria.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.springframework.ldap.query; - -public interface ConditionCriteria { - ContainerCriteria is(String value); -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/ContainerCriteria.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/ContainerCriteria.java deleted file mode 100644 index 7a68b9fbab7..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/ContainerCriteria.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.springframework.ldap.query; - -public interface ContainerCriteria extends LdapQuery { -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/LdapQuery.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/LdapQuery.java deleted file mode 100644 index c94bb75c20c..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/LdapQuery.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.springframework.ldap.query; - -public interface LdapQuery { -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/LdapQueryBuilder.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/LdapQueryBuilder.java deleted file mode 100644 index 2e6c76ccc55..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/query/LdapQueryBuilder.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.springframework.ldap.query; - -import javax.naming.Name; -import org.springframework.ldap.filter.Filter; - -public class LdapQueryBuilder { - public static LdapQueryBuilder query() { return null; } - public LdapQuery filter(String hardcodedFilter) { return null; } - public LdapQuery filter(Filter filter) { return null; } - public LdapQuery filter(String filterFormat, Object... params) { return null; } - public LdapQueryBuilder base(String baseDn) { return this; } - public Name base() { return null; } - public ConditionCriteria where(String attribute) { return null; } -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapEncoder.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapEncoder.java deleted file mode 100644 index a85d74192b3..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapEncoder.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.springframework.ldap.support; - -public class LdapEncoder { - public static String filterEncode(String value) { return null; } -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapNameBuilder.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapNameBuilder.java deleted file mode 100644 index 74333407853..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapNameBuilder.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.springframework.ldap.support; - -import javax.naming.ldap.LdapName; - -public class LdapNameBuilder { - public static LdapNameBuilder newInstance() { return null; } - public static LdapNameBuilder newInstance(String name) { return null; } - - public LdapNameBuilder add(String name) { return null; } - public LdapNameBuilder add(String key, Object value) { return null; } - public LdapName build() { return null; } -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapUtils.java b/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapUtils.java deleted file mode 100644 index 13fee96e004..00000000000 --- a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/support/LdapUtils.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.springframework.ldap.support; - -import javax.naming.ldap.LdapName; - -public class LdapUtils { - public static LdapName newLdapName(String distinguishedName) { return null; } -} diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/AttributesMapper.java b/java/ql/test/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/AttributesMapper.java similarity index 100% rename from java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/AttributesMapper.java rename to java/ql/test/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/AttributesMapper.java diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextProcessor.java b/java/ql/test/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextProcessor.java similarity index 100% rename from java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextProcessor.java rename to java/ql/test/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/DirContextProcessor.java diff --git a/java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapOperations.java b/java/ql/test/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapOperations.java similarity index 100% rename from java/ql/test/experimental/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapOperations.java rename to java/ql/test/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapOperations.java diff --git a/java/ql/test/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapTemplate.java b/java/ql/test/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapTemplate.java index 2c26a3b3b50..fee83cf4ec8 100644 --- a/java/ql/test/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapTemplate.java +++ b/java/ql/test/stubs/spring-ldap-2.3.2/org/springframework/ldap/core/LdapTemplate.java @@ -1,5 +1,7 @@ package org.springframework.ldap.core; +import org.springframework.beans.factory.InitializingBean; + import java.util.*; import javax.naming.Name; @@ -9,7 +11,7 @@ import org.springframework.ldap.filter.Filter; import org.springframework.ldap.query.LdapQuery; -public class LdapTemplate { +public class LdapTemplate implements LdapOperations, InitializingBean { public void authenticate(LdapQuery query, String password) { } public boolean authenticate(Name base, String filter, String password) { return true; } @@ -22,7 +24,53 @@ public class LdapTemplate { public void search(String base, String filter, int searchScope, boolean returningObjFlag, NameClassPairCallbackHandler handler) { } + public void search(final String base, final String filter, final SearchControls controls, NameClassPairCallbackHandler handler) {} + + public void search(final String base, final String filter, final SearchControls controls, NameClassPairCallbackHandler handler, DirContextProcessor processor) {} + + public void search(String base, String filter, NameClassPairCallbackHandler handler) {} + + public List search(String base, String filter, int searchScope, String[] attrs, AttributesMapper mapper) { return null; } + + public List search(String base, String filter, int searchScope, AttributesMapper mapper) { return null; } + + public List search(String base, String filter, AttributesMapper mapper) { return null; } + + public List search(String base, String filter, int searchScope, String[] attrs, ContextMapper mapper) { return null; } + + public List search(String base, String filter, int searchScope, ContextMapper mapper) { return null; } + + public List search(String base, String filter, ContextMapper mapper) { return null; } + + public List search(String base, String filter, SearchControls controls, ContextMapper mapper) { return null; } + + public List search(String base, String filter, SearchControls controls, AttributesMapper mapper) { return null; } + + public List search(String base, String filter, SearchControls controls, AttributesMapper mapper, DirContextProcessor processor) { return null; } + + public List search(String base, String filter, SearchControls controls, ContextMapper mapper, DirContextProcessor processor) { return null; } + public DirContextOperations searchForContext(LdapQuery query) { return null; } public T searchForObject(Name base, String filter, ContextMapper mapper) { return null; } + + public T searchForObject(String base, String filter, ContextMapper mapper) { return null; } + + public T searchForObject(String base, String filter, SearchControls searchControls, ContextMapper mapper) { return null; } + + public Object lookup(final String dn) { return new Object(); } + + public DirContextOperations lookupContext(String dn) { return null; } + + public T findByDn(Name dn, final Class clazz) { return null; } + + public void rename(final Name oldDn, final Name newDn) {} + + public List list(final Name base) { return null; } + + public List listBindings(final Name base) { return null; } + + public void unbind(final String dn) {} + + public void unbind(final String dn, boolean recursive) {} }