From f3c0bf78268013b2d54b9d8869e64dba13aa1db3 Mon Sep 17 00:00:00 2001
From: valeria-meli
Date: Tue, 27 Jul 2021 18:09:11 -0300
Subject: [PATCH 001/471] copy-paste from our repo
---
.../Security/CWE-918/SSRF/SSRF.qhelp | 49 +++++++++++++++
.../Security/CWE-918/SSRF/SSRF.ql | 18 ++++++
.../Security/CWE-918/SSRF/SSRF.qll | 60 +++++++++++++++++++
3 files changed, 127 insertions(+)
create mode 100644 javascript/ql/src/experimental/Security/CWE-918/SSRF/SSRF.qhelp
create mode 100644 javascript/ql/src/experimental/Security/CWE-918/SSRF/SSRF.ql
create mode 100644 javascript/ql/src/experimental/Security/CWE-918/SSRF/SSRF.qll
diff --git a/javascript/ql/src/experimental/Security/CWE-918/SSRF/SSRF.qhelp b/javascript/ql/src/experimental/Security/CWE-918/SSRF/SSRF.qhelp
new file mode 100644
index 00000000000..05ac30b2e95
--- /dev/null
+++ b/javascript/ql/src/experimental/Security/CWE-918/SSRF/SSRF.qhelp
@@ -0,0 +1,49 @@
+
+
+
+
+
+ Directly incorporating user input into an HTTP request without validating the input can facilitate
+ server side request forgery attacks, where the attacker essentially controls the request.
+
+
+
+
+
+ To guard against server side request forgery, it is advisable to avoid putting user input directly into a
+ network request. If using user input is necessary, then is mandatory to validate them. Only allow numeric and alphanumeric values.
+ URL encoding is not a solution in certain scenarios, such as, an architecture build over NGINX proxies.
+
+
+
+
+
+ The following example shows an HTTP request parameter being used directly in a URL request without
+ validating the input, which facilitates an SSRF attack. The request axios.get("https://example.com/current_api/"+target) is
+ vulnerable since attackers can choose the value of target to be anything they want. For
+ instance, the attacker can choose "../super_secret_api" as the target, causing the
+ URL to become "https://example.com/super_secret_api".
+
+
+
+ A request to https://example.com/super_secret_api may be problematic if that api is not
+ meant to be directly accessible from the attacker's machine.
+
+
+
+
+
+ One way to remedy the problem is to validate the user input to only allow alphanumeric values:
+
+
+
+
+
+
+
+OWASP: SSRF
+
+
+
\ No newline at end of file
diff --git a/javascript/ql/src/experimental/Security/CWE-918/SSRF/SSRF.ql b/javascript/ql/src/experimental/Security/CWE-918/SSRF/SSRF.ql
new file mode 100644
index 00000000000..1afbefe87e0
--- /dev/null
+++ b/javascript/ql/src/experimental/Security/CWE-918/SSRF/SSRF.ql
@@ -0,0 +1,18 @@
+/**
+ * @id javascript/ssrf-path
+ * @kind path-problem
+ * @name Uncontrolled data used in network request
+ * @description Sending network requests with user-controlled data as part of the URL allows for request forgery attacks.
+ * @problem.severity error
+ * @precision medium
+ * @tags security
+ * external/cwe/cwe-918
+ */
+
+import javascript
+import SSRF
+import DataFlow::PathGraph
+
+from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::Node request
+where cfg.hasFlowPath(source, sink) and request = sink.getNode().(RequestForgery::Sink).getARequest()
+select sink, source, sink, "The URL of this request depends on a user-provided value"
diff --git a/javascript/ql/src/experimental/Security/CWE-918/SSRF/SSRF.qll b/javascript/ql/src/experimental/Security/CWE-918/SSRF/SSRF.qll
new file mode 100644
index 00000000000..8b868d0c215
--- /dev/null
+++ b/javascript/ql/src/experimental/Security/CWE-918/SSRF/SSRF.qll
@@ -0,0 +1,60 @@
+import javascript
+
+import semmle.javascript.security.dataflow.RequestForgeryCustomizations
+import semmle.javascript.security.dataflow.UrlConcatenation
+
+class Configuration extends TaintTracking::Configuration {
+ Configuration() { this = "SSRF" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof RequestForgery::Source }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof RequestForgery::Sink }
+
+ override predicate isSanitizer(DataFlow::Node node) {
+ super.isSanitizer(node) or
+ node instanceof RequestForgery::Sanitizer
+ }
+
+ override predicate isSanitizerEdge(DataFlow::Node source, DataFlow::Node sink) {
+ sanitizingPrefixEdge(source, sink)
+ }
+ override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode nd) {
+ nd instanceof IntegerCheck or
+ nd instanceof ValidatorCheck
+ }
+}
+
+/**
+ * Number.isInteger is a sanitizer guard because a number can't be used to exploit a SSRF.
+ */
+class IntegerCheck extends TaintTracking::SanitizerGuardNode, DataFlow::CallNode{
+ IntegerCheck() {
+ this = DataFlow::globalVarRef("Number").getAMemberCall("isInteger")
+ }
+
+ override predicate sanitizes(boolean outcome, Expr e){
+ outcome = true and
+ e = getArgument(0).asExpr()
+ }
+}
+
+/**
+ * ValidatorCheck identifies if exists a call to validator's library methods.
+ * validator is a library which has a variety of input-validation functions. We are interesed in
+ * checking that source is a number (any type of number) or an alphanumeric value.
+ */
+class ValidatorCheck extends TaintTracking::SanitizerGuardNode, DataFlow::CallNode {
+ ValidatorCheck() {
+ exists(
+ DataFlow::SourceNode mod, string method |
+ mod = DataFlow::moduleImport("validator") and
+ this = mod.getAChainedMethodCall(method)
+ and method in ["isAlphanumeric", "isAlpha", "isDecimal", "isFloat",
+ "isHexadecimal", "isHexColor", "isInt", "isNumeric", "isOctal", "isUUID"]
+ )
+ }
+ override predicate sanitizes(boolean outcome, Expr e){
+ outcome = true and
+ e = getArgument(0).asExpr()
+ }
+}
From 92c874c2e2fb179ca3f77d53399ecf57dbb2513c Mon Sep 17 00:00:00 2001
From: valeria-meli
Date: Tue, 3 Aug 2021 17:32:36 -0300
Subject: [PATCH 002/471] rename query
---
javascript/ql/src/experimental/Security/CWE-918/SSRF/SSRF.ql | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-918/SSRF/SSRF.ql b/javascript/ql/src/experimental/Security/CWE-918/SSRF/SSRF.ql
index 1afbefe87e0..b4124e38cc3 100644
--- a/javascript/ql/src/experimental/Security/CWE-918/SSRF/SSRF.ql
+++ b/javascript/ql/src/experimental/Security/CWE-918/SSRF/SSRF.ql
@@ -1,5 +1,5 @@
/**
- * @id javascript/ssrf-path
+ * @id javascript/ssrf
* @kind path-problem
* @name Uncontrolled data used in network request
* @description Sending network requests with user-controlled data as part of the URL allows for request forgery attacks.
From 57ac944319ee22ecd24e526d5f02345c1e90214f Mon Sep 17 00:00:00 2001
From: valeria-meli
Date: Tue, 3 Aug 2021 17:39:48 -0300
Subject: [PATCH 003/471] rename folders
---
.../ql/src/experimental/Security/CWE-918/{SSRF => }/SSRF.qhelp | 0
.../ql/src/experimental/Security/CWE-918/{SSRF => }/SSRF.ql | 0
.../ql/src/experimental/Security/CWE-918/{SSRF => }/SSRF.qll | 0
3 files changed, 0 insertions(+), 0 deletions(-)
rename javascript/ql/src/experimental/Security/CWE-918/{SSRF => }/SSRF.qhelp (100%)
rename javascript/ql/src/experimental/Security/CWE-918/{SSRF => }/SSRF.ql (100%)
rename javascript/ql/src/experimental/Security/CWE-918/{SSRF => }/SSRF.qll (100%)
diff --git a/javascript/ql/src/experimental/Security/CWE-918/SSRF/SSRF.qhelp b/javascript/ql/src/experimental/Security/CWE-918/SSRF.qhelp
similarity index 100%
rename from javascript/ql/src/experimental/Security/CWE-918/SSRF/SSRF.qhelp
rename to javascript/ql/src/experimental/Security/CWE-918/SSRF.qhelp
diff --git a/javascript/ql/src/experimental/Security/CWE-918/SSRF/SSRF.ql b/javascript/ql/src/experimental/Security/CWE-918/SSRF.ql
similarity index 100%
rename from javascript/ql/src/experimental/Security/CWE-918/SSRF/SSRF.ql
rename to javascript/ql/src/experimental/Security/CWE-918/SSRF.ql
diff --git a/javascript/ql/src/experimental/Security/CWE-918/SSRF/SSRF.qll b/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll
similarity index 100%
rename from javascript/ql/src/experimental/Security/CWE-918/SSRF/SSRF.qll
rename to javascript/ql/src/experimental/Security/CWE-918/SSRF.qll
From 595ea6c383d3e01ae72b8d8679bb5eedf4fee9af Mon Sep 17 00:00:00 2001
From: valeria-meli
Date: Tue, 3 Aug 2021 18:00:29 -0300
Subject: [PATCH 004/471] files for qhelp
---
.../src/experimental/Security/CWE-918/SSRF.js | 15 ++++++++++++++
.../experimental/Security/CWE-918/SSRFGood.js | 20 +++++++++++++++++++
2 files changed, 35 insertions(+)
create mode 100644 javascript/ql/src/experimental/Security/CWE-918/SSRF.js
create mode 100644 javascript/ql/src/experimental/Security/CWE-918/SSRFGood.js
diff --git a/javascript/ql/src/experimental/Security/CWE-918/SSRF.js b/javascript/ql/src/experimental/Security/CWE-918/SSRF.js
new file mode 100644
index 00000000000..e39e9760441
--- /dev/null
+++ b/javascript/ql/src/experimental/Security/CWE-918/SSRF.js
@@ -0,0 +1,15 @@
+const axios = require('axios');
+
+export const handler = async (req, res, next) => {
+ const { targetĀ } = req.body;
+
+ try {
+ // BAD: `target` is controlled by the attacker
+ const response = await axios.get('https://example.com/current_api/' + target);
+
+ // process request response
+ use(response);
+ } catch (err) {
+ // process error
+ }
+};
diff --git a/javascript/ql/src/experimental/Security/CWE-918/SSRFGood.js b/javascript/ql/src/experimental/Security/CWE-918/SSRFGood.js
new file mode 100644
index 00000000000..10ab26e607b
--- /dev/null
+++ b/javascript/ql/src/experimental/Security/CWE-918/SSRFGood.js
@@ -0,0 +1,20 @@
+const axios = require('axios');
+const validator = require('validator');
+
+export const handler = async (req, res, next) => {
+ const { targetĀ } = req.body;
+
+ if (!validator.isAlphanumeric(target)) {
+ return next(new Error('Bad request'));
+ }
+
+ try {
+ // `target` is validated
+ const response = await axios.get('https://example.com/current_api/' + target);
+
+ // process request response
+ use(response);
+ } catch (err) {
+ // process error
+ }
+};
From 0b5c8909dd383494e5e387f2e2f8ffd7e49ab50e Mon Sep 17 00:00:00 2001
From: valeria-meli
Date: Tue, 3 Aug 2021 18:00:49 -0300
Subject: [PATCH 005/471] tests
---
.../Security/CWE-918/SSRF.expected | 176 ++++++++++++++++++
.../experimental/Security/CWE-918/SSRF.qlref | 1 +
.../Security/CWE-918/check-domain.js | 34 ++++
.../Security/CWE-918/check-middleware.js | 19 ++
.../Security/CWE-918/check-path.js | 54 ++++++
.../Security/CWE-918/check-regex.js | 46 +++++
.../Security/CWE-918/check-validator.js | 98 ++++++++++
7 files changed, 428 insertions(+)
create mode 100644 javascript/ql/test/experimental/Security/CWE-918/SSRF.expected
create mode 100644 javascript/ql/test/experimental/Security/CWE-918/SSRF.qlref
create mode 100644 javascript/ql/test/experimental/Security/CWE-918/check-domain.js
create mode 100644 javascript/ql/test/experimental/Security/CWE-918/check-middleware.js
create mode 100644 javascript/ql/test/experimental/Security/CWE-918/check-path.js
create mode 100644 javascript/ql/test/experimental/Security/CWE-918/check-regex.js
create mode 100644 javascript/ql/test/experimental/Security/CWE-918/check-validator.js
diff --git a/javascript/ql/test/experimental/Security/CWE-918/SSRF.expected b/javascript/ql/test/experimental/Security/CWE-918/SSRF.expected
new file mode 100644
index 00000000000..5e1ffe1e982
--- /dev/null
+++ b/javascript/ql/test/experimental/Security/CWE-918/SSRF.expected
@@ -0,0 +1,176 @@
+nodes
+| check-domain.js:16:9:16:27 | url |
+| check-domain.js:16:15:16:27 | req.query.url |
+| check-domain.js:16:15:16:27 | req.query.url |
+| check-domain.js:17:13:17:15 | url |
+| check-domain.js:17:13:17:15 | url |
+| check-domain.js:26:15:26:27 | req.query.url |
+| check-domain.js:26:15:26:27 | req.query.url |
+| check-domain.js:26:15:26:27 | req.query.url |
+| check-middleware.js:9:13:9:43 | "test.c ... tainted |
+| check-middleware.js:9:13:9:43 | "test.c ... tainted |
+| check-middleware.js:9:27:9:43 | req.query.tainted |
+| check-middleware.js:9:27:9:43 | req.query.tainted |
+| check-path.js:19:13:19:43 | 'test.c ... tainted |
+| check-path.js:19:13:19:43 | 'test.c ... tainted |
+| check-path.js:19:27:19:43 | req.query.tainted |
+| check-path.js:19:27:19:43 | req.query.tainted |
+| check-path.js:22:13:22:63 | 'test.c ... ainted) |
+| check-path.js:22:13:22:63 | 'test.c ... ainted) |
+| check-path.js:22:27:22:63 | encodeU ... ainted) |
+| check-path.js:22:46:22:62 | req.query.tainted |
+| check-path.js:22:46:22:62 | req.query.tainted |
+| check-path.js:23:13:23:45 | `/addre ... inted}` |
+| check-path.js:23:13:23:45 | `/addre ... inted}` |
+| check-path.js:23:27:23:43 | req.query.tainted |
+| check-path.js:23:27:23:43 | req.query.tainted |
+| check-path.js:24:13:24:65 | `/addre ... nted)}` |
+| check-path.js:24:13:24:65 | `/addre ... nted)}` |
+| check-path.js:24:27:24:63 | encodeU ... ainted) |
+| check-path.js:24:46:24:62 | req.query.tainted |
+| check-path.js:24:46:24:62 | req.query.tainted |
+| check-path.js:33:15:33:45 | 'test.c ... tainted |
+| check-path.js:33:15:33:45 | 'test.c ... tainted |
+| check-path.js:33:29:33:45 | req.query.tainted |
+| check-path.js:33:29:33:45 | req.query.tainted |
+| check-path.js:37:15:37:45 | 'test.c ... tainted |
+| check-path.js:37:15:37:45 | 'test.c ... tainted |
+| check-path.js:37:29:37:45 | req.query.tainted |
+| check-path.js:37:29:37:45 | req.query.tainted |
+| check-regex.js:24:15:24:42 | baseURL ... tainted |
+| check-regex.js:24:15:24:42 | baseURL ... tainted |
+| check-regex.js:24:25:24:42 | req.params.tainted |
+| check-regex.js:24:25:24:42 | req.params.tainted |
+| check-regex.js:31:15:31:45 | "test.c ... tainted |
+| check-regex.js:31:15:31:45 | "test.c ... tainted |
+| check-regex.js:31:29:31:45 | req.query.tainted |
+| check-regex.js:31:29:31:45 | req.query.tainted |
+| check-regex.js:34:15:34:42 | baseURL ... tainted |
+| check-regex.js:34:15:34:42 | baseURL ... tainted |
+| check-regex.js:34:25:34:42 | req.params.tainted |
+| check-regex.js:34:25:34:42 | req.params.tainted |
+| check-regex.js:41:13:41:43 | "test.c ... tainted |
+| check-regex.js:41:13:41:43 | "test.c ... tainted |
+| check-regex.js:41:27:41:43 | req.query.tainted |
+| check-regex.js:41:27:41:43 | req.query.tainted |
+| check-validator.js:15:15:15:45 | "test.c ... tainted |
+| check-validator.js:15:15:15:45 | "test.c ... tainted |
+| check-validator.js:15:29:15:45 | req.query.tainted |
+| check-validator.js:15:29:15:45 | req.query.tainted |
+| check-validator.js:27:15:27:45 | "test.c ... tainted |
+| check-validator.js:27:15:27:45 | "test.c ... tainted |
+| check-validator.js:27:29:27:45 | req.query.tainted |
+| check-validator.js:27:29:27:45 | req.query.tainted |
+| check-validator.js:50:15:50:45 | "test.c ... tainted |
+| check-validator.js:50:15:50:45 | "test.c ... tainted |
+| check-validator.js:50:29:50:45 | req.query.tainted |
+| check-validator.js:50:29:50:45 | req.query.tainted |
+| check-validator.js:54:9:54:37 | numberURL |
+| check-validator.js:54:21:54:37 | req.query.tainted |
+| check-validator.js:54:21:54:37 | req.query.tainted |
+| check-validator.js:59:15:59:45 | "test.c ... tainted |
+| check-validator.js:59:15:59:45 | "test.c ... tainted |
+| check-validator.js:59:29:59:45 | req.query.tainted |
+| check-validator.js:59:29:59:45 | req.query.tainted |
+| check-validator.js:62:15:62:37 | "test.c ... mberURL |
+| check-validator.js:62:15:62:37 | "test.c ... mberURL |
+| check-validator.js:62:29:62:37 | numberURL |
+| check-validator.js:68:15:68:45 | "test.c ... tainted |
+| check-validator.js:68:15:68:45 | "test.c ... tainted |
+| check-validator.js:68:29:68:45 | req.query.tainted |
+| check-validator.js:68:29:68:45 | req.query.tainted |
+edges
+| check-domain.js:16:9:16:27 | url | check-domain.js:17:13:17:15 | url |
+| check-domain.js:16:9:16:27 | url | check-domain.js:17:13:17:15 | url |
+| check-domain.js:16:15:16:27 | req.query.url | check-domain.js:16:9:16:27 | url |
+| check-domain.js:16:15:16:27 | req.query.url | check-domain.js:16:9:16:27 | url |
+| check-domain.js:26:15:26:27 | req.query.url | check-domain.js:26:15:26:27 | req.query.url |
+| check-middleware.js:9:27:9:43 | req.query.tainted | check-middleware.js:9:13:9:43 | "test.c ... tainted |
+| check-middleware.js:9:27:9:43 | req.query.tainted | check-middleware.js:9:13:9:43 | "test.c ... tainted |
+| check-middleware.js:9:27:9:43 | req.query.tainted | check-middleware.js:9:13:9:43 | "test.c ... tainted |
+| check-middleware.js:9:27:9:43 | req.query.tainted | check-middleware.js:9:13:9:43 | "test.c ... tainted |
+| check-path.js:19:27:19:43 | req.query.tainted | check-path.js:19:13:19:43 | 'test.c ... tainted |
+| check-path.js:19:27:19:43 | req.query.tainted | check-path.js:19:13:19:43 | 'test.c ... tainted |
+| check-path.js:19:27:19:43 | req.query.tainted | check-path.js:19:13:19:43 | 'test.c ... tainted |
+| check-path.js:19:27:19:43 | req.query.tainted | check-path.js:19:13:19:43 | 'test.c ... tainted |
+| check-path.js:22:27:22:63 | encodeU ... ainted) | check-path.js:22:13:22:63 | 'test.c ... ainted) |
+| check-path.js:22:27:22:63 | encodeU ... ainted) | check-path.js:22:13:22:63 | 'test.c ... ainted) |
+| check-path.js:22:46:22:62 | req.query.tainted | check-path.js:22:27:22:63 | encodeU ... ainted) |
+| check-path.js:22:46:22:62 | req.query.tainted | check-path.js:22:27:22:63 | encodeU ... ainted) |
+| check-path.js:23:27:23:43 | req.query.tainted | check-path.js:23:13:23:45 | `/addre ... inted}` |
+| check-path.js:23:27:23:43 | req.query.tainted | check-path.js:23:13:23:45 | `/addre ... inted}` |
+| check-path.js:23:27:23:43 | req.query.tainted | check-path.js:23:13:23:45 | `/addre ... inted}` |
+| check-path.js:23:27:23:43 | req.query.tainted | check-path.js:23:13:23:45 | `/addre ... inted}` |
+| check-path.js:24:27:24:63 | encodeU ... ainted) | check-path.js:24:13:24:65 | `/addre ... nted)}` |
+| check-path.js:24:27:24:63 | encodeU ... ainted) | check-path.js:24:13:24:65 | `/addre ... nted)}` |
+| check-path.js:24:46:24:62 | req.query.tainted | check-path.js:24:27:24:63 | encodeU ... ainted) |
+| check-path.js:24:46:24:62 | req.query.tainted | check-path.js:24:27:24:63 | encodeU ... ainted) |
+| check-path.js:33:29:33:45 | req.query.tainted | check-path.js:33:15:33:45 | 'test.c ... tainted |
+| check-path.js:33:29:33:45 | req.query.tainted | check-path.js:33:15:33:45 | 'test.c ... tainted |
+| check-path.js:33:29:33:45 | req.query.tainted | check-path.js:33:15:33:45 | 'test.c ... tainted |
+| check-path.js:33:29:33:45 | req.query.tainted | check-path.js:33:15:33:45 | 'test.c ... tainted |
+| check-path.js:37:29:37:45 | req.query.tainted | check-path.js:37:15:37:45 | 'test.c ... tainted |
+| check-path.js:37:29:37:45 | req.query.tainted | check-path.js:37:15:37:45 | 'test.c ... tainted |
+| check-path.js:37:29:37:45 | req.query.tainted | check-path.js:37:15:37:45 | 'test.c ... tainted |
+| check-path.js:37:29:37:45 | req.query.tainted | check-path.js:37:15:37:45 | 'test.c ... tainted |
+| check-regex.js:24:25:24:42 | req.params.tainted | check-regex.js:24:15:24:42 | baseURL ... tainted |
+| check-regex.js:24:25:24:42 | req.params.tainted | check-regex.js:24:15:24:42 | baseURL ... tainted |
+| check-regex.js:24:25:24:42 | req.params.tainted | check-regex.js:24:15:24:42 | baseURL ... tainted |
+| check-regex.js:24:25:24:42 | req.params.tainted | check-regex.js:24:15:24:42 | baseURL ... tainted |
+| check-regex.js:31:29:31:45 | req.query.tainted | check-regex.js:31:15:31:45 | "test.c ... tainted |
+| check-regex.js:31:29:31:45 | req.query.tainted | check-regex.js:31:15:31:45 | "test.c ... tainted |
+| check-regex.js:31:29:31:45 | req.query.tainted | check-regex.js:31:15:31:45 | "test.c ... tainted |
+| check-regex.js:31:29:31:45 | req.query.tainted | check-regex.js:31:15:31:45 | "test.c ... tainted |
+| check-regex.js:34:25:34:42 | req.params.tainted | check-regex.js:34:15:34:42 | baseURL ... tainted |
+| check-regex.js:34:25:34:42 | req.params.tainted | check-regex.js:34:15:34:42 | baseURL ... tainted |
+| check-regex.js:34:25:34:42 | req.params.tainted | check-regex.js:34:15:34:42 | baseURL ... tainted |
+| check-regex.js:34:25:34:42 | req.params.tainted | check-regex.js:34:15:34:42 | baseURL ... tainted |
+| check-regex.js:41:27:41:43 | req.query.tainted | check-regex.js:41:13:41:43 | "test.c ... tainted |
+| check-regex.js:41:27:41:43 | req.query.tainted | check-regex.js:41:13:41:43 | "test.c ... tainted |
+| check-regex.js:41:27:41:43 | req.query.tainted | check-regex.js:41:13:41:43 | "test.c ... tainted |
+| check-regex.js:41:27:41:43 | req.query.tainted | check-regex.js:41:13:41:43 | "test.c ... tainted |
+| check-validator.js:15:29:15:45 | req.query.tainted | check-validator.js:15:15:15:45 | "test.c ... tainted |
+| check-validator.js:15:29:15:45 | req.query.tainted | check-validator.js:15:15:15:45 | "test.c ... tainted |
+| check-validator.js:15:29:15:45 | req.query.tainted | check-validator.js:15:15:15:45 | "test.c ... tainted |
+| check-validator.js:15:29:15:45 | req.query.tainted | check-validator.js:15:15:15:45 | "test.c ... tainted |
+| check-validator.js:27:29:27:45 | req.query.tainted | check-validator.js:27:15:27:45 | "test.c ... tainted |
+| check-validator.js:27:29:27:45 | req.query.tainted | check-validator.js:27:15:27:45 | "test.c ... tainted |
+| check-validator.js:27:29:27:45 | req.query.tainted | check-validator.js:27:15:27:45 | "test.c ... tainted |
+| check-validator.js:27:29:27:45 | req.query.tainted | check-validator.js:27:15:27:45 | "test.c ... tainted |
+| check-validator.js:50:29:50:45 | req.query.tainted | check-validator.js:50:15:50:45 | "test.c ... tainted |
+| check-validator.js:50:29:50:45 | req.query.tainted | check-validator.js:50:15:50:45 | "test.c ... tainted |
+| check-validator.js:50:29:50:45 | req.query.tainted | check-validator.js:50:15:50:45 | "test.c ... tainted |
+| check-validator.js:50:29:50:45 | req.query.tainted | check-validator.js:50:15:50:45 | "test.c ... tainted |
+| check-validator.js:54:9:54:37 | numberURL | check-validator.js:62:29:62:37 | numberURL |
+| check-validator.js:54:21:54:37 | req.query.tainted | check-validator.js:54:9:54:37 | numberURL |
+| check-validator.js:54:21:54:37 | req.query.tainted | check-validator.js:54:9:54:37 | numberURL |
+| check-validator.js:59:29:59:45 | req.query.tainted | check-validator.js:59:15:59:45 | "test.c ... tainted |
+| check-validator.js:59:29:59:45 | req.query.tainted | check-validator.js:59:15:59:45 | "test.c ... tainted |
+| check-validator.js:59:29:59:45 | req.query.tainted | check-validator.js:59:15:59:45 | "test.c ... tainted |
+| check-validator.js:59:29:59:45 | req.query.tainted | check-validator.js:59:15:59:45 | "test.c ... tainted |
+| check-validator.js:62:29:62:37 | numberURL | check-validator.js:62:15:62:37 | "test.c ... mberURL |
+| check-validator.js:62:29:62:37 | numberURL | check-validator.js:62:15:62:37 | "test.c ... mberURL |
+| check-validator.js:68:29:68:45 | req.query.tainted | check-validator.js:68:15:68:45 | "test.c ... tainted |
+| check-validator.js:68:29:68:45 | req.query.tainted | check-validator.js:68:15:68:45 | "test.c ... tainted |
+| check-validator.js:68:29:68:45 | req.query.tainted | check-validator.js:68:15:68:45 | "test.c ... tainted |
+| check-validator.js:68:29:68:45 | req.query.tainted | check-validator.js:68:15:68:45 | "test.c ... tainted |
+#select
+| check-domain.js:17:13:17:15 | url | check-domain.js:16:15:16:27 | req.query.url | check-domain.js:17:13:17:15 | url | The URL of this request depends on a user-provided value |
+| check-domain.js:26:15:26:27 | req.query.url | check-domain.js:26:15:26:27 | req.query.url | check-domain.js:26:15:26:27 | req.query.url | The URL of this request depends on a user-provided value |
+| check-middleware.js:9:13:9:43 | "test.c ... tainted | check-middleware.js:9:27:9:43 | req.query.tainted | check-middleware.js:9:13:9:43 | "test.c ... tainted | The URL of this request depends on a user-provided value |
+| check-path.js:19:13:19:43 | 'test.c ... tainted | check-path.js:19:27:19:43 | req.query.tainted | check-path.js:19:13:19:43 | 'test.c ... tainted | The URL of this request depends on a user-provided value |
+| check-path.js:22:13:22:63 | 'test.c ... ainted) | check-path.js:22:46:22:62 | req.query.tainted | check-path.js:22:13:22:63 | 'test.c ... ainted) | The URL of this request depends on a user-provided value |
+| check-path.js:23:13:23:45 | `/addre ... inted}` | check-path.js:23:27:23:43 | req.query.tainted | check-path.js:23:13:23:45 | `/addre ... inted}` | The URL of this request depends on a user-provided value |
+| check-path.js:24:13:24:65 | `/addre ... nted)}` | check-path.js:24:46:24:62 | req.query.tainted | check-path.js:24:13:24:65 | `/addre ... nted)}` | The URL of this request depends on a user-provided value |
+| check-path.js:33:15:33:45 | 'test.c ... tainted | check-path.js:33:29:33:45 | req.query.tainted | check-path.js:33:15:33:45 | 'test.c ... tainted | The URL of this request depends on a user-provided value |
+| check-path.js:37:15:37:45 | 'test.c ... tainted | check-path.js:37:29:37:45 | req.query.tainted | check-path.js:37:15:37:45 | 'test.c ... tainted | The URL of this request depends on a user-provided value |
+| check-regex.js:24:15:24:42 | baseURL ... tainted | check-regex.js:24:25:24:42 | req.params.tainted | check-regex.js:24:15:24:42 | baseURL ... tainted | The URL of this request depends on a user-provided value |
+| check-regex.js:31:15:31:45 | "test.c ... tainted | check-regex.js:31:29:31:45 | req.query.tainted | check-regex.js:31:15:31:45 | "test.c ... tainted | The URL of this request depends on a user-provided value |
+| check-regex.js:34:15:34:42 | baseURL ... tainted | check-regex.js:34:25:34:42 | req.params.tainted | check-regex.js:34:15:34:42 | baseURL ... tainted | The URL of this request depends on a user-provided value |
+| check-regex.js:41:13:41:43 | "test.c ... tainted | check-regex.js:41:27:41:43 | req.query.tainted | check-regex.js:41:13:41:43 | "test.c ... tainted | The URL of this request depends on a user-provided value |
+| check-validator.js:15:15:15:45 | "test.c ... tainted | check-validator.js:15:29:15:45 | req.query.tainted | check-validator.js:15:15:15:45 | "test.c ... tainted | The URL of this request depends on a user-provided value |
+| check-validator.js:27:15:27:45 | "test.c ... tainted | check-validator.js:27:29:27:45 | req.query.tainted | check-validator.js:27:15:27:45 | "test.c ... tainted | The URL of this request depends on a user-provided value |
+| check-validator.js:50:15:50:45 | "test.c ... tainted | check-validator.js:50:29:50:45 | req.query.tainted | check-validator.js:50:15:50:45 | "test.c ... tainted | The URL of this request depends on a user-provided value |
+| check-validator.js:59:15:59:45 | "test.c ... tainted | check-validator.js:59:29:59:45 | req.query.tainted | check-validator.js:59:15:59:45 | "test.c ... tainted | The URL of this request depends on a user-provided value |
+| check-validator.js:62:15:62:37 | "test.c ... mberURL | check-validator.js:54:21:54:37 | req.query.tainted | check-validator.js:62:15:62:37 | "test.c ... mberURL | The URL of this request depends on a user-provided value |
+| check-validator.js:68:15:68:45 | "test.c ... tainted | check-validator.js:68:29:68:45 | req.query.tainted | check-validator.js:68:15:68:45 | "test.c ... tainted | The URL of this request depends on a user-provided value |
diff --git a/javascript/ql/test/experimental/Security/CWE-918/SSRF.qlref b/javascript/ql/test/experimental/Security/CWE-918/SSRF.qlref
new file mode 100644
index 00000000000..05a9c8145e6
--- /dev/null
+++ b/javascript/ql/test/experimental/Security/CWE-918/SSRF.qlref
@@ -0,0 +1 @@
+./experimental/Security/CWE-918/SSRF.ql
\ No newline at end of file
diff --git a/javascript/ql/test/experimental/Security/CWE-918/check-domain.js b/javascript/ql/test/experimental/Security/CWE-918/check-domain.js
new file mode 100644
index 00000000000..0821140ab5f
--- /dev/null
+++ b/javascript/ql/test/experimental/Security/CWE-918/check-domain.js
@@ -0,0 +1,34 @@
+// native modules
+const url = require('url');
+
+// dependencies
+const axios = require('axios');
+const express = require('express');
+
+// constants
+const VALID_DOMAINS = ['example.com', 'example-2.com'];
+
+// start
+const app = express();
+
+app.get('/check-with-axios', req => {
+ // without validation
+ const url = req.query.url;
+ axios.get(url); //SSRF
+
+ // validating domain only
+ const decodedURI = decodeURIComponent(req.query.url);
+ const { hostname } = url.parse(decodedURI);
+
+ const { hostname } = url.parse(decodedURI);
+
+ if (isValidDomain(hostname, validDomains)) {
+ axios.get(req.query.url); //SSRF
+ }
+});
+
+const isValidDomain = (hostname, validDomains) => (
+ validDomains.some(domain => (
+ hostname === domain || hostname.endsWith(`.${domain}`))
+ )
+);
\ No newline at end of file
diff --git a/javascript/ql/test/experimental/Security/CWE-918/check-middleware.js b/javascript/ql/test/experimental/Security/CWE-918/check-middleware.js
new file mode 100644
index 00000000000..2a1e6d54166
--- /dev/null
+++ b/javascript/ql/test/experimental/Security/CWE-918/check-middleware.js
@@ -0,0 +1,19 @@
+// dependencies
+const axios = require('axios');
+const express = require('express');
+
+// start
+const app = express();
+
+app.get('/check-with-axios', validationMiddleware, req => {
+ axios.get("test.com/" + req.query.tainted); // OK is sanitized by the middleware - False Positive
+});
+
+
+const validationMiddleware = (req, res, next) => {
+ if (!Number.isInteger(req.query.tainted)) {
+ return res.sendStatus(400);
+ }
+
+ next();
+}
diff --git a/javascript/ql/test/experimental/Security/CWE-918/check-path.js b/javascript/ql/test/experimental/Security/CWE-918/check-path.js
new file mode 100644
index 00000000000..35681d58491
--- /dev/null
+++ b/javascript/ql/test/experimental/Security/CWE-918/check-path.js
@@ -0,0 +1,54 @@
+// native modules
+const path = require('path');
+const url = require('url');
+
+// dependencies
+const axios = require('axios');
+const express = require('express');
+
+// constants
+const VALID_PATHS = ['/api/users/me', '/help', '/system/health'];
+
+// start
+const app = express();
+
+app.get('/check-with-axios', req => {
+ const hardcoded = 'hardcodeado';
+
+ axios.get('test.com/' + hardcoded); // OK
+ axios.get('test.com/' + req.query.tainted); // SSRF
+ axios.get('test.com/' + Number(req.query.tainted)); // OK
+ axios.get('test.com/' + req.user.id); // OK
+ axios.get('test.com/' + encodeURIComponent(req.query.tainted)); // SSRF
+ axios.get(`/addresses/${req.query.tainted}`); // SSRF
+ axios.get(`/addresses/${encodeURIComponent(req.query.tainted)}`); // SSRF
+
+ if (Number.isInteger(req.query.tainted)) {
+ axios.get('test.com/' + req.query.tainted); // OK
+ }
+
+ if (isValidInput(req.query.tainted)){
+ axios.get('test.com/' + req.query.tainted); // OK
+ } else {
+ axios.get('test.com/' + req.query.tainted); // SSRF
+ }
+
+ if (doesntCheckAnything(req.query.tainted)) {
+ axios.get('test.com/' + req.query.tainted); // SSRF
+ }
+
+ if (isValidPath(req.query.tainted, VALID_PATHS)) {
+ axios.get('test.com/' + req.query.tainted) // OK
+ }
+
+ if(!isValidInput(req.query.tainted)) {
+ return;
+ }
+ axios.get("test.com/" + req.query.tainted); // OK
+});
+
+const isValidPath = (path, validPaths) => validPaths.includes(path);
+
+const isValidInput = (path) => Number.isInteger(path);
+
+const doesntCheckAnything = (path) => true;
diff --git a/javascript/ql/test/experimental/Security/CWE-918/check-regex.js b/javascript/ql/test/experimental/Security/CWE-918/check-regex.js
new file mode 100644
index 00000000000..a05c1ae7ddf
--- /dev/null
+++ b/javascript/ql/test/experimental/Security/CWE-918/check-regex.js
@@ -0,0 +1,46 @@
+// dependencies
+const axios = require('axios');
+const express = require('express');
+
+// start
+const app = express();
+
+app.get('/check-with-axios', req => {
+ if (req.query.tainted.match(/^[0-9a-z]+$/)) { // letters and numbers
+ axios.get("test.com/" + req.query.tainted); // OK
+ }
+ if (req.query.tainted.match(/^[0-9a-z\-_]+$/)) { // letters, numbers, - and _
+ axios.get("test.com/" + req.query.tainted); // OK
+ }
+ if (req.query.tainted.match(/^.*$/)) { // anything
+ axios.get("test.com/" + req.query.tainted); // SSRF - False Negative
+ }
+
+ const baseURL = "test.com/"
+ if (isValidPath(req.params.tainted) ) {
+ axios.get(baseURL + req.params.tainted); // OK
+ }
+ if (!isValidPath(req.params.tainted) ) {
+ axios.get(baseURL + req.params.tainted); // SSRF
+ } else {
+ axios.get(baseURL + req.params.tainted); // OK
+ }
+
+ // Blacklists are not safe
+ if (!req.query.tainted.match(/^[/\.%]+$/)) {
+ axios.get("test.com/" + req.query.tainted); // SSRF
+ }
+ if (!isInBlacklist(req.params.tainted) ) {
+ axios.get(baseURL + req.params.tainted); // SSRF
+ }
+
+ if (!isValidPath(req.params.tainted)) {
+ return;
+ }
+
+ axios.get("test.com/" + req.query.tainted); // OK - False Positive
+});
+
+const isValidPath = path => path.match(/^[0-9a-z]+$/);
+
+const isInBlackList = path => path.match(/^[/\.%]+$/);
diff --git a/javascript/ql/test/experimental/Security/CWE-918/check-validator.js b/javascript/ql/test/experimental/Security/CWE-918/check-validator.js
new file mode 100644
index 00000000000..dfe3314b07b
--- /dev/null
+++ b/javascript/ql/test/experimental/Security/CWE-918/check-validator.js
@@ -0,0 +1,98 @@
+// dependencies
+const axios = require('axios');
+const express = require('express');
+const validator = require('validator');
+
+// start
+const app = express();
+
+app.get("/check-with-axios", req => {
+ // alphanumeric
+ if (validator.isAlphanumeric(req.query.tainted)) {
+ axios.get("test.com/" + req.query.tainted); // OK
+ }
+ if (isAlphanumeric(req.query.tainted)) {
+ axios.get("test.com/" + req.query.tainted); // SSRF
+ }
+ if (validAlphanumeric(req.query.tainted)) {
+ axios.get("test.com/" + req.query.tainted); // OK
+ }
+ if (validAlpha(req.query.tainted)) {
+ axios.get("test.com/" + req.query.tainted); // OK
+ }
+ if (validNumber(req.query.tainted)) {
+ axios.get("test.com/" + req.query.tainted); // OK
+ }
+ if (wrongValidation(req.query.tainted)) {
+ axios.get("test.com/" + req.query.tainted); // SSRF
+ }
+
+ // numbers
+ if (validHexadecimal(req.query.tainted)) {
+ axios.get("test.com/" + req.query.tainted); // OK
+ }
+ if (validHexaColor(req.query.tainted)) {
+ axios.get("test.com/" + req.query.tainted); // OK
+ }
+ if (validDecimal(req.query.tainted)) {
+ axios.get("test.com/" + req.query.tainted); // OK
+ }
+ if (validFloat(req.query.tainted)) {
+ axios.get("test.com/" + req.query.tainted); // OK
+ }
+ if (validInt(req.query.tainted)) {
+ axios.get("test.com/" + req.query.tainted); // OK
+ }
+ if (validOctal(req.query.tainted)) {
+ axios.get("test.com/" + req.query.tainted); // OK
+ }
+ if (validHexa(req.query.tainted)) {
+ axios.get("test.com/" + req.query.tainted); // OK. False Positive
+ }
+
+ // with simple assignation
+ const numberURL = req.query.tainted;
+ if (validNumber(numberURL)) {
+ axios.get("test.com/" + numberURL); // OK
+ }
+ if (validNumber(numberURL)) {
+ axios.get("test.com/" + req.query.tainted); // OK. False Positive
+ }
+ if (validNumber(req.query.tainted)) {
+ axios.get("test.com/" + numberURL); // OK. False Positive
+ }
+
+ if (validHexadecimal(req.query.tainted) || validHexaColor(req.query.tainted) ||
+ validDecimal(req.query.tainted) || validFloat(req.query.tainted) || validInt(req.query.tainted) ||
+ validNumber(req.query.tainted) || validOctal(req.query.tainted)) {
+ axios.get("test.com/" + req.query.tainted); // OK. False Positive
+ }
+});
+
+// safe validators
+const validAlphanumeric = url => validator.isAlphanumeric(url);
+
+const validAlpha = url => validator.isAlpha(url);
+
+const validDecimal = url => validator.isDecimal(url);
+
+const validFloat = url => validator.isFloat(url);
+
+const validInt = url => validator.isInt(url);
+
+const validNumber = url => validator.isNumeric(url);
+
+const validOctal = url => validator.isOctal(url);
+
+const validHexa = url => validator.isHexadecimal(url) || validator.isHexColor(url);
+
+const validHexadecimal = url => validator.isHexadecimal(url);
+
+const validHexaColor = url => validator.isHexColor(url);
+
+const validUUID = url => validator.isUUID(url);
+
+// unsafe validators
+const wrongValidation = url => validator.isByteLength(url, {min:4,max:8});
+
+const isAlphanumeric = url => true;
From 629efb85fb50188f6e2a1b9153059f698b2a4a1f Mon Sep 17 00:00:00 2001
From: Nati Pesaresi
Date: Thu, 2 Sep 2021 17:55:09 -0300
Subject: [PATCH 006/471] ternary operator
---
.../experimental/Security/CWE-918/SSRF.qll | 61 +++++-
.../Security/CWE-918/ternary-operator.js | 182 ++++++++++++++++++
2 files changed, 242 insertions(+), 1 deletion(-)
create mode 100644 javascript/ql/test/experimental/Security/CWE-918/ternary-operator.js
diff --git a/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll b/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll
index 8b868d0c215..3cfde23453f 100644
--- a/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll
+++ b/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll
@@ -20,7 +20,66 @@ class Configuration extends TaintTracking::Configuration {
}
override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode nd) {
nd instanceof IntegerCheck or
- nd instanceof ValidatorCheck
+ nd instanceof ValidatorCheck or
+ nd instanceof TernaryOperatorSanitizerGuard
+ }
+}
+
+/** TODO add comment */
+class TernaryOperatorSanitizerGuard extends TaintTracking::SanitizerGuardNode {
+ TaintTracking::SanitizerGuardNode originalGuard;
+
+ TernaryOperatorSanitizerGuard() {
+ exists(DataFlow::Node falseNode |
+ this.getAPredecessor+() = falseNode and
+ falseNode.asExpr().(BooleanLiteral).mayHaveBooleanValue(false)
+ ) and
+ this.getAPredecessor+() = originalGuard and
+ not this.asExpr() instanceof LogicalBinaryExpr
+ }
+
+ override predicate sanitizes(boolean outcome, Expr e) {
+ not this.asExpr() instanceof LogNotExpr and
+ originalGuard.sanitizes(outcome, e)
+ or
+ exists(boolean originalOutcome |
+ this.asExpr() instanceof LogNotExpr and
+ originalGuard.sanitizes(originalOutcome, e) and
+ (
+ originalOutcome = true and outcome = false
+ or
+ originalOutcome = false and outcome = true
+ )
+ )
+ }
+}
+
+/** TODO add comment */
+class TernaryOperatorSanitizer extends RequestForgery::Sanitizer {
+ TernaryOperatorSanitizer() {
+ exists(
+ TaintTracking::SanitizerGuardNode guard, IfStmt ifStmt, DataFlow::Node taintedInput, boolean outcome,
+ Stmt r, DataFlow::Node falseNode
+ |
+ ifStmt.getCondition().flow().getAPredecessor+() = guard and
+ ifStmt.getCondition().flow().getAPredecessor+() = falseNode and
+ falseNode.asExpr().(BooleanLiteral).mayHaveBooleanValue(false) and
+ not ifStmt.getCondition() instanceof LogicalBinaryExpr and
+ guard.sanitizes(outcome, taintedInput.asExpr()) and
+ (
+ outcome = true and r = ifStmt.getThen() and not ifStmt.getCondition() instanceof LogNotExpr
+ or
+ outcome = false and r = ifStmt.getElse() and not ifStmt.getCondition() instanceof LogNotExpr
+ or
+ outcome = false and r = ifStmt.getThen() and ifStmt.getCondition() instanceof LogNotExpr
+ or
+ outcome = true and r = ifStmt.getElse() and ifStmt.getCondition() instanceof LogNotExpr
+ ) and
+ r.getFirstControlFlowNode()
+ .getBasicBlock()
+ .(ReachableBasicBlock)
+ .dominates(this.getBasicBlock())
+ )
}
}
diff --git a/javascript/ql/test/experimental/Security/CWE-918/ternary-operator.js b/javascript/ql/test/experimental/Security/CWE-918/ternary-operator.js
new file mode 100644
index 00000000000..26e2607a80f
--- /dev/null
+++ b/javascript/ql/test/experimental/Security/CWE-918/ternary-operator.js
@@ -0,0 +1,182 @@
+const express = require('express');
+const app = express();
+
+app.use(express.json());
+
+app.get('/direct-ternary-operator', function (req, res) {
+ let taintedURL = req.params.url
+
+ let v = req.params.url ? req.params.url == "someURL" : false
+ if (v) {
+ req_frontend_restclient.get(req.params.url) // OK
+ }
+
+ let v1 = taintedURL ? taintedURL == "someURL" : false
+ if (v1) {
+ req_frontend_restclient.get(taintedURL) // OK
+ }
+
+ let v2 = taintedURL ? valid(taintedURL) : false
+ if (v2) {
+ req_frontend_restclient.get(taintedURL) // OK
+ }
+
+ let v3 = req.params.url ? valid(req.params.url) : false
+ if (v3) {
+ req_frontend_restclient.get(req.params.url) // OK
+ }
+
+ let v4 = req.params.url == undefined ? false : valid(req.params.url)
+ if (v4) {
+ req_frontend_restclient.get(req.params.url) // OK
+ }
+
+ let v5 = req.params.url == undefined ? true : valid(req.params.url)
+ if (v5) {
+ req_frontend_restclient.get(req.params.url) // SSRF
+ }
+
+ let v6 = req.params.url ? valid(req.params.url) : true
+ if (v6) {
+ req_frontend_restclient.get(req.params.url) // SSRF
+ }
+
+ let f = false
+ let v7 = req.params.url ? valid(req.params.url) : true
+ if (v7) {
+ req_frontend_restclient.get(req.params.url) // SSRF
+ }
+
+ let v8 = req.params.url == undefined ? false : valid(req.params.url)
+ if (!v8) {
+ return
+ }
+ req_frontend_restclient.get(req.params.url) // OK
+})
+
+app.get('/functions', function (req, res) {
+ let taintedURL = req.params.url
+
+ if (valid2(taintedURL)) {
+ req_frontend_restclient.get(taintedURL) // OK
+ }
+
+ if (!invalid(taintedURL)) {
+ req_frontend_restclient.get(taintedURL) // False positive
+ }
+
+ if (valid2(req.params.url)){
+ req_frontend_restclient.get(req.params.url) // OK
+ }
+
+ if (!assertAlphanumeric(req.params.url)) {
+ return
+ }
+ req_frontend_restclient.get(req.params.url); // OK
+})
+
+app.get('/normal-use-of-ternary-operator', function (req, res) {
+ let taintedURL = req.params.url
+
+ let url = valid(req.params.url) ? req.params.url : undefined
+ req_frontend_restclient.get(url) // OK
+
+ let url = valid(taintedURL) ? taintedURL : undefined
+ req_frontend_restclient.get(url) // OK
+
+ let url4 = req.params.url.match(/^[\w.-]+$/) ? req.params.url : undefined
+ req_frontend_restclient.get(url4) // OK
+})
+
+app.get('/throw-errors', function (req, res) {
+ req_frontend_restclient.get(valid3(req.params.url)) // False positive
+
+ req_frontend_restclient.get(assertOther(req.params.url)); // False positive
+
+ req_frontend_restclient.get(assertOther2(req.params.url)); // False positive
+});
+
+app.get('/bad-endpoint', function (req, res) {
+ req_frontend_restclient.get(req.params.url); // SSRF
+
+ const valid = req.params.url ? req.params.url == "someURL" : false
+ if (!valid) {
+ throw new Error(`Invalid parameter: "${req.params.url}", must be alphanumeric`);
+ }
+ req_frontend_restclient.get(req.params.url); // OK
+})
+
+
+app.get('/bad-endpoint-variable', function (req, res) {
+ let taintedURL = req.params.url
+ req_frontend_restclient.get(taintedURL); // SSRF
+
+ const valid = taintedURL ? taintedURL == "someURL" : false
+ if (!valid) {
+ return
+ }
+ req_frontend_restclient.get(taintedURL); // False positive
+})
+
+app.get('/not-invalid', function (req, res) {
+ const invalidParam = req.params.url ? !Number.isInteger(req.params.url) : false
+ if (invalidParam) {
+ return
+ }
+ req_frontend_restclient.get(req.params.url); // False positive
+})
+
+
+app.get('/bad-endpoint-2', function (req, res) {
+ other(req.params.url)
+})
+
+function other(taintedURL) {
+ req_frontend_restclient.get(taintedURL); // SSRF
+
+ const valid = taintedURL ? taintedURL == "someURL" : false
+ if (!valid) {
+ return
+ }
+ req_frontend_restclient.get(taintedURL); // False positive
+}
+
+function assertAlphanumeric(value) {
+ return value ? value.match(/^[\w.-]+$/) : false;
+}
+
+function assertOther(value) {
+ const valid = value ? !!value.match(/^[\w.-]+$/) : false;
+ if (!valid) {
+ throw new Error(`Invalid parameter: "${value}", must be alphanumeric`);
+ }
+ return value;
+}
+
+function assertOther2(value) {
+ const valid = value ? value.match(/^[\w.-]+$/) : false;
+ if (!valid) {
+ throw new Error(`Invalid parameter: "${value}", must be alphanumeric`);
+ }
+ return value;
+}
+
+function invalid(value) {
+ return value ? !Number.isInteger(value) : true
+}
+
+function valid(value) {
+ return value.match(/^[\w.-]+$/)
+}
+
+function valid2(value) {
+ return value ? value == "someURL" : false
+}
+
+function valid3(value) {
+ const valid = value ? value == "someURL" : false
+ if (!valid) {
+ throw new Error(`Invalid parameter: "${value}", must be alphanumeric`);
+ }
+ return value;
+}
\ No newline at end of file
From 0b0ac8317cbebe9ed78e90d83e42cc77d00ef1e8 Mon Sep 17 00:00:00 2001
From: luciaromeroML
Date: Fri, 17 Sep 2021 18:05:52 -0300
Subject: [PATCH 007/471] format ql code
---
.../src/experimental/Security/CWE-918/SSRF.ql | 3 +-
.../experimental/Security/CWE-918/SSRF.qll | 134 +++++++++---------
2 files changed, 69 insertions(+), 68 deletions(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-918/SSRF.ql b/javascript/ql/src/experimental/Security/CWE-918/SSRF.ql
index b4124e38cc3..f13f5fae14e 100644
--- a/javascript/ql/src/experimental/Security/CWE-918/SSRF.ql
+++ b/javascript/ql/src/experimental/Security/CWE-918/SSRF.ql
@@ -14,5 +14,6 @@ import SSRF
import DataFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::Node request
-where cfg.hasFlowPath(source, sink) and request = sink.getNode().(RequestForgery::Sink).getARequest()
+where
+ cfg.hasFlowPath(source, sink) and request = sink.getNode().(RequestForgery::Sink).getARequest()
select sink, source, sink, "The URL of this request depends on a user-provided value"
diff --git a/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll b/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll
index 3cfde23453f..698e1e8f379 100644
--- a/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll
+++ b/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll
@@ -1,72 +1,72 @@
import javascript
-
import semmle.javascript.security.dataflow.RequestForgeryCustomizations
import semmle.javascript.security.dataflow.UrlConcatenation
class Configuration extends TaintTracking::Configuration {
- Configuration() { this = "SSRF" }
+ Configuration() { this = "SSRF" }
- override predicate isSource(DataFlow::Node source) { source instanceof RequestForgery::Source }
+ override predicate isSource(DataFlow::Node source) { source instanceof RequestForgery::Source }
- override predicate isSink(DataFlow::Node sink) { sink instanceof RequestForgery::Sink }
+ override predicate isSink(DataFlow::Node sink) { sink instanceof RequestForgery::Sink }
- override predicate isSanitizer(DataFlow::Node node) {
- super.isSanitizer(node) or
- node instanceof RequestForgery::Sanitizer
- }
+ override predicate isSanitizer(DataFlow::Node node) {
+ super.isSanitizer(node) or
+ node instanceof RequestForgery::Sanitizer
+ }
- override predicate isSanitizerEdge(DataFlow::Node source, DataFlow::Node sink) {
- sanitizingPrefixEdge(source, sink)
- }
- override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode nd) {
- nd instanceof IntegerCheck or
- nd instanceof ValidatorCheck or
- nd instanceof TernaryOperatorSanitizerGuard
- }
+ override predicate isSanitizerEdge(DataFlow::Node source, DataFlow::Node sink) {
+ sanitizingPrefixEdge(source, sink)
+ }
+
+ override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode nd) {
+ nd instanceof IntegerCheck or
+ nd instanceof ValidatorCheck or
+ nd instanceof TernaryOperatorSanitizerGuard
+ }
}
/** TODO add comment */
class TernaryOperatorSanitizerGuard extends TaintTracking::SanitizerGuardNode {
- TaintTracking::SanitizerGuardNode originalGuard;
+ TaintTracking::SanitizerGuardNode originalGuard;
- TernaryOperatorSanitizerGuard() {
+ TernaryOperatorSanitizerGuard() {
exists(DataFlow::Node falseNode |
- this.getAPredecessor+() = falseNode and
- falseNode.asExpr().(BooleanLiteral).mayHaveBooleanValue(false)
+ this.getAPredecessor+() = falseNode and
+ falseNode.asExpr().(BooleanLiteral).mayHaveBooleanValue(false)
) and
- this.getAPredecessor+() = originalGuard and
+ this.getAPredecessor+() = originalGuard and
not this.asExpr() instanceof LogicalBinaryExpr
- }
+ }
- override predicate sanitizes(boolean outcome, Expr e) {
- not this.asExpr() instanceof LogNotExpr and
- originalGuard.sanitizes(outcome, e)
+ override predicate sanitizes(boolean outcome, Expr e) {
+ not this.asExpr() instanceof LogNotExpr and
+ originalGuard.sanitizes(outcome, e)
or
exists(boolean originalOutcome |
- this.asExpr() instanceof LogNotExpr and
- originalGuard.sanitizes(originalOutcome, e) and
- (
- originalOutcome = true and outcome = false
+ this.asExpr() instanceof LogNotExpr and
+ originalGuard.sanitizes(originalOutcome, e) and
+ (
+ originalOutcome = true and outcome = false
or
originalOutcome = false and outcome = true
- )
+ )
)
- }
+ }
}
/** TODO add comment */
class TernaryOperatorSanitizer extends RequestForgery::Sanitizer {
- TernaryOperatorSanitizer() {
+ TernaryOperatorSanitizer() {
exists(
- TaintTracking::SanitizerGuardNode guard, IfStmt ifStmt, DataFlow::Node taintedInput, boolean outcome,
- Stmt r, DataFlow::Node falseNode
+ TaintTracking::SanitizerGuardNode guard, IfStmt ifStmt, DataFlow::Node taintedInput,
+ boolean outcome, Stmt r, DataFlow::Node falseNode
|
- ifStmt.getCondition().flow().getAPredecessor+() = guard and
- ifStmt.getCondition().flow().getAPredecessor+() = falseNode and
- falseNode.asExpr().(BooleanLiteral).mayHaveBooleanValue(false) and
- not ifStmt.getCondition() instanceof LogicalBinaryExpr and
- guard.sanitizes(outcome, taintedInput.asExpr()) and
- (
+ ifStmt.getCondition().flow().getAPredecessor+() = guard and
+ ifStmt.getCondition().flow().getAPredecessor+() = falseNode and
+ falseNode.asExpr().(BooleanLiteral).mayHaveBooleanValue(false) and
+ not ifStmt.getCondition() instanceof LogicalBinaryExpr and
+ guard.sanitizes(outcome, taintedInput.asExpr()) and
+ (
outcome = true and r = ifStmt.getThen() and not ifStmt.getCondition() instanceof LogNotExpr
or
outcome = false and r = ifStmt.getElse() and not ifStmt.getCondition() instanceof LogNotExpr
@@ -74,27 +74,25 @@ class TernaryOperatorSanitizer extends RequestForgery::Sanitizer {
outcome = false and r = ifStmt.getThen() and ifStmt.getCondition() instanceof LogNotExpr
or
outcome = true and r = ifStmt.getElse() and ifStmt.getCondition() instanceof LogNotExpr
- ) and
- r.getFirstControlFlowNode()
- .getBasicBlock()
- .(ReachableBasicBlock)
- .dominates(this.getBasicBlock())
+ ) and
+ r.getFirstControlFlowNode()
+ .getBasicBlock()
+ .(ReachableBasicBlock)
+ .dominates(this.getBasicBlock())
)
- }
+ }
}
/**
* Number.isInteger is a sanitizer guard because a number can't be used to exploit a SSRF.
*/
-class IntegerCheck extends TaintTracking::SanitizerGuardNode, DataFlow::CallNode{
- IntegerCheck() {
- this = DataFlow::globalVarRef("Number").getAMemberCall("isInteger")
- }
+class IntegerCheck extends TaintTracking::SanitizerGuardNode, DataFlow::CallNode {
+ IntegerCheck() { this = DataFlow::globalVarRef("Number").getAMemberCall("isInteger") }
- override predicate sanitizes(boolean outcome, Expr e){
- outcome = true and
- e = getArgument(0).asExpr()
- }
+ override predicate sanitizes(boolean outcome, Expr e) {
+ outcome = true and
+ e = getArgument(0).asExpr()
+ }
}
/**
@@ -103,17 +101,19 @@ class IntegerCheck extends TaintTracking::SanitizerGuardNode, DataFlow::CallNode
* checking that source is a number (any type of number) or an alphanumeric value.
*/
class ValidatorCheck extends TaintTracking::SanitizerGuardNode, DataFlow::CallNode {
- ValidatorCheck() {
- exists(
- DataFlow::SourceNode mod, string method |
- mod = DataFlow::moduleImport("validator") and
- this = mod.getAChainedMethodCall(method)
- and method in ["isAlphanumeric", "isAlpha", "isDecimal", "isFloat",
- "isHexadecimal", "isHexColor", "isInt", "isNumeric", "isOctal", "isUUID"]
- )
- }
- override predicate sanitizes(boolean outcome, Expr e){
- outcome = true and
- e = getArgument(0).asExpr()
- }
+ ValidatorCheck() {
+ exists(DataFlow::SourceNode mod, string method |
+ mod = DataFlow::moduleImport("validator") and
+ this = mod.getAChainedMethodCall(method) and
+ method in [
+ "isAlphanumeric", "isAlpha", "isDecimal", "isFloat", "isHexadecimal", "isHexColor",
+ "isInt", "isNumeric", "isOctal", "isUUID"
+ ]
+ )
+ }
+
+ override predicate sanitizes(boolean outcome, Expr e) {
+ outcome = true and
+ e = getArgument(0).asExpr()
+ }
}
From 25065bc986c3f7a4ffc8c98029053809b33833a6 Mon Sep 17 00:00:00 2001
From: luciaromeroML
Date: Fri, 17 Sep 2021 18:07:04 -0300
Subject: [PATCH 008/471] simplifying sentence
---
javascript/ql/src/experimental/Security/CWE-918/SSRF.qll | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll b/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll
index 698e1e8f379..916438c31fc 100644
--- a/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll
+++ b/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll
@@ -30,10 +30,7 @@ class TernaryOperatorSanitizerGuard extends TaintTracking::SanitizerGuardNode {
TaintTracking::SanitizerGuardNode originalGuard;
TernaryOperatorSanitizerGuard() {
- exists(DataFlow::Node falseNode |
- this.getAPredecessor+() = falseNode and
- falseNode.asExpr().(BooleanLiteral).mayHaveBooleanValue(false)
- ) and
+ this.getAPredecessor+().asExpr().(BooleanLiteral).mayHaveBooleanValue(false) and
this.getAPredecessor+() = originalGuard and
not this.asExpr() instanceof LogicalBinaryExpr
}
From f348a5ce47be97ba77eb77423147b867042c1910 Mon Sep 17 00:00:00 2001
From: luciaromeroML
Date: Fri, 17 Sep 2021 18:25:14 -0300
Subject: [PATCH 009/471] adding comments to some functions
---
.../experimental/Security/CWE-918/SSRF.qll | 77 ++++++++++++-------
1 file changed, 50 insertions(+), 27 deletions(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll b/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll
index 916438c31fc..5488d60719a 100644
--- a/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll
+++ b/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll
@@ -25,33 +25,17 @@ class Configuration extends TaintTracking::Configuration {
}
}
-/** TODO add comment */
-class TernaryOperatorSanitizerGuard extends TaintTracking::SanitizerGuardNode {
- TaintTracking::SanitizerGuardNode originalGuard;
-
- TernaryOperatorSanitizerGuard() {
- this.getAPredecessor+().asExpr().(BooleanLiteral).mayHaveBooleanValue(false) and
- this.getAPredecessor+() = originalGuard and
- not this.asExpr() instanceof LogicalBinaryExpr
- }
-
- override predicate sanitizes(boolean outcome, Expr e) {
- not this.asExpr() instanceof LogNotExpr and
- originalGuard.sanitizes(outcome, e)
- or
- exists(boolean originalOutcome |
- this.asExpr() instanceof LogNotExpr and
- originalGuard.sanitizes(originalOutcome, e) and
- (
- originalOutcome = true and outcome = false
- or
- originalOutcome = false and outcome = true
- )
- )
- }
-}
-
-/** TODO add comment */
+/**
+ * This sanitizers models the next example:
+ * let valid = req.params.id ? Number.isInteger(req.params.id) : false
+ * if (valid) { sink(req.params.id) }
+ *
+ * This sanitizer models this way of using ternary operators,
+ * when the sanitizer guard is used as any of the branches
+ * instead of being used as the condition.
+ *
+ * This sanitizer sanitize the corresponding if statement branch.
+ */
class TernaryOperatorSanitizer extends RequestForgery::Sanitizer {
TernaryOperatorSanitizer() {
exists(
@@ -80,6 +64,45 @@ class TernaryOperatorSanitizer extends RequestForgery::Sanitizer {
}
}
+/**
+ * This sanitizer guard is another way of modeling the example from above
+ * In this case:
+ * let valid = req.params.id ? Number.isInteger(req.params.id) : false
+ * if (!valid) { return }
+ * sink(req.params.id)
+ *
+ * The previous sanitizer is not enough,
+ * because we are sanitizing the entire if statement branch
+ * but we need to sanitize the use of this variable from now on.
+ *
+ * Thats why we model this sanitizer guard which says that
+ * the result of the ternary operator execution is a sanitizer guard.
+ */
+class TernaryOperatorSanitizerGuard extends TaintTracking::SanitizerGuardNode {
+ TaintTracking::SanitizerGuardNode originalGuard;
+
+ TernaryOperatorSanitizerGuard() {
+ this.getAPredecessor+().asExpr().(BooleanLiteral).mayHaveBooleanValue(false) and
+ this.getAPredecessor+() = originalGuard and
+ not this.asExpr() instanceof LogicalBinaryExpr
+ }
+
+ override predicate sanitizes(boolean outcome, Expr e) {
+ not this.asExpr() instanceof LogNotExpr and
+ originalGuard.sanitizes(outcome, e)
+ or
+ exists(boolean originalOutcome |
+ this.asExpr() instanceof LogNotExpr and
+ originalGuard.sanitizes(originalOutcome, e) and
+ (
+ originalOutcome = true and outcome = false
+ or
+ originalOutcome = false and outcome = true
+ )
+ )
+ }
+}
+
/**
* Number.isInteger is a sanitizer guard because a number can't be used to exploit a SSRF.
*/
From 520a2da8ab6c4d1d1b7496d20aec2bf9d99d7419 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Fri, 24 Sep 2021 14:41:50 +0200
Subject: [PATCH 010/471] Python: Add tests for asyncpg
---
.../frameworks/asyncpg/ConceptsTest.expected | 0
.../frameworks/asyncpg/ConceptsTest.ql | 2 +
.../frameworks/asyncpg/FileSystemAccess.py | 29 ++++++
.../frameworks/asyncpg/SqlExecution.py | 94 +++++++++++++++++++
4 files changed, 125 insertions(+)
create mode 100644 python/ql/test/library-tests/frameworks/asyncpg/ConceptsTest.expected
create mode 100644 python/ql/test/library-tests/frameworks/asyncpg/ConceptsTest.ql
create mode 100644 python/ql/test/library-tests/frameworks/asyncpg/FileSystemAccess.py
create mode 100644 python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py
diff --git a/python/ql/test/library-tests/frameworks/asyncpg/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/asyncpg/ConceptsTest.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/python/ql/test/library-tests/frameworks/asyncpg/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/asyncpg/ConceptsTest.ql
new file mode 100644
index 00000000000..b557a0bccb6
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/asyncpg/ConceptsTest.ql
@@ -0,0 +1,2 @@
+import python
+import experimental.meta.ConceptsTest
diff --git a/python/ql/test/library-tests/frameworks/asyncpg/FileSystemAccess.py b/python/ql/test/library-tests/frameworks/asyncpg/FileSystemAccess.py
new file mode 100644
index 00000000000..1b9434bc1cd
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/asyncpg/FileSystemAccess.py
@@ -0,0 +1,29 @@
+import asyncio
+import asyncpg
+
+async def test_connection():
+ conn = await asyncpg.connect()
+
+ try:
+ # The file-like object is passed in as a keyword-only argument.
+ # See https://magicstack.github.io/asyncpg/current/api/index.html#asyncpg.connection.Connection.copy_from_query
+ await conn.copy_from_query("sql", output="filepath") # $ MISSING: getAPathArgument="filepath" getSql="sql"
+ await conn.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ MISSING: getAPathArgument="filepath" getSql="sql"
+
+ await conn.copy_from_table("table", output="filepath") # $ MISSING: getAPathArgument="filepath"
+ await conn.copy_to_table("table", source="filepath") # $ MISSING: getAPathArgument="filepath"
+
+ finally:
+ await conn.close()
+
+async def test_connection_pool():
+ pool = await asyncpg.create_pool()
+
+ try:
+ await pool.copy_from_query("sql", output="filepath") # $ MISSING: getAPathArgument="filepath"
+ await pool.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ MISSING: getAPathArgument="filepath"
+ await pool.copy_from_table("table", output="filepath") # $ MISSING: getAPathArgument="filepath"
+ await pool.copy_to_table("table", source="filepath") # $ MISSING: getAPathArgument="filepath"
+
+ finally:
+ await pool.close()
diff --git a/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py b/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py
new file mode 100644
index 00000000000..4d9bf9c0210
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py
@@ -0,0 +1,94 @@
+import asyncio
+import asyncpg
+
+async def test_connection():
+ conn = await asyncpg.connect()
+
+ try:
+ await conn.copy_from_query("sql", output="filepath") # $ MISSING: getSql="sql" getAPathArgument="filepath"
+ await conn.execute("sql") # $ MISSING: getSql="sql"
+ await conn.executemany("sql") # $ MISSING: getSql="sql"
+ await conn.fetch("sql") # $ MISSING: getSql="sql"
+ await conn.fetchrow("sql") # $ MISSING: getSql="sql"
+ await conn.fetchval("sql") # $ MISSING: getSql="sql"
+
+ finally:
+ await conn.close()
+
+
+async def test_prepared_statement():
+ conn = await asyncpg.connect()
+
+ try:
+ pstmt = await conn.prepare('psql')
+ pstmt.executemany() # $ MISSING: getSql="psql"
+ pstmt.fetch() # $ MISSING: getSql="psql"
+ pstmt.fetchrow() # $ MISSING: getSql="psql"
+ pstmt.fetchval() # $ MISSING: getSql="psql"
+
+ finally:
+ await conn.close()
+
+# The sql statement is executed when the `CursorFactory` (obtained by e.g. `conn.cursor()`) is awaited.
+# See https://magicstack.github.io/asyncpg/current/api/index.html#asyncpg.cursor.CursorFactory
+async def test_cursor():
+ conn = await asyncpg.connect()
+
+ try:
+ async with conn.transaction():
+ cursor = await conn.cursor("sql") # $ MISSING: getSql="sql"
+ await cursor.fetch()
+
+ pstmt = await conn.prepare('psql')
+ pcursor = await pstmt.cursor() # $ MISSING: getSql="psql"
+ await pcursor.fetch()
+
+ async for record in conn.cursor("sql"): # $ MISSING: getSql="sql"
+ pass
+
+ async for record in pstmt.cursor(): # $ MISSING: getSql="psql"
+ pass
+
+ cursor_factory = conn.cursor("sql")
+ cursor = await cursor_factory # $ MISSING: getSql="sql"
+
+ pcursor_factory = pstmt.cursor()
+ pcursor = await pcursor_factory # $ MISSING: getSql="psql"
+
+ finally:
+ await conn.close()
+
+async def test_connection_pool():
+ pool = await asyncpg.create_pool()
+
+ try:
+ await pool.copy_from_query("sql", output="filepath") # $ MISSING: getSql="sql" getAPathArgument="filepath"
+ await pool.execute("sql") # $ MISSING: getSql="sql"
+ await pool.executemany("sql") # $ MISSING: getSql="sql"
+ await pool.fetch("sql") # $ MISSING: getSql="sql"
+ await pool.fetchrow("sql") # $ MISSING: getSql="sql"
+ await pool.fetchval("sql") # $ MISSING: getSql="sql"
+
+ async with pool.acquire() as conn:
+ await conn.execute("sql") # $ MISSING: getSql="sql"
+
+ conn = await pool.acquire()
+ try:
+ await conn.fetch("sql") # $ MISSING: getSql="sql"
+ finally:
+ await pool.release(conn)
+
+ finally:
+ await pool.close()
+
+ async with asyncpg.create_pool() as pool:
+ await pool.execute("sql") # $ MISSING: getSql="sql"
+
+ async with pool.acquire() as conn:
+ await conn.execute("sql") # $ MISSING: getSql="sql"
+
+ conn = await pool.acquire()
+ try:
+ await conn.fetch("sql") # $ MISSING: getSql="sql"
+ finally:
+ await pool.release(conn)
From 15b07bfcc0654228179eebe055efd001bac03c14 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Mon, 27 Sep 2021 14:15:58 +0200
Subject: [PATCH 011/471] Python: Model sql executions
---
python/ql/lib/semmle/python/Frameworks.qll | 1 +
.../lib/semmle/python/frameworks/Asyncpg.qll | 118 ++++++++++++++++++
.../frameworks/asyncpg/FileSystemAccess.py | 8 +-
.../frameworks/asyncpg/SqlExecution.py | 46 +++----
4 files changed, 146 insertions(+), 27 deletions(-)
create mode 100644 python/ql/lib/semmle/python/frameworks/Asyncpg.qll
diff --git a/python/ql/lib/semmle/python/Frameworks.qll b/python/ql/lib/semmle/python/Frameworks.qll
index b3ff235c3ee..e13fc96ae17 100644
--- a/python/ql/lib/semmle/python/Frameworks.qll
+++ b/python/ql/lib/semmle/python/Frameworks.qll
@@ -6,6 +6,7 @@
// `docs/codeql/support/reusables/frameworks.rst`
private import semmle.python.frameworks.Aioch
private import semmle.python.frameworks.Aiohttp
+private import semmle.python.frameworks.Asyncpg
private import semmle.python.frameworks.ClickhouseDriver
private import semmle.python.frameworks.Cryptodome
private import semmle.python.frameworks.Cryptography
diff --git a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
new file mode 100644
index 00000000000..68a1a1cc471
--- /dev/null
+++ b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
@@ -0,0 +1,118 @@
+/**
+ * Provides classes modeling security-relevant aspects of the `asyncpg` PyPI package.
+ * See https://magicstack.github.io/asyncpg/.
+ */
+
+private import python
+private import semmle.python.dataflow.new.DataFlow
+private import semmle.python.Concepts
+private import semmle.python.ApiGraphs
+
+/** Provides models for the `asyncpg` PyPI package. */
+private module Asyncpg {
+ API::Node connectionPool() {
+ result = API::moduleImport("asyncpg").getMember("create_pool").getReturn().getAwaited()
+ }
+
+ API::Node connection() {
+ result = API::moduleImport("asyncpg").getMember("connect").getReturn().getAwaited()
+ or
+ result = connectionPool().getMember("acquire").getReturn().getAwaited()
+ }
+
+ private string queryMethodName(string queryArg) {
+ result in ["copy_from_query", "execute", "fetch", "fetchrow", "fetchval"] and
+ queryArg = "query"
+ or
+ result = "executemany" and
+ queryArg = "command"
+ }
+
+ class SqlExecutionOnConnection extends SqlExecution::Range, DataFlow::MethodCallNode {
+ string queryArg;
+
+ SqlExecutionOnConnection() {
+ this.calls([connectionPool().getAUse(), connection().getAUse()], queryMethodName(queryArg))
+ }
+
+ override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName(queryArg)] }
+ }
+
+ pragma[inline]
+ DataFlow::Node awaited(DataFlow::Node n) {
+ exists(Await await |
+ result.asExpr() = await and
+ await.getValue() = n.asExpr()
+ )
+ }
+
+ module PreparedStatement {
+ private DataFlow::TypeTrackingNode preparedStatementFactory(
+ DataFlow::TypeTracker t, DataFlow::Node sql
+ ) {
+ t.start() and
+ result = connection().getMember("prepare").getACall() and
+ sql in [
+ result.(DataFlow::CallCfgNode).getArg(0),
+ result.(DataFlow::CallCfgNode).getArgByName("query")
+ ]
+ or
+ exists(DataFlow::TypeTracker t2 | result = preparedStatementFactory(t2, sql).track(t2, t))
+ }
+
+ DataFlow::Node preparedStatementFactory(DataFlow::Node sql) {
+ preparedStatementFactory(DataFlow::TypeTracker::end(), sql).flowsTo(result)
+ }
+
+ private DataFlow::TypeTrackingNode preparedStatement(DataFlow::TypeTracker t, DataFlow::Node sql) {
+ t.start() and
+ result = awaited(preparedStatementFactory(sql))
+ or
+ exists(DataFlow::TypeTracker t2 | result = preparedStatement(t2, sql).track(t2, t))
+ }
+
+ DataFlow::Node preparedStatement(DataFlow::Node sql) {
+ preparedStatement(DataFlow::TypeTracker::end(), sql).flowsTo(result)
+ }
+
+ class PreparedStatementExecution extends SqlExecution::Range, DataFlow::MethodCallNode {
+ DataFlow::Node sql;
+
+ PreparedStatementExecution() {
+ this.calls(preparedStatement(sql), ["executemany", "fetch", "fetchrow", "fetchval"])
+ }
+
+ override DataFlow::Node getSql() { result = sql }
+ }
+ }
+
+ module Cursor {
+ private DataFlow::TypeTrackingNode cursorFactory(DataFlow::TypeTracker t, DataFlow::Node sql) {
+ // cursor created from connection
+ t.start() and
+ result = connection().getMember("cursor").getACall() and
+ sql in [
+ result.(DataFlow::CallCfgNode).getArg(0),
+ result.(DataFlow::CallCfgNode).getArgByName("query")
+ ]
+ or
+ // cursor created from prepared statement
+ t.start() and
+ result.(DataFlow::MethodCallNode).calls(PreparedStatement::preparedStatement(sql), "cursor")
+ or
+ exists(DataFlow::TypeTracker t2 | result = cursorFactory(t2, sql).track(t2, t))
+ }
+
+ DataFlow::Node cursorFactory(DataFlow::Node sql) {
+ cursorFactory(DataFlow::TypeTracker::end(), sql).flowsTo(result)
+ }
+
+ class CursorCreation extends SqlExecution::Range {
+ DataFlow::Node sql;
+
+ CursorCreation() { this = awaited(cursorFactory(sql)) }
+
+ override DataFlow::Node getSql() { result = sql }
+ }
+ }
+}
diff --git a/python/ql/test/library-tests/frameworks/asyncpg/FileSystemAccess.py b/python/ql/test/library-tests/frameworks/asyncpg/FileSystemAccess.py
index 1b9434bc1cd..0dc45cd9b1b 100644
--- a/python/ql/test/library-tests/frameworks/asyncpg/FileSystemAccess.py
+++ b/python/ql/test/library-tests/frameworks/asyncpg/FileSystemAccess.py
@@ -7,8 +7,8 @@ async def test_connection():
try:
# The file-like object is passed in as a keyword-only argument.
# See https://magicstack.github.io/asyncpg/current/api/index.html#asyncpg.connection.Connection.copy_from_query
- await conn.copy_from_query("sql", output="filepath") # $ MISSING: getAPathArgument="filepath" getSql="sql"
- await conn.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ MISSING: getAPathArgument="filepath" getSql="sql"
+ await conn.copy_from_query("sql", output="filepath") # $ getSql="sql" MISSING: getAPathArgument="filepath"
+ await conn.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ getSql="sql" MISSING: getAPathArgument="filepath"
await conn.copy_from_table("table", output="filepath") # $ MISSING: getAPathArgument="filepath"
await conn.copy_to_table("table", source="filepath") # $ MISSING: getAPathArgument="filepath"
@@ -20,8 +20,8 @@ async def test_connection_pool():
pool = await asyncpg.create_pool()
try:
- await pool.copy_from_query("sql", output="filepath") # $ MISSING: getAPathArgument="filepath"
- await pool.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ MISSING: getAPathArgument="filepath"
+ await pool.copy_from_query("sql", output="filepath") # $ getSql="sql" MISSING: getAPathArgument="filepath"
+ await pool.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ getSql="sql" MISSING: getAPathArgument="filepath"
await pool.copy_from_table("table", output="filepath") # $ MISSING: getAPathArgument="filepath"
await pool.copy_to_table("table", source="filepath") # $ MISSING: getAPathArgument="filepath"
diff --git a/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py b/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py
index 4d9bf9c0210..bac9df63d50 100644
--- a/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py
+++ b/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py
@@ -5,12 +5,12 @@ async def test_connection():
conn = await asyncpg.connect()
try:
- await conn.copy_from_query("sql", output="filepath") # $ MISSING: getSql="sql" getAPathArgument="filepath"
- await conn.execute("sql") # $ MISSING: getSql="sql"
- await conn.executemany("sql") # $ MISSING: getSql="sql"
- await conn.fetch("sql") # $ MISSING: getSql="sql"
- await conn.fetchrow("sql") # $ MISSING: getSql="sql"
- await conn.fetchval("sql") # $ MISSING: getSql="sql"
+ await conn.copy_from_query("sql", output="filepath") # $ getSql="sql" MISSING: getAPathArgument="filepath"
+ await conn.execute("sql") # $ getSql="sql"
+ await conn.executemany("sql") # $ getSql="sql"
+ await conn.fetch("sql") # $ getSql="sql"
+ await conn.fetchrow("sql") # $ getSql="sql"
+ await conn.fetchval("sql") # $ getSql="sql"
finally:
await conn.close()
@@ -20,11 +20,11 @@ async def test_prepared_statement():
conn = await asyncpg.connect()
try:
- pstmt = await conn.prepare('psql')
- pstmt.executemany() # $ MISSING: getSql="psql"
- pstmt.fetch() # $ MISSING: getSql="psql"
- pstmt.fetchrow() # $ MISSING: getSql="psql"
- pstmt.fetchval() # $ MISSING: getSql="psql"
+ pstmt = await conn.prepare("psql")
+ pstmt.executemany() # $ getSql="psql"
+ pstmt.fetch() # $ getSql="psql"
+ pstmt.fetchrow() # $ getSql="psql"
+ pstmt.fetchval() # $ getSql="psql"
finally:
await conn.close()
@@ -36,11 +36,11 @@ async def test_cursor():
try:
async with conn.transaction():
- cursor = await conn.cursor("sql") # $ MISSING: getSql="sql"
+ cursor = await conn.cursor("sql") # $ getSql="sql"
await cursor.fetch()
- pstmt = await conn.prepare('psql')
- pcursor = await pstmt.cursor() # $ MISSING: getSql="psql"
+ pstmt = await conn.prepare("psql")
+ pcursor = await pstmt.cursor() # $ getSql="psql"
await pcursor.fetch()
async for record in conn.cursor("sql"): # $ MISSING: getSql="sql"
@@ -50,10 +50,10 @@ async def test_cursor():
pass
cursor_factory = conn.cursor("sql")
- cursor = await cursor_factory # $ MISSING: getSql="sql"
+ cursor = await cursor_factory # $ getSql="sql"
pcursor_factory = pstmt.cursor()
- pcursor = await pcursor_factory # $ MISSING: getSql="psql"
+ pcursor = await pcursor_factory # $ getSql="psql"
finally:
await conn.close()
@@ -62,19 +62,19 @@ async def test_connection_pool():
pool = await asyncpg.create_pool()
try:
- await pool.copy_from_query("sql", output="filepath") # $ MISSING: getSql="sql" getAPathArgument="filepath"
- await pool.execute("sql") # $ MISSING: getSql="sql"
- await pool.executemany("sql") # $ MISSING: getSql="sql"
- await pool.fetch("sql") # $ MISSING: getSql="sql"
- await pool.fetchrow("sql") # $ MISSING: getSql="sql"
- await pool.fetchval("sql") # $ MISSING: getSql="sql"
+ await pool.copy_from_query("sql", output="filepath") # $ getSql="sql" MISSING: getAPathArgument="filepath"
+ await pool.execute("sql") # $ getSql="sql"
+ await pool.executemany("sql") # $ getSql="sql"
+ await pool.fetch("sql") # $ getSql="sql"
+ await pool.fetchrow("sql") # $ getSql="sql"
+ await pool.fetchval("sql") # $ getSql="sql"
async with pool.acquire() as conn:
await conn.execute("sql") # $ MISSING: getSql="sql"
conn = await pool.acquire()
try:
- await conn.fetch("sql") # $ MISSING: getSql="sql"
+ await conn.fetch("sql") # $ getSql="sql"
finally:
await pool.release(conn)
From f6311bf05176be7ed18a2da5104ac44ad10205a3 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Mon, 27 Sep 2021 14:32:55 +0200
Subject: [PATCH 012/471] Python: model other awaiting constructs
---
python/ql/lib/semmle/python/frameworks/Asyncpg.qll | 13 +++++++++++++
.../frameworks/asyncpg/SqlExecution.py | 4 ++--
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
index 68a1a1cc471..236ac9b9c48 100644
--- a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
+++ b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
@@ -38,12 +38,25 @@ private module Asyncpg {
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName(queryArg)] }
}
+ /**
+ * Holds if `result` is the result of awaiting `n`.
+ */
pragma[inline]
DataFlow::Node awaited(DataFlow::Node n) {
exists(Await await |
result.asExpr() = await and
await.getValue() = n.asExpr()
)
+ or
+ exists(AsyncFor asyncFor |
+ result.asExpr() = asyncFor.getTarget() and
+ asyncFor.getIter() = n.asExpr()
+ )
+ or
+ exists(AsyncWith asyncWith |
+ result.asExpr() = asyncWith.getContextExpr() and
+ asyncWith.getOptionalVars() = n.asExpr()
+ )
}
module PreparedStatement {
diff --git a/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py b/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py
index bac9df63d50..32477b61612 100644
--- a/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py
+++ b/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py
@@ -43,10 +43,10 @@ async def test_cursor():
pcursor = await pstmt.cursor() # $ getSql="psql"
await pcursor.fetch()
- async for record in conn.cursor("sql"): # $ MISSING: getSql="sql"
+ async for record in conn.cursor("sql"): # $ getSql="sql"
pass
- async for record in pstmt.cursor(): # $ MISSING: getSql="psql"
+ async for record in pstmt.cursor(): # $ getSql="psql"
pass
cursor_factory = conn.cursor("sql")
From 3c1206f8731f8908e6a67a6b4da126e111cc0bef Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Mon, 27 Sep 2021 16:41:01 +0200
Subject: [PATCH 013/471] Python: Model more awaiting construcs in API graphs.
Some unsatisfactory lack of understanding here.
---
python/ql/lib/semmle/python/ApiGraphs.qll | 39 +++++++++++++++++--
.../lib/semmle/python/frameworks/Asyncpg.qll | 9 +++++
.../frameworks/asyncpg/SqlExecution.py | 8 ++--
3 files changed, 49 insertions(+), 7 deletions(-)
diff --git a/python/ql/lib/semmle/python/ApiGraphs.qll b/python/ql/lib/semmle/python/ApiGraphs.qll
index 19a287df63a..bb44bde9343 100644
--- a/python/ql/lib/semmle/python/ApiGraphs.qll
+++ b/python/ql/lib/semmle/python/ApiGraphs.qll
@@ -485,6 +485,40 @@ module API {
)
}
+ /**
+ * Holds if `result` is the result of awaiting `awaitedValue`.
+ */
+ cached
+ DataFlow::Node awaited(DataFlow::Node awaitedValue) {
+ // `await` x
+ // - `awaitedValue` is `x`
+ // - `result` is `await x`
+ exists(Await await |
+ result.asExpr() = await and
+ await.getValue() = awaitedValue.asExpr()
+ )
+ or
+ // `async for x in l`
+ // - `awaitedValue` is `l`
+ // - `result` is `l` (should perhaps be `x`)
+ exists(AsyncFor asyncFor |
+ result.asExpr() = asyncFor.getTarget() and
+ // Morally, we should perhaps use asyncFor.getIter() = awaitedValue.asExpr()
+ // but that does not work.
+ asyncFor.getTarget() = awaitedValue.asExpr()
+ )
+ or
+ // `async with x as y`
+ // - `awaitedValue` is `x`
+ // - `result` is `x` (should probably be `y`)
+ exists(AsyncWith asyncWith |
+ result.asExpr() = asyncWith.getContextExpr() and
+ // Morally, we should perhaps use asyncWith.getOptionalVars() = awaitedValue.asExpr()
+ // but that does not work.
+ asyncWith.getContextExpr() = awaitedValue.asExpr()
+ )
+ }
+
/**
* Holds if `ref` is a use of a node that should have an incoming edge from `base` labeled
* `lbl` in the API graph.
@@ -516,10 +550,9 @@ module API {
)
or
// awaiting
- exists(Await await, DataFlow::Node awaitedValue |
+ exists(DataFlow::Node awaitedValue |
lbl = Label::await() and
- ref.asExpr() = await and
- await.getValue() = awaitedValue.asExpr() and
+ ref = awaited(awaitedValue) and
pred.flowsTo(awaitedValue)
)
)
diff --git a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
index 236ac9b9c48..ee0b3b1391f 100644
--- a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
+++ b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
@@ -43,16 +43,25 @@ private module Asyncpg {
*/
pragma[inline]
DataFlow::Node awaited(DataFlow::Node n) {
+ // `await` x
+ // - `awaitedValue` is `x`
+ // - `result` is `await x`
exists(Await await |
result.asExpr() = await and
await.getValue() = n.asExpr()
)
or
+ // `async for x in l`
+ // - `awaitedValue` is `l`
+ // - `result` is `x`
exists(AsyncFor asyncFor |
result.asExpr() = asyncFor.getTarget() and
asyncFor.getIter() = n.asExpr()
)
or
+ // `async with x as y`
+ // - `awaitedValue` is `x`
+ // - `result` is `y`
exists(AsyncWith asyncWith |
result.asExpr() = asyncWith.getContextExpr() and
asyncWith.getOptionalVars() = n.asExpr()
diff --git a/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py b/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py
index 32477b61612..a7f652dae10 100644
--- a/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py
+++ b/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py
@@ -70,7 +70,7 @@ async def test_connection_pool():
await pool.fetchval("sql") # $ getSql="sql"
async with pool.acquire() as conn:
- await conn.execute("sql") # $ MISSING: getSql="sql"
+ await conn.execute("sql") # $ getSql="sql"
conn = await pool.acquire()
try:
@@ -82,13 +82,13 @@ async def test_connection_pool():
await pool.close()
async with asyncpg.create_pool() as pool:
- await pool.execute("sql") # $ MISSING: getSql="sql"
+ await pool.execute("sql") # $ getSql="sql"
async with pool.acquire() as conn:
- await conn.execute("sql") # $ MISSING: getSql="sql"
+ await conn.execute("sql") # $ getSql="sql"
conn = await pool.acquire()
try:
- await conn.fetch("sql") # $ MISSING: getSql="sql"
+ await conn.fetch("sql") # $ getSql="sql"
finally:
await pool.release(conn)
From 1f2618b893e67feca17dc31742aa60e37a7c808c Mon Sep 17 00:00:00 2001
From: luciaromeroML
Date: Mon, 27 Sep 2021 17:37:11 -0300
Subject: [PATCH 014/471] new test case for unknown base url
---
.../ql/test/experimental/Security/CWE-918/SSRF.expected | 9 +++++++++
.../ql/test/experimental/Security/CWE-918/check-path.js | 3 +++
2 files changed, 12 insertions(+)
diff --git a/javascript/ql/test/experimental/Security/CWE-918/SSRF.expected b/javascript/ql/test/experimental/Security/CWE-918/SSRF.expected
index 5e1ffe1e982..a7bfb5d1f6c 100644
--- a/javascript/ql/test/experimental/Security/CWE-918/SSRF.expected
+++ b/javascript/ql/test/experimental/Security/CWE-918/SSRF.expected
@@ -37,6 +37,10 @@ nodes
| check-path.js:37:15:37:45 | 'test.c ... tainted |
| check-path.js:37:29:37:45 | req.query.tainted |
| check-path.js:37:29:37:45 | req.query.tainted |
+| check-path.js:45:13:45:44 | `${base ... inted}` |
+| check-path.js:45:13:45:44 | `${base ... inted}` |
+| check-path.js:45:26:45:42 | req.query.tainted |
+| check-path.js:45:26:45:42 | req.query.tainted |
| check-regex.js:24:15:24:42 | baseURL ... tainted |
| check-regex.js:24:15:24:42 | baseURL ... tainted |
| check-regex.js:24:25:24:42 | req.params.tainted |
@@ -113,6 +117,10 @@ edges
| check-path.js:37:29:37:45 | req.query.tainted | check-path.js:37:15:37:45 | 'test.c ... tainted |
| check-path.js:37:29:37:45 | req.query.tainted | check-path.js:37:15:37:45 | 'test.c ... tainted |
| check-path.js:37:29:37:45 | req.query.tainted | check-path.js:37:15:37:45 | 'test.c ... tainted |
+| check-path.js:45:26:45:42 | req.query.tainted | check-path.js:45:13:45:44 | `${base ... inted}` |
+| check-path.js:45:26:45:42 | req.query.tainted | check-path.js:45:13:45:44 | `${base ... inted}` |
+| check-path.js:45:26:45:42 | req.query.tainted | check-path.js:45:13:45:44 | `${base ... inted}` |
+| check-path.js:45:26:45:42 | req.query.tainted | check-path.js:45:13:45:44 | `${base ... inted}` |
| check-regex.js:24:25:24:42 | req.params.tainted | check-regex.js:24:15:24:42 | baseURL ... tainted |
| check-regex.js:24:25:24:42 | req.params.tainted | check-regex.js:24:15:24:42 | baseURL ... tainted |
| check-regex.js:24:25:24:42 | req.params.tainted | check-regex.js:24:15:24:42 | baseURL ... tainted |
@@ -164,6 +172,7 @@ edges
| check-path.js:24:13:24:65 | `/addre ... nted)}` | check-path.js:24:46:24:62 | req.query.tainted | check-path.js:24:13:24:65 | `/addre ... nted)}` | The URL of this request depends on a user-provided value |
| check-path.js:33:15:33:45 | 'test.c ... tainted | check-path.js:33:29:33:45 | req.query.tainted | check-path.js:33:15:33:45 | 'test.c ... tainted | The URL of this request depends on a user-provided value |
| check-path.js:37:15:37:45 | 'test.c ... tainted | check-path.js:37:29:37:45 | req.query.tainted | check-path.js:37:15:37:45 | 'test.c ... tainted | The URL of this request depends on a user-provided value |
+| check-path.js:45:13:45:44 | `${base ... inted}` | check-path.js:45:26:45:42 | req.query.tainted | check-path.js:45:13:45:44 | `${base ... inted}` | The URL of this request depends on a user-provided value |
| check-regex.js:24:15:24:42 | baseURL ... tainted | check-regex.js:24:25:24:42 | req.params.tainted | check-regex.js:24:15:24:42 | baseURL ... tainted | The URL of this request depends on a user-provided value |
| check-regex.js:31:15:31:45 | "test.c ... tainted | check-regex.js:31:29:31:45 | req.query.tainted | check-regex.js:31:15:31:45 | "test.c ... tainted | The URL of this request depends on a user-provided value |
| check-regex.js:34:15:34:42 | baseURL ... tainted | check-regex.js:34:25:34:42 | req.params.tainted | check-regex.js:34:15:34:42 | baseURL ... tainted | The URL of this request depends on a user-provided value |
diff --git a/javascript/ql/test/experimental/Security/CWE-918/check-path.js b/javascript/ql/test/experimental/Security/CWE-918/check-path.js
index 35681d58491..6ed1ab1fb7b 100644
--- a/javascript/ql/test/experimental/Security/CWE-918/check-path.js
+++ b/javascript/ql/test/experimental/Security/CWE-918/check-path.js
@@ -41,6 +41,9 @@ app.get('/check-with-axios', req => {
axios.get('test.com/' + req.query.tainted) // OK
}
+ let baseURL = require('config').base
+ axios.get(`${baseURL}${req.query.tainted}`); // SSRF
+
if(!isValidInput(req.query.tainted)) {
return;
}
From 1fc58e51a3838496a44fae3706530a5f7bbb62f1 Mon Sep 17 00:00:00 2001
From: luciaromeroML
Date: Mon, 27 Sep 2021 17:37:36 -0300
Subject: [PATCH 015/471] adding suggestion that removes sanitizer for unknown
base urls
---
.../src/experimental/Security/CWE-918/SSRF.qll | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll b/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll
index 5488d60719a..3bdca588c98 100644
--- a/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll
+++ b/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll
@@ -14,8 +14,22 @@ class Configuration extends TaintTracking::Configuration {
node instanceof RequestForgery::Sanitizer
}
+ private predicate hasSanitizingSubstring(DataFlow::Node nd) {
+ nd.getStringValue().regexpMatch(".*[?#].*")
+ or
+ hasSanitizingSubstring(StringConcatenation::getAnOperand(nd))
+ or
+ hasSanitizingSubstring(nd.getAPredecessor())
+ }
+
+ private predicate strictSanitizingPrefixEdge(DataFlow::Node source, DataFlow::Node sink) {
+ exists(DataFlow::Node operator, int n |
+ StringConcatenation::taintStep(source, sink, operator, n) and
+ hasSanitizingSubstring(StringConcatenation::getOperand(operator, [0 .. n - 1]))
+ )
+ }
override predicate isSanitizerEdge(DataFlow::Node source, DataFlow::Node sink) {
- sanitizingPrefixEdge(source, sink)
+ strictSanitizingPrefixEdge(source, sink)
}
override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode nd) {
From a5912ff76de38e9a9a63874c3e94c9cb3047e99a Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Tue, 28 Sep 2021 16:42:19 +0200
Subject: [PATCH 016/471] Python: Align implementations of `awaited`.
---
python/ql/lib/semmle/python/ApiGraphs.qll | 12 +++++-----
.../lib/semmle/python/frameworks/Asyncpg.qll | 22 ++++++++++---------
2 files changed, 18 insertions(+), 16 deletions(-)
diff --git a/python/ql/lib/semmle/python/ApiGraphs.qll b/python/ql/lib/semmle/python/ApiGraphs.qll
index bb44bde9343..4fd39e41491 100644
--- a/python/ql/lib/semmle/python/ApiGraphs.qll
+++ b/python/ql/lib/semmle/python/ApiGraphs.qll
@@ -500,21 +500,21 @@ module API {
or
// `async for x in l`
// - `awaitedValue` is `l`
- // - `result` is `l` (should perhaps be `x`)
+ // - `result` is `l` (should perhaps be `x`, but that should really be a read)
exists(AsyncFor asyncFor |
result.asExpr() = asyncFor.getTarget() and
- // Morally, we should perhaps use asyncFor.getIter() = awaitedValue.asExpr()
- // but that does not work.
+ // Morally, we should perhaps use asyncFor.getIter() = awaitedValue.asExpr(),
+ // but that is actually behind a read step rather than a flow step.
asyncFor.getTarget() = awaitedValue.asExpr()
)
or
// `async with x as y`
// - `awaitedValue` is `x`
- // - `result` is `x` (should probably be `y`)
+ // - `result` is `x` (should probably be `y` but it might not exist)
exists(AsyncWith asyncWith |
result.asExpr() = asyncWith.getContextExpr() and
- // Morally, we should perhaps use asyncWith.getOptionalVars() = awaitedValue.asExpr()
- // but that does not work.
+ // Morally, we should perhaps use asyncWith.getOptionalVars() = awaitedValue.asExpr(),
+ // but that might not exist.
asyncWith.getContextExpr() = awaitedValue.asExpr()
)
}
diff --git a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
index ee0b3b1391f..03697095260 100644
--- a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
+++ b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
@@ -42,29 +42,31 @@ private module Asyncpg {
* Holds if `result` is the result of awaiting `n`.
*/
pragma[inline]
- DataFlow::Node awaited(DataFlow::Node n) {
+ DataFlow::Node awaited(DataFlow::Node awaitedValue) {
// `await` x
// - `awaitedValue` is `x`
// - `result` is `await x`
exists(Await await |
result.asExpr() = await and
- await.getValue() = n.asExpr()
+ await.getValue() = awaitedValue.asExpr()
)
or
// `async for x in l`
- // - `awaitedValue` is `l`
- // - `result` is `x`
- exists(AsyncFor asyncFor |
+ // - `awaitedValue` is local source of `l`
+ // - `result` is `l`
+ exists(AsyncFor asyncFor, DataFlow::Node awaited |
result.asExpr() = asyncFor.getTarget() and
- asyncFor.getIter() = n.asExpr()
+ asyncFor.getIter() = awaited.asExpr() and
+ awaited.getALocalSource() = awaitedValue
)
or
// `async with x as y`
- // - `awaitedValue` is `x`
- // - `result` is `y`
- exists(AsyncWith asyncWith |
+ // - `awaitedValue` is local source of `x`
+ // - `result` is `x`
+ exists(AsyncWith asyncWith, DataFlow::Node awaited |
result.asExpr() = asyncWith.getContextExpr() and
- asyncWith.getOptionalVars() = n.asExpr()
+ asyncWith.getOptionalVars() = awaited.asExpr() and
+ awaited.getALocalSource() = awaitedValue
)
}
From b9c08167f3f9f3a6481e7cb7525fc150e1004803 Mon Sep 17 00:00:00 2001
From: Porcuiney Hairs
Date: Mon, 8 Feb 2021 01:26:54 +0530
Subject: [PATCH 017/471] C# : Add query to detect SSRF
---
.../experimental/CWE-918/RequestForgery.cs | 33 +++
.../experimental/CWE-918/RequestForgery.qhelp | 35 +++
.../experimental/CWE-918/RequestForgery.ql | 20 ++
.../experimental/CWE-918/RequestForgery.qll | 232 ++++++++++++++++++
.../experimental/CWE-918/RequestForgery.cs | 58 +++++
.../CWE-918/RequestForgery.expected | 7 +
.../experimental/CWE-918/RequestForgery.qlref | 1 +
7 files changed, 386 insertions(+)
create mode 100644 csharp/ql/src/experimental/CWE-918/RequestForgery.cs
create mode 100644 csharp/ql/src/experimental/CWE-918/RequestForgery.qhelp
create mode 100644 csharp/ql/src/experimental/CWE-918/RequestForgery.ql
create mode 100644 csharp/ql/src/experimental/CWE-918/RequestForgery.qll
create mode 100644 csharp/ql/test/experimental/CWE-918/RequestForgery.cs
create mode 100644 csharp/ql/test/experimental/CWE-918/RequestForgery.expected
create mode 100644 csharp/ql/test/experimental/CWE-918/RequestForgery.qlref
diff --git a/csharp/ql/src/experimental/CWE-918/RequestForgery.cs b/csharp/ql/src/experimental/CWE-918/RequestForgery.cs
new file mode 100644
index 00000000000..d9b83eeea3c
--- /dev/null
+++ b/csharp/ql/src/experimental/CWE-918/RequestForgery.cs
@@ -0,0 +1,33 @@
+namespace RequestForgery.Controllers
+{
+ public class SSRFController : Controller
+ {
+ [HttpPost]
+ [ValidateAntiForgeryToken]
+ public async Task Bad(string url)
+ {
+ var request = new HttpRequestMessage(HttpMethod.Get, url);
+
+ var client = new HttpClient();
+ await client.SendAsync(request);
+
+ return View();
+ }
+
+ [HttpPost]
+ [ValidateAntiForgeryToken]
+ public async Task Good(string url)
+ {
+ string baseUrl = "www.mysecuresite.com/";
+ if (url.StartsWith(baseUrl))
+ {
+ var request = new HttpRequestMessage(HttpMethod.Get, url);
+ var client = new HttpClient();
+ await client.SendAsync(request);
+
+ }
+
+ return View();
+ }
+ }
+}
\ No newline at end of file
diff --git a/csharp/ql/src/experimental/CWE-918/RequestForgery.qhelp b/csharp/ql/src/experimental/CWE-918/RequestForgery.qhelp
new file mode 100644
index 00000000000..fb04540e13d
--- /dev/null
+++ b/csharp/ql/src/experimental/CWE-918/RequestForgery.qhelp
@@ -0,0 +1,35 @@
+
+
+
+
+
+ Directly incorporating user input into a HTTP request without validating the input
+can facilitate Server Side Request Forgery (SSRF) attacks. In these attacks, the server
+may be tricked into making a request and interacting with an attacker-controlled server.
+
+
+
+
+
+ To guard against SSRF attacks, it is advisable to avoid putting user input
+directly into the request URL. Instead, maintain a list of authorized
+URLs on the server; then choose from that list based on the user input provided.
+
+
+
+
+ The following example shows an HTTP request parameter being used directly in a forming a
+new request without validating the input, which facilitates SSRF attacks.
+It also shows how to remedy the problem by validating the user input against a known fixed string.
+
+
+
+
+
+
+
+ OWASP SSRF
+
+
+
+
diff --git a/csharp/ql/src/experimental/CWE-918/RequestForgery.ql b/csharp/ql/src/experimental/CWE-918/RequestForgery.ql
new file mode 100644
index 00000000000..c0539de8fba
--- /dev/null
+++ b/csharp/ql/src/experimental/CWE-918/RequestForgery.ql
@@ -0,0 +1,20 @@
+/**
+ * @name Uncontrolled data used in a WebClient
+ * @description The WebClient class allows developers to request resources,
+ * accessing resources influenced by users can allow an attacker to access local files.
+ * @kind path-problem
+ * @problem.severity error
+ * @precision high
+ * @id cs/request-forgery
+ * @tags security
+ * external/cwe/cwe-918
+ */
+
+import csharp
+import RequestForgery::RequestForgery
+import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
+
+from RequestForgeryConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink
+where c.hasFlowPath(source, sink)
+select sink.getNode(), source, sink, "$@ flows to here and is used in a method of WebClient.",
+ source.getNode(), "User-provided value"
diff --git a/csharp/ql/src/experimental/CWE-918/RequestForgery.qll b/csharp/ql/src/experimental/CWE-918/RequestForgery.qll
new file mode 100644
index 00000000000..d146fcb2c53
--- /dev/null
+++ b/csharp/ql/src/experimental/CWE-918/RequestForgery.qll
@@ -0,0 +1,232 @@
+import csharp
+
+module RequestForgery {
+ import semmle.code.csharp.controlflow.Guards
+ import semmle.code.csharp.frameworks.System
+ import semmle.code.csharp.frameworks.system.Web
+ import semmle.code.csharp.frameworks.Format
+ import semmle.code.csharp.security.dataflow.flowsources.Remote
+
+ /**
+ * A data flow source for server side request forgery vulnerabilities.
+ */
+ abstract private class Source extends DataFlow::Node { }
+
+ /**
+ * A data flow sink for server side request forgery vulnerabilities.
+ */
+ abstract private class Sink extends DataFlow::ExprNode { }
+
+ /**
+ * A data flow BarrierGuard which blocks the flow of taint for
+ * server side request forgery vulnerabilities.
+ */
+ abstract private class BarrierGuard extends DataFlow::BarrierGuard { }
+
+ /**
+ * A taint-tracking configuration for detecting server side request forgery vulnerabilities.
+ */
+ class RequestForgeryConfiguration extends DataFlow::Configuration {
+ RequestForgeryConfiguration() { this = "Server Side Request forgery" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof Source }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
+
+ override predicate isAdditionalFlowStep(DataFlow::Node prev, DataFlow::Node succ) {
+ interpolatedStringFlowStep(prev, succ)
+ or
+ stringReplaceStep(prev, succ)
+ or
+ uriCreationStep(prev, succ)
+ or
+ formatConvertStep(prev, succ)
+ or
+ toStringStep(prev, succ)
+ or
+ stringConcatStep(prev, succ)
+ or
+ stringFormatStep(prev, succ)
+ or
+ pathCombineStep(prev, succ)
+ }
+
+ override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
+ guard instanceof BarrierGuard
+ }
+ }
+
+ /**
+ * A remote data flow source taken as a source
+ * for Server Side Request Forgery(SSRF) Vulnerabilities.
+ */
+ private class RemoteFlowSourceAsSource extends Source {
+ RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource }
+ }
+
+ /**
+ * An url argument to a `HttpRequestMessage` constructor call
+ * taken as a sink for Server Side Request Forgery(SSRF) Vulnerabilities.
+ */
+ private class SystemWebHttpRequestMessageSink extends Sink {
+ SystemWebHttpRequestMessageSink() {
+ exists(Class c | c.hasQualifiedName("System.Net.Http.HttpRequestMessage") |
+ c.getAConstructor().getACall().getArgument(1) = this.asExpr()
+ )
+ }
+ }
+
+ /**
+ * An argument to a `WebRequest.Create` call taken as a
+ * sink for Server Side Request Forgery(SSRF) Vulnerabilities. *
+ */
+ private class SystemNetWebRequestCreateSink extends Sink {
+ SystemNetWebRequestCreateSink() {
+ exists(Method m |
+ m.getDeclaringType().hasQualifiedName("System.Net.WebRequest") and m.hasName("Create")
+ |
+ m.getACall().getArgument(0) = this.asExpr()
+ )
+ }
+ }
+
+ /**
+ * An argument to a new HTTP Request call of a `System.Net.Http.HttpClient` object
+ * taken as a sink for Server Side Request Forgery(SSRF) Vulnerabilities.
+ */
+ private class SystemNetHttpClientSink extends Sink {
+ SystemNetHttpClientSink() {
+ exists(Method m |
+ m.getDeclaringType().hasQualifiedName("System.Net.Http.HttpClient") and
+ m.hasName([
+ "DeleteAsync", "GetAsync", "GetByteArrayAsync", "GetStreamAsync", "GetStringAsync",
+ "PatchAsync", "PostAsync", "PutAsync"
+ ])
+ |
+ m.getACall().getArgument(0) = this.asExpr()
+ )
+ }
+ }
+
+ /**
+ * An url argument to a method call of a `System.Net.WebClient` object
+ * taken as a sink for Server Side Request Forgery(SSRF) Vulnerabilities.
+ */
+ private class SystemNetClientBaseAddressSink extends Sink {
+ SystemNetClientBaseAddressSink() {
+ exists(Property p |
+ p.hasName("BaseAddress") and
+ p.getDeclaringType()
+ .hasQualifiedName(["System.Net.WebClient", "System.Net.Http.HttpClient"])
+ |
+ p.getAnAssignedValue() = this.asExpr()
+ )
+ }
+ }
+
+ /**
+ * A method call which checks the base of the tainted uri is assumed
+ * to be a guard for Server Side Request Forgery(SSRF) Vulnerabilities.
+ * This guard considers all checks as valid.
+ */
+ private class BaseUriGuard extends BarrierGuard, MethodCall {
+ BaseUriGuard() { this.getTarget().hasQualifiedName("System.Uri", "IsBaseOf") }
+
+ override predicate checks(Expr e, AbstractValue v) {
+ // we consider any checks against the tainted value to sainitize the taint.
+ // This implies any check such as shown below block the taint flow.
+ // Uri url = new Uri("whitelist.com")
+ // if (url.isBaseOf(`taint1))
+ (e = this.getArgument(0) or e = this.getQualifier()) and
+ v.(AbstractValues::BooleanValue).getValue() = true
+ }
+ }
+
+ /**
+ * A method call which checks if the Uri starts with a white-listed string is assumed
+ * to be a guard for Server Side Request Forgery(SSRF) Vulnerabilities.
+ * This guard considers all checks as valid.
+ */
+ private class StringStartsWithBarrierGuard extends BarrierGuard, MethodCall {
+ StringStartsWithBarrierGuard() { this.getTarget().hasQualifiedName("System.String.StartsWith") }
+
+ override predicate checks(Expr e, AbstractValue v) {
+ // Any check such as the ones shown below
+ // "https://myurl.com/".startsWith(`taint`)
+ // `taint`.startsWith("https://myurl.com/")
+ // are assumed to sainitize the taint
+ (e = this.getQualifier() or this.getArgument(0) = e) and
+ v.(AbstractValues::BooleanValue).getValue() = true
+ }
+ }
+
+ private predicate stringFormatStep(DataFlow::Node prev, DataFlow::Node succ) {
+ exists(FormatCall c | c.getArgument(0) = prev.asExpr() and c = succ.asExpr())
+ }
+
+ private predicate pathCombineStep(DataFlow::Node prev, DataFlow::Node succ) {
+ exists(MethodCall combineCall |
+ combineCall.getTarget().hasQualifiedName("System.IO.Path", "Combine") and
+ combineCall.getArgument(0) = prev.asExpr() and
+ combineCall = succ.asExpr()
+ )
+ }
+
+ private predicate uriCreationStep(DataFlow::Node prev, DataFlow::Node succ) {
+ exists(ObjectCreation oc |
+ oc.getTarget().getDeclaringType().hasQualifiedName("System.Uri") and
+ oc.getArgument(0) = prev.asExpr() and
+ oc = succ.asExpr()
+ )
+ }
+
+ private predicate interpolatedStringFlowStep(DataFlow::Node prev, DataFlow::Node succ) {
+ exists(InterpolatedStringExpr i |
+ // allow `$"http://{`taint`}/blabla/");"` or
+ // allow `$"https://{`taint`}/blabla/");"`
+ i.getText(0).getValue().matches(["http://", "http://"]) and
+ i.getInsert(1) = prev.asExpr() and
+ succ.asExpr() = i
+ or
+ // allow `$"{`taint`}/blabla/");"`
+ i.getInsert(0) = prev.asExpr() and
+ succ.asExpr() = i
+ )
+ }
+
+ private predicate stringReplaceStep(DataFlow::Node prev, DataFlow::Node succ) {
+ exists(MethodCall mc, SystemStringClass s |
+ mc = s.getReplaceMethod().getACall() and
+ mc.getQualifier() = prev.asExpr() and
+ succ.asExpr() = mc
+ )
+ }
+
+ private predicate stringConcatStep(DataFlow::Node prev, DataFlow::Node succ) {
+ exists(AddExpr a |
+ a.getLeftOperand() = prev.asExpr()
+ or
+ a.getRightOperand() = prev.asExpr() and
+ a.getLeftOperand().(StringLiteral).getValue() = ["http://", "https://"]
+ |
+ a = succ.asExpr()
+ )
+ }
+
+ private predicate formatConvertStep(DataFlow::Node prev, DataFlow::Node succ) {
+ exists(Method m |
+ m.hasQualifiedName("System.Convert",
+ ["FromBase64String", "FromHexString", "FromBase64CharArray"]) and
+ m.getParameter(0) = prev.asParameter() and
+ succ.asExpr() = m.getACall()
+ )
+ }
+
+ private predicate toStringStep(DataFlow::Node prev, DataFlow::Node succ) {
+ exists(MethodCall ma |
+ ma.getTarget().hasName("ToString") and
+ ma.getQualifier() = prev.asExpr() and
+ succ.asExpr() = ma
+ )
+ }
+}
diff --git a/csharp/ql/test/experimental/CWE-918/RequestForgery.cs b/csharp/ql/test/experimental/CWE-918/RequestForgery.cs
new file mode 100644
index 00000000000..32347505b35
--- /dev/null
+++ b/csharp/ql/test/experimental/CWE-918/RequestForgery.cs
@@ -0,0 +1,58 @@
+// semmle-extractor-options: ${testdir}/../../resources/stubs/System.Web.cs /r:System.Threading.Tasks.dll /r:System.Collections.Specialized.dll /r:System.Runtime.dll /r:System.Private.Uri.dll
+
+using System;
+using System.Threading.Tasks;
+using System.Web.Mvc;
+using System.Net.Http;
+
+namespace RequestForgery.Controllers
+{
+ public class SSRFController : Controller
+ {
+ [HttpPost]
+ [ValidateAntiForgeryToken]
+ public async Task Bad(string url)
+ {
+ var request = new HttpRequestMessage(HttpMethod.Get, url);
+
+ var client = new HttpClient();
+ await client.SendAsync(request);
+
+ return View();
+ }
+
+ [HttpPost]
+ [ValidateAntiForgeryToken]
+ public async Task Good(string url)
+ {
+ string baseUrl = "www.mysecuresite.com/";
+ if (url.StartsWith(baseUrl))
+ {
+ var request = new HttpRequestMessage(HttpMethod.Get, url);
+ var client = new HttpClient();
+ await client.SendAsync(request);
+
+ }
+
+ return View();
+ }
+ }
+}
+// Missing stubs:
+namespace System.Net.Http
+{
+ public class HttpClient
+ {
+ public async Task SendAsync(HttpRequestMessage request) => throw null;
+ }
+
+ public class HttpRequestMessage
+ {
+ public HttpRequestMessage(HttpMethod method, string requestUri) => throw null;
+ }
+
+ public class HttpMethod
+ {
+ public static readonly HttpMethod Get;
+ }
+}
diff --git a/csharp/ql/test/experimental/CWE-918/RequestForgery.expected b/csharp/ql/test/experimental/CWE-918/RequestForgery.expected
new file mode 100644
index 00000000000..b7a05ea1a05
--- /dev/null
+++ b/csharp/ql/test/experimental/CWE-918/RequestForgery.expected
@@ -0,0 +1,7 @@
+edges
+| RequestForgery.cs:14:52:14:54 | url : String | RequestForgery.cs:16:66:16:68 | access to parameter url |
+nodes
+| RequestForgery.cs:14:52:14:54 | url : String | semmle.label | url : String |
+| RequestForgery.cs:16:66:16:68 | access to parameter url | semmle.label | access to parameter url |
+#select
+| RequestForgery.cs:16:66:16:68 | access to parameter url | RequestForgery.cs:14:52:14:54 | url : String | RequestForgery.cs:16:66:16:68 | access to parameter url | $@ flows to here and is used in a method of WebClient. | RequestForgery.cs:14:52:14:54 | url | User-provided value |
diff --git a/csharp/ql/test/experimental/CWE-918/RequestForgery.qlref b/csharp/ql/test/experimental/CWE-918/RequestForgery.qlref
new file mode 100644
index 00000000000..3d529ae5a2c
--- /dev/null
+++ b/csharp/ql/test/experimental/CWE-918/RequestForgery.qlref
@@ -0,0 +1 @@
+experimental/CWE-918/RequestForgery.ql
\ No newline at end of file
From cc1c32cf0e02d27d8083493351d3d392018a9ef7 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Wed, 29 Sep 2021 16:53:25 +0200
Subject: [PATCH 018/471] Python: model file accesses
---
.../lib/semmle/python/frameworks/Asyncpg.qll | 18 ++++++++++++++++++
.../frameworks/asyncpg/FileSystemAccess.py | 16 ++++++++--------
.../frameworks/asyncpg/SqlExecution.py | 4 ++--
3 files changed, 28 insertions(+), 10 deletions(-)
diff --git a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
index 03697095260..1bb7ae5f2aa 100644
--- a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
+++ b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
@@ -38,6 +38,24 @@ private module Asyncpg {
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName(queryArg)] }
}
+ private string fileAccessMethodName(string pathArg) {
+ result in ["copy_from_query", "copy_from_table"] and
+ pathArg = "output"
+ or
+ result = "copy_to_table" and
+ pathArg = "source"
+ }
+
+ class FileAccessOnConnection extends FileSystemAccess::Range, DataFlow::MethodCallNode {
+ string pathArg;
+
+ FileAccessOnConnection() {
+ this.calls([connectionPool().getAUse(), connection().getAUse()], fileAccessMethodName(pathArg))
+ }
+
+ override DataFlow::Node getAPathArgument() { result in [this.getArgByName(pathArg)] }
+ }
+
/**
* Holds if `result` is the result of awaiting `n`.
*/
diff --git a/python/ql/test/library-tests/frameworks/asyncpg/FileSystemAccess.py b/python/ql/test/library-tests/frameworks/asyncpg/FileSystemAccess.py
index 0dc45cd9b1b..e835f4becdc 100644
--- a/python/ql/test/library-tests/frameworks/asyncpg/FileSystemAccess.py
+++ b/python/ql/test/library-tests/frameworks/asyncpg/FileSystemAccess.py
@@ -7,11 +7,11 @@ async def test_connection():
try:
# The file-like object is passed in as a keyword-only argument.
# See https://magicstack.github.io/asyncpg/current/api/index.html#asyncpg.connection.Connection.copy_from_query
- await conn.copy_from_query("sql", output="filepath") # $ getSql="sql" MISSING: getAPathArgument="filepath"
- await conn.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ getSql="sql" MISSING: getAPathArgument="filepath"
+ await conn.copy_from_query("sql", output="filepath") # $ getSql="sql" getAPathArgument="filepath"
+ await conn.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ getSql="sql" getAPathArgument="filepath"
- await conn.copy_from_table("table", output="filepath") # $ MISSING: getAPathArgument="filepath"
- await conn.copy_to_table("table", source="filepath") # $ MISSING: getAPathArgument="filepath"
+ await conn.copy_from_table("table", output="filepath") # $ getAPathArgument="filepath"
+ await conn.copy_to_table("table", source="filepath") # $ getAPathArgument="filepath"
finally:
await conn.close()
@@ -20,10 +20,10 @@ async def test_connection_pool():
pool = await asyncpg.create_pool()
try:
- await pool.copy_from_query("sql", output="filepath") # $ getSql="sql" MISSING: getAPathArgument="filepath"
- await pool.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ getSql="sql" MISSING: getAPathArgument="filepath"
- await pool.copy_from_table("table", output="filepath") # $ MISSING: getAPathArgument="filepath"
- await pool.copy_to_table("table", source="filepath") # $ MISSING: getAPathArgument="filepath"
+ await pool.copy_from_query("sql", output="filepath") # $ getSql="sql" getAPathArgument="filepath"
+ await pool.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ getSql="sql" getAPathArgument="filepath"
+ await pool.copy_from_table("table", output="filepath") # $ getAPathArgument="filepath"
+ await pool.copy_to_table("table", source="filepath") # $ getAPathArgument="filepath"
finally:
await pool.close()
diff --git a/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py b/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py
index a7f652dae10..e9e619afa66 100644
--- a/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py
+++ b/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py
@@ -5,7 +5,7 @@ async def test_connection():
conn = await asyncpg.connect()
try:
- await conn.copy_from_query("sql", output="filepath") # $ getSql="sql" MISSING: getAPathArgument="filepath"
+ await conn.copy_from_query("sql", output="filepath") # $ getSql="sql" getAPathArgument="filepath"
await conn.execute("sql") # $ getSql="sql"
await conn.executemany("sql") # $ getSql="sql"
await conn.fetch("sql") # $ getSql="sql"
@@ -62,7 +62,7 @@ async def test_connection_pool():
pool = await asyncpg.create_pool()
try:
- await pool.copy_from_query("sql", output="filepath") # $ getSql="sql" MISSING: getAPathArgument="filepath"
+ await pool.copy_from_query("sql", output="filepath") # $ getSql="sql" getAPathArgument="filepath"
await pool.execute("sql") # $ getSql="sql"
await pool.executemany("sql") # $ getSql="sql"
await pool.fetch("sql") # $ getSql="sql"
From 115113888fd4c133a4100965133bc1bd8c996c26 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Wed, 29 Sep 2021 16:58:14 +0200
Subject: [PATCH 019/471] Python: Add change note
---
python/change-notes/2021-09-29-model-asyncpg.md | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 python/change-notes/2021-09-29-model-asyncpg.md
diff --git a/python/change-notes/2021-09-29-model-asyncpg.md b/python/change-notes/2021-09-29-model-asyncpg.md
new file mode 100644
index 00000000000..0c44e6cb908
--- /dev/null
+++ b/python/change-notes/2021-09-29-model-asyncpg.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* Added modeling of `asyncpg` for sinks executing SQL and/or accessing the file system.
From fc9fb59082e74b816a21ad60da46dd0223473c71 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Thu, 30 Sep 2021 10:05:57 +0200
Subject: [PATCH 020/471] Python: Add comments
---
.../lib/semmle/python/frameworks/Asyncpg.qll | 34 ++++++++++++++++++-
1 file changed, 33 insertions(+), 1 deletion(-)
diff --git a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
index 1bb7ae5f2aa..37cf24d5146 100644
--- a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
+++ b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
@@ -10,16 +10,23 @@ private import semmle.python.ApiGraphs
/** Provides models for the `asyncpg` PyPI package. */
private module Asyncpg {
+ /** A `ConectionPool` is created when the result of `asyncpg.create_pool()` is awaited. */
API::Node connectionPool() {
result = API::moduleImport("asyncpg").getMember("create_pool").getReturn().getAwaited()
}
+ /**
+ * A `Connection` is created when
+ * - the result of `asyncpg.connect()` is awaited.
+ * - the result of calling `aquire` on a c=`ConnectionPool` is awaited.
+ */
API::Node connection() {
result = API::moduleImport("asyncpg").getMember("connect").getReturn().getAwaited()
or
result = connectionPool().getMember("acquire").getReturn().getAwaited()
}
+ /** Reverse lookup of the query argument name for a query method. */
private string queryMethodName(string queryArg) {
result in ["copy_from_query", "execute", "fetch", "fetchrow", "fetchval"] and
queryArg = "query"
@@ -28,6 +35,7 @@ private module Asyncpg {
queryArg = "command"
}
+ /** `Connection`s and `ConnectionPool`s provide some methods that execute SQL. */
class SqlExecutionOnConnection extends SqlExecution::Range, DataFlow::MethodCallNode {
string queryArg;
@@ -38,6 +46,7 @@ private module Asyncpg {
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName(queryArg)] }
}
+ /** Reverse lokup of the path argument name for a method accessing the file system. */
private string fileAccessMethodName(string pathArg) {
result in ["copy_from_query", "copy_from_table"] and
pathArg = "output"
@@ -46,6 +55,7 @@ private module Asyncpg {
pathArg = "source"
}
+ /** `Connection`s and `ConnectionPool`s provide some methods that access the file system. */
class FileAccessOnConnection extends FileSystemAccess::Range, DataFlow::MethodCallNode {
string pathArg;
@@ -53,11 +63,12 @@ private module Asyncpg {
this.calls([connectionPool().getAUse(), connection().getAUse()], fileAccessMethodName(pathArg))
}
+ // The path argument is keyword only.
override DataFlow::Node getAPathArgument() { result in [this.getArgByName(pathArg)] }
}
/**
- * Holds if `result` is the result of awaiting `n`.
+ * Holds if `result` is the result of awaiting `awaitedValue`.
*/
pragma[inline]
DataFlow::Node awaited(DataFlow::Node awaitedValue) {
@@ -88,6 +99,15 @@ private module Asyncpg {
)
}
+ /**
+ * Provides models of the `PreparedStatement` class in `asyncpg`.
+ * `PreparedStatement`s are created when the result of calling `prepare(query)` on a connection is awaited.
+ * The result of calling `prepare(query)` is a `PreparedStatementFactory` and the argument, `query` needs to
+ * be tracked to the place where a `PreparedStatement` is created and then futher to any executing methods.
+ * Hence the two type trackers.
+ *
+ * TODO: Rewrite this, once we have `API::CallNode` available.
+ */
module PreparedStatement {
private DataFlow::TypeTrackingNode preparedStatementFactory(
DataFlow::TypeTracker t, DataFlow::Node sql
@@ -128,6 +148,17 @@ private module Asyncpg {
}
}
+ /**
+ * Provides models of the `Cursor` class in `asyncpg`.
+ * `Cursor`s are created
+ * - when the result of calling `cursor(query)` on a connection is awaited.
+ * - when the result of calling `cursor()` on a prepared statement is awaited.
+ * The result of calling `cursor` in either case is a `CursorFactory` and the argument, `query` needs to
+ * be tracked to the place where a `Cursor` is created, hence the type tracker.
+ * The creation of the `Cursor` executes the query.
+ *
+ * TODO: Rewrite this, once we have `API::CallNode` available.
+ */
module Cursor {
private DataFlow::TypeTrackingNode cursorFactory(DataFlow::TypeTracker t, DataFlow::Node sql) {
// cursor created from connection
@@ -149,6 +180,7 @@ private module Asyncpg {
cursorFactory(DataFlow::TypeTracker::end(), sql).flowsTo(result)
}
+ /** The creation of a `Cursor` executes the associated query. */
class CursorCreation extends SqlExecution::Range {
DataFlow::Node sql;
From 3661ff3bd8b6d8dc139ab5d0c36098e5869d1634 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Sun, 12 Sep 2021 15:50:36 +0200
Subject: [PATCH 021/471] Python: Add basic FastAPI support
---
docs/codeql/support/reusables/frameworks.rst | 1 +
.../2021-09-12-add-FastAPI-modeling.md | 2 +
python/ql/lib/semmle/python/Frameworks.qll | 1 +
.../lib/semmle/python/frameworks/FastApi.qll | 75 +++++++++++++++++++
.../frameworks/fastapi/ConceptsTest.expected | 0
.../frameworks/fastapi/ConceptsTest.ql | 12 +++
.../fastapi/InlineTaintTest.expected | 3 +
.../frameworks/fastapi/InlineTaintTest.ql | 1 +
.../library-tests/frameworks/fastapi/basic.py | 73 ++++++++++++++++++
.../frameworks/fastapi/response_test.py | 1 +
10 files changed, 169 insertions(+)
create mode 100644 python/change-notes/2021-09-12-add-FastAPI-modeling.md
create mode 100644 python/ql/lib/semmle/python/frameworks/FastApi.qll
create mode 100644 python/ql/test/library-tests/frameworks/fastapi/ConceptsTest.expected
create mode 100644 python/ql/test/library-tests/frameworks/fastapi/ConceptsTest.ql
create mode 100644 python/ql/test/library-tests/frameworks/fastapi/InlineTaintTest.expected
create mode 100644 python/ql/test/library-tests/frameworks/fastapi/InlineTaintTest.ql
create mode 100644 python/ql/test/library-tests/frameworks/fastapi/basic.py
create mode 100644 python/ql/test/library-tests/frameworks/fastapi/response_test.py
diff --git a/docs/codeql/support/reusables/frameworks.rst b/docs/codeql/support/reusables/frameworks.rst
index 0846786db63..2b2f8db176d 100644
--- a/docs/codeql/support/reusables/frameworks.rst
+++ b/docs/codeql/support/reusables/frameworks.rst
@@ -155,6 +155,7 @@ Python built-in support
Name, Category
aiohttp.web, Web framework
Django, Web framework
+ FastAPI, Web framework
Flask, Web framework
Tornado, Web framework
Twisted, Web framework
diff --git a/python/change-notes/2021-09-12-add-FastAPI-modeling.md b/python/change-notes/2021-09-12-add-FastAPI-modeling.md
new file mode 100644
index 00000000000..99661ddb135
--- /dev/null
+++ b/python/change-notes/2021-09-12-add-FastAPI-modeling.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* Added modeling of sources/sinks when using FastAPI to create web servers.
diff --git a/python/ql/lib/semmle/python/Frameworks.qll b/python/ql/lib/semmle/python/Frameworks.qll
index b3ff235c3ee..2e6f1d54069 100644
--- a/python/ql/lib/semmle/python/Frameworks.qll
+++ b/python/ql/lib/semmle/python/Frameworks.qll
@@ -12,6 +12,7 @@ private import semmle.python.frameworks.Cryptography
private import semmle.python.frameworks.Dill
private import semmle.python.frameworks.Django
private import semmle.python.frameworks.Fabric
+private import semmle.python.frameworks.FastApi
private import semmle.python.frameworks.Flask
private import semmle.python.frameworks.FlaskSqlAlchemy
private import semmle.python.frameworks.Idna
diff --git a/python/ql/lib/semmle/python/frameworks/FastApi.qll b/python/ql/lib/semmle/python/frameworks/FastApi.qll
new file mode 100644
index 00000000000..60b3dbd2b17
--- /dev/null
+++ b/python/ql/lib/semmle/python/frameworks/FastApi.qll
@@ -0,0 +1,75 @@
+/**
+ * Provides classes modeling security-relevant aspects of the `fastapi` PyPI package.
+ * See https://fastapi.tiangolo.com/.
+ */
+
+private import python
+private import semmle.python.dataflow.new.DataFlow
+private import semmle.python.dataflow.new.RemoteFlowSources
+private import semmle.python.dataflow.new.TaintTracking
+private import semmle.python.Concepts
+private import semmle.python.ApiGraphs
+
+/**
+ * Provides models for the `fastapi` PyPI package.
+ * See https://fastapi.tiangolo.com/.
+ */
+private module FastApi {
+ /**
+ * Provides models for FastAPI applications (an instance of `fastapi.FastAPI`).
+ */
+ module App {
+ /** Gets a reference to a FastAPI application (an instance of `fastapi.FastAPI`). */
+ API::Node instance() { result = API::moduleImport("fastapi").getMember("FastAPI").getReturn() }
+ }
+
+ // ---------------------------------------------------------------------------
+ // routing modeling
+ // ---------------------------------------------------------------------------
+ /**
+ * A call to a method like `get` or `post` on a FastAPI application.
+ *
+ * See https://fastapi.tiangolo.com/tutorial/first-steps/#define-a-path-operation-decorator
+ */
+ private class FastApiRouteSetup extends HTTP::Server::RouteSetup::Range, DataFlow::CallCfgNode {
+ FastApiRouteSetup() { this = App::instance().getMember(any(HTTP::httpVerbLower())).getACall() }
+
+ override Parameter getARoutedParameter() {
+ // this will need to be refined a bit, since you can add special parameters to
+ // your request handler functions that are used to pass in the response. There
+ // might be other special cases as well, but as a start this is not too far off
+ // the mark.
+ result = this.getARequestHandler().getArgByName(_)
+ }
+
+ override DataFlow::Node getUrlPatternArg() {
+ result in [this.getArg(0), this.getArgByName("path")]
+ }
+
+ override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
+
+ override string getFramework() { result = "FastAPI" }
+ }
+
+ // ---------------------------------------------------------------------------
+ // Response modeling
+ // ---------------------------------------------------------------------------
+ /**
+ * Implicit response from returns of FastAPI request handlers
+ */
+ private class FastApiRequestHandlerReturn extends HTTP::Server::HttpResponse::Range,
+ DataFlow::CfgNode {
+ FastApiRequestHandlerReturn() {
+ exists(Function requestHandler |
+ requestHandler = any(FastApiRouteSetup rs).getARequestHandler() and
+ node = requestHandler.getAReturnValueFlowNode()
+ )
+ }
+
+ override DataFlow::Node getBody() { result = this }
+
+ override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
+
+ override string getMimetypeDefault() { result = "application/json" }
+ }
+}
diff --git a/python/ql/test/library-tests/frameworks/fastapi/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/fastapi/ConceptsTest.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/python/ql/test/library-tests/frameworks/fastapi/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/fastapi/ConceptsTest.ql
new file mode 100644
index 00000000000..1e2c1fab3ee
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/fastapi/ConceptsTest.ql
@@ -0,0 +1,12 @@
+import python
+import experimental.meta.ConceptsTest
+
+class DedicatedResponseTest extends HttpServerHttpResponseTest {
+ DedicatedResponseTest() { file.getShortName() = "response_test.py" }
+}
+
+class OtherResponseTest extends HttpServerHttpResponseTest {
+ OtherResponseTest() { not this instanceof DedicatedResponseTest }
+
+ override string getARelevantTag() { result = "HttpResponse" }
+}
diff --git a/python/ql/test/library-tests/frameworks/fastapi/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/fastapi/InlineTaintTest.expected
new file mode 100644
index 00000000000..79d760d87f4
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/fastapi/InlineTaintTest.expected
@@ -0,0 +1,3 @@
+argumentToEnsureNotTaintedNotMarkedAsSpurious
+untaintedArgumentToEnsureTaintedNotMarkedAsMissing
+failures
diff --git a/python/ql/test/library-tests/frameworks/fastapi/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/fastapi/InlineTaintTest.ql
new file mode 100644
index 00000000000..027ad8667be
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/fastapi/InlineTaintTest.ql
@@ -0,0 +1 @@
+import experimental.meta.InlineTaintTest
diff --git a/python/ql/test/library-tests/frameworks/fastapi/basic.py b/python/ql/test/library-tests/frameworks/fastapi/basic.py
new file mode 100644
index 00000000000..338a837c9d9
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/fastapi/basic.py
@@ -0,0 +1,73 @@
+# Taking inspiration from https://realpython.com/fastapi-python-web-apis/
+
+# run with
+# uvicorn basic:app --reload
+# Then visit http://127.0.0.1:8000/docs and http://127.0.0.1:8000/redoc
+
+from fastapi import FastAPI
+
+app = FastAPI()
+
+@app.get("/") # $ routeSetup="/"
+async def root(): # $ requestHandler
+ return {"message": "Hello World"} # $ HttpResponse
+
+@app.get("/non-async") # $ routeSetup="/non-async"
+def non_async(): # $ requestHandler
+ return {"message": "non-async"} # $ HttpResponse
+
+@app.get(path="/kw-arg") # $ routeSetup="/kw-arg"
+def kw_arg(): # $ requestHandler
+ return {"message": "kw arg"} # $ HttpResponse
+
+@app.get("/foo/{foo_id}") # $ routeSetup="/foo/{foo_id}"
+async def get_foo(foo_id: int): # $ requestHandler routedParameter=foo_id
+ # FastAPI does data validation (with `pydantic` PyPI package) under the hood based
+ # on the type annotation we did for `foo_id`, so it will auto-reject anything that's
+ # not an int.
+ return {"foo_id": foo_id} # $ HttpResponse
+
+# this will work as query param, so `/bar?bar_id=123`
+@app.get("/bar") # $ routeSetup="/bar"
+async def get_bar(bar_id: int = 42): # $ requestHandler routedParameter=bar_id
+ return {"bar_id": bar_id} # $ HttpResponse
+
+# The big deal is that FastAPI works so well together with pydantic, so you can do stuff like this
+from typing import Optional
+from pydantic import BaseModel
+
+class Item(BaseModel):
+ name: str
+ price: float
+ is_offer: Optional[bool] = None
+
+@app.post("/items/") # $ routeSetup="/items/"
+async def create_item(item: Item): # $ requestHandler routedParameter=item
+ # Note: calling `item` a routed parameter is slightly untrue, since it doesn't come
+ # from the URL itself, but from the body of the POST request
+ return item # $ HttpResponse
+
+# this also works fine
+@app.post("/2items") # $ routeSetup="/2items"
+async def create_item2(item1: Item, item2: Item): # $ requestHandler routedParameter=item1 routedParameter=item2
+ return (item1, item2) # $ HttpResponse
+
+
+
+
+# Docs:
+# see https://fastapi.tiangolo.com/tutorial/path-params/
+
+# More stuff that we should support:
+# - https://fastapi.tiangolo.com/tutorial/bigger-applications/
+# - https://fastapi.tiangolo.com/advanced/response-cookies/
+# - https://fastapi.tiangolo.com/tutorial/dependencies/
+# - Extra taint-steps for files
+# - https://fastapi.tiangolo.com/tutorial/request-files/
+# - https://fastapi.tiangolo.com/tutorial/request-files/#uploadfile
+# - https://fastapi.tiangolo.com/tutorial/background-tasks/
+#
+# - https://fastapi.tiangolo.com/tutorial/middleware/
+# - https://fastapi.tiangolo.com/tutorial/encoder/
+#
+# - `app.route()`
diff --git a/python/ql/test/library-tests/frameworks/fastapi/response_test.py b/python/ql/test/library-tests/frameworks/fastapi/response_test.py
new file mode 100644
index 00000000000..b3998a99d16
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/fastapi/response_test.py
@@ -0,0 +1 @@
+# TODO: Add detailed tests of ways to create responses in this file.
From b1f8b5352ba9551a72df54fad65dd1451f798c5e Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Sun, 12 Sep 2021 15:59:08 +0200
Subject: [PATCH 022/471] Python: FastAPI: Add support for `api_route`
Note that `route` did not actually work (that also comes from the
underlying web framework library Starlette)
---
python/ql/lib/semmle/python/frameworks/FastApi.qll | 10 +++++++++-
.../ql/test/library-tests/frameworks/fastapi/basic.py | 7 +++----
2 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/python/ql/lib/semmle/python/frameworks/FastApi.qll b/python/ql/lib/semmle/python/frameworks/FastApi.qll
index 60b3dbd2b17..20d4f1df7bc 100644
--- a/python/ql/lib/semmle/python/frameworks/FastApi.qll
+++ b/python/ql/lib/semmle/python/frameworks/FastApi.qll
@@ -32,7 +32,15 @@ private module FastApi {
* See https://fastapi.tiangolo.com/tutorial/first-steps/#define-a-path-operation-decorator
*/
private class FastApiRouteSetup extends HTTP::Server::RouteSetup::Range, DataFlow::CallCfgNode {
- FastApiRouteSetup() { this = App::instance().getMember(any(HTTP::httpVerbLower())).getACall() }
+ FastApiRouteSetup() {
+ exists(string routeAddingMethod |
+ routeAddingMethod = HTTP::httpVerbLower()
+ or
+ routeAddingMethod = "api_route"
+ |
+ this = App::instance().getMember(routeAddingMethod).getACall()
+ )
+ }
override Parameter getARoutedParameter() {
// this will need to be refined a bit, since you can add special parameters to
diff --git a/python/ql/test/library-tests/frameworks/fastapi/basic.py b/python/ql/test/library-tests/frameworks/fastapi/basic.py
index 338a837c9d9..fdccbd3243c 100644
--- a/python/ql/test/library-tests/frameworks/fastapi/basic.py
+++ b/python/ql/test/library-tests/frameworks/fastapi/basic.py
@@ -52,8 +52,9 @@ async def create_item(item: Item): # $ requestHandler routedParameter=item
async def create_item2(item1: Item, item2: Item): # $ requestHandler routedParameter=item1 routedParameter=item2
return (item1, item2) # $ HttpResponse
-
-
+@app.api_route("/baz/{baz_id}", methods=["GET"]) # $ routeSetup="/baz/{baz_id}"
+async def get_baz(baz_id: int): # $ requestHandler routedParameter=baz_id
+ return {"baz_id2": baz_id} # $ HttpResponse
# Docs:
# see https://fastapi.tiangolo.com/tutorial/path-params/
@@ -69,5 +70,3 @@ async def create_item2(item1: Item, item2: Item): # $ requestHandler routedParam
#
# - https://fastapi.tiangolo.com/tutorial/middleware/
# - https://fastapi.tiangolo.com/tutorial/encoder/
-#
-# - `app.route()`
From 285de2b4c8a7467fd90c6c75369b28ffcc419b1f Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 22 Sep 2021 14:37:22 +0200
Subject: [PATCH 023/471] Python: FastAPI: Add support for `APIRouter`
---
.../lib/semmle/python/frameworks/FastApi.qll | 14 ++++++++
.../library-tests/frameworks/fastapi/basic.py | 1 -
.../frameworks/fastapi/router.py | 33 +++++++++++++++++++
3 files changed, 47 insertions(+), 1 deletion(-)
create mode 100644 python/ql/test/library-tests/frameworks/fastapi/router.py
diff --git a/python/ql/lib/semmle/python/frameworks/FastApi.qll b/python/ql/lib/semmle/python/frameworks/FastApi.qll
index 20d4f1df7bc..dc36ad74549 100644
--- a/python/ql/lib/semmle/python/frameworks/FastApi.qll
+++ b/python/ql/lib/semmle/python/frameworks/FastApi.qll
@@ -23,6 +23,18 @@ private module FastApi {
API::Node instance() { result = API::moduleImport("fastapi").getMember("FastAPI").getReturn() }
}
+ /**
+ * Provides models for the `fastapi.APIRouter` class
+ *
+ * See https://fastapi.tiangolo.com/tutorial/bigger-applications/.
+ */
+ module APIRouter {
+ /** Gets a reference to an instance of `fastapi.APIRouter`. */
+ API::Node instance() {
+ result = API::moduleImport("fastapi").getMember("APIRouter").getReturn()
+ }
+ }
+
// ---------------------------------------------------------------------------
// routing modeling
// ---------------------------------------------------------------------------
@@ -39,6 +51,8 @@ private module FastApi {
routeAddingMethod = "api_route"
|
this = App::instance().getMember(routeAddingMethod).getACall()
+ or
+ this = APIRouter::instance().getMember(routeAddingMethod).getACall()
)
}
diff --git a/python/ql/test/library-tests/frameworks/fastapi/basic.py b/python/ql/test/library-tests/frameworks/fastapi/basic.py
index fdccbd3243c..3873a0c9ec4 100644
--- a/python/ql/test/library-tests/frameworks/fastapi/basic.py
+++ b/python/ql/test/library-tests/frameworks/fastapi/basic.py
@@ -60,7 +60,6 @@ async def get_baz(baz_id: int): # $ requestHandler routedParameter=baz_id
# see https://fastapi.tiangolo.com/tutorial/path-params/
# More stuff that we should support:
-# - https://fastapi.tiangolo.com/tutorial/bigger-applications/
# - https://fastapi.tiangolo.com/advanced/response-cookies/
# - https://fastapi.tiangolo.com/tutorial/dependencies/
# - Extra taint-steps for files
diff --git a/python/ql/test/library-tests/frameworks/fastapi/router.py b/python/ql/test/library-tests/frameworks/fastapi/router.py
new file mode 100644
index 00000000000..05cc4860d94
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/fastapi/router.py
@@ -0,0 +1,33 @@
+# like blueprints in Flask
+# see https://fastapi.tiangolo.com/tutorial/bigger-applications/
+
+from fastapi import APIRouter, FastAPI
+
+
+inner_router = APIRouter()
+
+@inner_router.get("/foo") # $ routeSetup="/foo"
+async def root(): # $ requestHandler
+ return {"msg": "inner_router /foo"} # $ HttpResponse
+
+outer_router = APIRouter()
+outer_router.include_router(inner_router, prefix="/inner")
+
+
+items_router = APIRouter(
+ prefix="/items",
+ tags=["items"],
+)
+
+
+@items_router.get("/") # $ routeSetup="/"
+async def items(): # $ requestHandler
+ return {"msg": "items_router /"} # $ HttpResponse
+
+
+app = FastAPI()
+
+app.include_router(outer_router, prefix="/outer")
+app.include_router(items_router)
+
+# see basic.py for instructions for how to run this code.
From d34c5fd72f7896f950e7bd6ccc74dc9d13aea489 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 22 Sep 2021 15:49:57 +0200
Subject: [PATCH 024/471] Python: FastAPI: Add tests with response parameter
---
.../library-tests/frameworks/fastapi/basic.py | 1 -
.../frameworks/fastapi/response_test.py | 39 ++++++++++++++++++-
2 files changed, 38 insertions(+), 2 deletions(-)
diff --git a/python/ql/test/library-tests/frameworks/fastapi/basic.py b/python/ql/test/library-tests/frameworks/fastapi/basic.py
index 3873a0c9ec4..472ac37ba10 100644
--- a/python/ql/test/library-tests/frameworks/fastapi/basic.py
+++ b/python/ql/test/library-tests/frameworks/fastapi/basic.py
@@ -60,7 +60,6 @@ async def get_baz(baz_id: int): # $ requestHandler routedParameter=baz_id
# see https://fastapi.tiangolo.com/tutorial/path-params/
# More stuff that we should support:
-# - https://fastapi.tiangolo.com/advanced/response-cookies/
# - https://fastapi.tiangolo.com/tutorial/dependencies/
# - Extra taint-steps for files
# - https://fastapi.tiangolo.com/tutorial/request-files/
diff --git a/python/ql/test/library-tests/frameworks/fastapi/response_test.py b/python/ql/test/library-tests/frameworks/fastapi/response_test.py
index b3998a99d16..d044af8eb2e 100644
--- a/python/ql/test/library-tests/frameworks/fastapi/response_test.py
+++ b/python/ql/test/library-tests/frameworks/fastapi/response_test.py
@@ -1 +1,38 @@
-# TODO: Add detailed tests of ways to create responses in this file.
+# see https://fastapi.tiangolo.com/advanced/response-cookies/
+
+from fastapi import FastAPI, Response
+
+
+app = FastAPI()
+
+
+@app.get("/response_parameter") # $ routeSetup="/response_parameter"
+async def response_parameter(response: Response): # $ requestHandler SPURIOUS: routedParameter=response
+ response.set_cookie("key", "value") # $ MISSING: CookieWrite CookieName="key" CookieValue="value"
+ response.set_cookie(key="key", value="value") # $ MISSING: CookieWrite CookieName="key" CookieValue="value"
+ response.headers.append("Set-Cookie", "key2=value2") # $ MISSING: CookieWrite CookieRawHeader="key2=value2"
+ response.headers.append(key="Set-Cookie", value="key2=value2") # $ MISSING: CookieWrite CookieRawHeader="key2=value2"
+ response.headers["X-MyHeader"] = "header-value"
+ response.status_code = 418
+ return {"message": "response as parameter"} # $ HttpResponse mimetype=application/json responseBody=Dict
+
+
+@app.get("/resp_parameter") # $ routeSetup="/resp_parameter"
+async def resp_parameter(resp: Response): # $ requestHandler SPURIOUS: routedParameter=resp
+ resp.status_code = 418
+ return {"message": "resp as parameter"} # $ HttpResponse mimetype=application/json responseBody=Dict
+
+
+@app.get("/response_parameter_no_type") # $ routeSetup="/response_parameter_no_type"
+async def response_parameter_no_type(response): # $ requestHandler routedParameter=response
+ # NOTE: This does in fact not work, since FastAPI relies on the type annotations,
+ # and not on the name of the parameter
+ response.status_code = 418
+ return {"message": "response as parameter"} # $ HttpResponse mimetype=application/json responseBody=Dict
+
+
+# Direct response
+
+# see https://fastapi.tiangolo.com/advanced/response-directly/
+
+# TODO
From c50c805f5fd2a1ee9ab9a39f2e2fc0c431e2fe82 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 23 Sep 2021 10:00:45 +0200
Subject: [PATCH 025/471] Python: FastAPI: Model Cookie Writes
---
.../lib/semmle/python/frameworks/FastApi.qll | 91 ++++++++++++++++++-
.../frameworks/fastapi/response_test.py | 12 +--
2 files changed, 96 insertions(+), 7 deletions(-)
diff --git a/python/ql/lib/semmle/python/frameworks/FastApi.qll b/python/ql/lib/semmle/python/frameworks/FastApi.qll
index dc36ad74549..657cf120aaf 100644
--- a/python/ql/lib/semmle/python/frameworks/FastApi.qll
+++ b/python/ql/lib/semmle/python/frameworks/FastApi.qll
@@ -61,7 +61,9 @@ private module FastApi {
// your request handler functions that are used to pass in the response. There
// might be other special cases as well, but as a start this is not too far off
// the mark.
- result = this.getARequestHandler().getArgByName(_)
+ result = this.getARequestHandler().getArgByName(_) and
+ // type-annotated with `Response`
+ not any(Response::RequestHandlerParam src).asExpr() = result
}
override DataFlow::Node getUrlPatternArg() {
@@ -76,6 +78,93 @@ private module FastApi {
// ---------------------------------------------------------------------------
// Response modeling
// ---------------------------------------------------------------------------
+ /**
+ * Provides models for the `fastapi.Response` class
+ */
+ module Response {
+ /** Gets a reference to the `fastapi.Response` class. */
+ private API::Node classRef() { result = API::moduleImport("fastapi").getMember("Response") }
+
+ /**
+ * A source of instances of `fastapi.Response`, extend this class to model new instances.
+ *
+ * This can include instantiations of the class, return values from function
+ * calls, or a special parameter that will be set when functions are called by an external
+ * library.
+ *
+ * Use the predicate `Response::instance()` to get references to instances of `fastapi.Response`.
+ */
+ abstract class InstanceSource extends DataFlow::LocalSourceNode { }
+
+ /** A direct instantiation of `fastapi.Response`. */
+ private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
+ ClassInstantiation() { this = classRef().getACall() }
+ }
+
+ /**
+ * INTERNAL: Do not use.
+ *
+ * A parameter to a FastAPI request-handler that has a `fastapi.Response`
+ * type-annotation.
+ */
+ class RequestHandlerParam extends InstanceSource, DataFlow::ParameterNode {
+ RequestHandlerParam() {
+ this.getParameter().getAnnotation() = classRef().getAUse().asExpr() and
+ any(FastApiRouteSetup rs).getARequestHandler().getArgByName(_) = this.getParameter()
+ }
+ }
+
+ /** Gets a reference to an instance of `fastapi.Response`. */
+ private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
+ t.start() and
+ result instanceof InstanceSource
+ or
+ exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
+ }
+
+ /** Gets a reference to an instance of `fastapi.Response`. */
+ DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
+
+ /**
+ * A call to `set_cookie` on a FastAPI Response.
+ */
+ private class SetCookieCall extends HTTP::Server::CookieWrite::Range, DataFlow::MethodCallNode {
+ SetCookieCall() { this.calls(instance(), "set_cookie") }
+
+ override DataFlow::Node getHeaderArg() { none() }
+
+ override DataFlow::Node getNameArg() { result in [this.getArg(0), this.getArgByName("key")] }
+
+ override DataFlow::Node getValueArg() {
+ result in [this.getArg(1), this.getArgByName("value")]
+ }
+ }
+
+ /**
+ * A call to `append` on a `headers` of a FastAPI Response, with the `Set-Cookie`
+ * header-key.
+ */
+ private class HeadersAppendCookie extends HTTP::Server::CookieWrite::Range,
+ DataFlow::MethodCallNode {
+ HeadersAppendCookie() {
+ exists(DataFlow::AttrRead headers, DataFlow::Node keyArg |
+ headers.accesses(instance(), "headers") and
+ this.calls(headers, "append") and
+ keyArg in [this.getArg(0), this.getArgByName("key")] and
+ keyArg.getALocalSource().asExpr().(StrConst).getText().toLowerCase() = "set-cookie"
+ )
+ }
+
+ override DataFlow::Node getHeaderArg() {
+ result in [this.getArg(1), this.getArgByName("value")]
+ }
+
+ override DataFlow::Node getNameArg() { none() }
+
+ override DataFlow::Node getValueArg() { none() }
+ }
+ }
+
/**
* Implicit response from returns of FastAPI request handlers
*/
diff --git a/python/ql/test/library-tests/frameworks/fastapi/response_test.py b/python/ql/test/library-tests/frameworks/fastapi/response_test.py
index d044af8eb2e..c2e2883934e 100644
--- a/python/ql/test/library-tests/frameworks/fastapi/response_test.py
+++ b/python/ql/test/library-tests/frameworks/fastapi/response_test.py
@@ -7,18 +7,18 @@ app = FastAPI()
@app.get("/response_parameter") # $ routeSetup="/response_parameter"
-async def response_parameter(response: Response): # $ requestHandler SPURIOUS: routedParameter=response
- response.set_cookie("key", "value") # $ MISSING: CookieWrite CookieName="key" CookieValue="value"
- response.set_cookie(key="key", value="value") # $ MISSING: CookieWrite CookieName="key" CookieValue="value"
- response.headers.append("Set-Cookie", "key2=value2") # $ MISSING: CookieWrite CookieRawHeader="key2=value2"
- response.headers.append(key="Set-Cookie", value="key2=value2") # $ MISSING: CookieWrite CookieRawHeader="key2=value2"
+async def response_parameter(response: Response): # $ requestHandler
+ response.set_cookie("key", "value") # $ CookieWrite CookieName="key" CookieValue="value"
+ response.set_cookie(key="key", value="value") # $ CookieWrite CookieName="key" CookieValue="value"
+ response.headers.append("Set-Cookie", "key2=value2") # $ CookieWrite CookieRawHeader="key2=value2"
+ response.headers.append(key="Set-Cookie", value="key2=value2") # $ CookieWrite CookieRawHeader="key2=value2"
response.headers["X-MyHeader"] = "header-value"
response.status_code = 418
return {"message": "response as parameter"} # $ HttpResponse mimetype=application/json responseBody=Dict
@app.get("/resp_parameter") # $ routeSetup="/resp_parameter"
-async def resp_parameter(resp: Response): # $ requestHandler SPURIOUS: routedParameter=resp
+async def resp_parameter(resp: Response): # $ requestHandler
resp.status_code = 418
return {"message": "resp as parameter"} # $ HttpResponse mimetype=application/json responseBody=Dict
From c9895b54fe18643969cf7e3f14b6a0a71d2b8af0 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Fri, 24 Sep 2021 21:22:33 +0200
Subject: [PATCH 026/471] Python: FastAPI: Add tests for direct response
construction
---
.../frameworks/fastapi/response_test.py | 99 ++++++++++++++++++-
1 file changed, 96 insertions(+), 3 deletions(-)
diff --git a/python/ql/test/library-tests/frameworks/fastapi/response_test.py b/python/ql/test/library-tests/frameworks/fastapi/response_test.py
index c2e2883934e..5c0e764ced3 100644
--- a/python/ql/test/library-tests/frameworks/fastapi/response_test.py
+++ b/python/ql/test/library-tests/frameworks/fastapi/response_test.py
@@ -1,7 +1,7 @@
# see https://fastapi.tiangolo.com/advanced/response-cookies/
from fastapi import FastAPI, Response
-
+import asyncio
app = FastAPI()
@@ -31,8 +31,101 @@ async def response_parameter_no_type(response): # $ requestHandler routedParamet
return {"message": "response as parameter"} # $ HttpResponse mimetype=application/json responseBody=Dict
-# Direct response
+# Direct response construction
# see https://fastapi.tiangolo.com/advanced/response-directly/
+# see https://fastapi.tiangolo.com/advanced/custom-response/
-# TODO
+import fastapi.responses
+
+
+@app.get("/direct_response") # $ routeSetup="/direct_response"
+async def direct_response(): # $ requestHandler
+ xml_data = "FOO"
+ resp = fastapi.responses.Response(xml_data, 200, None, "application/xml") # $ MISSING: HttpResponse mimetype=application/xml responseBody=xml_data
+ resp = fastapi.responses.Response(content=xml_data, media_type="application/xml") # $ MISSING: HttpResponse mimetype=application/xml responseBody=xml_data
+ return resp # $ SPURIOUS: HttpResponse mimetype=application/json responseBody=resp
+
+
+@app.get("/direct_response2", response_class=fastapi.responses.Response) # $ routeSetup="/direct_response2"
+async def direct_response2(): # $ requestHandler
+ xml_data = "FOO"
+ return xml_data # $ HttpResponse responseBody=xml_data SPURIOUS: mimetype=application/json
+
+
+class MyXmlResponse(fastapi.responses.Response):
+ media_type = "application/xml"
+
+
+@app.get("/my_xml_response") # $ routeSetup="/my_xml_response"
+async def my_xml_response(): # $ requestHandler
+ xml_data = "FOO"
+ resp = MyXmlResponse(content=xml_data) # $ MISSING: HttpResponse mimetype=application/xml responseBody=xml_data
+ return resp # $ SPURIOUS: HttpResponse mimetype=application/json responseBody=resp
+
+
+@app.get("/my_xml_response2", response_class=MyXmlResponse) # $ routeSetup="/my_xml_response2"
+async def my_xml_response2(): # $ requestHandler
+ xml_data = "FOO"
+ return xml_data # $ HttpResponse responseBody=xml_data SPURIOUS: mimetype=application/json MISSING: mimetype=application/xml
+
+
+@app.get("/html_response") # $ routeSetup="/html_response"
+async def html_response(): # $ requestHandler
+ hello_world = "Hello World!
"
+ resp = fastapi.responses.HTMLResponse(hello_world) # $ MISSING: HttpResponse mimetype=text/html responseBody=hello_world
+ return resp # $ SPURIOUS: HttpResponse mimetype=application/json responseBody=resp
+
+
+@app.get("/html_response2", response_class=fastapi.responses.HTMLResponse) # $ routeSetup="/html_response2"
+async def html_response2(): # $ requestHandler
+ hello_world = "Hello World!
"
+ return hello_world # $ HttpResponse responseBody=hello_world SPURIOUS: mimetype=application/json MISSING: mimetype=text/html
+
+
+@app.get("/redirect") # $ routeSetup="/redirect"
+async def redirect(): # $ requestHandler
+ next = "https://www.example.com"
+ resp = fastapi.responses.RedirectResponse(next) # $ MISSING: HttpResponse HttpRedirectResponse redirectLocation=next
+ return resp # $ SPURIOUS: HttpResponse mimetype=application/json responseBody=resp
+
+
+@app.get("/redirect2", response_class=fastapi.responses.RedirectResponse) # $ routeSetup="/redirect2"
+async def redirect2(): # $ requestHandler
+ next = "https://www.example.com"
+ return next # $ HttpResponse SPURIOUS: mimetype=application/json responseBody=next MISSING: HttpRedirectResponse redirectLocation=next
+
+
+@app.get("/streaming_response") # $ routeSetup="/streaming_response"
+async def streaming_response(): # $ requestHandler
+ # You can test this with curl:
+ # curl --no-buffer http://127.0.0.1:8000/streaming_response
+ async def content():
+ yield b"Hello "
+ await asyncio.sleep(0.5)
+ yield b"World"
+ await asyncio.sleep(0.5)
+ yield b"!"
+
+ resp = fastapi.responses.StreamingResponse(content()) # $ MISSING: HttpResponse responseBody=content()
+ return resp # $ SPURIOUS: HttpResponse mimetype=application/json responseBody=resp
+
+
+# setting `response_class` to `StreamingResponse` does not seem to work
+# so no such example here
+
+
+@app.get("/file_response") # $ routeSetup="/file_response"
+async def file_response(): # $ requestHandler
+ # has internal dependency on PyPI package `aiofiles`
+ # will guess MIME type from file extension
+
+ # We don't really have any good QL modeling of passing a file-path, whose content
+ # will be returned as part of the response... so will leave this as a TODO for now.
+ resp = fastapi.responses.FileResponse(__file__) # $ MISSING: HttpResponse
+ return resp # $ SPURIOUS: HttpResponse mimetype=application/json responseBody=resp
+
+
+@app.get("/file_response2", response_class=fastapi.responses.FileResponse) # $ routeSetup="/file_response2"
+async def file_response2(): # $ requestHandler
+ return __file__ # $ HttpResponse SPURIOUS: mimetype=application/json responseBody=__file__
From eef946a0c8804ac742cf6608a897e6b5368c5a4a Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 29 Sep 2021 13:27:40 +0200
Subject: [PATCH 027/471] Python: FastAPI: Add test for custom response
annotation
It really is rather contrived, but it also _does_ work.
---
.../frameworks/fastapi/response_test.py | 24 +++++++++++++++----
1 file changed, 19 insertions(+), 5 deletions(-)
diff --git a/python/ql/test/library-tests/frameworks/fastapi/response_test.py b/python/ql/test/library-tests/frameworks/fastapi/response_test.py
index 5c0e764ced3..36ddb397461 100644
--- a/python/ql/test/library-tests/frameworks/fastapi/response_test.py
+++ b/python/ql/test/library-tests/frameworks/fastapi/response_test.py
@@ -1,6 +1,7 @@
# see https://fastapi.tiangolo.com/advanced/response-cookies/
from fastapi import FastAPI, Response
+import fastapi.responses
import asyncio
app = FastAPI()
@@ -31,12 +32,29 @@ async def response_parameter_no_type(response): # $ requestHandler routedParamet
return {"message": "response as parameter"} # $ HttpResponse mimetype=application/json responseBody=Dict
+class MyXmlResponse(fastapi.responses.Response):
+ media_type = "application/xml"
+
+
+@app.get("/response_parameter_custom_type", response_class=MyXmlResponse) # $ routeSetup="/response_parameter_custom_type"
+async def response_parameter_custom_type(response: MyXmlResponse): # $ requestHandler SPURIOUS: routedParameter=response
+ # NOTE: This is a contrived example of using a wrong annotation for the response
+ # parameter. It will be passed a `fastapi.responses.Response` value when handling an
+ # incoming request, so NOT a `MyXmlResponse` value. Cookies/Headers are still
+ # propagated to the final response though.
+ print(type(response))
+ assert type(response) == fastapi.responses.Response
+ response.set_cookie("key", "value") # $ MISSING: CookieWrite CookieName="key" CookieValue="value"
+ response.headers["Custom-Response-Type"] = "yes, but only after function has run"
+ xml_data = "FOO"
+ return xml_data # $ HttpResponse responseBody=xml_data SPURIOUS: mimetype=application/json MISSING: mimetype=application/xml
+
+
# Direct response construction
# see https://fastapi.tiangolo.com/advanced/response-directly/
# see https://fastapi.tiangolo.com/advanced/custom-response/
-import fastapi.responses
@app.get("/direct_response") # $ routeSetup="/direct_response"
@@ -53,10 +71,6 @@ async def direct_response2(): # $ requestHandler
return xml_data # $ HttpResponse responseBody=xml_data SPURIOUS: mimetype=application/json
-class MyXmlResponse(fastapi.responses.Response):
- media_type = "application/xml"
-
-
@app.get("/my_xml_response") # $ routeSetup="/my_xml_response"
async def my_xml_response(): # $ requestHandler
xml_data = "FOO"
From 50147708bf0338c4d74dd4a1c8a4da2b0a614e42 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 29 Sep 2021 17:30:49 +0200
Subject: [PATCH 028/471] Python: FastAPI: Model response classes
Figuring out how to do the `media_type` tracking was quite difficult.
---
.../lib/semmle/python/frameworks/FastApi.qll | 115 ++++++++++++++++--
.../frameworks/fastapi/response_test.py | 18 +--
2 files changed, 116 insertions(+), 17 deletions(-)
diff --git a/python/ql/lib/semmle/python/frameworks/FastApi.qll b/python/ql/lib/semmle/python/frameworks/FastApi.qll
index 657cf120aaf..cff56c16d5f 100644
--- a/python/ql/lib/semmle/python/frameworks/FastApi.qll
+++ b/python/ql/lib/semmle/python/frameworks/FastApi.qll
@@ -79,14 +79,80 @@ private module FastApi {
// Response modeling
// ---------------------------------------------------------------------------
/**
- * Provides models for the `fastapi.Response` class
+ * Provides models for the `fastapi.Response` class and subclasses.
+ *
+ * See https://fastapi.tiangolo.com/advanced/custom-response/#response.
*/
module Response {
- /** Gets a reference to the `fastapi.Response` class. */
- private API::Node classRef() { result = API::moduleImport("fastapi").getMember("Response") }
+ /**
+ * Gets the `API::Node` for the manually modeled response classes called `name`.
+ */
+ private API::Node getModeledResponseClass(string name) {
+ name = "Response" and
+ result = API::moduleImport("fastapi").getMember("Response")
+ or
+ // see https://github.com/tiangolo/fastapi/blob/master/fastapi/responses.py
+ name in [
+ "Response", "HTMLResponse", "PlainTextResponse", "JSONResponse", "UJSONResponse",
+ "ORJSONResponse", "RedirectResponse", "StreamingResponse", "FileResponse"
+ ] and
+ result = API::moduleImport("fastapi").getMember("responses").getMember(name)
+ }
/**
- * A source of instances of `fastapi.Response`, extend this class to model new instances.
+ * Gets the default MIME type for a FastAPI response class (defined with the
+ * `media_type` class-attribute).
+ *
+ * Also models user-defined classes and tries to take inheritance into account.
+ *
+ * TODO: build easy way to solve problems like this, like we used to have the
+ * `ClassValue.lookup` predicate.
+ */
+ private string getDefaultMimeType(API::Node responseClass) {
+ exists(string name | responseClass = getModeledResponseClass(name) |
+ // no defaults for these.
+ name in ["Response", "RedirectResponse", "StreamingResponse"] and
+ none()
+ or
+ // For `FileResponse` the code will guess what mimetype
+ // to use, or fall back to "text/plain", but claiming that all responses will
+ // have "text/plain" per default is also highly inaccurate, so just going to not
+ // do anything about this.
+ name = "FileResponse" and
+ none()
+ or
+ name = "HTMLResponse" and
+ result = "text/html"
+ or
+ name = "PlainTextResponse" and
+ result = "text/plain"
+ or
+ name in ["JSONResponse", "UJSONResponse", "ORJSONResponse"] and
+ result = "application/json"
+ )
+ or
+ // user-defined subclasses
+ exists(Class cls, API::Node base |
+ cls.getABase() = base.getAUse().asExpr() and
+ // since we _know_ that the base has an API Node, that means there is a subclass
+ // edge leading to the `ClassExpr` for the class.
+ responseClass.getAnImmediateUse().asExpr().(ClassExpr) = cls.getParent()
+ |
+ exists(Assign assign | assign = cls.getAStmt() |
+ assign.getATarget().(Name).getId() = "media_type" and
+ result = assign.getValue().(StrConst).getText()
+ )
+ or
+ // TODO: this should use a proper MRO calculation instead
+ not exists(Assign assign | assign = cls.getAStmt() |
+ assign.getATarget().(Name).getId() = "media_type"
+ ) and
+ result = getDefaultMimeType(base)
+ )
+ }
+
+ /**
+ * A source of instances of `fastapi.Response` and its' subclasses, extend this class to model new instances.
*
* This can include instantiations of the class, return values from function
* calls, or a special parameter that will be set when functions are called by an external
@@ -96,9 +162,41 @@ private module FastApi {
*/
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
- /** A direct instantiation of `fastapi.Response`. */
- private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
- ClassInstantiation() { this = classRef().getACall() }
+ /** A direct instantiation of a response class. */
+ private class ResponseInstantiation extends InstanceSource, HTTP::Server::HttpResponse::Range,
+ DataFlow::CallCfgNode {
+ API::Node baseApiNode;
+ API::Node responseClass;
+
+ ResponseInstantiation() {
+ baseApiNode = getModeledResponseClass(_) and
+ responseClass = baseApiNode.getASubclass*() and
+ this = responseClass.getACall()
+ }
+
+ override DataFlow::Node getBody() {
+ not baseApiNode = getModeledResponseClass(["RedirectResponse", "FileResponse"]) and
+ result in [this.getArg(0), this.getArgByName("content")]
+ }
+
+ override DataFlow::Node getMimetypeOrContentTypeArg() {
+ not baseApiNode = getModeledResponseClass("RedirectResponse") and
+ result in [this.getArg(3), this.getArgByName("media_type")]
+ }
+
+ override string getMimetypeDefault() { result = getDefaultMimeType(responseClass) }
+ }
+
+ /**
+ * A direct instantiation of a redirect response.
+ */
+ private class RedirectResponseInstantiation extends ResponseInstantiation,
+ HTTP::Server::HttpRedirectResponse::Range {
+ RedirectResponseInstantiation() { baseApiNode = getModeledResponseClass("RedirectResponse") }
+
+ override DataFlow::Node getRedirectLocation() {
+ result in [this.getArg(0), this.getArgByName("url")]
+ }
}
/**
@@ -109,7 +207,8 @@ private module FastApi {
*/
class RequestHandlerParam extends InstanceSource, DataFlow::ParameterNode {
RequestHandlerParam() {
- this.getParameter().getAnnotation() = classRef().getAUse().asExpr() and
+ this.getParameter().getAnnotation() =
+ getModeledResponseClass(_).getASubclass*().getAUse().asExpr() and
any(FastApiRouteSetup rs).getARequestHandler().getArgByName(_) = this.getParameter()
}
}
diff --git a/python/ql/test/library-tests/frameworks/fastapi/response_test.py b/python/ql/test/library-tests/frameworks/fastapi/response_test.py
index 36ddb397461..6e4999316f3 100644
--- a/python/ql/test/library-tests/frameworks/fastapi/response_test.py
+++ b/python/ql/test/library-tests/frameworks/fastapi/response_test.py
@@ -37,14 +37,14 @@ class MyXmlResponse(fastapi.responses.Response):
@app.get("/response_parameter_custom_type", response_class=MyXmlResponse) # $ routeSetup="/response_parameter_custom_type"
-async def response_parameter_custom_type(response: MyXmlResponse): # $ requestHandler SPURIOUS: routedParameter=response
+async def response_parameter_custom_type(response: MyXmlResponse): # $ requestHandler
# NOTE: This is a contrived example of using a wrong annotation for the response
# parameter. It will be passed a `fastapi.responses.Response` value when handling an
# incoming request, so NOT a `MyXmlResponse` value. Cookies/Headers are still
# propagated to the final response though.
print(type(response))
assert type(response) == fastapi.responses.Response
- response.set_cookie("key", "value") # $ MISSING: CookieWrite CookieName="key" CookieValue="value"
+ response.set_cookie("key", "value") # $ CookieWrite CookieName="key" CookieValue="value"
response.headers["Custom-Response-Type"] = "yes, but only after function has run"
xml_data = "FOO"
return xml_data # $ HttpResponse responseBody=xml_data SPURIOUS: mimetype=application/json MISSING: mimetype=application/xml
@@ -60,8 +60,8 @@ async def response_parameter_custom_type(response: MyXmlResponse): # $ requestHa
@app.get("/direct_response") # $ routeSetup="/direct_response"
async def direct_response(): # $ requestHandler
xml_data = "FOO"
- resp = fastapi.responses.Response(xml_data, 200, None, "application/xml") # $ MISSING: HttpResponse mimetype=application/xml responseBody=xml_data
- resp = fastapi.responses.Response(content=xml_data, media_type="application/xml") # $ MISSING: HttpResponse mimetype=application/xml responseBody=xml_data
+ resp = fastapi.responses.Response(xml_data, 200, None, "application/xml") # $ HttpResponse mimetype=application/xml responseBody=xml_data
+ resp = fastapi.responses.Response(content=xml_data, media_type="application/xml") # $ HttpResponse mimetype=application/xml responseBody=xml_data
return resp # $ SPURIOUS: HttpResponse mimetype=application/json responseBody=resp
@@ -74,7 +74,7 @@ async def direct_response2(): # $ requestHandler
@app.get("/my_xml_response") # $ routeSetup="/my_xml_response"
async def my_xml_response(): # $ requestHandler
xml_data = "FOO"
- resp = MyXmlResponse(content=xml_data) # $ MISSING: HttpResponse mimetype=application/xml responseBody=xml_data
+ resp = MyXmlResponse(content=xml_data) # $ HttpResponse mimetype=application/xml responseBody=xml_data
return resp # $ SPURIOUS: HttpResponse mimetype=application/json responseBody=resp
@@ -87,7 +87,7 @@ async def my_xml_response2(): # $ requestHandler
@app.get("/html_response") # $ routeSetup="/html_response"
async def html_response(): # $ requestHandler
hello_world = "Hello World!
"
- resp = fastapi.responses.HTMLResponse(hello_world) # $ MISSING: HttpResponse mimetype=text/html responseBody=hello_world
+ resp = fastapi.responses.HTMLResponse(hello_world) # $ HttpResponse mimetype=text/html responseBody=hello_world
return resp # $ SPURIOUS: HttpResponse mimetype=application/json responseBody=resp
@@ -100,7 +100,7 @@ async def html_response2(): # $ requestHandler
@app.get("/redirect") # $ routeSetup="/redirect"
async def redirect(): # $ requestHandler
next = "https://www.example.com"
- resp = fastapi.responses.RedirectResponse(next) # $ MISSING: HttpResponse HttpRedirectResponse redirectLocation=next
+ resp = fastapi.responses.RedirectResponse(next) # $ HttpResponse HttpRedirectResponse redirectLocation=next
return resp # $ SPURIOUS: HttpResponse mimetype=application/json responseBody=resp
@@ -121,7 +121,7 @@ async def streaming_response(): # $ requestHandler
await asyncio.sleep(0.5)
yield b"!"
- resp = fastapi.responses.StreamingResponse(content()) # $ MISSING: HttpResponse responseBody=content()
+ resp = fastapi.responses.StreamingResponse(content()) # $ HttpResponse responseBody=content()
return resp # $ SPURIOUS: HttpResponse mimetype=application/json responseBody=resp
@@ -136,7 +136,7 @@ async def file_response(): # $ requestHandler
# We don't really have any good QL modeling of passing a file-path, whose content
# will be returned as part of the response... so will leave this as a TODO for now.
- resp = fastapi.responses.FileResponse(__file__) # $ MISSING: HttpResponse
+ resp = fastapi.responses.FileResponse(__file__) # $ HttpResponse
return resp # $ SPURIOUS: HttpResponse mimetype=application/json responseBody=resp
From c839f354851c110a2a87f365bd698c0ed71754b7 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 29 Sep 2021 17:49:48 +0200
Subject: [PATCH 029/471] Python: FastAPI: Proper modeling of implicit returns
---
.../lib/semmle/python/frameworks/FastApi.qll | 81 ++++++++++++++-----
.../frameworks/fastapi/response_test.py | 12 +--
2 files changed, 68 insertions(+), 25 deletions(-)
diff --git a/python/ql/lib/semmle/python/frameworks/FastApi.qll b/python/ql/lib/semmle/python/frameworks/FastApi.qll
index cff56c16d5f..39c36ded717 100644
--- a/python/ql/lib/semmle/python/frameworks/FastApi.qll
+++ b/python/ql/lib/semmle/python/frameworks/FastApi.qll
@@ -73,6 +73,9 @@ private module FastApi {
override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
override string getFramework() { result = "FastAPI" }
+
+ /** Gets the argument specifying the response class to use, if any. */
+ DataFlow::Node getResponseClassArg() { result = this.getArgByName("response_class") }
}
// ---------------------------------------------------------------------------
@@ -199,6 +202,65 @@ private module FastApi {
}
}
+ /**
+ * An implicit response from a return of FastAPI request handler.
+ */
+ private class FastApiRequestHandlerReturn extends HTTP::Server::HttpResponse::Range,
+ DataFlow::CfgNode {
+ FastApiRouteSetup routeSetup;
+
+ FastApiRequestHandlerReturn() {
+ node = routeSetup.getARequestHandler().getAReturnValueFlowNode()
+ }
+
+ override DataFlow::Node getBody() { result = this }
+
+ override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
+
+ override string getMimetypeDefault() {
+ exists(API::Node responseClass |
+ responseClass.getAUse() = routeSetup.getResponseClassArg() and
+ result = getDefaultMimeType(responseClass)
+ )
+ or
+ not exists(routeSetup.getResponseClassArg()) and
+ result = "application/json"
+ }
+ }
+
+ /**
+ * An implicit response from a return of FastAPI request handler, that has
+ * `response_class` set to a `FileResponse`.
+ */
+ private class FastApiRequestHandlerFileResponseReturn extends FastApiRequestHandlerReturn {
+ FastApiRequestHandlerFileResponseReturn() {
+ exists(API::Node responseClass |
+ responseClass.getAUse() = routeSetup.getResponseClassArg() and
+ responseClass = getModeledResponseClass("FileResponse").getASubclass*()
+ )
+ }
+
+ override DataFlow::Node getBody() { none() }
+ }
+
+ /**
+ * An implicit response from a return of FastAPI request handler, that has
+ * `response_class` set to a `RedirectResponse`.
+ */
+ private class FastApiRequestHandlerRedirectReturn extends FastApiRequestHandlerReturn,
+ HTTP::Server::HttpRedirectResponse::Range {
+ FastApiRequestHandlerRedirectReturn() {
+ exists(API::Node responseClass |
+ responseClass.getAUse() = routeSetup.getResponseClassArg() and
+ responseClass = getModeledResponseClass("RedirectResponse").getASubclass*()
+ )
+ }
+
+ override DataFlow::Node getBody() { none() }
+
+ override DataFlow::Node getRedirectLocation() { result = this }
+ }
+
/**
* INTERNAL: Do not use.
*
@@ -263,23 +325,4 @@ private module FastApi {
override DataFlow::Node getValueArg() { none() }
}
}
-
- /**
- * Implicit response from returns of FastAPI request handlers
- */
- private class FastApiRequestHandlerReturn extends HTTP::Server::HttpResponse::Range,
- DataFlow::CfgNode {
- FastApiRequestHandlerReturn() {
- exists(Function requestHandler |
- requestHandler = any(FastApiRouteSetup rs).getARequestHandler() and
- node = requestHandler.getAReturnValueFlowNode()
- )
- }
-
- override DataFlow::Node getBody() { result = this }
-
- override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
-
- override string getMimetypeDefault() { result = "application/json" }
- }
}
diff --git a/python/ql/test/library-tests/frameworks/fastapi/response_test.py b/python/ql/test/library-tests/frameworks/fastapi/response_test.py
index 6e4999316f3..e5f0f039de2 100644
--- a/python/ql/test/library-tests/frameworks/fastapi/response_test.py
+++ b/python/ql/test/library-tests/frameworks/fastapi/response_test.py
@@ -47,7 +47,7 @@ async def response_parameter_custom_type(response: MyXmlResponse): # $ requestHa
response.set_cookie("key", "value") # $ CookieWrite CookieName="key" CookieValue="value"
response.headers["Custom-Response-Type"] = "yes, but only after function has run"
xml_data = "FOO"
- return xml_data # $ HttpResponse responseBody=xml_data SPURIOUS: mimetype=application/json MISSING: mimetype=application/xml
+ return xml_data # $ HttpResponse responseBody=xml_data mimetype=application/xml
# Direct response construction
@@ -68,7 +68,7 @@ async def direct_response(): # $ requestHandler
@app.get("/direct_response2", response_class=fastapi.responses.Response) # $ routeSetup="/direct_response2"
async def direct_response2(): # $ requestHandler
xml_data = "FOO"
- return xml_data # $ HttpResponse responseBody=xml_data SPURIOUS: mimetype=application/json
+ return xml_data # $ HttpResponse responseBody=xml_data
@app.get("/my_xml_response") # $ routeSetup="/my_xml_response"
@@ -81,7 +81,7 @@ async def my_xml_response(): # $ requestHandler
@app.get("/my_xml_response2", response_class=MyXmlResponse) # $ routeSetup="/my_xml_response2"
async def my_xml_response2(): # $ requestHandler
xml_data = "FOO"
- return xml_data # $ HttpResponse responseBody=xml_data SPURIOUS: mimetype=application/json MISSING: mimetype=application/xml
+ return xml_data # $ HttpResponse responseBody=xml_data mimetype=application/xml
@app.get("/html_response") # $ routeSetup="/html_response"
@@ -94,7 +94,7 @@ async def html_response(): # $ requestHandler
@app.get("/html_response2", response_class=fastapi.responses.HTMLResponse) # $ routeSetup="/html_response2"
async def html_response2(): # $ requestHandler
hello_world = "Hello World!
"
- return hello_world # $ HttpResponse responseBody=hello_world SPURIOUS: mimetype=application/json MISSING: mimetype=text/html
+ return hello_world # $ HttpResponse responseBody=hello_world mimetype=text/html
@app.get("/redirect") # $ routeSetup="/redirect"
@@ -107,7 +107,7 @@ async def redirect(): # $ requestHandler
@app.get("/redirect2", response_class=fastapi.responses.RedirectResponse) # $ routeSetup="/redirect2"
async def redirect2(): # $ requestHandler
next = "https://www.example.com"
- return next # $ HttpResponse SPURIOUS: mimetype=application/json responseBody=next MISSING: HttpRedirectResponse redirectLocation=next
+ return next # $ HttpResponse HttpRedirectResponse redirectLocation=next
@app.get("/streaming_response") # $ routeSetup="/streaming_response"
@@ -142,4 +142,4 @@ async def file_response(): # $ requestHandler
@app.get("/file_response2", response_class=fastapi.responses.FileResponse) # $ routeSetup="/file_response2"
async def file_response2(): # $ requestHandler
- return __file__ # $ HttpResponse SPURIOUS: mimetype=application/json responseBody=__file__
+ return __file__ # $ HttpResponse
From 2d5c6e2723154d5458c872e7c0e131516163ee79 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 30 Sep 2021 17:39:22 +0200
Subject: [PATCH 030/471] Python: FastAPI: Add taint test
---
.../library-tests/frameworks/fastapi/basic.py | 6 +-
.../frameworks/fastapi/taint_test.py | 96 +++++++++++++++++++
2 files changed, 97 insertions(+), 5 deletions(-)
create mode 100644 python/ql/test/library-tests/frameworks/fastapi/taint_test.py
diff --git a/python/ql/test/library-tests/frameworks/fastapi/basic.py b/python/ql/test/library-tests/frameworks/fastapi/basic.py
index 472ac37ba10..b8b6073abaa 100644
--- a/python/ql/test/library-tests/frameworks/fastapi/basic.py
+++ b/python/ql/test/library-tests/frameworks/fastapi/basic.py
@@ -59,12 +59,8 @@ async def get_baz(baz_id: int): # $ requestHandler routedParameter=baz_id
# Docs:
# see https://fastapi.tiangolo.com/tutorial/path-params/
-# More stuff that we should support:
+# Things we should look at supporting:
# - https://fastapi.tiangolo.com/tutorial/dependencies/
-# - Extra taint-steps for files
-# - https://fastapi.tiangolo.com/tutorial/request-files/
-# - https://fastapi.tiangolo.com/tutorial/request-files/#uploadfile
# - https://fastapi.tiangolo.com/tutorial/background-tasks/
-#
# - https://fastapi.tiangolo.com/tutorial/middleware/
# - https://fastapi.tiangolo.com/tutorial/encoder/
diff --git a/python/ql/test/library-tests/frameworks/fastapi/taint_test.py b/python/ql/test/library-tests/frameworks/fastapi/taint_test.py
new file mode 100644
index 00000000000..a4de846fe21
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/fastapi/taint_test.py
@@ -0,0 +1,96 @@
+# --- to make things runable ---
+
+ensure_tainted = ensure_not_tainted = print
+
+# --- real code ---
+
+from fastapi import FastAPI
+from typing import Optional, List
+from pydantic import BaseModel
+
+
+app = FastAPI()
+
+
+class Foo(BaseModel):
+ foo: str
+
+
+class MyComplexModel(BaseModel):
+ field: str
+ main_foo: Foo
+ other_foos: List[Foo]
+
+
+@app.post("/test_taint/{name}/{number}") # $ routeSetup="/test_taint/{name}/{number}"
+async def test_taint(name : str, number : int, also_input: MyComplexModel): # $ requestHandler routedParameter=name routedParameter=number routedParameter=also_input
+ ensure_tainted(
+ name, # $ tainted
+ number, # $ tainted
+
+ also_input, # $ tainted
+ also_input.field, # $ MISSING: tainted
+
+ also_input.main_foo, # $ MISSING: tainted
+ also_input.main_foo.foo, # $ MISSING: tainted
+
+ also_input.other_foos, # $ MISSING: tainted
+ also_input.other_foos[0], # $ MISSING: tainted
+ also_input.other_foos[0].foo, # $ MISSING: tainted
+ [f.foo for f in also_input.other_foos], # $ MISSING: tainted
+ )
+
+ return "ok" # $ HttpResponse
+
+
+# --- body ---
+# see https://fastapi.tiangolo.com/tutorial/body-multiple-params/
+
+from fastapi import Body
+
+# request is made such as `/will-be-query-param?name=foo`
+@app.post("/will-be-query-param") # $ routeSetup="/will-be-query-param"
+async def will_be_query_param(name: str): # $ requestHandler routedParameter=name
+ ensure_tainted(name) # $ tainted
+ return "ok" # $ HttpResponse
+
+# with the `= Body(...)` "annotation" FastAPI will know to transmit `name` as part of
+# the HTTP post body
+@app.post("/will-not-be-query-param") # $ routeSetup="/will-not-be-query-param"
+async def will_not_be_query_param(name: str = Body("foo", media_type="text/plain")): # $ requestHandler routedParameter=name
+ ensure_tainted(name) # $ tainted
+ return "ok" # $ HttpResponse
+
+
+# --- form data ---
+# see https://fastapi.tiangolo.com/tutorial/request-forms/
+
+from fastapi import Form
+
+@app.post("/form-example") # $ routeSetup="/form-example"
+async def form_example(username: str = Form(None)): # $ requestHandler routedParameter=username
+ ensure_tainted(username) # $ tainted
+ return "ok" # $ HttpResponse
+
+
+# --- file upload ---
+# see https://fastapi.tiangolo.com/tutorial/request-files/
+# see https://fastapi.tiangolo.com/tutorial/request-files/#uploadfile
+
+from fastapi import File, UploadFile
+
+@app.post("/file-upload") # $ routeSetup="/file-upload"
+async def file_upload(f1: bytes = File(None), f2: UploadFile = File(None)): # $ requestHandler routedParameter=f1 routedParameter=f2
+ ensure_tainted(
+ f1, # $ tainted
+
+ f2, # $ tainted
+ f2.filename, # $ MISSING: tainted
+ f2.content_type, # $ MISSING: tainted
+ f2.file, # $ MISSING: tainted
+ f2.file.read(), # $ MISSING: tainted
+ f2.file.readline(), # $ MISSING: tainted
+ f2.file.readlines(), # $ MISSING: tainted
+ await f2.read(), # $ MISSING: tainted
+ )
+ return "ok" # $ HttpResponse
From 94e2676c0f71cff9b311d838ae4924c3c63f1dd8 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 30 Sep 2021 12:09:50 +0200
Subject: [PATCH 031/471] naive conversion of ldapjs model to API node
---
.../experimental/Security/CWE-090/Ldapjs.qll | 28 +++++++++----------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-090/Ldapjs.qll b/javascript/ql/src/experimental/Security/CWE-090/Ldapjs.qll
index 5cefa9a517d..afb8649945c 100644
--- a/javascript/ql/src/experimental/Security/CWE-090/Ldapjs.qll
+++ b/javascript/ql/src/experimental/Security/CWE-090/Ldapjs.qll
@@ -15,35 +15,35 @@ module Ldapjs {
/**
* Gets a data flow source node for an LDAP client.
*/
- abstract class LdapClient extends DataFlow::SourceNode { }
+ abstract class LdapClient extends API::Node { }
/**
* Gets a data flow source node for the ldapjs library.
*/
- private DataFlow::SourceNode ldapjs() { result = DataFlow::moduleImport("ldapjs") }
+ private API::Node ldapjs() { result = API::moduleImport("ldapjs") }
/**
* Gets a data flow source node for the ldapjs client.
*/
class LdapjsClient extends LdapClient {
- LdapjsClient() { this = ldapjs().getAMemberCall("createClient") }
+ LdapjsClient() { this = ldapjs().getMember("createClient").getReturn() }
}
/**
* Gets a data flow node for the client `search` options.
*/
- class LdapjsSearchOptions extends DataFlow::SourceNode {
- DataFlow::CallNode queryCall;
+ class LdapjsSearchOptions extends API::Node {
+ API::CallNode queryCall;
LdapjsSearchOptions() {
- queryCall = any(LdapjsClient client).getAMemberCall("search") and
- this = queryCall.getArgument(1).getALocalSource()
+ queryCall = any(LdapjsClient client).getMember("search").getACall() and
+ this = queryCall.getParameter(1)
}
/**
* Gets the LDAP query call that these options are used in.
*/
- DataFlow::InvokeNode getQueryCall() { result = queryCall }
+ API::CallNode getQueryCall() { result = queryCall }
}
/**
@@ -52,20 +52,20 @@ module Ldapjs {
class LdapjsSearchFilter extends DataFlow::Node {
LdapjsSearchOptions options;
- LdapjsSearchFilter() { this = options.getAPropertyWrite("filter").getRhs() }
+ LdapjsSearchFilter() { this = options.getMember("filter").getARhs() }
/**
* Gets the LDAP query call that this filter is used in.
*/
- DataFlow::InvokeNode getQueryCall() { result = options.getQueryCall() }
+ API::CallNode getQueryCall() { result = options.getQueryCall() }
}
/**
* A call to the ldapjs Client API methods.
*/
- class LdapjsClientAPICall extends DataFlow::CallNode {
+ class LdapjsClientAPICall extends API::CallNode {
LdapjsClientAPICall() {
- this = any(LdapjsClient client).getAMemberCall(getLdapjsClientDNMethodName())
+ this = any(LdapjsClient client).getMember(getLdapjsClientDNMethodName()).getACall()
}
}
@@ -80,13 +80,13 @@ module Ldapjs {
/**
* Gets the LDAP query call that this DN is used in.
*/
- DataFlow::InvokeNode getQueryCall() { result = queryCall }
+ API::CallNode getQueryCall() { result = queryCall }
}
/**
* Ldapjs parseFilter method call.
*/
class LdapjsParseFilter extends DataFlow::CallNode {
- LdapjsParseFilter() { this = ldapjs().getAMemberCall("parseFilter") }
+ LdapjsParseFilter() { this = ldapjs().getMember("parseFilter").getACall() }
}
}
From 2b286a856c04179fe7b81c5e0e4f8a3b84f9d55f Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 30 Sep 2021 12:17:29 +0200
Subject: [PATCH 032/471] naively move ldap into the SQL injection query
---
javascript/ql/lib/javascript.qll | 1 +
.../semmle/javascript/frameworks}/Ldapjs.qll | 0
.../dataflow/SqlInjectionCustomizations.qll | 40 ++++++++++
.../Security/CWE-090/LdapInjection.qhelp | 50 -------------
.../Security/CWE-090/LdapInjection.ql | 20 -----
.../Security/CWE-090/LdapInjection.qll | 25 -------
.../CWE-090/LdapInjectionCustomizations.qll | 73 -------------------
7 files changed, 41 insertions(+), 168 deletions(-)
rename javascript/ql/{src/experimental/Security/CWE-090 => lib/semmle/javascript/frameworks}/Ldapjs.qll (100%)
delete mode 100644 javascript/ql/src/experimental/Security/CWE-090/LdapInjection.qhelp
delete mode 100644 javascript/ql/src/experimental/Security/CWE-090/LdapInjection.ql
delete mode 100644 javascript/ql/src/experimental/Security/CWE-090/LdapInjection.qll
delete mode 100644 javascript/ql/src/experimental/Security/CWE-090/LdapInjectionCustomizations.qll
diff --git a/javascript/ql/lib/javascript.qll b/javascript/ql/lib/javascript.qll
index 3a0aa544348..f866be7778c 100644
--- a/javascript/ql/lib/javascript.qll
+++ b/javascript/ql/lib/javascript.qll
@@ -99,6 +99,7 @@ import semmle.javascript.frameworks.History
import semmle.javascript.frameworks.Immutable
import semmle.javascript.frameworks.Knex
import semmle.javascript.frameworks.LazyCache
+import semmle.javascript.frameworks.Ldapjs
import semmle.javascript.frameworks.LodashUnderscore
import semmle.javascript.frameworks.Logging
import semmle.javascript.frameworks.HttpFrameworks
diff --git a/javascript/ql/src/experimental/Security/CWE-090/Ldapjs.qll b/javascript/ql/lib/semmle/javascript/frameworks/Ldapjs.qll
similarity index 100%
rename from javascript/ql/src/experimental/Security/CWE-090/Ldapjs.qll
rename to javascript/ql/lib/semmle/javascript/frameworks/Ldapjs.qll
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll
index 4c40ac4d2a5..939ad66cd64 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll
@@ -41,4 +41,44 @@ module SqlInjection {
class GraphqlInjectionSink extends Sink {
GraphqlInjectionSink() { this instanceof GraphQL::GraphQLString }
}
+
+ /**
+ * An LDAP filter for an API call that executes an operation against the LDAP server.
+ */
+ class LdapjsSearchFilterAsSink extends Sink {
+ // TODO: As taint-step?
+ /*
+ * override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
+ * exists(LdapjsParseFilter filter |
+ * pred = filter.getArgument(0) and
+ * succ = filter
+ * )
+ * }
+ */
+
+ LdapjsSearchFilterAsSink() { this instanceof Ldapjs::LdapjsSearchFilter }
+ }
+
+ /**
+ * An LDAP DN argument for an API call that executes an operation against the LDAP server.
+ */
+ class LdapjsDNArgumentAsSink extends Sink {
+ LdapjsDNArgumentAsSink() { this instanceof Ldapjs::LdapjsDNArgument }
+ }
+
+ /**
+ * A call to a function whose name suggests that it escapes LDAP search query parameter.
+ */
+ class FilterOrDNSanitizationCall extends Sanitizer, DataFlow::CallNode {
+ // TODO: remove, or use something else? (AdhocWhitelistSanitizer?)
+ FilterOrDNSanitizationCall() {
+ exists(string sanitize, string input |
+ sanitize = "(?:escape|saniti[sz]e|validate|filter)" and
+ input = "[Ii]nput?"
+ |
+ this.getCalleeName()
+ .regexpMatch("(?i)(" + sanitize + input + ")" + "|(" + input + sanitize + ")")
+ )
+ }
+ }
}
diff --git a/javascript/ql/src/experimental/Security/CWE-090/LdapInjection.qhelp b/javascript/ql/src/experimental/Security/CWE-090/LdapInjection.qhelp
deleted file mode 100644
index cd5d6fe169e..00000000000
--- a/javascript/ql/src/experimental/Security/CWE-090/LdapInjection.qhelp
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-If an LDAP query is built using string concatenation or string formatting, and the
-components of the concatenation include user input without any proper sanitization, a user
-is likely to be able to run malicious LDAP queries.
-
-
-
-If user input must be included in an LDAP query, it should be escaped to
-avoid a malicious user providing special characters that change the meaning
-of the query. In NodeJS, it is possible to build the LDAP query using frameworks like ldapjs.
-The library provides a Filter API, however it's still possibile to pass a string version of an LDAP filter.
-A good practice is to escape filter characters that could change the meaning of the query (https://tools.ietf.org/search/rfc4515#section-3).
-
-
-
-In the following examples, the code accepts a username from the user, which it uses in a LDAP query.
-
-The first and the second example uses the unsanitized user input directly
-in the search filter for the LDAP query.
-A malicious user could provide special characters to change the meaning of these
-queries, and search for a completely different set of values.
-
-
-
-
-
-In the third example the username is sanitized before it is included in the search filters.
-This ensures the meaning of the query cannot be changed by a malicious user.
-
-
-
-In the fourth example the username is passed to an OrFilter filter before it is included in the search filters.
-This ensures the meaning of the query cannot be changed by a malicious user.
-
-
-
-
-
-OWASP: LDAP Injection Prevention Cheat Sheet.
-LDAPjs: Documentation for LDAPjs.
-Github: ldapjs.
-Wikipedia: LDAP injection.
-BlackHat: LDAP Injection and Blind LDAP Injection.
-LDAP: Understanding and Defending Against LDAP Injection Attacks.
-
-
diff --git a/javascript/ql/src/experimental/Security/CWE-090/LdapInjection.ql b/javascript/ql/src/experimental/Security/CWE-090/LdapInjection.ql
deleted file mode 100644
index f19d1ac6125..00000000000
--- a/javascript/ql/src/experimental/Security/CWE-090/LdapInjection.ql
+++ /dev/null
@@ -1,20 +0,0 @@
-/**
- * @name LDAP query built from user-controlled sources
- * @description Building an LDAP query from user-controlled sources is vulnerable to insertion of
- * malicious LDAP code by the user.
- * @kind path-problem
- * @problem.severity error
- * @precision high
- * @id javascript/ldap-injection
- * @tags security
- * external/cwe/cwe-090
- */
-
-import javascript
-import DataFlow::PathGraph
-import LdapInjection::LdapInjection
-
-from LdapInjectionConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
-where config.hasFlowPath(source, sink)
-select sink.getNode(), source, sink, "$@ might include code from $@.",
- sink.getNode().(Sink).getQueryCall(), "LDAP query call", source.getNode(), "user-provided value"
diff --git a/javascript/ql/src/experimental/Security/CWE-090/LdapInjection.qll b/javascript/ql/src/experimental/Security/CWE-090/LdapInjection.qll
deleted file mode 100644
index 406a350d2d3..00000000000
--- a/javascript/ql/src/experimental/Security/CWE-090/LdapInjection.qll
+++ /dev/null
@@ -1,25 +0,0 @@
-import javascript
-
-module LdapInjection {
- import LdapInjectionCustomizations::LdapInjection
-
- /**
- * A taint-tracking configuration for reasoning about LDAP injection vulnerabilities.
- */
- class LdapInjectionConfiguration extends TaintTracking::Configuration {
- LdapInjectionConfiguration() { this = "LdapInjection" }
-
- override predicate isSource(DataFlow::Node source) { source instanceof Source }
-
- override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
-
- override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
-
- override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
- exists(LdapjsParseFilter filter |
- pred = filter.getArgument(0) and
- succ = filter
- )
- }
- }
-}
diff --git a/javascript/ql/src/experimental/Security/CWE-090/LdapInjectionCustomizations.qll b/javascript/ql/src/experimental/Security/CWE-090/LdapInjectionCustomizations.qll
deleted file mode 100644
index cb0292a5c4d..00000000000
--- a/javascript/ql/src/experimental/Security/CWE-090/LdapInjectionCustomizations.qll
+++ /dev/null
@@ -1,73 +0,0 @@
-/**
- * Provides default sources, sinks and sanitizers for reasoning about
- * LDAP injection vulnerabilities, as well as extension points for
- * adding your own.
- */
-
-import javascript
-
-module LdapInjection {
- import Ldapjs::Ldapjs
-
- /**
- * A data flow source for LDAP injection vulnerabilities.
- */
- abstract class Source extends DataFlow::Node { }
-
- /**
- * A data flow sink for LDAP injection vulnerabilities.
- */
- abstract class Sink extends DataFlow::Node {
- /**
- * Gets the LDAP query call that the sink flows into.
- */
- abstract DataFlow::Node getQueryCall();
- }
-
- /**
- * A sanitizer for LDAP injection vulnerabilities.
- */
- abstract class Sanitizer extends DataFlow::Node { }
-
- /**
- * A source of remote user input, considered as a flow source for LDAP injection.
- */
- class RemoteSource extends Source {
- RemoteSource() { this instanceof RemoteFlowSource }
- }
-
- /**
- * An LDAP filter for an API call that executes an operation against the LDAP server.
- */
- class LdapjsSearchFilterAsSink extends Sink {
- LdapjsSearchFilterAsSink() { this instanceof LdapjsSearchFilter }
-
- override DataFlow::InvokeNode getQueryCall() {
- result = this.(LdapjsSearchFilter).getQueryCall()
- }
- }
-
- /**
- * An LDAP DN argument for an API call that executes an operation against the LDAP server.
- */
- class LdapjsDNArgumentAsSink extends Sink {
- LdapjsDNArgumentAsSink() { this instanceof LdapjsDNArgument }
-
- override DataFlow::InvokeNode getQueryCall() { result = this.(LdapjsDNArgument).getQueryCall() }
- }
-
- /**
- * A call to a function whose name suggests that it escapes LDAP search query parameter.
- */
- class FilterOrDNSanitizationCall extends Sanitizer, DataFlow::CallNode {
- FilterOrDNSanitizationCall() {
- exists(string sanitize, string input |
- sanitize = "(?:escape|saniti[sz]e|validate|filter)" and
- input = "[Ii]nput?"
- |
- this.getCalleeName()
- .regexpMatch("(?i)(" + sanitize + input + ")" + "|(" + input + sanitize + ")")
- )
- }
- }
-}
From 9b5ff66b68642404595daed9d6c23ffc53e8e24d Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 30 Sep 2021 12:43:23 +0200
Subject: [PATCH 033/471] naively port tests from ldap examples
---
.../CWE-089/untyped/SqlInjection.expected | 37 +++++++++++
.../Security/CWE-089/untyped/ldap.js | 64 +++++++++++++++++++
2 files changed, 101 insertions(+)
create mode 100644 javascript/ql/test/query-tests/Security/CWE-089/untyped/ldap.js
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 daed7f03e20..5f93d2fc99d 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
@@ -68,6 +68,22 @@ nodes
| json-schema-validator.js:59:22:59:26 | query |
| json-schema-validator.js:61:22:61:26 | query |
| json-schema-validator.js:61:22:61:26 | query |
+| ldap.js:20:7:20:34 | q |
+| ldap.js:20:11:20:34 | url.par ... , true) |
+| ldap.js:20:21:20:27 | req.url |
+| ldap.js:20:21:20:27 | req.url |
+| ldap.js:22:7:22:33 | username |
+| ldap.js:22:18:22:18 | q |
+| ldap.js:22:18:22:24 | q.query |
+| ldap.js:22:18:22:33 | q.query.username |
+| ldap.js:25:13:25:57 | `(\|(nam ... ame}))` |
+| ldap.js:25:13:25:57 | `(\|(nam ... ame}))` |
+| ldap.js:25:24:25:31 | username |
+| ldap.js:25:46:25:53 | username |
+| ldap.js:32:15:32:59 | `(\|(nam ... ame}))` |
+| ldap.js:32:15:32:59 | `(\|(nam ... ame}))` |
+| ldap.js:32:26:32:33 | username |
+| ldap.js:32:48:32:55 | username |
| marsdb-flow-to.js:10:9:10:18 | query |
| marsdb-flow-to.js:10:17:10:18 | {} |
| marsdb-flow-to.js:11:17:11:24 | req.body |
@@ -444,6 +460,25 @@ edges
| json-schema-validator.js:50:23:50:48 | JSON.pa ... y.data) | json-schema-validator.js:50:15:50:48 | query |
| json-schema-validator.js:50:34:50:47 | req.query.data | json-schema-validator.js:50:23:50:48 | JSON.pa ... y.data) |
| json-schema-validator.js:50:34:50:47 | req.query.data | json-schema-validator.js:50:23:50:48 | JSON.pa ... y.data) |
+| ldap.js:20:7:20:34 | q | ldap.js:22:18:22:18 | q |
+| ldap.js:20:11:20:34 | url.par ... , true) | ldap.js:20:7:20:34 | q |
+| ldap.js:20:21:20:27 | req.url | ldap.js:20:11:20:34 | url.par ... , true) |
+| ldap.js:20:21:20:27 | req.url | ldap.js:20:11:20:34 | url.par ... , true) |
+| ldap.js:22:7:22:33 | username | ldap.js:25:24:25:31 | username |
+| ldap.js:22:7:22:33 | username | ldap.js:25:46:25:53 | username |
+| ldap.js:22:7:22:33 | username | ldap.js:32:26:32:33 | username |
+| ldap.js:22:7:22:33 | username | ldap.js:32:48:32:55 | username |
+| ldap.js:22:18:22:18 | q | ldap.js:22:18:22:24 | q.query |
+| ldap.js:22:18:22:24 | q.query | ldap.js:22:18:22:33 | q.query.username |
+| ldap.js:22:18:22:33 | q.query.username | ldap.js:22:7:22:33 | username |
+| ldap.js:25:24:25:31 | username | ldap.js:25:13:25:57 | `(\|(nam ... ame}))` |
+| ldap.js:25:24:25:31 | username | ldap.js:25:13:25:57 | `(\|(nam ... ame}))` |
+| ldap.js:25:46:25:53 | username | ldap.js:25:13:25:57 | `(\|(nam ... ame}))` |
+| ldap.js:25:46:25:53 | username | ldap.js:25:13:25:57 | `(\|(nam ... ame}))` |
+| ldap.js:32:26:32:33 | username | ldap.js:32:15:32:59 | `(\|(nam ... ame}))` |
+| ldap.js:32:26:32:33 | username | ldap.js:32:15:32:59 | `(\|(nam ... ame}))` |
+| ldap.js:32:48:32:55 | username | ldap.js:32:15:32:59 | `(\|(nam ... ame}))` |
+| ldap.js:32:48:32:55 | username | ldap.js:32:15:32:59 | `(\|(nam ... ame}))` |
| marsdb-flow-to.js:10:9:10:18 | query | marsdb-flow-to.js:14:17:14:21 | query |
| marsdb-flow-to.js:10:9:10:18 | query | marsdb-flow-to.js:14:17:14:21 | query |
| marsdb-flow-to.js:10:17:10:18 | {} | marsdb-flow-to.js:10:9:10:18 | query |
@@ -852,6 +887,8 @@ edges
| json-schema-validator.js:55:22:55:26 | query | json-schema-validator.js:50:34:50:47 | req.query.data | json-schema-validator.js:55:22:55:26 | query | This query depends on $@. | json-schema-validator.js:50:34:50:47 | req.query.data | a user-provided value |
| json-schema-validator.js:59:22:59:26 | query | json-schema-validator.js:50:34:50:47 | req.query.data | json-schema-validator.js:59:22:59:26 | query | This query depends on $@. | json-schema-validator.js:50:34:50:47 | req.query.data | a user-provided value |
| json-schema-validator.js:61:22:61:26 | query | json-schema-validator.js:50:34:50:47 | req.query.data | json-schema-validator.js:61:22:61:26 | query | This query depends on $@. | json-schema-validator.js:50:34:50:47 | req.query.data | a user-provided value |
+| ldap.js:25:13:25:57 | `(\|(nam ... ame}))` | ldap.js:20:21:20:27 | req.url | ldap.js:25:13:25:57 | `(\|(nam ... ame}))` | This query depends on $@. | ldap.js:20:21:20:27 | req.url | a user-provided value |
+| ldap.js:32:15:32:59 | `(\|(nam ... ame}))` | ldap.js:20:21:20:27 | req.url | ldap.js:32:15:32:59 | `(\|(nam ... ame}))` | This query depends on $@. | ldap.js:20:21:20:27 | req.url | a user-provided value |
| marsdb-flow-to.js:14:17:14:21 | query | marsdb-flow-to.js:11:17:11:24 | req.body | marsdb-flow-to.js:14:17:14:21 | query | This query depends on $@. | marsdb-flow-to.js:11:17:11:24 | req.body | a user-provided value |
| marsdb.js:16:12:16:16 | query | marsdb.js:13:17:13:24 | req.body | marsdb.js:16:12:16:16 | query | This query depends on $@. | marsdb.js:13:17:13:24 | req.body | a user-provided value |
| minimongo.js:18:12:18:16 | query | minimongo.js:15:17:15:24 | req.body | minimongo.js:18:12:18:16 | query | This query depends on $@. | minimongo.js:15:17:15:24 | req.body | a user-provided value |
diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/ldap.js b/javascript/ql/test/query-tests/Security/CWE-089/untyped/ldap.js
new file mode 100644
index 00000000000..a4a4691050e
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/ldap.js
@@ -0,0 +1,64 @@
+const http = require("http");
+const url = require("url");
+const ldap = require("ldapjs");
+const client = ldap.createClient({
+ url: "ldap://127.0.0.1:1389",
+});
+
+// https://github.com/vesse/node-ldapauth-fork/commit/3feea43e243698bcaeffa904a7324f4d96df60e4
+const sanitizeInput = function (input) {
+ return input
+ .replace(/\*/g, "\\2a")
+ .replace(/\(/g, "\\28")
+ .replace(/\)/g, "\\29")
+ .replace(/\\/g, "\\5c")
+ .replace(/\0/g, "\\00")
+ .replace(/\//g, "\\2f");
+};
+
+const server = http.createServer((req, res) => {
+ let q = url.parse(req.url, true);
+
+ let username = q.query.username;
+
+ var opts1 = {
+ filter: `(|(name=${username})(username=${username}))`, // NOT OK
+ };
+
+ client.search("o=example", opts1, function (err, res) {});
+
+ client.search(
+ "o=example",
+ { filter: `(|(name=${username})(username=${username}))` }, // NOT OK
+ function (err, res) {}
+ );
+
+ // GOOD
+ client.search(
+ "o=example",
+ {
+ filter: `(|(name=${sanitizeInput(username)})(username=${sanitizeInput(
+ username
+ )}))`,
+ },
+ function (err, res) {}
+ );
+
+ // GOOD (https://github.com/ldapjs/node-ldapjs/issues/181)
+ let f = new OrFilter({
+ filters: [
+ new EqualityFilter({
+ attribute: "name",
+ value: username,
+ }),
+ new EqualityFilter({
+ attribute: "username",
+ value: username,
+ }),
+ ],
+ });
+
+ client.search("o=example", { filter: f }, function (err, res) {});
+});
+
+server.listen(389, () => {});
From c55b7bcd8519009dee32b65943705ec0c5562d86 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 30 Sep 2021 13:32:55 +0200
Subject: [PATCH 034/471] model ldap filters as taint steps
---
.../semmle/javascript/frameworks/Ldapjs.qll | 97 +++++++++++--------
.../dataflow/SqlInjectionCustomizations.qll | 27 ++----
.../security/dataflow/SqlInjectionQuery.qll | 7 ++
.../CWE-089/untyped/SqlInjection.expected | 36 +++++--
.../Security/CWE-089/untyped/ldap.js | 9 +-
5 files changed, 104 insertions(+), 72 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Ldapjs.qll b/javascript/ql/lib/semmle/javascript/frameworks/Ldapjs.qll
index afb8649945c..d783ca993dc 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/Ldapjs.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/Ldapjs.qll
@@ -5,38 +5,38 @@
import javascript
module Ldapjs {
- /**
- * Gets a method name on an LDAPjs client that accepts a DN as the first argument.
- */
- private string getLdapjsClientDNMethodName() {
- result = ["add", "bind", "compare", "del", "modify", "modifyDN", "search"]
- }
-
- /**
- * Gets a data flow source node for an LDAP client.
- */
- abstract class LdapClient extends API::Node { }
-
/**
* Gets a data flow source node for the ldapjs library.
*/
- private API::Node ldapjs() { result = API::moduleImport("ldapjs") }
+ private API::Node ldapjs() { result = API::moduleImport("ldapjs") } // TODO: createServer, parseDN.
/**
- * Gets a data flow source node for the ldapjs client.
+ * Gets an LDAP client.
*/
- class LdapjsClient extends LdapClient {
- LdapjsClient() { this = ldapjs().getMember("createClient").getReturn() }
+ private API::Node ldapClient() { result = ldapjs().getMember("createClient").getReturn() }
+
+ /**
+ * A call to the ldapjs Client API methods.
+ */
+ class LdapjsClientAPICall extends API::CallNode {
+ string methodName;
+
+ LdapjsClientAPICall() {
+ methodName = ["add", "bind", "compare", "del", "modify", "modifyDN", "search"] and
+ this = ldapClient().getMember(methodName).getACall()
+ }
+
+ string getMethodName() { result = methodName }
}
/**
* Gets a data flow node for the client `search` options.
*/
class LdapjsSearchOptions extends API::Node {
- API::CallNode queryCall;
+ LdapjsClientAPICall queryCall;
LdapjsSearchOptions() {
- queryCall = any(LdapjsClient client).getMember("search").getACall() and
+ queryCall.getMethodName() = "search" and
this = queryCall.getParameter(1)
}
@@ -46,29 +46,6 @@ module Ldapjs {
API::CallNode getQueryCall() { result = queryCall }
}
- /**
- * A filter used in a `search` operation against the LDAP server.
- */
- class LdapjsSearchFilter extends DataFlow::Node {
- LdapjsSearchOptions options;
-
- LdapjsSearchFilter() { this = options.getMember("filter").getARhs() }
-
- /**
- * Gets the LDAP query call that this filter is used in.
- */
- API::CallNode getQueryCall() { result = options.getQueryCall() }
- }
-
- /**
- * A call to the ldapjs Client API methods.
- */
- class LdapjsClientAPICall extends API::CallNode {
- LdapjsClientAPICall() {
- this = any(LdapjsClient client).getMember(getLdapjsClientDNMethodName()).getACall()
- }
- }
-
/**
* A distinguished name (DN) used in a Client API call against the LDAP server.
*/
@@ -83,10 +60,46 @@ module Ldapjs {
API::CallNode getQueryCall() { result = queryCall }
}
+ /**
+ * A creation of an Ldap filter that doesn't sanitizes the input.
+ */
+ abstract class LdapFilter extends DataFlow::Node {
+ /**
+ * The input that creates (part of) an Ldap filter.
+ */
+ abstract DataFlow::Node getInput();
+
+ /**
+ * The resulting Ldap filter.
+ */
+ abstract DataFlow::Node getOutput();
+ }
+
/**
* Ldapjs parseFilter method call.
*/
- class LdapjsParseFilter extends DataFlow::CallNode {
+ private class LdapjsParseFilter extends LdapFilter, API::CallNode {
LdapjsParseFilter() { this = ldapjs().getMember("parseFilter").getACall() }
+
+ override DataFlow::Node getInput() { result = this.getArgument(0) }
+
+ override DataFlow::Node getOutput() { result = this }
+ }
+
+ /**
+ * A filter used in call to "search" on an LDAP client.
+ * We model that as a step from the ".filter" write to the options object itself.
+ */
+ class LdapjsSearchFilter extends LdapFilter {
+ LdapjsSearchOptions options;
+
+ LdapjsSearchFilter() {
+ options = ldapClient().getMember("search").getACall().getParameter(1) and
+ this = options.getARhs()
+ }
+
+ override DataFlow::Node getInput() { result = options.getMember("filter").getARhs() }
+
+ override DataFlow::Node getOutput() { result = this }
}
}
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll
index 939ad66cd64..6e78c4d6d57 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll
@@ -43,27 +43,14 @@ module SqlInjection {
}
/**
- * An LDAP filter for an API call that executes an operation against the LDAP server.
+ * An LDAPjs sink.
*/
- class LdapjsSearchFilterAsSink extends Sink {
- // TODO: As taint-step?
- /*
- * override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
- * exists(LdapjsParseFilter filter |
- * pred = filter.getArgument(0) and
- * succ = filter
- * )
- * }
- */
-
- LdapjsSearchFilterAsSink() { this instanceof Ldapjs::LdapjsSearchFilter }
- }
-
- /**
- * An LDAP DN argument for an API call that executes an operation against the LDAP server.
- */
- class LdapjsDNArgumentAsSink extends Sink {
- LdapjsDNArgumentAsSink() { this instanceof Ldapjs::LdapjsDNArgument }
+ class LdapJSSink extends Sink {
+ LdapJSSink() {
+ this instanceof Ldapjs::LdapjsDNArgument
+ or
+ this = any(Ldapjs::LdapjsSearchOptions opt).getARhs()
+ }
}
/**
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionQuery.qll
index 0adbf564fb0..c195ef26370 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionQuery.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionQuery.qll
@@ -24,4 +24,11 @@ class Configuration extends TaintTracking::Configuration {
super.isSanitizer(node) or
node instanceof Sanitizer
}
+
+ override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
+ exists(Ldapjs::LdapFilter filter |
+ pred = filter.getInput() and
+ succ = filter.getOutput()
+ )
+ }
}
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 5f93d2fc99d..6b4e5bb275f 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
@@ -77,13 +77,23 @@ nodes
| ldap.js:22:18:22:24 | q.query |
| ldap.js:22:18:22:33 | q.query.username |
| ldap.js:25:13:25:57 | `(\|(nam ... ame}))` |
-| ldap.js:25:13:25:57 | `(\|(nam ... ame}))` |
| ldap.js:25:24:25:31 | username |
| ldap.js:25:46:25:53 | username |
-| ldap.js:32:15:32:59 | `(\|(nam ... ame}))` |
+| ldap.js:28:30:28:34 | opts1 |
+| ldap.js:28:30:28:34 | opts1 |
+| ldap.js:32:5:32:61 | { filte ... e}))` } |
+| ldap.js:32:5:32:61 | { filte ... e}))` } |
| ldap.js:32:15:32:59 | `(\|(nam ... ame}))` |
| ldap.js:32:26:32:33 | username |
| ldap.js:32:48:32:55 | username |
+| ldap.js:63:9:65:3 | parsedFilter |
+| ldap.js:63:24:65:3 | ldap.pa ... ))`\\n ) |
+| ldap.js:64:5:64:49 | `(\|(nam ... ame}))` |
+| ldap.js:64:16:64:23 | username |
+| ldap.js:64:38:64:45 | username |
+| ldap.js:66:30:66:53 | { filte ... ilter } |
+| ldap.js:66:30:66:53 | { filte ... ilter } |
+| ldap.js:66:40:66:51 | parsedFilter |
| marsdb-flow-to.js:10:9:10:18 | query |
| marsdb-flow-to.js:10:17:10:18 | {} |
| marsdb-flow-to.js:11:17:11:24 | req.body |
@@ -468,17 +478,26 @@ edges
| ldap.js:22:7:22:33 | username | ldap.js:25:46:25:53 | username |
| ldap.js:22:7:22:33 | username | ldap.js:32:26:32:33 | username |
| ldap.js:22:7:22:33 | username | ldap.js:32:48:32:55 | username |
+| ldap.js:22:7:22:33 | username | ldap.js:64:16:64:23 | username |
+| ldap.js:22:7:22:33 | username | ldap.js:64:38:64:45 | username |
| ldap.js:22:18:22:18 | q | ldap.js:22:18:22:24 | q.query |
| ldap.js:22:18:22:24 | q.query | ldap.js:22:18:22:33 | q.query.username |
| ldap.js:22:18:22:33 | q.query.username | ldap.js:22:7:22:33 | username |
-| ldap.js:25:24:25:31 | username | ldap.js:25:13:25:57 | `(\|(nam ... ame}))` |
+| ldap.js:25:13:25:57 | `(\|(nam ... ame}))` | ldap.js:28:30:28:34 | opts1 |
+| ldap.js:25:13:25:57 | `(\|(nam ... ame}))` | ldap.js:28:30:28:34 | opts1 |
| ldap.js:25:24:25:31 | username | ldap.js:25:13:25:57 | `(\|(nam ... ame}))` |
| ldap.js:25:46:25:53 | username | ldap.js:25:13:25:57 | `(\|(nam ... ame}))` |
-| ldap.js:25:46:25:53 | username | ldap.js:25:13:25:57 | `(\|(nam ... ame}))` |
-| ldap.js:32:26:32:33 | username | ldap.js:32:15:32:59 | `(\|(nam ... ame}))` |
+| ldap.js:32:15:32:59 | `(\|(nam ... ame}))` | ldap.js:32:5:32:61 | { filte ... e}))` } |
+| ldap.js:32:15:32:59 | `(\|(nam ... ame}))` | ldap.js:32:5:32:61 | { filte ... e}))` } |
| ldap.js:32:26:32:33 | username | ldap.js:32:15:32:59 | `(\|(nam ... ame}))` |
| ldap.js:32:48:32:55 | username | ldap.js:32:15:32:59 | `(\|(nam ... ame}))` |
-| ldap.js:32:48:32:55 | username | ldap.js:32:15:32:59 | `(\|(nam ... ame}))` |
+| ldap.js:63:9:65:3 | parsedFilter | ldap.js:66:40:66:51 | parsedFilter |
+| ldap.js:63:24:65:3 | ldap.pa ... ))`\\n ) | ldap.js:63:9:65:3 | parsedFilter |
+| ldap.js:64:5:64:49 | `(\|(nam ... ame}))` | ldap.js:63:24:65:3 | ldap.pa ... ))`\\n ) |
+| ldap.js:64:16:64:23 | username | ldap.js:64:5:64:49 | `(\|(nam ... ame}))` |
+| ldap.js:64:38:64:45 | username | ldap.js:64:5:64:49 | `(\|(nam ... ame}))` |
+| ldap.js:66:40:66:51 | parsedFilter | ldap.js:66:30:66:53 | { filte ... ilter } |
+| ldap.js:66:40:66:51 | parsedFilter | ldap.js:66:30:66:53 | { filte ... ilter } |
| marsdb-flow-to.js:10:9:10:18 | query | marsdb-flow-to.js:14:17:14:21 | query |
| marsdb-flow-to.js:10:9:10:18 | query | marsdb-flow-to.js:14:17:14:21 | query |
| marsdb-flow-to.js:10:17:10:18 | {} | marsdb-flow-to.js:10:9:10:18 | query |
@@ -887,8 +906,9 @@ edges
| json-schema-validator.js:55:22:55:26 | query | json-schema-validator.js:50:34:50:47 | req.query.data | json-schema-validator.js:55:22:55:26 | query | This query depends on $@. | json-schema-validator.js:50:34:50:47 | req.query.data | a user-provided value |
| json-schema-validator.js:59:22:59:26 | query | json-schema-validator.js:50:34:50:47 | req.query.data | json-schema-validator.js:59:22:59:26 | query | This query depends on $@. | json-schema-validator.js:50:34:50:47 | req.query.data | a user-provided value |
| json-schema-validator.js:61:22:61:26 | query | json-schema-validator.js:50:34:50:47 | req.query.data | json-schema-validator.js:61:22:61:26 | query | This query depends on $@. | json-schema-validator.js:50:34:50:47 | req.query.data | a user-provided value |
-| ldap.js:25:13:25:57 | `(\|(nam ... ame}))` | ldap.js:20:21:20:27 | req.url | ldap.js:25:13:25:57 | `(\|(nam ... ame}))` | This query depends on $@. | ldap.js:20:21:20:27 | req.url | a user-provided value |
-| ldap.js:32:15:32:59 | `(\|(nam ... ame}))` | ldap.js:20:21:20:27 | req.url | ldap.js:32:15:32:59 | `(\|(nam ... ame}))` | This query depends on $@. | ldap.js:20:21:20:27 | req.url | a user-provided value |
+| ldap.js:28:30:28:34 | opts1 | ldap.js:20:21:20:27 | req.url | ldap.js:28:30:28:34 | opts1 | This query depends on $@. | ldap.js:20:21:20:27 | req.url | a user-provided value |
+| ldap.js:32:5:32:61 | { filte ... e}))` } | ldap.js:20:21:20:27 | req.url | ldap.js:32:5:32:61 | { filte ... e}))` } | This query depends on $@. | ldap.js:20:21:20:27 | req.url | a user-provided value |
+| ldap.js:66:30:66:53 | { filte ... ilter } | ldap.js:20:21:20:27 | req.url | ldap.js:66:30:66:53 | { filte ... ilter } | This query depends on $@. | ldap.js:20:21:20:27 | req.url | a user-provided value |
| marsdb-flow-to.js:14:17:14:21 | query | marsdb-flow-to.js:11:17:11:24 | req.body | marsdb-flow-to.js:14:17:14:21 | query | This query depends on $@. | marsdb-flow-to.js:11:17:11:24 | req.body | a user-provided value |
| marsdb.js:16:12:16:16 | query | marsdb.js:13:17:13:24 | req.body | marsdb.js:16:12:16:16 | query | This query depends on $@. | marsdb.js:13:17:13:24 | req.body | a user-provided value |
| minimongo.js:18:12:18:16 | query | minimongo.js:15:17:15:24 | req.body | minimongo.js:18:12:18:16 | query | This query depends on $@. | minimongo.js:15:17:15:24 | req.body | a user-provided value |
diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/ldap.js b/javascript/ql/test/query-tests/Security/CWE-089/untyped/ldap.js
index a4a4691050e..3258d6057d0 100644
--- a/javascript/ql/test/query-tests/Security/CWE-089/untyped/ldap.js
+++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/ldap.js
@@ -22,10 +22,10 @@ const server = http.createServer((req, res) => {
let username = q.query.username;
var opts1 = {
- filter: `(|(name=${username})(username=${username}))`, // NOT OK
+ filter: `(|(name=${username})(username=${username}))`,
};
- client.search("o=example", opts1, function (err, res) {});
+ client.search("o=example", opts1, function (err, res) {}); // NOT OK
client.search(
"o=example",
@@ -59,6 +59,11 @@ const server = http.createServer((req, res) => {
});
client.search("o=example", { filter: f }, function (err, res) {});
+
+ const parsedFilter = ldap.parseFilter(
+ `(|(name=${username})(username=${username}))`
+ );
+ client.search("o=example", { filter: parsedFilter }, function (err, res) {}); // NOT OK
});
server.listen(389, () => {});
From bcf4626fd0b4c9581921d011734036de7371d601 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 30 Sep 2021 13:34:06 +0200
Subject: [PATCH 035/471] remove ldap examples from experimental folder
---
.../Security/CWE-090/examples/example_bad1.js | 21 -------------
.../Security/CWE-090/examples/example_bad2.js | 16 ----------
.../CWE-090/examples/example_good1.js | 31 -------------------
.../CWE-090/examples/example_good2.js | 29 -----------------
4 files changed, 97 deletions(-)
delete mode 100644 javascript/ql/src/experimental/Security/CWE-090/examples/example_bad1.js
delete mode 100644 javascript/ql/src/experimental/Security/CWE-090/examples/example_bad2.js
delete mode 100644 javascript/ql/src/experimental/Security/CWE-090/examples/example_good1.js
delete mode 100644 javascript/ql/src/experimental/Security/CWE-090/examples/example_good2.js
diff --git a/javascript/ql/src/experimental/Security/CWE-090/examples/example_bad1.js b/javascript/ql/src/experimental/Security/CWE-090/examples/example_bad1.js
deleted file mode 100644
index 48263b59d03..00000000000
--- a/javascript/ql/src/experimental/Security/CWE-090/examples/example_bad1.js
+++ /dev/null
@@ -1,21 +0,0 @@
-const http = require('http');
-const url = require('url');
-const ldap = require('ldapjs');
-const client = ldap.createClient({
- url: 'ldap://127.0.0.1:1389'
-});
-
-const server = http.createServer((req, res) => {
- let q = url.parse(req.url, true);
-
- let username = q.query.username;
-
- var opts = {
- // BAD
- filter: `(|(name=${username})(username=${username}))`
- };
-
- client.search('o=example', opts, function (err, res) {
-
- });
-});
diff --git a/javascript/ql/src/experimental/Security/CWE-090/examples/example_bad2.js b/javascript/ql/src/experimental/Security/CWE-090/examples/example_bad2.js
deleted file mode 100644
index 5b89a58bedb..00000000000
--- a/javascript/ql/src/experimental/Security/CWE-090/examples/example_bad2.js
+++ /dev/null
@@ -1,16 +0,0 @@
-const http = require('http');
-const url = require('url');
-const ldap = require('ldapjs');
-const client = ldap.createClient({
- url: 'ldap://127.0.0.1:1389'
-});
-
-const server = http.createServer((req, res) => {
- let q = url.parse(req.url, true);
-
- let username = q.query.username;
-
- // BAD
- client.search('o=example', { filter: `(|(name=${username})(username=${username}))` }, function (err, res) {
- });
-});
diff --git a/javascript/ql/src/experimental/Security/CWE-090/examples/example_good1.js b/javascript/ql/src/experimental/Security/CWE-090/examples/example_good1.js
deleted file mode 100644
index 2028fa58476..00000000000
--- a/javascript/ql/src/experimental/Security/CWE-090/examples/example_good1.js
+++ /dev/null
@@ -1,31 +0,0 @@
-const http = require('http');
-const url = require('url');
-const ldap = require('ldapjs');
-const client = ldap.createClient({
- url: 'ldap://127.0.0.1:1389'
-});
-
-
-// https://github.com/vesse/node-ldapauth-fork/commit/3feea43e243698bcaeffa904a7324f4d96df60e4
-const sanitizeInput = function (input) {
- return input
- .replace(/\*/g, '\\2a')
- .replace(/\(/g, '\\28')
- .replace(/\)/g, '\\29')
- .replace(/\\/g, '\\5c')
- .replace(/\0/g, '\\00')
- .replace(/\//g, '\\2f');
-};
-
-const server = http.createServer((req, res) => {
- let q = url.parse(req.url, true);
-
- let username = q.query.username;
-
- // GOOD
- username = sanitizeInput(username);
-
- client.search('o=example', { filter: `(|(name=${username})(username=${username}))` }, function (err, res) {
- });
-
-});
diff --git a/javascript/ql/src/experimental/Security/CWE-090/examples/example_good2.js b/javascript/ql/src/experimental/Security/CWE-090/examples/example_good2.js
deleted file mode 100644
index a1807b387db..00000000000
--- a/javascript/ql/src/experimental/Security/CWE-090/examples/example_good2.js
+++ /dev/null
@@ -1,29 +0,0 @@
-const http = require('http');
-const url = require('url');
-const ldap = require('ldapjs');
-const client = ldap.createClient({
- url: 'ldap://127.0.0.1:1389'
-});
-
-const server = http.createServer((req, res) => {
- let q = url.parse(req.url, true);
-
- let username = q.query.username;
-
- // GOOD (https://github.com/ldapjs/node-ldapjs/issues/181)
- let f = new OrFilter({
- filters: [
- new EqualityFilter({
- attribute: 'name',
- value: username
- }),
- new EqualityFilter({
- attribute: 'username',
- value: username
- })
- ]
- });
-
- client.search('o=example', { filter: f }, function (err, res) {
- });
-});
From d4de5e32485b40a2873d7e5eff93f4b266026960 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 30 Sep 2021 13:56:41 +0200
Subject: [PATCH 036/471] refactoring and renamings in the ldap model
---
javascript/ql/lib/javascript.qll | 2 +-
.../semmle/javascript/frameworks/LDAPjs.qll | 67 +++++++++++
.../semmle/javascript/frameworks/Ldapjs.qll | 105 ------------------
.../dataflow/SqlInjectionCustomizations.qll | 6 +-
.../security/dataflow/SqlInjectionQuery.qll | 2 +-
5 files changed, 73 insertions(+), 109 deletions(-)
create mode 100644 javascript/ql/lib/semmle/javascript/frameworks/LDAPjs.qll
delete mode 100644 javascript/ql/lib/semmle/javascript/frameworks/Ldapjs.qll
diff --git a/javascript/ql/lib/javascript.qll b/javascript/ql/lib/javascript.qll
index f866be7778c..268920ff782 100644
--- a/javascript/ql/lib/javascript.qll
+++ b/javascript/ql/lib/javascript.qll
@@ -99,7 +99,7 @@ import semmle.javascript.frameworks.History
import semmle.javascript.frameworks.Immutable
import semmle.javascript.frameworks.Knex
import semmle.javascript.frameworks.LazyCache
-import semmle.javascript.frameworks.Ldapjs
+import semmle.javascript.frameworks.LDAPjs
import semmle.javascript.frameworks.LodashUnderscore
import semmle.javascript.frameworks.Logging
import semmle.javascript.frameworks.HttpFrameworks
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/LDAPjs.qll b/javascript/ql/lib/semmle/javascript/frameworks/LDAPjs.qll
new file mode 100644
index 00000000000..b847579e7fa
--- /dev/null
+++ b/javascript/ql/lib/semmle/javascript/frameworks/LDAPjs.qll
@@ -0,0 +1,67 @@
+/**
+ * Provides classes for working with [LDAPjs](https://www.npmjs.com/package/ldapjs)
+ */
+
+import javascript
+
+module LDAPjs {
+ /** Gets a reference to the ldapjs library. */
+ API::Node ldapjs() { result = API::moduleImport("ldapjs") }
+
+ /** Gets an LDAPjs client. */
+ private API::Node ldapClient() { result = ldapjs().getMember("createClient").getReturn() }
+
+ /** A call to a LDAPjs Client API method. */
+ class ClientCall extends API::CallNode {
+ string methodName;
+
+ ClientCall() {
+ methodName = ["add", "bind", "compare", "del", "modify", "modifyDN", "search"] and
+ this = ldapClient().getMember(methodName).getACall()
+ }
+
+ string getMethodName() { result = methodName }
+ }
+
+ /** A reference to a LDAPjs client `search` options. */
+ class SearchOptions extends API::Node {
+ ClientCall call;
+
+ SearchOptions() { call.getMethodName() = "search" and this = call.getParameter(1) }
+ }
+
+ /** A creation of an LDAPjs filter, or object containing a filter, that doesn't sanitizes the input. */
+ abstract class LDAPFilterStep extends DataFlow::Node {
+ /** The input that creates (part of) an LDAPjs filter. */
+ abstract DataFlow::Node getInput();
+
+ /** The resulting LDAPjs filter. */
+ abstract DataFlow::Node getOutput();
+ }
+
+ /** A call to the ldap utility method "parseFilter". */
+ private class ParseFilter extends LDAPFilterStep, API::CallNode {
+ ParseFilter() { this = ldapjs().getMember("parseFilter").getACall() }
+
+ override DataFlow::Node getInput() { result = this.getArgument(0) }
+
+ override DataFlow::Node getOutput() { result = this }
+ }
+
+ /**
+ * A filter used in call to "search" on an LDAPjs client.
+ * We model that as a step from the ".filter" write to the options object itself.
+ */
+ private class SearchFilter extends LDAPFilterStep {
+ SearchOptions options;
+
+ SearchFilter() {
+ options = ldapClient().getMember("search").getACall().getParameter(1) and
+ this = options.getARhs()
+ }
+
+ override DataFlow::Node getInput() { result = options.getMember("filter").getARhs() }
+
+ override DataFlow::Node getOutput() { result = this }
+ }
+}
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Ldapjs.qll b/javascript/ql/lib/semmle/javascript/frameworks/Ldapjs.qll
deleted file mode 100644
index d783ca993dc..00000000000
--- a/javascript/ql/lib/semmle/javascript/frameworks/Ldapjs.qll
+++ /dev/null
@@ -1,105 +0,0 @@
-/**
- * Provides classes for working with [ldapjs](https://github.com/ldapjs/node-ldapjs) (Client only)
- */
-
-import javascript
-
-module Ldapjs {
- /**
- * Gets a data flow source node for the ldapjs library.
- */
- private API::Node ldapjs() { result = API::moduleImport("ldapjs") } // TODO: createServer, parseDN.
-
- /**
- * Gets an LDAP client.
- */
- private API::Node ldapClient() { result = ldapjs().getMember("createClient").getReturn() }
-
- /**
- * A call to the ldapjs Client API methods.
- */
- class LdapjsClientAPICall extends API::CallNode {
- string methodName;
-
- LdapjsClientAPICall() {
- methodName = ["add", "bind", "compare", "del", "modify", "modifyDN", "search"] and
- this = ldapClient().getMember(methodName).getACall()
- }
-
- string getMethodName() { result = methodName }
- }
-
- /**
- * Gets a data flow node for the client `search` options.
- */
- class LdapjsSearchOptions extends API::Node {
- LdapjsClientAPICall queryCall;
-
- LdapjsSearchOptions() {
- queryCall.getMethodName() = "search" and
- this = queryCall.getParameter(1)
- }
-
- /**
- * Gets the LDAP query call that these options are used in.
- */
- API::CallNode getQueryCall() { result = queryCall }
- }
-
- /**
- * A distinguished name (DN) used in a Client API call against the LDAP server.
- */
- class LdapjsDNArgument extends DataFlow::Node {
- LdapjsClientAPICall queryCall;
-
- LdapjsDNArgument() { this = queryCall.getArgument(0) }
-
- /**
- * Gets the LDAP query call that this DN is used in.
- */
- API::CallNode getQueryCall() { result = queryCall }
- }
-
- /**
- * A creation of an Ldap filter that doesn't sanitizes the input.
- */
- abstract class LdapFilter extends DataFlow::Node {
- /**
- * The input that creates (part of) an Ldap filter.
- */
- abstract DataFlow::Node getInput();
-
- /**
- * The resulting Ldap filter.
- */
- abstract DataFlow::Node getOutput();
- }
-
- /**
- * Ldapjs parseFilter method call.
- */
- private class LdapjsParseFilter extends LdapFilter, API::CallNode {
- LdapjsParseFilter() { this = ldapjs().getMember("parseFilter").getACall() }
-
- override DataFlow::Node getInput() { result = this.getArgument(0) }
-
- override DataFlow::Node getOutput() { result = this }
- }
-
- /**
- * A filter used in call to "search" on an LDAP client.
- * We model that as a step from the ".filter" write to the options object itself.
- */
- class LdapjsSearchFilter extends LdapFilter {
- LdapjsSearchOptions options;
-
- LdapjsSearchFilter() {
- options = ldapClient().getMember("search").getACall().getParameter(1) and
- this = options.getARhs()
- }
-
- override DataFlow::Node getInput() { result = options.getMember("filter").getARhs() }
-
- override DataFlow::Node getOutput() { result = this }
- }
-}
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll
index 6e78c4d6d57..c9233f4f7df 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll
@@ -47,9 +47,11 @@ module SqlInjection {
*/
class LdapJSSink extends Sink {
LdapJSSink() {
- this instanceof Ldapjs::LdapjsDNArgument
+ // A distinguished name (DN) used in a call to the client API.
+ this = any(LDAPjs::ClientCall call).getArgument(0)
or
- this = any(Ldapjs::LdapjsSearchOptions opt).getARhs()
+ // A search options object, which contains a filter and a baseDN.
+ this = any(LDAPjs::SearchOptions opt).getARhs()
}
}
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionQuery.qll
index c195ef26370..26fece12073 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionQuery.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionQuery.qll
@@ -26,7 +26,7 @@ class Configuration extends TaintTracking::Configuration {
}
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
- exists(Ldapjs::LdapFilter filter |
+ exists(LDAPjs::LDAPFilterStep filter |
pred = filter.getInput() and
succ = filter.getOutput()
)
From 2062afc8681a150f3b99a0e96d855d0797b01fd7 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 30 Sep 2021 14:04:39 +0200
Subject: [PATCH 037/471] add calls to parseDN as sinks for ldap-injection
---
.../security/dataflow/SqlInjectionCustomizations.qll | 3 +++
.../Security/CWE-089/untyped/SqlInjection.expected | 7 +++++++
.../ql/test/query-tests/Security/CWE-089/untyped/ldap.js | 2 ++
3 files changed, 12 insertions(+)
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll
index c9233f4f7df..852ade18cc5 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll
@@ -52,6 +52,9 @@ module SqlInjection {
or
// A search options object, which contains a filter and a baseDN.
this = any(LDAPjs::SearchOptions opt).getARhs()
+ or
+ // A call to "parseDN", which parses a DN from a string.
+ this = LDAPjs::ldapjs().getMember("parseDN").getACall().getArgument(0)
}
}
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 6b4e5bb275f..4b8c629d9e5 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
@@ -94,6 +94,9 @@ nodes
| ldap.js:66:30:66:53 | { filte ... ilter } |
| ldap.js:66:30:66:53 | { filte ... ilter } |
| ldap.js:66:40:66:51 | parsedFilter |
+| ldap.js:68:27:68:42 | `cn=${username}` |
+| ldap.js:68:27:68:42 | `cn=${username}` |
+| ldap.js:68:33:68:40 | username |
| marsdb-flow-to.js:10:9:10:18 | query |
| marsdb-flow-to.js:10:17:10:18 | {} |
| marsdb-flow-to.js:11:17:11:24 | req.body |
@@ -480,6 +483,7 @@ edges
| ldap.js:22:7:22:33 | username | ldap.js:32:48:32:55 | username |
| ldap.js:22:7:22:33 | username | ldap.js:64:16:64:23 | username |
| ldap.js:22:7:22:33 | username | ldap.js:64:38:64:45 | username |
+| ldap.js:22:7:22:33 | username | ldap.js:68:33:68:40 | username |
| ldap.js:22:18:22:18 | q | ldap.js:22:18:22:24 | q.query |
| ldap.js:22:18:22:24 | q.query | ldap.js:22:18:22:33 | q.query.username |
| ldap.js:22:18:22:33 | q.query.username | ldap.js:22:7:22:33 | username |
@@ -498,6 +502,8 @@ edges
| ldap.js:64:38:64:45 | username | ldap.js:64:5:64:49 | `(\|(nam ... ame}))` |
| ldap.js:66:40:66:51 | parsedFilter | ldap.js:66:30:66:53 | { filte ... ilter } |
| ldap.js:66:40:66:51 | parsedFilter | ldap.js:66:30:66:53 | { filte ... ilter } |
+| ldap.js:68:33:68:40 | username | ldap.js:68:27:68:42 | `cn=${username}` |
+| ldap.js:68:33:68:40 | username | ldap.js:68:27:68:42 | `cn=${username}` |
| marsdb-flow-to.js:10:9:10:18 | query | marsdb-flow-to.js:14:17:14:21 | query |
| marsdb-flow-to.js:10:9:10:18 | query | marsdb-flow-to.js:14:17:14:21 | query |
| marsdb-flow-to.js:10:17:10:18 | {} | marsdb-flow-to.js:10:9:10:18 | query |
@@ -909,6 +915,7 @@ edges
| ldap.js:28:30:28:34 | opts1 | ldap.js:20:21:20:27 | req.url | ldap.js:28:30:28:34 | opts1 | This query depends on $@. | ldap.js:20:21:20:27 | req.url | a user-provided value |
| ldap.js:32:5:32:61 | { filte ... e}))` } | ldap.js:20:21:20:27 | req.url | ldap.js:32:5:32:61 | { filte ... e}))` } | This query depends on $@. | ldap.js:20:21:20:27 | req.url | a user-provided value |
| ldap.js:66:30:66:53 | { filte ... ilter } | ldap.js:20:21:20:27 | req.url | ldap.js:66:30:66:53 | { filte ... ilter } | This query depends on $@. | ldap.js:20:21:20:27 | req.url | a user-provided value |
+| ldap.js:68:27:68:42 | `cn=${username}` | ldap.js:20:21:20:27 | req.url | ldap.js:68:27:68:42 | `cn=${username}` | This query depends on $@. | ldap.js:20:21:20:27 | req.url | a user-provided value |
| marsdb-flow-to.js:14:17:14:21 | query | marsdb-flow-to.js:11:17:11:24 | req.body | marsdb-flow-to.js:14:17:14:21 | query | This query depends on $@. | marsdb-flow-to.js:11:17:11:24 | req.body | a user-provided value |
| marsdb.js:16:12:16:16 | query | marsdb.js:13:17:13:24 | req.body | marsdb.js:16:12:16:16 | query | This query depends on $@. | marsdb.js:13:17:13:24 | req.body | a user-provided value |
| minimongo.js:18:12:18:16 | query | minimongo.js:15:17:15:24 | req.body | minimongo.js:18:12:18:16 | query | This query depends on $@. | minimongo.js:15:17:15:24 | req.body | a user-provided value |
diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/ldap.js b/javascript/ql/test/query-tests/Security/CWE-089/untyped/ldap.js
index 3258d6057d0..ea815dd1eef 100644
--- a/javascript/ql/test/query-tests/Security/CWE-089/untyped/ldap.js
+++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/ldap.js
@@ -64,6 +64,8 @@ const server = http.createServer((req, res) => {
`(|(name=${username})(username=${username}))`
);
client.search("o=example", { filter: parsedFilter }, function (err, res) {}); // NOT OK
+
+ const dn = ldap.parseDN(`cn=${username}`, function (err, dn) {}); // NOT OK
});
server.listen(389, () => {});
From 51b56a9e288825c2256fbccf3fca036e46f03459 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 30 Sep 2021 14:25:19 +0200
Subject: [PATCH 038/471] add cwe 090 (ldap injection) and cwe 943 (Improper
Neutralization of Special Elements in Data Query Logic) to SqlInjection.ql
---
javascript/ql/src/Security/CWE-089/SqlInjection.ql | 2 ++
1 file changed, 2 insertions(+)
diff --git a/javascript/ql/src/Security/CWE-089/SqlInjection.ql b/javascript/ql/src/Security/CWE-089/SqlInjection.ql
index cd68319c429..671b5c8d9db 100644
--- a/javascript/ql/src/Security/CWE-089/SqlInjection.ql
+++ b/javascript/ql/src/Security/CWE-089/SqlInjection.ql
@@ -9,6 +9,8 @@
* @id js/sql-injection
* @tags security
* external/cwe/cwe-089
+ * external/cwe/cwe-090
+ * external/cwe/cwe-943
*/
import javascript
From 6a9277b5cedb17741effde95d3b1e0ef5efe4568 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 30 Sep 2021 14:47:37 +0200
Subject: [PATCH 039/471] recognize string sanitizers for ldap-injection
---
.../dataflow/SqlInjectionCustomizations.qll | 19 +++++++++----------
.../Security/CWE-089/untyped/ldap.js | 2 +-
2 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll
index 852ade18cc5..82e5101bf45 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll
@@ -58,18 +58,17 @@ module SqlInjection {
}
}
+ import semmle.javascript.security.IncompleteBlacklistSanitizer as IncompleteBlacklistSanitizer
+
/**
- * A call to a function whose name suggests that it escapes LDAP search query parameter.
+ * A chain of replace calls that replaces all unsafe chars for ldap injection.
+ * For simplicity it's used as a sanitizer for all of `js/sql-injection`.
*/
- class FilterOrDNSanitizationCall extends Sanitizer, DataFlow::CallNode {
- // TODO: remove, or use something else? (AdhocWhitelistSanitizer?)
- FilterOrDNSanitizationCall() {
- exists(string sanitize, string input |
- sanitize = "(?:escape|saniti[sz]e|validate|filter)" and
- input = "[Ii]nput?"
- |
- this.getCalleeName()
- .regexpMatch("(?i)(" + sanitize + input + ")" + "|(" + input + sanitize + ")")
+ class LDAPStringSanitizer extends Sanitizer,
+ IncompleteBlacklistSanitizer::StringReplaceCallSequence {
+ LDAPStringSanitizer() {
+ forall(string char | char = ["*", "(", ")", "\\", "/"] |
+ this.getAMember().getAReplacedString() = char
)
}
}
diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/ldap.js b/javascript/ql/test/query-tests/Security/CWE-089/untyped/ldap.js
index ea815dd1eef..9502cace21a 100644
--- a/javascript/ql/test/query-tests/Security/CWE-089/untyped/ldap.js
+++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/ldap.js
@@ -36,7 +36,7 @@ const server = http.createServer((req, res) => {
// GOOD
client.search(
"o=example",
- {
+ { // OK
filter: `(|(name=${sanitizeInput(username)})(username=${sanitizeInput(
username
)}))`,
From 694016dcbed52613ee10e0efb9d30765b349222f Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 30 Sep 2021 15:02:35 +0200
Subject: [PATCH 040/471] add missing qldoc
---
javascript/ql/lib/semmle/javascript/frameworks/LDAPjs.qll | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/LDAPjs.qll b/javascript/ql/lib/semmle/javascript/frameworks/LDAPjs.qll
index b847579e7fa..b027d23e020 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/LDAPjs.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/LDAPjs.qll
@@ -4,6 +4,9 @@
import javascript
+/**
+ * A module providing sinks and sanitizers for LDAP injection.
+ */
module LDAPjs {
/** Gets a reference to the ldapjs library. */
API::Node ldapjs() { result = API::moduleImport("ldapjs") }
@@ -20,6 +23,7 @@ module LDAPjs {
this = ldapClient().getMember(methodName).getACall()
}
+ /** Gets the name of the LDAPjs Client API method. */
string getMethodName() { result = methodName }
}
From 5a1eb1995c58c1c975b5b6e1ee679425fa2b9088 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Fri, 1 Oct 2021 11:13:41 +0200
Subject: [PATCH 041/471] add change note
---
javascript/change-notes/2021-10-01-ldap.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 javascript/change-notes/2021-10-01-ldap.md
diff --git a/javascript/change-notes/2021-10-01-ldap.md b/javascript/change-notes/2021-10-01-ldap.md
new file mode 100644
index 00000000000..79ec900b931
--- /dev/null
+++ b/javascript/change-notes/2021-10-01-ldap.md
@@ -0,0 +1,4 @@
+lgtm,codescanning
+* The `js/sql-injection` now recognizes the `ldapjs` library as a sink.
+ Affected packages are
+ [ldapjs](https://www.npmjs.com/package/ldapjs)
\ No newline at end of file
From cf31b6e7f6f6e7bcc9759c59796adef81ca73379 Mon Sep 17 00:00:00 2001
From: Porcuiney Hairs
Date: Sat, 2 Oct 2021 02:10:18 +0530
Subject: [PATCH 042/471] fix testcases
---
csharp/ql/test/experimental/CWE-918/RequestForgery.expected | 1 +
1 file changed, 1 insertion(+)
diff --git a/csharp/ql/test/experimental/CWE-918/RequestForgery.expected b/csharp/ql/test/experimental/CWE-918/RequestForgery.expected
index b7a05ea1a05..a76eec6e39d 100644
--- a/csharp/ql/test/experimental/CWE-918/RequestForgery.expected
+++ b/csharp/ql/test/experimental/CWE-918/RequestForgery.expected
@@ -3,5 +3,6 @@ edges
nodes
| RequestForgery.cs:14:52:14:54 | url : String | semmle.label | url : String |
| RequestForgery.cs:16:66:16:68 | access to parameter url | semmle.label | access to parameter url |
+subpaths
#select
| RequestForgery.cs:16:66:16:68 | access to parameter url | RequestForgery.cs:14:52:14:54 | url : String | RequestForgery.cs:16:66:16:68 | access to parameter url | $@ flows to here and is used in a method of WebClient. | RequestForgery.cs:14:52:14:54 | url | User-provided value |
From 516674697b729827e569bc744cc9cec9eeaad064 Mon Sep 17 00:00:00 2001
From: hubwriter
Date: Tue, 12 Oct 2021 11:30:02 +0100
Subject: [PATCH 043/471] Fix one-word typo
---
docs/codeql/reusables/license-note.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/codeql/reusables/license-note.rst b/docs/codeql/reusables/license-note.rst
index ad440e3bfa6..1f2d4d69401 100644
--- a/docs/codeql/reusables/license-note.rst
+++ b/docs/codeql/reusables/license-note.rst
@@ -4,7 +4,7 @@
`GitHub CodeQL Terms and Conditions `__.
GitHub CodeQL is licensed on a per-user basis. Under the license restrictions,
- you can CodeQL to perform the following tasks:
+ you can use CodeQL to perform the following tasks:
- To perform academic research.
- To demonstrate the software.
From c40b3a953380f696e58ae86ed77a94a84a6f064b Mon Sep 17 00:00:00 2001
From: yoff
Date: Tue, 12 Oct 2021 15:19:07 +0200
Subject: [PATCH 044/471] Update
python/ql/lib/semmle/python/frameworks/Asyncpg.qll
Co-authored-by: Rasmus Wriedt Larsen
---
python/ql/lib/semmle/python/frameworks/Asyncpg.qll | 3 +++
1 file changed, 3 insertions(+)
diff --git a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
index 37cf24d5146..124ca2e128e 100644
--- a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
+++ b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
@@ -69,6 +69,9 @@ private module Asyncpg {
/**
* Holds if `result` is the result of awaiting `awaitedValue`.
+ *
+ * Internal helper predicate to achieve the same as `.awaited()` does for API graphs,
+ * but sutiable for use with type-tracking.
*/
pragma[inline]
DataFlow::Node awaited(DataFlow::Node awaitedValue) {
From e904e7410b9c94feead8ea6156423e75f373a613 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Tue, 12 Oct 2021 15:21:38 +0200
Subject: [PATCH 045/471] Python: Update frameworks.rst
---
docs/codeql/support/reusables/frameworks.rst | 1 +
1 file changed, 1 insertion(+)
diff --git a/docs/codeql/support/reusables/frameworks.rst b/docs/codeql/support/reusables/frameworks.rst
index 0846786db63..0d759030427 100644
--- a/docs/codeql/support/reusables/frameworks.rst
+++ b/docs/codeql/support/reusables/frameworks.rst
@@ -169,6 +169,7 @@ Python built-in support
multidict, Utility library
yarl, Utility library
aioch, Database
+ asyncpg, Database
clickhouse-driver, Database
mysql-connector-python, Database
mysql-connector, Database
From 047aee313c06c19fcf6971139c6e794946f933e0 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 14 Oct 2021 12:34:25 +0200
Subject: [PATCH 046/471] add pragma[noinline] to predicates where the qldoc
mentions join-order
---
.../ql/lib/semmle/javascript/dataflow/AbstractProperties.qll | 1 +
.../ql/lib/semmle/javascript/dataflow/Configuration.qll | 4 ++++
2 files changed, 5 insertions(+)
diff --git a/javascript/ql/lib/semmle/javascript/dataflow/AbstractProperties.qll b/javascript/ql/lib/semmle/javascript/dataflow/AbstractProperties.qll
index 8494f2c100c..51976c7225f 100644
--- a/javascript/ql/lib/semmle/javascript/dataflow/AbstractProperties.qll
+++ b/javascript/ql/lib/semmle/javascript/dataflow/AbstractProperties.qll
@@ -86,6 +86,7 @@ class AbstractProtoProperty extends AbstractProperty {
* has to be toplevel predicate to avoid a spurious type join with `AbstractProperty`,
* which in turn introduces a materialization.
*/
+pragma[noinline]
private AbstractValue getAnAssignedValue(AbstractValue b, string p) {
exists(AnalyzedPropertyWrite apw | apw.writesValue(b, p, result))
}
diff --git a/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll b/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll
index f9a8f45b3a3..8a12bf8d6d7 100644
--- a/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll
+++ b/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll
@@ -493,6 +493,7 @@ private predicate barrierGuardBlocksEdge(
*
* This predicate exists to get a better join-order for the `barrierGuardBlocksEdge` predicate above.
*/
+pragma[noinline]
private BasicBlock getADominatedBasicBlock(BarrierGuardNode guard, ConditionGuardNode cond) {
barrierGuardIsRelevant(guard) and
guard.getEnclosingExpr() = cond.getTest() and
@@ -996,6 +997,7 @@ private predicate exploratoryLoadStep(
*
* This private predicate is only used in `exploratoryLoadStep`, and exists as a separate predicate to give the compiler a hint about join-ordering.
*/
+pragma[noinline]
private string getAForwardRelevantLoadProperty(DataFlow::Configuration cfg) {
exists(DataFlow::Node previous | isRelevantForward(previous, cfg) |
basicStoreStep(previous, _, result) or
@@ -1055,6 +1057,7 @@ private predicate exploratoryBackwardStoreStep(
*
* This private predicate is only used in `exploratoryBackwardStoreStep`, and exists as a separate predicate to give the compiler a hint about join-ordering.
*/
+pragma[noinline]
private string getABackwardsRelevantStoreProperty(DataFlow::Configuration cfg) {
exists(DataFlow::Node mid | isRelevant(mid, cfg) |
basicLoadStep(mid, _, result) or
@@ -1672,6 +1675,7 @@ private predicate onPath(DataFlow::Node nd, DataFlow::Configuration cfg, PathSum
*
* This predicate has been outlined from `onPath` to give the optimizer a hint about join-ordering.
*/
+pragma[noinline]
private predicate onPathStep(
DataFlow::Node nd, DataFlow::Configuration cfg, PathSummary summary, PathSummary stepSummary,
DataFlow::Node mid
From 5cbf632573a82dfb31cdb1cec9a78ae153620606 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Fri, 15 Oct 2021 09:39:04 +0100
Subject: [PATCH 047/471] C++: Inline and simplify 'Assignment to another stack
variable' case in NullTermination.qll.
---
.../code/cpp/commons/NullTermination.qll | 25 ++++++-------------
1 file changed, 7 insertions(+), 18 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll b/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
index 2f811ab83a0..471cb176f95 100644
--- a/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
+++ b/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
@@ -3,17 +3,8 @@ private import semmle.code.cpp.models.interfaces.ArrayFunction
private import semmle.code.cpp.models.implementations.Strcat
import semmle.code.cpp.dataflow.DataFlow
-private predicate mayAddNullTerminatorHelper(Expr e, VariableAccess va, Expr e0) {
- exists(StackVariable v0, Expr val |
- exprDefinition(v0, e, val) and
- val.getAChild*() = va and
- mayAddNullTerminator(e0, v0.getAnAccess())
- )
-}
-
/**
- * Holds if the expression `e` may add a null terminator to the string in
- * variable `v`.
+ * Holds if the expression `e` may add a null terminator to the string in `va`.
*/
predicate mayAddNullTerminator(Expr e, VariableAccess va) {
// Assignment: dereferencing or array access
@@ -30,14 +21,12 @@ predicate mayAddNullTerminator(Expr e, VariableAccess va) {
)
or
// Assignment to another stack variable
- exists(Expr e0, BasicBlock bb, int pos, BasicBlock bb0, int pos0 |
- mayAddNullTerminatorHelper(e, va, e0) and
- bb.getNode(pos) = e and
- bb0.getNode(pos0) = e0
- |
- bb = bb0 and pos < pos0
- or
- bb.getASuccessor+() = bb0
+ exists(Expr e0 |
+ exists(StackVariable v0, Expr val |
+ exprDefinition(v0, e, val) and // e resembles `v0 := val`
+ val.getAChild*() = va and
+ mayAddNullTerminator(e0, v0.getAnAccess())
+ )
)
or
// Assignment to non-stack variable
From 6b0360acca790f01c652d52a6e30f76b11de8880 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Fri, 15 Oct 2021 17:42:16 +0100
Subject: [PATCH 048/471] Revert "C++: Disable the two null termination queries
enabled by 6794."
This reverts commit f38dade578d703a8868062fcff0578505535d77a.
---
.../src/Likely Bugs/Memory Management/ImproperNullTermination.ql | 1 +
.../src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql | 1 +
2 files changed, 2 insertions(+)
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.ql b/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.ql
index ed378dce60a..5c92b0a3db7 100644
--- a/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.ql
+++ b/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.ql
@@ -5,6 +5,7 @@
* @kind problem
* @id cpp/improper-null-termination
* @problem.severity warning
+ * @precision medium
* @security-severity 7.8
* @tags security
* external/cwe/cwe-170
diff --git a/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql b/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql
index b2844c319ba..31ce1037b27 100644
--- a/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql
+++ b/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql
@@ -5,6 +5,7 @@
* @kind problem
* @id cpp/user-controlled-null-termination-tainted
* @problem.severity warning
+ * @precision medium
* @security-severity 10.0
* @tags security
* external/cwe/cwe-170
From d006db9d201c9e5bf52b77a2e58e1b5b3f5bbf1d Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Tue, 27 Jul 2021 12:39:29 +0200
Subject: [PATCH 049/471] First version of the query
---
.../CWE/CWE-940/AndroidIntentRedirect.ql | 23 ++++++++++++++++++
.../security/AndroidIntentRedirectQuery.qll | 24 +++++++++++++++++++
2 files changed, 47 insertions(+)
create mode 100644 java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirect.ql
create mode 100644 java/ql/src/semmle/code/java/security/AndroidIntentRedirectQuery.qll
diff --git a/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirect.ql b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirect.ql
new file mode 100644
index 00000000000..de3597281ad
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirect.ql
@@ -0,0 +1,23 @@
+/**
+ * @name Android Intent redirect
+ * @description xxx
+ * @kind path-problem
+ * @problem.severity error
+ * @security-severity xx
+ * @precision high
+ * @id java/android/unsafe-android-webview-fetch
+ * @tags security
+ * external/cwe/cwe-926
+ * external/cwe/cwe-940
+ */
+
+import java
+import semmle.code.java.dataflow.DataFlow
+import semmle.code.java.security.AndroidIntentRedirectQuery
+import DataFlow::PathGraph
+
+from DataFlow::PathNode source, DataFlow::PathNode sink, IntentRedirectConfiguration conf
+where conf.hasFlowPath(source, sink)
+select sink.getNode(), source, sink,
+ "Arbitrary Android activities or services can be started from $@.", source.getNode(),
+ "this user input"
diff --git a/java/ql/src/semmle/code/java/security/AndroidIntentRedirectQuery.qll b/java/ql/src/semmle/code/java/security/AndroidIntentRedirectQuery.qll
new file mode 100644
index 00000000000..9eb97a8fbf7
--- /dev/null
+++ b/java/ql/src/semmle/code/java/security/AndroidIntentRedirectQuery.qll
@@ -0,0 +1,24 @@
+/** Provides taint tracking configurations to be used in Android Intent Redirect queries. */
+
+import java
+import semmle.code.java.dataflow.FlowSources
+import semmle.code.java.dataflow.TaintTracking
+
+/**
+ * A taint tracking configuration for user-provided Intents being used to start Android components.
+ */
+class IntentRedirectConfiguration extends TaintTracking::Configuration {
+ IntentRedirectConfiguration() { this = "IntentRedirectConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(MethodAccess ma |
+ ma.getMethod() instanceof StartActivityMethod or
+ ma.getMethod() instanceof StartServiceMethod or
+ ma.getMethod() instanceof SendBroadcastMethod
+ |
+ ma.getArgument(0) = sink.asExpr()
+ )
+ }
+}
From 8c400d9b1bd34b501674b15fcf8441e8539f849d Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Tue, 27 Jul 2021 15:05:58 +0200
Subject: [PATCH 050/471] Added tests and stubs
---
.../java/security/AndroidIntentRedirect.qll | 29 +
.../security/AndroidIntentRedirectQuery.qll | 17 +-
.../AndroidIntentRedirectTest.expected | 0
.../CWE-940/AndroidIntentRedirectTest.java | 55 +
.../CWE-940/AndroidIntentRedirectTest.ql | 20 +
.../security/CWE-940/AndroidManifest.xml | 24 +
.../test/query-tests/security/CWE-940/options | 1 +
.../android/annotation/NonNull.java | 19 +
.../annotation/RequiresPermission.java | 42 +
.../android/app/Activity.java | 1567 +++++------------
.../android/app/Fragment.java | 63 +
.../android/content/ComponentName.java | 1 -
.../android/content/ContextWrapper.java | 21 +-
.../android/content/Intent.java | 13 +-
.../android/content/IntentFilter.java | 10 +-
.../android/content/ServiceConnection.java | 1 -
.../android/os/Handler.java | 2 -
.../android/os/UserHandle.java | 3 -
18 files changed, 727 insertions(+), 1161 deletions(-)
create mode 100644 java/ql/src/semmle/code/java/security/AndroidIntentRedirect.qll
create mode 100644 java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectTest.expected
create mode 100644 java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectTest.java
create mode 100644 java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectTest.ql
create mode 100644 java/ql/test/query-tests/security/CWE-940/AndroidManifest.xml
create mode 100644 java/ql/test/query-tests/security/CWE-940/options
create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/annotation/NonNull.java
create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/annotation/RequiresPermission.java
create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/app/Fragment.java
diff --git a/java/ql/src/semmle/code/java/security/AndroidIntentRedirect.qll b/java/ql/src/semmle/code/java/security/AndroidIntentRedirect.qll
new file mode 100644
index 00000000000..0d20a6c7dd8
--- /dev/null
+++ b/java/ql/src/semmle/code/java/security/AndroidIntentRedirect.qll
@@ -0,0 +1,29 @@
+import java
+private import semmle.code.java.dataflow.DataFlow
+private import semmle.code.java.frameworks.android.Intent
+
+abstract class IntentRedirectSink extends DataFlow::Node { }
+
+abstract class IntentRedirectSanitizer extends DataFlow::Node { }
+
+class IntentRedirectAdditionalTaintStep extends Unit {
+ abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
+}
+
+private class DefaultIntentRedirectSink extends IntentRedirectSink {
+ DefaultIntentRedirectSink() {
+ exists(MethodAccess ma, Method m |
+ ma.getMethod() = m and
+ this.asExpr() = ma.getAnArgument() and
+ (
+ this.asExpr().getType() instanceof TypeIntent
+ or
+ this.asExpr().getType().(Array).getComponentType() instanceof TypeIntent
+ )
+ |
+ m instanceof StartActivityMethod or
+ m instanceof StartServiceMethod or
+ m instanceof SendBroadcastMethod
+ )
+ }
+}
diff --git a/java/ql/src/semmle/code/java/security/AndroidIntentRedirectQuery.qll b/java/ql/src/semmle/code/java/security/AndroidIntentRedirectQuery.qll
index 9eb97a8fbf7..0f13955214f 100644
--- a/java/ql/src/semmle/code/java/security/AndroidIntentRedirectQuery.qll
+++ b/java/ql/src/semmle/code/java/security/AndroidIntentRedirectQuery.qll
@@ -3,6 +3,7 @@
import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking
+import semmle.code.java.security.AndroidIntentRedirect
/**
* A taint tracking configuration for user-provided Intents being used to start Android components.
@@ -12,13 +13,13 @@ class IntentRedirectConfiguration extends TaintTracking::Configuration {
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
- override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma |
- ma.getMethod() instanceof StartActivityMethod or
- ma.getMethod() instanceof StartServiceMethod or
- ma.getMethod() instanceof SendBroadcastMethod
- |
- ma.getArgument(0) = sink.asExpr()
- )
+ override predicate isSink(DataFlow::Node sink) { sink instanceof IntentRedirectSink }
+
+ override predicate isSanitizer(DataFlow::Node sanitizer) {
+ sanitizer instanceof IntentRedirectSanitizer
+ }
+
+ override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
+ any(IntentRedirectAdditionalTaintStep c).step(node1, node2)
}
}
diff --git a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectTest.expected b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectTest.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectTest.java b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectTest.java
new file mode 100644
index 00000000000..b1983ffa65a
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectTest.java
@@ -0,0 +1,55 @@
+package com.example.app;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class AndroidIntentRedirectTest extends Activity {
+ AndroidIntentRedirectTest(Context base) {
+ super(base);
+ }
+
+ public void onCreate(Bundle savedInstanceState) {
+ {
+ Intent intent = (Intent) getIntent().getParcelableExtra("forward_intent");
+ startActivities(new Intent[] {intent}); // $ hasAndroidIntentRedirect
+ startActivities(new Intent[] {intent}, null); // $ hasAndroidIntentRedirect
+ startActivity(intent); // $ hasAndroidIntentRedirect
+ startActivity(intent, null); // $ hasAndroidIntentRedirect
+ startActivityAsUser(intent, null); // $ hasAndroidIntentRedirect
+ startActivityAsUser(intent, null, null); // $ hasAndroidIntentRedirect
+ startActivityAsCaller(intent, null, false, 0); // $ hasAndroidIntentRedirect
+ startActivityAsUserFromFragment(null, intent, 0, null, null); // $ hasAndroidIntentRedirect
+ startActivityForResult(intent, 0); // $ hasAndroidIntentRedirect
+ startActivityForResult(intent, 0, null); // $ hasAndroidIntentRedirect
+ startActivityForResult(null, intent, 0, null); // $ hasAndroidIntentRedirect
+ startActivityForResultAsUser(intent, null, 0, null, null); // $ hasAndroidIntentRedirect
+ startActivityForResultAsUser(intent, 0, null, null); // $ hasAndroidIntentRedirect
+ startActivityForResultAsUser(intent, 0, null); // $ hasAndroidIntentRedirect
+ }
+ {
+ Intent intent = (Intent) getIntent().getParcelableExtra("forward_intent");
+ startService(intent); // $ hasAndroidIntentRedirect
+ startServiceAsUser(intent, null); // $ hasAndroidIntentRedirect
+ }
+ {
+ Intent intent = (Intent) getIntent().getParcelableExtra("forward_intent");
+ sendBroadcast(intent); // $ hasAndroidIntentRedirect
+ sendBroadcast(intent, null); // $ hasAndroidIntentRedirect
+ sendBroadcast(intent, null, null); // $ hasAndroidIntentRedirect
+ sendBroadcast(intent, null, 0); // $ hasAndroidIntentRedirect
+ sendBroadcastAsUser(intent, null); // $ hasAndroidIntentRedirect
+ sendBroadcastAsUser(intent, null, null); // $ hasAndroidIntentRedirect
+ sendBroadcastAsUser(intent, null, null, null); // $ hasAndroidIntentRedirect
+ sendBroadcastAsUser(intent, null, null, 0); // $ hasAndroidIntentRedirect
+ sendBroadcastAsUserMultiplePermissions(intent, null, null); // $ hasAndroidIntentRedirect
+ sendStickyBroadcast(intent); // $ hasAndroidIntentRedirect
+ sendStickyBroadcastAsUser(intent, null); // $ hasAndroidIntentRedirect
+ sendStickyBroadcastAsUser(intent, null, null); // $ hasAndroidIntentRedirect
+ sendStickyOrderedBroadcast(intent, null, null, 0, null, null); // $ hasAndroidIntentRedirect
+ sendStickyOrderedBroadcastAsUser(intent, null, null, null, 0, null, null); // $ hasAndroidIntentRedirect
+ }
+
+ }
+}
diff --git a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectTest.ql b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectTest.ql
new file mode 100644
index 00000000000..77e7f56ddf1
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectTest.ql
@@ -0,0 +1,20 @@
+import java
+import semmle.code.java.security.AndroidIntentRedirectQuery
+import TestUtilities.InlineExpectationsTest
+
+class HasAndroidIntentRedirectTest extends InlineExpectationsTest {
+ HasAndroidIntentRedirectTest() { this = "HasAndroidIntentRedirectTest" }
+
+ override string getARelevantTag() { result = "hasAndroidIntentRedirect" }
+
+ override predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "hasAndroidIntentRedirect" and
+ exists(DataFlow::Node src, DataFlow::Node sink, IntentRedirectConfiguration conf |
+ conf.hasFlow(src, sink)
+ |
+ sink.getLocation() = location and
+ element = sink.toString() and
+ value = ""
+ )
+ }
+}
diff --git a/java/ql/test/query-tests/security/CWE-940/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-940/AndroidManifest.xml
new file mode 100644
index 00000000000..ed7de7a7519
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-940/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/java/ql/test/query-tests/security/CWE-940/options b/java/ql/test/query-tests/security/CWE-940/options
new file mode 100644
index 00000000000..f017f81ff2f
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-940/options
@@ -0,0 +1 @@
+// semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/google-android-9.0.0
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/annotation/NonNull.java b/java/ql/test/stubs/google-android-9.0.0/android/annotation/NonNull.java
new file mode 100644
index 00000000000..3e13f4203c0
--- /dev/null
+++ b/java/ql/test/stubs/google-android-9.0.0/android/annotation/NonNull.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.annotation;
+
+public @interface NonNull {
+}
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/annotation/RequiresPermission.java b/java/ql/test/stubs/google-android-9.0.0/android/annotation/RequiresPermission.java
new file mode 100644
index 00000000000..cf034a3fa5b
--- /dev/null
+++ b/java/ql/test/stubs/google-android-9.0.0/android/annotation/RequiresPermission.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.annotation;
+
+import java.lang.annotation.Target;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+
+public @interface RequiresPermission {
+ String value() default "";
+
+ String[] allOf() default {};
+
+ String[] anyOf() default {};
+
+ boolean conditional() default false;
+
+ @Target({FIELD, METHOD, PARAMETER})
+
+ @interface Read {
+ RequiresPermission value() default @RequiresPermission;
+
+ }
+ @Target({FIELD, METHOD, PARAMETER})
+
+ @interface Write {
+ RequiresPermission value() default @RequiresPermission;
+
+ }
+}
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java b/java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java
index 9e31a58a260..ce8bf030aca 100644
--- a/java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java
+++ b/java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java
@@ -1,1150 +1,489 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
*/
+
package android.app;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ContextWrapper;
import android.content.Intent;
-import android.content.ComponentCallbacks2;
+import android.net.Uri;
import android.os.Bundle;
-import android.view.*;
+import android.os.UserHandle;
+import android.view.View;
+
+public class Activity extends ContextWrapper {
+ public Activity(Context base) {
+ super(base);
+ }
-/**
- * An activity is a single, focused thing that the user can do. Almost all
- * activities interact with the user, so the Activity class takes care of
- * creating a window for you in which you can place your UI with
- * {@link #setContentView}. While activities are often presented to the user as
- * full-screen windows, they can also be used in other ways: as floating windows
- * (via a theme with {@link android.R.attr#windowIsFloating} set) or embedded
- * inside of another activity (using {@link ActivityGroup}).
- *
- * There are two methods almost all subclasses of Activity will implement:
- *
- *
- * - {@link #onCreate} is where you initialize your activity. Most
- * importantly, here you will usually call {@link #setContentView(int)} with a
- * layout resource defining your UI, and using {@link #findViewById} to retrieve
- * the widgets in that UI that you need to interact with programmatically.
- *
- *
- {@link #onPause} is where you deal with the user leaving your activity.
- * Most importantly, any changes made by the user should at this point be
- * committed (usually to the {@link android.content.ContentProvider} holding the
- * data).
- *
- *
- *
- * To be of use with {@link android.content.Context#startActivity
- * Context.startActivity()}, all activity classes must have a corresponding
- * {@link android.R.styleable#AndroidManifestActivity <activity>}
- * declaration in their package's AndroidManifest.xml.
- *
- *
- *
- * Topics covered here:
- *
- * - Fragments
- *
- Activity Lifecycle
- *
- Configuration Changes
- *
- Starting Activities and Getting Results
- *
- Saving Persistent State
- *
- Permissions
- *
- Process Lifecycle
- *
- *
- *
- *
Developer Guides
- *
- * The Activity class is an important part of an application's overall
- * lifecycle, and the way activities are launched and put together is a
- * fundamental part of the platform's application model. For a detailed
- * perspective on the structure of an Android application and how activities
- * behave, please read the
- * Application
- * Fundamentals and
- * Tasks and Back
- * Stack developer guides.
- *
- *
- *
- * You can also find a detailed discussion about how to create activities in the
- * Activities developer
- * guide.
- *
- *
- *
- *
- * Fragments
- *
- *
- * The {@link android.support.v4.app.FragmentActivity} subclass can make use of
- * the {@link android.support.v4.app.Fragment} class to better modularize their
- * code, build more sophisticated user interfaces for larger screens, and help
- * scale their application between small and large screens.
- *
- *
- *
- * For more information about using fragments, read the
- * Fragments developer
- * guide.
- *
- *
- *
- * Activity Lifecycle
- *
- *
- * Activities in the system are managed as an activity stack. When a
- * new activity is started, it is placed on the top of the stack and becomes the
- * running activity -- the previous activity always remains below it in the
- * stack, and will not come to the foreground again until the new activity
- * exits.
- *
- *
- *
- * An activity has essentially four states:
- *
- *
- * - If an activity is in the foreground of the screen (at the top of the
- * stack), it is active or running.
- * - If an activity has lost focus but is still visible (that is, a new
- * non-full-sized or transparent activity has focus on top of your activity), it
- * is paused. A paused activity is completely alive (it maintains all
- * state and member information and remains attached to the window manager), but
- * can be killed by the system in extreme low memory situations.
- *
- If an activity is completely obscured by another activity, it is
- * stopped. It still retains all state and member information, however,
- * it is no longer visible to the user so its window is hidden and it will often
- * be killed by the system when memory is needed elsewhere.
- * - If an activity is paused or stopped, the system can drop the activity
- * from memory by either asking it to finish, or simply killing its process.
- * When it is displayed again to the user, it must be completely restarted and
- * restored to its previous state.
- *
- *
- *
- * The following diagram shows the important state paths of an Activity. The
- * square rectangles represent callback methods you can implement to perform
- * operations when the Activity moves between states. The colored ovals are
- * major states the Activity can be in.
- *
- *
- *
- *
- *
- *
- *
- * There are three key loops you may be interested in monitoring within your
- * activity:
- *
- *
- * - The entire lifetime of an activity happens between the first call
- * to {@link android.app.Activity#onCreate} through to a single final call to
- * {@link android.app.Activity#onDestroy}. An activity will do all setup of
- * "global" state in onCreate(), and release all remaining resources in
- * onDestroy(). For example, if it has a thread running in the background to
- * download data from the network, it may create that thread in onCreate() and
- * then stop the thread in onDestroy().
- *
- *
- The visible lifetime of an activity happens between a call to
- * {@link android.app.Activity#onStart} until a corresponding call to
- * {@link android.app.Activity#onStop}. During this time the user can see the
- * activity on-screen, though it may not be in the foreground and interacting
- * with the user. Between these two methods you can maintain resources that are
- * needed to show the activity to the user. For example, you can register a
- * {@link android.content.BroadcastReceiver} in onStart() to monitor for changes
- * that impact your UI, and unregister it in onStop() when the user no longer
- * sees what you are displaying. The onStart() and onStop() methods can be
- * called multiple times, as the activity becomes visible and hidden to the
- * user.
- *
- *
- The foreground lifetime of an activity happens between a call to
- * {@link android.app.Activity#onResume} until a corresponding call to
- * {@link android.app.Activity#onPause}. During this time the activity is in
- * front of all other activities and interacting with the user. An activity can
- * frequently go between the resumed and paused states -- for example when the
- * device goes to sleep, when an activity result is delivered, when a new intent
- * is delivered -- so the code in these methods should be fairly lightweight.
- *
- *
- *
- * The entire lifecycle of an activity is defined by the following Activity
- * methods. All of these are hooks that you can override to do appropriate work
- * when the activity changes state. All activities will implement
- * {@link android.app.Activity#onCreate} to do their initial setup; many will
- * also implement {@link android.app.Activity#onPause} to commit changes to data
- * and otherwise prepare to stop interacting with the user. You should always
- * call up to your superclass when implementing these methods.
- *
- *
- *
- *
- *
- * public class Activity extends ApplicationContext {
- * protected void onCreate(Bundle savedInstanceState);
- *
- * protected void onStart();
- *
- * protected void onRestart();
- *
- * protected void onResume();
- *
- * protected void onPause();
- *
- * protected void onStop();
- *
- * protected void onDestroy();
- * }
- *
- *
- *
- * In general the movement through an activity's lifecycle looks like this:
- *
- *
- *
- *
- *
- *
- *
- *
- * | Method |
- * Description |
- * Killable? |
- * Next |
- *
- *
- *
- *
- *
- * | {@link android.app.Activity#onCreate
- * onCreate()} |
- * Called when the activity is first created. This is where you should do
- * all of your normal static set up: create views, bind data to lists, etc. This
- * method also provides you with a Bundle containing the activity's previously
- * frozen state, if there was one.
- *
- * Always followed by onStart(). |
- * No |
- * onStart() |
- *
- *
- *
- * | |
- * {@link android.app.Activity#onRestart
- * onRestart()} |
- * Called after your activity has been stopped, prior to it being started
- * again.
- *
- * Always followed by onStart() |
- * No |
- * onStart() |
- *
- *
- *
- * | {@link android.app.Activity#onStart
- * onStart()} |
- * Called when the activity is becoming visible to the user.
- *
- * Followed by onResume() if the activity comes to the foreground,
- * or onStop() if it becomes hidden. |
- * No |
- * onResume() or onStop() |
- *
- *
- *
- * | |
- * {@link android.app.Activity#onResume
- * onResume()} |
- * Called when the activity will start interacting with the user. At this
- * point your activity is at the top of the activity stack, with user input
- * going to it.
- *
- * Always followed by onPause(). |
- * No |
- * onPause() |
- *
- *
- *
- * | {@link android.app.Activity#onPause
- * onPause()} |
- * Called when the system is about to start resuming a previous activity.
- * This is typically used to commit unsaved changes to persistent data, stop
- * animations and other things that may be consuming CPU, etc. Implementations
- * of this method must be very quick because the next activity will not be
- * resumed until this method returns.
- *
- * Followed by either onResume() if the activity returns back to
- * the front, or onStop() if it becomes invisible to the user. |
- * Pre-{@link android.os.Build.VERSION_CODES#HONEYCOMB} |
- * onResume() or
- * onStop() |
- *
- *
- *
- * | {@link android.app.Activity#onStop
- * onStop()} |
- * Called when the activity is no longer visible to the user, because
- * another activity has been resumed and is covering this one. This may happen
- * either because a new activity is being started, an existing one is being
- * brought in front of this one, or this one is being destroyed.
- *
- * Followed by either onRestart() if this activity is coming back
- * to interact with the user, or onDestroy() if this activity is
- * going away. |
- * Yes |
- * onRestart() or
- * onDestroy() |
- *
- *
- *
- * | {@link android.app.Activity#onDestroy
- * onDestroy()} |
- * The final call you receive before your activity is destroyed. This can
- * happen either because the activity is finishing (someone called
- * {@link Activity#finish} on it, or because the system is temporarily
- * destroying this instance of the activity to save space. You can distinguish
- * between these two scenarios with the {@link Activity#isFinishing}
- * method. |
- * Yes |
- * nothing |
- *
- *
- *
- *
- *
- * Note the "Killable" column in the above table -- for those methods that are
- * marked as being killable, after that method returns the process hosting the
- * activity may be killed by the system at any time without another
- * line of its code being executed. Because of this, you should use the
- * {@link #onPause} method to write any persistent data (such as user edits) to
- * storage. In addition, the method {@link #onSaveInstanceState(Bundle)} is
- * called before placing the activity in such a background state, allowing you
- * to save away any dynamic instance state in your activity into the given
- * Bundle, to be later received in {@link #onCreate} if the activity needs to be
- * re-created. See the Process Lifecycle section
- * for more information on how the lifecycle of a process is tied to the
- * activities it is hosting. Note that it is important to save persistent data
- * in {@link #onPause} instead of {@link #onSaveInstanceState} because the
- * latter is not part of the lifecycle callbacks, so will not be called in every
- * situation as described in its documentation.
- *
- *
- *
- * Be aware that these semantics will change slightly between applications
- * targeting platforms starting with
- * {@link android.os.Build.VERSION_CODES#HONEYCOMB} vs. those targeting prior
- * platforms. Starting with Honeycomb, an application is not in the killable
- * state until its {@link #onStop} has returned. This impacts when
- * {@link #onSaveInstanceState(Bundle)} may be called (it may be safely called
- * after {@link #onPause()}) and allows an application to safely wait until
- * {@link #onStop()} to save persistent state.
- *
- *
- *
- * For applications targeting platforms starting with
- * {@link android.os.Build.VERSION_CODES#P} {@link #onSaveInstanceState(Bundle)}
- * will always be called after {@link #onStop}, so an application may safely
- * perform fragment transactions in {@link #onStop} and will be able to save
- * persistent state later.
- *
- *
- *
- * For those methods that are not marked as being killable, the activity's
- * process will not be killed by the system starting from the time the method is
- * called and continuing after it returns. Thus an activity is in the killable
- * state, for example, between after onPause() to the start of
- * onResume().
- *
- *
- *
- * Configuration Changes
- *
- *
- * If the configuration of the device (as defined by the {@link Configuration
- * Resources.Configuration} class) changes, then anything displaying a user
- * interface will need to update to match that configuration. Because Activity
- * is the primary mechanism for interacting with the user, it includes special
- * support for handling configuration changes.
- *
- *
- *
- * Unless you specify otherwise, a configuration change (such as a change in
- * screen orientation, language, input devices, etc) will cause your current
- * activity to be destroyed, going through the normal activity
- * lifecycle process of {@link #onPause}, {@link #onStop}, and
- * {@link #onDestroy} as appropriate. If the activity had been in the foreground
- * or visible to the user, once {@link #onDestroy} is called in that instance
- * then a new instance of the activity will be created, with whatever
- * savedInstanceState the previous instance had generated from
- * {@link #onSaveInstanceState}.
- *
- *
- *
- * This is done because any application resource, including layout files, can
- * change based on any configuration value. Thus the only safe way to handle a
- * configuration change is to re-retrieve all resources, including layouts,
- * drawables, and strings. Because activities must already know how to save
- * their state and re-create themselves from that state, this is a convenient
- * way to have an activity restart itself with a new configuration.
- *
- *
- *
- * In some special cases, you may want to bypass restarting of your activity
- * based on one or more types of configuration changes. This is done with the
- * {@link android.R.attr#configChanges android:configChanges} attribute in its
- * manifest. For any types of configuration changes you say that you handle
- * there, you will receive a call to your current activity's
- * {@link #onConfigurationChanged} method instead of being restarted. If a
- * configuration change involves any that you do not handle, however, the
- * activity will still be restarted and {@link #onConfigurationChanged} will not
- * be called.
- *
- *
- *
- * Starting Activities and Getting Results
- *
- *
- * The {@link android.app.Activity#startActivity} method is used to start a new
- * activity, which will be placed at the top of the activity stack. It takes a
- * single argument, an {@link android.content.Intent Intent}, which describes
- * the activity to be executed.
- *
- *
- *
- * Sometimes you want to get a result back from an activity when it ends. For
- * example, you may start an activity that lets the user pick a person in a list
- * of contacts; when it ends, it returns the person that was selected. To do
- * this, you call the
- * {@link android.app.Activity#startActivityForResult(Intent, int)} version with
- * a second integer parameter identifying the call. The result will come back
- * through your {@link android.app.Activity#onActivityResult} method.
- *
- *
- *
- * When an activity exits, it can call
- * {@link android.app.Activity#setResult(int)} to return data back to its
- * parent. It must always supply a result code, which can be the standard
- * results RESULT_CANCELED, RESULT_OK, or any custom values starting at
- * RESULT_FIRST_USER. In addition, it can optionally return back an Intent
- * containing any additional data it wants. All of this information appears back
- * on the parent's Activity.onActivityResult(), along with the
- * integer identifier it originally supplied.
- *
- *
- *
- * If a child activity fails for any reason (such as crashing), the parent
- * activity will receive a result with the code RESULT_CANCELED.
- *
- *
- *
- * public class MyActivity extends Activity {
- * ...
- *
- * static final int PICK_CONTACT_REQUEST = 0;
- *
- * public boolean onKeyDown(int keyCode, KeyEvent event) {
- * if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
- * // When the user center presses, let them pick a contact.
- * startActivityForResult(
- * new Intent(Intent.ACTION_PICK,
- * new Uri("content://contacts")),
- * PICK_CONTACT_REQUEST);
- * return true;
- * }
- * return false;
- * }
- *
- * protected void onActivityResult(int requestCode, int resultCode,
- * Intent data) {
- * if (requestCode == PICK_CONTACT_REQUEST) {
- * if (resultCode == RESULT_OK) {
- * // A contact was picked. Here we will just display it
- * // to the user.
- * startActivity(new Intent(Intent.ACTION_VIEW, data));
- * }
- * }
- * }
- * }
- *
- *
- *
- * Saving Persistent State
- *
- *
- * There are generally two kinds of persistent state than an activity will deal
- * with: shared document-like data (typically stored in a SQLite database using
- * a {@linkplain android.content.ContentProvider content provider}) and internal
- * state such as user preferences.
- *
- *
- *
- * For content provider data, we suggest that activities use a "edit in place"
- * user model. That is, any edits a user makes are effectively made immediately
- * without requiring an additional confirmation step. Supporting this model is
- * generally a simple matter of following two rules:
- *
- *
- *
- * -
- *
- * When creating a new document, the backing database entry or file for it is
- * created immediately. For example, if the user chooses to write a new email, a
- * new entry for that email is created as soon as they start entering data, so
- * that if they go to any other activity after that point this email will now
- * appear in the list of drafts.
- *
- * -
- *
- * When an activity's onPause() method is called, it should commit
- * to the backing content provider or file any changes the user has made. This
- * ensures that those changes will be seen by any other activity that is about
- * to run. You will probably want to commit your data even more aggressively at
- * key times during your activity's lifecycle: for example before starting a new
- * activity, before finishing your own activity, when the user switches between
- * input fields, etc.
- *
- *
- *
- *
- * This model is designed to prevent data loss when a user is navigating between
- * activities, and allows the system to safely kill an activity (because system
- * resources are needed somewhere else) at any time after it has been paused.
- * Note this implies that the user pressing BACK from your activity does
- * not mean "cancel" -- it means to leave the activity with its current
- * contents saved away. Canceling edits in an activity must be provided through
- * some other mechanism, such as an explicit "revert" or "undo" option.
- *
- *
- *
- * See the {@linkplain android.content.ContentProvider content package} for more
- * information about content providers. These are a key aspect of how different
- * activities invoke and propagate data between themselves.
- *
- *
- *
- * The Activity class also provides an API for managing internal persistent
- * state associated with an activity. This can be used, for example, to remember
- * the user's preferred initial display in a calendar (day view or week view) or
- * the user's default home page in a web browser.
- *
- *
- *
- * Activity persistent state is managed with the method {@link #getPreferences},
- * allowing you to retrieve and modify a set of name/value pairs associated with
- * the activity. To use preferences that are shared across multiple application
- * components (activities, receivers, services, providers), you can use the
- * underlying {@link Context#getSharedPreferences
- * Context.getSharedPreferences()} method to retrieve a preferences object
- * stored under a specific name. (Note that it is not possible to share settings
- * data across application packages -- for that you will need a content
- * provider.)
- *
- *
- *
- * Here is an excerpt from a calendar activity that stores the user's preferred
- * view mode in its persistent settings:
- *
- *
- *
- * public class CalendarActivity extends Activity {
- * ...
- *
- * static final int DAY_VIEW_MODE = 0;
- * static final int WEEK_VIEW_MODE = 1;
- *
- * private SharedPreferences mPrefs;
- * private int mCurViewMode;
- *
- * protected void onCreate(Bundle savedInstanceState) {
- * super.onCreate(savedInstanceState);
- *
- * SharedPreferences mPrefs = getSharedPreferences();
- * mCurViewMode = mPrefs.getInt("view_mode", DAY_VIEW_MODE);
- * }
- *
- * protected void onPause() {
- * super.onPause();
- *
- * SharedPreferences.Editor ed = mPrefs.edit();
- * ed.putInt("view_mode", mCurViewMode);
- * ed.commit();
- * }
- * }
- *
- *
- *
- * Permissions
- *
- *
- * The ability to start a particular Activity can be enforced when it is
- * declared in its manifest's {@link android.R.styleable#AndroidManifestActivity
- * <activity>} tag. By doing so, other applications will need to declare a
- * corresponding {@link android.R.styleable#AndroidManifestUsesPermission
- * <uses-permission>} element in their own manifest to be able to start
- * that activity.
- *
- *
- * When starting an Activity you can set
- * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION
- * Intent.FLAG_GRANT_READ_URI_PERMISSION} and/or
- * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION
- * Intent.FLAG_GRANT_WRITE_URI_PERMISSION} on the Intent. This will grant the
- * Activity access to the specific URIs in the Intent. Access will remain until
- * the Activity has finished (it will remain across the hosting process being
- * killed and other temporary destruction). As of
- * {@link android.os.Build.VERSION_CODES#GINGERBREAD}, if the Activity was
- * already created and a new Intent is being delivered to
- * {@link #onNewIntent(Intent)}, any newly granted URI permissions will be added
- * to the existing ones it holds.
- *
- *
- * See the Security and
- * Permissions document for more information on permissions and security in
- * general.
- *
- *
- *
Process Lifecycle
- *
- *
- * The Android system attempts to keep an application process around for as long
- * as possible, but eventually will need to remove old processes when memory
- * runs low. As described in Activity
- * Lifecycle, the decision about which process to remove is intimately tied
- * to the state of the user's interaction with it. In general, there are four
- * states a process can be in based on the activities running in it, listed here
- * in order of importance. The system will kill less important processes (the
- * last ones) before it resorts to killing more important processes (the first
- * ones).
- *
- *
- * -
- *
- * The foreground activity (the activity at the top of the screen that
- * the user is currently interacting with) is considered the most important. Its
- * process will only be killed as a last resort, if it uses more memory than is
- * available on the device. Generally at this point the device has reached a
- * memory paging state, so this is required in order to keep the user interface
- * responsive.
- *
-
- *
- * A visible activity (an activity that is visible to the user but not in
- * the foreground, such as one sitting behind a foreground dialog) is considered
- * extremely important and will not be killed unless that is required to keep
- * the foreground activity running.
- *
-
- *
- * A background activity (an activity that is not visible to the user and
- * has been paused) is no longer critical, so the system may safely kill its
- * process to reclaim memory for other foreground or visible processes. If its
- * process needs to be killed, when the user navigates back to the activity
- * (making it visible on the screen again), its {@link #onCreate} method will be
- * called with the savedInstanceState it had previously supplied in
- * {@link #onSaveInstanceState} so that it can restart itself in the same state
- * as the user last left it.
- *
-
- *
- * An empty process is one hosting no activities or other application
- * components (such as {@link Service} or
- * {@link android.content.BroadcastReceiver} classes). These are killed very
- * quickly by the system as memory becomes low. For this reason, any background
- * operation you do outside of an activity must be executed in the context of an
- * activity BroadcastReceiver or Service to ensure that the system knows it
- * needs to keep your process around.
- *
- *
- *
- * Sometimes an Activity may need to do a long-running operation that exists
- * independently of the activity lifecycle itself. An example may be a camera
- * application that allows you to upload a picture to a web site. The upload may
- * take a long time, and the application should allow the user to leave the
- * application while it is executing. To accomplish this, your Activity should
- * start a {@link Service} in which the upload takes place. This allows the
- * system to properly prioritize your process (considering it to be more
- * important than other non-visible applications) for the duration of the
- * upload, independent of whether the original activity is paused, stopped, or
- * finished.
- */
-public class Activity extends ContextThemeWrapper {
- /** Return the intent that started this activity. */
public Intent getIntent() {
return null;
}
- /**
- * Change the intent returned by {@link #getIntent}. This holds a reference to
- * the given intent; it does not copy it. Often used in conjunction with
- * {@link #onNewIntent}.
- *
- * @param newIntent The new Intent object to return from getIntent
- *
- * @see #getIntent
- * @see #onNewIntent
- */
- public void setIntent(Intent newIntent) {
+ public void setIntent(Intent newIntent) {}
+
+ public final boolean isChild() {
+ return false;
}
- /**
- * Called when the activity is starting. This is where most initialization
- * should go: calling {@link #setContentView(int)} to inflate the activity's UI,
- * using {@link #findViewById} to programmatically interact with widgets in the
- * UI, calling
- * {@link #managedQuery(android.net.Uri , String[], String, String[], String)}
- * to retrieve cursors for data being displayed, etc.
- *
- *
- * You can call {@link #finish} from within this function, in which case
- * onDestroy() will be immediately called after {@link #onCreate} without any of
- * the rest of the activity lifecycle ({@link #onStart}, {@link #onResume},
- * {@link #onPause}, etc) executing.
- *
- *
- * Derived classes must call through to the super class's implementation of
- * this method. If they do not, an exception will be thrown.
- *
- *
- * @param savedInstanceState If the activity is being re-initialized after
- * previously being shut down then this Bundle
- * contains the data it most recently supplied in
- * {@link #onSaveInstanceState}. Note: Otherwise
- * it is null.
- *
- * @see #onStart
- * @see #onSaveInstanceState
- * @see #onRestoreInstanceState
- * @see #onPostCreate
- */
- protected void onCreate(Bundle savedInstanceState) {
- }
-
- /**
- * This method is called after {@link #onStart} when the activity is being
- * re-initialized from a previously saved state, given here in
- * savedInstanceState. Most implementations will simply use
- * {@link #onCreate} to restore their state, but it is sometimes convenient to
- * do it here after all of the initialization has been done or to allow
- * subclasses to decide whether to use your default implementation. The default
- * implementation of this method performs a restore of any view state that had
- * previously been frozen by {@link #onSaveInstanceState}.
- *
- *
- * This method is called between {@link #onStart} and {@link #onPostCreate}.
- *
- * @param savedInstanceState the data most recently supplied in
- * {@link #onSaveInstanceState}.
- *
- * @see #onCreate
- * @see #onPostCreate
- * @see #onResume
- * @see #onSaveInstanceState
- */
- protected void onRestoreInstanceState(Bundle savedInstanceState) {
- }
-
- /**
- * Called when activity start-up is complete (after {@link #onStart} and
- * {@link #onRestoreInstanceState} have been called). Applications will
- * generally not implement this method; it is intended for system classes to do
- * final initialization after application code has run.
- *
- *
- * Derived classes must call through to the super class's implementation of
- * this method. If they do not, an exception will be thrown.
- *
- *
- * @param savedInstanceState If the activity is being re-initialized after
- * previously being shut down then this Bundle
- * contains the data it most recently supplied in
- * {@link #onSaveInstanceState}. Note: Otherwise
- * it is null.
- * @see #onCreate
- */
- protected void onPostCreate(Bundle savedInstanceState) {
- }
-
- /**
- * Called after {@link #onCreate} — or after {@link #onRestart} when the
- * activity had been stopped, but is now again being displayed to the user. It
- * will be followed by {@link #onResume}.
- *
- *
- * Derived classes must call through to the super class's implementation of
- * this method. If they do not, an exception will be thrown.
- *
- *
- * @see #onCreate
- * @see #onStop
- * @see #onResume
- */
- protected void onStart() {
- }
-
- /**
- * Called after {@link #onRestoreInstanceState}, {@link #onRestart}, or
- * {@link #onPause}, for your activity to start interacting with the user. This
- * is a good place to begin animations, open exclusive-access devices (such as
- * the camera), etc.
- *
- *
- * Keep in mind that onResume is not the best indicator that your activity is
- * visible to the user; a system window such as the keyguard may be in front.
- * Use {@link #onWindowFocusChanged} to know for certain that your activity is
- * visible to the user (for example, to resume a game).
- *
- *
- * Derived classes must call through to the super class's implementation of
- * this method. If they do not, an exception will be thrown.
- *
- *
- * @see #onRestoreInstanceState
- * @see #onRestart
- * @see #onPostResume
- * @see #onPause
- */
- protected void onResume() {
- }
-
- /**
- * Called when activity resume is complete (after {@link #onResume} has been
- * called). Applications will generally not implement this method; it is
- * intended for system classes to do final setup after application resume code
- * has run.
- *
- *
- * Derived classes must call through to the super class's implementation of
- * this method. If they do not, an exception will be thrown.
- *
- *
- * @see #onResume
- */
- protected void onPostResume() {
- }
-
- /**
- * This is called for activities that set launchMode to "singleTop" in their
- * package, or if a client used the {@link Intent#FLAG_ACTIVITY_SINGLE_TOP} flag
- * when calling {@link #startActivity}. In either case, when the activity is
- * re-launched while at the top of the activity stack instead of a new instance
- * of the activity being started, onNewIntent() will be called on the existing
- * instance with the Intent that was used to re-launch it.
- *
- *
- * An activity will always be paused before receiving a new intent, so you can
- * count on {@link #onResume} being called after this method.
- *
- *
- * Note that {@link #getIntent} still returns the original Intent. You can use
- * {@link #setIntent} to update it to this new Intent.
- *
- * @param intent The new intent that was started for the activity.
- *
- * @see #getIntent
- * @see #setIntent
- * @see #onResume
- */
- protected void onNewIntent(Intent intent) {
- }
-
- /**
- * Called to retrieve per-instance state from an activity before being killed so
- * that the state can be restored in {@link #onCreate} or
- * {@link #onRestoreInstanceState} (the {@link Bundle} populated by this method
- * will be passed to both).
- *
- *
- * This method is called before an activity may be killed so that when it comes
- * back some time in the future it can restore its state. For example, if
- * activity B is launched in front of activity A, and at some point activity A
- * is killed to reclaim resources, activity A will have a chance to save the
- * current state of its user interface via this method so that when the user
- * returns to activity A, the state of the user interface can be restored via
- * {@link #onCreate} or {@link #onRestoreInstanceState}.
- *
- *
- * Do not confuse this method with activity lifecycle callbacks such as
- * {@link #onPause}, which is always called when an activity is being placed in
- * the background or on its way to destruction, or {@link #onStop} which is
- * called before destruction. One example of when {@link #onPause} and
- * {@link #onStop} is called and not this method is when a user navigates back
- * from activity B to activity A: there is no need to call
- * {@link #onSaveInstanceState} on B because that particular instance will never
- * be restored, so the system avoids calling it. An example when
- * {@link #onPause} is called and not {@link #onSaveInstanceState} is when
- * activity B is launched in front of activity A: the system may avoid calling
- * {@link #onSaveInstanceState} on activity A if it isn't killed during the
- * lifetime of B since the state of the user interface of A will stay intact.
- *
- *
- * The default implementation takes care of most of the UI per-instance state
- * for you by calling {@link android.view.View#onSaveInstanceState()} on each
- * view in the hierarchy that has an id, and by saving the id of the currently
- * focused view (all of which is restored by the default implementation of
- * {@link #onRestoreInstanceState}). If you override this method to save
- * additional information not captured by each individual view, you will likely
- * want to call through to the default implementation, otherwise be prepared to
- * save all of the state of each view yourself.
- *
- *
- * If called, this method will occur after {@link #onStop} for applications
- * targeting platforms starting with {@link android.os.Build.VERSION_CODES#P}.
- * For applications targeting earlier platform versions this method will occur
- * before {@link #onStop} and there are no guarantees about whether it will
- * occur before or after {@link #onPause}.
- *
- * @param outState Bundle in which to place your saved state.
- *
- * @see #onCreate
- * @see #onRestoreInstanceState
- * @see #onPause
- */
- protected void onSaveInstanceState(Bundle outState) {
- }
-
- /**
- * Called as part of the activity lifecycle when an activity is going into the
- * background, but has not (yet) been killed. The counterpart to
- * {@link #onResume}.
- *
- *
- * When activity B is launched in front of activity A, this callback will be
- * invoked on A. B will not be created until A's {@link #onPause} returns, so be
- * sure to not do anything lengthy here.
- *
- *
- * This callback is mostly used for saving any persistent state the activity is
- * editing, to present a "edit in place" model to the user and making sure
- * nothing is lost if there are not enough resources to start the new activity
- * without first killing this one. This is also a good place to do things like
- * stop animations and other things that consume a noticeable amount of CPU in
- * order to make the switch to the next activity as fast as possible, or to
- * close resources that are exclusive access such as the camera.
- *
- *
- * In situations where the system needs more memory it may kill paused processes
- * to reclaim resources. Because of this, you should be sure that all of your
- * state is saved by the time you return from this function. In general
- * {@link #onSaveInstanceState} is used to save per-instance state in the
- * activity and this method is used to store global persistent data (in content
- * providers, files, etc.)
- *
- *
- * After receiving this call you will usually receive a following call to
- * {@link #onStop} (after the next activity has been resumed and displayed),
- * however in some cases there will be a direct call back to {@link #onResume}
- * without going through the stopped state.
- *
- *
- * Derived classes must call through to the super class's implementation of
- * this method. If they do not, an exception will be thrown.
- *
- *
- * @see #onResume
- * @see #onSaveInstanceState
- * @see #onStop
- */
- protected void onPause() {
- }
-
- /**
- * Called when you are no longer visible to the user. You will next receive
- * either {@link #onRestart}, {@link #onDestroy}, or nothing, depending on later
- * user activity.
- *
- *
- * Derived classes must call through to the super class's implementation of
- * this method. If they do not, an exception will be thrown.
- *
- *
- * @see #onRestart
- * @see #onResume
- * @see #onSaveInstanceState
- * @see #onDestroy
- */
- protected void onStop() {
- }
-
- /**
- * Perform any final cleanup before an activity is destroyed. This can happen
- * either because the activity is finishing (someone called {@link #finish} on
- * it, or because the system is temporarily destroying this instance of the
- * activity to save space. You can distinguish between these two scenarios with
- * the {@link #isFinishing} method.
- *
- *
- * Note: do not count on this method being called as a place for saving
- * data! For example, if an activity is editing data in a content provider,
- * those edits should be committed in either {@link #onPause} or
- * {@link #onSaveInstanceState}, not here. This method is usually
- * implemented to free resources like threads that are associated with an
- * activity, so that a destroyed activity does not leave such things around
- * while the rest of its application is still running. There are situations
- * where the system will simply kill the activity's hosting process without
- * calling this method (or any others) in it, so it should not be used to do
- * things that are intended to remain around after the process goes away.
- *
- *
- * Derived classes must call through to the super class's implementation of
- * this method. If they do not, an exception will be thrown.
- *
- *
- * @see #onPause
- * @see #onStop
- * @see #finish
- * @see #isFinishing
- */
- protected void onDestroy() {
- }
-
- /**
- * Finds a view that was identified by the {@code android:id} XML attribute that
- * was processed in {@link #onCreate}.
- *
- * Note: In most cases -- depending on compiler support -- the
- * resulting view is automatically cast to the target class type. If the target
- * class type is unconstrained, an explicit cast may be necessary.
- *
- * @param id the ID to search for
- * @return a view with given ID if found, or {@code null} otherwise
- * @see View#findViewById(int)
- * @see Activity#requireViewById(int)
- */
- public T findViewById(int id) {
+ public final Activity getParent() {
return null;
}
- /**
- * Set the activity content from a layout resource. The resource will be
- * inflated, adding all top-level views to the activity.
- *
- * @param layoutResID Resource ID to be inflated.
- *
- * @see #setContentView(android.view.View)
- * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
- */
- public void setContentView(int layoutResID) {
+ public View getCurrentFocus() {
+ return null;
}
- /**
- * Set the activity content to an explicit view. This view is placed directly
- * into the activity's view hierarchy. It can itself be a complex view
- * hierarchy. When calling this method, the layout parameters of the specified
- * view are ignored. Both the width and the height of the view are set by
- * default to {@link ViewGroup.LayoutParams#MATCH_PARENT}. To use your own
- * layout parameters, invoke
- * {@link #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}
- * instead.
- *
- * @param view The desired content to display.
- *
- * @see #setContentView(int)
- * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
- */
- public void setContentView(View view) {
+ public void onStateNotSaved() {}
+
+ @Override
+ public int getNextAutofillId() {
+ return 0;
}
- /**
- * Same as calling {@link #startActivityForResult(Intent, int, Bundle)} with no
- * options.
- *
- * @param intent The intent to start.
- * @param requestCode If >= 0, this code will be returned in onActivityResult()
- * when the activity exits.
- *
- * @throws android.content.ActivityNotFoundException
- *
- * @see #startActivity
- */
- public void startActivityForResult(Intent intent, int requestCode) {
+ public boolean isVoiceInteraction() {
+ return false;
}
- /**
- * Same as {@link #startActivity(Intent, Bundle)} with no options specified.
- *
- * @param intent The intent to start.
- *
- * @throws android.content.ActivityNotFoundException
- *
- * @see #startActivity(Intent, Bundle)
- * @see #startActivityForResult
- */
- public void startActivity(Intent intent) {
+ public boolean isVoiceInteractionRoot() {
+ return false;
}
- /**
- * Launch a new activity. You will not receive any information about when the
- * activity exits. This implementation overrides the base version, providing
- * information about the activity performing the launch. Because of this
- * additional information, the {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag
- * is not required; if not specified, the new activity will be added to the task
- * of the caller.
- *
- *
- * This method throws {@link android.content.ActivityNotFoundException} if there
- * was no Activity found to run the given Intent.
- *
- * @param intent The intent to start.
- * @param options Additional options for how the Activity should be started. See
- * {@link android.content.Context#startActivity(Intent, Bundle)}
- * Context.startActivity(Intent, Bundle)} for more details.
- *
- * @throws android.content.ActivityNotFoundException
- *
- * @see #startActivity(Intent)
- * @see #startActivityForResult
- */
- public void startActivity(Intent intent, Bundle options) {
+ public boolean isLocalVoiceInteractionSupported() {
+ return false;
}
- /**
- * Same as {@link #startActivities(Intent[], Bundle)} with no options specified.
- *
- * @param intents The intents to start.
- *
- * @throws android.content.ActivityNotFoundException
- *
- * @see #startActivities(Intent[], Bundle)
- * @see #startActivityForResult
- */
- public void startActivities(Intent[] intents) {
+ public void startLocalVoiceInteraction(Bundle privateOptions) {}
+
+ public void onLocalVoiceInteractionStarted() {}
+
+ public void onLocalVoiceInteractionStopped() {}
+
+ public void stopLocalVoiceInteraction() {}
+
+ public CharSequence onCreateDescription() {
+ return null;
}
- /**
- * Launch a new activity. You will not receive any information about when the
- * activity exits. This implementation overrides the base version, providing
- * information about the activity performing the launch. Because of this
- * additional information, the {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag
- * is not required; if not specified, the new activity will be added to the task
- * of the caller.
- *
- *
- * This method throws {@link android.content.ActivityNotFoundException} if there
- * was no Activity found to run the given Intent.
- *
- * @param intents The intents to start.
- * @param options Additional options for how the Activity should be started. See
- * {@link android.content.Context#startActivity(Intent, Bundle)}
- * Context.startActivity(Intent, Bundle)} for more details.
- *
- * @throws android.content.ActivityNotFoundException
- *
- * @see #startActivities(Intent[])
- * @see #startActivityForResult
- */
- public void startActivities(Intent[] intents, Bundle options) {
+ public void onProvideAssistData(Bundle data) {}
+
+ public final void requestShowKeyboardShortcuts() {}
+
+ public final void dismissKeyboardShortcutsHelper() {}
+
+ public boolean showAssist(Bundle args) {
+ return false;
}
- protected void onActivityResult(int requestCode, int resultCode, Intent data) { }
+ public void reportFullyDrawn() {}
- public static final int RESULT_OK = -1;
+ public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {}
+
+ public boolean isInMultiWindowMode() {
+ return false;
+ }
+
+ public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {}
+
+ public boolean isInPictureInPictureMode() {
+ return false;
+ }
+
+ public void enterPictureInPictureMode() {}
+
+ public int getMaxNumPictureInPictureActions() {
+ return 0;
+ }
+
+ public int getChangingConfigurations() {
+ return 0;
+ }
+
+ public Object getLastNonConfigurationInstance() {
+ return null;
+ }
+
+ public Object onRetainNonConfigurationInstance() {
+ return null;
+ }
+
+ public void onLowMemory() {}
+
+ public void onTrimMemory(int level) {}
+
+ public void setPersistent(boolean isPersistent) {}
+
+ public void setContentView(View view) {}
+
+ public void setFinishOnTouchOutside(boolean finish) {}
+
+ public void onBackPressed() {}
+
+ public void onUserInteraction() {}
+
+ public void onContentChanged() {}
+
+ public void onWindowFocusChanged(boolean hasFocus) {}
+
+ public void onAttachedToWindow() {}
+
+ public void onDetachedFromWindow() {}
+
+ public boolean hasWindowFocus() {
+ return false;
+ }
+
+ public View onCreatePanelView(int featureId) {
+ return null;
+ }
+
+ public void invalidateOptionsMenu() {}
+
+ public boolean onNavigateUp() {
+ return false;
+ }
+
+ public boolean onNavigateUpFromChild(Activity child) {
+ return false;
+ }
+
+ public void openOptionsMenu() {}
+
+ public void closeOptionsMenu() {}
+
+ public void registerForContextMenu(View view) {}
+
+ public void unregisterForContextMenu(View view) {}
+
+ public void openContextMenu(View view) {}
+
+ public void closeContextMenu() {}
+
+ public final void showDialog(int id) {}
+
+ public final boolean showDialog(int id, Bundle args) {
+ return false;
+ }
+
+ public final void dismissDialog(int id) {}
+
+ public final void removeDialog(int id) {}
+
+ public boolean onSearchRequested() {
+ return false;
+ }
+
+ public void startSearch(@Nullable String initialQuery, boolean selectInitialQuery,
+ @Nullable Bundle appSearchData, boolean globalSearch) {}
+
+ public void triggerSearch(String query, @Nullable Bundle appSearchData) {}
+
+ public void takeKeyEvents(boolean get) {}
+
+ public final boolean requestWindowFeature(int featureId) {
+ return false;
+ }
+
+ public final void setFeatureDrawableUri(int featureId, Uri uri) {}
+
+ public final void setFeatureDrawableAlpha(int featureId, int alpha) {}
+
+ public final void requestPermissions(@NonNull String[] permissions, int requestCode) {}
+
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults) {}
+
+ public boolean shouldShowRequestPermissionRationale(@NonNull String permission) {
+ return false;
+ }
+
+ public void startActivityForResult(@RequiresPermission Intent intent, int requestCode) {}
+
+ public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
+ @Nullable Bundle options) {}
+
+ public boolean isActivityTransitionRunning() {
+ return false;
+ }
+
+ public void startActivityForResultAsUser(Intent intent, int requestCode, UserHandle user) {}
+
+ public void startActivityForResultAsUser(Intent intent, int requestCode,
+ @Nullable Bundle options, UserHandle user) {}
+
+ public void startActivityForResultAsUser(Intent intent, String resultWho, int requestCode,
+ @Nullable Bundle options, UserHandle user) {}
+
+ public void startActivityAsUser(Intent intent, UserHandle user) {}
+
+ public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {}
+
+ public void startActivityAsCaller(Intent intent, @Nullable Bundle options,
+ boolean ignoreTargetSecurity, int userId) {}
+
+ @Override
+ public void startActivity(Intent intent) {}
+
+ @Override
+ public void startActivity(Intent intent, @Nullable Bundle options) {}
+
+ @Override
+ public void startActivities(Intent[] intents) {}
+
+ @Override
+ public void startActivities(Intent[] intents, @Nullable Bundle options) {}
+
+ public boolean startActivityIfNeeded(@RequiresPermission @NonNull Intent intent,
+ int requestCode) {
+ return false;
+ }
+
+ public boolean startActivityIfNeeded(@RequiresPermission @NonNull Intent intent,
+ int requestCode, @Nullable Bundle options) {
+ return false;
+ }
+
+ public boolean startNextMatchingActivity(@RequiresPermission @NonNull Intent intent) {
+ return false;
+ }
+
+ public boolean startNextMatchingActivity(@RequiresPermission @NonNull Intent intent,
+ @Nullable Bundle options) {
+ return false;
+ }
+
+ public void startActivityFromChild(@NonNull Activity child, @RequiresPermission Intent intent,
+ int requestCode) {}
+
+ public void startActivityFromChild(@NonNull Activity child, @RequiresPermission Intent intent,
+ int requestCode, @Nullable Bundle options) {}
+
+ public void startActivityFromFragment(@NonNull Fragment fragment,
+ @RequiresPermission Intent intent, int requestCode) {}
+
+ public void startActivityFromFragment(@NonNull Fragment fragment,
+ @RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) {}
+
+ public void startActivityAsUserFromFragment(@NonNull Fragment fragment,
+ @RequiresPermission Intent intent, int requestCode, @Nullable Bundle options,
+ UserHandle user) {}
+
+ @Override
+ public void startActivityForResult(String who, Intent intent, int requestCode,
+ @Nullable Bundle options) {}
+
+ @Override
+ public boolean canStartActivityForResult() {
+ return false;
+ }
+
+ public void overridePendingTransition(int enterAnim, int exitAnim) {}
+
+ public final void setResult(int resultCode) {}
+
+ public final void setResult(int resultCode, Intent data) {}
+
+ public Uri getReferrer() {
+ return null;
+ }
+
+ public Uri onProvideReferrer() {
+ return null;
+ }
+
+ public String getCallingPackage() {
+ return null;
+ }
+
+ public ComponentName getCallingActivity() {
+ return null;
+ }
+
+ public void setVisible(boolean visible) {}
+
+ public boolean isFinishing() {
+ return false;
+ }
+
+ public boolean isDestroyed() {
+ return false;
+ }
+
+ public boolean isChangingConfigurations() {
+ return false;
+ }
+
+ public void recreate() {}
+
+ public void finish() {}
+
+ public void finishAffinity() {}
+
+ public void finishFromChild(Activity child) {}
+
+ public void finishAfterTransition() {}
+
+ public void finishActivity(int requestCode) {}
+
+ public void finishActivityFromChild(@NonNull Activity child, int requestCode) {}
+
+ public void finishAndRemoveTask() {}
+
+ public boolean releaseInstance() {
+ return false;
+ }
+
+ public void onActivityReenter(int resultCode, Intent data) {}
+
+ public int getRequestedOrientation() {
+ return 0;
+ }
+
+ public int getTaskId() {
+ return 0;
+ }
+
+ public boolean moveTaskToBack(boolean nonRoot) {
+ return false;
+ }
+
+ public String getLocalClassName() {
+ return null;
+ }
+
+ public ComponentName getComponentName() {
+ return null;
+ }
+
+ @Override
+ public Object getSystemService(@ServiceName @NonNull String name) {
+ return null;
+ }
+
+ public void setTitle(CharSequence title) {}
+
+ public void setTitle(int titleId) {}
+
+ public void setTitleColor(int textColor) {}
+
+ public final CharSequence getTitle() {
+ return null;
+ }
+
+ public final int getTitleColor() {
+ return 0;
+ }
+
+ public final void setProgressBarVisibility(boolean visible) {}
+
+ public final void setProgressBarIndeterminateVisibility(boolean visible) {}
+
+ public final void setProgressBarIndeterminate(boolean indeterminate) {}
+
+ public final void setProgress(int progress) {}
+
+ public final void setSecondaryProgress(int secondaryProgress) {}
+
+ public final void setVolumeControlStream(int streamType) {}
+
+ public final int getVolumeControlStream() {
+ return 0;
+ }
+
+ public final void runOnUiThread(Runnable action) {}
+
+ public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {}
+
+ public boolean isImmersive() {
+ return false;
+ }
+
+ public void convertFromTranslucent() {}
+
+ public boolean requestVisibleBehind(boolean visible) {
+ return false;
+ }
+
+ public void onVisibleBehindCanceled() {}
+
+ public boolean isBackgroundVisibleBehind() {
+ return false;
+ }
+
+ public void onBackgroundVisibleBehindChanged(boolean visible) {}
+
+ public void onEnterAnimationComplete() {}
+
+ public void dispatchEnterAnimationComplete() {}
+
+ public void setImmersive(boolean i) {}
+
+ public boolean shouldUpRecreateTask(Intent targetIntent) {
+ return false;
+ }
+
+ public boolean navigateUpTo(Intent upIntent) {
+ return false;
+ }
+
+ public boolean navigateUpToFromChild(Activity child, Intent upIntent) {
+ return false;
+ }
+
+ public Intent getParentActivityIntent() {
+ return null;
+ }
+
+ public void postponeEnterTransition() {}
+
+ public void startPostponedEnterTransition() {}
+
+ public final boolean isResumed() {
+ return false;
+ }
+
+ public void startLockTask() {}
+
+ public void stopLockTask() {}
+
+ public void showLockTaskEscapeMessage() {}
+
+ public boolean isOverlayWithDecorCaptionEnabled() {
+ return false;
+ }
+
+ public void setOverlayWithDecorCaptionEnabled(boolean enabled) {}
+
+ public interface TranslucentConversionListener {
+ public void onTranslucentConversionComplete(boolean drawComplete);
+
+ }
+
+ public final @Nullable View autofillClientFindViewByAccessibilityIdTraversal(int viewId,
+ int windowId) {
+ return null;
+ }
+
+ public void setDisablePreviewScreenshots(boolean disable) {}
+
+ public void setShowWhenLocked(boolean showWhenLocked) {}
+
+ public void setTurnScreenOn(boolean turnScreenOn) {}
}
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/Fragment.java b/java/ql/test/stubs/google-android-9.0.0/android/app/Fragment.java
new file mode 100644
index 00000000000..18b2a846d05
--- /dev/null
+++ b/java/ql/test/stubs/google-android-9.0.0/android/app/Fragment.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.app;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+
+public class Fragment {
+
+ public static class SavedState implements Parcelable {
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {}
+
+ }
+
+ static public class InstantiationException {
+ public InstantiationException(String msg, Exception cause) {}
+
+ }
+
+ public Fragment() {}
+
+ public static Fragment instantiate(Context context, String fname) {
+ return null;
+ }
+
+ public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) {
+ return null;
+ }
+
+ @Override
+ final public boolean equals(Object o) {
+ return false;
+ }
+
+ @Override
+ final public int hashCode() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return null;
+ }
+
+}
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/ComponentName.java b/java/ql/test/stubs/google-android-9.0.0/android/content/ComponentName.java
index 2c72a0a5125..0af4637f67c 100644
--- a/java/ql/test/stubs/google-android-9.0.0/android/content/ComponentName.java
+++ b/java/ql/test/stubs/google-android-9.0.0/android/content/ComponentName.java
@@ -2,7 +2,6 @@
package android.content;
-import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/ContextWrapper.java b/java/ql/test/stubs/google-android-9.0.0/android/content/ContextWrapper.java
index bffda8f1956..cdb31daf487 100644
--- a/java/ql/test/stubs/google-android-9.0.0/android/content/ContextWrapper.java
+++ b/java/ql/test/stubs/google-android-9.0.0/android/content/ContextWrapper.java
@@ -15,22 +15,15 @@
*/
package android.content;
-import android.content.BroadcastReceiver;
-import android.content.ComponentCallbacks;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.IntentSender;
-import android.content.ServiceConnection;
-import android.content.SharedPreferences;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
-import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.content.res.TypedArray;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
@@ -40,13 +33,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
-import android.util.AttributeSet;
import android.view.Display;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.util.concurrent.Executor;
/**
* Proxying implementation of Context that simply delegates all of its calls to
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/Intent.java b/java/ql/test/stubs/google-android-9.0.0/android/content/Intent.java
index fe8f9f5a942..1800bec6885 100644
--- a/java/ql/test/stubs/google-android-9.0.0/android/content/Intent.java
+++ b/java/ql/test/stubs/google-android-9.0.0/android/content/Intent.java
@@ -2,11 +2,10 @@
package android.content;
-import android.content.ClipData;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.IntentSender;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Set;
+import org.xmlpull.v1.XmlPullParser;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
@@ -16,10 +15,6 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Set;
-import org.xmlpull.v1.XmlPullParser;
public class Intent implements Cloneable, Parcelable
{
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/IntentFilter.java b/java/ql/test/stubs/google-android-9.0.0/android/content/IntentFilter.java
index 99de8481e14..d671c7f75c8 100644
--- a/java/ql/test/stubs/google-android-9.0.0/android/content/IntentFilter.java
+++ b/java/ql/test/stubs/google-android-9.0.0/android/content/IntentFilter.java
@@ -2,18 +2,16 @@
package android.content;
-import android.content.ContentResolver;
-import android.content.Intent;
+import java.util.Iterator;
+import java.util.Set;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PatternMatcher;
import android.util.AndroidException;
import android.util.Printer;
-import java.util.Iterator;
-import java.util.Set;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
public class IntentFilter implements Parcelable
{
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/ServiceConnection.java b/java/ql/test/stubs/google-android-9.0.0/android/content/ServiceConnection.java
index 1b8534539c4..9c385ac3319 100644
--- a/java/ql/test/stubs/google-android-9.0.0/android/content/ServiceConnection.java
+++ b/java/ql/test/stubs/google-android-9.0.0/android/content/ServiceConnection.java
@@ -2,7 +2,6 @@
package android.content;
-import android.content.ComponentName;
import android.os.IBinder;
public interface ServiceConnection
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/Handler.java b/java/ql/test/stubs/google-android-9.0.0/android/os/Handler.java
index 5d1ae91db88..519a8bcc779 100644
--- a/java/ql/test/stubs/google-android-9.0.0/android/os/Handler.java
+++ b/java/ql/test/stubs/google-android-9.0.0/android/os/Handler.java
@@ -2,8 +2,6 @@
package android.os;
-import android.os.Looper;
-import android.os.Message;
import android.util.Printer;
public class Handler
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/UserHandle.java b/java/ql/test/stubs/google-android-9.0.0/android/os/UserHandle.java
index a2e4d596054..1898d42de19 100644
--- a/java/ql/test/stubs/google-android-9.0.0/android/os/UserHandle.java
+++ b/java/ql/test/stubs/google-android-9.0.0/android/os/UserHandle.java
@@ -2,9 +2,6 @@
package android.os;
-import android.os.Parcel;
-import android.os.Parcelable;
-
public class UserHandle implements Parcelable
{
protected UserHandle() {}
From 09d96e65b81a5e386c6cbc14ef449d280d2bfa4e Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Wed, 28 Jul 2021 11:42:50 +0200
Subject: [PATCH 051/471] Added QLDoc
---
.../CWE/CWE-940/AndroidIntentRedirect.qhelp | 18 ++++++++++++++++++
.../java/security/AndroidIntentRedirect.qll | 13 +++++++++++++
2 files changed, 31 insertions(+)
create mode 100644 java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirect.qhelp
diff --git a/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirect.qhelp b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirect.qhelp
new file mode 100644
index 00000000000..bb1975e0842
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirect.qhelp
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/java/ql/src/semmle/code/java/security/AndroidIntentRedirect.qll b/java/ql/src/semmle/code/java/security/AndroidIntentRedirect.qll
index 0d20a6c7dd8..dc2d2c16337 100644
--- a/java/ql/src/semmle/code/java/security/AndroidIntentRedirect.qll
+++ b/java/ql/src/semmle/code/java/security/AndroidIntentRedirect.qll
@@ -1,15 +1,28 @@
+/** Provides classes to reason about Androind Intent redirect vulnerabilities. */
+
import java
private import semmle.code.java.dataflow.DataFlow
private import semmle.code.java.frameworks.android.Intent
+/**
+ * A sink for Intent redirect vulnerabilities in Android,
+ * that is, method calls that start Android components (like activities or services).
+ */
abstract class IntentRedirectSink extends DataFlow::Node { }
+/** A sanitizer for data used to start an Android component. */
abstract class IntentRedirectSanitizer extends DataFlow::Node { }
+/**
+ * A unit class for adding additional taint steps.
+ *
+ * Extend this class to add additional taint steps that should apply to `IntentRedirectConfiguration`.
+ */
class IntentRedirectAdditionalTaintStep extends Unit {
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
}
+/** Default sink for Intent redirect vulnerabilities. */
private class DefaultIntentRedirectSink extends IntentRedirectSink {
DefaultIntentRedirectSink() {
exists(MethodAccess ma, Method m |
From fd8a128693149ed7c01ddfc5cd44a42bd8ce6c61 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Mon, 2 Aug 2021 12:29:52 +0200
Subject: [PATCH 052/471] Renamed to AndroidIntentRedirection
Added qhelp
---
.../CWE/CWE-940/AndroidIntentRedirect.qhelp | 18 ------
.../CWE-940/AndroidIntentRedirection.qhelp | 33 ++++++++++
...edirect.ql => AndroidIntentRedirection.ql} | 14 ++--
.../AndroidIntentRedirectionSample.java | 18 ++++++
...irect.qll => AndroidIntentRedirection.qll} | 16 ++---
....qll => AndroidIntentRedirectionQuery.qll} | 14 ++--
... => AndroidIntentRedirectionTest.expected} | 0
...java => AndroidIntentRedirectionTest.java} | 64 +++++++++----------
...est.ql => AndroidIntentRedirectionTest.ql} | 12 ++--
.../security/CWE-940/AndroidManifest.xml | 2 +-
10 files changed, 113 insertions(+), 78 deletions(-)
delete mode 100644 java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirect.qhelp
create mode 100644 java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp
rename java/ql/src/Security/CWE/CWE-940/{AndroidIntentRedirect.ql => AndroidIntentRedirection.ql} (52%)
create mode 100644 java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirectionSample.java
rename java/ql/src/semmle/code/java/security/{AndroidIntentRedirect.qll => AndroidIntentRedirection.qll} (67%)
rename java/ql/src/semmle/code/java/security/{AndroidIntentRedirectQuery.qll => AndroidIntentRedirectionQuery.qll} (60%)
rename java/ql/test/query-tests/security/CWE-940/{AndroidIntentRedirectTest.expected => AndroidIntentRedirectionTest.expected} (100%)
rename java/ql/test/query-tests/security/CWE-940/{AndroidIntentRedirectTest.java => AndroidIntentRedirectionTest.java} (73%)
rename java/ql/test/query-tests/security/CWE-940/{AndroidIntentRedirectTest.ql => AndroidIntentRedirectionTest.ql} (58%)
diff --git a/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirect.qhelp b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirect.qhelp
deleted file mode 100644
index bb1975e0842..00000000000
--- a/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirect.qhelp
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp
new file mode 100644
index 00000000000..4a32cdb3290
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp
@@ -0,0 +1,33 @@
+
+
+
+ An exported Android component that obtains a user-provided Intent and uses it to launch another component
+ can be exploited to obtain access to private, unexported components of the same app or to launch other apps' components
+ in behalf of the victim app.
+
+
+ Do not export compontents that start other components from a user-provided Intent.
+ They can be made private by setting the `android:exported` property to `false` in the app's Android Manifest.
+ If this is not possible, restrict either which apps can send Intents to the affected component, or which components can be started from it.
+
+
+ The following snippet contains two examples.
+ In the first example, an arbitrary component can be started from the externally provided `forward_intent` Intent.
+ In the second example, the destination component of the Intent is first checked to make sure it is safe.
+
+
+
+
+ Google:
+ Remediation for Intent Redirection Vulnerability.
+
+
+ OWASP Mobile Security Testing Guide:
+ Intents.
+
+
+ Android Developers:
+ The `android:exported` attribute.
+
+
+
\ No newline at end of file
diff --git a/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirect.ql b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.ql
similarity index 52%
rename from java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirect.ql
rename to java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.ql
index de3597281ad..a039cd95bc6 100644
--- a/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirect.ql
+++ b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.ql
@@ -1,11 +1,13 @@
/**
- * @name Android Intent redirect
- * @description xxx
+ * @name Android Intent redirection
+ * @description Starting Android components with user-provided Intents
+ * can provide access to internal components of the application,
+ * increasing the attack surface and potentially causing unintended effects.
* @kind path-problem
* @problem.severity error
- * @security-severity xx
+ * @security-severity 7.5
* @precision high
- * @id java/android/unsafe-android-webview-fetch
+ * @id java/android/intent-redirection
* @tags security
* external/cwe/cwe-926
* external/cwe/cwe-940
@@ -13,10 +15,10 @@
import java
import semmle.code.java.dataflow.DataFlow
-import semmle.code.java.security.AndroidIntentRedirectQuery
+import semmle.code.java.security.AndroidIntentRedirectionQuery
import DataFlow::PathGraph
-from DataFlow::PathNode source, DataFlow::PathNode sink, IntentRedirectConfiguration conf
+from DataFlow::PathNode source, DataFlow::PathNode sink, IntentRedirectionConfiguration conf
where conf.hasFlowPath(source, sink)
select sink.getNode(), source, sink,
"Arbitrary Android activities or services can be started from $@.", source.getNode(),
diff --git a/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirectionSample.java b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirectionSample.java
new file mode 100644
index 00000000000..ee2d72bfec5
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirectionSample.java
@@ -0,0 +1,18 @@
+// BAD: A user-provided Intent is used to launch an arbitrary component
+Intent forwardIntent = (Intent) getIntent().getParcelableExtra("forward_intent");
+startActivity(forwardIntent);
+
+// GOOD: The destination component is checked before launching it
+Intent forwardIntent = (Intent) getIntent().getParcelableExtra("forward_intent");
+ComponentName destinationComponent = forwardIntent.resolveActivity(getPackageManager());
+if (destinationComponent.getPackageName().equals("safe.package") &&
+ destinationComponent.getClassName().equals("SafeClass")) {
+ startActivity(forwardIntent);
+}
+
+// GOOD: The component that sent the Intent is checked before launching the destination component
+Intent forwardIntent = (Intent) getIntent().getParcelableExtra("forward_intent");
+ComponentName originComponent = getCallingActivity();
+if (originComponent.getPackageName().equals("trusted.package") && originComponent.getClassName("TrustedClass")) {
+ startActivity(forwardIntent);
+}
diff --git a/java/ql/src/semmle/code/java/security/AndroidIntentRedirect.qll b/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll
similarity index 67%
rename from java/ql/src/semmle/code/java/security/AndroidIntentRedirect.qll
rename to java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll
index dc2d2c16337..625cc887c5c 100644
--- a/java/ql/src/semmle/code/java/security/AndroidIntentRedirect.qll
+++ b/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll
@@ -5,26 +5,26 @@ private import semmle.code.java.dataflow.DataFlow
private import semmle.code.java.frameworks.android.Intent
/**
- * A sink for Intent redirect vulnerabilities in Android,
+ * A sink for Intent redirection vulnerabilities in Android,
* that is, method calls that start Android components (like activities or services).
*/
-abstract class IntentRedirectSink extends DataFlow::Node { }
+abstract class IntentRedirectionSink extends DataFlow::Node { }
/** A sanitizer for data used to start an Android component. */
-abstract class IntentRedirectSanitizer extends DataFlow::Node { }
+abstract class IntentRedirectionSanitizer extends DataFlow::Node { }
/**
* A unit class for adding additional taint steps.
*
- * Extend this class to add additional taint steps that should apply to `IntentRedirectConfiguration`.
+ * Extend this class to add additional taint steps that should apply to `IntentRedirectionConfiguration`.
*/
-class IntentRedirectAdditionalTaintStep extends Unit {
+class IntentRedirectionAdditionalTaintStep extends Unit {
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
}
-/** Default sink for Intent redirect vulnerabilities. */
-private class DefaultIntentRedirectSink extends IntentRedirectSink {
- DefaultIntentRedirectSink() {
+/** Default sink for Intent redirection vulnerabilities. */
+private class DefaultIntentRedirectionSink extends IntentRedirectionSink {
+ DefaultIntentRedirectionSink() {
exists(MethodAccess ma, Method m |
ma.getMethod() = m and
this.asExpr() = ma.getAnArgument() and
diff --git a/java/ql/src/semmle/code/java/security/AndroidIntentRedirectQuery.qll b/java/ql/src/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
similarity index 60%
rename from java/ql/src/semmle/code/java/security/AndroidIntentRedirectQuery.qll
rename to java/ql/src/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
index 0f13955214f..a14b2a5eb73 100644
--- a/java/ql/src/semmle/code/java/security/AndroidIntentRedirectQuery.qll
+++ b/java/ql/src/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
@@ -1,25 +1,25 @@
-/** Provides taint tracking configurations to be used in Android Intent Redirect queries. */
+/** Provides taint tracking configurations to be used in Android Intent redirection queries. */
import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking
-import semmle.code.java.security.AndroidIntentRedirect
+import semmle.code.java.security.AndroidIntentRedirection
/**
* A taint tracking configuration for user-provided Intents being used to start Android components.
*/
-class IntentRedirectConfiguration extends TaintTracking::Configuration {
- IntentRedirectConfiguration() { this = "IntentRedirectConfiguration" }
+class IntentRedirectionConfiguration extends TaintTracking::Configuration {
+ IntentRedirectionConfiguration() { this = "IntentRedirectionConfiguration" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
- override predicate isSink(DataFlow::Node sink) { sink instanceof IntentRedirectSink }
+ override predicate isSink(DataFlow::Node sink) { sink instanceof IntentRedirectionSink }
override predicate isSanitizer(DataFlow::Node sanitizer) {
- sanitizer instanceof IntentRedirectSanitizer
+ sanitizer instanceof IntentRedirectionSanitizer
}
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
- any(IntentRedirectAdditionalTaintStep c).step(node1, node2)
+ any(IntentRedirectionAdditionalTaintStep c).step(node1, node2)
}
}
diff --git a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectTest.expected b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.expected
similarity index 100%
rename from java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectTest.expected
rename to java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.expected
diff --git a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectTest.java b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
similarity index 73%
rename from java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectTest.java
rename to java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
index b1983ffa65a..2a17f0ea1c3 100644
--- a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectTest.java
+++ b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
@@ -5,50 +5,50 @@ import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
-public class AndroidIntentRedirectTest extends Activity {
- AndroidIntentRedirectTest(Context base) {
+public class AndroidIntentRedirectionTest extends Activity {
+ AndroidIntentRedirectionTest(Context base) {
super(base);
}
public void onCreate(Bundle savedInstanceState) {
{
Intent intent = (Intent) getIntent().getParcelableExtra("forward_intent");
- startActivities(new Intent[] {intent}); // $ hasAndroidIntentRedirect
- startActivities(new Intent[] {intent}, null); // $ hasAndroidIntentRedirect
- startActivity(intent); // $ hasAndroidIntentRedirect
- startActivity(intent, null); // $ hasAndroidIntentRedirect
- startActivityAsUser(intent, null); // $ hasAndroidIntentRedirect
- startActivityAsUser(intent, null, null); // $ hasAndroidIntentRedirect
- startActivityAsCaller(intent, null, false, 0); // $ hasAndroidIntentRedirect
- startActivityAsUserFromFragment(null, intent, 0, null, null); // $ hasAndroidIntentRedirect
- startActivityForResult(intent, 0); // $ hasAndroidIntentRedirect
- startActivityForResult(intent, 0, null); // $ hasAndroidIntentRedirect
- startActivityForResult(null, intent, 0, null); // $ hasAndroidIntentRedirect
- startActivityForResultAsUser(intent, null, 0, null, null); // $ hasAndroidIntentRedirect
- startActivityForResultAsUser(intent, 0, null, null); // $ hasAndroidIntentRedirect
- startActivityForResultAsUser(intent, 0, null); // $ hasAndroidIntentRedirect
+ startActivities(new Intent[] {intent}); // $ hasAndroidIntentRedirection
+ startActivities(new Intent[] {intent}, null); // $ hasAndroidIntentRedirection
+ startActivity(intent); // $ hasAndroidIntentRedirection
+ startActivity(intent, null); // $ hasAndroidIntentRedirection
+ startActivityAsUser(intent, null); // $ hasAndroidIntentRedirection
+ startActivityAsUser(intent, null, null); // $ hasAndroidIntentRedirection
+ startActivityAsCaller(intent, null, false, 0); // $ hasAndroidIntentRedirection
+ startActivityAsUserFromFragment(null, intent, 0, null, null); // $ hasAndroidIntentRedirection
+ startActivityForResult(intent, 0); // $ hasAndroidIntentRedirection
+ startActivityForResult(intent, 0, null); // $ hasAndroidIntentRedirection
+ startActivityForResult(null, intent, 0, null); // $ hasAndroidIntentRedirection
+ startActivityForResultAsUser(intent, null, 0, null, null); // $ hasAndroidIntentRedirection
+ startActivityForResultAsUser(intent, 0, null, null); // $ hasAndroidIntentRedirection
+ startActivityForResultAsUser(intent, 0, null); // $ hasAndroidIntentRedirection
}
{
Intent intent = (Intent) getIntent().getParcelableExtra("forward_intent");
- startService(intent); // $ hasAndroidIntentRedirect
- startServiceAsUser(intent, null); // $ hasAndroidIntentRedirect
+ startService(intent); // $ hasAndroidIntentRedirection
+ startServiceAsUser(intent, null); // $ hasAndroidIntentRedirection
}
{
Intent intent = (Intent) getIntent().getParcelableExtra("forward_intent");
- sendBroadcast(intent); // $ hasAndroidIntentRedirect
- sendBroadcast(intent, null); // $ hasAndroidIntentRedirect
- sendBroadcast(intent, null, null); // $ hasAndroidIntentRedirect
- sendBroadcast(intent, null, 0); // $ hasAndroidIntentRedirect
- sendBroadcastAsUser(intent, null); // $ hasAndroidIntentRedirect
- sendBroadcastAsUser(intent, null, null); // $ hasAndroidIntentRedirect
- sendBroadcastAsUser(intent, null, null, null); // $ hasAndroidIntentRedirect
- sendBroadcastAsUser(intent, null, null, 0); // $ hasAndroidIntentRedirect
- sendBroadcastAsUserMultiplePermissions(intent, null, null); // $ hasAndroidIntentRedirect
- sendStickyBroadcast(intent); // $ hasAndroidIntentRedirect
- sendStickyBroadcastAsUser(intent, null); // $ hasAndroidIntentRedirect
- sendStickyBroadcastAsUser(intent, null, null); // $ hasAndroidIntentRedirect
- sendStickyOrderedBroadcast(intent, null, null, 0, null, null); // $ hasAndroidIntentRedirect
- sendStickyOrderedBroadcastAsUser(intent, null, null, null, 0, null, null); // $ hasAndroidIntentRedirect
+ sendBroadcast(intent); // $ hasAndroidIntentRedirection
+ sendBroadcast(intent, null); // $ hasAndroidIntentRedirection
+ sendBroadcast(intent, null, null); // $ hasAndroidIntentRedirection
+ sendBroadcast(intent, null, 0); // $ hasAndroidIntentRedirection
+ sendBroadcastAsUser(intent, null); // $ hasAndroidIntentRedirection
+ sendBroadcastAsUser(intent, null, null); // $ hasAndroidIntentRedirection
+ sendBroadcastAsUser(intent, null, null, null); // $ hasAndroidIntentRedirection
+ sendBroadcastAsUser(intent, null, null, 0); // $ hasAndroidIntentRedirection
+ sendBroadcastAsUserMultiplePermissions(intent, null, null); // $ hasAndroidIntentRedirection
+ sendStickyBroadcast(intent); // $ hasAndroidIntentRedirection
+ sendStickyBroadcastAsUser(intent, null); // $ hasAndroidIntentRedirection
+ sendStickyBroadcastAsUser(intent, null, null); // $ hasAndroidIntentRedirection
+ sendStickyOrderedBroadcast(intent, null, null, 0, null, null); // $ hasAndroidIntentRedirection
+ sendStickyOrderedBroadcastAsUser(intent, null, null, null, 0, null, null); // $ hasAndroidIntentRedirection
}
}
diff --git a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectTest.ql b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.ql
similarity index 58%
rename from java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectTest.ql
rename to java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.ql
index 77e7f56ddf1..9e883b26498 100644
--- a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectTest.ql
+++ b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.ql
@@ -1,15 +1,15 @@
import java
-import semmle.code.java.security.AndroidIntentRedirectQuery
+import semmle.code.java.security.AndroidIntentRedirectionQuery
import TestUtilities.InlineExpectationsTest
-class HasAndroidIntentRedirectTest extends InlineExpectationsTest {
- HasAndroidIntentRedirectTest() { this = "HasAndroidIntentRedirectTest" }
+class HasAndroidIntentRedirectionTest extends InlineExpectationsTest {
+ HasAndroidIntentRedirectionTest() { this = "HasAndroidIntentRedirectionTest" }
- override string getARelevantTag() { result = "hasAndroidIntentRedirect" }
+ override string getARelevantTag() { result = "hasAndroidIntentRedirection" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
- tag = "hasAndroidIntentRedirect" and
- exists(DataFlow::Node src, DataFlow::Node sink, IntentRedirectConfiguration conf |
+ tag = "hasAndroidIntentRedirection" and
+ exists(DataFlow::Node src, DataFlow::Node sink, IntentRedirectionConfiguration conf |
conf.hasFlow(src, sink)
|
sink.getLocation() = location and
diff --git a/java/ql/test/query-tests/security/CWE-940/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-940/AndroidManifest.xml
index ed7de7a7519..3ee5313936c 100644
--- a/java/ql/test/query-tests/security/CWE-940/AndroidManifest.xml
+++ b/java/ql/test/query-tests/security/CWE-940/AndroidManifest.xml
@@ -9,7 +9,7 @@
android:label="@string/app_name"
android:theme="@style/AppTheme" >
From 7b949e8db2c40fc78fcf979924f83ed43787c025 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Mon, 2 Aug 2021 12:33:53 +0200
Subject: [PATCH 053/471] QLDoc
---
.../semmle/code/java/security/AndroidIntentRedirection.qll | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll b/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll
index 625cc887c5c..71daccd0cdd 100644
--- a/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll
+++ b/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll
@@ -19,6 +19,10 @@ abstract class IntentRedirectionSanitizer extends DataFlow::Node { }
* Extend this class to add additional taint steps that should apply to `IntentRedirectionConfiguration`.
*/
class IntentRedirectionAdditionalTaintStep extends Unit {
+ /**
+ * Holds if the step from `node1` to `node2` should be considered a taint
+ * step for the `IntentRedirectionConfiguration` configuration.
+ */
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
}
From ef30ca211ab842f159f56f48ae6422b9daeacf67 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Mon, 2 Aug 2021 12:43:11 +0200
Subject: [PATCH 054/471] Fix stubs
---
.../CWE-940/AndroidIntentRedirectionTest.java | 2 +-
.../android/content/Context.java | 19 +++++--------------
2 files changed, 6 insertions(+), 15 deletions(-)
diff --git a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
index 2a17f0ea1c3..18d4b3aa235 100644
--- a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
+++ b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
@@ -43,7 +43,7 @@ public class AndroidIntentRedirectionTest extends Activity {
sendBroadcastAsUser(intent, null, null); // $ hasAndroidIntentRedirection
sendBroadcastAsUser(intent, null, null, null); // $ hasAndroidIntentRedirection
sendBroadcastAsUser(intent, null, null, 0); // $ hasAndroidIntentRedirection
- sendBroadcastAsUserMultiplePermissions(intent, null, null); // $ hasAndroidIntentRedirection
+ sendBroadcastWithMultiplePermissions(intent, null); // $ hasAndroidIntentRedirection
sendStickyBroadcast(intent); // $ hasAndroidIntentRedirection
sendStickyBroadcastAsUser(intent, null); // $ hasAndroidIntentRedirection
sendStickyBroadcastAsUser(intent, null, null); // $ hasAndroidIntentRedirection
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java b/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java
index a857f97e20f..b5443430446 100644
--- a/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java
+++ b/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java
@@ -2,15 +2,11 @@
package android.content;
-import android.content.BroadcastReceiver;
-import android.content.ComponentCallbacks;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.IntentSender;
-import android.content.ServiceConnection;
-import android.content.SharedPreferences;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.util.concurrent.Executor;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
@@ -29,11 +25,6 @@ import android.os.Looper;
import android.os.UserHandle;
import android.util.AttributeSet;
import android.view.Display;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.util.concurrent.Executor;
abstract public class Context
{
From 5dfb0d4d64fa0ca6a726e314fb3741da4378d252 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Mon, 2 Aug 2021 17:18:35 +0200
Subject: [PATCH 055/471] Fix Android tests affected by changes in stubs
---
.../library-tests/dataflow/taintsources/IntentSources.java | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/java/ql/test/library-tests/dataflow/taintsources/IntentSources.java b/java/ql/test/library-tests/dataflow/taintsources/IntentSources.java
index 17cc6b6a199..015fdaec74e 100644
--- a/java/ql/test/library-tests/dataflow/taintsources/IntentSources.java
+++ b/java/ql/test/library-tests/dataflow/taintsources/IntentSources.java
@@ -1,11 +1,16 @@
package com.example.myapp;
import android.app.Activity;
+import android.content.Context;
public class IntentSources extends Activity {
private static void sink(Object o) {}
+ public IntentSources(Context base) {
+ super(base);
+ }
+
public void test() throws java.io.IOException {
String trouble = this.getIntent().getStringExtra("key");
From 529a3d9d61072c1edc14d89147849568fd5ab665 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Mon, 2 Aug 2021 17:21:25 +0200
Subject: [PATCH 056/471] Added change note
---
.../change-notes/2021-08-02-android-intent-redirect-query.md | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 java/change-notes/2021-08-02-android-intent-redirect-query.md
diff --git a/java/change-notes/2021-08-02-android-intent-redirect-query.md b/java/change-notes/2021-08-02-android-intent-redirect-query.md
new file mode 100644
index 00000000000..c3af866edcc
--- /dev/null
+++ b/java/change-notes/2021-08-02-android-intent-redirect-query.md
@@ -0,0 +1,5 @@
+lgtm,codescanning
+* A new query "Android Intent redirection" (`java/android/intent-redirection`) has been added.
+This query finds exported Android components using received Intents to start other components,
+which can provide access to internal components of the application or cause other unintended
+effects.
\ No newline at end of file
From 031fa2199ceb3857d69f136c8602e651110f9888 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Tue, 3 Aug 2021 17:44:55 +0200
Subject: [PATCH 057/471] Fix stubs and tests
---
.../CWE-940/AndroidIntentRedirectionTest.java | 15 +-
.../android/app/Activity.java | 12 +-
.../android/content/ContextWrapper.java | 11 +-
.../android/webkit/WebView.java | 698 +++---------------
4 files changed, 149 insertions(+), 587 deletions(-)
diff --git a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
index 18d4b3aa235..d70aedfc2b1 100644
--- a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
+++ b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
@@ -6,9 +6,6 @@ import android.content.Intent;
import android.os.Bundle;
public class AndroidIntentRedirectionTest extends Activity {
- AndroidIntentRedirectionTest(Context base) {
- super(base);
- }
public void onCreate(Bundle savedInstanceState) {
{
@@ -20,11 +17,13 @@ public class AndroidIntentRedirectionTest extends Activity {
startActivityAsUser(intent, null); // $ hasAndroidIntentRedirection
startActivityAsUser(intent, null, null); // $ hasAndroidIntentRedirection
startActivityAsCaller(intent, null, false, 0); // $ hasAndroidIntentRedirection
- startActivityAsUserFromFragment(null, intent, 0, null, null); // $ hasAndroidIntentRedirection
+ startActivityAsUserFromFragment(null, intent, 0, null, null); // $
+ // hasAndroidIntentRedirection
startActivityForResult(intent, 0); // $ hasAndroidIntentRedirection
startActivityForResult(intent, 0, null); // $ hasAndroidIntentRedirection
startActivityForResult(null, intent, 0, null); // $ hasAndroidIntentRedirection
- startActivityForResultAsUser(intent, null, 0, null, null); // $ hasAndroidIntentRedirection
+ startActivityForResultAsUser(intent, null, 0, null, null); // $
+ // hasAndroidIntentRedirection
startActivityForResultAsUser(intent, 0, null, null); // $ hasAndroidIntentRedirection
startActivityForResultAsUser(intent, 0, null); // $ hasAndroidIntentRedirection
}
@@ -47,8 +46,10 @@ public class AndroidIntentRedirectionTest extends Activity {
sendStickyBroadcast(intent); // $ hasAndroidIntentRedirection
sendStickyBroadcastAsUser(intent, null); // $ hasAndroidIntentRedirection
sendStickyBroadcastAsUser(intent, null, null); // $ hasAndroidIntentRedirection
- sendStickyOrderedBroadcast(intent, null, null, 0, null, null); // $ hasAndroidIntentRedirection
- sendStickyOrderedBroadcastAsUser(intent, null, null, null, 0, null, null); // $ hasAndroidIntentRedirection
+ sendStickyOrderedBroadcast(intent, null, null, 0, null, null); // $
+ // hasAndroidIntentRedirection
+ sendStickyOrderedBroadcastAsUser(intent, null, null, null, 0, null, null); // $
+ // hasAndroidIntentRedirection
}
}
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java b/java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java
index ce8bf030aca..a34e7907c38 100644
--- a/java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java
+++ b/java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java
@@ -20,7 +20,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.content.ComponentName;
-import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.net.Uri;
@@ -29,9 +28,8 @@ import android.os.UserHandle;
import android.view.View;
public class Activity extends ContextWrapper {
- public Activity(Context base) {
- super(base);
- }
+
+ public void onCreate(Bundle savedInstanceState) {}
public Intent getIntent() {
return null;
@@ -132,6 +130,8 @@ public class Activity extends ContextWrapper {
public void setContentView(View view) {}
+ public void setContentView(int layoutResID) {}
+
public void setFinishOnTouchOutside(boolean finish) {}
public void onBackPressed() {}
@@ -486,4 +486,8 @@ public class Activity extends ContextWrapper {
public void setShowWhenLocked(boolean showWhenLocked) {}
public void setTurnScreenOn(boolean turnScreenOn) {}
+
+ public T findViewById(int id) {
+ return null;
+ }
}
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/ContextWrapper.java b/java/ql/test/stubs/google-android-9.0.0/android/content/ContextWrapper.java
index cdb31daf487..58b74144752 100644
--- a/java/ql/test/stubs/google-android-9.0.0/android/content/ContextWrapper.java
+++ b/java/ql/test/stubs/google-android-9.0.0/android/content/ContextWrapper.java
@@ -19,6 +19,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
+import java.util.concurrent.Executor;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
@@ -41,7 +42,15 @@ import android.view.Display;
* the original Context.
*/
public class ContextWrapper extends Context {
- public ContextWrapper() {
+ public ContextWrapper() {}
+
+ public Context getBaseContext() {
+ return null;
+ }
+
+ @Override
+ public Executor getMainExecutor() {
+ return null;
}
public ContextWrapper(Context base) {
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebView.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebView.java
index 1b377ad3e17..d9625b70771 100644
--- a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebView.java
+++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebView.java
@@ -1,602 +1,150 @@
/*
- * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (C) 2008 The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
*/
+
package android.webkit;
import android.content.Context;
-import android.content.Intent;
+import android.view.View;
-import java.io.File;
-import java.util.List;
-import java.util.Map;
-
-/**
- *
- * A View that displays web pages. This class is the basis upon which you can
- * roll your own web browser or simply display some online content within your
- * Activity. It uses the WebKit rendering engine to display web pages and
- * includes methods to navigate forward and backward through a history, zoom in
- * and out, perform text searches and more.
- *
- *
- * Note that, in order for your Activity to access the Internet and load web
- * pages in a WebView, you must add the {@code INTERNET} permissions to your
- * Android Manifest file:
- *
- *
- * {@code }
- *
- *
- *
- * This must be a child of the {@code }
- * element.
- *
- *
- * For more information, read
- * Building Web Apps in
- * WebView.
- *
- *
Basic usage
- *
- *
- * By default, a WebView provides no browser-like widgets, does not enable
- * JavaScript and web page errors are ignored. If your goal is only to display
- * some HTML as a part of your UI, this is probably fine; the user won't need to
- * interact with the web page beyond reading it, and the web page won't need to
- * interact with the user. If you actually want a full-blown web browser, then
- * you probably want to invoke the Browser application with a URL Intent rather
- * than show it with a WebView. For example:
- *
- *
- * Uri uri = Uri.parse("https://www.example.com");
- * Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- * startActivity(intent);
- *
- *
- * See {@link android.content.Intent} for more information.
- *
- *
- * To provide a WebView in your own Activity, include a {@code } in
- * your layout, or set the entire Activity window as a WebView during
- * {@link android.app.Activity#onCreate(Bundle) onCreate()}:
- *
- *
- * WebView webview = new WebView(this);
- * setContentView(webview);
- *
- *
- *
- * Then load the desired web page:
- *
- *
- * // Simplest usage: note that an exception will NOT be thrown
- * // if there is an error loading this page (see below).
- * webview.loadUrl("https://example.com/");
- *
- * // OR, you can also load from an HTML string:
- * String summary = "<html><body>You scored <b>192</b> points.</body></html>";
- * webview.loadData(summary, "text/html", null);
- * // ... although note that there are restrictions on what this HTML can do.
- * // See {@link #loadData(String,String,String)} and {@link
- * #loadDataWithBaseURL(String,String,String,String,String)} for more info.
- * // Also see {@link #loadData(String,String,String)} for information on encoding special
- * // characters.
- *
- *
- *
- * A WebView has several customization points where you can add your own
- * behavior. These are:
- *
- *
- * - Creating and setting a {@link android.webkit.WebChromeClient} subclass.
- * This class is called when something that might impact a browser UI happens,
- * for instance, progress updates and JavaScript alerts are sent here (see
- * Debugging
- * Tasks).
- * - Creating and setting a {@link android.webkit.WebViewClient} subclass. It
- * will be called when things happen that impact the rendering of the content,
- * eg, errors or form submissions. You can also intercept URL loading here (via
- * {@link android.webkit.WebViewClient#shouldOverrideUrlLoading(WebView,String)
- * shouldOverrideUrlLoading()}).
- * - Modifying the {@link android.webkit.WebSettings}, such as enabling
- * JavaScript with
- * {@link android.webkit.WebSettings#setJavaScriptEnabled(boolean)
- * setJavaScriptEnabled()}.
- * - Injecting Java objects into the WebView using the
- * {@link android.webkit.WebView#addJavascriptInterface} method. This method
- * allows you to inject Java objects into a page's JavaScript context, so that
- * they can be accessed by JavaScript in the page.
- *
- *
- *
- * Here's a more complicated example, showing error handling, settings, and
- * progress notification:
- *
- *
- * // Let's display the progress in the activity title bar, like the
- * // browser app does.
- * getWindow().requestFeature(Window.FEATURE_PROGRESS);
- *
- * webview.getSettings().setJavaScriptEnabled(true);
- *
- * final Activity activity = this;
- * webview.setWebChromeClient(new WebChromeClient() {
- * public void onProgressChanged(WebView view, int progress) {
- * // Activities and WebViews measure progress with different scales.
- * // The progress meter will automatically disappear when we reach 100%
- * activity.setProgress(progress * 1000);
- * }
- * });
- * webview.setWebViewClient(new WebViewClient() {
- * public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
- * Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();
- * }
- * });
- *
- * webview.loadUrl("https://developer.android.com/");
- *
- *
- * Zoom
- *
- *
- * To enable the built-in zoom, set {@link #getSettings()
- * WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)} (introduced
- * in API level {@link android.os.Build.VERSION_CODES#CUPCAKE}).
- *
- *
- * Note: Using zoom if either the height or width is set to
- * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} may lead to
- * undefined behavior and should be avoided.
- *
- *
Cookie and window management
- *
- *
- * For obvious security reasons, your application has its own cache, cookie
- * store etc.—it does not share the Browser application's data.
- *
- *
- * By default, requests by the HTML to open new windows are ignored. This is
- * {@code true} whether they be opened by JavaScript or by the target attribute
- * on a link. You can customize your {@link WebChromeClient} to provide your own
- * behavior for opening multiple windows, and render them in whatever manner you
- * want.
- *
- *
- * The standard behavior for an Activity is to be destroyed and recreated when
- * the device orientation or any other configuration changes. This will cause
- * the WebView to reload the current page. If you don't want that, you can set
- * your Activity to handle the {@code orientation} and {@code keyboardHidden}
- * changes, and then just leave the WebView alone. It'll automatically re-orient
- * itself as appropriate. Read
- * Handling
- * Runtime Changes for more information about how to handle configuration
- * changes during runtime.
- *
- *
- *
Building web pages to support different screen densities
- *
- *
- * The screen density of a device is based on the screen resolution. A screen
- * with low density has fewer available pixels per inch, where a screen with
- * high density has more — sometimes significantly more — pixels per
- * inch. The density of a screen is important because, other things being equal,
- * a UI element (such as a button) whose height and width are defined in terms
- * of screen pixels will appear larger on the lower density screen and smaller
- * on the higher density screen. For simplicity, Android collapses all actual
- * screen densities into three generalized densities: high, medium, and low.
- *
- * By default, WebView scales a web page so that it is drawn at a size that
- * matches the default appearance on a medium density screen. So, it applies
- * 1.5x scaling on a high density screen (because its pixels are smaller) and
- * 0.75x scaling on a low density screen (because its pixels are bigger).
- * Starting with API level {@link android.os.Build.VERSION_CODES#ECLAIR},
- * WebView supports DOM, CSS, and meta tag features to help you (as a web
- * developer) target screens with different screen densities.
- *
- * Here's a summary of the features you can use to handle different screen
- * densities:
- *
- * - The {@code window.devicePixelRatio} DOM property. The value of this
- * property specifies the default scaling factor used for the current device.
- * For example, if the value of {@code
- * window.devicePixelRatio} is "1.0", then the device is considered a medium
- * density (mdpi) device and default scaling is not applied to the web page; if
- * the value is "1.5", then the device is considered a high density device
- * (hdpi) and the page content is scaled 1.5x; if the value is "0.75", then the
- * device is considered a low density device (ldpi) and the content is scaled
- * 0.75x.
- * - The {@code -webkit-device-pixel-ratio} CSS media query. Use this to
- * specify the screen densities for which this style sheet is to be used. The
- * corresponding value should be either "0.75", "1", or "1.5", to indicate that
- * the styles are for devices with low density, medium density, or high density
- * screens, respectively. For example:
- *
- *
- * <link rel="stylesheet" media="screen and (-webkit-device-pixel-ratio:1.5)" href="hdpi.css" />
- *
- *
- * The {@code hdpi.css} stylesheet is only used for devices with a screen pixel
- * ratio of 1.5, which is the high density pixel ratio.
- *
- *
- * HTML5 Video support
- *
- *
- * In order to support inline HTML5 video in your application you need to have
- * hardware acceleration turned on.
- *
- *
Full screen support
- *
- *
- * In order to support full screen — for video or other HTML content
- * — you need to set a {@link android.webkit.WebChromeClient} and
- * implement both
- * {@link WebChromeClient#onShowCustomView(View, WebChromeClient.CustomViewCallback)}
- * and {@link WebChromeClient#onHideCustomView()}. If the implementation of
- * either of these two methods is missing then the web contents will not be
- * allowed to enter full screen. Optionally you can implement
- * {@link WebChromeClient#getVideoLoadingProgressView()} to customize the View
- * displayed whilst a video is loading.
- *
- *
HTML5 Geolocation API support
- *
- *
- * For applications targeting Android N and later releases (API level >
- * {@link android.os.Build.VERSION_CODES#M}) the geolocation api is only
- * supported on secure origins such as https. For such applications requests to
- * geolocation api on non-secure origins are automatically denied without
- * invoking the corresponding
- * {@link WebChromeClient#onGeolocationPermissionsShowPrompt(String, GeolocationPermissions.Callback)}
- * method.
- *
- *
Layout size
- *
- * It is recommended to set the WebView layout height to a fixed value or to
- * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT} instead of using
- * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}. When using
- * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT} for the height none
- * of the WebView's parents should use a
- * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} layout height since
- * that could result in incorrect sizing of the views.
- *
- *
- * Setting the WebView's height to
- * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} enables the
- * following behaviors:
- *
- * - The HTML body layout height is set to a fixed value. This means that
- * elements with a height relative to the HTML body may not be sized correctly.
- *
- * - For applications targeting {@link android.os.Build.VERSION_CODES#KITKAT}
- * and earlier SDKs the HTML viewport meta tag will be ignored in order to
- * preserve backwards compatibility.
- *
- *
- *
- * Using a layout width of
- * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} is not supported. If
- * such a width is used the WebView will attempt to use the width of the parent
- * instead.
- *
- *
Metrics
- *
- *
- * WebView may upload anonymous diagnostic data to Google when the user has
- * consented. This data helps Google improve WebView. Data is collected on a
- * per-app basis for each app which has instantiated a WebView. An individual
- * app can opt out of this feature by putting the following tag in its
- * manifest's {@code } element:
- *
- *
- * <manifest>
- * <application>
- * ...
- * <meta-data android:name="android.webkit.WebView.MetricsOptOut"
- * android:value="true" />
- * </application>
- * </manifest>
- *
- *
- * Data will only be uploaded for a given app if the user has consented AND the
- * app has not opted out.
- *
- *
Safe Browsing
- *
- *
- * With Safe Browsing, WebView will block malicious URLs and present a warning
- * UI to the user to allow them to navigate back safely or proceed to the
- * malicious page.
- *
- * Safe Browsing is enabled by default on devices which support it. If your app
- * needs to disable Safe Browsing for all WebViews, it can do so in the
- * manifest's {@code } element:
- *
- *
- *
- * <manifest>
- * <application>
- * ...
- * <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
- * android:value="false" />
- * </application>
- * </manifest>
- *
- *
- *
- * Otherwise, see {@link WebSettings#setSafeBrowsingEnabled}.
- *
- */
-// Implementation notes.
-// The WebView is a thin API class that delegates its public API to a backend
-// WebViewProvider
-// class instance. WebView extends {@link AbsoluteLayout} for backward
-// compatibility reasons.
-// Methods are delegated to the provider implementation: all public API methods
-// introduced in this
-// file are fully delegated, whereas public and protected methods from the View
-// base classes are
-// only delegated where a specific need exists for them to do so.
-public class WebView {
-
- /**
- * Constructs a new WebView with an Activity Context object.
- *
- *
- * Note: WebView should always be instantiated with an Activity Context.
- * If instantiated with an Application Context, WebView will be unable to
- * provide several features, such as JavaScript dialogs and autofill.
- *
- * @param context an Activity Context to access application assets
- */
+public class WebView extends View {
public WebView(Context context) {
+ super(context);
}
- /**
- * Loads the given URL with the specified additional HTTP headers.
- *
- * Also see compatibility note on {@link #evaluateJavascript}.
- *
- * @param url the URL of the resource to load
- * @param additionalHttpHeaders the additional headers to be used in the HTTP
- * request for this URL, specified as a map from
- * name to value. Note that if this map contains
- * any of the headers that are set by default by
- * this WebView, such as those controlling caching,
- * accept types or the User-Agent, their values may
- * be overridden by this WebView's defaults.
- */
- public void loadUrl(String url, Map additionalHttpHeaders) {
+ public void setHorizontalScrollbarOverlay(boolean overlay) {}
+
+ public void setVerticalScrollbarOverlay(boolean overlay) {}
+
+ public boolean overlayHorizontalScrollbar() {
+ return false;
}
- /**
- * Loads the given URL.
- *
- * Also see compatibility note on {@link #evaluateJavascript}.
- *
- * @param url the URL of the resource to load
- */
- public void loadUrl(String url) {
+ public boolean overlayVerticalScrollbar() {
+ return false;
}
- /**
- * Loads the URL with postData using "POST" method into this WebView. If url is
- * not a network URL, it will be loaded with {@link #loadUrl(String)} instead,
- * ignoring the postData param.
- *
- * @param url the URL of the resource to load
- * @param postData the data will be passed to "POST" request, which must be be
- * "application/x-www-form-urlencoded" encoded.
- */
- public void postUrl(String url, byte[] postData) {
- }
+ public void savePassword(String host, String username, String password) {}
- /**
- * Loads the given data into this WebView using a 'data' scheme URL.
- *
- * Note that JavaScript's same origin policy means that script running in a page
- * loaded using this method will be unable to access content loaded using any
- * scheme other than 'data', including 'http(s)'. To avoid this restriction, use
- * {@link #loadDataWithBaseURL(String,String,String,String,String)
- * loadDataWithBaseURL()} with an appropriate base URL.
- *
- * The {@code encoding} parameter specifies whether the data is base64 or URL
- * encoded. If the data is base64 encoded, the value of the encoding parameter
- * must be 'base64'. HTML can be encoded with
- * {@link android.util.Base64#encodeToString(byte[],int)} like so:
- *
- *
- * String unencodedHtml = "<html><body>'%28' is the code for '('</body></html>";
- * String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(), Base64.NO_PADDING);
- * webView.loadData(encodedHtml, "text/html", "base64");
- *
- *
- * For all other values of {@code encoding} (including {@code null}) it is
- * assumed that the data uses ASCII encoding for octets inside the range of safe
- * URL characters and use the standard %xx hex encoding of URLs for octets
- * outside that range. See
- * RFC 3986 for
- * more information.
- *
- * The {@code mimeType} parameter specifies the format of the data. If WebView
- * can't handle the specified MIME type, it will download the data. If
- * {@code null}, defaults to 'text/html'.
- *
- * The 'data' scheme URL formed by this method uses the default US-ASCII
- * charset. If you need to set a different charset, you should form a 'data'
- * scheme URL which explicitly specifies a charset parameter in the mediatype
- * portion of the URL and call {@link #loadUrl(String)} instead. Note that the
- * charset obtained from the mediatype portion of a data URL always overrides
- * that specified in the HTML or XML document itself.
- *
- * Content loaded using this method will have a {@code window.origin} value of
- * {@code "null"}. This must not be considered to be a trusted origin by the
- * application or by any JavaScript code running inside the WebView (for
- * example, event sources in DOM event handlers or web messages), because
- * malicious content can also create frames with a null origin. If you need to
- * identify the main frame's origin in a trustworthy way, you should use
- * {@link #loadDataWithBaseURL(String,String,String,String,String)
- * loadDataWithBaseURL()} with a valid HTTP or HTTPS base URL to set the origin.
- *
- * @param data a String of data in the given encoding
- * @param mimeType the MIME type of the data, e.g. 'text/html'.
- * @param encoding the encoding of the data
- */
- public void loadData(String data, String mimeType, String encoding) {
- }
+ public void setHttpAuthUsernamePassword(String host, String realm, String username,
+ String password) {}
- /**
- * Loads the given data into this WebView, using baseUrl as the base URL for the
- * content. The base URL is used both to resolve relative URLs and when applying
- * JavaScript's same origin policy. The historyUrl is used for the history
- * entry.
- *
- * The {@code mimeType} parameter specifies the format of the data. If WebView
- * can't handle the specified MIME type, it will download the data. If
- * {@code null}, defaults to 'text/html'.
- *
- * Note that content specified in this way can access local device files (via
- * 'file' scheme URLs) only if baseUrl specifies a scheme other than 'http',
- * 'https', 'ftp', 'ftps', 'about' or 'javascript'.
- *
- * If the base URL uses the data scheme, this method is equivalent to calling
- * {@link #loadData(String,String,String) loadData()} and the historyUrl is
- * ignored, and the data will be treated as part of a data: URL. If the base URL
- * uses any other scheme, then the data will be loaded into the WebView as a
- * plain string (i.e. not part of a data URL) and any URL-encoded entities in
- * the string will not be decoded.
- *
- * Note that the baseUrl is sent in the 'Referer' HTTP header when requesting
- * subresources (images, etc.) of the page loaded using this method.
- *
- * If a valid HTTP or HTTPS base URL is not specified in {@code baseUrl}, then
- * content loaded using this method will have a {@code window.origin} value of
- * {@code "null"}. This must not be considered to be a trusted origin by the
- * application or by any JavaScript code running inside the WebView (for
- * example, event sources in DOM event handlers or web messages), because
- * malicious content can also create frames with a null origin. If you need to
- * identify the main frame's origin in a trustworthy way, you should use a valid
- * HTTP or HTTPS base URL to set the origin.
- *
- * @param baseUrl the URL to use as the page's base URL. If {@code null}
- * defaults to 'about:blank'.
- * @param data a String of data in the given encoding
- * @param mimeType the MIME type of the data, e.g. 'text/html'.
- * @param encoding the encoding of the data
- * @param historyUrl the URL to use as the history entry. If {@code null}
- * defaults to 'about:blank'. If non-null, this must be a
- * valid URL.
- */
- public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl) {
- }
-
- /**
- * Sets the WebViewClient that will receive various notifications and
- * requests. This will replace the current handler.
- *
- * @param client an implementation of WebViewClient
- * @see #getWebViewClient
- */
- public void setWebViewClient(WebViewClient client) {
- }
- /**
- * Gets the WebViewClient.
- *
- * @return the WebViewClient, or a default client if not yet set
- * @see #setWebViewClient
- */
- public WebViewClient getWebViewClient() {
+ public String[] getHttpAuthUsernamePassword(String host, String realm) {
return null;
}
- /**
- * Injects the supplied Java object into this WebView. The object is
- * injected into the JavaScript context of the main frame, using the
- * supplied name. This allows the Java object's methods to be
- * accessed from JavaScript. For applications targeted to API
- * level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
- * and above, only public methods that are annotated with
- * {@link android.webkit.JavascriptInterface} can be accessed from JavaScript.
- * For applications targeted to API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN} or below,
- * all public methods (including the inherited ones) can be accessed, see the
- * important security note below for implications.
- *
Note that injected objects will not appear in JavaScript until the page is next
- * (re)loaded. JavaScript should be enabled before injecting the object. For example:
- *
- * class JsObject {
- * {@literal @}JavascriptInterface
- * public String toString() { return "injectedObject"; }
- * }
- * webview.getSettings().setJavaScriptEnabled(true);
- * webView.addJavascriptInterface(new JsObject(), "injectedObject");
- * webView.loadData("", "text/html", null);
- * webView.loadUrl("javascript:alert(injectedObject.toString())");
- *
- * IMPORTANT:
- *
- * - This method can be used to allow JavaScript to control the host
- * application. This is a powerful feature, but also presents a security
- * risk for apps targeting {@link android.os.Build.VERSION_CODES#JELLY_BEAN} or earlier.
- * Apps that target a version later than {@link android.os.Build.VERSION_CODES#JELLY_BEAN}
- * are still vulnerable if the app runs on a device running Android earlier than 4.2.
- * The most secure way to use this method is to target {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
- * and to ensure the method is called only when running on Android 4.2 or later.
- * With these older versions, JavaScript could use reflection to access an
- * injected object's public fields. Use of this method in a WebView
- * containing untrusted content could allow an attacker to manipulate the
- * host application in unintended ways, executing Java code with the
- * permissions of the host application. Use extreme care when using this
- * method in a WebView which could contain untrusted content.
- * - JavaScript interacts with Java object on a private, background
- * thread of this WebView. Care is therefore required to maintain thread
- * safety.
- *
- * - The Java object's fields are not accessible.
- * - For applications targeted to API level {@link android.os.Build.VERSION_CODES#LOLLIPOP}
- * and above, methods of injected Java objects are enumerable from
- * JavaScript.
- *
- *
- * @param object the Java object to inject into this WebView's JavaScript
- * context. {@code null} values are ignored.
- * @param name the name used to expose the object in JavaScript
- */
- public void addJavascriptInterface(Object object, String name) {
- }
- /**
- * Removes a previously injected Java object from this WebView. Note that
- * the removal will not be reflected in JavaScript until the page is next
- * (re)loaded. See {@link #addJavascriptInterface}.
- *
- * @param name the name used to expose the object in JavaScript
- */
- public void removeJavascriptInterface(String name) {
+ public void destroy() {}
+
+ public static void enablePlatformNotifications() {}
+
+ public static void disablePlatformNotifications() {}
+
+ public void loadUrl(String url) {}
+
+ public void loadData(String data, String mimeType, String encoding) {}
+
+ public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding,
+ String failUrl) {}
+
+ public void stopLoading() {}
+
+ public void reload() {}
+
+ public boolean canGoBack() {
+ return false;
+ }
+
+ public void goBack() {}
+
+ public boolean canGoForward() {
+ return false;
+ }
+
+ public void goForward() {}
+
+ public boolean canGoBackOrForward(int steps) {
+ return false;
+ }
+
+ public void goBackOrForward(int steps) {}
+
+ public boolean pageUp(boolean top) {
+ return false;
+ }
+
+ public boolean pageDown(boolean bottom) {
+ return false;
+ }
+
+ public void clearView() {}
+
+ public float getScale() {
+ return 0;
+ }
+
+ public void setInitialScale(int scaleInPercent) {}
+
+ public void invokeZoomPicker() {}
+
+ public String getUrl() {
+ return null;
+ }
+
+ public String getTitle() {
+ return null;
+ }
+
+ public int getProgress() {
+ return 0;
+ }
+
+ public int getContentHeight() {
+ return 0;
+ }
+
+ public void pauseTimers() {}
+
+ public void resumeTimers() {}
+
+ public void clearCache() {}
+
+ public void clearFormData() {}
+
+ public void clearHistory() {}
+
+ public void clearSslPreferences() {}
+
+ public static String findAddress(String addr) {
+ return null;
+ }
+
+ public void setWebViewClient(WebViewClient client) {}
+
+ public void addJavascriptInterface(Object obj, String interfaceName) {}
+
+ public View getZoomControls() {
+ return null;
+ }
+
+ public boolean zoomIn() {
+ return false;
+ }
+
+ public boolean zoomOut() {
+ return false;
}
- /**
- * Gets the WebSettings object used to control the settings for this
- * WebView.
- *
- * @return a WebSettings object that can be used to control this WebView's
- * settings
- */
public WebSettings getSettings() {
return null;
}
-
-
-
}
From 9eb4cda1afdfee81c4d19412568b2a2d2fe7a502 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Tue, 3 Aug 2021 17:50:04 +0200
Subject: [PATCH 058/471] Fix qhelp and formatting
---
.../CWE/CWE-940/AndroidIntentRedirection.qhelp | 6 +++---
.../CWE-940/AndroidIntentRedirectionTest.java | 15 ++++++---------
2 files changed, 9 insertions(+), 12 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp
index 4a32cdb3290..86548430f2a 100644
--- a/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp
+++ b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp
@@ -7,12 +7,12 @@
Do not export compontents that start other components from a user-provided Intent.
- They can be made private by setting the `android:exported` property to `false` in the app's Android Manifest.
+ They can be made private by setting the android:exported property to false in the app's Android Manifest.
If this is not possible, restrict either which apps can send Intents to the affected component, or which components can be started from it.
The following snippet contains two examples.
- In the first example, an arbitrary component can be started from the externally provided `forward_intent` Intent.
+ In the first example, an arbitrary component can be started from the externally provided forward_intent Intent.
In the second example, the destination component of the Intent is first checked to make sure it is safe.
@@ -27,7 +27,7 @@
Android Developers:
- The `android:exported` attribute.
+ The android:exported attribute.
\ No newline at end of file
diff --git a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
index d70aedfc2b1..f265465f265 100644
--- a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
+++ b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
@@ -8,6 +8,7 @@ import android.os.Bundle;
public class AndroidIntentRedirectionTest extends Activity {
public void onCreate(Bundle savedInstanceState) {
+ // @formatter:off
{
Intent intent = (Intent) getIntent().getParcelableExtra("forward_intent");
startActivities(new Intent[] {intent}); // $ hasAndroidIntentRedirection
@@ -17,13 +18,11 @@ public class AndroidIntentRedirectionTest extends Activity {
startActivityAsUser(intent, null); // $ hasAndroidIntentRedirection
startActivityAsUser(intent, null, null); // $ hasAndroidIntentRedirection
startActivityAsCaller(intent, null, false, 0); // $ hasAndroidIntentRedirection
- startActivityAsUserFromFragment(null, intent, 0, null, null); // $
- // hasAndroidIntentRedirection
+ startActivityAsUserFromFragment(null, intent, 0, null, null); // $ hasAndroidIntentRedirection
startActivityForResult(intent, 0); // $ hasAndroidIntentRedirection
startActivityForResult(intent, 0, null); // $ hasAndroidIntentRedirection
startActivityForResult(null, intent, 0, null); // $ hasAndroidIntentRedirection
- startActivityForResultAsUser(intent, null, 0, null, null); // $
- // hasAndroidIntentRedirection
+ startActivityForResultAsUser(intent, null, 0, null, null); // $ hasAndroidIntentRedirection
startActivityForResultAsUser(intent, 0, null, null); // $ hasAndroidIntentRedirection
startActivityForResultAsUser(intent, 0, null); // $ hasAndroidIntentRedirection
}
@@ -46,11 +45,9 @@ public class AndroidIntentRedirectionTest extends Activity {
sendStickyBroadcast(intent); // $ hasAndroidIntentRedirection
sendStickyBroadcastAsUser(intent, null); // $ hasAndroidIntentRedirection
sendStickyBroadcastAsUser(intent, null, null); // $ hasAndroidIntentRedirection
- sendStickyOrderedBroadcast(intent, null, null, 0, null, null); // $
- // hasAndroidIntentRedirection
- sendStickyOrderedBroadcastAsUser(intent, null, null, null, 0, null, null); // $
- // hasAndroidIntentRedirection
+ sendStickyOrderedBroadcast(intent, null, null, 0, null, null); // $ hasAndroidIntentRedirection
+ sendStickyOrderedBroadcastAsUser(intent, null, null, null, 0, null, null); // $ hasAndroidIntentRedirection
}
-
+ // @formatter:on
}
}
From a6f2ebe82027ee01f64a4e67db002f6ca37a801b Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Wed, 4 Aug 2021 11:13:15 +0200
Subject: [PATCH 059/471] Fix stubs
---
.../test/library-tests/dataflow/taintsources/IntentSources.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/java/ql/test/library-tests/dataflow/taintsources/IntentSources.java b/java/ql/test/library-tests/dataflow/taintsources/IntentSources.java
index 015fdaec74e..95432330f7e 100644
--- a/java/ql/test/library-tests/dataflow/taintsources/IntentSources.java
+++ b/java/ql/test/library-tests/dataflow/taintsources/IntentSources.java
@@ -1,7 +1,6 @@
package com.example.myapp;
import android.app.Activity;
-import android.content.Context;
public class IntentSources extends Activity {
From 5f0ce4d23284a8ea5f147a3dbd6d7e4d663d0afc Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Wed, 4 Aug 2021 11:23:57 +0200
Subject: [PATCH 060/471] Add suggestions from code review
---
java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp
index 86548430f2a..eb3f61f3bb5 100644
--- a/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp
+++ b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp
@@ -3,7 +3,7 @@
An exported Android component that obtains a user-provided Intent and uses it to launch another component
can be exploited to obtain access to private, unexported components of the same app or to launch other apps' components
- in behalf of the victim app.
+ on behalf of the victim app.
Do not export compontents that start other components from a user-provided Intent.
From d7973592daca0211e8e0ecbddea1fe3b2d4f3ce2 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Wed, 4 Aug 2021 11:29:59 +0200
Subject: [PATCH 061/471] Update
java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll
Co-authored-by: Marcono1234
---
.../src/semmle/code/java/security/AndroidIntentRedirection.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll b/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll
index 71daccd0cdd..bd318ac4ca7 100644
--- a/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll
+++ b/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll
@@ -1,4 +1,4 @@
-/** Provides classes to reason about Androind Intent redirect vulnerabilities. */
+/** Provides classes to reason about Android Intent redirect vulnerabilities. */
import java
private import semmle.code.java.dataflow.DataFlow
From 9604f88ae0a16dd03016ca96a7f79f004bc01739 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Mon, 9 Aug 2021 17:34:01 +0200
Subject: [PATCH 062/471] Undo autoformatting
---
.../test/library-tests/dataflow/taintsources/IntentSources.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/java/ql/test/library-tests/dataflow/taintsources/IntentSources.java b/java/ql/test/library-tests/dataflow/taintsources/IntentSources.java
index 95432330f7e..042ba1fbe98 100644
--- a/java/ql/test/library-tests/dataflow/taintsources/IntentSources.java
+++ b/java/ql/test/library-tests/dataflow/taintsources/IntentSources.java
@@ -33,7 +33,6 @@ public class IntentSources extends Activity {
}
-
class OtherClass {
private static void sink(Object o) {}
From 21b70a009e99de3b87dc3ec8b9c7911fd9affd63 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Tue, 10 Aug 2021 10:22:03 +0200
Subject: [PATCH 063/471] Use CSV models
---
.../java/security/AndroidIntentRedirection.qll | 17 ++---------------
.../CWE-940/AndroidIntentRedirectionTest.java | 1 -
2 files changed, 2 insertions(+), 16 deletions(-)
diff --git a/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll b/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll
index bd318ac4ca7..edbe2f6c5fe 100644
--- a/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll
+++ b/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll
@@ -2,6 +2,7 @@
import java
private import semmle.code.java.dataflow.DataFlow
+private import semmle.code.java.dataflow.ExternalFlow
private import semmle.code.java.frameworks.android.Intent
/**
@@ -28,19 +29,5 @@ class IntentRedirectionAdditionalTaintStep extends Unit {
/** Default sink for Intent redirection vulnerabilities. */
private class DefaultIntentRedirectionSink extends IntentRedirectionSink {
- DefaultIntentRedirectionSink() {
- exists(MethodAccess ma, Method m |
- ma.getMethod() = m and
- this.asExpr() = ma.getAnArgument() and
- (
- this.asExpr().getType() instanceof TypeIntent
- or
- this.asExpr().getType().(Array).getComponentType() instanceof TypeIntent
- )
- |
- m instanceof StartActivityMethod or
- m instanceof StartServiceMethod or
- m instanceof SendBroadcastMethod
- )
- }
+ DefaultIntentRedirectionSink() { sinkNode(this, "intent-start") }
}
diff --git a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
index f265465f265..69f12b1f712 100644
--- a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
+++ b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
@@ -18,7 +18,6 @@ public class AndroidIntentRedirectionTest extends Activity {
startActivityAsUser(intent, null); // $ hasAndroidIntentRedirection
startActivityAsUser(intent, null, null); // $ hasAndroidIntentRedirection
startActivityAsCaller(intent, null, false, 0); // $ hasAndroidIntentRedirection
- startActivityAsUserFromFragment(null, intent, 0, null, null); // $ hasAndroidIntentRedirection
startActivityForResult(intent, 0); // $ hasAndroidIntentRedirection
startActivityForResult(intent, 0, null); // $ hasAndroidIntentRedirection
startActivityForResult(null, intent, 0, null); // $ hasAndroidIntentRedirection
From 9a537f9c232b7aba5ac42b796a46c0c9cf631c19 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Tue, 10 Aug 2021 10:59:04 +0200
Subject: [PATCH 064/471] Add guard sanitizer for component name checks
---
.../security/AndroidIntentRedirection.qll | 16 ++++
.../CWE-940/AndroidIntentRedirectionTest.java | 76 ++++++++++---------
2 files changed, 56 insertions(+), 36 deletions(-)
diff --git a/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll b/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll
index edbe2f6c5fe..d8a1c12a79a 100644
--- a/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll
+++ b/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll
@@ -1,6 +1,7 @@
/** Provides classes to reason about Android Intent redirect vulnerabilities. */
import java
+private import semmle.code.java.controlflow.Guards
private import semmle.code.java.dataflow.DataFlow
private import semmle.code.java.dataflow.ExternalFlow
private import semmle.code.java.frameworks.android.Intent
@@ -31,3 +32,18 @@ class IntentRedirectionAdditionalTaintStep extends Unit {
private class DefaultIntentRedirectionSink extends IntentRedirectionSink {
DefaultIntentRedirectionSink() { sinkNode(this, "intent-start") }
}
+
+/**
+ * A default sanitizer for nodes dominated by calls to `ComponentName.getPackageName`
+ * or `ComponentName.getClassName`. These are used to check whether the origin or destination
+ * components are trusted.
+ */
+private class DefaultIntentRedirectionSanitizer extends IntentRedirectionSanitizer {
+ DefaultIntentRedirectionSanitizer() {
+ exists(MethodAccess ma, Method m |
+ ma.getMethod() = m and
+ m.hasQualifiedName("android.content", "ComponentName", ["getPackageName", "getClassName"]) and
+ ma.getBasicBlock().(ConditionBlock).controls(this.asExpr().getBasicBlock(), true)
+ )
+ }
+}
diff --git a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
index 69f12b1f712..0306431f8dc 100644
--- a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
+++ b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
@@ -8,45 +8,49 @@ import android.os.Bundle;
public class AndroidIntentRedirectionTest extends Activity {
public void onCreate(Bundle savedInstanceState) {
- // @formatter:off
- {
- Intent intent = (Intent) getIntent().getParcelableExtra("forward_intent");
- startActivities(new Intent[] {intent}); // $ hasAndroidIntentRedirection
- startActivities(new Intent[] {intent}, null); // $ hasAndroidIntentRedirection
+ Intent intent = (Intent) getIntent().getParcelableExtra("forward_intent");
+
+ if (intent.getComponent().getPackageName().equals("something")) {
+ startActivity(intent); // Safe - sanitized
+ } else {
startActivity(intent); // $ hasAndroidIntentRedirection
- startActivity(intent, null); // $ hasAndroidIntentRedirection
- startActivityAsUser(intent, null); // $ hasAndroidIntentRedirection
- startActivityAsUser(intent, null, null); // $ hasAndroidIntentRedirection
- startActivityAsCaller(intent, null, false, 0); // $ hasAndroidIntentRedirection
- startActivityForResult(intent, 0); // $ hasAndroidIntentRedirection
- startActivityForResult(intent, 0, null); // $ hasAndroidIntentRedirection
- startActivityForResult(null, intent, 0, null); // $ hasAndroidIntentRedirection
- startActivityForResultAsUser(intent, null, 0, null, null); // $ hasAndroidIntentRedirection
- startActivityForResultAsUser(intent, 0, null, null); // $ hasAndroidIntentRedirection
- startActivityForResultAsUser(intent, 0, null); // $ hasAndroidIntentRedirection
}
- {
- Intent intent = (Intent) getIntent().getParcelableExtra("forward_intent");
- startService(intent); // $ hasAndroidIntentRedirection
- startServiceAsUser(intent, null); // $ hasAndroidIntentRedirection
- }
- {
- Intent intent = (Intent) getIntent().getParcelableExtra("forward_intent");
- sendBroadcast(intent); // $ hasAndroidIntentRedirection
- sendBroadcast(intent, null); // $ hasAndroidIntentRedirection
- sendBroadcast(intent, null, null); // $ hasAndroidIntentRedirection
- sendBroadcast(intent, null, 0); // $ hasAndroidIntentRedirection
- sendBroadcastAsUser(intent, null); // $ hasAndroidIntentRedirection
- sendBroadcastAsUser(intent, null, null); // $ hasAndroidIntentRedirection
- sendBroadcastAsUser(intent, null, null, null); // $ hasAndroidIntentRedirection
- sendBroadcastAsUser(intent, null, null, 0); // $ hasAndroidIntentRedirection
- sendBroadcastWithMultiplePermissions(intent, null); // $ hasAndroidIntentRedirection
- sendStickyBroadcast(intent); // $ hasAndroidIntentRedirection
- sendStickyBroadcastAsUser(intent, null); // $ hasAndroidIntentRedirection
- sendStickyBroadcastAsUser(intent, null, null); // $ hasAndroidIntentRedirection
- sendStickyOrderedBroadcast(intent, null, null, 0, null, null); // $ hasAndroidIntentRedirection
- sendStickyOrderedBroadcastAsUser(intent, null, null, null, 0, null, null); // $ hasAndroidIntentRedirection
+ if (intent.getComponent().getClassName().equals("something")) {
+ startActivity(intent); // Safe - sanitized
+ } else {
+ startActivity(intent); // $ hasAndroidIntentRedirection
}
+
+ // @formatter:off
+ startActivities(new Intent[] {intent}); // $ hasAndroidIntentRedirection
+ startActivities(new Intent[] {intent}, null); // $ hasAndroidIntentRedirection
+ startActivity(intent); // $ hasAndroidIntentRedirection
+ startActivity(intent, null); // $ hasAndroidIntentRedirection
+ startActivityAsUser(intent, null); // $ hasAndroidIntentRedirection
+ startActivityAsUser(intent, null, null); // $ hasAndroidIntentRedirection
+ startActivityAsCaller(intent, null, false, 0); // $ hasAndroidIntentRedirection
+ startActivityForResult(intent, 0); // $ hasAndroidIntentRedirection
+ startActivityForResult(intent, 0, null); // $ hasAndroidIntentRedirection
+ startActivityForResult(null, intent, 0, null); // $ hasAndroidIntentRedirection
+ startActivityForResultAsUser(intent, null, 0, null, null); // $ hasAndroidIntentRedirection
+ startActivityForResultAsUser(intent, 0, null, null); // $ hasAndroidIntentRedirection
+ startActivityForResultAsUser(intent, 0, null); // $ hasAndroidIntentRedirection
+ startService(intent); // $ hasAndroidIntentRedirection
+ startServiceAsUser(intent, null); // $ hasAndroidIntentRedirection
+ sendBroadcast(intent); // $ hasAndroidIntentRedirection
+ sendBroadcast(intent, null); // $ hasAndroidIntentRedirection
+ sendBroadcast(intent, null, null); // $ hasAndroidIntentRedirection
+ sendBroadcast(intent, null, 0); // $ hasAndroidIntentRedirection
+ sendBroadcastAsUser(intent, null); // $ hasAndroidIntentRedirection
+ sendBroadcastAsUser(intent, null, null); // $ hasAndroidIntentRedirection
+ sendBroadcastAsUser(intent, null, null, null); // $ hasAndroidIntentRedirection
+ sendBroadcastAsUser(intent, null, null, 0); // $ hasAndroidIntentRedirection
+ sendBroadcastWithMultiplePermissions(intent, null); // $ hasAndroidIntentRedirection
+ sendStickyBroadcast(intent); // $ hasAndroidIntentRedirection
+ sendStickyBroadcastAsUser(intent, null); // $ hasAndroidIntentRedirection
+ sendStickyBroadcastAsUser(intent, null, null); // $ hasAndroidIntentRedirection
+ sendStickyOrderedBroadcast(intent, null, null, 0, null, null); // $ hasAndroidIntentRedirection
+ sendStickyOrderedBroadcastAsUser(intent, null, null, null, 0, null, null); // $ hasAndroidIntentRedirection
// @formatter:on
}
}
From f90220436ffa74272b2d094efd26ca2143214fa2 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Wed, 11 Aug 2021 13:03:08 +0200
Subject: [PATCH 065/471] Move sinks to security library
---
.../security/AndroidIntentRedirection.qll | 28 +++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll b/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll
index d8a1c12a79a..e110c09b916 100644
--- a/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll
+++ b/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll
@@ -28,6 +28,34 @@ class IntentRedirectionAdditionalTaintStep extends Unit {
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
}
+private class DefaultIntentRedirectionSinkModel extends SinkModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ "android.app;Activity;true;startActivityAsCaller;;;Argument[0];intent-start",
+ "android.app;Activity;true;startActivityForResult;(Intent,int);;Argument[0];intent-start",
+ "android.app;Activity;true;startActivityForResult;(Intent,int,Bundle);;Argument[0];intent-start",
+ "android.app;Activity;true;startActivityForResult;(String,Intent,int,Bundle);;Argument[1];intent-start",
+ "android.app;Activity;true;startActivityForResultAsUser;;;Argument[0];intent-start",
+ "android.content;Context;true;startActivities;;;Argument[0];intent-start",
+ "android.content;Context;true;startActivity;;;Argument[0];intent-start",
+ "android.content;Context;true;startActivityAsUser;;;Argument[0];intent-start",
+ "android.content;Context;true;startActivityFromChild;;;Argument[1];intent-start",
+ "android.content;Context;true;startActivityFromFragment;;;Argument[1];intent-start",
+ "android.content;Context;true;startActivityIfNeeded;;;Argument[0];intent-start",
+ "android.content;Context;true;startService;;;Argument[0];intent-start",
+ "android.content;Context;true;startServiceAsUser;;;Argument[0];intent-start",
+ "android.content;Context;true;sendBroadcast;;;Argument[0];intent-start",
+ "android.content;Context;true;sendBroadcastAsUser;;;Argument[0];intent-start",
+ "android.content;Context;true;sendBroadcastWithMultiplePermissions;;;Argument[0];intent-start",
+ "android.content;Context;true;sendStickyBroadcast;;;Argument[0];intent-start",
+ "android.content;Context;true;sendStickyBroadcastAsUser;;;Argument[0];intent-start",
+ "android.content;Context;true;sendStickyOrderedBroadcast;;;Argument[0];intent-start",
+ "android.content;Context;true;sendStickyOrderedBroadcastAsUser;;;Argument[0];intent-start"
+ ]
+ }
+}
+
/** Default sink for Intent redirection vulnerabilities. */
private class DefaultIntentRedirectionSink extends IntentRedirectionSink {
DefaultIntentRedirectionSink() { sinkNode(this, "intent-start") }
From aa2cdb7a53433cacf1354785c6b55880a953c4af Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Thu, 19 Aug 2021 16:34:04 +0200
Subject: [PATCH 066/471] Add intermediate dataflow
Make sure that source intents are obtained from another intent's extras
---
.../AndroidIntentRedirectionQuery.qll | 34 ++++++++++++++++++-
.../CWE-940/AndroidIntentRedirectionTest.java | 2 ++
2 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/java/ql/src/semmle/code/java/security/AndroidIntentRedirectionQuery.qll b/java/ql/src/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
index a14b2a5eb73..d0e6fdd0db3 100644
--- a/java/ql/src/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
+++ b/java/ql/src/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
@@ -11,7 +11,7 @@ import semmle.code.java.security.AndroidIntentRedirection
class IntentRedirectionConfiguration extends TaintTracking::Configuration {
IntentRedirectionConfiguration() { this = "IntentRedirectionConfiguration" }
- override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
+ override predicate isSource(DataFlow::Node source) { source instanceof IntentRedirectionSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof IntentRedirectionSink }
@@ -23,3 +23,35 @@ class IntentRedirectionConfiguration extends TaintTracking::Configuration {
any(IntentRedirectionAdditionalTaintStep c).step(node1, node2)
}
}
+
+/** The method `getParcelableExtra` called on a tainted `Intent`. */
+private class IntentRedirectionSource extends DataFlow::Node {
+ IntentRedirectionSource() {
+ exists(GetParcelableExtra ma | this.asExpr() = ma.getQualifier()) and
+ exists(IntentToGetParcelableExtraConf conf | conf.hasFlowTo(this))
+ }
+}
+
+/**
+ * Data flow from a remote intent to the qualifier of a `getParcelableExtra` call.
+ */
+private class IntentToGetParcelableExtraConf extends DataFlow2::Configuration {
+ IntentToGetParcelableExtraConf() { this = "IntentToGetParcelableExtraConf" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(GetParcelableExtra ma | sink.asExpr() = ma.getQualifier())
+ }
+}
+
+/** A call to the method `Intent.getParcelableExtra`. */
+private class GetParcelableExtra extends MethodAccess {
+ GetParcelableExtra() {
+ exists(Method m |
+ this.getMethod() = m and
+ m.getDeclaringType() instanceof TypeIntent and
+ m.hasName("getParcelableExtra")
+ )
+ }
+}
diff --git a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
index 0306431f8dc..20ed02e87a2 100644
--- a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
+++ b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
@@ -21,6 +21,8 @@ public class AndroidIntentRedirectionTest extends Activity {
startActivity(intent); // $ hasAndroidIntentRedirection
}
+ startActivity(getIntent()); // Safe - not an intent obtained from the Extras
+
// @formatter:off
startActivities(new Intent[] {intent}); // $ hasAndroidIntentRedirection
startActivities(new Intent[] {intent}, null); // $ hasAndroidIntentRedirection
From 28369d1822e5ec36645afcf0c69748e76da2ef53 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Thu, 19 Aug 2021 16:43:39 +0200
Subject: [PATCH 067/471] Apply suggestions from code review
Co-authored-by: Steve Guntrip <12534592+stevecat@users.noreply.github.com>
---
.../src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp
index eb3f61f3bb5..47ba77b6e25 100644
--- a/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp
+++ b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp
@@ -6,14 +6,15 @@
on behalf of the victim app.
- Do not export compontents that start other components from a user-provided Intent.
+
Do not export components that start other components from a user-provided Intent.
They can be made private by setting the android:exported property to false in the app's Android Manifest.
If this is not possible, restrict either which apps can send Intents to the affected component, or which components can be started from it.
The following snippet contains two examples.
In the first example, an arbitrary component can be started from the externally provided forward_intent Intent.
- In the second example, the destination component of the Intent is first checked to make sure it is safe.
+ In the second example, the destination component of the Intent is first checked to make sure it is safe.
+ In the third example, the component that created the Intent is first checked to make sure it comes from a trusted origin.
From 2ab7a555453d7ce973a162c5b95806d52986a5e2 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Mon, 23 Aug 2021 11:57:12 +0200
Subject: [PATCH 068/471] Improve intermediate flow to add more potential
sources
---
.../security/AndroidIntentRedirection.qll | 3 +-
.../AndroidIntentRedirectionQuery.qll | 59 ++++++++++---
.../CWE-940/AndroidIntentRedirectionTest.java | 82 +++++++++++++++++++
3 files changed, 131 insertions(+), 13 deletions(-)
diff --git a/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll b/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll
index e110c09b916..90d513fbcc1 100644
--- a/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll
+++ b/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll
@@ -70,7 +70,8 @@ private class DefaultIntentRedirectionSanitizer extends IntentRedirectionSanitiz
DefaultIntentRedirectionSanitizer() {
exists(MethodAccess ma, Method m |
ma.getMethod() = m and
- m.hasQualifiedName("android.content", "ComponentName", ["getPackageName", "getClassName"]) and
+ m.getDeclaringType() instanceof TypeComponentName and
+ m.hasName(["getPackageName", "getClassName"]) and
ma.getBasicBlock().(ConditionBlock).controls(this.asExpr().getBasicBlock(), true)
)
}
diff --git a/java/ql/src/semmle/code/java/security/AndroidIntentRedirectionQuery.qll b/java/ql/src/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
index d0e6fdd0db3..22f51239252 100644
--- a/java/ql/src/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
+++ b/java/ql/src/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
@@ -3,10 +3,11 @@
import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking
+import semmle.code.java.dataflow.TaintTracking2
import semmle.code.java.security.AndroidIntentRedirection
/**
- * A taint tracking configuration for user-provided Intents being used to start Android components.
+ * A taint tracking configuration for tainted Intents being used to start Android components.
*/
class IntentRedirectionConfiguration extends TaintTracking::Configuration {
IntentRedirectionConfiguration() { this = "IntentRedirectionConfiguration" }
@@ -24,30 +25,34 @@ class IntentRedirectionConfiguration extends TaintTracking::Configuration {
}
}
-/** The method `getParcelableExtra` called on a tainted `Intent`. */
+/** An expression modifying an `Intent` component with tainted data. */
private class IntentRedirectionSource extends DataFlow::Node {
IntentRedirectionSource() {
- exists(GetParcelableExtra ma | this.asExpr() = ma.getQualifier()) and
- exists(IntentToGetParcelableExtraConf conf | conf.hasFlowTo(this))
+ changesIntentComponent(this.asExpr()) and
+ exists(TaintedIntentComponentConf conf | conf.hasFlowTo(this))
}
}
/**
- * Data flow from a remote intent to the qualifier of a `getParcelableExtra` call.
+ * A taint tracking configuration for tainted data flowing to an `Intent`'s component.
*/
-private class IntentToGetParcelableExtraConf extends DataFlow2::Configuration {
- IntentToGetParcelableExtraConf() { this = "IntentToGetParcelableExtraConf" }
+private class TaintedIntentComponentConf extends TaintTracking2::Configuration {
+ TaintedIntentComponentConf() { this = "TaintedIntentComponentConf" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
- override predicate isSink(DataFlow::Node sink) {
- exists(GetParcelableExtra ma | sink.asExpr() = ma.getQualifier())
- }
+ override predicate isSink(DataFlow::Node sink) { changesIntentComponent(sink.asExpr()) }
+}
+
+/** Holds if `expr` modifies the component of an `Intent`. */
+private predicate changesIntentComponent(Expr expr) {
+ any(IntentGetParcelableExtra igpe).getQualifier() = expr or
+ any(IntentSetComponent isc).getSink() = expr
}
/** A call to the method `Intent.getParcelableExtra`. */
-private class GetParcelableExtra extends MethodAccess {
- GetParcelableExtra() {
+private class IntentGetParcelableExtra extends MethodAccess {
+ IntentGetParcelableExtra() {
exists(Method m |
this.getMethod() = m and
m.getDeclaringType() instanceof TypeIntent and
@@ -55,3 +60,33 @@ private class GetParcelableExtra extends MethodAccess {
)
}
}
+
+/** A call to a method that changes the component of an `Intent`. */
+private class IntentSetComponent extends MethodAccess {
+ int sinkArg;
+
+ IntentSetComponent() {
+ exists(Method m |
+ this.getMethod() = m and
+ m.getDeclaringType() instanceof TypeIntent
+ |
+ m.hasName("setClass") and
+ sinkArg = 1
+ or
+ m.hasName("setClassName") and
+ exists(Parameter p |
+ p = m.getAParameter() and
+ p.getType() instanceof TypeString and
+ sinkArg = p.getPosition()
+ )
+ or
+ m.hasName("setComponent") and
+ sinkArg = 0
+ or
+ m.hasName("setPackage") and
+ sinkArg = 0
+ )
+ }
+
+ Expr getSink() { result = this.getArgument(sinkArg) }
+}
diff --git a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
index 20ed02e87a2..9ab81c74500 100644
--- a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
+++ b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
@@ -1,6 +1,7 @@
package com.example.app;
import android.app.Activity;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -54,5 +55,86 @@ public class AndroidIntentRedirectionTest extends Activity {
sendStickyOrderedBroadcast(intent, null, null, 0, null, null); // $ hasAndroidIntentRedirection
sendStickyOrderedBroadcastAsUser(intent, null, null, null, 0, null, null); // $ hasAndroidIntentRedirection
// @formatter:on
+
+ try {
+ {
+ Intent fwdIntent = new Intent();
+ fwdIntent.setClassName((Context) null, (String) intent.getExtra("className"));
+ startActivity(fwdIntent); // $ hasAndroidIntentRedirection
+ }
+ {
+ Intent fwdIntent = new Intent();
+ fwdIntent.setClassName((String) intent.getExtra("packageName"), null);
+ startActivity(fwdIntent); // $ hasAndroidIntentRedirection
+ }
+ {
+ Intent fwdIntent = new Intent();
+ fwdIntent.setClassName((String) intent.getExtra("packageName"),
+ (String) intent.getExtra("className"));
+ startActivity(fwdIntent); // $ hasAndroidIntentRedirection
+ }
+ {
+ Intent fwdIntent = new Intent();
+ fwdIntent.setClass(null, Class.forName((String) intent.getExtra("className")));
+ // needs taint step for Class.forName
+ startActivity(fwdIntent); // $ MISSING: $hasAndroidIntentRedirection
+ }
+ {
+ Intent fwdIntent = new Intent();
+ fwdIntent.setPackage((String) intent.getExtra("packageName"));
+ startActivity(fwdIntent); // $ hasAndroidIntentRedirection
+ }
+ {
+ Intent fwdIntent = new Intent();
+ ComponentName component =
+ new ComponentName((String) intent.getExtra("packageName"), null);
+ fwdIntent.setComponent(component);
+ startActivity(fwdIntent); // $ hasAndroidIntentRedirection
+ }
+ {
+ Intent fwdIntent = new Intent();
+ ComponentName component =
+ new ComponentName("", (String) intent.getExtra("className"));
+ fwdIntent.setComponent(component);
+ startActivity(fwdIntent); // $ hasAndroidIntentRedirection
+ }
+ {
+ Intent fwdIntent = new Intent();
+ ComponentName component =
+ new ComponentName((Context) null, (String) intent.getExtra("className"));
+ fwdIntent.setComponent(component);
+ startActivity(fwdIntent); // $ hasAndroidIntentRedirection
+ }
+ {
+ Intent fwdIntent = new Intent();
+ ComponentName component = new ComponentName((Context) null,
+ Class.forName((String) intent.getExtra("className")));
+ fwdIntent.setComponent(component);
+ // needs taint step for Class.forName
+ startActivity(fwdIntent); // $ MISSING: $hasAndroidIntentRedirection
+ }
+ {
+ Intent fwdIntent = new Intent();
+ ComponentName component =
+ ComponentName.createRelative("", (String) intent.getExtra("className"));
+ fwdIntent.setComponent(component);
+ startActivity(fwdIntent); // $ hasAndroidIntentRedirection
+ }
+ {
+ Intent fwdIntent = new Intent();
+ ComponentName component =
+ ComponentName.createRelative((String) intent.getExtra("packageName"), "");
+ fwdIntent.setComponent(component);
+ startActivity(fwdIntent); // $ hasAndroidIntentRedirection
+ }
+ {
+ Intent fwdIntent = new Intent();
+ ComponentName component = ComponentName.createRelative((Context) null,
+ (String) intent.getExtra("className"));
+ fwdIntent.setComponent(component);
+ startActivity(fwdIntent); // $ hasAndroidIntentRedirection
+ }
+ } catch (Exception e) {
+ }
}
}
From 8263524d7041b8a1e9a124794e808aba6ae8fa43 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Mon, 23 Aug 2021 14:19:18 +0200
Subject: [PATCH 069/471] Add tests for Intent and ComponentName summaries
---
.../frameworks/android/intents/Test.java | 218 ++++++++++++++++++
.../frameworks/android/intents/options | 1 +
.../frameworks/android/intents/test.expected | 0
.../frameworks/android/intents/test.ql | 53 +++++
4 files changed, 272 insertions(+)
create mode 100644 java/ql/test/library-tests/frameworks/android/intents/Test.java
create mode 100644 java/ql/test/library-tests/frameworks/android/intents/options
create mode 100644 java/ql/test/library-tests/frameworks/android/intents/test.expected
create mode 100644 java/ql/test/library-tests/frameworks/android/intents/test.ql
diff --git a/java/ql/test/library-tests/frameworks/android/intents/Test.java b/java/ql/test/library-tests/frameworks/android/intents/Test.java
new file mode 100644
index 00000000000..0ff36933eb3
--- /dev/null
+++ b/java/ql/test/library-tests/frameworks/android/intents/Test.java
@@ -0,0 +1,218 @@
+package generatedtest;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Parcel;
+
+// Test case generated by GenerateFlowTestCase.ql
+public class Test {
+
+ Object source() {
+ return null;
+ }
+
+ void sink(Object o) {}
+
+ public void test() throws Exception {
+
+ {
+ // "android.content;ComponentName;false;ComponentName;(Context,Class);;Argument[1];Argument[-1];taint"
+ ComponentName out = null;
+ Class in = (Class) source();
+ out = new ComponentName((Context) null, in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;ComponentName;(Context,String);;Argument[1];Argument[-1];taint"
+ ComponentName out = null;
+ String in = (String) source();
+ out = new ComponentName((Context) null, in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;ComponentName;(Parcel);;Argument[0];Argument[-1];taint"
+ ComponentName out = null;
+ Parcel in = (Parcel) source();
+ out = new ComponentName(in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;ComponentName;(String,String);;Argument[0..1];Argument[-1];taint"
+ ComponentName out = null;
+ String in = (String) source();
+ out = new ComponentName(in, (String) null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;ComponentName;(String,String);;Argument[0..1];Argument[-1];taint"
+ ComponentName out = null;
+ String in = (String) source();
+ out = new ComponentName((String) null, in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;createRelative;(Context,String);;Argument[1];ReturnValue;taint"
+ ComponentName out = null;
+ String in = (String) source();
+ out = ComponentName.createRelative((Context) null, in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;createRelative;(String,String);;Argument[0..1];ReturnValue;taint"
+ ComponentName out = null;
+ String in = (String) source();
+ out = ComponentName.createRelative(in, (String) null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;createRelative;(String,String);;Argument[0..1];ReturnValue;taint"
+ ComponentName out = null;
+ String in = (String) source();
+ out = ComponentName.createRelative((String) null, in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;flattenToShortString;;;Argument[-1];ReturnValue;taint"
+ String out = null;
+ ComponentName in = (ComponentName) source();
+ out = in.flattenToShortString();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;flattenToString;;;Argument[-1];ReturnValue;taint"
+ String out = null;
+ ComponentName in = (ComponentName) source();
+ out = in.flattenToString();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;getClassName;;;Argument[-1];ReturnValue;taint"
+ String out = null;
+ ComponentName in = (ComponentName) source();
+ out = in.getClassName();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;getPackageName;;;Argument[-1];ReturnValue;taint"
+ String out = null;
+ ComponentName in = (ComponentName) source();
+ out = in.getPackageName();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;getShortClassName;;;Argument[-1];ReturnValue;taint"
+ String out = null;
+ ComponentName in = (ComponentName) source();
+ out = in.getShortClassName();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;unflattenFromString;;;Argument[0];ReturnValue;taint"
+ ComponentName out = null;
+ String in = (String) source();
+ out = ComponentName.unflattenFromString(in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;Intent;true;Intent;(Context,Class);;Argument[1];Argument[-1];taint"
+ Intent out = null;
+ Class in = (Class) source();
+ out = new Intent((Context) null, in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;Intent;true;Intent;(Intent);;Argument[0];Argument[-1];taint"
+ Intent out = null;
+ Intent in = (Intent) source();
+ out = new Intent(in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;Intent;true;Intent;(String,Uri,Context,Class);;Argument[3];Argument[-1];taint"
+ Intent out = null;
+ Class in = (Class) source();
+ out = new Intent(null, null, null, in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;Intent;true;setClass;;;Argument[-1];ReturnValue;taint"
+ Intent out = null;
+ Intent in = (Intent) source();
+ out = in.setClass(null, null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;Intent;true;setClass;;;Argument[1];Argument[-1];taint"
+ Intent out = null;
+ Class in = (Class) source();
+ out.setClass(null, in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;Intent;true;setClassName;(Context,String);;Argument[1];Argument[-1];taint"
+ Intent out = null;
+ String in = (String) source();
+ out.setClassName((Context) null, in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;Intent;true;setClassName;(String,String);;Argument[0..1];Argument[-1];taint"
+ Intent out = null;
+ String in = (String) source();
+ out.setClassName(in, (String) null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;Intent;true;setClassName;(String,String);;Argument[0..1];Argument[-1];taint"
+ Intent out = null;
+ String in = (String) source();
+ out.setClassName((String) null, in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;Intent;true;setClassName;;;Argument[-1];ReturnValue;taint"
+ Intent out = null;
+ Intent in = (Intent) source();
+ out = in.setClassName((String) null, (String) null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;Intent;true;setClassName;;;Argument[-1];ReturnValue;taint"
+ Intent out = null;
+ Intent in = (Intent) source();
+ out = in.setClassName((Context) null, (String) null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;Intent;true;setComponent;;;Argument[-1];ReturnValue;taint"
+ Intent out = null;
+ Intent in = (Intent) source();
+ out = in.setComponent(null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;Intent;true;setComponent;;;Argument[0];Argument[-1];taint"
+ Intent out = null;
+ ComponentName in = (ComponentName) source();
+ out.setComponent(in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;Intent;true;setPackage;;;Argument[-1];ReturnValue;taint"
+ Intent out = null;
+ Intent in = (Intent) source();
+ out = in.setPackage(null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;Intent;true;setPackage;;;Argument[0];Argument[-1];taint"
+ Intent out = null;
+ String in = (String) source();
+ out.setPackage(in);
+ sink(out); // $ hasTaintFlow
+ }
+
+ }
+
+}
diff --git a/java/ql/test/library-tests/frameworks/android/intents/options b/java/ql/test/library-tests/frameworks/android/intents/options
new file mode 100644
index 00000000000..33cdc1ea940
--- /dev/null
+++ b/java/ql/test/library-tests/frameworks/android/intents/options
@@ -0,0 +1 @@
+//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/google-android-9.0.0
diff --git a/java/ql/test/library-tests/frameworks/android/intents/test.expected b/java/ql/test/library-tests/frameworks/android/intents/test.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/java/ql/test/library-tests/frameworks/android/intents/test.ql b/java/ql/test/library-tests/frameworks/android/intents/test.ql
new file mode 100644
index 00000000000..465161863cc
--- /dev/null
+++ b/java/ql/test/library-tests/frameworks/android/intents/test.ql
@@ -0,0 +1,53 @@
+import java
+import semmle.code.java.dataflow.DataFlow
+import semmle.code.java.dataflow.ExternalFlow
+import semmle.code.java.dataflow.TaintTracking
+import TestUtilities.InlineExpectationsTest
+
+class ValueFlowConf extends DataFlow::Configuration {
+ ValueFlowConf() { this = "qltest:valueFlowConf" }
+
+ override predicate isSource(DataFlow::Node n) {
+ n.asExpr().(MethodAccess).getMethod().hasName("source")
+ }
+
+ override predicate isSink(DataFlow::Node n) {
+ n.asExpr().(Argument).getCall().getCallee().hasName("sink")
+ }
+}
+
+class TaintFlowConf extends TaintTracking::Configuration {
+ TaintFlowConf() { this = "qltest:taintFlowConf" }
+
+ override predicate isSource(DataFlow::Node n) {
+ n.asExpr().(MethodAccess).getMethod().hasName("source")
+ }
+
+ override predicate isSink(DataFlow::Node n) {
+ n.asExpr().(Argument).getCall().getCallee().hasName("sink")
+ }
+}
+
+class HasFlowTest extends InlineExpectationsTest {
+ HasFlowTest() { this = "HasFlowTest" }
+
+ override string getARelevantTag() { result = ["hasValueFlow", "hasTaintFlow"] }
+
+ override predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "hasValueFlow" and
+ exists(DataFlow::Node src, DataFlow::Node sink, ValueFlowConf conf | conf.hasFlow(src, sink) |
+ sink.getLocation() = location and
+ element = sink.toString() and
+ value = ""
+ )
+ or
+ tag = "hasTaintFlow" and
+ exists(DataFlow::Node src, DataFlow::Node sink, TaintFlowConf conf |
+ conf.hasFlow(src, sink) and not any(ValueFlowConf c).hasFlow(src, sink)
+ |
+ sink.getLocation() = location and
+ element = sink.toString() and
+ value = ""
+ )
+ }
+}
From 445da1e71e126c020806057d11b3cecf2e6b6649 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Mon, 23 Aug 2021 16:22:55 +0200
Subject: [PATCH 070/471] Move files to new location
---
.../semmle/code/java/security/AndroidIntentRedirection.qll | 0
.../semmle/code/java/security/AndroidIntentRedirectionQuery.qll | 0
2 files changed, 0 insertions(+), 0 deletions(-)
rename java/ql/{src => lib}/semmle/code/java/security/AndroidIntentRedirection.qll (100%)
rename java/ql/{src => lib}/semmle/code/java/security/AndroidIntentRedirectionQuery.qll (100%)
diff --git a/java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll b/java/ql/lib/semmle/code/java/security/AndroidIntentRedirection.qll
similarity index 100%
rename from java/ql/src/semmle/code/java/security/AndroidIntentRedirection.qll
rename to java/ql/lib/semmle/code/java/security/AndroidIntentRedirection.qll
diff --git a/java/ql/src/semmle/code/java/security/AndroidIntentRedirectionQuery.qll b/java/ql/lib/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
similarity index 100%
rename from java/ql/src/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
rename to java/ql/lib/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
From 14963103aa8b14d8ddf1b670de649e1ca268ee5d Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Tue, 24 Aug 2021 14:54:20 +0200
Subject: [PATCH 071/471] Add full path reconstruction from RemoteFlowSource to
sink
---
.../AndroidIntentRedirectionQuery.qll | 36 +++++++++++++---
.../CWE/CWE-940/AndroidIntentRedirection.ql | 4 +-
.../CWE-940/AndroidIntentRedirectionTest.java | 43 +++++++++++++------
.../CWE-940/AndroidIntentRedirectionTest.ql | 6 +--
4 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/AndroidIntentRedirectionQuery.qll b/java/ql/lib/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
index 22f51239252..56d56b3ef9b 100644
--- a/java/ql/lib/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
@@ -6,13 +6,32 @@ import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.TaintTracking2
import semmle.code.java.security.AndroidIntentRedirection
+/**
+ * Holds if taint may flow from `source` to `sink` for `IntentRedirectionConfiguration`.
+ *
+ * It ensures that `ChangeIntentComponent` is an intermediate step in the flow.
+ */
+predicate hasIntentRedirectionFlowPath(DataFlow::PathNode source, DataFlow::PathNode sink) {
+ exists(IntentRedirectionConfiguration conf, DataFlow::PathNode intermediate |
+ intermediate.getNode().(ChangeIntentComponent).hasFlowFrom(source.getNode()) and
+ conf.hasFlowPath(intermediate, sink)
+ )
+}
+
/**
* A taint tracking configuration for tainted Intents being used to start Android components.
*/
-class IntentRedirectionConfiguration extends TaintTracking::Configuration {
+private class IntentRedirectionConfiguration extends TaintTracking::Configuration {
IntentRedirectionConfiguration() { this = "IntentRedirectionConfiguration" }
- override predicate isSource(DataFlow::Node source) { source instanceof IntentRedirectionSource }
+ override predicate isSource(DataFlow::Node source) {
+ exists(ChangeIntentComponent c |
+ c = source
+ or
+ // This is needed for PathGraph to be able to build the full flow
+ c.hasFlowFrom(source)
+ )
+ }
override predicate isSink(DataFlow::Node sink) { sink instanceof IntentRedirectionSink }
@@ -26,11 +45,16 @@ class IntentRedirectionConfiguration extends TaintTracking::Configuration {
}
/** An expression modifying an `Intent` component with tainted data. */
-private class IntentRedirectionSource extends DataFlow::Node {
- IntentRedirectionSource() {
+private class ChangeIntentComponent extends DataFlow::Node {
+ DataFlow::Node src;
+
+ ChangeIntentComponent() {
changesIntentComponent(this.asExpr()) and
- exists(TaintedIntentComponentConf conf | conf.hasFlowTo(this))
+ exists(TaintedIntentComponentConf conf | conf.hasFlow(src, this))
}
+
+ /** Holds if `source` flows to `this`. */
+ predicate hasFlowFrom(DataFlow::Node source) { source = src }
}
/**
@@ -46,7 +70,7 @@ private class TaintedIntentComponentConf extends TaintTracking2::Configuration {
/** Holds if `expr` modifies the component of an `Intent`. */
private predicate changesIntentComponent(Expr expr) {
- any(IntentGetParcelableExtra igpe).getQualifier() = expr or
+ any(IntentGetParcelableExtra igpe) = expr or
any(IntentSetComponent isc).getSink() = expr
}
diff --git a/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.ql b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.ql
index a039cd95bc6..c21cb1d75dd 100644
--- a/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.ql
+++ b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.ql
@@ -18,8 +18,8 @@ import semmle.code.java.dataflow.DataFlow
import semmle.code.java.security.AndroidIntentRedirectionQuery
import DataFlow::PathGraph
-from DataFlow::PathNode source, DataFlow::PathNode sink, IntentRedirectionConfiguration conf
-where conf.hasFlowPath(source, sink)
+from DataFlow::PathNode source, DataFlow::PathNode sink
+where hasIntentRedirectionFlowPath(source, sink)
select sink.getNode(), source, sink,
"Arbitrary Android activities or services can be started from $@.", source.getNode(),
"this user input"
diff --git a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
index 9ab81c74500..12d68abf95a 100644
--- a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
+++ b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
@@ -11,19 +11,6 @@ public class AndroidIntentRedirectionTest extends Activity {
public void onCreate(Bundle savedInstanceState) {
Intent intent = (Intent) getIntent().getParcelableExtra("forward_intent");
- if (intent.getComponent().getPackageName().equals("something")) {
- startActivity(intent); // Safe - sanitized
- } else {
- startActivity(intent); // $ hasAndroidIntentRedirection
- }
- if (intent.getComponent().getClassName().equals("something")) {
- startActivity(intent); // Safe - sanitized
- } else {
- startActivity(intent); // $ hasAndroidIntentRedirection
- }
-
- startActivity(getIntent()); // Safe - not an intent obtained from the Extras
-
// @formatter:off
startActivities(new Intent[] {intent}); // $ hasAndroidIntentRedirection
startActivities(new Intent[] {intent}, null); // $ hasAndroidIntentRedirection
@@ -56,6 +43,17 @@ public class AndroidIntentRedirectionTest extends Activity {
sendStickyOrderedBroadcastAsUser(intent, null, null, null, 0, null, null); // $ hasAndroidIntentRedirection
// @formatter:on
+ if (intent.getComponent().getPackageName().equals("something")) {
+ startActivity(intent); // Safe - sanitized
+ } else {
+ startActivity(intent); // $ hasAndroidIntentRedirection
+ }
+ if (intent.getComponent().getClassName().equals("something")) {
+ startActivity(intent); // Safe - sanitized
+ } else {
+ startActivity(intent); // $ hasAndroidIntentRedirection
+ }
+
try {
{
Intent fwdIntent = new Intent();
@@ -134,6 +132,25 @@ public class AndroidIntentRedirectionTest extends Activity {
fwdIntent.setComponent(component);
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
}
+ {
+ Intent originalIntent = getIntent();
+ Intent fwdIntent = (Intent) originalIntent.getParcelableExtra("forward_intent");
+ startActivity(originalIntent); // Safe - not an Intent obtained from the Extras
+ }
+ {
+ Intent originalIntent = getIntent();
+ ComponentName cp = new ComponentName(originalIntent.getStringExtra("packageName"),
+ originalIntent.getStringExtra("className"));
+ Intent anotherIntent = new Intent();
+ anotherIntent.setComponent(cp);
+ startActivity(originalIntent); // Safe - not a tainted Intent
+ }
+ {
+ // Delayed cast
+ Object obj = getIntent().getParcelableExtra("forward_intent");
+ Intent fwdIntent = (Intent) obj;
+ startActivity(fwdIntent); // $ hasAndroidIntentRedirection
+ }
} catch (Exception e) {
}
}
diff --git a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.ql b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.ql
index 9e883b26498..f66a7995dfe 100644
--- a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.ql
+++ b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.ql
@@ -9,10 +9,10 @@ class HasAndroidIntentRedirectionTest extends InlineExpectationsTest {
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasAndroidIntentRedirection" and
- exists(DataFlow::Node src, DataFlow::Node sink, IntentRedirectionConfiguration conf |
- conf.hasFlow(src, sink)
+ exists(DataFlow::PathNode src, DataFlow::PathNode sink |
+ hasIntentRedirectionFlowPath(src, sink)
|
- sink.getLocation() = location and
+ sink.getNode().getLocation() = location and
element = sink.toString() and
value = ""
)
From 4dd9e7d6a003925e369d293fd5a81cc2e202d1e8 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Tue, 24 Aug 2021 16:46:54 +0200
Subject: [PATCH 072/471] Remove unnecessary import
Add comment
---
.../code/java/security/AndroidIntentRedirectionQuery.qll | 4 +++-
java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.ql | 1 -
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/AndroidIntentRedirectionQuery.qll b/java/ql/lib/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
index 56d56b3ef9b..3e5f2e053dd 100644
--- a/java/ql/lib/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
@@ -70,8 +70,10 @@ private class TaintedIntentComponentConf extends TaintTracking2::Configuration {
/** Holds if `expr` modifies the component of an `Intent`. */
private predicate changesIntentComponent(Expr expr) {
- any(IntentGetParcelableExtra igpe) = expr or
any(IntentSetComponent isc).getSink() = expr
+ or
+ // obtaining an arbitrary Intent as a Parcelable extra
+ expr instanceof IntentGetParcelableExtra
}
/** A call to the method `Intent.getParcelableExtra`. */
diff --git a/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.ql b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.ql
index c21cb1d75dd..4bfdfa12738 100644
--- a/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.ql
+++ b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.ql
@@ -14,7 +14,6 @@
*/
import java
-import semmle.code.java.dataflow.DataFlow
import semmle.code.java.security.AndroidIntentRedirectionQuery
import DataFlow::PathGraph
From bc6c13be698a1789ed2b7be81661127399d49cfb Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Thu, 26 Aug 2021 12:21:57 +0200
Subject: [PATCH 073/471] Refactor to actually build the full flows from src to
sink
Add more tests for edge cases
---
.../AndroidIntentRedirectionQuery.qll | 92 +++++++++----------
.../CWE/CWE-940/AndroidIntentRedirection.ql | 4 +-
.../CWE-940/AndroidIntentRedirectionTest.java | 44 +++++++--
.../CWE-940/AndroidIntentRedirectionTest.ql | 6 +-
4 files changed, 84 insertions(+), 62 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/AndroidIntentRedirectionQuery.qll b/java/ql/lib/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
index 3e5f2e053dd..9173680a778 100644
--- a/java/ql/lib/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
@@ -2,36 +2,18 @@
import java
import semmle.code.java.dataflow.FlowSources
+import semmle.code.java.dataflow.DataFlow3
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.TaintTracking2
import semmle.code.java.security.AndroidIntentRedirection
-/**
- * Holds if taint may flow from `source` to `sink` for `IntentRedirectionConfiguration`.
- *
- * It ensures that `ChangeIntentComponent` is an intermediate step in the flow.
- */
-predicate hasIntentRedirectionFlowPath(DataFlow::PathNode source, DataFlow::PathNode sink) {
- exists(IntentRedirectionConfiguration conf, DataFlow::PathNode intermediate |
- intermediate.getNode().(ChangeIntentComponent).hasFlowFrom(source.getNode()) and
- conf.hasFlowPath(intermediate, sink)
- )
-}
-
/**
* A taint tracking configuration for tainted Intents being used to start Android components.
*/
-private class IntentRedirectionConfiguration extends TaintTracking::Configuration {
+class IntentRedirectionConfiguration extends TaintTracking::Configuration {
IntentRedirectionConfiguration() { this = "IntentRedirectionConfiguration" }
- override predicate isSource(DataFlow::Node source) {
- exists(ChangeIntentComponent c |
- c = source
- or
- // This is needed for PathGraph to be able to build the full flow
- c.hasFlowFrom(source)
- )
- }
+ override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof IntentRedirectionSink }
@@ -44,17 +26,48 @@ private class IntentRedirectionConfiguration extends TaintTracking::Configuratio
}
}
-/** An expression modifying an `Intent` component with tainted data. */
-private class ChangeIntentComponent extends DataFlow::Node {
- DataFlow::Node src;
+/**
+ * A sanitizer for sinks that receive the original incoming Intent,
+ * since its component cannot be arbitrarily set.
+ */
+private class OriginalIntentSanitizer extends IntentRedirectionSanitizer {
+ OriginalIntentSanitizer() { any(SameIntentBeingRelaunchedConfiguration c).hasFlowTo(this) }
+}
- ChangeIntentComponent() {
- changesIntentComponent(this.asExpr()) and
- exists(TaintedIntentComponentConf conf | conf.hasFlow(src, this))
+/**
+ * Data flow configuration used to discard incoming Intents
+ * flowing directly to sinks that start Android components.
+ */
+private class SameIntentBeingRelaunchedConfiguration extends DataFlow3::Configuration {
+ SameIntentBeingRelaunchedConfiguration() { this = "SameIntentBeingRelaunchedConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof IntentRedirectionSink }
+
+ override predicate isBarrier(DataFlow::Node barrier) {
+ // Don't discard the Intent if its original component is tainted
+ barrier instanceof IntentWithTaintedComponent
}
- /** Holds if `source` flows to `this`. */
- predicate hasFlowFrom(DataFlow::Node source) { source = src }
+ override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
+ // Intents being built with the copy constructor from the original Intent are discarded too
+ exists(ClassInstanceExpr cie |
+ cie.getConstructedType() instanceof TypeIntent and
+ node1.asExpr() = cie.getArgument(0) and
+ node2.asExpr() = cie
+ )
+ }
+}
+
+/** An `Intent` with a tainted component. */
+private class IntentWithTaintedComponent extends DataFlow::Node {
+ IntentWithTaintedComponent() {
+ exists(IntentSetComponent setExpr, TaintedIntentComponentConf conf |
+ setExpr.getQualifier() = this.asExpr() and
+ conf.hasFlowTo(DataFlow::exprNode(setExpr.getSink()))
+ )
+ }
}
/**
@@ -65,25 +78,8 @@ private class TaintedIntentComponentConf extends TaintTracking2::Configuration {
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
- override predicate isSink(DataFlow::Node sink) { changesIntentComponent(sink.asExpr()) }
-}
-
-/** Holds if `expr` modifies the component of an `Intent`. */
-private predicate changesIntentComponent(Expr expr) {
- any(IntentSetComponent isc).getSink() = expr
- or
- // obtaining an arbitrary Intent as a Parcelable extra
- expr instanceof IntentGetParcelableExtra
-}
-
-/** A call to the method `Intent.getParcelableExtra`. */
-private class IntentGetParcelableExtra extends MethodAccess {
- IntentGetParcelableExtra() {
- exists(Method m |
- this.getMethod() = m and
- m.getDeclaringType() instanceof TypeIntent and
- m.hasName("getParcelableExtra")
- )
+ override predicate isSink(DataFlow::Node sink) {
+ any(IntentSetComponent setComponent).getSink() = sink.asExpr()
}
}
diff --git a/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.ql b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.ql
index 4bfdfa12738..03d99a6f211 100644
--- a/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.ql
+++ b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.ql
@@ -17,8 +17,8 @@ import java
import semmle.code.java.security.AndroidIntentRedirectionQuery
import DataFlow::PathGraph
-from DataFlow::PathNode source, DataFlow::PathNode sink
-where hasIntentRedirectionFlowPath(source, sink)
+from DataFlow::PathNode source, DataFlow::PathNode sink, IntentRedirectionConfiguration conf
+where conf.hasFlowPath(source, sink)
select sink.getNode(), source, sink,
"Arbitrary Android activities or services can be started from $@.", source.getNode(),
"this user input"
diff --git a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
index 12d68abf95a..1dc7b647714 100644
--- a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
+++ b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
@@ -55,6 +55,12 @@ public class AndroidIntentRedirectionTest extends Activity {
}
try {
+ {
+ // Delayed cast
+ Object obj = getIntent().getParcelableExtra("forward_intent");
+ Intent fwdIntent = (Intent) obj;
+ startActivity(fwdIntent); // $ hasAndroidIntentRedirection
+ }
{
Intent fwdIntent = new Intent();
fwdIntent.setClassName((Context) null, (String) intent.getExtra("className"));
@@ -132,11 +138,6 @@ public class AndroidIntentRedirectionTest extends Activity {
fwdIntent.setComponent(component);
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
}
- {
- Intent originalIntent = getIntent();
- Intent fwdIntent = (Intent) originalIntent.getParcelableExtra("forward_intent");
- startActivity(originalIntent); // Safe - not an Intent obtained from the Extras
- }
{
Intent originalIntent = getIntent();
ComponentName cp = new ComponentName(originalIntent.getStringExtra("packageName"),
@@ -146,10 +147,35 @@ public class AndroidIntentRedirectionTest extends Activity {
startActivity(originalIntent); // Safe - not a tainted Intent
}
{
- // Delayed cast
- Object obj = getIntent().getParcelableExtra("forward_intent");
- Intent fwdIntent = (Intent) obj;
- startActivity(fwdIntent); // $ hasAndroidIntentRedirection
+ Intent originalIntent = getIntent();
+ Intent fwdIntent = (Intent) originalIntent.getParcelableExtra("forward_intent");
+ if (originalIntent.getBooleanExtra("use_fwd_intent", false)) {
+ startActivity(fwdIntent); // $ hasAndroidIntentRedirection
+ } else {
+ startActivity(originalIntent); // Safe - not an Intent obtained from the Extras
+ }
+ }
+ {
+ Intent originalIntent = getIntent();
+ originalIntent.setClassName(originalIntent.getStringExtra("package_name"),
+ originalIntent.getStringExtra("class_name"));
+ startActivity(originalIntent); // $ hasAndroidIntentRedirection
+ }
+ {
+ Intent originalIntent = getIntent();
+ originalIntent.setClassName("not_user_provided", "not_user_provided");
+ startActivity(originalIntent); // Safe - component changed but not tainted
+ }
+ {
+ Intent originalIntent = getIntent();
+ Intent fwdIntent;
+ if (originalIntent.getBooleanExtra("use_fwd_intent", false)) {
+ fwdIntent = (Intent) originalIntent.getParcelableExtra("forward_intent");
+ } else {
+ fwdIntent = originalIntent;
+ }
+ // Conditionally tainted sinks aren't supported currently
+ startActivity(fwdIntent); // $ MISSING: $hasAndroidIntentRedirection
}
} catch (Exception e) {
}
diff --git a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.ql b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.ql
index f66a7995dfe..9e883b26498 100644
--- a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.ql
+++ b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.ql
@@ -9,10 +9,10 @@ class HasAndroidIntentRedirectionTest extends InlineExpectationsTest {
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasAndroidIntentRedirection" and
- exists(DataFlow::PathNode src, DataFlow::PathNode sink |
- hasIntentRedirectionFlowPath(src, sink)
+ exists(DataFlow::Node src, DataFlow::Node sink, IntentRedirectionConfiguration conf |
+ conf.hasFlow(src, sink)
|
- sink.getNode().getLocation() = location and
+ sink.getLocation() = location and
element = sink.toString() and
value = ""
)
From e7983fb26972f0ca3ae232bc11cdefb45aaca480 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Thu, 26 Aug 2021 13:51:06 +0200
Subject: [PATCH 074/471] Add test and check for another edge case
---
.../code/java/security/AndroidIntentRedirectionQuery.qll | 1 +
.../security/CWE-940/AndroidIntentRedirectionTest.java | 5 +++++
2 files changed, 6 insertions(+)
diff --git a/java/ql/lib/semmle/code/java/security/AndroidIntentRedirectionQuery.qll b/java/ql/lib/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
index 9173680a778..7d1235393ff 100644
--- a/java/ql/lib/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/AndroidIntentRedirectionQuery.qll
@@ -55,6 +55,7 @@ private class SameIntentBeingRelaunchedConfiguration extends DataFlow3::Configur
exists(ClassInstanceExpr cie |
cie.getConstructedType() instanceof TypeIntent and
node1.asExpr() = cie.getArgument(0) and
+ node1.asExpr().getType() instanceof TypeIntent and
node2.asExpr() = cie
)
}
diff --git a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
index 1dc7b647714..860b3e3716b 100644
--- a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
+++ b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
@@ -146,6 +146,11 @@ public class AndroidIntentRedirectionTest extends Activity {
anotherIntent.setComponent(cp);
startActivity(originalIntent); // Safe - not a tainted Intent
}
+ {
+ Intent originalIntent = getIntent();
+ Intent anotherIntent = new Intent(originalIntent);
+ startActivity(anotherIntent); // Safe - copy constructor from original Intent
+ }
{
Intent originalIntent = getIntent();
Intent fwdIntent = (Intent) originalIntent.getParcelableExtra("forward_intent");
From 28ae4c211f30caf755cd8c3c8d77fa080fc6c666 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Tue, 31 Aug 2021 14:32:31 +0200
Subject: [PATCH 075/471] Update
java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp
Co-authored-by: Ethan Palm <56270045+ethanpalm@users.noreply.github.com>
---
java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp
index 47ba77b6e25..6c275cc01e9 100644
--- a/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp
+++ b/java/ql/src/Security/CWE/CWE-940/AndroidIntentRedirection.qhelp
@@ -11,7 +11,7 @@
If this is not possible, restrict either which apps can send Intents to the affected component, or which components can be started from it.
- The following snippet contains two examples.
+
The following snippet contains three examples.
In the first example, an arbitrary component can be started from the externally provided forward_intent Intent.
In the second example, the destination component of the Intent is first checked to make sure it is safe.
In the third example, the component that created the Intent is first checked to make sure it comes from a trusted origin.
From d1d2d61d7e1825fbed087ca61fc7d956aa8372f7 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Mon, 18 Oct 2021 12:00:07 +0200
Subject: [PATCH 076/471] Add more sinks
Also, fix things after rebase
---
.../code/java/dataflow/ExternalFlow.qll | 1 +
.../code/java/frameworks/android/Intent.qll | 37 +++++++++++++++++++
.../security/AndroidIntentRedirection.qll | 3 ++
.../CWE-940/AndroidIntentRedirectionTest.java | 35 ++++++++----------
.../android/app/Activity.java | 7 +---
.../android/app/Fragment.java | 4 ++
.../android/content/Context.java | 3 ++
.../android/content/ContextWrapper.java | 3 ++
8 files changed, 68 insertions(+), 25 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
index cfbcd16f75e..a03b39af7a2 100644
--- a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
@@ -104,6 +104,7 @@ private module Frameworks {
private import semmle.code.java.frameworks.spring.SpringBeans
private import semmle.code.java.frameworks.spring.SpringWebMultipart
private import semmle.code.java.frameworks.spring.SpringWebUtil
+ private import semmle.code.java.security.AndroidIntentRedirection
private import semmle.code.java.security.ResponseSplitting
private import semmle.code.java.security.InformationLeak
private import semmle.code.java.security.GroovyInjection
diff --git a/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll b/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll
index 3798d91d99a..57aa25775e9 100644
--- a/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll
+++ b/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll
@@ -10,6 +10,11 @@ class TypeIntent extends Class {
TypeIntent() { hasQualifiedName("android.content", "Intent") }
}
+/** The class `android.content.ComponentName`. */
+class TypeComponentName extends Class {
+ TypeComponentName() { this.hasQualifiedName("android.content", "ComponentName") }
+}
+
/**
* The class `android.app.Activity`.
*/
@@ -236,3 +241,35 @@ private class IntentBundleFlowSteps extends SummaryModelCsv {
]
}
}
+
+private class IntentComponentTaintSteps extends SummaryModelCsv {
+ override predicate row(string s) {
+ s =
+ [
+ "android.content;Intent;true;Intent;(Intent);;Argument[0];Argument[-1];taint",
+ "android.content;Intent;true;Intent;(Context,Class);;Argument[1];Argument[-1];taint",
+ "android.content;Intent;true;Intent;(String,Uri,Context,Class);;Argument[3];Argument[-1];taint",
+ "android.content;Intent;true;setPackage;;;Argument[0];Argument[-1];taint",
+ "android.content;Intent;true;setPackage;;;Argument[-1];ReturnValue;taint",
+ "android.content;Intent;true;setClass;;;Argument[1];Argument[-1];taint",
+ "android.content;Intent;true;setClass;;;Argument[-1];ReturnValue;taint",
+ "android.content;Intent;true;setClassName;(Context,String);;Argument[1];Argument[-1];taint",
+ "android.content;Intent;true;setClassName;(String,String);;Argument[0..1];Argument[-1];taint",
+ "android.content;Intent;true;setClassName;;;Argument[-1];ReturnValue;taint",
+ "android.content;Intent;true;setComponent;;;Argument[0];Argument[-1];taint",
+ "android.content;Intent;true;setComponent;;;Argument[-1];ReturnValue;taint",
+ "android.content;ComponentName;false;ComponentName;(String,String);;Argument[0..1];Argument[-1];taint",
+ "android.content;ComponentName;false;ComponentName;(Context,String);;Argument[1];Argument[-1];taint",
+ "android.content;ComponentName;false;ComponentName;(Context,Class);;Argument[1];Argument[-1];taint",
+ "android.content;ComponentName;false;ComponentName;(Parcel);;Argument[0];Argument[-1];taint",
+ "android.content;ComponentName;false;createRelative;(String,String);;Argument[0..1];ReturnValue;taint",
+ "android.content;ComponentName;false;createRelative;(Context,String);;Argument[1];ReturnValue;taint",
+ "android.content;ComponentName;false;flattenToShortString;;;Argument[-1];ReturnValue;taint",
+ "android.content;ComponentName;false;flattenToString;;;Argument[-1];ReturnValue;taint",
+ "android.content;ComponentName;false;getClassName;;;Argument[-1];ReturnValue;taint",
+ "android.content;ComponentName;false;getPackageName;;;Argument[-1];ReturnValue;taint",
+ "android.content;ComponentName;false;getShortClassName;;;Argument[-1];ReturnValue;taint",
+ "android.content;ComponentName;false;unflattenFromString;;;Argument[0];ReturnValue;taint"
+ ]
+ }
+}
diff --git a/java/ql/lib/semmle/code/java/security/AndroidIntentRedirection.qll b/java/ql/lib/semmle/code/java/security/AndroidIntentRedirection.qll
index 90d513fbcc1..641c3489c04 100644
--- a/java/ql/lib/semmle/code/java/security/AndroidIntentRedirection.qll
+++ b/java/ql/lib/semmle/code/java/security/AndroidIntentRedirection.qll
@@ -32,6 +32,8 @@ private class DefaultIntentRedirectionSinkModel extends SinkModelCsv {
override predicate row(string row) {
row =
[
+ "android.app;Activity;true;bindService;;;Argument[0];intent-start",
+ "android.app;Activity;true;bindServiceAsUser;;;Argument[0];intent-start",
"android.app;Activity;true;startActivityAsCaller;;;Argument[0];intent-start",
"android.app;Activity;true;startActivityForResult;(Intent,int);;Argument[0];intent-start",
"android.app;Activity;true;startActivityForResult;(Intent,int,Bundle);;Argument[0];intent-start",
@@ -43,6 +45,7 @@ private class DefaultIntentRedirectionSinkModel extends SinkModelCsv {
"android.content;Context;true;startActivityFromChild;;;Argument[1];intent-start",
"android.content;Context;true;startActivityFromFragment;;;Argument[1];intent-start",
"android.content;Context;true;startActivityIfNeeded;;;Argument[0];intent-start",
+ "android.content;Context;true;startForegroundService;;;Argument[0];intent-start",
"android.content;Context;true;startService;;;Argument[0];intent-start",
"android.content;Context;true;startServiceAsUser;;;Argument[0];intent-start",
"android.content;Context;true;sendBroadcast;;;Argument[0];intent-start",
diff --git a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
index 860b3e3716b..fd41fe227fb 100644
--- a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
+++ b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
@@ -17,7 +17,6 @@ public class AndroidIntentRedirectionTest extends Activity {
startActivity(intent); // $ hasAndroidIntentRedirection
startActivity(intent, null); // $ hasAndroidIntentRedirection
startActivityAsUser(intent, null); // $ hasAndroidIntentRedirection
- startActivityAsUser(intent, null, null); // $ hasAndroidIntentRedirection
startActivityAsCaller(intent, null, false, 0); // $ hasAndroidIntentRedirection
startActivityForResult(intent, 0); // $ hasAndroidIntentRedirection
startActivityForResult(intent, 0, null); // $ hasAndroidIntentRedirection
@@ -25,20 +24,18 @@ public class AndroidIntentRedirectionTest extends Activity {
startActivityForResultAsUser(intent, null, 0, null, null); // $ hasAndroidIntentRedirection
startActivityForResultAsUser(intent, 0, null, null); // $ hasAndroidIntentRedirection
startActivityForResultAsUser(intent, 0, null); // $ hasAndroidIntentRedirection
+ bindService(intent, null, 0);
+ bindServiceAsUser(intent, null, 0, null);
startService(intent); // $ hasAndroidIntentRedirection
startServiceAsUser(intent, null); // $ hasAndroidIntentRedirection
+ startForegroundService(intent); // $ hasAndroidIntentRedirection
sendBroadcast(intent); // $ hasAndroidIntentRedirection
sendBroadcast(intent, null); // $ hasAndroidIntentRedirection
- sendBroadcast(intent, null, null); // $ hasAndroidIntentRedirection
- sendBroadcast(intent, null, 0); // $ hasAndroidIntentRedirection
sendBroadcastAsUser(intent, null); // $ hasAndroidIntentRedirection
sendBroadcastAsUser(intent, null, null); // $ hasAndroidIntentRedirection
- sendBroadcastAsUser(intent, null, null, null); // $ hasAndroidIntentRedirection
- sendBroadcastAsUser(intent, null, null, 0); // $ hasAndroidIntentRedirection
sendBroadcastWithMultiplePermissions(intent, null); // $ hasAndroidIntentRedirection
sendStickyBroadcast(intent); // $ hasAndroidIntentRedirection
sendStickyBroadcastAsUser(intent, null); // $ hasAndroidIntentRedirection
- sendStickyBroadcastAsUser(intent, null, null); // $ hasAndroidIntentRedirection
sendStickyOrderedBroadcast(intent, null, null, 0, null, null); // $ hasAndroidIntentRedirection
sendStickyOrderedBroadcastAsUser(intent, null, null, null, 0, null, null); // $ hasAndroidIntentRedirection
// @formatter:on
@@ -63,56 +60,56 @@ public class AndroidIntentRedirectionTest extends Activity {
}
{
Intent fwdIntent = new Intent();
- fwdIntent.setClassName((Context) null, (String) intent.getExtra("className"));
+ fwdIntent.setClassName((Context) null, intent.getStringExtra("className"));
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
}
{
Intent fwdIntent = new Intent();
- fwdIntent.setClassName((String) intent.getExtra("packageName"), null);
+ fwdIntent.setClassName(intent.getStringExtra("packageName"), null);
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
}
{
Intent fwdIntent = new Intent();
- fwdIntent.setClassName((String) intent.getExtra("packageName"),
- (String) intent.getExtra("className"));
+ fwdIntent.setClassName(intent.getStringExtra("packageName"),
+ intent.getStringExtra("className"));
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
}
{
Intent fwdIntent = new Intent();
- fwdIntent.setClass(null, Class.forName((String) intent.getExtra("className")));
+ fwdIntent.setClass(null, Class.forName(intent.getStringExtra("className")));
// needs taint step for Class.forName
startActivity(fwdIntent); // $ MISSING: $hasAndroidIntentRedirection
}
{
Intent fwdIntent = new Intent();
- fwdIntent.setPackage((String) intent.getExtra("packageName"));
+ fwdIntent.setPackage(intent.getStringExtra("packageName"));
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
}
{
Intent fwdIntent = new Intent();
ComponentName component =
- new ComponentName((String) intent.getExtra("packageName"), null);
+ new ComponentName(intent.getStringExtra("packageName"), null);
fwdIntent.setComponent(component);
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
}
{
Intent fwdIntent = new Intent();
ComponentName component =
- new ComponentName("", (String) intent.getExtra("className"));
+ new ComponentName("", intent.getStringExtra("className"));
fwdIntent.setComponent(component);
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
}
{
Intent fwdIntent = new Intent();
ComponentName component =
- new ComponentName((Context) null, (String) intent.getExtra("className"));
+ new ComponentName((Context) null, intent.getStringExtra("className"));
fwdIntent.setComponent(component);
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
}
{
Intent fwdIntent = new Intent();
ComponentName component = new ComponentName((Context) null,
- Class.forName((String) intent.getExtra("className")));
+ Class.forName(intent.getStringExtra("className")));
fwdIntent.setComponent(component);
// needs taint step for Class.forName
startActivity(fwdIntent); // $ MISSING: $hasAndroidIntentRedirection
@@ -120,21 +117,21 @@ public class AndroidIntentRedirectionTest extends Activity {
{
Intent fwdIntent = new Intent();
ComponentName component =
- ComponentName.createRelative("", (String) intent.getExtra("className"));
+ ComponentName.createRelative("", intent.getStringExtra("className"));
fwdIntent.setComponent(component);
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
}
{
Intent fwdIntent = new Intent();
ComponentName component =
- ComponentName.createRelative((String) intent.getExtra("packageName"), "");
+ ComponentName.createRelative(intent.getStringExtra("packageName"), "");
fwdIntent.setComponent(component);
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
}
{
Intent fwdIntent = new Intent();
ComponentName component = ComponentName.createRelative((Context) null,
- (String) intent.getExtra("className"));
+ intent.getStringExtra("className"));
fwdIntent.setComponent(component);
startActivity(fwdIntent); // $ hasAndroidIntentRedirection
}
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java b/java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java
index a34e7907c38..9b13ab192ac 100644
--- a/java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java
+++ b/java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java
@@ -51,11 +51,6 @@ public class Activity extends ContextWrapper {
public void onStateNotSaved() {}
- @Override
- public int getNextAutofillId() {
- return 0;
- }
-
public boolean isVoiceInteraction() {
return false;
}
@@ -373,7 +368,7 @@ public class Activity extends ContextWrapper {
}
@Override
- public Object getSystemService(@ServiceName @NonNull String name) {
+ public Object getSystemService(@NonNull String name) {
return null;
}
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/Fragment.java b/java/ql/test/stubs/google-android-9.0.0/android/app/Fragment.java
index 18b2a846d05..d327aea80ef 100644
--- a/java/ql/test/stubs/google-android-9.0.0/android/app/Fragment.java
+++ b/java/ql/test/stubs/google-android-9.0.0/android/app/Fragment.java
@@ -28,6 +28,10 @@ public class Fragment {
@Override
public void writeToParcel(Parcel dest, int flags) {}
+ @Override
+ public int describeContents() {
+ return 0;
+ }
}
static public class InstantiationException {
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java b/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java
index b5443430446..6e5678022a5 100644
--- a/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java
+++ b/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java
@@ -36,6 +36,7 @@ abstract public class Context
public abstract ClassLoader getClassLoader();
public abstract ComponentName startForegroundService(Intent p0);
public abstract ComponentName startService(Intent p0);
+ public abstract ComponentName startServiceAsUser(Intent p0, UserHandle p1);
public abstract ContentResolver getContentResolver();
public abstract Context createConfigurationContext(Configuration p0);
public abstract Context createContextForSplit(String p0);
@@ -81,6 +82,7 @@ abstract public class Context
public abstract String[] databaseList();
public abstract String[] fileList();
public abstract boolean bindService(Intent p0, ServiceConnection p1, int p2);
+ public abstract boolean bindServiceAsUser(Intent p0, ServiceConnection p1, int p2, UserHandle p3);
public abstract boolean deleteDatabase(String p0);
public abstract boolean deleteFile(String p0);
public abstract boolean deleteSharedPreferences(String p0);
@@ -132,6 +134,7 @@ abstract public class Context
public abstract void startActivities(Intent[] p0, Bundle p1);
public abstract void startActivity(Intent p0);
public abstract void startActivity(Intent p0, Bundle p1);
+ public abstract void startActivityAsUser(Intent intent, UserHandle user);
public abstract void startIntentSender(IntentSender p0, Intent p1, int p2, int p3, int p4);
public abstract void startIntentSender(IntentSender p0, Intent p1, int p2, int p3, int p4, Bundle p5);
public abstract void unbindService(ServiceConnection p0);
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/ContextWrapper.java b/java/ql/test/stubs/google-android-9.0.0/android/content/ContextWrapper.java
index 58b74144752..80c1a73e993 100644
--- a/java/ql/test/stubs/google-android-9.0.0/android/content/ContextWrapper.java
+++ b/java/ql/test/stubs/google-android-9.0.0/android/content/ContextWrapper.java
@@ -71,6 +71,7 @@ public class ContextWrapper extends Context {
@Override public ClassLoader getClassLoader() { return null; }
@Override public ComponentName startForegroundService(Intent p0) { return null; }
@Override public ComponentName startService(Intent p0) { return null; }
+ @Override public ComponentName startServiceAsUser(Intent p0, UserHandle p1) { return null; }
@Override public ContentResolver getContentResolver() { return null; }
@Override public Context createConfigurationContext(Configuration p0) { return null; }
@Override public Context createContextForSplit(String p0) { return null; }
@@ -116,6 +117,7 @@ public class ContextWrapper extends Context {
@Override public String[] databaseList() { return null; }
@Override public String[] fileList() { return null; }
@Override public boolean bindService(Intent p0, ServiceConnection p1, int p2) { return false; }
+ @Override public boolean bindServiceAsUser(Intent p0, ServiceConnection p1, int p2, UserHandle p3) { return false; }
@Override public boolean deleteDatabase(String p0) { return false; }
@Override public boolean deleteFile(String p0) { return false; }
@Override public boolean deleteSharedPreferences(String p0) { return false; }
@@ -167,6 +169,7 @@ public class ContextWrapper extends Context {
@Override public void startActivities(Intent[] p0, Bundle p1) { }
@Override public void startActivity(Intent p0) { }
@Override public void startActivity(Intent p0, Bundle p1) { }
+ @Override public void startActivityAsUser(Intent p0, UserHandle p1) { }
@Override public void startIntentSender(IntentSender p0, Intent p1, int p2, int p3, int p4) { }
@Override public void startIntentSender(IntentSender p0, Intent p1, int p2, int p3, int p4, Bundle p5) { }
@Override public void unbindService(ServiceConnection p0) { }
From 392e2eebebb7168be7a42661c7006436b4316eb6 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Mon, 18 Oct 2021 12:18:07 +0200
Subject: [PATCH 077/471] Add intent creation from a URI as a taint step
---
.../semmle/code/java/frameworks/android/Intent.qll | 3 +++
.../CWE-940/AndroidIntentRedirectionTest.java | 12 ++++++++++++
2 files changed, 15 insertions(+)
diff --git a/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll b/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll
index 57aa25775e9..31c57a6dd5f 100644
--- a/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll
+++ b/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll
@@ -249,6 +249,9 @@ private class IntentComponentTaintSteps extends SummaryModelCsv {
"android.content;Intent;true;Intent;(Intent);;Argument[0];Argument[-1];taint",
"android.content;Intent;true;Intent;(Context,Class);;Argument[1];Argument[-1];taint",
"android.content;Intent;true;Intent;(String,Uri,Context,Class);;Argument[3];Argument[-1];taint",
+ "android.content;Intent;true;getIntent;(String);;Argument[0];ReturnValue;taint",
+ "android.content;Intent;true;getIntentOld;(String);;Argument[0];ReturnValue;taint",
+ "android.content;Intent;true;parseUri;(String,int);;Argument[0];ReturnValue;taint",
"android.content;Intent;true;setPackage;;;Argument[0];Argument[-1];taint",
"android.content;Intent;true;setPackage;;;Argument[-1];ReturnValue;taint",
"android.content;Intent;true;setClass;;;Argument[1];Argument[-1];taint",
diff --git a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
index fd41fe227fb..c577ca96620 100644
--- a/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
+++ b/java/ql/test/query-tests/security/CWE-940/AndroidIntentRedirectionTest.java
@@ -179,6 +179,18 @@ public class AndroidIntentRedirectionTest extends Activity {
// Conditionally tainted sinks aren't supported currently
startActivity(fwdIntent); // $ MISSING: $hasAndroidIntentRedirection
}
+ {
+ Intent fwdIntent = Intent.parseUri(getIntent().getStringExtra("uri"), 0);
+ startActivity(fwdIntent); // $ hasAndroidIntentRedirection
+ }
+ {
+ Intent fwdIntent = Intent.getIntent(getIntent().getStringExtra("uri"));
+ startActivity(fwdIntent); // $ hasAndroidIntentRedirection
+ }
+ {
+ Intent fwdIntent = Intent.getIntentOld(getIntent().getStringExtra("uri"));
+ startActivity(fwdIntent); // $ hasAndroidIntentRedirection
+ }
} catch (Exception e) {
}
}
From a5749a5eb14fca38948dcc9ccd3362b815e6094c Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Mon, 18 Oct 2021 15:13:09 +0200
Subject: [PATCH 078/471] Add ComponentName tests to existing Intent tests
---
.../code/java/frameworks/android/Intent.qll | 4 -
.../dataflow/taintsources/IntentSources.java | 4 -
.../frameworks/android/intent/Test.java | 162 +++++++++++++
.../frameworks/android/intents/Test.java | 218 ------------------
.../frameworks/android/intents/options | 1 -
.../frameworks/android/intents/test.expected | 0
.../frameworks/android/intents/test.ql | 53 -----
.../android/app/Activity.java | 3 +
8 files changed, 165 insertions(+), 280 deletions(-)
delete mode 100644 java/ql/test/library-tests/frameworks/android/intents/Test.java
delete mode 100644 java/ql/test/library-tests/frameworks/android/intents/options
delete mode 100644 java/ql/test/library-tests/frameworks/android/intents/test.expected
delete mode 100644 java/ql/test/library-tests/frameworks/android/intents/test.ql
diff --git a/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll b/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll
index 31c57a6dd5f..4689b5e620a 100644
--- a/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll
+++ b/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll
@@ -253,14 +253,10 @@ private class IntentComponentTaintSteps extends SummaryModelCsv {
"android.content;Intent;true;getIntentOld;(String);;Argument[0];ReturnValue;taint",
"android.content;Intent;true;parseUri;(String,int);;Argument[0];ReturnValue;taint",
"android.content;Intent;true;setPackage;;;Argument[0];Argument[-1];taint",
- "android.content;Intent;true;setPackage;;;Argument[-1];ReturnValue;taint",
"android.content;Intent;true;setClass;;;Argument[1];Argument[-1];taint",
- "android.content;Intent;true;setClass;;;Argument[-1];ReturnValue;taint",
"android.content;Intent;true;setClassName;(Context,String);;Argument[1];Argument[-1];taint",
"android.content;Intent;true;setClassName;(String,String);;Argument[0..1];Argument[-1];taint",
- "android.content;Intent;true;setClassName;;;Argument[-1];ReturnValue;taint",
"android.content;Intent;true;setComponent;;;Argument[0];Argument[-1];taint",
- "android.content;Intent;true;setComponent;;;Argument[-1];ReturnValue;taint",
"android.content;ComponentName;false;ComponentName;(String,String);;Argument[0..1];Argument[-1];taint",
"android.content;ComponentName;false;ComponentName;(Context,String);;Argument[1];Argument[-1];taint",
"android.content;ComponentName;false;ComponentName;(Context,Class);;Argument[1];Argument[-1];taint",
diff --git a/java/ql/test/library-tests/dataflow/taintsources/IntentSources.java b/java/ql/test/library-tests/dataflow/taintsources/IntentSources.java
index 042ba1fbe98..4753afb7405 100644
--- a/java/ql/test/library-tests/dataflow/taintsources/IntentSources.java
+++ b/java/ql/test/library-tests/dataflow/taintsources/IntentSources.java
@@ -6,10 +6,6 @@ public class IntentSources extends Activity {
private static void sink(Object o) {}
- public IntentSources(Context base) {
- super(base);
- }
-
public void test() throws java.io.IOException {
String trouble = this.getIntent().getStringExtra("key");
diff --git a/java/ql/test/library-tests/frameworks/android/intent/Test.java b/java/ql/test/library-tests/frameworks/android/intent/Test.java
index 9172c4c19ad..365caa8ddeb 100644
--- a/java/ql/test/library-tests/frameworks/android/intent/Test.java
+++ b/java/ql/test/library-tests/frameworks/android/intent/Test.java
@@ -1,5 +1,6 @@
package generatedtest;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
@@ -1597,6 +1598,167 @@ public class Test {
out.readFromParcel(in);
sink(getMapValue(out)); // $ hasTaintFlow
}
+ {
+ // "android.content;ComponentName;false;ComponentName;(Context,Class);;Argument[1];Argument[-1];taint"
+ ComponentName out = null;
+ Class in = (Class) source();
+ out = new ComponentName((Context) null, in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;ComponentName;(Context,String);;Argument[1];Argument[-1];taint"
+ ComponentName out = null;
+ String in = (String) source();
+ out = new ComponentName((Context) null, in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;ComponentName;(Parcel);;Argument[0];Argument[-1];taint"
+ ComponentName out = null;
+ Parcel in = (Parcel) source();
+ out = new ComponentName(in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;ComponentName;(String,String);;Argument[0..1];Argument[-1];taint"
+ ComponentName out = null;
+ String in = (String) source();
+ out = new ComponentName(in, (String) null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;ComponentName;(String,String);;Argument[0..1];Argument[-1];taint"
+ ComponentName out = null;
+ String in = (String) source();
+ out = new ComponentName((String) null, in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;createRelative;(Context,String);;Argument[1];ReturnValue;taint"
+ ComponentName out = null;
+ String in = (String) source();
+ out = ComponentName.createRelative((Context) null, in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;createRelative;(String,String);;Argument[0..1];ReturnValue;taint"
+ ComponentName out = null;
+ String in = (String) source();
+ out = ComponentName.createRelative(in, (String) null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;createRelative;(String,String);;Argument[0..1];ReturnValue;taint"
+ ComponentName out = null;
+ String in = (String) source();
+ out = ComponentName.createRelative((String) null, in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;flattenToShortString;;;Argument[-1];ReturnValue;taint"
+ String out = null;
+ ComponentName in = (ComponentName) source();
+ out = in.flattenToShortString();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;flattenToString;;;Argument[-1];ReturnValue;taint"
+ String out = null;
+ ComponentName in = (ComponentName) source();
+ out = in.flattenToString();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;getClassName;;;Argument[-1];ReturnValue;taint"
+ String out = null;
+ ComponentName in = (ComponentName) source();
+ out = in.getClassName();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;getPackageName;;;Argument[-1];ReturnValue;taint"
+ String out = null;
+ ComponentName in = (ComponentName) source();
+ out = in.getPackageName();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;getShortClassName;;;Argument[-1];ReturnValue;taint"
+ String out = null;
+ ComponentName in = (ComponentName) source();
+ out = in.getShortClassName();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;ComponentName;false;unflattenFromString;;;Argument[0];ReturnValue;taint"
+ ComponentName out = null;
+ String in = (String) source();
+ out = ComponentName.unflattenFromString(in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;Intent;true;Intent;(Context,Class);;Argument[1];Argument[-1];taint"
+ Intent out = null;
+ Class in = (Class) source();
+ out = new Intent((Context) null, in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;Intent;true;Intent;(Intent);;Argument[0];Argument[-1];taint"
+ Intent out = null;
+ Intent in = (Intent) source();
+ out = new Intent(in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;Intent;true;Intent;(String,Uri,Context,Class);;Argument[3];Argument[-1];taint"
+ Intent out = null;
+ Class in = (Class) source();
+ out = new Intent(null, null, null, in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;Intent;true;setClass;;;Argument[1];Argument[-1];taint"
+ Intent out = null;
+ Class in = (Class) source();
+ out.setClass(null, in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;Intent;true;setClassName;(Context,String);;Argument[1];Argument[-1];taint"
+ Intent out = null;
+ String in = (String) source();
+ out.setClassName((Context) null, in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;Intent;true;setClassName;(String,String);;Argument[0..1];Argument[-1];taint"
+ Intent out = null;
+ String in = (String) source();
+ out.setClassName(in, (String) null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;Intent;true;setClassName;(String,String);;Argument[0..1];Argument[-1];taint"
+ Intent out = null;
+ String in = (String) source();
+ out.setClassName((String) null, in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;Intent;true;setComponent;;;Argument[0];Argument[-1];taint"
+ Intent out = null;
+ ComponentName in = (ComponentName) source();
+ out.setComponent(in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "android.content;Intent;true;setPackage;;;Argument[0];Argument[-1];taint"
+ Intent out = null;
+ String in = (String) source();
+ out.setPackage(in);
+ sink(out); // $ hasTaintFlow
+ }
}
diff --git a/java/ql/test/library-tests/frameworks/android/intents/Test.java b/java/ql/test/library-tests/frameworks/android/intents/Test.java
deleted file mode 100644
index 0ff36933eb3..00000000000
--- a/java/ql/test/library-tests/frameworks/android/intents/Test.java
+++ /dev/null
@@ -1,218 +0,0 @@
-package generatedtest;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Parcel;
-
-// Test case generated by GenerateFlowTestCase.ql
-public class Test {
-
- Object source() {
- return null;
- }
-
- void sink(Object o) {}
-
- public void test() throws Exception {
-
- {
- // "android.content;ComponentName;false;ComponentName;(Context,Class);;Argument[1];Argument[-1];taint"
- ComponentName out = null;
- Class in = (Class) source();
- out = new ComponentName((Context) null, in);
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;ComponentName;false;ComponentName;(Context,String);;Argument[1];Argument[-1];taint"
- ComponentName out = null;
- String in = (String) source();
- out = new ComponentName((Context) null, in);
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;ComponentName;false;ComponentName;(Parcel);;Argument[0];Argument[-1];taint"
- ComponentName out = null;
- Parcel in = (Parcel) source();
- out = new ComponentName(in);
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;ComponentName;false;ComponentName;(String,String);;Argument[0..1];Argument[-1];taint"
- ComponentName out = null;
- String in = (String) source();
- out = new ComponentName(in, (String) null);
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;ComponentName;false;ComponentName;(String,String);;Argument[0..1];Argument[-1];taint"
- ComponentName out = null;
- String in = (String) source();
- out = new ComponentName((String) null, in);
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;ComponentName;false;createRelative;(Context,String);;Argument[1];ReturnValue;taint"
- ComponentName out = null;
- String in = (String) source();
- out = ComponentName.createRelative((Context) null, in);
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;ComponentName;false;createRelative;(String,String);;Argument[0..1];ReturnValue;taint"
- ComponentName out = null;
- String in = (String) source();
- out = ComponentName.createRelative(in, (String) null);
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;ComponentName;false;createRelative;(String,String);;Argument[0..1];ReturnValue;taint"
- ComponentName out = null;
- String in = (String) source();
- out = ComponentName.createRelative((String) null, in);
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;ComponentName;false;flattenToShortString;;;Argument[-1];ReturnValue;taint"
- String out = null;
- ComponentName in = (ComponentName) source();
- out = in.flattenToShortString();
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;ComponentName;false;flattenToString;;;Argument[-1];ReturnValue;taint"
- String out = null;
- ComponentName in = (ComponentName) source();
- out = in.flattenToString();
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;ComponentName;false;getClassName;;;Argument[-1];ReturnValue;taint"
- String out = null;
- ComponentName in = (ComponentName) source();
- out = in.getClassName();
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;ComponentName;false;getPackageName;;;Argument[-1];ReturnValue;taint"
- String out = null;
- ComponentName in = (ComponentName) source();
- out = in.getPackageName();
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;ComponentName;false;getShortClassName;;;Argument[-1];ReturnValue;taint"
- String out = null;
- ComponentName in = (ComponentName) source();
- out = in.getShortClassName();
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;ComponentName;false;unflattenFromString;;;Argument[0];ReturnValue;taint"
- ComponentName out = null;
- String in = (String) source();
- out = ComponentName.unflattenFromString(in);
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;Intent;true;Intent;(Context,Class);;Argument[1];Argument[-1];taint"
- Intent out = null;
- Class in = (Class) source();
- out = new Intent((Context) null, in);
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;Intent;true;Intent;(Intent);;Argument[0];Argument[-1];taint"
- Intent out = null;
- Intent in = (Intent) source();
- out = new Intent(in);
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;Intent;true;Intent;(String,Uri,Context,Class);;Argument[3];Argument[-1];taint"
- Intent out = null;
- Class in = (Class) source();
- out = new Intent(null, null, null, in);
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;Intent;true;setClass;;;Argument[-1];ReturnValue;taint"
- Intent out = null;
- Intent in = (Intent) source();
- out = in.setClass(null, null);
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;Intent;true;setClass;;;Argument[1];Argument[-1];taint"
- Intent out = null;
- Class in = (Class) source();
- out.setClass(null, in);
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;Intent;true;setClassName;(Context,String);;Argument[1];Argument[-1];taint"
- Intent out = null;
- String in = (String) source();
- out.setClassName((Context) null, in);
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;Intent;true;setClassName;(String,String);;Argument[0..1];Argument[-1];taint"
- Intent out = null;
- String in = (String) source();
- out.setClassName(in, (String) null);
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;Intent;true;setClassName;(String,String);;Argument[0..1];Argument[-1];taint"
- Intent out = null;
- String in = (String) source();
- out.setClassName((String) null, in);
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;Intent;true;setClassName;;;Argument[-1];ReturnValue;taint"
- Intent out = null;
- Intent in = (Intent) source();
- out = in.setClassName((String) null, (String) null);
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;Intent;true;setClassName;;;Argument[-1];ReturnValue;taint"
- Intent out = null;
- Intent in = (Intent) source();
- out = in.setClassName((Context) null, (String) null);
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;Intent;true;setComponent;;;Argument[-1];ReturnValue;taint"
- Intent out = null;
- Intent in = (Intent) source();
- out = in.setComponent(null);
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;Intent;true;setComponent;;;Argument[0];Argument[-1];taint"
- Intent out = null;
- ComponentName in = (ComponentName) source();
- out.setComponent(in);
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;Intent;true;setPackage;;;Argument[-1];ReturnValue;taint"
- Intent out = null;
- Intent in = (Intent) source();
- out = in.setPackage(null);
- sink(out); // $ hasTaintFlow
- }
- {
- // "android.content;Intent;true;setPackage;;;Argument[0];Argument[-1];taint"
- Intent out = null;
- String in = (String) source();
- out.setPackage(in);
- sink(out); // $ hasTaintFlow
- }
-
- }
-
-}
diff --git a/java/ql/test/library-tests/frameworks/android/intents/options b/java/ql/test/library-tests/frameworks/android/intents/options
deleted file mode 100644
index 33cdc1ea940..00000000000
--- a/java/ql/test/library-tests/frameworks/android/intents/options
+++ /dev/null
@@ -1 +0,0 @@
-//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/google-android-9.0.0
diff --git a/java/ql/test/library-tests/frameworks/android/intents/test.expected b/java/ql/test/library-tests/frameworks/android/intents/test.expected
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/java/ql/test/library-tests/frameworks/android/intents/test.ql b/java/ql/test/library-tests/frameworks/android/intents/test.ql
deleted file mode 100644
index 465161863cc..00000000000
--- a/java/ql/test/library-tests/frameworks/android/intents/test.ql
+++ /dev/null
@@ -1,53 +0,0 @@
-import java
-import semmle.code.java.dataflow.DataFlow
-import semmle.code.java.dataflow.ExternalFlow
-import semmle.code.java.dataflow.TaintTracking
-import TestUtilities.InlineExpectationsTest
-
-class ValueFlowConf extends DataFlow::Configuration {
- ValueFlowConf() { this = "qltest:valueFlowConf" }
-
- override predicate isSource(DataFlow::Node n) {
- n.asExpr().(MethodAccess).getMethod().hasName("source")
- }
-
- override predicate isSink(DataFlow::Node n) {
- n.asExpr().(Argument).getCall().getCallee().hasName("sink")
- }
-}
-
-class TaintFlowConf extends TaintTracking::Configuration {
- TaintFlowConf() { this = "qltest:taintFlowConf" }
-
- override predicate isSource(DataFlow::Node n) {
- n.asExpr().(MethodAccess).getMethod().hasName("source")
- }
-
- override predicate isSink(DataFlow::Node n) {
- n.asExpr().(Argument).getCall().getCallee().hasName("sink")
- }
-}
-
-class HasFlowTest extends InlineExpectationsTest {
- HasFlowTest() { this = "HasFlowTest" }
-
- override string getARelevantTag() { result = ["hasValueFlow", "hasTaintFlow"] }
-
- override predicate hasActualResult(Location location, string element, string tag, string value) {
- tag = "hasValueFlow" and
- exists(DataFlow::Node src, DataFlow::Node sink, ValueFlowConf conf | conf.hasFlow(src, sink) |
- sink.getLocation() = location and
- element = sink.toString() and
- value = ""
- )
- or
- tag = "hasTaintFlow" and
- exists(DataFlow::Node src, DataFlow::Node sink, TaintFlowConf conf |
- conf.hasFlow(src, sink) and not any(ValueFlowConf c).hasFlow(src, sink)
- |
- sink.getLocation() = location and
- element = sink.toString() and
- value = ""
- )
- }
-}
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java b/java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java
index 9b13ab192ac..cad1d58cad5 100644
--- a/java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java
+++ b/java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java
@@ -28,6 +28,7 @@ import android.os.UserHandle;
import android.view.View;
public class Activity extends ContextWrapper {
+ public static final int RESULT_OK = -1;
public void onCreate(Bundle savedInstanceState) {}
@@ -347,6 +348,8 @@ public class Activity extends ContextWrapper {
public void onActivityReenter(int resultCode, Intent data) {}
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {}
+
public int getRequestedOrientation() {
return 0;
}
From e487832823238651bd4ff739e1be60deb8fc3208 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Tue, 19 Oct 2021 14:08:46 +0100
Subject: [PATCH 079/471] C++: Clean up QL.
---
cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll b/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
index 471cb176f95..636607e1eca 100644
--- a/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
+++ b/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
@@ -21,12 +21,10 @@ predicate mayAddNullTerminator(Expr e, VariableAccess va) {
)
or
// Assignment to another stack variable
- exists(Expr e0 |
- exists(StackVariable v0, Expr val |
- exprDefinition(v0, e, val) and // e resembles `v0 := val`
- val.getAChild*() = va and
- mayAddNullTerminator(e0, v0.getAnAccess())
- )
+ exists(StackVariable v0, Expr val |
+ exprDefinition(v0, e, val) and // e resembles `v0 := val`
+ val.getAChild*() = va and
+ mayAddNullTerminator(_, v0.getAnAccess())
)
or
// Assignment to non-stack variable
From b4b8392748f967a0d456c3d7d59918a9074383d2 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Tue, 19 Oct 2021 15:05:53 +0100
Subject: [PATCH 080/471] C++: New, behaviour preserving solution.
---
.../code/cpp/commons/NullTermination.qll | 23 +++++++++++++++----
1 file changed, 18 insertions(+), 5 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll b/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
index 636607e1eca..8dd2e035128 100644
--- a/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
+++ b/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
@@ -3,8 +3,17 @@ private import semmle.code.cpp.models.interfaces.ArrayFunction
private import semmle.code.cpp.models.implementations.Strcat
import semmle.code.cpp.dataflow.DataFlow
+private predicate mayAddNullTerminatorHelper(Expr e, VariableAccess va, Expr e0) {
+ exists(StackVariable v0, Expr val |
+ exprDefinition(v0, e, val) and
+ val.getAChild*() = va and
+ mayAddNullTerminator(e0, v0.getAnAccess())
+ )
+}
+
/**
- * Holds if the expression `e` may add a null terminator to the string in `va`.
+ * Holds if the expression `e` may add a null terminator to the string in
+ * variable `v`.
*/
predicate mayAddNullTerminator(Expr e, VariableAccess va) {
// Assignment: dereferencing or array access
@@ -21,10 +30,14 @@ predicate mayAddNullTerminator(Expr e, VariableAccess va) {
)
or
// Assignment to another stack variable
- exists(StackVariable v0, Expr val |
- exprDefinition(v0, e, val) and // e resembles `v0 := val`
- val.getAChild*() = va and
- mayAddNullTerminator(_, v0.getAnAccess())
+ exists(Expr e0, BasicBlock bb, int pos, BasicBlock bb0, int pos0 |
+ mayAddNullTerminatorHelper(pragma[only_bind_into](e), va, pragma[only_bind_into](e0)) and
+ pragma[only_bind_into](bb).getNode(pos) = e and
+ pragma[only_bind_into](bb0).getNode(pos0) = e0
+ |
+ bb = bb0 and pos < pos0
+ or
+ bb.getASuccessor+() = bb0
)
or
// Assignment to non-stack variable
From 57fe4b9a31637898ef708f3081f743511edc3eca Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Tue, 19 Oct 2021 16:48:06 +0100
Subject: [PATCH 081/471] C++: Also fix variableMustBeNullTerminated.
---
cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll b/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
index 8dd2e035128..5b18b5d1fc8 100644
--- a/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
+++ b/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
@@ -120,8 +120,8 @@ predicate variableMustBeNullTerminated(VariableAccess va) {
// Simplified: check that `p` may not be null terminated on *any*
// path to `use` (including the one found via `parameterUsePair`)
not exists(Expr e, BasicBlock bb1, int pos1, BasicBlock bb2, int pos2 |
- mayAddNullTerminator(e, p.getAnAccess()) and
- bb1.getNode(pos1) = e and
+ mayAddNullTerminator(pragma[only_bind_into](e), p.getAnAccess()) and
+ pragma[only_bind_into](bb1).getNode(pos1) = e and
bb2.getNode(pos2) = use
|
bb1 = bb2 and pos1 < pos2
From f7bd74ea59340d6582177dfcc94f08aba4651822 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Tue, 19 Oct 2021 20:08:55 +0100
Subject: [PATCH 082/471] C++: Prototype nodeBefore predicate.
---
.../code/cpp/commons/NullTermination.qll | 31 ++++++++++---------
1 file changed, 17 insertions(+), 14 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll b/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
index 5b18b5d1fc8..7a11742e03d 100644
--- a/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
+++ b/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
@@ -11,6 +11,19 @@ private predicate mayAddNullTerminatorHelper(Expr e, VariableAccess va, Expr e0)
)
}
+bindingset[n1, n2]
+predicate nodeBefore(ControlFlowNode n1, ControlFlowNode n2) {
+ exists(BasicBlock bb1, int pos1, BasicBlock bb2, int pos2 |
+ pragma[only_bind_into](bb1).getNode(pos1) = n1 and
+ pragma[only_bind_into](bb2).getNode(pos2) = n2 and
+ (
+ bb1 = bb2 and pos1 < pos2
+ or
+ bb1.getASuccessor+() = bb2
+ )
+ )
+}
+
/**
* Holds if the expression `e` may add a null terminator to the string in
* variable `v`.
@@ -30,14 +43,9 @@ predicate mayAddNullTerminator(Expr e, VariableAccess va) {
)
or
// Assignment to another stack variable
- exists(Expr e0, BasicBlock bb, int pos, BasicBlock bb0, int pos0 |
+ exists(Expr e0 |
mayAddNullTerminatorHelper(pragma[only_bind_into](e), va, pragma[only_bind_into](e0)) and
- pragma[only_bind_into](bb).getNode(pos) = e and
- pragma[only_bind_into](bb0).getNode(pos0) = e0
- |
- bb = bb0 and pos < pos0
- or
- bb.getASuccessor+() = bb0
+ nodeBefore(e0, e)
)
or
// Assignment to non-stack variable
@@ -119,14 +127,9 @@ predicate variableMustBeNullTerminated(VariableAccess va) {
variableMustBeNullTerminated(use) and
// Simplified: check that `p` may not be null terminated on *any*
// path to `use` (including the one found via `parameterUsePair`)
- not exists(Expr e, BasicBlock bb1, int pos1, BasicBlock bb2, int pos2 |
+ not exists(Expr e |
mayAddNullTerminator(pragma[only_bind_into](e), p.getAnAccess()) and
- pragma[only_bind_into](bb1).getNode(pos1) = e and
- bb2.getNode(pos2) = use
- |
- bb1 = bb2 and pos1 < pos2
- or
- bb1.getASuccessor+() = bb2
+ nodeBefore(e, use)
)
)
)
From f17c06a37f930ed14fc7e14f6d1543fe60cff224 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Wed, 20 Oct 2021 15:30:37 +0100
Subject: [PATCH 083/471] C++: Fix mistake in previous commit.
---
cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll b/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
index 7a11742e03d..61d48f57421 100644
--- a/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
+++ b/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
@@ -45,7 +45,7 @@ predicate mayAddNullTerminator(Expr e, VariableAccess va) {
// Assignment to another stack variable
exists(Expr e0 |
mayAddNullTerminatorHelper(pragma[only_bind_into](e), va, pragma[only_bind_into](e0)) and
- nodeBefore(e0, e)
+ nodeBefore(e, e0)
)
or
// Assignment to non-stack variable
From 5379b25146331895dc5bbb432915ad51dae76814 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Wed, 20 Oct 2021 15:29:02 +0100
Subject: [PATCH 084/471] C++: Add tests.
---
.../ImproperNullTermination.expected | 1 +
.../ImproperNullTermination/test.cpp | 48 +++++++++++++++++++
2 files changed, 49 insertions(+)
diff --git a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/ImproperNullTermination.expected b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/ImproperNullTermination.expected
index 19edbd28c6a..0325ba89a22 100644
--- a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/ImproperNullTermination.expected
+++ b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/ImproperNullTermination.expected
@@ -27,3 +27,4 @@
| test.cpp:454:18:454:23 | buffer | Variable $@ may not be null terminated. | test.cpp:452:8:452:13 | buffer | buffer |
| test.cpp:513:10:513:15 | buffer | Variable $@ may not be null terminated. | test.cpp:511:8:511:13 | buffer | buffer |
| test.cpp:519:16:519:21 | buffer | Variable $@ may not be null terminated. | test.cpp:517:8:517:13 | buffer | buffer |
+| test.cpp:558:10:558:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:553:8:553:14 | buffer2 | buffer2 |
diff --git a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/test.cpp b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/test.cpp
index 9adf409af4a..88fbc46119b 100644
--- a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/test.cpp
+++ b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/test.cpp
@@ -536,3 +536,51 @@ void test_printf(char *str)
}
}
+void test_reassignment()
+{
+ {
+ char buffer1[1024];
+ char buffer2[1024];
+ char *buffer_ptr = buffer1;
+
+ buffer_ptr = buffer2;
+ strcpy(buffer_ptr, "content"); // null terminates buffer2
+ strdup(buffer2); // GOOD
+ }
+
+ {
+ char buffer1[1024];
+ char buffer2[1024];
+ char *buffer_ptr = buffer1;
+
+ strcpy(buffer_ptr, "content"); // null terminates buffer1
+ buffer_ptr = buffer2;
+ strdup(buffer2); // BAD
+ }
+
+ {
+ char buffer1[1024];
+ char buffer2[1024];
+ char *buffer_ptr = buffer1;
+
+ while (cond())
+ {
+ buffer_ptr = buffer2;
+ strcpy(buffer_ptr, "content"); // null terminates buffer2
+ strdup(buffer2); // GOOD
+ }
+ }
+
+ {
+ char buffer1[1024];
+ char buffer2[1024];
+ char *buffer_ptr = buffer1;
+
+ while (cond())
+ {
+ strcpy(buffer_ptr, "content"); // null terminates buffer1 or buffer2
+ buffer_ptr = buffer2;
+ strdup(buffer2); // BAD [NOT DETECTED]
+ }
+ }
+}
From 0e5cfd3469e05257a11ebad52ef5c7a9e70a26cf Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Wed, 20 Oct 2021 15:54:13 +0100
Subject: [PATCH 085/471] C++: Rename the predicate and make it private.
---
cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll b/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
index 61d48f57421..cdca6a194ae 100644
--- a/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
+++ b/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
@@ -12,7 +12,7 @@ private predicate mayAddNullTerminatorHelper(Expr e, VariableAccess va, Expr e0)
}
bindingset[n1, n2]
-predicate nodeBefore(ControlFlowNode n1, ControlFlowNode n2) {
+private predicate controlFlowNodeSuccessorTransitive(ControlFlowNode n1, ControlFlowNode n2) {
exists(BasicBlock bb1, int pos1, BasicBlock bb2, int pos2 |
pragma[only_bind_into](bb1).getNode(pos1) = n1 and
pragma[only_bind_into](bb2).getNode(pos2) = n2 and
@@ -45,7 +45,7 @@ predicate mayAddNullTerminator(Expr e, VariableAccess va) {
// Assignment to another stack variable
exists(Expr e0 |
mayAddNullTerminatorHelper(pragma[only_bind_into](e), va, pragma[only_bind_into](e0)) and
- nodeBefore(e, e0)
+ controlFlowNodeSuccessorTransitive(e, e0)
)
or
// Assignment to non-stack variable
@@ -129,7 +129,7 @@ predicate variableMustBeNullTerminated(VariableAccess va) {
// path to `use` (including the one found via `parameterUsePair`)
not exists(Expr e |
mayAddNullTerminator(pragma[only_bind_into](e), p.getAnAccess()) and
- nodeBefore(e, use)
+ controlFlowNodeSuccessorTransitive(e, use)
)
)
)
From 9fe822f41c2cd835784264fb4852d6f924d06bd5 Mon Sep 17 00:00:00 2001
From: Porcuiney Hairs
Date: Fri, 22 Oct 2021 00:55:01 +0530
Subject: [PATCH 086/471] Include suggestions from review
---
csharp/ql/src/experimental/CWE-918/RequestForgery.ql | 7 +++----
csharp/ql/src/experimental/CWE-918/RequestForgery.qll | 6 ++++--
2 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/csharp/ql/src/experimental/CWE-918/RequestForgery.ql b/csharp/ql/src/experimental/CWE-918/RequestForgery.ql
index c0539de8fba..8b2025c4ce2 100644
--- a/csharp/ql/src/experimental/CWE-918/RequestForgery.ql
+++ b/csharp/ql/src/experimental/CWE-918/RequestForgery.ql
@@ -1,7 +1,6 @@
/**
- * @name Uncontrolled data used in a WebClient
- * @description The WebClient class allows developers to request resources,
- * accessing resources influenced by users can allow an attacker to access local files.
+ * @name Uncontrolled data used in network request
+ * @description Sending network requests with user-controlled data allows for request forgery attacks.
* @kind path-problem
* @problem.severity error
* @precision high
@@ -16,5 +15,5 @@ import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
from RequestForgeryConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink
where c.hasFlowPath(source, sink)
-select sink.getNode(), source, sink, "$@ flows to here and is used in a method of WebClient.",
+select sink.getNode(), source, sink, "$@ flows to here and is used in a server side web request.",
source.getNode(), "User-provided value"
diff --git a/csharp/ql/src/experimental/CWE-918/RequestForgery.qll b/csharp/ql/src/experimental/CWE-918/RequestForgery.qll
index d146fcb2c53..0f73e0f3d8b 100644
--- a/csharp/ql/src/experimental/CWE-918/RequestForgery.qll
+++ b/csharp/ql/src/experimental/CWE-918/RequestForgery.qll
@@ -24,7 +24,7 @@ module RequestForgery {
abstract private class BarrierGuard extends DataFlow::BarrierGuard { }
/**
- * A taint-tracking configuration for detecting server side request forgery vulnerabilities.
+ * A data flow configuration for detecting server side request forgery vulnerabilities.
*/
class RequestForgeryConfiguration extends DataFlow::Configuration {
RequestForgeryConfiguration() { this = "Server Side Request forgery" }
@@ -148,7 +148,9 @@ module RequestForgery {
* This guard considers all checks as valid.
*/
private class StringStartsWithBarrierGuard extends BarrierGuard, MethodCall {
- StringStartsWithBarrierGuard() { this.getTarget().hasQualifiedName("System.String.StartsWith") }
+ StringStartsWithBarrierGuard() {
+ this.getTarget().hasQualifiedName("System.String", "StartsWith")
+ }
override predicate checks(Expr e, AbstractValue v) {
// Any check such as the ones shown below
From f70d808e2fd30feb83da5a2c7e3b235e8336aeca Mon Sep 17 00:00:00 2001
From: Porcuiney Hairs
Date: Fri, 22 Oct 2021 00:58:59 +0530
Subject: [PATCH 087/471] fix testcases
---
csharp/ql/test/experimental/CWE-918/RequestForgery.expected | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/csharp/ql/test/experimental/CWE-918/RequestForgery.expected b/csharp/ql/test/experimental/CWE-918/RequestForgery.expected
index a76eec6e39d..70ba2594fdf 100644
--- a/csharp/ql/test/experimental/CWE-918/RequestForgery.expected
+++ b/csharp/ql/test/experimental/CWE-918/RequestForgery.expected
@@ -5,4 +5,4 @@ nodes
| RequestForgery.cs:16:66:16:68 | access to parameter url | semmle.label | access to parameter url |
subpaths
#select
-| RequestForgery.cs:16:66:16:68 | access to parameter url | RequestForgery.cs:14:52:14:54 | url : String | RequestForgery.cs:16:66:16:68 | access to parameter url | $@ flows to here and is used in a method of WebClient. | RequestForgery.cs:14:52:14:54 | url | User-provided value |
+| RequestForgery.cs:16:66:16:68 | access to parameter url | RequestForgery.cs:14:52:14:54 | url : String | RequestForgery.cs:16:66:16:68 | access to parameter url | $@ flows to here and is used in a server side web request. | RequestForgery.cs:14:52:14:54 | url | User-provided value |
From ed5a386618b328a551a579f00abd0b936aea167f Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Mon, 25 Oct 2021 12:48:24 +0200
Subject: [PATCH 088/471] Python: add concept `SqlCopnstruction`
---
python/ql/lib/semmle/python/Concepts.qll | 41 ++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/python/ql/lib/semmle/python/Concepts.qll b/python/ql/lib/semmle/python/Concepts.qll
index 5517347e692..99d7e8ac038 100644
--- a/python/ql/lib/semmle/python/Concepts.qll
+++ b/python/ql/lib/semmle/python/Concepts.qll
@@ -326,9 +326,47 @@ module CodeExecution {
}
}
+/**
+ * A data-flow node that constructs an SQL statement.
+ * Often, it is worthy of an alert if an SQL statement is constructed such that
+ * executing it would be a security risk.
+ *
+ * If it is important that the SQL statement is indeed executed, then use `SQLExecution`.
+ *
+ * Extend this class to refine existing API models. If you want to model new APIs,
+ * extend `SqlConstruction::Range` instead.
+ */
+class SqlConstruction extends DataFlow::Node {
+ SqlConstruction::Range range;
+
+ SqlConstruction() { this = range }
+
+ /** Gets the argument that specifies the SQL statements to be executed. */
+ DataFlow::Node getSql() { result = range.getSql() }
+}
+
+/** Provides a class for modeling new SQL execution APIs. */
+module SqlConstruction {
+ /**
+ * A data-flow node that constructs an SQL statement.
+ * Often, it is worthy of an alert if an SQL statement is constructed such that
+ * executing it would be a security risk.
+ *
+ * Extend this class to model new APIs. If you want to refine existing API models,
+ * extend `SqlExecution` instead.
+ */
+ abstract class Range extends DataFlow::Node {
+ /** Gets the argument that specifies the SQL statements to be executed. */
+ abstract DataFlow::Node getSql();
+ }
+}
+
/**
* A data-flow node that executes SQL statements.
*
+ * If the context of interest is such that merely constructing an SQL statement
+ * would be valuabe to report, then consider using `SqlConstruction`.
+ *
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `SqlExecution::Range` instead.
*/
@@ -346,6 +384,9 @@ module SqlExecution {
/**
* A data-flow node that executes SQL statements.
*
+ * If the context of interest is such that merely constructing an SQL statement
+ * would be valuabe to report, then consider using `SqlConstruction`.
+ *
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `SqlExecution` instead.
*/
From 03ada6e97abb9eb1a47aece577220920ec45cbc0 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Mon, 25 Oct 2021 13:09:43 +0200
Subject: [PATCH 089/471] Python: Add concept test for `SqlConstruction`
---
.../ql/test/experimental/meta/ConceptsTest.qll | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/python/ql/test/experimental/meta/ConceptsTest.qll b/python/ql/test/experimental/meta/ConceptsTest.qll
index de53f053eb9..9df337fbf0e 100644
--- a/python/ql/test/experimental/meta/ConceptsTest.qll
+++ b/python/ql/test/experimental/meta/ConceptsTest.qll
@@ -128,6 +128,24 @@ class CodeExecutionTest extends InlineExpectationsTest {
}
}
+class SqlConstructionTest extends InlineExpectationsTest {
+ SqlConstructionTest() { this = "SqlConstructionTest" }
+
+ override string getARelevantTag() { result = "constructedSql" }
+
+ override predicate hasActualResult(Location location, string element, string tag, string value) {
+ exists(location.getFile().getRelativePath()) and
+ exists(SqlConstruction e, DataFlow::Node sql |
+ exists(location.getFile().getRelativePath()) and
+ sql = e.getSql() and
+ location = e.getLocation() and
+ element = sql.toString() and
+ value = prettyNodeForInlineTest(sql) and
+ tag = "constructedSql"
+ )
+ }
+}
+
class SqlExecutionTest extends InlineExpectationsTest {
SqlExecutionTest() { this = "SqlExecutionTest" }
From e5b68d68cb4f73993090340178cf3d9a1e923be3 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Mon, 25 Oct 2021 13:15:09 +0200
Subject: [PATCH 090/471] Python: Use `SqlConstruction` in `Asyncpg.qll`
---
.../lib/semmle/python/frameworks/Asyncpg.qll | 26 +++++++++++--------
.../frameworks/asyncpg/SqlExecution.py | 10 +++----
2 files changed, 20 insertions(+), 16 deletions(-)
diff --git a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
index 124ca2e128e..0b3af86abca 100644
--- a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
+++ b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
@@ -18,7 +18,7 @@ private module Asyncpg {
/**
* A `Connection` is created when
* - the result of `asyncpg.connect()` is awaited.
- * - the result of calling `aquire` on a c=`ConnectionPool` is awaited.
+ * - the result of calling `aquire` on a `ConnectionPool` is awaited.
*/
API::Node connection() {
result = API::moduleImport("asyncpg").getMember("connect").getReturn().getAwaited()
@@ -112,15 +112,17 @@ private module Asyncpg {
* TODO: Rewrite this, once we have `API::CallNode` available.
*/
module PreparedStatement {
+ class PreparedStatementConstruction extends SqlConstruction::Range, DataFlow::CallCfgNode {
+ PreparedStatementConstruction() { this = connection().getMember("prepare").getACall() }
+
+ override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("query")] }
+ }
+
private DataFlow::TypeTrackingNode preparedStatementFactory(
DataFlow::TypeTracker t, DataFlow::Node sql
) {
t.start() and
- result = connection().getMember("prepare").getACall() and
- sql in [
- result.(DataFlow::CallCfgNode).getArg(0),
- result.(DataFlow::CallCfgNode).getArgByName("query")
- ]
+ sql = result.(PreparedStatementConstruction).getSql()
or
exists(DataFlow::TypeTracker t2 | result = preparedStatementFactory(t2, sql).track(t2, t))
}
@@ -163,14 +165,16 @@ private module Asyncpg {
* TODO: Rewrite this, once we have `API::CallNode` available.
*/
module Cursor {
+ class CursorConstruction extends SqlConstruction::Range, DataFlow::CallCfgNode {
+ CursorConstruction() { this = connection().getMember("cursor").getACall() }
+
+ override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("query")] }
+ }
+
private DataFlow::TypeTrackingNode cursorFactory(DataFlow::TypeTracker t, DataFlow::Node sql) {
// cursor created from connection
t.start() and
- result = connection().getMember("cursor").getACall() and
- sql in [
- result.(DataFlow::CallCfgNode).getArg(0),
- result.(DataFlow::CallCfgNode).getArgByName("query")
- ]
+ sql = result.(CursorConstruction).getSql()
or
// cursor created from prepared statement
t.start() and
diff --git a/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py b/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py
index e9e619afa66..0ffaaacba62 100644
--- a/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py
+++ b/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py
@@ -20,7 +20,7 @@ async def test_prepared_statement():
conn = await asyncpg.connect()
try:
- pstmt = await conn.prepare("psql")
+ pstmt = await conn.prepare("psql") # $ constructedSql="psql"
pstmt.executemany() # $ getSql="psql"
pstmt.fetch() # $ getSql="psql"
pstmt.fetchrow() # $ getSql="psql"
@@ -36,20 +36,20 @@ async def test_cursor():
try:
async with conn.transaction():
- cursor = await conn.cursor("sql") # $ getSql="sql"
+ cursor = await conn.cursor("sql") # $ getSql="sql" constructedSql="sql"
await cursor.fetch()
- pstmt = await conn.prepare("psql")
+ pstmt = await conn.prepare("psql") # $ constructedSql="psql"
pcursor = await pstmt.cursor() # $ getSql="psql"
await pcursor.fetch()
- async for record in conn.cursor("sql"): # $ getSql="sql"
+ async for record in conn.cursor("sql"): # $ getSql="sql" constructedSql="sql"
pass
async for record in pstmt.cursor(): # $ getSql="psql"
pass
- cursor_factory = conn.cursor("sql")
+ cursor_factory = conn.cursor("sql") # $ constructedSql="sql"
cursor = await cursor_factory # $ getSql="sql"
pcursor_factory = pstmt.cursor()
From 8e8a324fa6af33cd046ceed93ea6681e340c3b54 Mon Sep 17 00:00:00 2001
From: ihsinme
Date: Mon, 25 Oct 2021 14:23:19 +0300
Subject: [PATCH 091/471] Add files via upload
---
.../CWE/CWE-377/InsecureTemporaryFile.cpp | 14 +++
.../CWE/CWE-377/InsecureTemporaryFile.qhelp | 22 +++++
.../CWE/CWE-377/InsecureTemporaryFile.ql | 98 +++++++++++++++++++
3 files changed, 134 insertions(+)
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.cpp
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.qhelp
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.ql
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.cpp b/cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.cpp
new file mode 100644
index 00000000000..a45b3fd3dfe
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.cpp
@@ -0,0 +1,14 @@
+...
+ fp = fopen("/tmp/name.tmp","w"); // BAD
+...
+ char filename = tmpnam(NULL);
+ fp = fopen(filename,"w"); // BAD
+...
+
+ strcat (filename, "/tmp/name.XXXXXX");
+ fd = mkstemp(filename);
+ if ( fd < 0 ) {
+ return error;
+ }
+ fp = fdopen(fd,"w") // GOOD
+...
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.qhelp
new file mode 100644
index 00000000000..3c488cf1cc5
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.qhelp
@@ -0,0 +1,22 @@
+
+
+
+Working with a file, without checking its existence and its rights, as well as working with names that can be predicted, may not be safe. Requires the attention of developers.
+
+
+
+The following example demonstrates erroneous and corrected work with file.
+
+
+
+
+
+
+ CERT C Coding Standard:
+ CON33-C. Avoid race conditions when using library functions.
+
+
+
+
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.ql b/cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.ql
new file mode 100644
index 00000000000..83314d3cb79
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.ql
@@ -0,0 +1,98 @@
+/**
+ * @name Find insecure work with the file name.
+ * @description The file can be used to influence the correct operation of the program.
+ * @kind problem
+ * @id cpp/insecure-work-with-file-name
+ * @problem.severity warning
+ * @precision medium
+ * @tags correctness
+ * security
+ * external/cwe/cwe-377
+ */
+
+import cpp
+import semmle.code.cpp.valuenumbering.GlobalValueNumbering
+
+/** Holds for a function `f` that has an argument at index `apos` used to read the file. */
+predicate numberArgumentRead(Function f, int apos) {
+ f.hasGlobalOrStdName("fgets") and apos = 2
+ or
+ f.hasGlobalOrStdName("fread") and apos = 3
+ or
+ f.hasGlobalOrStdName("read") and apos = 0
+ or
+ f.hasGlobalOrStdName("fscanf") and apos = 0
+}
+
+/** Holds for a function `f` that has an argument at index `apos` used to write to file */
+predicate numberArgumentWrite(Function f, int apos) {
+ f.hasGlobalOrStdName("fprintf") and apos = 0
+ or
+ f.hasGlobalOrStdName("fputs") and apos = 1
+ or
+ f.hasGlobalOrStdName("write") and apos = 0
+ or
+ f.hasGlobalOrStdName("fwrite") and apos = 3
+ or
+ f.hasGlobalOrStdName("fflush") and apos = 0
+}
+
+from FunctionCall fc, string msg
+where
+ (
+ fc.getTarget().hasGlobalOrStdName("tmpnam") or
+ fc.getTarget().hasGlobalOrStdName("tmpnam_s") or
+ fc.getTarget().hasGlobalOrStdName("tmpnam_r")
+ ) and
+ not exists(FunctionCall fctmp |
+ fctmp.getTarget().hasGlobalOrStdName("mktemp") or
+ fctmp.getTarget().hasGlobalOrStdName("mkstemp") or
+ fctmp.getTarget().hasGlobalOrStdName("mkstemps") or
+ fctmp.getTarget().hasGlobalOrStdName("mkdtemp")
+ ) and
+ msg =
+ "Finding the name of a file that does not exist does not mean that it will not be exist at the next operation."
+ or
+ (
+ fc.getTarget().hasGlobalOrStdName("fopen") or
+ fc.getTarget().hasGlobalOrStdName("open")
+ ) and
+ fc.getNumberOfArguments() = 2 and
+ exists(FunctionCall fctmp, int i |
+ numberArgumentWrite(fctmp.getTarget(), i) and
+ globalValueNumber(fc) = globalValueNumber(fctmp.getArgument(i))
+ ) and
+ not exists(FunctionCall fctmp, int i |
+ numberArgumentRead(fctmp.getTarget(), i) and
+ globalValueNumber(fc) = globalValueNumber(fctmp.getArgument(i))
+ ) and
+ exists(FunctionCall fctmp |
+ (
+ fctmp.getTarget().hasGlobalOrStdName("strcat") or
+ fctmp.getTarget().hasGlobalOrStdName("strcpy")
+ ) and
+ globalValueNumber(fc.getArgument(0)) = globalValueNumber(fctmp.getAnArgument())
+ or
+ fctmp.getTarget().hasGlobalOrStdName("getenv") and
+ globalValueNumber(fc.getArgument(0)) = globalValueNumber(fctmp)
+ or
+ (
+ fctmp.getTarget().hasGlobalOrStdName("asprintf") or
+ fctmp.getTarget().hasGlobalOrStdName("vasprintf") or
+ fctmp.getTarget().hasGlobalOrStdName("xasprintf") or
+ fctmp.getTarget().hasGlobalOrStdName("xvasprintf ")
+ ) and
+ exists(Variable vrtmp |
+ vrtmp = fc.getArgument(0).(VariableAccess).getTarget() and
+ vrtmp = fctmp.getArgument(0).(AddressOfExpr).getAddressable().(Variable) and
+ not vrtmp instanceof Field
+ )
+ ) and
+ not exists(FunctionCall fctmp |
+ fctmp.getTarget().hasGlobalOrStdName("umask") or
+ fctmp.getTarget().hasGlobalOrStdName("fchmod") or
+ fctmp.getTarget().hasGlobalOrStdName("chmod")
+ ) and
+ msg =
+ "Дreating a file for writing without evaluating its existence and setting permissions can be unsafe."
+select fc, msg
From 3f3988ce1c187a5ac84bbc1ca416837bd4e4f156 Mon Sep 17 00:00:00 2001
From: ihsinme
Date: Mon, 25 Oct 2021 14:24:35 +0300
Subject: [PATCH 092/471] Add files via upload
---
.../semmle/tests/InsecureTemporaryFile.expected | 1 +
.../semmle/tests/InsecureTemporaryFile.qlref | 1 +
.../Security/CWE/CWE-377/semmle/tests/test.cpp | 16 ++++++++++++++++
3 files changed, 18 insertions(+)
create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-377/semmle/tests/InsecureTemporaryFile.expected
create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-377/semmle/tests/InsecureTemporaryFile.qlref
create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-377/semmle/tests/test.cpp
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-377/semmle/tests/InsecureTemporaryFile.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-377/semmle/tests/InsecureTemporaryFile.expected
new file mode 100644
index 00000000000..3dc08c29827
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-377/semmle/tests/InsecureTemporaryFile.expected
@@ -0,0 +1 @@
+| test.cpp:11:20:11:25 | call to tmpnam | Finding the name of a file that does not exist does not mean that it will not be exist at the next operation. |
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-377/semmle/tests/InsecureTemporaryFile.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-377/semmle/tests/InsecureTemporaryFile.qlref
new file mode 100644
index 00000000000..beec38ab5dc
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-377/semmle/tests/InsecureTemporaryFile.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-377/InsecureTemporaryFile.ql
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-377/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-377/semmle/tests/test.cpp
new file mode 100644
index 00000000000..aa7f16bdd00
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-377/semmle/tests/test.cpp
@@ -0,0 +1,16 @@
+typedef int FILE;
+#define NULL (0)
+FILE *fopen(char *filename, const char *mode);
+char * tmpnam(char * name);
+int fprintf(FILE *fp,const char *fmt, ...);
+int fclose(FILE *stream);
+
+int main(int argc, char *argv[])
+{
+ FILE *fp;
+ char *filename = tmpnam(NULL); // BAD
+ fp = fopen(filename,"w");
+ fprintf(fp,"%s\n","data to file");
+ fclose(fp);
+ return 0;
+}
From 5a02b3880e39bd4f31c29308c5577f895b7dd356 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Mon, 25 Oct 2021 13:30:14 +0200
Subject: [PATCH 093/471] Python: use `SqlConstruction` in `SqlAlchemy` and
`SqlInjection`
---
.../lib/semmle/python/frameworks/SqlAlchemy.qll | 6 ++++--
.../dataflow/SqlInjectionCustomizations.qll | 14 +++++++-------
.../frameworks/flask_sqlalchemy/SqlExecution.py | 6 +++---
.../frameworks/sqlalchemy/SqlExecution.py | 2 +-
.../frameworks/sqlalchemy/new_tests.py | 6 +++---
.../frameworks/sqlalchemy/taint_test.py | 16 ++++++++--------
6 files changed, 26 insertions(+), 24 deletions(-)
diff --git a/python/ql/lib/semmle/python/frameworks/SqlAlchemy.qll b/python/ql/lib/semmle/python/frameworks/SqlAlchemy.qll
index a74d44573f7..6f8bca5aa7e 100644
--- a/python/ql/lib/semmle/python/frameworks/SqlAlchemy.qll
+++ b/python/ql/lib/semmle/python/frameworks/SqlAlchemy.qll
@@ -313,9 +313,11 @@ module SqlAlchemy {
* A construction of a `sqlalchemy.sql.expression.TextClause`, which represents a
* textual SQL string directly.
*/
- abstract class TextClauseConstruction extends DataFlow::CallCfgNode {
+ abstract class TextClauseConstruction extends SqlConstruction::Range, DataFlow::CallCfgNode {
/** Gets the argument that specifies the SQL text. */
- DataFlow::Node getTextArg() { result in [this.getArg(0), this.getArgByName("text")] }
+ final override DataFlow::Node getSql() {
+ result in [this.getArg(0), this.getArgByName("text")]
+ }
}
/** `TextClause` constructions from the `sqlalchemy` package. */
diff --git a/python/ql/lib/semmle/python/security/dataflow/SqlInjectionCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/SqlInjectionCustomizations.qll
index c132878951f..756a1f6b773 100644
--- a/python/ql/lib/semmle/python/security/dataflow/SqlInjectionCustomizations.qll
+++ b/python/ql/lib/semmle/python/security/dataflow/SqlInjectionCustomizations.qll
@@ -42,6 +42,13 @@ module SqlInjection {
*/
class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { }
+ /**
+ * A SQL statement of a SQL construction, considered as a flow sink.
+ */
+ class SqlConstructionAsSink extends Sink {
+ SqlConstructionAsSink() { this = any(SqlConstruction c).getSql() }
+ }
+
/**
* A SQL statement of a SQL execution, considered as a flow sink.
*/
@@ -49,13 +56,6 @@ module SqlInjection {
SqlExecutionAsSink() { this = any(SqlExecution e).getSql() }
}
- /**
- * The text argument of a SQLAlchemy TextClause construction, considered as a flow sink.
- */
- class TextArgAsSink extends Sink {
- TextArgAsSink() { this = any(SqlAlchemy::TextClause::TextClauseConstruction tcc).getTextArg() }
- }
-
/**
* A comparison with a constant string, considered as a sanitizer-guard.
*/
diff --git a/python/ql/test/library-tests/frameworks/flask_sqlalchemy/SqlExecution.py b/python/ql/test/library-tests/frameworks/flask_sqlalchemy/SqlExecution.py
index 7560aff2d30..39cd49195d2 100644
--- a/python/ql/test/library-tests/frameworks/flask_sqlalchemy/SqlExecution.py
+++ b/python/ql/test/library-tests/frameworks/flask_sqlalchemy/SqlExecution.py
@@ -12,7 +12,7 @@ db = SQLAlchemy(app)
# - https://github.com/pallets/flask-sqlalchemy/blob/931ec00d1e27f51508e05706eef41cc4419a0b32/src/flask_sqlalchemy/__init__.py#L765
# - https://github.com/pallets/flask-sqlalchemy/blob/931ec00d1e27f51508e05706eef41cc4419a0b32/src/flask_sqlalchemy/__init__.py#L99-L109
-assert str(type(db.text("Foo"))) == ""
+assert str(type(db.text("Foo"))) == "" # $ constructedSql="Foo"
# also has engine/session instantiated
@@ -44,8 +44,8 @@ assert result.fetchall() == [("Foo",)]
# text
-t = db.text("foo")
+t = db.text("foo") # $ constructedSql="foo"
assert isinstance(t, sqlalchemy.sql.expression.TextClause)
-t = db.text(text="foo")
+t = db.text(text="foo") # $ constructedSql="foo"
assert isinstance(t, sqlalchemy.sql.expression.TextClause)
diff --git a/python/ql/test/library-tests/frameworks/sqlalchemy/SqlExecution.py b/python/ql/test/library-tests/frameworks/sqlalchemy/SqlExecution.py
index a7c9af9da32..fe75fc17d5c 100644
--- a/python/ql/test/library-tests/frameworks/sqlalchemy/SqlExecution.py
+++ b/python/ql/test/library-tests/frameworks/sqlalchemy/SqlExecution.py
@@ -46,7 +46,7 @@ with engine.begin() as connection:
connection.execute("some sql") # $ getSql="some sql"
# Injection requiring the text() taint-step
-t = text("some sql")
+t = text("some sql") # $ constructedSql="some sql"
session.query(User).filter(t)
session.query(User).group_by(User.id).having(t)
session.query(User).group_by(t).first()
diff --git a/python/ql/test/library-tests/frameworks/sqlalchemy/new_tests.py b/python/ql/test/library-tests/frameworks/sqlalchemy/new_tests.py
index 9726e08f6bc..6ccf7b3b47d 100644
--- a/python/ql/test/library-tests/frameworks/sqlalchemy/new_tests.py
+++ b/python/ql/test/library-tests/frameworks/sqlalchemy/new_tests.py
@@ -6,7 +6,7 @@ import sqlalchemy.orm
# either v1.4 or v2.0, such that we cover both.
raw_sql = "select 'FOO'"
-text_sql = sqlalchemy.text(raw_sql)
+text_sql = sqlalchemy.text(raw_sql) # $ constructedSql=raw_sql
Base = sqlalchemy.orm.declarative_base()
@@ -169,7 +169,7 @@ assert session.query(For14).all()[0].id == 14
# and now we can do the actual querying
-text_foo = sqlalchemy.text("'FOO'")
+text_foo = sqlalchemy.text("'FOO'") # $ constructedSql="'FOO'"
# filter_by is only vulnerable to injection if sqlalchemy.text is used, which is evident
# from the logs produced if this file is run
@@ -298,7 +298,7 @@ with engine.connect() as conn:
assert scalar_result == "FOO"
# This is a contrived example
- select = sqlalchemy.select(sqlalchemy.text("'BAR'"))
+ select = sqlalchemy.select(sqlalchemy.text("'BAR'")) # $ constructedSql="'BAR'"
result = conn.execute(select) # $ getSql=select
assert result.fetchall() == [("BAR",)]
diff --git a/python/ql/test/library-tests/frameworks/sqlalchemy/taint_test.py b/python/ql/test/library-tests/frameworks/sqlalchemy/taint_test.py
index 9e9764a411f..884d30d3672 100644
--- a/python/ql/test/library-tests/frameworks/sqlalchemy/taint_test.py
+++ b/python/ql/test/library-tests/frameworks/sqlalchemy/taint_test.py
@@ -8,14 +8,14 @@ def test_taint():
ensure_tainted(ts) # $ tainted
- t1 = sqlalchemy.text(ts)
- t2 = sqlalchemy.text(text=ts)
- t3 = sqlalchemy.sql.text(ts)
- t4 = sqlalchemy.sql.text(text=ts)
- t5 = sqlalchemy.sql.expression.text(ts)
- t6 = sqlalchemy.sql.expression.text(text=ts)
- t7 = sqlalchemy.sql.expression.TextClause(ts)
- t8 = sqlalchemy.sql.expression.TextClause(text=ts)
+ t1 = sqlalchemy.text(ts) # $ constructedSql=ts
+ t2 = sqlalchemy.text(text=ts) # $ constructedSql=ts
+ t3 = sqlalchemy.sql.text(ts) # $ constructedSql=ts
+ t4 = sqlalchemy.sql.text(text=ts) # $ constructedSql=ts
+ t5 = sqlalchemy.sql.expression.text(ts) # $ constructedSql=ts
+ t6 = sqlalchemy.sql.expression.text(text=ts) # $ constructedSql=ts
+ t7 = sqlalchemy.sql.expression.TextClause(ts) # $ constructedSql=ts
+ t8 = sqlalchemy.sql.expression.TextClause(text=ts) # $ constructedSql=ts
# Since we flag user-input to a TextClause with its' own query, we don't want to
# have a taint-step for it as that would lead to us also giving an alert for normal
From baec1863598690fa998a8c385a1a5e254682e511 Mon Sep 17 00:00:00 2001
From: ihsinme
Date: Mon, 25 Oct 2021 14:33:01 +0300
Subject: [PATCH 094/471] Add files via upload
---
.../IncorrectChangingWorkingDirectory.cpp | 24 +++++++
.../IncorrectChangingWorkingDirectory.qhelp | 22 ++++++
.../IncorrectChangingWorkingDirectory.ql | 70 +++++++++++++++++++
3 files changed, 116 insertions(+)
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.cpp
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.qhelp
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.ql
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.cpp b/cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.cpp
new file mode 100644
index 00000000000..1a4259dab47
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.cpp
@@ -0,0 +1,24 @@
+...
+ chroot("/myFold/myTmp"); // BAD
+...
+ chdir("/myFold/myTmp"); // BAD
+...
+ int fd = open("/myFold/myTmp", O_RDONLY | O_DIRECTORY);
+ fchdir(fd); // BAD
+...
+ if (chdir("/myFold/myTmp") == -1) {
+ exit(-1);
+ }
+ if (chroot("/myFold/myTmp") == -1) { // GOOD
+ exit(-1);
+ }
+...
+ if (chdir("/myFold/myTmp") == -1) { // GOOD
+ exit(-1);
+ }
+...
+ int fd = open("/myFold/myTmp", O_RDONLY | O_DIRECTORY);
+ if(fchdir(fd) == -1) { // GOOD
+ exit(-1);
+ }
+...
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.qhelp
new file mode 100644
index 00000000000..c22d0341682
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.qhelp
@@ -0,0 +1,22 @@
+
+
+
+Working with changing directories, without checking the return value or pinning the directory, may not be safe. Requires the attention of developers.
+
+
+
+The following example demonstrates erroneous and corrected work with changing working directories.
+
+
+
+
+
+
+ CERT C Coding Standard:
+ POS05-C. Limit access to files by creating a jail.
+
+
+
+
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.ql b/cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.ql
new file mode 100644
index 00000000000..a6026adbce3
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.ql
@@ -0,0 +1,70 @@
+/**
+ * @name Find work with changing working directories, with security errors.
+ * @description Not validating the return value or pinning the directory can be unsafe.
+ * @kind problem
+ * @id cpp/work-with-changing-working-directories
+ * @problem.severity warning
+ * @precision medium
+ * @tags correctness
+ * security
+ * external/cwe/cwe-243
+ * external/cwe/cwe-252
+ */
+
+import cpp
+
+/** Holds if a `fc` function call is available before or before a `chdir` function call. */
+predicate inExistsChdir(FunctionCall fcp) {
+ exists(FunctionCall fctmp |
+ (
+ fctmp.getTarget().hasGlobalOrStdName("chdir") or
+ fctmp.getTarget().hasGlobalOrStdName("fchdir")
+ ) and
+ (
+ fctmp.getASuccessor*() = fcp or
+ fcp.getASuccessor*() = fctmp
+ )
+ )
+}
+
+/** Holds if a `fc` function call is available before or before a function call containing a `chdir` call. */
+predicate outExistsChdir(FunctionCall fcp) {
+ exists(FunctionCall fctmp |
+ exists(FunctionCall fctmp2 |
+ (
+ fctmp2.getTarget().hasGlobalOrStdName("chdir") or
+ fctmp2.getTarget().hasGlobalOrStdName("fchdir")
+ ) and
+ fctmp2.getEnclosingStmt().getParentStmt*() = fctmp.getTarget().getEntryPoint().getChildStmt*()
+ ) and
+ (
+ fctmp.getASuccessor*() = fcp or
+ fcp.getASuccessor*() = fctmp
+ )
+ )
+}
+
+from FunctionCall fc, string msg
+where
+ fc.getTarget().hasGlobalOrStdName("chroot") and
+ not inExistsChdir(fc) and
+ not outExistsChdir(fc) and
+ exists(FunctionCall fctmp |
+ fc.getEnclosingStmt().getParentStmt*() = fctmp.getTarget().getEntryPoint().getChildStmt*() and
+ not inExistsChdir(fctmp) and
+ not outExistsChdir(fctmp)
+ ) and
+ msg = "Creation of chroot Jail Without Changing Working Directory out"
+ or
+ (
+ fc.getTarget().hasGlobalOrStdName("chdir") or
+ fc.getTarget().hasGlobalOrStdName("fchdir")
+ ) and
+ not exists(ConditionalStmt cotmp | cotmp.getControllingExpr().getAChild*() = fc) and
+ not exists(Loop lptmp | lptmp.getCondition().getAChild*() = fc) and
+ not exists(ReturnStmt rttmp | rttmp.getExpr().getAChild*() = fc) and
+ not exists(Assignment astmp | astmp.getAChild*() = fc) and
+ not exists(Initializer ittmp | ittmp.getExpr().getAChild*() = fc) and
+ not fc.isInMacroExpansion() and
+ msg = fc.getTarget().getName() + " unchecked return value."
+select fc, msg
From 5d5d6bcc6959487240b658b0c0e501456fba37a3 Mon Sep 17 00:00:00 2001
From: ihsinme
Date: Mon, 25 Oct 2021 14:34:10 +0300
Subject: [PATCH 095/471] Add files via upload
---
...IncorrectChangingWorkingDirectory.expected | 2 +
.../IncorrectChangingWorkingDirectory.qlref | 1 +
.../CWE/CWE-243/semmle/tests/test.cpp | 46 +++++++++++++++++++
3 files changed, 49 insertions(+)
create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-243/semmle/tests/IncorrectChangingWorkingDirectory.expected
create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-243/semmle/tests/IncorrectChangingWorkingDirectory.qlref
create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-243/semmle/tests/test.cpp
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-243/semmle/tests/IncorrectChangingWorkingDirectory.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-243/semmle/tests/IncorrectChangingWorkingDirectory.expected
new file mode 100644
index 00000000000..c706f907fb3
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-243/semmle/tests/IncorrectChangingWorkingDirectory.expected
@@ -0,0 +1,2 @@
+| test.cpp:12:7:12:12 | call to chroot | Creation of chroot Jail Without Changing Working Directory out |
+| test.cpp:29:3:29:7 | call to chdir | chdir unchecked return value. |
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-243/semmle/tests/IncorrectChangingWorkingDirectory.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-243/semmle/tests/IncorrectChangingWorkingDirectory.qlref
new file mode 100644
index 00000000000..6e521340437
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-243/semmle/tests/IncorrectChangingWorkingDirectory.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.ql
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-243/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-243/semmle/tests/test.cpp
new file mode 100644
index 00000000000..24ff440d140
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-243/semmle/tests/test.cpp
@@ -0,0 +1,46 @@
+typedef int FILE;
+#define size_t int
+size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
+FILE *fopen(const char *filename, const char *mode);
+int fread(char *buf, int size, int count, FILE *fp);
+int fclose(FILE *fp);
+int chroot(char *path);
+int chdir(char *path);
+void exit(int status);
+
+int funTest1(){
+ if (chroot("/myFold/myTmp") == -1) { // BAD
+ exit(-1);
+ }
+ return 0;
+}
+
+int funTest2(){
+ if (chdir("/myFold/myTmp") == -1) { // GOOD
+ exit(-1);
+ }
+ if (chroot("/myFold/myTmp") == -1) { // GOOD
+ exit(-1);
+ }
+ return 0;
+}
+
+int funTest3(){
+ chdir("/myFold/myTmp"); // BAD
+ return 0;
+}
+int main(int argc, char *argv[])
+{
+ if(argc = 0) {
+ funTest3();
+ return 2;
+ }
+ if(argc = 1)
+ funTest1();
+ else
+ funTest2();
+ FILE *fp = fopen(argv[1], "w");
+ fwrite("12345", 5, 1, fp);
+ fclose(fp);
+ return 0;
+}
From cb61f87aa30ff199d4231ed5c436bee7147a84c7 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Mon, 25 Oct 2021 13:40:45 +0200
Subject: [PATCH 096/471] Python: rewrite "clever" reverse lookup
---
.../lib/semmle/python/frameworks/Asyncpg.qll | 35 +++++++++++--------
1 file changed, 20 insertions(+), 15 deletions(-)
diff --git a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
index 0b3af86abca..8d64a99e97e 100644
--- a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
+++ b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
@@ -26,24 +26,22 @@ private module Asyncpg {
result = connectionPool().getMember("acquire").getReturn().getAwaited()
}
- /** Reverse lookup of the query argument name for a query method. */
- private string queryMethodName(string queryArg) {
- result in ["copy_from_query", "execute", "fetch", "fetchrow", "fetchval"] and
- queryArg = "query"
- or
- result = "executemany" and
- queryArg = "command"
- }
-
/** `Connection`s and `ConnectionPool`s provide some methods that execute SQL. */
class SqlExecutionOnConnection extends SqlExecution::Range, DataFlow::MethodCallNode {
- string queryArg;
+ string methodName;
SqlExecutionOnConnection() {
- this.calls([connectionPool().getAUse(), connection().getAUse()], queryMethodName(queryArg))
+ methodName in ["copy_from_query", "execute", "fetch", "fetchrow", "fetchval", "executemany"] and
+ this.calls([connectionPool().getAUse(), connection().getAUse()], methodName)
}
- override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName(queryArg)] }
+ override DataFlow::Node getSql() {
+ methodName in ["copy_from_query", "execute", "fetch", "fetchrow", "fetchval"] and
+ result in [this.getArg(0), this.getArgByName("query")]
+ or
+ methodName = "executemany" and
+ result in [this.getArg(0), this.getArgByName("command")]
+ }
}
/** Reverse lokup of the path argument name for a method accessing the file system. */
@@ -57,14 +55,21 @@ private module Asyncpg {
/** `Connection`s and `ConnectionPool`s provide some methods that access the file system. */
class FileAccessOnConnection extends FileSystemAccess::Range, DataFlow::MethodCallNode {
- string pathArg;
+ string methodName;
FileAccessOnConnection() {
- this.calls([connectionPool().getAUse(), connection().getAUse()], fileAccessMethodName(pathArg))
+ methodName in ["copy_from_query", "copy_from_table", "copy_to_table"] and
+ this.calls([connectionPool().getAUse(), connection().getAUse()], methodName)
}
// The path argument is keyword only.
- override DataFlow::Node getAPathArgument() { result in [this.getArgByName(pathArg)] }
+ override DataFlow::Node getAPathArgument() {
+ methodName in ["copy_from_query", "copy_from_table"] and
+ result = this.getArgByName("output")
+ or
+ methodName = "copy_to_table" and
+ result = this.getArgByName("source")
+ }
}
/**
From 149b235c7ab7b70ea2afdca20bedd2aa0d04a243 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Mon, 25 Oct 2021 13:41:29 +0200
Subject: [PATCH 097/471] Python: delete unused predicate
---
python/ql/lib/semmle/python/frameworks/Asyncpg.qll | 9 ---------
1 file changed, 9 deletions(-)
diff --git a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
index 8d64a99e97e..58d725f21a4 100644
--- a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
+++ b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
@@ -44,15 +44,6 @@ private module Asyncpg {
}
}
- /** Reverse lokup of the path argument name for a method accessing the file system. */
- private string fileAccessMethodName(string pathArg) {
- result in ["copy_from_query", "copy_from_table"] and
- pathArg = "output"
- or
- result = "copy_to_table" and
- pathArg = "source"
- }
-
/** `Connection`s and `ConnectionPool`s provide some methods that access the file system. */
class FileAccessOnConnection extends FileSystemAccess::Range, DataFlow::MethodCallNode {
string methodName;
From 7e7a6464eca4eddcaf3574af093270ba1676e46c Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Mon, 25 Oct 2021 10:47:29 +0200
Subject: [PATCH 098/471] Python: FastAPI: Model extra-taint for pydantic
models
It feels a bit strange to add it to `frameworks.rst` since we only
support a little bit of it, but if I don't do it now, we will most
likely forget to do it later on (since it has already been added to
`frameworks.qll`).
---
docs/codeql/support/reusables/frameworks.rst | 1 +
python/ql/lib/semmle/python/Frameworks.qll | 1 +
.../lib/semmle/python/frameworks/FastApi.qll | 13 +++
.../lib/semmle/python/frameworks/Pydantic.qll | 101 ++++++++++++++++++
.../frameworks/fastapi/taint_test.py | 12 +--
5 files changed, 122 insertions(+), 6 deletions(-)
create mode 100644 python/ql/lib/semmle/python/frameworks/Pydantic.qll
diff --git a/docs/codeql/support/reusables/frameworks.rst b/docs/codeql/support/reusables/frameworks.rst
index 2b2f8db176d..ad629d61604 100644
--- a/docs/codeql/support/reusables/frameworks.rst
+++ b/docs/codeql/support/reusables/frameworks.rst
@@ -168,6 +168,7 @@ Python built-in support
invoke, Utility library
jmespath, Utility library
multidict, Utility library
+ pydantic, Utility library
yarl, Utility library
aioch, Database
clickhouse-driver, Database
diff --git a/python/ql/lib/semmle/python/Frameworks.qll b/python/ql/lib/semmle/python/Frameworks.qll
index 2e6f1d54069..74cc9e9637d 100644
--- a/python/ql/lib/semmle/python/Frameworks.qll
+++ b/python/ql/lib/semmle/python/Frameworks.qll
@@ -24,6 +24,7 @@ private import semmle.python.frameworks.Mysql
private import semmle.python.frameworks.MySQLdb
private import semmle.python.frameworks.Peewee
private import semmle.python.frameworks.Psycopg2
+private import semmle.python.frameworks.Pydantic
private import semmle.python.frameworks.PyMySQL
private import semmle.python.frameworks.Rsa
private import semmle.python.frameworks.Simplejson
diff --git a/python/ql/lib/semmle/python/frameworks/FastApi.qll b/python/ql/lib/semmle/python/frameworks/FastApi.qll
index 39c36ded717..ed40dee82ac 100644
--- a/python/ql/lib/semmle/python/frameworks/FastApi.qll
+++ b/python/ql/lib/semmle/python/frameworks/FastApi.qll
@@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
+private import semmle.python.frameworks.Pydantic
/**
* Provides models for the `fastapi` PyPI package.
@@ -78,6 +79,18 @@ private module FastApi {
DataFlow::Node getResponseClassArg() { result = this.getArgByName("response_class") }
}
+ /**
+ * A parameter to a request handler that has a type-annotation with a class that is a
+ * Pydantic model.
+ */
+ private class PydanticModelRequestHandlerParam extends Pydantic::BaseModel::InstanceSource,
+ DataFlow::ParameterNode {
+ PydanticModelRequestHandlerParam() {
+ this.getParameter().getAnnotation() = Pydantic::BaseModel::subclassRef().getAUse().asExpr() and
+ any(FastApiRouteSetup rs).getARequestHandler().getArgByName(_) = this.getParameter()
+ }
+ }
+
// ---------------------------------------------------------------------------
// Response modeling
// ---------------------------------------------------------------------------
diff --git a/python/ql/lib/semmle/python/frameworks/Pydantic.qll b/python/ql/lib/semmle/python/frameworks/Pydantic.qll
new file mode 100644
index 00000000000..938f3059d15
--- /dev/null
+++ b/python/ql/lib/semmle/python/frameworks/Pydantic.qll
@@ -0,0 +1,101 @@
+/**
+ * Provides classes modeling security-relevant aspects of the `pydantic` PyPI package.
+ *
+ * See
+ * - https://pypi.org/project/pydantic/
+ * - https://pydantic-docs.helpmanual.io/
+ */
+
+private import python
+private import semmle.python.dataflow.new.DataFlow
+private import semmle.python.dataflow.new.TaintTracking
+private import semmle.python.Concepts
+private import semmle.python.ApiGraphs
+
+/**
+ * INTERNAL: Do not use.
+ *
+ * Provides models for `pydantic` PyPI package.
+ *
+ * See
+ * - https://pypi.org/project/pydantic/
+ * - https://pydantic-docs.helpmanual.io/
+ */
+module Pydantic {
+ /**
+ * Provides models for `pydantic.BaseModel` subclasses (a pydantic model).
+ *
+ * See https://pydantic-docs.helpmanual.io/usage/models/.
+ */
+ module BaseModel {
+ /** Gets a reference to a `pydantic.BaseModel` subclass (a pydantic model). */
+ API::Node subclassRef() {
+ result = API::moduleImport("pydantic").getMember("BaseModel").getASubclass+()
+ }
+
+ /**
+ * A source of instances of `pydantic.BaseModel` subclasses, extend this class to model new instances.
+ *
+ * This can include instantiations of the class, return values from function
+ * calls, or a special parameter that will be set when functions are called by an external
+ * library.
+ *
+ * Use the predicate `BaseModel::instance()` to get references to instances of `pydantic.BaseModel`.
+ */
+ abstract class InstanceSource extends DataFlow::LocalSourceNode { }
+
+ /** Gets a reference to an instance of a `pydantic.BaseModel` subclass. */
+ private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
+ t.start() and
+ result instanceof InstanceSource
+ or
+ t.start() and
+ instanceStepToPydanticModel(_, result)
+ or
+ exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
+ }
+
+ /** Gets a reference to an instance of a `pydantic.BaseModel` subclass. */
+ DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
+
+ /**
+ * A step from an instance of a `pydantic.BaseModel` subclass, that might result in
+ * an instance of a `pydantic.BaseModel` subclass.
+ *
+ * NOTE: We currently overapproximate, and treat all attributes as containing another
+ * pydantic model. For the code below, we _could_ limit this to `main_foo` and
+ * members of `other_foos`.
+ *
+ * ```py
+ * class MyComplexModel(BaseModel):
+ * field: str
+ * main_foo: Foo
+ * other_foos: List[Foo]
+ * ```
+ */
+ private predicate instanceStepToPydanticModel(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ // attributes (such as `model.foo`)
+ nodeFrom = instance() and
+ nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom
+ or
+ // subscripts on attributes (such as `model.foo[0]`)
+ nodeFrom.(DataFlow::AttrRead).getObject() = instance() and
+ nodeTo.asCfgNode().(SubscriptNode).getObject() = nodeFrom.asCfgNode()
+ }
+
+ /**
+ * Extra taint propagation for `pydantic.BaseModel` subclasses. (note that these could also be `pydantic.BaseModel` subclasses)
+ */
+ private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
+ override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ // attributes (such as `model.foo`)
+ nodeFrom = instance() and
+ nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom
+ or
+ // subscripts on attributes (such as `model.foo[0]`)
+ nodeFrom.(DataFlow::AttrRead).getObject() = instance() and
+ nodeTo.asCfgNode().(SubscriptNode).getObject() = nodeFrom.asCfgNode()
+ }
+ }
+ }
+}
diff --git a/python/ql/test/library-tests/frameworks/fastapi/taint_test.py b/python/ql/test/library-tests/frameworks/fastapi/taint_test.py
index a4de846fe21..a661a546b9e 100644
--- a/python/ql/test/library-tests/frameworks/fastapi/taint_test.py
+++ b/python/ql/test/library-tests/frameworks/fastapi/taint_test.py
@@ -29,14 +29,14 @@ async def test_taint(name : str, number : int, also_input: MyComplexModel): # $
number, # $ tainted
also_input, # $ tainted
- also_input.field, # $ MISSING: tainted
+ also_input.field, # $ tainted
- also_input.main_foo, # $ MISSING: tainted
- also_input.main_foo.foo, # $ MISSING: tainted
+ also_input.main_foo, # $ tainted
+ also_input.main_foo.foo, # $ tainted
- also_input.other_foos, # $ MISSING: tainted
- also_input.other_foos[0], # $ MISSING: tainted
- also_input.other_foos[0].foo, # $ MISSING: tainted
+ also_input.other_foos, # $ tainted
+ also_input.other_foos[0], # $ tainted
+ also_input.other_foos[0].foo, # $ tainted
[f.foo for f in also_input.other_foos], # $ MISSING: tainted
)
From 54ab5d4bc8b6067afba867a2fc32be1df6eaa875 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Mon, 25 Oct 2021 10:47:51 +0200
Subject: [PATCH 099/471] Python: Fix date for FastAPI change-note
---
...add-FastAPI-modeling.md => 2021-10-25-add-FastAPI-modeling.md} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename python/change-notes/{2021-09-12-add-FastAPI-modeling.md => 2021-10-25-add-FastAPI-modeling.md} (100%)
diff --git a/python/change-notes/2021-09-12-add-FastAPI-modeling.md b/python/change-notes/2021-10-25-add-FastAPI-modeling.md
similarity index 100%
rename from python/change-notes/2021-09-12-add-FastAPI-modeling.md
rename to python/change-notes/2021-10-25-add-FastAPI-modeling.md
From bd8eec8475586fabab5dd093218aeecaed006846 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Mon, 25 Oct 2021 14:22:58 +0200
Subject: [PATCH 100/471] Python: FastAPI: Add websocket test
---
.../frameworks/fastapi/taint_test.py | 63 +++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/python/ql/test/library-tests/frameworks/fastapi/taint_test.py b/python/ql/test/library-tests/frameworks/fastapi/taint_test.py
index a661a546b9e..6371d3c994c 100644
--- a/python/ql/test/library-tests/frameworks/fastapi/taint_test.py
+++ b/python/ql/test/library-tests/frameworks/fastapi/taint_test.py
@@ -94,3 +94,66 @@ async def file_upload(f1: bytes = File(None), f2: UploadFile = File(None)): # $
await f2.read(), # $ MISSING: tainted
)
return "ok" # $ HttpResponse
+
+# --- WebSocket ---
+
+import starlette.websockets
+from fastapi import WebSocket
+
+
+assert WebSocket == starlette.websockets.WebSocket
+
+
+@app.websocket("/ws")
+async def websocket_test(websocket: WebSocket):
+ await websocket.accept()
+
+ ensure_tainted(
+ websocket, # $ MISSING: tainted
+
+ websocket.url, # $ MISSING: tainted
+
+ websocket.url.scheme, # $ MISSING: tainted
+ websocket.url.netloc, # $ MISSING: tainted
+ websocket.url.path, # $ MISSING: tainted
+ websocket.url.query, # $ MISSING: tainted
+ websocket.url.fragment, # $ MISSING: tainted
+ websocket.url.username, # $ MISSING: tainted
+ websocket.url.password, # $ MISSING: tainted
+ websocket.url.hostname, # $ MISSING: tainted
+ websocket.url.port, # $ MISSING: tainted
+
+ websocket.url.components, # $ MISSING: tainted
+ websocket.url.components.scheme, # $ MISSING: tainted
+ websocket.url.components.netloc, # $ MISSING: tainted
+ websocket.url.components.path, # $ MISSING: tainted
+ websocket.url.components.query, # $ MISSING: tainted
+ websocket.url.components.fragment, # $ MISSING: tainted
+ websocket.url.components.username, # $ MISSING: tainted
+ websocket.url.components.password, # $ MISSING: tainted
+ websocket.url.components.hostname, # $ MISSING: tainted
+ websocket.url.components.port, # $ MISSING: tainted
+
+ websocket.headers, # $ MISSING: tainted
+ websocket.headers["key"], # $ MISSING: tainted
+
+ websocket.query_params, # $ MISSING: tainted
+ websocket.query_params["key"], # $ MISSING: tainted
+
+ websocket.cookies, # $ MISSING: tainted
+ websocket.cookies["key"], # $ MISSING: tainted
+
+ await websocket.receive(), # $ MISSING: tainted
+ await websocket.receive_bytes(), # $ MISSING: tainted
+ await websocket.receive_text(), # $ MISSING: tainted
+ await websocket.receive_json(), # $ MISSING: tainted
+ )
+
+ async for data in websocket.iter_bytes():
+ ensure_tainted(data) # $ MISSING: tainted
+
+ async for data in websocket.iter_text():
+ ensure_tainted(data) # $ MISSING: tainted
+
+ async for data in websocket.iter_json():
+ ensure_tainted(data) # $ MISSING: tainted
From b69977b37acde2861e5ca87f6b79e057fbc70840 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Mon, 25 Oct 2021 14:29:25 +0200
Subject: [PATCH 101/471] Python: FastAPI: Ignore scheme as tainted
reasoning highlighted in the comment
---
.../test/library-tests/frameworks/fastapi/taint_test.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/python/ql/test/library-tests/frameworks/fastapi/taint_test.py b/python/ql/test/library-tests/frameworks/fastapi/taint_test.py
index 6371d3c994c..b1a43198277 100644
--- a/python/ql/test/library-tests/frameworks/fastapi/taint_test.py
+++ b/python/ql/test/library-tests/frameworks/fastapi/taint_test.py
@@ -113,7 +113,6 @@ async def websocket_test(websocket: WebSocket):
websocket.url, # $ MISSING: tainted
- websocket.url.scheme, # $ MISSING: tainted
websocket.url.netloc, # $ MISSING: tainted
websocket.url.path, # $ MISSING: tainted
websocket.url.query, # $ MISSING: tainted
@@ -124,7 +123,6 @@ async def websocket_test(websocket: WebSocket):
websocket.url.port, # $ MISSING: tainted
websocket.url.components, # $ MISSING: tainted
- websocket.url.components.scheme, # $ MISSING: tainted
websocket.url.components.netloc, # $ MISSING: tainted
websocket.url.components.path, # $ MISSING: tainted
websocket.url.components.query, # $ MISSING: tainted
@@ -149,6 +147,12 @@ async def websocket_test(websocket: WebSocket):
await websocket.receive_json(), # $ MISSING: tainted
)
+ # scheme seems very unlikely to give interesting results, but very likely to give FPs.
+ ensure_not_tainted(
+ websocket.url.scheme,
+ websocket.url.components.scheme,
+ )
+
async for data in websocket.iter_bytes():
ensure_tainted(data) # $ MISSING: tainted
From 7619d0fc33d8994470b21d45fa2c2d5fc8557ade Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Mon, 25 Oct 2021 15:22:24 +0200
Subject: [PATCH 102/471] Python: FastAPI: Model WebSocket usage
---
docs/codeql/support/reusables/frameworks.rst | 1 +
python/ql/lib/semmle/python/Frameworks.qll | 1 +
.../lib/semmle/python/frameworks/FastApi.qll | 14 +-
.../semmle/python/frameworks/Starlette.qll | 162 ++++++++++++++++++
.../lib/semmle/python/frameworks/Stdlib.qll | 92 ++++++++++
.../frameworks/fastapi/taint_test.py | 68 ++++----
6 files changed, 303 insertions(+), 35 deletions(-)
create mode 100644 python/ql/lib/semmle/python/frameworks/Starlette.qll
diff --git a/docs/codeql/support/reusables/frameworks.rst b/docs/codeql/support/reusables/frameworks.rst
index ad629d61604..7a7db2b25af 100644
--- a/docs/codeql/support/reusables/frameworks.rst
+++ b/docs/codeql/support/reusables/frameworks.rst
@@ -159,6 +159,7 @@ Python built-in support
Flask, Web framework
Tornado, Web framework
Twisted, Web framework
+ starlette, Asynchronous Server Gateway Interface (ASGI)
PyYAML, Serialization
dill, Serialization
simplejson, Serialization
diff --git a/python/ql/lib/semmle/python/Frameworks.qll b/python/ql/lib/semmle/python/Frameworks.qll
index 74cc9e9637d..f32675f6f47 100644
--- a/python/ql/lib/semmle/python/Frameworks.qll
+++ b/python/ql/lib/semmle/python/Frameworks.qll
@@ -29,6 +29,7 @@ private import semmle.python.frameworks.PyMySQL
private import semmle.python.frameworks.Rsa
private import semmle.python.frameworks.Simplejson
private import semmle.python.frameworks.SqlAlchemy
+private import semmle.python.frameworks.Starlette
private import semmle.python.frameworks.Stdlib
private import semmle.python.frameworks.Tornado
private import semmle.python.frameworks.Twisted
diff --git a/python/ql/lib/semmle/python/frameworks/FastApi.qll b/python/ql/lib/semmle/python/frameworks/FastApi.qll
index ed40dee82ac..a6d9e52efad 100644
--- a/python/ql/lib/semmle/python/frameworks/FastApi.qll
+++ b/python/ql/lib/semmle/python/frameworks/FastApi.qll
@@ -10,6 +10,7 @@ private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
private import semmle.python.frameworks.Pydantic
+private import semmle.python.frameworks.Starlette
/**
* Provides models for the `fastapi` PyPI package.
@@ -49,7 +50,7 @@ private module FastApi {
exists(string routeAddingMethod |
routeAddingMethod = HTTP::httpVerbLower()
or
- routeAddingMethod = "api_route"
+ routeAddingMethod in ["api_route", "websocket"]
|
this = App::instance().getMember(routeAddingMethod).getACall()
or
@@ -94,6 +95,17 @@ private module FastApi {
// ---------------------------------------------------------------------------
// Response modeling
// ---------------------------------------------------------------------------
+ /**
+ * A parameter to a request handler that has a WebSocket type-annotation.
+ */
+ private class WebSocketRequestHandlerParam extends Starlette::WebSocket::InstanceSource,
+ DataFlow::ParameterNode {
+ WebSocketRequestHandlerParam() {
+ this.getParameter().getAnnotation() = Starlette::WebSocket::classRef().getAUse().asExpr() and
+ any(FastApiRouteSetup rs).getARequestHandler().getArgByName(_) = this.getParameter()
+ }
+ }
+
/**
* Provides models for the `fastapi.Response` class and subclasses.
*
diff --git a/python/ql/lib/semmle/python/frameworks/Starlette.qll b/python/ql/lib/semmle/python/frameworks/Starlette.qll
new file mode 100644
index 00000000000..7fc46209760
--- /dev/null
+++ b/python/ql/lib/semmle/python/frameworks/Starlette.qll
@@ -0,0 +1,162 @@
+/**
+ * Provides classes modeling security-relevant aspects of the `starlette` PyPI package.
+ *
+ * See
+ * - https://pypi.org/project/starlette/
+ * - https://www.starlette.io/
+ */
+
+private import python
+private import semmle.python.dataflow.new.DataFlow
+private import semmle.python.dataflow.new.TaintTracking
+private import semmle.python.Concepts
+private import semmle.python.ApiGraphs
+private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
+private import semmle.python.frameworks.Stdlib
+
+/**
+ * INTERNAL: Do not use.
+ *
+ * Provides models for `starlette` PyPI package.
+ *
+ * See
+ * - https://pypi.org/project/starlette/
+ * - https://www.starlette.io/
+ */
+module Starlette {
+ /**
+ * Provides models for the `starlette.websockets.WebSocket` class
+ *
+ * See https://www.starlette.io/websockets/.
+ */
+ module WebSocket {
+ /** Gets a reference to the `starlette.websockets.WebSocket` class. */
+ API::Node classRef() {
+ result = API::moduleImport("starlette").getMember("websockets").getMember("WebSocket")
+ or
+ result = API::moduleImport("fastapi").getMember("WebSocket")
+ }
+
+ /**
+ * A source of instances of `starlette.websockets.WebSocket`, extend this class to model new instances.
+ *
+ * This can include instantiations of the class, return values from function
+ * calls, or a special parameter that will be set when functions are called by an external
+ * library.
+ *
+ * Use the predicate `WebSocket::instance()` to get references to instances of `starlette.websockets.WebSocket`.
+ */
+ abstract class InstanceSource extends DataFlow::LocalSourceNode { }
+
+ /** A direct instantiation of `starlette.websockets.WebSocket`. */
+ private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
+ ClassInstantiation() { this = classRef().getACall() }
+ }
+
+ /** Gets a reference to an instance of `starlette.websockets.WebSocket`. */
+ private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
+ t.start() and
+ result instanceof InstanceSource
+ or
+ exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
+ }
+
+ /** Gets a reference to an instance of `starlette.websockets.WebSocket`. */
+ DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
+
+ /**
+ * Taint propagation for `starlette.websockets.WebSocket`.
+ */
+ private class InstanceTaintSteps extends InstanceTaintStepsHelper {
+ InstanceTaintSteps() { this = "starlette.websockets.WebSocket" }
+
+ override DataFlow::Node getInstance() { result = instance() }
+
+ override string getAttributeName() { result in ["url", "headers", "query_params", "cookies"] }
+
+ override string getMethodName() { none() }
+
+ override string getAsyncMethodName() {
+ result in [
+ "receive", "receive_bytes", "receive_text", "receive_json", "iter_bytes", "iter_text",
+ "iter_json"
+ ]
+ }
+ }
+
+ /** An attribute read on a `starlette.websockets.WebSocket` instance that is a `starlette.requests.URL` instance. */
+ private class UrlInstances extends URL::InstanceSource {
+ UrlInstances() {
+ this.(DataFlow::AttrRead).getObject() = instance() and
+ this.(DataFlow::AttrRead).getAttributeName() = "url"
+ }
+ }
+ }
+
+ /**
+ * Provides models for the `starlette.requests.URL` class
+ *
+ * See the URL part of https://www.starlette.io/websockets/.
+ */
+ module URL {
+ /** Gets a reference to the `starlette.requests.URL` class. */
+ private API::Node classRef() {
+ result = API::moduleImport("starlette").getMember("requests").getMember("URL")
+ }
+
+ /**
+ * A source of instances of `starlette.requests.URL`, extend this class to model new instances.
+ *
+ * This can include instantiations of the class, return values from function
+ * calls, or a special parameter that will be set when functions are called by an external
+ * library.
+ *
+ * Use the predicate `URL::instance()` to get references to instances of `starlette.requests.URL`.
+ */
+ abstract class InstanceSource extends DataFlow::LocalSourceNode { }
+
+ /** A direct instantiation of `starlette.requests.URL`. */
+ private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
+ ClassInstantiation() { this = classRef().getACall() }
+ }
+
+ /** Gets a reference to an instance of `starlette.requests.URL`. */
+ private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
+ t.start() and
+ result instanceof InstanceSource
+ or
+ exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
+ }
+
+ /** Gets a reference to an instance of `starlette.requests.URL`. */
+ DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
+
+ /**
+ * Taint propagation for `starlette.requests.URL`.
+ */
+ private class InstanceTaintSteps extends InstanceTaintStepsHelper {
+ InstanceTaintSteps() { this = "starlette.requests.URL" }
+
+ override DataFlow::Node getInstance() { result = instance() }
+
+ override string getAttributeName() {
+ result in [
+ "components", "netloc", "path", "query", "fragment", "username", "password", "hostname",
+ "port"
+ ]
+ }
+
+ override string getMethodName() { none() }
+
+ override string getAsyncMethodName() { none() }
+ }
+
+ /** An attribute read on a `starlette.requests.URL` instance that is a `urllib.parse.SplitResult` instance. */
+ private class UrlSplitInstances extends Stdlib::SplitResult::InstanceSource {
+ UrlSplitInstances() {
+ this.(DataFlow::AttrRead).getObject() = instance() and
+ this.(DataFlow::AttrRead).getAttributeName() = "components"
+ }
+ }
+ }
+}
diff --git a/python/ql/lib/semmle/python/frameworks/Stdlib.qll b/python/ql/lib/semmle/python/frameworks/Stdlib.qll
index 973af899896..abe9afe3a39 100644
--- a/python/ql/lib/semmle/python/frameworks/Stdlib.qll
+++ b/python/ql/lib/semmle/python/frameworks/Stdlib.qll
@@ -167,6 +167,74 @@ module Stdlib {
override string getAsyncMethodName() { none() }
}
}
+
+ /**
+ * Provides models for the `urllib.parse.SplitResult` class
+ *
+ * See https://docs.python.org/3.9/library/urllib.parse.html#urllib.parse.SplitResult.
+ */
+ module SplitResult {
+ /** Gets a reference to the `urllib.parse.SplitResult` class. */
+ private API::Node classRef() {
+ result = API::moduleImport("urllib").getMember("parse").getMember("SplitResult")
+ }
+
+ /**
+ * A source of instances of `urllib.parse.SplitResult`, extend this class to model new instances.
+ *
+ * This can include instantiations of the class, return values from function
+ * calls, or a special parameter that will be set when functions are called by an external
+ * library.
+ *
+ * Use the predicate `SplitResult::instance()` to get references to instances of `urllib.parse.SplitResult`.
+ */
+ abstract class InstanceSource extends DataFlow::LocalSourceNode { }
+
+ /** A direct instantiation of `urllib.parse.SplitResult`. */
+ private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
+ ClassInstantiation() { this = classRef().getACall() }
+ }
+
+ /** Gets a reference to an instance of `urllib.parse.SplitResult`. */
+ private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
+ t.start() and
+ result instanceof InstanceSource
+ or
+ exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
+ }
+
+ /** Gets a reference to an instance of `urllib.parse.SplitResult`. */
+ DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
+
+ /**
+ * Taint propagation for `urllib.parse.SplitResult`.
+ */
+ private class InstanceTaintSteps extends InstanceTaintStepsHelper {
+ InstanceTaintSteps() { this = "urllib.parse.SplitResult" }
+
+ override DataFlow::Node getInstance() { result = instance() }
+
+ override string getAttributeName() {
+ result in [
+ "netloc", "path", "query", "fragment", "username", "password", "hostname", "port"
+ ]
+ }
+
+ override string getMethodName() { none() }
+
+ override string getAsyncMethodName() { none() }
+ }
+
+ /**
+ * Extra taint propagation for `urllib.parse.SplitResult`, not covered by `InstanceTaintSteps`.
+ */
+ private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
+ override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ // TODO
+ none()
+ }
+ }
+ }
}
/**
@@ -1749,6 +1817,30 @@ private module StdlibPrivate {
override string getKind() { result = Escaping::getRegexKind() }
}
+
+ // ---------------------------------------------------------------------------
+ // urllib
+ // ---------------------------------------------------------------------------
+ /**
+ * A call to `urllib.parse.urlsplit`
+ *
+ * See https://docs.python.org/3.9/library/urllib.parse.html#urllib.parse.urlsplit
+ */
+ class UrllibParseUrlsplitCall extends Stdlib::SplitResult::InstanceSource, DataFlow::CallCfgNode {
+ UrllibParseUrlsplitCall() {
+ this = API::moduleImport("urllib").getMember("parse").getMember("urlsplit").getACall()
+ }
+
+ /** Gets the argument that specifies the URL. */
+ DataFlow::Node getUrl() { result in [this.getArg(0), this.getArgByName("url")] }
+ }
+
+ /** Extra taint-step such that the result of `urllib.parse.urlsplit(tainted_string)` is tainted. */
+ private class UrllibParseUrlsplitCallAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
+ override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ nodeTo.(UrllibParseUrlsplitCall).getUrl() = nodeFrom
+ }
+ }
}
// ---------------------------------------------------------------------------
diff --git a/python/ql/test/library-tests/frameworks/fastapi/taint_test.py b/python/ql/test/library-tests/frameworks/fastapi/taint_test.py
index b1a43198277..591d74a887b 100644
--- a/python/ql/test/library-tests/frameworks/fastapi/taint_test.py
+++ b/python/ql/test/library-tests/frameworks/fastapi/taint_test.py
@@ -104,47 +104,47 @@ from fastapi import WebSocket
assert WebSocket == starlette.websockets.WebSocket
-@app.websocket("/ws")
-async def websocket_test(websocket: WebSocket):
+@app.websocket("/ws") # $ routeSetup="/ws"
+async def websocket_test(websocket: WebSocket): # $ requestHandler routedParameter=websocket
await websocket.accept()
ensure_tainted(
- websocket, # $ MISSING: tainted
+ websocket, # $ tainted
- websocket.url, # $ MISSING: tainted
+ websocket.url, # $ tainted
- websocket.url.netloc, # $ MISSING: tainted
- websocket.url.path, # $ MISSING: tainted
- websocket.url.query, # $ MISSING: tainted
- websocket.url.fragment, # $ MISSING: tainted
- websocket.url.username, # $ MISSING: tainted
- websocket.url.password, # $ MISSING: tainted
- websocket.url.hostname, # $ MISSING: tainted
- websocket.url.port, # $ MISSING: tainted
+ websocket.url.netloc, # $ tainted
+ websocket.url.path, # $ tainted
+ websocket.url.query, # $ tainted
+ websocket.url.fragment, # $ tainted
+ websocket.url.username, # $ tainted
+ websocket.url.password, # $ tainted
+ websocket.url.hostname, # $ tainted
+ websocket.url.port, # $ tainted
- websocket.url.components, # $ MISSING: tainted
- websocket.url.components.netloc, # $ MISSING: tainted
- websocket.url.components.path, # $ MISSING: tainted
- websocket.url.components.query, # $ MISSING: tainted
- websocket.url.components.fragment, # $ MISSING: tainted
- websocket.url.components.username, # $ MISSING: tainted
- websocket.url.components.password, # $ MISSING: tainted
- websocket.url.components.hostname, # $ MISSING: tainted
- websocket.url.components.port, # $ MISSING: tainted
+ websocket.url.components, # $ tainted
+ websocket.url.components.netloc, # $ tainted
+ websocket.url.components.path, # $ tainted
+ websocket.url.components.query, # $ tainted
+ websocket.url.components.fragment, # $ tainted
+ websocket.url.components.username, # $ tainted
+ websocket.url.components.password, # $ tainted
+ websocket.url.components.hostname, # $ tainted
+ websocket.url.components.port, # $ tainted
- websocket.headers, # $ MISSING: tainted
- websocket.headers["key"], # $ MISSING: tainted
+ websocket.headers, # $ tainted
+ websocket.headers["key"], # $ tainted
- websocket.query_params, # $ MISSING: tainted
- websocket.query_params["key"], # $ MISSING: tainted
+ websocket.query_params, # $ tainted
+ websocket.query_params["key"], # $ tainted
- websocket.cookies, # $ MISSING: tainted
- websocket.cookies["key"], # $ MISSING: tainted
+ websocket.cookies, # $ tainted
+ websocket.cookies["key"], # $ tainted
- await websocket.receive(), # $ MISSING: tainted
- await websocket.receive_bytes(), # $ MISSING: tainted
- await websocket.receive_text(), # $ MISSING: tainted
- await websocket.receive_json(), # $ MISSING: tainted
+ await websocket.receive(), # $ tainted
+ await websocket.receive_bytes(), # $ tainted
+ await websocket.receive_text(), # $ tainted
+ await websocket.receive_json(), # $ tainted
)
# scheme seems very unlikely to give interesting results, but very likely to give FPs.
@@ -154,10 +154,10 @@ async def websocket_test(websocket: WebSocket):
)
async for data in websocket.iter_bytes():
- ensure_tainted(data) # $ MISSING: tainted
+ ensure_tainted(data) # $ tainted
async for data in websocket.iter_text():
- ensure_tainted(data) # $ MISSING: tainted
+ ensure_tainted(data) # $ tainted
async for data in websocket.iter_json():
- ensure_tainted(data) # $ MISSING: tainted
+ ensure_tainted(data) # $ tainted
From ba32c5403884027c8ec56f078b886f7c65cb6ead Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Mon, 25 Oct 2021 16:11:59 +0200
Subject: [PATCH 103/471] Move files to ruby subfolder
---
.gitmodules | 2 +-
.codeqlmanifest.json => ruby/.codeqlmanifest.json | 0
.gitattributes => ruby/.gitattributes | 0
.gitignore => ruby/.gitignore | 0
{.vscode => ruby/.vscode}/tasks.json | 0
CODE_OF_CONDUCT.md => ruby/CODE_OF_CONDUCT.md | 0
Cargo.lock => ruby/Cargo.lock | Bin
Cargo.toml => ruby/Cargo.toml | 0
LICENSE => ruby/LICENSE | 0
Makefile => ruby/Makefile | 0
README.md => ruby/README.md | 0
{autobuilder => ruby/autobuilder}/Cargo.toml | 0
{autobuilder => ruby/autobuilder}/src/main.rs | 0
codeql => ruby/codeql | 0
codeql-extractor.yml => ruby/codeql-extractor.yml | 0
.../codeql-ruby.code-workspace | 0
{doc => ruby/doc}/prepare-db-upgrade.md | 0
{extractor => ruby/extractor}/Cargo.toml | 0
{extractor => ruby/extractor}/src/extractor.rs | 0
{extractor => ruby/extractor}/src/main.rs | 0
{generator => ruby/generator}/Cargo.toml | 0
{generator => ruby/generator}/src/dbscheme.rs | 0
{generator => ruby/generator}/src/language.rs | 0
{generator => ruby/generator}/src/main.rs | 0
{generator => ruby/generator}/src/ql.rs | 0
{generator => ruby/generator}/src/ql_gen.rs | 0
{node-types => ruby/node-types}/Cargo.toml | 0
{node-types => ruby/node-types}/src/lib.rs | 0
.../ql}/consistency-queries/AstConsistency.ql | 0
.../ql}/consistency-queries/CfgConsistency.ql | 0
.../ql}/consistency-queries/DataFlowConsistency.ql | 0
.../ql}/consistency-queries/SsaConsistency.ql | 0
.../ql}/consistency-queries/VariablesConsistency.ql | 0
{ql => ruby/ql}/consistency-queries/qlpack.yml | 0
{ql => ruby/ql}/docs/experimental.md | 0
{ql => ruby/ql}/examples/qlpack.lock.yml | 0
{ql => ruby/ql}/examples/qlpack.yml | 0
{ql => ruby/ql}/examples/queries.xml | 0
{ql => ruby/ql}/examples/snippets/emptythen.ql | 0
{ql => ruby/ql}/lib/codeql/IDEContextual.qll | 0
{ql => ruby/ql}/lib/codeql/Locations.qll | 0
{ql => ruby/ql}/lib/codeql/files/FileSystem.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/AST.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/ApiGraphs.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/CFG.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/Concepts.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/DataFlow.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/Diagnostics.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/Frameworks.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/TaintTracking.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/ast/Call.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/ast/Constant.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/ast/Control.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/ast/Erb.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/ast/Expr.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/ast/Literal.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/ast/Method.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/ast/Module.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/ast/Operation.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/ast/Parameter.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/ast/Pattern.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/ast/Scope.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/ast/Statement.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/ast/Variable.qll | 0
.../ql}/lib/codeql/ruby/ast/internal/AST.qll | 0
.../ql}/lib/codeql/ruby/ast/internal/Call.qll | 0
.../ql}/lib/codeql/ruby/ast/internal/Erb.qll | 0
.../ql}/lib/codeql/ruby/ast/internal/Module.qll | 0
.../ql}/lib/codeql/ruby/ast/internal/Operation.qll | 0
.../ql}/lib/codeql/ruby/ast/internal/Parameter.qll | 0
.../ql}/lib/codeql/ruby/ast/internal/Pattern.qll | 0
.../ql}/lib/codeql/ruby/ast/internal/Scope.qll | 0
.../ql}/lib/codeql/ruby/ast/internal/Synthesis.qll | 0
.../ql}/lib/codeql/ruby/ast/internal/TreeSitter.qll | 0
.../ql}/lib/codeql/ruby/ast/internal/Variable.qll | 0
.../ql}/lib/codeql/ruby/controlflow/BasicBlocks.qll | 0
.../ql}/lib/codeql/ruby/controlflow/CfgNodes.qll | 0
.../codeql/ruby/controlflow/ControlFlowGraph.qll | 0
.../codeql/ruby/controlflow/internal/Completion.qll | 0
.../controlflow/internal/ControlFlowGraphImpl.qll | 0
.../internal/ControlFlowGraphImplShared.qll | 0
.../internal/ControlFlowGraphImplSpecific.qll | 0
.../ruby/controlflow/internal/NonReturning.qll | 0
.../codeql/ruby/controlflow/internal/Splitting.qll | 0
.../ql}/lib/codeql/ruby/dataflow/BarrierGuards.qll | 0
.../ql}/lib/codeql/ruby/dataflow/FlowSummary.qll | 0
.../lib/codeql/ruby/dataflow/RemoteFlowSources.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/dataflow/SSA.qll | 0
.../ruby/dataflow/internal/DataFlowDispatch.qll | 0
.../codeql/ruby/dataflow/internal/DataFlowImpl.qll | 0
.../ruby/dataflow/internal/DataFlowImplCommon.qll | 0
.../dataflow/internal/DataFlowImplConsistency.qll | 0
.../ruby/dataflow/internal/DataFlowImplSpecific.qll | 0
.../ruby/dataflow/internal/DataFlowPrivate.qll | 0
.../ruby/dataflow/internal/DataFlowPublic.qll | 0
.../ruby/dataflow/internal/FlowSummaryImpl.qll | 0
.../dataflow/internal/FlowSummaryImplSpecific.qll | 0
.../lib/codeql/ruby/dataflow/internal/SsaImpl.qll | 0
.../codeql/ruby/dataflow/internal/SsaImplCommon.qll | 0
.../ruby/dataflow/internal/SsaImplSpecific.qll | 0
.../ruby/dataflow/internal/TaintTrackingPrivate.qll | 0
.../ruby/dataflow/internal/TaintTrackingPublic.qll | 0
.../internal/tainttracking1/TaintTrackingImpl.qll | 0
.../tainttracking1/TaintTrackingParameter.qll | 0
.../ql}/lib/codeql/ruby/filters/GeneratedCode.qll | 0
.../lib/codeql/ruby/frameworks/ActionController.qll | 0
.../ql}/lib/codeql/ruby/frameworks/ActionView.qll | 0
.../ql}/lib/codeql/ruby/frameworks/ActiveRecord.qll | 0
.../ql}/lib/codeql/ruby/frameworks/Files.qll | 0
.../ql}/lib/codeql/ruby/frameworks/HTTPClients.qll | 0
.../lib/codeql/ruby/frameworks/StandardLibrary.qll | 0
.../codeql/ruby/frameworks/http_clients/Excon.qll | 0
.../codeql/ruby/frameworks/http_clients/Faraday.qll | 0
.../codeql/ruby/frameworks/http_clients/NetHTTP.qll | 0
.../ruby/frameworks/http_clients/RestClient.qll | 0
{ql => ruby/ql}/lib/codeql/ruby/printAst.qll | 0
.../codeql/ruby/regexp/ExponentialBackTracking.qll | 0
.../ql}/lib/codeql/ruby/regexp/ParseRegExp.qll | 0
.../ruby/regexp/PolynomialReDoSCustomizations.qll | 0
.../lib/codeql/ruby/regexp/PolynomialReDoSQuery.qll | 0
.../ql}/lib/codeql/ruby/regexp/ReDoSUtil.qll | 0
.../ql}/lib/codeql/ruby/regexp/RegExpTreeView.qll | 0
.../codeql/ruby/regexp/SuperlinearBackTracking.qll | 0
.../ruby/security/CodeInjectionCustomizations.qll | 0
.../lib/codeql/ruby/security/CodeInjectionQuery.qll | 0
.../security/CommandInjectionCustomizations.qll | 0
.../codeql/ruby/security/CommandInjectionQuery.qll | 0
.../ruby/security/ReflectedXSSCustomizations.qll | 0
.../lib/codeql/ruby/security/ReflectedXSSQuery.qll | 0
.../UnsafeDeserializationCustomizations.qll | 0
.../ruby/security/UnsafeDeserializationQuery.qll | 0
.../ruby/security/UrlRedirectCustomizations.qll | 0
.../lib/codeql/ruby/security/UrlRedirectQuery.qll | 0
.../lib/codeql/ruby/typetracking/TypeTracker.qll | 0
.../ruby/typetracking/TypeTrackerSpecific.qll | 0
{ql => ruby/ql}/lib/qlpack.lock.yml | 0
{ql => ruby/ql}/lib/qlpack.yml | 0
{ql => ruby/ql}/lib/ruby.dbscheme | 0
{ql => ruby/ql}/lib/ruby.dbscheme.stats | 0
{ql => ruby/ql}/lib/ruby.qll | 0
.../old.dbscheme | 0
.../ruby.dbscheme | 0
.../upgrade.properties | 0
.../old.dbscheme | 0
.../ruby.dbscheme | 0
.../upgrade.properties | 0
.../old.dbscheme | 0
.../ruby.dbscheme | 0
.../ruby.dbscheme.stats | 0
.../upgrade.properties | 0
.../old.dbscheme | 0
.../ruby.dbscheme | 0
.../upgrade.properties | 0
.../old.dbscheme | 0
.../ruby.dbscheme | 0
.../upgrade.properties | 0
{ql => ruby/ql}/lib/upgrades/initial/ruby.dbscheme | 0
{ql => ruby/ql}/src/AlertSuppression.ql | 0
.../ql}/src/codeql-suites/ruby-code-scanning.qls | 0
.../ql}/src/codeql-suites/ruby-lgtm-full.qls | 0
{ql => ruby/ql}/src/codeql-suites/ruby-lgtm.qls | 0
.../src/codeql-suites/ruby-security-and-quality.qls | 0
.../src/codeql-suites/ruby-security-extended.qls | 0
{ql => ruby/ql}/src/experimental/README.md | 0
.../ql}/src/experimental/performance/UseDetect.ql | 0
{ql => ruby/ql}/src/filters/ClassifyFiles.ql | 0
.../src/ide-contextual-queries/localDefinitions.ql | 0
.../src/ide-contextual-queries/localReferences.ql | 0
.../ql}/src/ide-contextual-queries/printAst.ql | 0
{ql => ruby/ql}/src/qlpack.lock.yml | 0
{ql => ruby/ql}/src/qlpack.yml | 0
{ql => ruby/ql}/src/queries/analysis/Definitions.ql | 0
.../ql}/src/queries/diagnostics/ExtractionErrors.ql | 0
.../diagnostics/SuccessfullyExtractedFiles.ql | 0
{ql => ruby/ql}/src/queries/metrics/FLines.ql | 0
{ql => ruby/ql}/src/queries/metrics/FLinesOfCode.ql | 0
.../ql}/src/queries/metrics/FLinesOfComments.ql | 0
.../queries/security/cwe-078/CommandInjection.qhelp | 0
.../queries/security/cwe-078/CommandInjection.ql | 0
.../security/cwe-078/examples/command_injection.rb | 0
.../src/queries/security/cwe-079/ReflectedXSS.qhelp | 0
.../src/queries/security/cwe-079/ReflectedXSS.ql | 0
.../cwe-079/examples/reflective_xss.html.erb | 0
.../queries/security/cwe-079/examples/safe.html.erb | 0
.../src/queries/security/cwe-089/SqlInjection.qhelp | 0
.../src/queries/security/cwe-089/SqlInjection.ql | 0
.../security/cwe-089/examples/SqlInjection.rb | 0
.../queries/security/cwe-094/CodeInjection.qhelp | 0
.../src/queries/security/cwe-094/CodeInjection.ql | 0
.../security/cwe-094/examples/code_injection.rb | 0
.../queries/security/cwe-1333/PolynomialReDoS.qhelp | 0
.../queries/security/cwe-1333/PolynomialReDoS.ql | 0
.../ql}/src/queries/security/cwe-1333/ReDoS.qhelp | 0
.../ql}/src/queries/security/cwe-1333/ReDoS.ql | 0
.../security/cwe-1333/ReDoSIntroduction.inc.qhelp | 0
.../security/cwe-1333/ReDoSReferences.inc.qhelp | 0
.../security/cwe-502/UnsafeDeserialization.qhelp | 0
.../security/cwe-502/UnsafeDeserialization.ql | 0
.../cwe-502/examples/UnsafeDeserializationBad.rb | 0
.../cwe-502/examples/UnsafeDeserializationGood.rb | 0
.../src/queries/security/cwe-601/UrlRedirect.qhelp | 0
.../ql}/src/queries/security/cwe-601/UrlRedirect.ql | 0
.../security/cwe-601/examples/redirect_bad.rb | 0
.../security/cwe-601/examples/redirect_good.rb | 0
.../security/cwe-732/WeakFilePermissions.qhelp | 0
.../queries/security/cwe-732/WeakFilePermissions.ql | 0
.../security/cwe-798/HardcodedCredentials.qhelp | 0
.../security/cwe-798/HardcodedCredentials.ql | 0
.../security/cwe-798/HardcodedCredentials.rb | 0
{ql => ruby/ql}/src/queries/summary/LinesOfCode.ql | 0
.../ql}/src/queries/summary/LinesOfUserCode.ql | 0
.../summary/NumberOfFilesExtractedWithErrors.ql | 0
.../summary/NumberOfSuccessfullyExtractedFiles.ql | 0
.../ql}/src/queries/variables/DeadStoreOfLocal.ql | 0
.../ql}/src/queries/variables/UninitializedLocal.ql | 0
.../ql}/src/queries/variables/UnusedParameter.ql | 0
.../test/TestUtilities/InlineExpectationsTest.qll | 0
.../TestUtilities/InlineExpectationsTestPrivate.qll | 0
{ql => ruby/ql}/test/library-tests/ast/Ast.expected | 0
{ql => ruby/ql}/test/library-tests/ast/Ast.ql | 0
.../ql}/test/library-tests/ast/AstDesugar.expected | 0
.../ql}/test/library-tests/ast/AstDesugar.ql | 0
.../test/library-tests/ast/calls/arguments.expected | 0
.../ql}/test/library-tests/ast/calls/arguments.ql | 0
.../ql}/test/library-tests/ast/calls/calls.expected | 0
.../ql}/test/library-tests/ast/calls/calls.ql | 0
.../ql}/test/library-tests/ast/calls/calls.rb | 0
.../library-tests/ast/constants/constants.expected | 0
.../test/library-tests/ast/constants/constants.ql | 0
.../test/library-tests/ast/constants/constants.rb | 0
.../library-tests/ast/control/CaseExpr.expected | 0
.../ql}/test/library-tests/ast/control/CaseExpr.ql | 0
.../ast/control/ConditionalExpr.expected | 0
.../library-tests/ast/control/ConditionalExpr.ql | 0
.../library-tests/ast/control/ControlExpr.expected | 0
.../test/library-tests/ast/control/ControlExpr.ql | 0
.../test/library-tests/ast/control/Loop.expected | 0
.../ql}/test/library-tests/ast/control/Loop.ql | 0
.../ql}/test/library-tests/ast/control/cases.rb | 0
.../test/library-tests/ast/control/conditionals.rb | 0
.../ql}/test/library-tests/ast/control/loops.rb | 0
.../ql}/test/library-tests/ast/erb/Erb.expected | 0
{ql => ruby/ql}/test/library-tests/ast/erb/Erb.ql | 0
.../test/library-tests/ast/erb/template.html.erb | 0
{ql => ruby/ql}/test/library-tests/ast/gems/Gemfile | 0
.../ql}/test/library-tests/ast/gems/lib/test.rb | 0
.../ql}/test/library-tests/ast/gems/test.expected | 0
.../ql}/test/library-tests/ast/gems/test.gemspec | 0
{ql => ruby/ql}/test/library-tests/ast/gems/test.ql | 0
.../library-tests/ast/literals/literals.expected | 0
.../ql}/test/library-tests/ast/literals/literals.ql | 0
.../ql}/test/library-tests/ast/literals/literals.rb | 0
.../ql}/test/library-tests/ast/misc/misc.erb | 0
.../ql}/test/library-tests/ast/misc/misc.expected | 0
{ql => ruby/ql}/test/library-tests/ast/misc/misc.ql | 0
{ql => ruby/ql}/test/library-tests/ast/misc/misc.rb | 0
.../test/library-tests/ast/modules/classes.expected | 0
.../ql}/test/library-tests/ast/modules/classes.ql | 0
.../ql}/test/library-tests/ast/modules/classes.rb | 0
.../library-tests/ast/modules/module_base.expected | 0
.../test/library-tests/ast/modules/module_base.ql | 0
.../test/library-tests/ast/modules/modules.expected | 0
.../ql}/test/library-tests/ast/modules/modules.ql | 0
.../ql}/test/library-tests/ast/modules/modules.rb | 0
.../ast/modules/singleton_classes.expected | 0
.../library-tests/ast/modules/singleton_classes.ql | 0
.../library-tests/ast/modules/toplevel.expected | 0
.../ql}/test/library-tests/ast/modules/toplevel.ql | 0
.../ql}/test/library-tests/ast/modules/toplevel.rb | 0
.../ast/operations/assignment.expected | 0
.../test/library-tests/ast/operations/assignment.ql | 0
.../library-tests/ast/operations/binary.expected | 0
.../ql}/test/library-tests/ast/operations/binary.ql | 0
.../library-tests/ast/operations/operation.expected | 0
.../test/library-tests/ast/operations/operation.ql | 0
.../test/library-tests/ast/operations/operations.rb | 0
.../library-tests/ast/operations/unary.expected | 0
.../ql}/test/library-tests/ast/operations/unary.ql | 0
.../test/library-tests/ast/params/params.expected | 0
.../ql}/test/library-tests/ast/params/params.ql | 0
.../ql}/test/library-tests/ast/params/params.rb | 0
.../library-tests/controlflow/graph/Cfg.expected | 0
.../ql}/test/library-tests/controlflow/graph/Cfg.ql | 0
.../library-tests/controlflow/graph/break_ensure.rb | 0
.../test/library-tests/controlflow/graph/case.rb | 0
.../library-tests/controlflow/graph/cfg.html.erb | 0
.../ql}/test/library-tests/controlflow/graph/cfg.rb | 0
.../test/library-tests/controlflow/graph/desugar.rb | 0
.../test/library-tests/controlflow/graph/exit.rb | 0
.../test/library-tests/controlflow/graph/heredoc.rb | 0
.../ql}/test/library-tests/controlflow/graph/ifs.rb | 0
.../test/library-tests/controlflow/graph/loops.rb | 0
.../test/library-tests/controlflow/graph/raise.rb | 0
.../test/library-tests/dataflow/api-graphs/test1.rb | 0
.../library-tests/dataflow/api-graphs/use.expected | 0
.../test/library-tests/dataflow/api-graphs/use.ql | 0
.../dataflow/barrier-guards/barrier-guards.expected | 0
.../dataflow/barrier-guards/barrier-guards.ql | 0
.../dataflow/barrier-guards/barrier-guards.rb | 0
.../call-sensitivity/call-sensitivity.expected | 0
.../dataflow/call-sensitivity/call-sensitivity.ql | 0
.../dataflow/call-sensitivity/call_sensitivity.rb | 0
.../dataflow/local/DataflowStep.expected | 0
.../library-tests/dataflow/local/DataflowStep.ql | 0
.../dataflow/local/ReturnNodes.expected | 0
.../library-tests/dataflow/local/ReturnNodes.ql | 0
.../library-tests/dataflow/local/local_dataflow.rb | 0
.../dataflow/summaries/Summaries.expected | 0
.../library-tests/dataflow/summaries/Summaries.ql | 0
.../library-tests/dataflow/summaries/summaries.rb | 0
.../frameworks/ActionController.expected | 0
.../library-tests/frameworks/ActionController.ql | 0
.../library-tests/frameworks/ActionView.expected | 0
.../ql}/test/library-tests/frameworks/ActionView.ql | 0
.../library-tests/frameworks/ActiveRecord.expected | 0
.../test/library-tests/frameworks/ActiveRecord.ql | 0
.../frameworks/ActiveRecordInjection.rb | 0
.../library-tests/frameworks/CommandExecution.rb | 0
.../ql}/test/library-tests/frameworks/Eval.rb | 0
.../test/library-tests/frameworks/Files.expected | 0
.../ql}/test/library-tests/frameworks/Files.ql | 0
.../ql}/test/library-tests/frameworks/Files.rb | 0
.../frameworks/StandardLibrary.expected | 0
.../library-tests/frameworks/StandardLibrary.ql | 0
.../frameworks/app/components/DummyComponent.rb | 0
.../app/controllers/foo/bars_controller.rb | 0
.../frameworks/app/views/foo/bars/_widget.html.erb | 0
.../frameworks/app/views/foo/bars/show.html.erb | 0
.../frameworks/http_clients/Excon.expected | 0
.../library-tests/frameworks/http_clients/Excon.ql | 0
.../library-tests/frameworks/http_clients/Excon.rb | 0
.../frameworks/http_clients/Faraday.expected | 0
.../frameworks/http_clients/Faraday.ql | 0
.../frameworks/http_clients/Faraday.rb | 0
.../frameworks/http_clients/NetHTTP.expected | 0
.../frameworks/http_clients/NetHTTP.ql | 0
.../frameworks/http_clients/NetHTTP.rb | 0
.../frameworks/http_clients/RestClient.expected | 0
.../frameworks/http_clients/RestClient.ql | 0
.../frameworks/http_clients/RestClient.rb | 0
.../test/library-tests/modules/ancestors.expected | 0
.../ql}/test/library-tests/modules/ancestors.ql | 0
.../test/library-tests/modules/callgraph.expected | 0
.../ql}/test/library-tests/modules/callgraph.ql | 0
{ql => ruby/ql}/test/library-tests/modules/calls.rb | 0
{ql => ruby/ql}/test/library-tests/modules/hello.rb | 0
.../ql}/test/library-tests/modules/methods.expected | 0
.../ql}/test/library-tests/modules/methods.ql | 0
.../ql}/test/library-tests/modules/modules.expected | 0
.../ql}/test/library-tests/modules/modules.ql | 0
.../ql}/test/library-tests/modules/modules.rb | 0
.../ql}/test/library-tests/modules/private.rb | 0
.../library-tests/modules/superclasses.expected | 0
.../ql}/test/library-tests/modules/superclasses.ql | 0
.../ql}/test/library-tests/regexp/parse.expected | 0
{ql => ruby/ql}/test/library-tests/regexp/parse.ql | 0
{ql => ruby/ql}/test/library-tests/regexp/regexp.rb | 0
.../test/library-tests/variables/class_variables.rb | 0
.../library-tests/variables/instance_variables.rb | 0
.../test/library-tests/variables/nested_scopes.rb | 0
.../test/library-tests/variables/parameter.expected | 0
.../ql}/test/library-tests/variables/parameter.ql | 0
.../ql}/test/library-tests/variables/parameters.rb | 0
.../ql}/test/library-tests/variables/scopes.rb | 0
.../ql}/test/library-tests/variables/ssa.expected | 0
{ql => ruby/ql}/test/library-tests/variables/ssa.ql | 0
{ql => ruby/ql}/test/library-tests/variables/ssa.rb | 0
.../test/library-tests/variables/varaccess.expected | 0
.../ql}/test/library-tests/variables/varaccess.ql | 0
.../test/library-tests/variables/variable.expected | 0
.../ql}/test/library-tests/variables/variable.ql | 0
.../test/library-tests/variables/varscopes.expected | 0
.../ql}/test/library-tests/variables/varscopes.ql | 0
{ql => ruby/ql}/test/qlpack.lock.yml | 0
{ql => ruby/ql}/test/qlpack.yml | 0
.../query-tests/AlertSuppression/.gitattributes | 0
.../AlertSuppression/AlertSuppression.expected | 0
.../AlertSuppression/AlertSuppression.qlref | 0
.../ql}/test/query-tests/AlertSuppression/Test.rb | 0
.../query-tests/AlertSuppression/TestWindows.rb | 0
.../test/query-tests/analysis/Definitions.expected | 0
.../ql}/test/query-tests/analysis/Definitions.qlref | 0
.../ql}/test/query-tests/analysis/Definitions.rb | 0
.../diagnostics/ExtractionErrors.expected | 0
.../query-tests/diagnostics/ExtractionErrors.qlref | 0
.../NumberOfFilesExtractedWithErrors.expected | 0
.../NumberOfFilesExtractedWithErrors.qlref | 0
.../NumberOfSuccessfullyExtractedFiles.expected | 0
.../NumberOfSuccessfullyExtractedFiles.qlref | 0
.../diagnostics/SuccessfullyExtractedFiles.expected | 0
.../diagnostics/SuccessfullyExtractedFiles.qlref | 0
.../ql}/test/query-tests/diagnostics/src/bar.erb | 0
.../ql}/test/query-tests/diagnostics/src/foo.rb | 0
.../test/query-tests/diagnostics/src/not_ruby.rb | 0
.../diagnostics/src/unsupported_feature.rb | 0
.../query-tests/diagnostics/src/vendor/cache/lib.rb | 0
.../ql}/test/query-tests/metrics/FLines/Empty.rb | 0
.../test/query-tests/metrics/FLines/FLines.expected | 0
.../test/query-tests/metrics/FLines/FLines.qlref | 0
.../ql}/test/query-tests/metrics/FLines/FLines.rb | 0
.../metrics/FLines/FLinesOfCode.expected | 0
.../query-tests/metrics/FLines/FLinesOfCode.qlref | 0
.../metrics/FLines/FLinesOfComments.expected | 0
.../metrics/FLines/FLinesOfComments.qlref | 0
.../performance/UseDetect/UseDetect.expected | 0
.../performance/UseDetect/UseDetect.qlref | 0
.../query-tests/performance/UseDetect/UseDetect.rb | 0
.../security/cwe-078/CommandInjection.expected | 0
.../security/cwe-078/CommandInjection.qlref | 0
.../security/cwe-078/CommandInjection.rb | 0
.../security/cwe-079/ReflectedXSS.expected | 0
.../query-tests/security/cwe-079/ReflectedXSS.qlref | 0
.../cwe-079/app/controllers/foo/bars_controller.rb | 0
.../cwe-079/app/views/foo/bars/_widget.html.erb | 0
.../cwe-079/app/views/foo/bars/show.html.erb | 0
.../security/cwe-089/ActiveRecordInjection.rb | 0
.../security/cwe-089/SqlInjection.expected | 0
.../query-tests/security/cwe-089/SqlInjection.qlref | 0
.../security/cwe-094/CodeInjection.expected | 0
.../security/cwe-094/CodeInjection.qlref | 0
.../query-tests/security/cwe-094/CodeInjection.rb | 0
.../cwe-1333-exponential-redos/ANodeBlog-LICENSE | 0
.../cwe-1333-exponential-redos/CodeMirror-LICENSE | 0
.../cwe-1333-exponential-redos/Prism-LICENSE | 0
.../cwe-1333-exponential-redos/Prototype.js-LICENSE | 0
.../cwe-1333-exponential-redos/ReDoS.expected | 0
.../security/cwe-1333-exponential-redos/ReDoS.qlref | 0
.../brace-expansion-LICENSE | 0
.../cwe-1333-exponential-redos/jest-LICENSE | 0
.../cwe-1333-exponential-redos/knockout-LICENSE | 0
.../cwe-1333-exponential-redos/marked-LICENSE | 0
.../security/cwe-1333-exponential-redos/tst.rb | 0
.../PolynomialReDoS.expected | 0
.../cwe-1333-polynomial-redos/PolynomialReDoS.qlref | 0
.../cwe-1333-polynomial-redos/PolynomialReDoS.rb | 0
.../security/cwe-502/UnsafeDeserialization.expected | 0
.../security/cwe-502/UnsafeDeserialization.qlref | 0
.../security/cwe-502/UnsafeDeserialization.rb | 0
.../security/cwe-601/UrlRedirect.expected | 0
.../query-tests/security/cwe-601/UrlRedirect.qlref | 0
.../query-tests/security/cwe-601/UrlRedirect.rb | 0
.../query-tests/security/cwe-732/FilePermissions.rb | 0
.../security/cwe-732/WeakFilePermissions.expected | 0
.../security/cwe-732/WeakFilePermissions.qlref | 0
.../security/cwe-798/HardcodedCredentials.expected | 0
.../security/cwe-798/HardcodedCredentials.qlref | 0
.../security/cwe-798/HardcodedCredentials.rb | 0
.../test/query-tests/summary/LinesOfCode.expected | 0
.../ql}/test/query-tests/summary/LinesOfCode.qlref | 0
.../query-tests/summary/LinesOfUserCode.expected | 0
.../test/query-tests/summary/LinesOfUserCode.qlref | 0
{ql => ruby/ql}/test/query-tests/summary/src/foo.rb | 0
.../query-tests/summary/src/vendor/cache/lib.rb | 0
{scripts => ruby/scripts}/create-extractor-pack.ps1 | 0
{scripts => ruby/scripts}/create-extractor-pack.sh | 0
{scripts => ruby/scripts}/identical-files.json | 0
{scripts => ruby/scripts}/merge_stats.py | 0
{scripts => ruby/scripts}/prepare-db-upgrade.sh | 0
{scripts => ruby/scripts}/sync-identical-files.py | 0
{tools => ruby/tools}/autobuild.cmd | 0
{tools => ruby/tools}/autobuild.sh | 0
{tools => ruby/tools}/index-files.cmd | 0
{tools => ruby/tools}/index-files.sh | 0
{tools => ruby/tools}/qltest.cmd | 0
{tools => ruby/tools}/qltest.sh | 0
465 files changed, 1 insertion(+), 1 deletion(-)
rename .codeqlmanifest.json => ruby/.codeqlmanifest.json (100%)
rename .gitattributes => ruby/.gitattributes (100%)
rename .gitignore => ruby/.gitignore (100%)
rename {.vscode => ruby/.vscode}/tasks.json (100%)
rename CODE_OF_CONDUCT.md => ruby/CODE_OF_CONDUCT.md (100%)
rename Cargo.lock => ruby/Cargo.lock (100%)
rename Cargo.toml => ruby/Cargo.toml (100%)
rename LICENSE => ruby/LICENSE (100%)
rename Makefile => ruby/Makefile (100%)
rename README.md => ruby/README.md (100%)
rename {autobuilder => ruby/autobuilder}/Cargo.toml (100%)
rename {autobuilder => ruby/autobuilder}/src/main.rs (100%)
rename codeql => ruby/codeql (100%)
rename codeql-extractor.yml => ruby/codeql-extractor.yml (100%)
rename codeql-ruby.code-workspace => ruby/codeql-ruby.code-workspace (100%)
rename {doc => ruby/doc}/prepare-db-upgrade.md (100%)
rename {extractor => ruby/extractor}/Cargo.toml (100%)
rename {extractor => ruby/extractor}/src/extractor.rs (100%)
rename {extractor => ruby/extractor}/src/main.rs (100%)
rename {generator => ruby/generator}/Cargo.toml (100%)
rename {generator => ruby/generator}/src/dbscheme.rs (100%)
rename {generator => ruby/generator}/src/language.rs (100%)
rename {generator => ruby/generator}/src/main.rs (100%)
rename {generator => ruby/generator}/src/ql.rs (100%)
rename {generator => ruby/generator}/src/ql_gen.rs (100%)
rename {node-types => ruby/node-types}/Cargo.toml (100%)
rename {node-types => ruby/node-types}/src/lib.rs (100%)
rename {ql => ruby/ql}/consistency-queries/AstConsistency.ql (100%)
rename {ql => ruby/ql}/consistency-queries/CfgConsistency.ql (100%)
rename {ql => ruby/ql}/consistency-queries/DataFlowConsistency.ql (100%)
rename {ql => ruby/ql}/consistency-queries/SsaConsistency.ql (100%)
rename {ql => ruby/ql}/consistency-queries/VariablesConsistency.ql (100%)
rename {ql => ruby/ql}/consistency-queries/qlpack.yml (100%)
rename {ql => ruby/ql}/docs/experimental.md (100%)
rename {ql => ruby/ql}/examples/qlpack.lock.yml (100%)
rename {ql => ruby/ql}/examples/qlpack.yml (100%)
rename {ql => ruby/ql}/examples/queries.xml (100%)
rename {ql => ruby/ql}/examples/snippets/emptythen.ql (100%)
rename {ql => ruby/ql}/lib/codeql/IDEContextual.qll (100%)
rename {ql => ruby/ql}/lib/codeql/Locations.qll (100%)
rename {ql => ruby/ql}/lib/codeql/files/FileSystem.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/AST.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ApiGraphs.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/CFG.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/Concepts.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/DataFlow.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/Diagnostics.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/Frameworks.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/TaintTracking.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/Call.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/Constant.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/Control.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/Erb.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/Expr.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/Literal.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/Method.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/Module.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/Operation.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/Parameter.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/Pattern.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/Scope.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/Statement.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/Variable.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/internal/AST.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/internal/Call.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/internal/Erb.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/internal/Module.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/internal/Operation.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/internal/Parameter.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/internal/Pattern.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/internal/Scope.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/internal/Synthesis.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/internal/TreeSitter.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/ast/internal/Variable.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/controlflow/BasicBlocks.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/controlflow/CfgNodes.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/controlflow/ControlFlowGraph.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/controlflow/internal/Completion.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplSpecific.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/controlflow/internal/NonReturning.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/controlflow/internal/Splitting.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/dataflow/BarrierGuards.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/dataflow/FlowSummary.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/dataflow/RemoteFlowSources.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/dataflow/SSA.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/dataflow/internal/DataFlowImplSpecific.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/dataflow/internal/FlowSummaryImplSpecific.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/dataflow/internal/SsaImpl.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/dataflow/internal/SsaImplSpecific.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/dataflow/internal/TaintTrackingPublic.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingParameter.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/filters/GeneratedCode.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/frameworks/ActionController.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/frameworks/ActionView.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/frameworks/ActiveRecord.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/frameworks/Files.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/frameworks/HTTPClients.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/frameworks/StandardLibrary.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/frameworks/http_clients/Excon.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/frameworks/http_clients/Faraday.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/frameworks/http_clients/NetHTTP.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/frameworks/http_clients/RestClient.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/printAst.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/regexp/ExponentialBackTracking.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/regexp/ParseRegExp.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/regexp/PolynomialReDoSCustomizations.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/regexp/PolynomialReDoSQuery.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/regexp/ReDoSUtil.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/regexp/RegExpTreeView.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/regexp/SuperlinearBackTracking.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/security/CodeInjectionCustomizations.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/security/CodeInjectionQuery.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/security/CommandInjectionCustomizations.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/security/CommandInjectionQuery.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/security/ReflectedXSSCustomizations.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/security/ReflectedXSSQuery.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/security/UnsafeDeserializationQuery.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/security/UrlRedirectCustomizations.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/security/UrlRedirectQuery.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/typetracking/TypeTracker.qll (100%)
rename {ql => ruby/ql}/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll (100%)
rename {ql => ruby/ql}/lib/qlpack.lock.yml (100%)
rename {ql => ruby/ql}/lib/qlpack.yml (100%)
rename {ql => ruby/ql}/lib/ruby.dbscheme (100%)
rename {ql => ruby/ql}/lib/ruby.dbscheme.stats (100%)
rename {ql => ruby/ql}/lib/ruby.qll (100%)
rename {ql => ruby/ql}/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/old.dbscheme (100%)
rename {ql => ruby/ql}/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/ruby.dbscheme (100%)
rename {ql => ruby/ql}/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/upgrade.properties (100%)
rename {ql => ruby/ql}/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/old.dbscheme (100%)
rename {ql => ruby/ql}/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/ruby.dbscheme (100%)
rename {ql => ruby/ql}/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/upgrade.properties (100%)
rename {ql => ruby/ql}/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/old.dbscheme (100%)
rename {ql => ruby/ql}/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/ruby.dbscheme (100%)
rename {ql => ruby/ql}/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/ruby.dbscheme.stats (100%)
rename {ql => ruby/ql}/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/upgrade.properties (100%)
rename {ql => ruby/ql}/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/old.dbscheme (100%)
rename {ql => ruby/ql}/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/ruby.dbscheme (100%)
rename {ql => ruby/ql}/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/upgrade.properties (100%)
rename {ql => ruby/ql}/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/old.dbscheme (100%)
rename {ql => ruby/ql}/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/ruby.dbscheme (100%)
rename {ql => ruby/ql}/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/upgrade.properties (100%)
rename {ql => ruby/ql}/lib/upgrades/initial/ruby.dbscheme (100%)
rename {ql => ruby/ql}/src/AlertSuppression.ql (100%)
rename {ql => ruby/ql}/src/codeql-suites/ruby-code-scanning.qls (100%)
rename {ql => ruby/ql}/src/codeql-suites/ruby-lgtm-full.qls (100%)
rename {ql => ruby/ql}/src/codeql-suites/ruby-lgtm.qls (100%)
rename {ql => ruby/ql}/src/codeql-suites/ruby-security-and-quality.qls (100%)
rename {ql => ruby/ql}/src/codeql-suites/ruby-security-extended.qls (100%)
rename {ql => ruby/ql}/src/experimental/README.md (100%)
rename {ql => ruby/ql}/src/experimental/performance/UseDetect.ql (100%)
rename {ql => ruby/ql}/src/filters/ClassifyFiles.ql (100%)
rename {ql => ruby/ql}/src/ide-contextual-queries/localDefinitions.ql (100%)
rename {ql => ruby/ql}/src/ide-contextual-queries/localReferences.ql (100%)
rename {ql => ruby/ql}/src/ide-contextual-queries/printAst.ql (100%)
rename {ql => ruby/ql}/src/qlpack.lock.yml (100%)
rename {ql => ruby/ql}/src/qlpack.yml (100%)
rename {ql => ruby/ql}/src/queries/analysis/Definitions.ql (100%)
rename {ql => ruby/ql}/src/queries/diagnostics/ExtractionErrors.ql (100%)
rename {ql => ruby/ql}/src/queries/diagnostics/SuccessfullyExtractedFiles.ql (100%)
rename {ql => ruby/ql}/src/queries/metrics/FLines.ql (100%)
rename {ql => ruby/ql}/src/queries/metrics/FLinesOfCode.ql (100%)
rename {ql => ruby/ql}/src/queries/metrics/FLinesOfComments.ql (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-078/CommandInjection.qhelp (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-078/CommandInjection.ql (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-078/examples/command_injection.rb (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-079/ReflectedXSS.qhelp (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-079/ReflectedXSS.ql (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-079/examples/reflective_xss.html.erb (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-079/examples/safe.html.erb (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-089/SqlInjection.qhelp (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-089/SqlInjection.ql (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-089/examples/SqlInjection.rb (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-094/CodeInjection.qhelp (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-094/CodeInjection.ql (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-094/examples/code_injection.rb (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-1333/PolynomialReDoS.qhelp (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-1333/PolynomialReDoS.ql (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-1333/ReDoS.qhelp (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-1333/ReDoS.ql (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-1333/ReDoSIntroduction.inc.qhelp (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-1333/ReDoSReferences.inc.qhelp (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-502/UnsafeDeserialization.qhelp (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-502/UnsafeDeserialization.ql (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-502/examples/UnsafeDeserializationBad.rb (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-502/examples/UnsafeDeserializationGood.rb (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-601/UrlRedirect.qhelp (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-601/UrlRedirect.ql (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-601/examples/redirect_bad.rb (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-601/examples/redirect_good.rb (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-732/WeakFilePermissions.qhelp (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-732/WeakFilePermissions.ql (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-798/HardcodedCredentials.qhelp (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-798/HardcodedCredentials.ql (100%)
rename {ql => ruby/ql}/src/queries/security/cwe-798/HardcodedCredentials.rb (100%)
rename {ql => ruby/ql}/src/queries/summary/LinesOfCode.ql (100%)
rename {ql => ruby/ql}/src/queries/summary/LinesOfUserCode.ql (100%)
rename {ql => ruby/ql}/src/queries/summary/NumberOfFilesExtractedWithErrors.ql (100%)
rename {ql => ruby/ql}/src/queries/summary/NumberOfSuccessfullyExtractedFiles.ql (100%)
rename {ql => ruby/ql}/src/queries/variables/DeadStoreOfLocal.ql (100%)
rename {ql => ruby/ql}/src/queries/variables/UninitializedLocal.ql (100%)
rename {ql => ruby/ql}/src/queries/variables/UnusedParameter.ql (100%)
rename {ql => ruby/ql}/test/TestUtilities/InlineExpectationsTest.qll (100%)
rename {ql => ruby/ql}/test/TestUtilities/InlineExpectationsTestPrivate.qll (100%)
rename {ql => ruby/ql}/test/library-tests/ast/Ast.expected (100%)
rename {ql => ruby/ql}/test/library-tests/ast/Ast.ql (100%)
rename {ql => ruby/ql}/test/library-tests/ast/AstDesugar.expected (100%)
rename {ql => ruby/ql}/test/library-tests/ast/AstDesugar.ql (100%)
rename {ql => ruby/ql}/test/library-tests/ast/calls/arguments.expected (100%)
rename {ql => ruby/ql}/test/library-tests/ast/calls/arguments.ql (100%)
rename {ql => ruby/ql}/test/library-tests/ast/calls/calls.expected (100%)
rename {ql => ruby/ql}/test/library-tests/ast/calls/calls.ql (100%)
rename {ql => ruby/ql}/test/library-tests/ast/calls/calls.rb (100%)
rename {ql => ruby/ql}/test/library-tests/ast/constants/constants.expected (100%)
rename {ql => ruby/ql}/test/library-tests/ast/constants/constants.ql (100%)
rename {ql => ruby/ql}/test/library-tests/ast/constants/constants.rb (100%)
rename {ql => ruby/ql}/test/library-tests/ast/control/CaseExpr.expected (100%)
rename {ql => ruby/ql}/test/library-tests/ast/control/CaseExpr.ql (100%)
rename {ql => ruby/ql}/test/library-tests/ast/control/ConditionalExpr.expected (100%)
rename {ql => ruby/ql}/test/library-tests/ast/control/ConditionalExpr.ql (100%)
rename {ql => ruby/ql}/test/library-tests/ast/control/ControlExpr.expected (100%)
rename {ql => ruby/ql}/test/library-tests/ast/control/ControlExpr.ql (100%)
rename {ql => ruby/ql}/test/library-tests/ast/control/Loop.expected (100%)
rename {ql => ruby/ql}/test/library-tests/ast/control/Loop.ql (100%)
rename {ql => ruby/ql}/test/library-tests/ast/control/cases.rb (100%)
rename {ql => ruby/ql}/test/library-tests/ast/control/conditionals.rb (100%)
rename {ql => ruby/ql}/test/library-tests/ast/control/loops.rb (100%)
rename {ql => ruby/ql}/test/library-tests/ast/erb/Erb.expected (100%)
rename {ql => ruby/ql}/test/library-tests/ast/erb/Erb.ql (100%)
rename {ql => ruby/ql}/test/library-tests/ast/erb/template.html.erb (100%)
rename {ql => ruby/ql}/test/library-tests/ast/gems/Gemfile (100%)
rename {ql => ruby/ql}/test/library-tests/ast/gems/lib/test.rb (100%)
rename {ql => ruby/ql}/test/library-tests/ast/gems/test.expected (100%)
rename {ql => ruby/ql}/test/library-tests/ast/gems/test.gemspec (100%)
rename {ql => ruby/ql}/test/library-tests/ast/gems/test.ql (100%)
rename {ql => ruby/ql}/test/library-tests/ast/literals/literals.expected (100%)
rename {ql => ruby/ql}/test/library-tests/ast/literals/literals.ql (100%)
rename {ql => ruby/ql}/test/library-tests/ast/literals/literals.rb (100%)
rename {ql => ruby/ql}/test/library-tests/ast/misc/misc.erb (100%)
rename {ql => ruby/ql}/test/library-tests/ast/misc/misc.expected (100%)
rename {ql => ruby/ql}/test/library-tests/ast/misc/misc.ql (100%)
rename {ql => ruby/ql}/test/library-tests/ast/misc/misc.rb (100%)
rename {ql => ruby/ql}/test/library-tests/ast/modules/classes.expected (100%)
rename {ql => ruby/ql}/test/library-tests/ast/modules/classes.ql (100%)
rename {ql => ruby/ql}/test/library-tests/ast/modules/classes.rb (100%)
rename {ql => ruby/ql}/test/library-tests/ast/modules/module_base.expected (100%)
rename {ql => ruby/ql}/test/library-tests/ast/modules/module_base.ql (100%)
rename {ql => ruby/ql}/test/library-tests/ast/modules/modules.expected (100%)
rename {ql => ruby/ql}/test/library-tests/ast/modules/modules.ql (100%)
rename {ql => ruby/ql}/test/library-tests/ast/modules/modules.rb (100%)
rename {ql => ruby/ql}/test/library-tests/ast/modules/singleton_classes.expected (100%)
rename {ql => ruby/ql}/test/library-tests/ast/modules/singleton_classes.ql (100%)
rename {ql => ruby/ql}/test/library-tests/ast/modules/toplevel.expected (100%)
rename {ql => ruby/ql}/test/library-tests/ast/modules/toplevel.ql (100%)
rename {ql => ruby/ql}/test/library-tests/ast/modules/toplevel.rb (100%)
rename {ql => ruby/ql}/test/library-tests/ast/operations/assignment.expected (100%)
rename {ql => ruby/ql}/test/library-tests/ast/operations/assignment.ql (100%)
rename {ql => ruby/ql}/test/library-tests/ast/operations/binary.expected (100%)
rename {ql => ruby/ql}/test/library-tests/ast/operations/binary.ql (100%)
rename {ql => ruby/ql}/test/library-tests/ast/operations/operation.expected (100%)
rename {ql => ruby/ql}/test/library-tests/ast/operations/operation.ql (100%)
rename {ql => ruby/ql}/test/library-tests/ast/operations/operations.rb (100%)
rename {ql => ruby/ql}/test/library-tests/ast/operations/unary.expected (100%)
rename {ql => ruby/ql}/test/library-tests/ast/operations/unary.ql (100%)
rename {ql => ruby/ql}/test/library-tests/ast/params/params.expected (100%)
rename {ql => ruby/ql}/test/library-tests/ast/params/params.ql (100%)
rename {ql => ruby/ql}/test/library-tests/ast/params/params.rb (100%)
rename {ql => ruby/ql}/test/library-tests/controlflow/graph/Cfg.expected (100%)
rename {ql => ruby/ql}/test/library-tests/controlflow/graph/Cfg.ql (100%)
rename {ql => ruby/ql}/test/library-tests/controlflow/graph/break_ensure.rb (100%)
rename {ql => ruby/ql}/test/library-tests/controlflow/graph/case.rb (100%)
rename {ql => ruby/ql}/test/library-tests/controlflow/graph/cfg.html.erb (100%)
rename {ql => ruby/ql}/test/library-tests/controlflow/graph/cfg.rb (100%)
rename {ql => ruby/ql}/test/library-tests/controlflow/graph/desugar.rb (100%)
rename {ql => ruby/ql}/test/library-tests/controlflow/graph/exit.rb (100%)
rename {ql => ruby/ql}/test/library-tests/controlflow/graph/heredoc.rb (100%)
rename {ql => ruby/ql}/test/library-tests/controlflow/graph/ifs.rb (100%)
rename {ql => ruby/ql}/test/library-tests/controlflow/graph/loops.rb (100%)
rename {ql => ruby/ql}/test/library-tests/controlflow/graph/raise.rb (100%)
rename {ql => ruby/ql}/test/library-tests/dataflow/api-graphs/test1.rb (100%)
rename {ql => ruby/ql}/test/library-tests/dataflow/api-graphs/use.expected (100%)
rename {ql => ruby/ql}/test/library-tests/dataflow/api-graphs/use.ql (100%)
rename {ql => ruby/ql}/test/library-tests/dataflow/barrier-guards/barrier-guards.expected (100%)
rename {ql => ruby/ql}/test/library-tests/dataflow/barrier-guards/barrier-guards.ql (100%)
rename {ql => ruby/ql}/test/library-tests/dataflow/barrier-guards/barrier-guards.rb (100%)
rename {ql => ruby/ql}/test/library-tests/dataflow/call-sensitivity/call-sensitivity.expected (100%)
rename {ql => ruby/ql}/test/library-tests/dataflow/call-sensitivity/call-sensitivity.ql (100%)
rename {ql => ruby/ql}/test/library-tests/dataflow/call-sensitivity/call_sensitivity.rb (100%)
rename {ql => ruby/ql}/test/library-tests/dataflow/local/DataflowStep.expected (100%)
rename {ql => ruby/ql}/test/library-tests/dataflow/local/DataflowStep.ql (100%)
rename {ql => ruby/ql}/test/library-tests/dataflow/local/ReturnNodes.expected (100%)
rename {ql => ruby/ql}/test/library-tests/dataflow/local/ReturnNodes.ql (100%)
rename {ql => ruby/ql}/test/library-tests/dataflow/local/local_dataflow.rb (100%)
rename {ql => ruby/ql}/test/library-tests/dataflow/summaries/Summaries.expected (100%)
rename {ql => ruby/ql}/test/library-tests/dataflow/summaries/Summaries.ql (100%)
rename {ql => ruby/ql}/test/library-tests/dataflow/summaries/summaries.rb (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/ActionController.expected (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/ActionController.ql (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/ActionView.expected (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/ActionView.ql (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/ActiveRecord.expected (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/ActiveRecord.ql (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/ActiveRecordInjection.rb (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/CommandExecution.rb (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/Eval.rb (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/Files.expected (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/Files.ql (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/Files.rb (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/StandardLibrary.expected (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/StandardLibrary.ql (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/app/components/DummyComponent.rb (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/app/controllers/foo/bars_controller.rb (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/app/views/foo/bars/_widget.html.erb (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/app/views/foo/bars/show.html.erb (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/http_clients/Excon.expected (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/http_clients/Excon.ql (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/http_clients/Excon.rb (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/http_clients/Faraday.expected (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/http_clients/Faraday.ql (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/http_clients/Faraday.rb (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/http_clients/NetHTTP.expected (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/http_clients/NetHTTP.ql (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/http_clients/NetHTTP.rb (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/http_clients/RestClient.expected (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/http_clients/RestClient.ql (100%)
rename {ql => ruby/ql}/test/library-tests/frameworks/http_clients/RestClient.rb (100%)
rename {ql => ruby/ql}/test/library-tests/modules/ancestors.expected (100%)
rename {ql => ruby/ql}/test/library-tests/modules/ancestors.ql (100%)
rename {ql => ruby/ql}/test/library-tests/modules/callgraph.expected (100%)
rename {ql => ruby/ql}/test/library-tests/modules/callgraph.ql (100%)
rename {ql => ruby/ql}/test/library-tests/modules/calls.rb (100%)
rename {ql => ruby/ql}/test/library-tests/modules/hello.rb (100%)
rename {ql => ruby/ql}/test/library-tests/modules/methods.expected (100%)
rename {ql => ruby/ql}/test/library-tests/modules/methods.ql (100%)
rename {ql => ruby/ql}/test/library-tests/modules/modules.expected (100%)
rename {ql => ruby/ql}/test/library-tests/modules/modules.ql (100%)
rename {ql => ruby/ql}/test/library-tests/modules/modules.rb (100%)
rename {ql => ruby/ql}/test/library-tests/modules/private.rb (100%)
rename {ql => ruby/ql}/test/library-tests/modules/superclasses.expected (100%)
rename {ql => ruby/ql}/test/library-tests/modules/superclasses.ql (100%)
rename {ql => ruby/ql}/test/library-tests/regexp/parse.expected (100%)
rename {ql => ruby/ql}/test/library-tests/regexp/parse.ql (100%)
rename {ql => ruby/ql}/test/library-tests/regexp/regexp.rb (100%)
rename {ql => ruby/ql}/test/library-tests/variables/class_variables.rb (100%)
rename {ql => ruby/ql}/test/library-tests/variables/instance_variables.rb (100%)
rename {ql => ruby/ql}/test/library-tests/variables/nested_scopes.rb (100%)
rename {ql => ruby/ql}/test/library-tests/variables/parameter.expected (100%)
rename {ql => ruby/ql}/test/library-tests/variables/parameter.ql (100%)
rename {ql => ruby/ql}/test/library-tests/variables/parameters.rb (100%)
rename {ql => ruby/ql}/test/library-tests/variables/scopes.rb (100%)
rename {ql => ruby/ql}/test/library-tests/variables/ssa.expected (100%)
rename {ql => ruby/ql}/test/library-tests/variables/ssa.ql (100%)
rename {ql => ruby/ql}/test/library-tests/variables/ssa.rb (100%)
rename {ql => ruby/ql}/test/library-tests/variables/varaccess.expected (100%)
rename {ql => ruby/ql}/test/library-tests/variables/varaccess.ql (100%)
rename {ql => ruby/ql}/test/library-tests/variables/variable.expected (100%)
rename {ql => ruby/ql}/test/library-tests/variables/variable.ql (100%)
rename {ql => ruby/ql}/test/library-tests/variables/varscopes.expected (100%)
rename {ql => ruby/ql}/test/library-tests/variables/varscopes.ql (100%)
rename {ql => ruby/ql}/test/qlpack.lock.yml (100%)
rename {ql => ruby/ql}/test/qlpack.yml (100%)
rename {ql => ruby/ql}/test/query-tests/AlertSuppression/.gitattributes (100%)
rename {ql => ruby/ql}/test/query-tests/AlertSuppression/AlertSuppression.expected (100%)
rename {ql => ruby/ql}/test/query-tests/AlertSuppression/AlertSuppression.qlref (100%)
rename {ql => ruby/ql}/test/query-tests/AlertSuppression/Test.rb (100%)
rename {ql => ruby/ql}/test/query-tests/AlertSuppression/TestWindows.rb (100%)
rename {ql => ruby/ql}/test/query-tests/analysis/Definitions.expected (100%)
rename {ql => ruby/ql}/test/query-tests/analysis/Definitions.qlref (100%)
rename {ql => ruby/ql}/test/query-tests/analysis/Definitions.rb (100%)
rename {ql => ruby/ql}/test/query-tests/diagnostics/ExtractionErrors.expected (100%)
rename {ql => ruby/ql}/test/query-tests/diagnostics/ExtractionErrors.qlref (100%)
rename {ql => ruby/ql}/test/query-tests/diagnostics/NumberOfFilesExtractedWithErrors.expected (100%)
rename {ql => ruby/ql}/test/query-tests/diagnostics/NumberOfFilesExtractedWithErrors.qlref (100%)
rename {ql => ruby/ql}/test/query-tests/diagnostics/NumberOfSuccessfullyExtractedFiles.expected (100%)
rename {ql => ruby/ql}/test/query-tests/diagnostics/NumberOfSuccessfullyExtractedFiles.qlref (100%)
rename {ql => ruby/ql}/test/query-tests/diagnostics/SuccessfullyExtractedFiles.expected (100%)
rename {ql => ruby/ql}/test/query-tests/diagnostics/SuccessfullyExtractedFiles.qlref (100%)
rename {ql => ruby/ql}/test/query-tests/diagnostics/src/bar.erb (100%)
rename {ql => ruby/ql}/test/query-tests/diagnostics/src/foo.rb (100%)
rename {ql => ruby/ql}/test/query-tests/diagnostics/src/not_ruby.rb (100%)
rename {ql => ruby/ql}/test/query-tests/diagnostics/src/unsupported_feature.rb (100%)
rename {ql => ruby/ql}/test/query-tests/diagnostics/src/vendor/cache/lib.rb (100%)
rename {ql => ruby/ql}/test/query-tests/metrics/FLines/Empty.rb (100%)
rename {ql => ruby/ql}/test/query-tests/metrics/FLines/FLines.expected (100%)
rename {ql => ruby/ql}/test/query-tests/metrics/FLines/FLines.qlref (100%)
rename {ql => ruby/ql}/test/query-tests/metrics/FLines/FLines.rb (100%)
rename {ql => ruby/ql}/test/query-tests/metrics/FLines/FLinesOfCode.expected (100%)
rename {ql => ruby/ql}/test/query-tests/metrics/FLines/FLinesOfCode.qlref (100%)
rename {ql => ruby/ql}/test/query-tests/metrics/FLines/FLinesOfComments.expected (100%)
rename {ql => ruby/ql}/test/query-tests/metrics/FLines/FLinesOfComments.qlref (100%)
rename {ql => ruby/ql}/test/query-tests/performance/UseDetect/UseDetect.expected (100%)
rename {ql => ruby/ql}/test/query-tests/performance/UseDetect/UseDetect.qlref (100%)
rename {ql => ruby/ql}/test/query-tests/performance/UseDetect/UseDetect.rb (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-078/CommandInjection.expected (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-078/CommandInjection.qlref (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-078/CommandInjection.rb (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-079/ReflectedXSS.expected (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-079/ReflectedXSS.qlref (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-079/app/controllers/foo/bars_controller.rb (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-079/app/views/foo/bars/_widget.html.erb (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-079/app/views/foo/bars/show.html.erb (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-089/ActiveRecordInjection.rb (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-089/SqlInjection.expected (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-089/SqlInjection.qlref (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-094/CodeInjection.expected (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-094/CodeInjection.qlref (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-094/CodeInjection.rb (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-1333-exponential-redos/ANodeBlog-LICENSE (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-1333-exponential-redos/CodeMirror-LICENSE (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-1333-exponential-redos/Prism-LICENSE (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-1333-exponential-redos/Prototype.js-LICENSE (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-1333-exponential-redos/ReDoS.expected (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-1333-exponential-redos/ReDoS.qlref (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-1333-exponential-redos/brace-expansion-LICENSE (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-1333-exponential-redos/jest-LICENSE (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-1333-exponential-redos/knockout-LICENSE (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-1333-exponential-redos/marked-LICENSE (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-1333-exponential-redos/tst.rb (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.expected (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.qlref (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.rb (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-502/UnsafeDeserialization.expected (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-502/UnsafeDeserialization.qlref (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-502/UnsafeDeserialization.rb (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-601/UrlRedirect.expected (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-601/UrlRedirect.qlref (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-601/UrlRedirect.rb (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-732/FilePermissions.rb (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-732/WeakFilePermissions.expected (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-732/WeakFilePermissions.qlref (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-798/HardcodedCredentials.expected (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-798/HardcodedCredentials.qlref (100%)
rename {ql => ruby/ql}/test/query-tests/security/cwe-798/HardcodedCredentials.rb (100%)
rename {ql => ruby/ql}/test/query-tests/summary/LinesOfCode.expected (100%)
rename {ql => ruby/ql}/test/query-tests/summary/LinesOfCode.qlref (100%)
rename {ql => ruby/ql}/test/query-tests/summary/LinesOfUserCode.expected (100%)
rename {ql => ruby/ql}/test/query-tests/summary/LinesOfUserCode.qlref (100%)
rename {ql => ruby/ql}/test/query-tests/summary/src/foo.rb (100%)
rename {ql => ruby/ql}/test/query-tests/summary/src/vendor/cache/lib.rb (100%)
rename {scripts => ruby/scripts}/create-extractor-pack.ps1 (100%)
rename {scripts => ruby/scripts}/create-extractor-pack.sh (100%)
rename {scripts => ruby/scripts}/identical-files.json (100%)
rename {scripts => ruby/scripts}/merge_stats.py (100%)
rename {scripts => ruby/scripts}/prepare-db-upgrade.sh (100%)
rename {scripts => ruby/scripts}/sync-identical-files.py (100%)
rename {tools => ruby/tools}/autobuild.cmd (100%)
rename {tools => ruby/tools}/autobuild.sh (100%)
rename {tools => ruby/tools}/index-files.cmd (100%)
rename {tools => ruby/tools}/index-files.sh (100%)
rename {tools => ruby/tools}/qltest.cmd (100%)
rename {tools => ruby/tools}/qltest.sh (100%)
diff --git a/.gitmodules b/.gitmodules
index b9b6762c49b..1372e928c4e 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,3 @@
[submodule "codeql"]
- path = codeql
+ path = ruby/codeql
url = https://github.com/github/codeql.git
diff --git a/.codeqlmanifest.json b/ruby/.codeqlmanifest.json
similarity index 100%
rename from .codeqlmanifest.json
rename to ruby/.codeqlmanifest.json
diff --git a/.gitattributes b/ruby/.gitattributes
similarity index 100%
rename from .gitattributes
rename to ruby/.gitattributes
diff --git a/.gitignore b/ruby/.gitignore
similarity index 100%
rename from .gitignore
rename to ruby/.gitignore
diff --git a/.vscode/tasks.json b/ruby/.vscode/tasks.json
similarity index 100%
rename from .vscode/tasks.json
rename to ruby/.vscode/tasks.json
diff --git a/CODE_OF_CONDUCT.md b/ruby/CODE_OF_CONDUCT.md
similarity index 100%
rename from CODE_OF_CONDUCT.md
rename to ruby/CODE_OF_CONDUCT.md
diff --git a/Cargo.lock b/ruby/Cargo.lock
similarity index 100%
rename from Cargo.lock
rename to ruby/Cargo.lock
diff --git a/Cargo.toml b/ruby/Cargo.toml
similarity index 100%
rename from Cargo.toml
rename to ruby/Cargo.toml
diff --git a/LICENSE b/ruby/LICENSE
similarity index 100%
rename from LICENSE
rename to ruby/LICENSE
diff --git a/Makefile b/ruby/Makefile
similarity index 100%
rename from Makefile
rename to ruby/Makefile
diff --git a/README.md b/ruby/README.md
similarity index 100%
rename from README.md
rename to ruby/README.md
diff --git a/autobuilder/Cargo.toml b/ruby/autobuilder/Cargo.toml
similarity index 100%
rename from autobuilder/Cargo.toml
rename to ruby/autobuilder/Cargo.toml
diff --git a/autobuilder/src/main.rs b/ruby/autobuilder/src/main.rs
similarity index 100%
rename from autobuilder/src/main.rs
rename to ruby/autobuilder/src/main.rs
diff --git a/codeql b/ruby/codeql
similarity index 100%
rename from codeql
rename to ruby/codeql
diff --git a/codeql-extractor.yml b/ruby/codeql-extractor.yml
similarity index 100%
rename from codeql-extractor.yml
rename to ruby/codeql-extractor.yml
diff --git a/codeql-ruby.code-workspace b/ruby/codeql-ruby.code-workspace
similarity index 100%
rename from codeql-ruby.code-workspace
rename to ruby/codeql-ruby.code-workspace
diff --git a/doc/prepare-db-upgrade.md b/ruby/doc/prepare-db-upgrade.md
similarity index 100%
rename from doc/prepare-db-upgrade.md
rename to ruby/doc/prepare-db-upgrade.md
diff --git a/extractor/Cargo.toml b/ruby/extractor/Cargo.toml
similarity index 100%
rename from extractor/Cargo.toml
rename to ruby/extractor/Cargo.toml
diff --git a/extractor/src/extractor.rs b/ruby/extractor/src/extractor.rs
similarity index 100%
rename from extractor/src/extractor.rs
rename to ruby/extractor/src/extractor.rs
diff --git a/extractor/src/main.rs b/ruby/extractor/src/main.rs
similarity index 100%
rename from extractor/src/main.rs
rename to ruby/extractor/src/main.rs
diff --git a/generator/Cargo.toml b/ruby/generator/Cargo.toml
similarity index 100%
rename from generator/Cargo.toml
rename to ruby/generator/Cargo.toml
diff --git a/generator/src/dbscheme.rs b/ruby/generator/src/dbscheme.rs
similarity index 100%
rename from generator/src/dbscheme.rs
rename to ruby/generator/src/dbscheme.rs
diff --git a/generator/src/language.rs b/ruby/generator/src/language.rs
similarity index 100%
rename from generator/src/language.rs
rename to ruby/generator/src/language.rs
diff --git a/generator/src/main.rs b/ruby/generator/src/main.rs
similarity index 100%
rename from generator/src/main.rs
rename to ruby/generator/src/main.rs
diff --git a/generator/src/ql.rs b/ruby/generator/src/ql.rs
similarity index 100%
rename from generator/src/ql.rs
rename to ruby/generator/src/ql.rs
diff --git a/generator/src/ql_gen.rs b/ruby/generator/src/ql_gen.rs
similarity index 100%
rename from generator/src/ql_gen.rs
rename to ruby/generator/src/ql_gen.rs
diff --git a/node-types/Cargo.toml b/ruby/node-types/Cargo.toml
similarity index 100%
rename from node-types/Cargo.toml
rename to ruby/node-types/Cargo.toml
diff --git a/node-types/src/lib.rs b/ruby/node-types/src/lib.rs
similarity index 100%
rename from node-types/src/lib.rs
rename to ruby/node-types/src/lib.rs
diff --git a/ql/consistency-queries/AstConsistency.ql b/ruby/ql/consistency-queries/AstConsistency.ql
similarity index 100%
rename from ql/consistency-queries/AstConsistency.ql
rename to ruby/ql/consistency-queries/AstConsistency.ql
diff --git a/ql/consistency-queries/CfgConsistency.ql b/ruby/ql/consistency-queries/CfgConsistency.ql
similarity index 100%
rename from ql/consistency-queries/CfgConsistency.ql
rename to ruby/ql/consistency-queries/CfgConsistency.ql
diff --git a/ql/consistency-queries/DataFlowConsistency.ql b/ruby/ql/consistency-queries/DataFlowConsistency.ql
similarity index 100%
rename from ql/consistency-queries/DataFlowConsistency.ql
rename to ruby/ql/consistency-queries/DataFlowConsistency.ql
diff --git a/ql/consistency-queries/SsaConsistency.ql b/ruby/ql/consistency-queries/SsaConsistency.ql
similarity index 100%
rename from ql/consistency-queries/SsaConsistency.ql
rename to ruby/ql/consistency-queries/SsaConsistency.ql
diff --git a/ql/consistency-queries/VariablesConsistency.ql b/ruby/ql/consistency-queries/VariablesConsistency.ql
similarity index 100%
rename from ql/consistency-queries/VariablesConsistency.ql
rename to ruby/ql/consistency-queries/VariablesConsistency.ql
diff --git a/ql/consistency-queries/qlpack.yml b/ruby/ql/consistency-queries/qlpack.yml
similarity index 100%
rename from ql/consistency-queries/qlpack.yml
rename to ruby/ql/consistency-queries/qlpack.yml
diff --git a/ql/docs/experimental.md b/ruby/ql/docs/experimental.md
similarity index 100%
rename from ql/docs/experimental.md
rename to ruby/ql/docs/experimental.md
diff --git a/ql/examples/qlpack.lock.yml b/ruby/ql/examples/qlpack.lock.yml
similarity index 100%
rename from ql/examples/qlpack.lock.yml
rename to ruby/ql/examples/qlpack.lock.yml
diff --git a/ql/examples/qlpack.yml b/ruby/ql/examples/qlpack.yml
similarity index 100%
rename from ql/examples/qlpack.yml
rename to ruby/ql/examples/qlpack.yml
diff --git a/ql/examples/queries.xml b/ruby/ql/examples/queries.xml
similarity index 100%
rename from ql/examples/queries.xml
rename to ruby/ql/examples/queries.xml
diff --git a/ql/examples/snippets/emptythen.ql b/ruby/ql/examples/snippets/emptythen.ql
similarity index 100%
rename from ql/examples/snippets/emptythen.ql
rename to ruby/ql/examples/snippets/emptythen.ql
diff --git a/ql/lib/codeql/IDEContextual.qll b/ruby/ql/lib/codeql/IDEContextual.qll
similarity index 100%
rename from ql/lib/codeql/IDEContextual.qll
rename to ruby/ql/lib/codeql/IDEContextual.qll
diff --git a/ql/lib/codeql/Locations.qll b/ruby/ql/lib/codeql/Locations.qll
similarity index 100%
rename from ql/lib/codeql/Locations.qll
rename to ruby/ql/lib/codeql/Locations.qll
diff --git a/ql/lib/codeql/files/FileSystem.qll b/ruby/ql/lib/codeql/files/FileSystem.qll
similarity index 100%
rename from ql/lib/codeql/files/FileSystem.qll
rename to ruby/ql/lib/codeql/files/FileSystem.qll
diff --git a/ql/lib/codeql/ruby/AST.qll b/ruby/ql/lib/codeql/ruby/AST.qll
similarity index 100%
rename from ql/lib/codeql/ruby/AST.qll
rename to ruby/ql/lib/codeql/ruby/AST.qll
diff --git a/ql/lib/codeql/ruby/ApiGraphs.qll b/ruby/ql/lib/codeql/ruby/ApiGraphs.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ApiGraphs.qll
rename to ruby/ql/lib/codeql/ruby/ApiGraphs.qll
diff --git a/ql/lib/codeql/ruby/CFG.qll b/ruby/ql/lib/codeql/ruby/CFG.qll
similarity index 100%
rename from ql/lib/codeql/ruby/CFG.qll
rename to ruby/ql/lib/codeql/ruby/CFG.qll
diff --git a/ql/lib/codeql/ruby/Concepts.qll b/ruby/ql/lib/codeql/ruby/Concepts.qll
similarity index 100%
rename from ql/lib/codeql/ruby/Concepts.qll
rename to ruby/ql/lib/codeql/ruby/Concepts.qll
diff --git a/ql/lib/codeql/ruby/DataFlow.qll b/ruby/ql/lib/codeql/ruby/DataFlow.qll
similarity index 100%
rename from ql/lib/codeql/ruby/DataFlow.qll
rename to ruby/ql/lib/codeql/ruby/DataFlow.qll
diff --git a/ql/lib/codeql/ruby/Diagnostics.qll b/ruby/ql/lib/codeql/ruby/Diagnostics.qll
similarity index 100%
rename from ql/lib/codeql/ruby/Diagnostics.qll
rename to ruby/ql/lib/codeql/ruby/Diagnostics.qll
diff --git a/ql/lib/codeql/ruby/Frameworks.qll b/ruby/ql/lib/codeql/ruby/Frameworks.qll
similarity index 100%
rename from ql/lib/codeql/ruby/Frameworks.qll
rename to ruby/ql/lib/codeql/ruby/Frameworks.qll
diff --git a/ql/lib/codeql/ruby/TaintTracking.qll b/ruby/ql/lib/codeql/ruby/TaintTracking.qll
similarity index 100%
rename from ql/lib/codeql/ruby/TaintTracking.qll
rename to ruby/ql/lib/codeql/ruby/TaintTracking.qll
diff --git a/ql/lib/codeql/ruby/ast/Call.qll b/ruby/ql/lib/codeql/ruby/ast/Call.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/Call.qll
rename to ruby/ql/lib/codeql/ruby/ast/Call.qll
diff --git a/ql/lib/codeql/ruby/ast/Constant.qll b/ruby/ql/lib/codeql/ruby/ast/Constant.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/Constant.qll
rename to ruby/ql/lib/codeql/ruby/ast/Constant.qll
diff --git a/ql/lib/codeql/ruby/ast/Control.qll b/ruby/ql/lib/codeql/ruby/ast/Control.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/Control.qll
rename to ruby/ql/lib/codeql/ruby/ast/Control.qll
diff --git a/ql/lib/codeql/ruby/ast/Erb.qll b/ruby/ql/lib/codeql/ruby/ast/Erb.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/Erb.qll
rename to ruby/ql/lib/codeql/ruby/ast/Erb.qll
diff --git a/ql/lib/codeql/ruby/ast/Expr.qll b/ruby/ql/lib/codeql/ruby/ast/Expr.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/Expr.qll
rename to ruby/ql/lib/codeql/ruby/ast/Expr.qll
diff --git a/ql/lib/codeql/ruby/ast/Literal.qll b/ruby/ql/lib/codeql/ruby/ast/Literal.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/Literal.qll
rename to ruby/ql/lib/codeql/ruby/ast/Literal.qll
diff --git a/ql/lib/codeql/ruby/ast/Method.qll b/ruby/ql/lib/codeql/ruby/ast/Method.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/Method.qll
rename to ruby/ql/lib/codeql/ruby/ast/Method.qll
diff --git a/ql/lib/codeql/ruby/ast/Module.qll b/ruby/ql/lib/codeql/ruby/ast/Module.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/Module.qll
rename to ruby/ql/lib/codeql/ruby/ast/Module.qll
diff --git a/ql/lib/codeql/ruby/ast/Operation.qll b/ruby/ql/lib/codeql/ruby/ast/Operation.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/Operation.qll
rename to ruby/ql/lib/codeql/ruby/ast/Operation.qll
diff --git a/ql/lib/codeql/ruby/ast/Parameter.qll b/ruby/ql/lib/codeql/ruby/ast/Parameter.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/Parameter.qll
rename to ruby/ql/lib/codeql/ruby/ast/Parameter.qll
diff --git a/ql/lib/codeql/ruby/ast/Pattern.qll b/ruby/ql/lib/codeql/ruby/ast/Pattern.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/Pattern.qll
rename to ruby/ql/lib/codeql/ruby/ast/Pattern.qll
diff --git a/ql/lib/codeql/ruby/ast/Scope.qll b/ruby/ql/lib/codeql/ruby/ast/Scope.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/Scope.qll
rename to ruby/ql/lib/codeql/ruby/ast/Scope.qll
diff --git a/ql/lib/codeql/ruby/ast/Statement.qll b/ruby/ql/lib/codeql/ruby/ast/Statement.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/Statement.qll
rename to ruby/ql/lib/codeql/ruby/ast/Statement.qll
diff --git a/ql/lib/codeql/ruby/ast/Variable.qll b/ruby/ql/lib/codeql/ruby/ast/Variable.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/Variable.qll
rename to ruby/ql/lib/codeql/ruby/ast/Variable.qll
diff --git a/ql/lib/codeql/ruby/ast/internal/AST.qll b/ruby/ql/lib/codeql/ruby/ast/internal/AST.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/internal/AST.qll
rename to ruby/ql/lib/codeql/ruby/ast/internal/AST.qll
diff --git a/ql/lib/codeql/ruby/ast/internal/Call.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Call.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/internal/Call.qll
rename to ruby/ql/lib/codeql/ruby/ast/internal/Call.qll
diff --git a/ql/lib/codeql/ruby/ast/internal/Erb.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Erb.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/internal/Erb.qll
rename to ruby/ql/lib/codeql/ruby/ast/internal/Erb.qll
diff --git a/ql/lib/codeql/ruby/ast/internal/Module.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Module.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/internal/Module.qll
rename to ruby/ql/lib/codeql/ruby/ast/internal/Module.qll
diff --git a/ql/lib/codeql/ruby/ast/internal/Operation.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Operation.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/internal/Operation.qll
rename to ruby/ql/lib/codeql/ruby/ast/internal/Operation.qll
diff --git a/ql/lib/codeql/ruby/ast/internal/Parameter.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Parameter.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/internal/Parameter.qll
rename to ruby/ql/lib/codeql/ruby/ast/internal/Parameter.qll
diff --git a/ql/lib/codeql/ruby/ast/internal/Pattern.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Pattern.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/internal/Pattern.qll
rename to ruby/ql/lib/codeql/ruby/ast/internal/Pattern.qll
diff --git a/ql/lib/codeql/ruby/ast/internal/Scope.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Scope.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/internal/Scope.qll
rename to ruby/ql/lib/codeql/ruby/ast/internal/Scope.qll
diff --git a/ql/lib/codeql/ruby/ast/internal/Synthesis.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Synthesis.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/internal/Synthesis.qll
rename to ruby/ql/lib/codeql/ruby/ast/internal/Synthesis.qll
diff --git a/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll b/ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
rename to ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
diff --git a/ql/lib/codeql/ruby/ast/internal/Variable.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Variable.qll
similarity index 100%
rename from ql/lib/codeql/ruby/ast/internal/Variable.qll
rename to ruby/ql/lib/codeql/ruby/ast/internal/Variable.qll
diff --git a/ql/lib/codeql/ruby/controlflow/BasicBlocks.qll b/ruby/ql/lib/codeql/ruby/controlflow/BasicBlocks.qll
similarity index 100%
rename from ql/lib/codeql/ruby/controlflow/BasicBlocks.qll
rename to ruby/ql/lib/codeql/ruby/controlflow/BasicBlocks.qll
diff --git a/ql/lib/codeql/ruby/controlflow/CfgNodes.qll b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll
similarity index 100%
rename from ql/lib/codeql/ruby/controlflow/CfgNodes.qll
rename to ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll
diff --git a/ql/lib/codeql/ruby/controlflow/ControlFlowGraph.qll b/ruby/ql/lib/codeql/ruby/controlflow/ControlFlowGraph.qll
similarity index 100%
rename from ql/lib/codeql/ruby/controlflow/ControlFlowGraph.qll
rename to ruby/ql/lib/codeql/ruby/controlflow/ControlFlowGraph.qll
diff --git a/ql/lib/codeql/ruby/controlflow/internal/Completion.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/Completion.qll
similarity index 100%
rename from ql/lib/codeql/ruby/controlflow/internal/Completion.qll
rename to ruby/ql/lib/codeql/ruby/controlflow/internal/Completion.qll
diff --git a/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll
similarity index 100%
rename from ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll
rename to ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll
diff --git a/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll
similarity index 100%
rename from ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll
rename to ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll
diff --git a/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplSpecific.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplSpecific.qll
similarity index 100%
rename from ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplSpecific.qll
rename to ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplSpecific.qll
diff --git a/ql/lib/codeql/ruby/controlflow/internal/NonReturning.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/NonReturning.qll
similarity index 100%
rename from ql/lib/codeql/ruby/controlflow/internal/NonReturning.qll
rename to ruby/ql/lib/codeql/ruby/controlflow/internal/NonReturning.qll
diff --git a/ql/lib/codeql/ruby/controlflow/internal/Splitting.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/Splitting.qll
similarity index 100%
rename from ql/lib/codeql/ruby/controlflow/internal/Splitting.qll
rename to ruby/ql/lib/codeql/ruby/controlflow/internal/Splitting.qll
diff --git a/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll b/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll
similarity index 100%
rename from ql/lib/codeql/ruby/dataflow/BarrierGuards.qll
rename to ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll
diff --git a/ql/lib/codeql/ruby/dataflow/FlowSummary.qll b/ruby/ql/lib/codeql/ruby/dataflow/FlowSummary.qll
similarity index 100%
rename from ql/lib/codeql/ruby/dataflow/FlowSummary.qll
rename to ruby/ql/lib/codeql/ruby/dataflow/FlowSummary.qll
diff --git a/ql/lib/codeql/ruby/dataflow/RemoteFlowSources.qll b/ruby/ql/lib/codeql/ruby/dataflow/RemoteFlowSources.qll
similarity index 100%
rename from ql/lib/codeql/ruby/dataflow/RemoteFlowSources.qll
rename to ruby/ql/lib/codeql/ruby/dataflow/RemoteFlowSources.qll
diff --git a/ql/lib/codeql/ruby/dataflow/SSA.qll b/ruby/ql/lib/codeql/ruby/dataflow/SSA.qll
similarity index 100%
rename from ql/lib/codeql/ruby/dataflow/SSA.qll
rename to ruby/ql/lib/codeql/ruby/dataflow/SSA.qll
diff --git a/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll
similarity index 100%
rename from ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll
rename to ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll
diff --git a/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll
similarity index 100%
rename from ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll
rename to ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll
diff --git a/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll
similarity index 100%
rename from ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll
rename to ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll
diff --git a/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll
similarity index 100%
rename from ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll
rename to ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll
diff --git a/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplSpecific.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplSpecific.qll
similarity index 100%
rename from ql/lib/codeql/ruby/dataflow/internal/DataFlowImplSpecific.qll
rename to ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplSpecific.qll
diff --git a/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll
similarity index 100%
rename from ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll
rename to ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll
diff --git a/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
similarity index 100%
rename from ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
rename to ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
diff --git a/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll
similarity index 100%
rename from ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll
rename to ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll
diff --git a/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImplSpecific.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImplSpecific.qll
similarity index 100%
rename from ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImplSpecific.qll
rename to ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImplSpecific.qll
diff --git a/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll
similarity index 100%
rename from ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll
rename to ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll
diff --git a/ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll
similarity index 100%
rename from ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll
rename to ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll
diff --git a/ql/lib/codeql/ruby/dataflow/internal/SsaImplSpecific.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplSpecific.qll
similarity index 100%
rename from ql/lib/codeql/ruby/dataflow/internal/SsaImplSpecific.qll
rename to ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplSpecific.qll
diff --git a/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll
similarity index 100%
rename from ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll
rename to ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll
diff --git a/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPublic.qll
similarity index 100%
rename from ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPublic.qll
rename to ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPublic.qll
diff --git a/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
similarity index 100%
rename from ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
rename to ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
diff --git a/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingParameter.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingParameter.qll
similarity index 100%
rename from ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingParameter.qll
rename to ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingParameter.qll
diff --git a/ql/lib/codeql/ruby/filters/GeneratedCode.qll b/ruby/ql/lib/codeql/ruby/filters/GeneratedCode.qll
similarity index 100%
rename from ql/lib/codeql/ruby/filters/GeneratedCode.qll
rename to ruby/ql/lib/codeql/ruby/filters/GeneratedCode.qll
diff --git a/ql/lib/codeql/ruby/frameworks/ActionController.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll
similarity index 100%
rename from ql/lib/codeql/ruby/frameworks/ActionController.qll
rename to ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll
diff --git a/ql/lib/codeql/ruby/frameworks/ActionView.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionView.qll
similarity index 100%
rename from ql/lib/codeql/ruby/frameworks/ActionView.qll
rename to ruby/ql/lib/codeql/ruby/frameworks/ActionView.qll
diff --git a/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll
similarity index 100%
rename from ql/lib/codeql/ruby/frameworks/ActiveRecord.qll
rename to ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll
diff --git a/ql/lib/codeql/ruby/frameworks/Files.qll b/ruby/ql/lib/codeql/ruby/frameworks/Files.qll
similarity index 100%
rename from ql/lib/codeql/ruby/frameworks/Files.qll
rename to ruby/ql/lib/codeql/ruby/frameworks/Files.qll
diff --git a/ql/lib/codeql/ruby/frameworks/HTTPClients.qll b/ruby/ql/lib/codeql/ruby/frameworks/HTTPClients.qll
similarity index 100%
rename from ql/lib/codeql/ruby/frameworks/HTTPClients.qll
rename to ruby/ql/lib/codeql/ruby/frameworks/HTTPClients.qll
diff --git a/ql/lib/codeql/ruby/frameworks/StandardLibrary.qll b/ruby/ql/lib/codeql/ruby/frameworks/StandardLibrary.qll
similarity index 100%
rename from ql/lib/codeql/ruby/frameworks/StandardLibrary.qll
rename to ruby/ql/lib/codeql/ruby/frameworks/StandardLibrary.qll
diff --git a/ql/lib/codeql/ruby/frameworks/http_clients/Excon.qll b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Excon.qll
similarity index 100%
rename from ql/lib/codeql/ruby/frameworks/http_clients/Excon.qll
rename to ruby/ql/lib/codeql/ruby/frameworks/http_clients/Excon.qll
diff --git a/ql/lib/codeql/ruby/frameworks/http_clients/Faraday.qll b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Faraday.qll
similarity index 100%
rename from ql/lib/codeql/ruby/frameworks/http_clients/Faraday.qll
rename to ruby/ql/lib/codeql/ruby/frameworks/http_clients/Faraday.qll
diff --git a/ql/lib/codeql/ruby/frameworks/http_clients/NetHTTP.qll b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/NetHTTP.qll
similarity index 100%
rename from ql/lib/codeql/ruby/frameworks/http_clients/NetHTTP.qll
rename to ruby/ql/lib/codeql/ruby/frameworks/http_clients/NetHTTP.qll
diff --git a/ql/lib/codeql/ruby/frameworks/http_clients/RestClient.qll b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/RestClient.qll
similarity index 100%
rename from ql/lib/codeql/ruby/frameworks/http_clients/RestClient.qll
rename to ruby/ql/lib/codeql/ruby/frameworks/http_clients/RestClient.qll
diff --git a/ql/lib/codeql/ruby/printAst.qll b/ruby/ql/lib/codeql/ruby/printAst.qll
similarity index 100%
rename from ql/lib/codeql/ruby/printAst.qll
rename to ruby/ql/lib/codeql/ruby/printAst.qll
diff --git a/ql/lib/codeql/ruby/regexp/ExponentialBackTracking.qll b/ruby/ql/lib/codeql/ruby/regexp/ExponentialBackTracking.qll
similarity index 100%
rename from ql/lib/codeql/ruby/regexp/ExponentialBackTracking.qll
rename to ruby/ql/lib/codeql/ruby/regexp/ExponentialBackTracking.qll
diff --git a/ql/lib/codeql/ruby/regexp/ParseRegExp.qll b/ruby/ql/lib/codeql/ruby/regexp/ParseRegExp.qll
similarity index 100%
rename from ql/lib/codeql/ruby/regexp/ParseRegExp.qll
rename to ruby/ql/lib/codeql/ruby/regexp/ParseRegExp.qll
diff --git a/ql/lib/codeql/ruby/regexp/PolynomialReDoSCustomizations.qll b/ruby/ql/lib/codeql/ruby/regexp/PolynomialReDoSCustomizations.qll
similarity index 100%
rename from ql/lib/codeql/ruby/regexp/PolynomialReDoSCustomizations.qll
rename to ruby/ql/lib/codeql/ruby/regexp/PolynomialReDoSCustomizations.qll
diff --git a/ql/lib/codeql/ruby/regexp/PolynomialReDoSQuery.qll b/ruby/ql/lib/codeql/ruby/regexp/PolynomialReDoSQuery.qll
similarity index 100%
rename from ql/lib/codeql/ruby/regexp/PolynomialReDoSQuery.qll
rename to ruby/ql/lib/codeql/ruby/regexp/PolynomialReDoSQuery.qll
diff --git a/ql/lib/codeql/ruby/regexp/ReDoSUtil.qll b/ruby/ql/lib/codeql/ruby/regexp/ReDoSUtil.qll
similarity index 100%
rename from ql/lib/codeql/ruby/regexp/ReDoSUtil.qll
rename to ruby/ql/lib/codeql/ruby/regexp/ReDoSUtil.qll
diff --git a/ql/lib/codeql/ruby/regexp/RegExpTreeView.qll b/ruby/ql/lib/codeql/ruby/regexp/RegExpTreeView.qll
similarity index 100%
rename from ql/lib/codeql/ruby/regexp/RegExpTreeView.qll
rename to ruby/ql/lib/codeql/ruby/regexp/RegExpTreeView.qll
diff --git a/ql/lib/codeql/ruby/regexp/SuperlinearBackTracking.qll b/ruby/ql/lib/codeql/ruby/regexp/SuperlinearBackTracking.qll
similarity index 100%
rename from ql/lib/codeql/ruby/regexp/SuperlinearBackTracking.qll
rename to ruby/ql/lib/codeql/ruby/regexp/SuperlinearBackTracking.qll
diff --git a/ql/lib/codeql/ruby/security/CodeInjectionCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/CodeInjectionCustomizations.qll
similarity index 100%
rename from ql/lib/codeql/ruby/security/CodeInjectionCustomizations.qll
rename to ruby/ql/lib/codeql/ruby/security/CodeInjectionCustomizations.qll
diff --git a/ql/lib/codeql/ruby/security/CodeInjectionQuery.qll b/ruby/ql/lib/codeql/ruby/security/CodeInjectionQuery.qll
similarity index 100%
rename from ql/lib/codeql/ruby/security/CodeInjectionQuery.qll
rename to ruby/ql/lib/codeql/ruby/security/CodeInjectionQuery.qll
diff --git a/ql/lib/codeql/ruby/security/CommandInjectionCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/CommandInjectionCustomizations.qll
similarity index 100%
rename from ql/lib/codeql/ruby/security/CommandInjectionCustomizations.qll
rename to ruby/ql/lib/codeql/ruby/security/CommandInjectionCustomizations.qll
diff --git a/ql/lib/codeql/ruby/security/CommandInjectionQuery.qll b/ruby/ql/lib/codeql/ruby/security/CommandInjectionQuery.qll
similarity index 100%
rename from ql/lib/codeql/ruby/security/CommandInjectionQuery.qll
rename to ruby/ql/lib/codeql/ruby/security/CommandInjectionQuery.qll
diff --git a/ql/lib/codeql/ruby/security/ReflectedXSSCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/ReflectedXSSCustomizations.qll
similarity index 100%
rename from ql/lib/codeql/ruby/security/ReflectedXSSCustomizations.qll
rename to ruby/ql/lib/codeql/ruby/security/ReflectedXSSCustomizations.qll
diff --git a/ql/lib/codeql/ruby/security/ReflectedXSSQuery.qll b/ruby/ql/lib/codeql/ruby/security/ReflectedXSSQuery.qll
similarity index 100%
rename from ql/lib/codeql/ruby/security/ReflectedXSSQuery.qll
rename to ruby/ql/lib/codeql/ruby/security/ReflectedXSSQuery.qll
diff --git a/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
similarity index 100%
rename from ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
rename to ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
diff --git a/ql/lib/codeql/ruby/security/UnsafeDeserializationQuery.qll b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationQuery.qll
similarity index 100%
rename from ql/lib/codeql/ruby/security/UnsafeDeserializationQuery.qll
rename to ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationQuery.qll
diff --git a/ql/lib/codeql/ruby/security/UrlRedirectCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/UrlRedirectCustomizations.qll
similarity index 100%
rename from ql/lib/codeql/ruby/security/UrlRedirectCustomizations.qll
rename to ruby/ql/lib/codeql/ruby/security/UrlRedirectCustomizations.qll
diff --git a/ql/lib/codeql/ruby/security/UrlRedirectQuery.qll b/ruby/ql/lib/codeql/ruby/security/UrlRedirectQuery.qll
similarity index 100%
rename from ql/lib/codeql/ruby/security/UrlRedirectQuery.qll
rename to ruby/ql/lib/codeql/ruby/security/UrlRedirectQuery.qll
diff --git a/ql/lib/codeql/ruby/typetracking/TypeTracker.qll b/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll
similarity index 100%
rename from ql/lib/codeql/ruby/typetracking/TypeTracker.qll
rename to ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll
diff --git a/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll b/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll
similarity index 100%
rename from ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll
rename to ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll
diff --git a/ql/lib/qlpack.lock.yml b/ruby/ql/lib/qlpack.lock.yml
similarity index 100%
rename from ql/lib/qlpack.lock.yml
rename to ruby/ql/lib/qlpack.lock.yml
diff --git a/ql/lib/qlpack.yml b/ruby/ql/lib/qlpack.yml
similarity index 100%
rename from ql/lib/qlpack.yml
rename to ruby/ql/lib/qlpack.yml
diff --git a/ql/lib/ruby.dbscheme b/ruby/ql/lib/ruby.dbscheme
similarity index 100%
rename from ql/lib/ruby.dbscheme
rename to ruby/ql/lib/ruby.dbscheme
diff --git a/ql/lib/ruby.dbscheme.stats b/ruby/ql/lib/ruby.dbscheme.stats
similarity index 100%
rename from ql/lib/ruby.dbscheme.stats
rename to ruby/ql/lib/ruby.dbscheme.stats
diff --git a/ql/lib/ruby.qll b/ruby/ql/lib/ruby.qll
similarity index 100%
rename from ql/lib/ruby.qll
rename to ruby/ql/lib/ruby.qll
diff --git a/ql/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/old.dbscheme b/ruby/ql/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/old.dbscheme
similarity index 100%
rename from ql/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/old.dbscheme
rename to ruby/ql/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/old.dbscheme
diff --git a/ql/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/ruby.dbscheme b/ruby/ql/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/ruby.dbscheme
similarity index 100%
rename from ql/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/ruby.dbscheme
rename to ruby/ql/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/ruby.dbscheme
diff --git a/ql/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/upgrade.properties b/ruby/ql/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/upgrade.properties
similarity index 100%
rename from ql/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/upgrade.properties
rename to ruby/ql/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/upgrade.properties
diff --git a/ql/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/old.dbscheme b/ruby/ql/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/old.dbscheme
similarity index 100%
rename from ql/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/old.dbscheme
rename to ruby/ql/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/old.dbscheme
diff --git a/ql/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/ruby.dbscheme b/ruby/ql/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/ruby.dbscheme
similarity index 100%
rename from ql/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/ruby.dbscheme
rename to ruby/ql/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/ruby.dbscheme
diff --git a/ql/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/upgrade.properties b/ruby/ql/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/upgrade.properties
similarity index 100%
rename from ql/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/upgrade.properties
rename to ruby/ql/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/upgrade.properties
diff --git a/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/old.dbscheme b/ruby/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/old.dbscheme
similarity index 100%
rename from ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/old.dbscheme
rename to ruby/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/old.dbscheme
diff --git a/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/ruby.dbscheme b/ruby/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/ruby.dbscheme
similarity index 100%
rename from ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/ruby.dbscheme
rename to ruby/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/ruby.dbscheme
diff --git a/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/ruby.dbscheme.stats b/ruby/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/ruby.dbscheme.stats
similarity index 100%
rename from ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/ruby.dbscheme.stats
rename to ruby/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/ruby.dbscheme.stats
diff --git a/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/upgrade.properties b/ruby/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/upgrade.properties
similarity index 100%
rename from ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/upgrade.properties
rename to ruby/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/upgrade.properties
diff --git a/ql/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/old.dbscheme b/ruby/ql/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/old.dbscheme
similarity index 100%
rename from ql/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/old.dbscheme
rename to ruby/ql/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/old.dbscheme
diff --git a/ql/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/ruby.dbscheme b/ruby/ql/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/ruby.dbscheme
similarity index 100%
rename from ql/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/ruby.dbscheme
rename to ruby/ql/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/ruby.dbscheme
diff --git a/ql/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/upgrade.properties b/ruby/ql/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/upgrade.properties
similarity index 100%
rename from ql/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/upgrade.properties
rename to ruby/ql/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/upgrade.properties
diff --git a/ql/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/old.dbscheme b/ruby/ql/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/old.dbscheme
similarity index 100%
rename from ql/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/old.dbscheme
rename to ruby/ql/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/old.dbscheme
diff --git a/ql/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/ruby.dbscheme b/ruby/ql/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/ruby.dbscheme
similarity index 100%
rename from ql/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/ruby.dbscheme
rename to ruby/ql/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/ruby.dbscheme
diff --git a/ql/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/upgrade.properties b/ruby/ql/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/upgrade.properties
similarity index 100%
rename from ql/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/upgrade.properties
rename to ruby/ql/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/upgrade.properties
diff --git a/ql/lib/upgrades/initial/ruby.dbscheme b/ruby/ql/lib/upgrades/initial/ruby.dbscheme
similarity index 100%
rename from ql/lib/upgrades/initial/ruby.dbscheme
rename to ruby/ql/lib/upgrades/initial/ruby.dbscheme
diff --git a/ql/src/AlertSuppression.ql b/ruby/ql/src/AlertSuppression.ql
similarity index 100%
rename from ql/src/AlertSuppression.ql
rename to ruby/ql/src/AlertSuppression.ql
diff --git a/ql/src/codeql-suites/ruby-code-scanning.qls b/ruby/ql/src/codeql-suites/ruby-code-scanning.qls
similarity index 100%
rename from ql/src/codeql-suites/ruby-code-scanning.qls
rename to ruby/ql/src/codeql-suites/ruby-code-scanning.qls
diff --git a/ql/src/codeql-suites/ruby-lgtm-full.qls b/ruby/ql/src/codeql-suites/ruby-lgtm-full.qls
similarity index 100%
rename from ql/src/codeql-suites/ruby-lgtm-full.qls
rename to ruby/ql/src/codeql-suites/ruby-lgtm-full.qls
diff --git a/ql/src/codeql-suites/ruby-lgtm.qls b/ruby/ql/src/codeql-suites/ruby-lgtm.qls
similarity index 100%
rename from ql/src/codeql-suites/ruby-lgtm.qls
rename to ruby/ql/src/codeql-suites/ruby-lgtm.qls
diff --git a/ql/src/codeql-suites/ruby-security-and-quality.qls b/ruby/ql/src/codeql-suites/ruby-security-and-quality.qls
similarity index 100%
rename from ql/src/codeql-suites/ruby-security-and-quality.qls
rename to ruby/ql/src/codeql-suites/ruby-security-and-quality.qls
diff --git a/ql/src/codeql-suites/ruby-security-extended.qls b/ruby/ql/src/codeql-suites/ruby-security-extended.qls
similarity index 100%
rename from ql/src/codeql-suites/ruby-security-extended.qls
rename to ruby/ql/src/codeql-suites/ruby-security-extended.qls
diff --git a/ql/src/experimental/README.md b/ruby/ql/src/experimental/README.md
similarity index 100%
rename from ql/src/experimental/README.md
rename to ruby/ql/src/experimental/README.md
diff --git a/ql/src/experimental/performance/UseDetect.ql b/ruby/ql/src/experimental/performance/UseDetect.ql
similarity index 100%
rename from ql/src/experimental/performance/UseDetect.ql
rename to ruby/ql/src/experimental/performance/UseDetect.ql
diff --git a/ql/src/filters/ClassifyFiles.ql b/ruby/ql/src/filters/ClassifyFiles.ql
similarity index 100%
rename from ql/src/filters/ClassifyFiles.ql
rename to ruby/ql/src/filters/ClassifyFiles.ql
diff --git a/ql/src/ide-contextual-queries/localDefinitions.ql b/ruby/ql/src/ide-contextual-queries/localDefinitions.ql
similarity index 100%
rename from ql/src/ide-contextual-queries/localDefinitions.ql
rename to ruby/ql/src/ide-contextual-queries/localDefinitions.ql
diff --git a/ql/src/ide-contextual-queries/localReferences.ql b/ruby/ql/src/ide-contextual-queries/localReferences.ql
similarity index 100%
rename from ql/src/ide-contextual-queries/localReferences.ql
rename to ruby/ql/src/ide-contextual-queries/localReferences.ql
diff --git a/ql/src/ide-contextual-queries/printAst.ql b/ruby/ql/src/ide-contextual-queries/printAst.ql
similarity index 100%
rename from ql/src/ide-contextual-queries/printAst.ql
rename to ruby/ql/src/ide-contextual-queries/printAst.ql
diff --git a/ql/src/qlpack.lock.yml b/ruby/ql/src/qlpack.lock.yml
similarity index 100%
rename from ql/src/qlpack.lock.yml
rename to ruby/ql/src/qlpack.lock.yml
diff --git a/ql/src/qlpack.yml b/ruby/ql/src/qlpack.yml
similarity index 100%
rename from ql/src/qlpack.yml
rename to ruby/ql/src/qlpack.yml
diff --git a/ql/src/queries/analysis/Definitions.ql b/ruby/ql/src/queries/analysis/Definitions.ql
similarity index 100%
rename from ql/src/queries/analysis/Definitions.ql
rename to ruby/ql/src/queries/analysis/Definitions.ql
diff --git a/ql/src/queries/diagnostics/ExtractionErrors.ql b/ruby/ql/src/queries/diagnostics/ExtractionErrors.ql
similarity index 100%
rename from ql/src/queries/diagnostics/ExtractionErrors.ql
rename to ruby/ql/src/queries/diagnostics/ExtractionErrors.ql
diff --git a/ql/src/queries/diagnostics/SuccessfullyExtractedFiles.ql b/ruby/ql/src/queries/diagnostics/SuccessfullyExtractedFiles.ql
similarity index 100%
rename from ql/src/queries/diagnostics/SuccessfullyExtractedFiles.ql
rename to ruby/ql/src/queries/diagnostics/SuccessfullyExtractedFiles.ql
diff --git a/ql/src/queries/metrics/FLines.ql b/ruby/ql/src/queries/metrics/FLines.ql
similarity index 100%
rename from ql/src/queries/metrics/FLines.ql
rename to ruby/ql/src/queries/metrics/FLines.ql
diff --git a/ql/src/queries/metrics/FLinesOfCode.ql b/ruby/ql/src/queries/metrics/FLinesOfCode.ql
similarity index 100%
rename from ql/src/queries/metrics/FLinesOfCode.ql
rename to ruby/ql/src/queries/metrics/FLinesOfCode.ql
diff --git a/ql/src/queries/metrics/FLinesOfComments.ql b/ruby/ql/src/queries/metrics/FLinesOfComments.ql
similarity index 100%
rename from ql/src/queries/metrics/FLinesOfComments.ql
rename to ruby/ql/src/queries/metrics/FLinesOfComments.ql
diff --git a/ql/src/queries/security/cwe-078/CommandInjection.qhelp b/ruby/ql/src/queries/security/cwe-078/CommandInjection.qhelp
similarity index 100%
rename from ql/src/queries/security/cwe-078/CommandInjection.qhelp
rename to ruby/ql/src/queries/security/cwe-078/CommandInjection.qhelp
diff --git a/ql/src/queries/security/cwe-078/CommandInjection.ql b/ruby/ql/src/queries/security/cwe-078/CommandInjection.ql
similarity index 100%
rename from ql/src/queries/security/cwe-078/CommandInjection.ql
rename to ruby/ql/src/queries/security/cwe-078/CommandInjection.ql
diff --git a/ql/src/queries/security/cwe-078/examples/command_injection.rb b/ruby/ql/src/queries/security/cwe-078/examples/command_injection.rb
similarity index 100%
rename from ql/src/queries/security/cwe-078/examples/command_injection.rb
rename to ruby/ql/src/queries/security/cwe-078/examples/command_injection.rb
diff --git a/ql/src/queries/security/cwe-079/ReflectedXSS.qhelp b/ruby/ql/src/queries/security/cwe-079/ReflectedXSS.qhelp
similarity index 100%
rename from ql/src/queries/security/cwe-079/ReflectedXSS.qhelp
rename to ruby/ql/src/queries/security/cwe-079/ReflectedXSS.qhelp
diff --git a/ql/src/queries/security/cwe-079/ReflectedXSS.ql b/ruby/ql/src/queries/security/cwe-079/ReflectedXSS.ql
similarity index 100%
rename from ql/src/queries/security/cwe-079/ReflectedXSS.ql
rename to ruby/ql/src/queries/security/cwe-079/ReflectedXSS.ql
diff --git a/ql/src/queries/security/cwe-079/examples/reflective_xss.html.erb b/ruby/ql/src/queries/security/cwe-079/examples/reflective_xss.html.erb
similarity index 100%
rename from ql/src/queries/security/cwe-079/examples/reflective_xss.html.erb
rename to ruby/ql/src/queries/security/cwe-079/examples/reflective_xss.html.erb
diff --git a/ql/src/queries/security/cwe-079/examples/safe.html.erb b/ruby/ql/src/queries/security/cwe-079/examples/safe.html.erb
similarity index 100%
rename from ql/src/queries/security/cwe-079/examples/safe.html.erb
rename to ruby/ql/src/queries/security/cwe-079/examples/safe.html.erb
diff --git a/ql/src/queries/security/cwe-089/SqlInjection.qhelp b/ruby/ql/src/queries/security/cwe-089/SqlInjection.qhelp
similarity index 100%
rename from ql/src/queries/security/cwe-089/SqlInjection.qhelp
rename to ruby/ql/src/queries/security/cwe-089/SqlInjection.qhelp
diff --git a/ql/src/queries/security/cwe-089/SqlInjection.ql b/ruby/ql/src/queries/security/cwe-089/SqlInjection.ql
similarity index 100%
rename from ql/src/queries/security/cwe-089/SqlInjection.ql
rename to ruby/ql/src/queries/security/cwe-089/SqlInjection.ql
diff --git a/ql/src/queries/security/cwe-089/examples/SqlInjection.rb b/ruby/ql/src/queries/security/cwe-089/examples/SqlInjection.rb
similarity index 100%
rename from ql/src/queries/security/cwe-089/examples/SqlInjection.rb
rename to ruby/ql/src/queries/security/cwe-089/examples/SqlInjection.rb
diff --git a/ql/src/queries/security/cwe-094/CodeInjection.qhelp b/ruby/ql/src/queries/security/cwe-094/CodeInjection.qhelp
similarity index 100%
rename from ql/src/queries/security/cwe-094/CodeInjection.qhelp
rename to ruby/ql/src/queries/security/cwe-094/CodeInjection.qhelp
diff --git a/ql/src/queries/security/cwe-094/CodeInjection.ql b/ruby/ql/src/queries/security/cwe-094/CodeInjection.ql
similarity index 100%
rename from ql/src/queries/security/cwe-094/CodeInjection.ql
rename to ruby/ql/src/queries/security/cwe-094/CodeInjection.ql
diff --git a/ql/src/queries/security/cwe-094/examples/code_injection.rb b/ruby/ql/src/queries/security/cwe-094/examples/code_injection.rb
similarity index 100%
rename from ql/src/queries/security/cwe-094/examples/code_injection.rb
rename to ruby/ql/src/queries/security/cwe-094/examples/code_injection.rb
diff --git a/ql/src/queries/security/cwe-1333/PolynomialReDoS.qhelp b/ruby/ql/src/queries/security/cwe-1333/PolynomialReDoS.qhelp
similarity index 100%
rename from ql/src/queries/security/cwe-1333/PolynomialReDoS.qhelp
rename to ruby/ql/src/queries/security/cwe-1333/PolynomialReDoS.qhelp
diff --git a/ql/src/queries/security/cwe-1333/PolynomialReDoS.ql b/ruby/ql/src/queries/security/cwe-1333/PolynomialReDoS.ql
similarity index 100%
rename from ql/src/queries/security/cwe-1333/PolynomialReDoS.ql
rename to ruby/ql/src/queries/security/cwe-1333/PolynomialReDoS.ql
diff --git a/ql/src/queries/security/cwe-1333/ReDoS.qhelp b/ruby/ql/src/queries/security/cwe-1333/ReDoS.qhelp
similarity index 100%
rename from ql/src/queries/security/cwe-1333/ReDoS.qhelp
rename to ruby/ql/src/queries/security/cwe-1333/ReDoS.qhelp
diff --git a/ql/src/queries/security/cwe-1333/ReDoS.ql b/ruby/ql/src/queries/security/cwe-1333/ReDoS.ql
similarity index 100%
rename from ql/src/queries/security/cwe-1333/ReDoS.ql
rename to ruby/ql/src/queries/security/cwe-1333/ReDoS.ql
diff --git a/ql/src/queries/security/cwe-1333/ReDoSIntroduction.inc.qhelp b/ruby/ql/src/queries/security/cwe-1333/ReDoSIntroduction.inc.qhelp
similarity index 100%
rename from ql/src/queries/security/cwe-1333/ReDoSIntroduction.inc.qhelp
rename to ruby/ql/src/queries/security/cwe-1333/ReDoSIntroduction.inc.qhelp
diff --git a/ql/src/queries/security/cwe-1333/ReDoSReferences.inc.qhelp b/ruby/ql/src/queries/security/cwe-1333/ReDoSReferences.inc.qhelp
similarity index 100%
rename from ql/src/queries/security/cwe-1333/ReDoSReferences.inc.qhelp
rename to ruby/ql/src/queries/security/cwe-1333/ReDoSReferences.inc.qhelp
diff --git a/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp b/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp
similarity index 100%
rename from ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp
rename to ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp
diff --git a/ql/src/queries/security/cwe-502/UnsafeDeserialization.ql b/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.ql
similarity index 100%
rename from ql/src/queries/security/cwe-502/UnsafeDeserialization.ql
rename to ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.ql
diff --git a/ql/src/queries/security/cwe-502/examples/UnsafeDeserializationBad.rb b/ruby/ql/src/queries/security/cwe-502/examples/UnsafeDeserializationBad.rb
similarity index 100%
rename from ql/src/queries/security/cwe-502/examples/UnsafeDeserializationBad.rb
rename to ruby/ql/src/queries/security/cwe-502/examples/UnsafeDeserializationBad.rb
diff --git a/ql/src/queries/security/cwe-502/examples/UnsafeDeserializationGood.rb b/ruby/ql/src/queries/security/cwe-502/examples/UnsafeDeserializationGood.rb
similarity index 100%
rename from ql/src/queries/security/cwe-502/examples/UnsafeDeserializationGood.rb
rename to ruby/ql/src/queries/security/cwe-502/examples/UnsafeDeserializationGood.rb
diff --git a/ql/src/queries/security/cwe-601/UrlRedirect.qhelp b/ruby/ql/src/queries/security/cwe-601/UrlRedirect.qhelp
similarity index 100%
rename from ql/src/queries/security/cwe-601/UrlRedirect.qhelp
rename to ruby/ql/src/queries/security/cwe-601/UrlRedirect.qhelp
diff --git a/ql/src/queries/security/cwe-601/UrlRedirect.ql b/ruby/ql/src/queries/security/cwe-601/UrlRedirect.ql
similarity index 100%
rename from ql/src/queries/security/cwe-601/UrlRedirect.ql
rename to ruby/ql/src/queries/security/cwe-601/UrlRedirect.ql
diff --git a/ql/src/queries/security/cwe-601/examples/redirect_bad.rb b/ruby/ql/src/queries/security/cwe-601/examples/redirect_bad.rb
similarity index 100%
rename from ql/src/queries/security/cwe-601/examples/redirect_bad.rb
rename to ruby/ql/src/queries/security/cwe-601/examples/redirect_bad.rb
diff --git a/ql/src/queries/security/cwe-601/examples/redirect_good.rb b/ruby/ql/src/queries/security/cwe-601/examples/redirect_good.rb
similarity index 100%
rename from ql/src/queries/security/cwe-601/examples/redirect_good.rb
rename to ruby/ql/src/queries/security/cwe-601/examples/redirect_good.rb
diff --git a/ql/src/queries/security/cwe-732/WeakFilePermissions.qhelp b/ruby/ql/src/queries/security/cwe-732/WeakFilePermissions.qhelp
similarity index 100%
rename from ql/src/queries/security/cwe-732/WeakFilePermissions.qhelp
rename to ruby/ql/src/queries/security/cwe-732/WeakFilePermissions.qhelp
diff --git a/ql/src/queries/security/cwe-732/WeakFilePermissions.ql b/ruby/ql/src/queries/security/cwe-732/WeakFilePermissions.ql
similarity index 100%
rename from ql/src/queries/security/cwe-732/WeakFilePermissions.ql
rename to ruby/ql/src/queries/security/cwe-732/WeakFilePermissions.ql
diff --git a/ql/src/queries/security/cwe-798/HardcodedCredentials.qhelp b/ruby/ql/src/queries/security/cwe-798/HardcodedCredentials.qhelp
similarity index 100%
rename from ql/src/queries/security/cwe-798/HardcodedCredentials.qhelp
rename to ruby/ql/src/queries/security/cwe-798/HardcodedCredentials.qhelp
diff --git a/ql/src/queries/security/cwe-798/HardcodedCredentials.ql b/ruby/ql/src/queries/security/cwe-798/HardcodedCredentials.ql
similarity index 100%
rename from ql/src/queries/security/cwe-798/HardcodedCredentials.ql
rename to ruby/ql/src/queries/security/cwe-798/HardcodedCredentials.ql
diff --git a/ql/src/queries/security/cwe-798/HardcodedCredentials.rb b/ruby/ql/src/queries/security/cwe-798/HardcodedCredentials.rb
similarity index 100%
rename from ql/src/queries/security/cwe-798/HardcodedCredentials.rb
rename to ruby/ql/src/queries/security/cwe-798/HardcodedCredentials.rb
diff --git a/ql/src/queries/summary/LinesOfCode.ql b/ruby/ql/src/queries/summary/LinesOfCode.ql
similarity index 100%
rename from ql/src/queries/summary/LinesOfCode.ql
rename to ruby/ql/src/queries/summary/LinesOfCode.ql
diff --git a/ql/src/queries/summary/LinesOfUserCode.ql b/ruby/ql/src/queries/summary/LinesOfUserCode.ql
similarity index 100%
rename from ql/src/queries/summary/LinesOfUserCode.ql
rename to ruby/ql/src/queries/summary/LinesOfUserCode.ql
diff --git a/ql/src/queries/summary/NumberOfFilesExtractedWithErrors.ql b/ruby/ql/src/queries/summary/NumberOfFilesExtractedWithErrors.ql
similarity index 100%
rename from ql/src/queries/summary/NumberOfFilesExtractedWithErrors.ql
rename to ruby/ql/src/queries/summary/NumberOfFilesExtractedWithErrors.ql
diff --git a/ql/src/queries/summary/NumberOfSuccessfullyExtractedFiles.ql b/ruby/ql/src/queries/summary/NumberOfSuccessfullyExtractedFiles.ql
similarity index 100%
rename from ql/src/queries/summary/NumberOfSuccessfullyExtractedFiles.ql
rename to ruby/ql/src/queries/summary/NumberOfSuccessfullyExtractedFiles.ql
diff --git a/ql/src/queries/variables/DeadStoreOfLocal.ql b/ruby/ql/src/queries/variables/DeadStoreOfLocal.ql
similarity index 100%
rename from ql/src/queries/variables/DeadStoreOfLocal.ql
rename to ruby/ql/src/queries/variables/DeadStoreOfLocal.ql
diff --git a/ql/src/queries/variables/UninitializedLocal.ql b/ruby/ql/src/queries/variables/UninitializedLocal.ql
similarity index 100%
rename from ql/src/queries/variables/UninitializedLocal.ql
rename to ruby/ql/src/queries/variables/UninitializedLocal.ql
diff --git a/ql/src/queries/variables/UnusedParameter.ql b/ruby/ql/src/queries/variables/UnusedParameter.ql
similarity index 100%
rename from ql/src/queries/variables/UnusedParameter.ql
rename to ruby/ql/src/queries/variables/UnusedParameter.ql
diff --git a/ql/test/TestUtilities/InlineExpectationsTest.qll b/ruby/ql/test/TestUtilities/InlineExpectationsTest.qll
similarity index 100%
rename from ql/test/TestUtilities/InlineExpectationsTest.qll
rename to ruby/ql/test/TestUtilities/InlineExpectationsTest.qll
diff --git a/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll b/ruby/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll
similarity index 100%
rename from ql/test/TestUtilities/InlineExpectationsTestPrivate.qll
rename to ruby/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll
diff --git a/ql/test/library-tests/ast/Ast.expected b/ruby/ql/test/library-tests/ast/Ast.expected
similarity index 100%
rename from ql/test/library-tests/ast/Ast.expected
rename to ruby/ql/test/library-tests/ast/Ast.expected
diff --git a/ql/test/library-tests/ast/Ast.ql b/ruby/ql/test/library-tests/ast/Ast.ql
similarity index 100%
rename from ql/test/library-tests/ast/Ast.ql
rename to ruby/ql/test/library-tests/ast/Ast.ql
diff --git a/ql/test/library-tests/ast/AstDesugar.expected b/ruby/ql/test/library-tests/ast/AstDesugar.expected
similarity index 100%
rename from ql/test/library-tests/ast/AstDesugar.expected
rename to ruby/ql/test/library-tests/ast/AstDesugar.expected
diff --git a/ql/test/library-tests/ast/AstDesugar.ql b/ruby/ql/test/library-tests/ast/AstDesugar.ql
similarity index 100%
rename from ql/test/library-tests/ast/AstDesugar.ql
rename to ruby/ql/test/library-tests/ast/AstDesugar.ql
diff --git a/ql/test/library-tests/ast/calls/arguments.expected b/ruby/ql/test/library-tests/ast/calls/arguments.expected
similarity index 100%
rename from ql/test/library-tests/ast/calls/arguments.expected
rename to ruby/ql/test/library-tests/ast/calls/arguments.expected
diff --git a/ql/test/library-tests/ast/calls/arguments.ql b/ruby/ql/test/library-tests/ast/calls/arguments.ql
similarity index 100%
rename from ql/test/library-tests/ast/calls/arguments.ql
rename to ruby/ql/test/library-tests/ast/calls/arguments.ql
diff --git a/ql/test/library-tests/ast/calls/calls.expected b/ruby/ql/test/library-tests/ast/calls/calls.expected
similarity index 100%
rename from ql/test/library-tests/ast/calls/calls.expected
rename to ruby/ql/test/library-tests/ast/calls/calls.expected
diff --git a/ql/test/library-tests/ast/calls/calls.ql b/ruby/ql/test/library-tests/ast/calls/calls.ql
similarity index 100%
rename from ql/test/library-tests/ast/calls/calls.ql
rename to ruby/ql/test/library-tests/ast/calls/calls.ql
diff --git a/ql/test/library-tests/ast/calls/calls.rb b/ruby/ql/test/library-tests/ast/calls/calls.rb
similarity index 100%
rename from ql/test/library-tests/ast/calls/calls.rb
rename to ruby/ql/test/library-tests/ast/calls/calls.rb
diff --git a/ql/test/library-tests/ast/constants/constants.expected b/ruby/ql/test/library-tests/ast/constants/constants.expected
similarity index 100%
rename from ql/test/library-tests/ast/constants/constants.expected
rename to ruby/ql/test/library-tests/ast/constants/constants.expected
diff --git a/ql/test/library-tests/ast/constants/constants.ql b/ruby/ql/test/library-tests/ast/constants/constants.ql
similarity index 100%
rename from ql/test/library-tests/ast/constants/constants.ql
rename to ruby/ql/test/library-tests/ast/constants/constants.ql
diff --git a/ql/test/library-tests/ast/constants/constants.rb b/ruby/ql/test/library-tests/ast/constants/constants.rb
similarity index 100%
rename from ql/test/library-tests/ast/constants/constants.rb
rename to ruby/ql/test/library-tests/ast/constants/constants.rb
diff --git a/ql/test/library-tests/ast/control/CaseExpr.expected b/ruby/ql/test/library-tests/ast/control/CaseExpr.expected
similarity index 100%
rename from ql/test/library-tests/ast/control/CaseExpr.expected
rename to ruby/ql/test/library-tests/ast/control/CaseExpr.expected
diff --git a/ql/test/library-tests/ast/control/CaseExpr.ql b/ruby/ql/test/library-tests/ast/control/CaseExpr.ql
similarity index 100%
rename from ql/test/library-tests/ast/control/CaseExpr.ql
rename to ruby/ql/test/library-tests/ast/control/CaseExpr.ql
diff --git a/ql/test/library-tests/ast/control/ConditionalExpr.expected b/ruby/ql/test/library-tests/ast/control/ConditionalExpr.expected
similarity index 100%
rename from ql/test/library-tests/ast/control/ConditionalExpr.expected
rename to ruby/ql/test/library-tests/ast/control/ConditionalExpr.expected
diff --git a/ql/test/library-tests/ast/control/ConditionalExpr.ql b/ruby/ql/test/library-tests/ast/control/ConditionalExpr.ql
similarity index 100%
rename from ql/test/library-tests/ast/control/ConditionalExpr.ql
rename to ruby/ql/test/library-tests/ast/control/ConditionalExpr.ql
diff --git a/ql/test/library-tests/ast/control/ControlExpr.expected b/ruby/ql/test/library-tests/ast/control/ControlExpr.expected
similarity index 100%
rename from ql/test/library-tests/ast/control/ControlExpr.expected
rename to ruby/ql/test/library-tests/ast/control/ControlExpr.expected
diff --git a/ql/test/library-tests/ast/control/ControlExpr.ql b/ruby/ql/test/library-tests/ast/control/ControlExpr.ql
similarity index 100%
rename from ql/test/library-tests/ast/control/ControlExpr.ql
rename to ruby/ql/test/library-tests/ast/control/ControlExpr.ql
diff --git a/ql/test/library-tests/ast/control/Loop.expected b/ruby/ql/test/library-tests/ast/control/Loop.expected
similarity index 100%
rename from ql/test/library-tests/ast/control/Loop.expected
rename to ruby/ql/test/library-tests/ast/control/Loop.expected
diff --git a/ql/test/library-tests/ast/control/Loop.ql b/ruby/ql/test/library-tests/ast/control/Loop.ql
similarity index 100%
rename from ql/test/library-tests/ast/control/Loop.ql
rename to ruby/ql/test/library-tests/ast/control/Loop.ql
diff --git a/ql/test/library-tests/ast/control/cases.rb b/ruby/ql/test/library-tests/ast/control/cases.rb
similarity index 100%
rename from ql/test/library-tests/ast/control/cases.rb
rename to ruby/ql/test/library-tests/ast/control/cases.rb
diff --git a/ql/test/library-tests/ast/control/conditionals.rb b/ruby/ql/test/library-tests/ast/control/conditionals.rb
similarity index 100%
rename from ql/test/library-tests/ast/control/conditionals.rb
rename to ruby/ql/test/library-tests/ast/control/conditionals.rb
diff --git a/ql/test/library-tests/ast/control/loops.rb b/ruby/ql/test/library-tests/ast/control/loops.rb
similarity index 100%
rename from ql/test/library-tests/ast/control/loops.rb
rename to ruby/ql/test/library-tests/ast/control/loops.rb
diff --git a/ql/test/library-tests/ast/erb/Erb.expected b/ruby/ql/test/library-tests/ast/erb/Erb.expected
similarity index 100%
rename from ql/test/library-tests/ast/erb/Erb.expected
rename to ruby/ql/test/library-tests/ast/erb/Erb.expected
diff --git a/ql/test/library-tests/ast/erb/Erb.ql b/ruby/ql/test/library-tests/ast/erb/Erb.ql
similarity index 100%
rename from ql/test/library-tests/ast/erb/Erb.ql
rename to ruby/ql/test/library-tests/ast/erb/Erb.ql
diff --git a/ql/test/library-tests/ast/erb/template.html.erb b/ruby/ql/test/library-tests/ast/erb/template.html.erb
similarity index 100%
rename from ql/test/library-tests/ast/erb/template.html.erb
rename to ruby/ql/test/library-tests/ast/erb/template.html.erb
diff --git a/ql/test/library-tests/ast/gems/Gemfile b/ruby/ql/test/library-tests/ast/gems/Gemfile
similarity index 100%
rename from ql/test/library-tests/ast/gems/Gemfile
rename to ruby/ql/test/library-tests/ast/gems/Gemfile
diff --git a/ql/test/library-tests/ast/gems/lib/test.rb b/ruby/ql/test/library-tests/ast/gems/lib/test.rb
similarity index 100%
rename from ql/test/library-tests/ast/gems/lib/test.rb
rename to ruby/ql/test/library-tests/ast/gems/lib/test.rb
diff --git a/ql/test/library-tests/ast/gems/test.expected b/ruby/ql/test/library-tests/ast/gems/test.expected
similarity index 100%
rename from ql/test/library-tests/ast/gems/test.expected
rename to ruby/ql/test/library-tests/ast/gems/test.expected
diff --git a/ql/test/library-tests/ast/gems/test.gemspec b/ruby/ql/test/library-tests/ast/gems/test.gemspec
similarity index 100%
rename from ql/test/library-tests/ast/gems/test.gemspec
rename to ruby/ql/test/library-tests/ast/gems/test.gemspec
diff --git a/ql/test/library-tests/ast/gems/test.ql b/ruby/ql/test/library-tests/ast/gems/test.ql
similarity index 100%
rename from ql/test/library-tests/ast/gems/test.ql
rename to ruby/ql/test/library-tests/ast/gems/test.ql
diff --git a/ql/test/library-tests/ast/literals/literals.expected b/ruby/ql/test/library-tests/ast/literals/literals.expected
similarity index 100%
rename from ql/test/library-tests/ast/literals/literals.expected
rename to ruby/ql/test/library-tests/ast/literals/literals.expected
diff --git a/ql/test/library-tests/ast/literals/literals.ql b/ruby/ql/test/library-tests/ast/literals/literals.ql
similarity index 100%
rename from ql/test/library-tests/ast/literals/literals.ql
rename to ruby/ql/test/library-tests/ast/literals/literals.ql
diff --git a/ql/test/library-tests/ast/literals/literals.rb b/ruby/ql/test/library-tests/ast/literals/literals.rb
similarity index 100%
rename from ql/test/library-tests/ast/literals/literals.rb
rename to ruby/ql/test/library-tests/ast/literals/literals.rb
diff --git a/ql/test/library-tests/ast/misc/misc.erb b/ruby/ql/test/library-tests/ast/misc/misc.erb
similarity index 100%
rename from ql/test/library-tests/ast/misc/misc.erb
rename to ruby/ql/test/library-tests/ast/misc/misc.erb
diff --git a/ql/test/library-tests/ast/misc/misc.expected b/ruby/ql/test/library-tests/ast/misc/misc.expected
similarity index 100%
rename from ql/test/library-tests/ast/misc/misc.expected
rename to ruby/ql/test/library-tests/ast/misc/misc.expected
diff --git a/ql/test/library-tests/ast/misc/misc.ql b/ruby/ql/test/library-tests/ast/misc/misc.ql
similarity index 100%
rename from ql/test/library-tests/ast/misc/misc.ql
rename to ruby/ql/test/library-tests/ast/misc/misc.ql
diff --git a/ql/test/library-tests/ast/misc/misc.rb b/ruby/ql/test/library-tests/ast/misc/misc.rb
similarity index 100%
rename from ql/test/library-tests/ast/misc/misc.rb
rename to ruby/ql/test/library-tests/ast/misc/misc.rb
diff --git a/ql/test/library-tests/ast/modules/classes.expected b/ruby/ql/test/library-tests/ast/modules/classes.expected
similarity index 100%
rename from ql/test/library-tests/ast/modules/classes.expected
rename to ruby/ql/test/library-tests/ast/modules/classes.expected
diff --git a/ql/test/library-tests/ast/modules/classes.ql b/ruby/ql/test/library-tests/ast/modules/classes.ql
similarity index 100%
rename from ql/test/library-tests/ast/modules/classes.ql
rename to ruby/ql/test/library-tests/ast/modules/classes.ql
diff --git a/ql/test/library-tests/ast/modules/classes.rb b/ruby/ql/test/library-tests/ast/modules/classes.rb
similarity index 100%
rename from ql/test/library-tests/ast/modules/classes.rb
rename to ruby/ql/test/library-tests/ast/modules/classes.rb
diff --git a/ql/test/library-tests/ast/modules/module_base.expected b/ruby/ql/test/library-tests/ast/modules/module_base.expected
similarity index 100%
rename from ql/test/library-tests/ast/modules/module_base.expected
rename to ruby/ql/test/library-tests/ast/modules/module_base.expected
diff --git a/ql/test/library-tests/ast/modules/module_base.ql b/ruby/ql/test/library-tests/ast/modules/module_base.ql
similarity index 100%
rename from ql/test/library-tests/ast/modules/module_base.ql
rename to ruby/ql/test/library-tests/ast/modules/module_base.ql
diff --git a/ql/test/library-tests/ast/modules/modules.expected b/ruby/ql/test/library-tests/ast/modules/modules.expected
similarity index 100%
rename from ql/test/library-tests/ast/modules/modules.expected
rename to ruby/ql/test/library-tests/ast/modules/modules.expected
diff --git a/ql/test/library-tests/ast/modules/modules.ql b/ruby/ql/test/library-tests/ast/modules/modules.ql
similarity index 100%
rename from ql/test/library-tests/ast/modules/modules.ql
rename to ruby/ql/test/library-tests/ast/modules/modules.ql
diff --git a/ql/test/library-tests/ast/modules/modules.rb b/ruby/ql/test/library-tests/ast/modules/modules.rb
similarity index 100%
rename from ql/test/library-tests/ast/modules/modules.rb
rename to ruby/ql/test/library-tests/ast/modules/modules.rb
diff --git a/ql/test/library-tests/ast/modules/singleton_classes.expected b/ruby/ql/test/library-tests/ast/modules/singleton_classes.expected
similarity index 100%
rename from ql/test/library-tests/ast/modules/singleton_classes.expected
rename to ruby/ql/test/library-tests/ast/modules/singleton_classes.expected
diff --git a/ql/test/library-tests/ast/modules/singleton_classes.ql b/ruby/ql/test/library-tests/ast/modules/singleton_classes.ql
similarity index 100%
rename from ql/test/library-tests/ast/modules/singleton_classes.ql
rename to ruby/ql/test/library-tests/ast/modules/singleton_classes.ql
diff --git a/ql/test/library-tests/ast/modules/toplevel.expected b/ruby/ql/test/library-tests/ast/modules/toplevel.expected
similarity index 100%
rename from ql/test/library-tests/ast/modules/toplevel.expected
rename to ruby/ql/test/library-tests/ast/modules/toplevel.expected
diff --git a/ql/test/library-tests/ast/modules/toplevel.ql b/ruby/ql/test/library-tests/ast/modules/toplevel.ql
similarity index 100%
rename from ql/test/library-tests/ast/modules/toplevel.ql
rename to ruby/ql/test/library-tests/ast/modules/toplevel.ql
diff --git a/ql/test/library-tests/ast/modules/toplevel.rb b/ruby/ql/test/library-tests/ast/modules/toplevel.rb
similarity index 100%
rename from ql/test/library-tests/ast/modules/toplevel.rb
rename to ruby/ql/test/library-tests/ast/modules/toplevel.rb
diff --git a/ql/test/library-tests/ast/operations/assignment.expected b/ruby/ql/test/library-tests/ast/operations/assignment.expected
similarity index 100%
rename from ql/test/library-tests/ast/operations/assignment.expected
rename to ruby/ql/test/library-tests/ast/operations/assignment.expected
diff --git a/ql/test/library-tests/ast/operations/assignment.ql b/ruby/ql/test/library-tests/ast/operations/assignment.ql
similarity index 100%
rename from ql/test/library-tests/ast/operations/assignment.ql
rename to ruby/ql/test/library-tests/ast/operations/assignment.ql
diff --git a/ql/test/library-tests/ast/operations/binary.expected b/ruby/ql/test/library-tests/ast/operations/binary.expected
similarity index 100%
rename from ql/test/library-tests/ast/operations/binary.expected
rename to ruby/ql/test/library-tests/ast/operations/binary.expected
diff --git a/ql/test/library-tests/ast/operations/binary.ql b/ruby/ql/test/library-tests/ast/operations/binary.ql
similarity index 100%
rename from ql/test/library-tests/ast/operations/binary.ql
rename to ruby/ql/test/library-tests/ast/operations/binary.ql
diff --git a/ql/test/library-tests/ast/operations/operation.expected b/ruby/ql/test/library-tests/ast/operations/operation.expected
similarity index 100%
rename from ql/test/library-tests/ast/operations/operation.expected
rename to ruby/ql/test/library-tests/ast/operations/operation.expected
diff --git a/ql/test/library-tests/ast/operations/operation.ql b/ruby/ql/test/library-tests/ast/operations/operation.ql
similarity index 100%
rename from ql/test/library-tests/ast/operations/operation.ql
rename to ruby/ql/test/library-tests/ast/operations/operation.ql
diff --git a/ql/test/library-tests/ast/operations/operations.rb b/ruby/ql/test/library-tests/ast/operations/operations.rb
similarity index 100%
rename from ql/test/library-tests/ast/operations/operations.rb
rename to ruby/ql/test/library-tests/ast/operations/operations.rb
diff --git a/ql/test/library-tests/ast/operations/unary.expected b/ruby/ql/test/library-tests/ast/operations/unary.expected
similarity index 100%
rename from ql/test/library-tests/ast/operations/unary.expected
rename to ruby/ql/test/library-tests/ast/operations/unary.expected
diff --git a/ql/test/library-tests/ast/operations/unary.ql b/ruby/ql/test/library-tests/ast/operations/unary.ql
similarity index 100%
rename from ql/test/library-tests/ast/operations/unary.ql
rename to ruby/ql/test/library-tests/ast/operations/unary.ql
diff --git a/ql/test/library-tests/ast/params/params.expected b/ruby/ql/test/library-tests/ast/params/params.expected
similarity index 100%
rename from ql/test/library-tests/ast/params/params.expected
rename to ruby/ql/test/library-tests/ast/params/params.expected
diff --git a/ql/test/library-tests/ast/params/params.ql b/ruby/ql/test/library-tests/ast/params/params.ql
similarity index 100%
rename from ql/test/library-tests/ast/params/params.ql
rename to ruby/ql/test/library-tests/ast/params/params.ql
diff --git a/ql/test/library-tests/ast/params/params.rb b/ruby/ql/test/library-tests/ast/params/params.rb
similarity index 100%
rename from ql/test/library-tests/ast/params/params.rb
rename to ruby/ql/test/library-tests/ast/params/params.rb
diff --git a/ql/test/library-tests/controlflow/graph/Cfg.expected b/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected
similarity index 100%
rename from ql/test/library-tests/controlflow/graph/Cfg.expected
rename to ruby/ql/test/library-tests/controlflow/graph/Cfg.expected
diff --git a/ql/test/library-tests/controlflow/graph/Cfg.ql b/ruby/ql/test/library-tests/controlflow/graph/Cfg.ql
similarity index 100%
rename from ql/test/library-tests/controlflow/graph/Cfg.ql
rename to ruby/ql/test/library-tests/controlflow/graph/Cfg.ql
diff --git a/ql/test/library-tests/controlflow/graph/break_ensure.rb b/ruby/ql/test/library-tests/controlflow/graph/break_ensure.rb
similarity index 100%
rename from ql/test/library-tests/controlflow/graph/break_ensure.rb
rename to ruby/ql/test/library-tests/controlflow/graph/break_ensure.rb
diff --git a/ql/test/library-tests/controlflow/graph/case.rb b/ruby/ql/test/library-tests/controlflow/graph/case.rb
similarity index 100%
rename from ql/test/library-tests/controlflow/graph/case.rb
rename to ruby/ql/test/library-tests/controlflow/graph/case.rb
diff --git a/ql/test/library-tests/controlflow/graph/cfg.html.erb b/ruby/ql/test/library-tests/controlflow/graph/cfg.html.erb
similarity index 100%
rename from ql/test/library-tests/controlflow/graph/cfg.html.erb
rename to ruby/ql/test/library-tests/controlflow/graph/cfg.html.erb
diff --git a/ql/test/library-tests/controlflow/graph/cfg.rb b/ruby/ql/test/library-tests/controlflow/graph/cfg.rb
similarity index 100%
rename from ql/test/library-tests/controlflow/graph/cfg.rb
rename to ruby/ql/test/library-tests/controlflow/graph/cfg.rb
diff --git a/ql/test/library-tests/controlflow/graph/desugar.rb b/ruby/ql/test/library-tests/controlflow/graph/desugar.rb
similarity index 100%
rename from ql/test/library-tests/controlflow/graph/desugar.rb
rename to ruby/ql/test/library-tests/controlflow/graph/desugar.rb
diff --git a/ql/test/library-tests/controlflow/graph/exit.rb b/ruby/ql/test/library-tests/controlflow/graph/exit.rb
similarity index 100%
rename from ql/test/library-tests/controlflow/graph/exit.rb
rename to ruby/ql/test/library-tests/controlflow/graph/exit.rb
diff --git a/ql/test/library-tests/controlflow/graph/heredoc.rb b/ruby/ql/test/library-tests/controlflow/graph/heredoc.rb
similarity index 100%
rename from ql/test/library-tests/controlflow/graph/heredoc.rb
rename to ruby/ql/test/library-tests/controlflow/graph/heredoc.rb
diff --git a/ql/test/library-tests/controlflow/graph/ifs.rb b/ruby/ql/test/library-tests/controlflow/graph/ifs.rb
similarity index 100%
rename from ql/test/library-tests/controlflow/graph/ifs.rb
rename to ruby/ql/test/library-tests/controlflow/graph/ifs.rb
diff --git a/ql/test/library-tests/controlflow/graph/loops.rb b/ruby/ql/test/library-tests/controlflow/graph/loops.rb
similarity index 100%
rename from ql/test/library-tests/controlflow/graph/loops.rb
rename to ruby/ql/test/library-tests/controlflow/graph/loops.rb
diff --git a/ql/test/library-tests/controlflow/graph/raise.rb b/ruby/ql/test/library-tests/controlflow/graph/raise.rb
similarity index 100%
rename from ql/test/library-tests/controlflow/graph/raise.rb
rename to ruby/ql/test/library-tests/controlflow/graph/raise.rb
diff --git a/ql/test/library-tests/dataflow/api-graphs/test1.rb b/ruby/ql/test/library-tests/dataflow/api-graphs/test1.rb
similarity index 100%
rename from ql/test/library-tests/dataflow/api-graphs/test1.rb
rename to ruby/ql/test/library-tests/dataflow/api-graphs/test1.rb
diff --git a/ql/test/library-tests/dataflow/api-graphs/use.expected b/ruby/ql/test/library-tests/dataflow/api-graphs/use.expected
similarity index 100%
rename from ql/test/library-tests/dataflow/api-graphs/use.expected
rename to ruby/ql/test/library-tests/dataflow/api-graphs/use.expected
diff --git a/ql/test/library-tests/dataflow/api-graphs/use.ql b/ruby/ql/test/library-tests/dataflow/api-graphs/use.ql
similarity index 100%
rename from ql/test/library-tests/dataflow/api-graphs/use.ql
rename to ruby/ql/test/library-tests/dataflow/api-graphs/use.ql
diff --git a/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected
similarity index 100%
rename from ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected
rename to ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected
diff --git a/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.ql b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.ql
similarity index 100%
rename from ql/test/library-tests/dataflow/barrier-guards/barrier-guards.ql
rename to ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.ql
diff --git a/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb
similarity index 100%
rename from ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb
rename to ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb
diff --git a/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.expected b/ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.expected
similarity index 100%
rename from ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.expected
rename to ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.expected
diff --git a/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.ql b/ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.ql
similarity index 100%
rename from ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.ql
rename to ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.ql
diff --git a/ql/test/library-tests/dataflow/call-sensitivity/call_sensitivity.rb b/ruby/ql/test/library-tests/dataflow/call-sensitivity/call_sensitivity.rb
similarity index 100%
rename from ql/test/library-tests/dataflow/call-sensitivity/call_sensitivity.rb
rename to ruby/ql/test/library-tests/dataflow/call-sensitivity/call_sensitivity.rb
diff --git a/ql/test/library-tests/dataflow/local/DataflowStep.expected b/ruby/ql/test/library-tests/dataflow/local/DataflowStep.expected
similarity index 100%
rename from ql/test/library-tests/dataflow/local/DataflowStep.expected
rename to ruby/ql/test/library-tests/dataflow/local/DataflowStep.expected
diff --git a/ql/test/library-tests/dataflow/local/DataflowStep.ql b/ruby/ql/test/library-tests/dataflow/local/DataflowStep.ql
similarity index 100%
rename from ql/test/library-tests/dataflow/local/DataflowStep.ql
rename to ruby/ql/test/library-tests/dataflow/local/DataflowStep.ql
diff --git a/ql/test/library-tests/dataflow/local/ReturnNodes.expected b/ruby/ql/test/library-tests/dataflow/local/ReturnNodes.expected
similarity index 100%
rename from ql/test/library-tests/dataflow/local/ReturnNodes.expected
rename to ruby/ql/test/library-tests/dataflow/local/ReturnNodes.expected
diff --git a/ql/test/library-tests/dataflow/local/ReturnNodes.ql b/ruby/ql/test/library-tests/dataflow/local/ReturnNodes.ql
similarity index 100%
rename from ql/test/library-tests/dataflow/local/ReturnNodes.ql
rename to ruby/ql/test/library-tests/dataflow/local/ReturnNodes.ql
diff --git a/ql/test/library-tests/dataflow/local/local_dataflow.rb b/ruby/ql/test/library-tests/dataflow/local/local_dataflow.rb
similarity index 100%
rename from ql/test/library-tests/dataflow/local/local_dataflow.rb
rename to ruby/ql/test/library-tests/dataflow/local/local_dataflow.rb
diff --git a/ql/test/library-tests/dataflow/summaries/Summaries.expected b/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected
similarity index 100%
rename from ql/test/library-tests/dataflow/summaries/Summaries.expected
rename to ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected
diff --git a/ql/test/library-tests/dataflow/summaries/Summaries.ql b/ruby/ql/test/library-tests/dataflow/summaries/Summaries.ql
similarity index 100%
rename from ql/test/library-tests/dataflow/summaries/Summaries.ql
rename to ruby/ql/test/library-tests/dataflow/summaries/Summaries.ql
diff --git a/ql/test/library-tests/dataflow/summaries/summaries.rb b/ruby/ql/test/library-tests/dataflow/summaries/summaries.rb
similarity index 100%
rename from ql/test/library-tests/dataflow/summaries/summaries.rb
rename to ruby/ql/test/library-tests/dataflow/summaries/summaries.rb
diff --git a/ql/test/library-tests/frameworks/ActionController.expected b/ruby/ql/test/library-tests/frameworks/ActionController.expected
similarity index 100%
rename from ql/test/library-tests/frameworks/ActionController.expected
rename to ruby/ql/test/library-tests/frameworks/ActionController.expected
diff --git a/ql/test/library-tests/frameworks/ActionController.ql b/ruby/ql/test/library-tests/frameworks/ActionController.ql
similarity index 100%
rename from ql/test/library-tests/frameworks/ActionController.ql
rename to ruby/ql/test/library-tests/frameworks/ActionController.ql
diff --git a/ql/test/library-tests/frameworks/ActionView.expected b/ruby/ql/test/library-tests/frameworks/ActionView.expected
similarity index 100%
rename from ql/test/library-tests/frameworks/ActionView.expected
rename to ruby/ql/test/library-tests/frameworks/ActionView.expected
diff --git a/ql/test/library-tests/frameworks/ActionView.ql b/ruby/ql/test/library-tests/frameworks/ActionView.ql
similarity index 100%
rename from ql/test/library-tests/frameworks/ActionView.ql
rename to ruby/ql/test/library-tests/frameworks/ActionView.ql
diff --git a/ql/test/library-tests/frameworks/ActiveRecord.expected b/ruby/ql/test/library-tests/frameworks/ActiveRecord.expected
similarity index 100%
rename from ql/test/library-tests/frameworks/ActiveRecord.expected
rename to ruby/ql/test/library-tests/frameworks/ActiveRecord.expected
diff --git a/ql/test/library-tests/frameworks/ActiveRecord.ql b/ruby/ql/test/library-tests/frameworks/ActiveRecord.ql
similarity index 100%
rename from ql/test/library-tests/frameworks/ActiveRecord.ql
rename to ruby/ql/test/library-tests/frameworks/ActiveRecord.ql
diff --git a/ql/test/library-tests/frameworks/ActiveRecordInjection.rb b/ruby/ql/test/library-tests/frameworks/ActiveRecordInjection.rb
similarity index 100%
rename from ql/test/library-tests/frameworks/ActiveRecordInjection.rb
rename to ruby/ql/test/library-tests/frameworks/ActiveRecordInjection.rb
diff --git a/ql/test/library-tests/frameworks/CommandExecution.rb b/ruby/ql/test/library-tests/frameworks/CommandExecution.rb
similarity index 100%
rename from ql/test/library-tests/frameworks/CommandExecution.rb
rename to ruby/ql/test/library-tests/frameworks/CommandExecution.rb
diff --git a/ql/test/library-tests/frameworks/Eval.rb b/ruby/ql/test/library-tests/frameworks/Eval.rb
similarity index 100%
rename from ql/test/library-tests/frameworks/Eval.rb
rename to ruby/ql/test/library-tests/frameworks/Eval.rb
diff --git a/ql/test/library-tests/frameworks/Files.expected b/ruby/ql/test/library-tests/frameworks/Files.expected
similarity index 100%
rename from ql/test/library-tests/frameworks/Files.expected
rename to ruby/ql/test/library-tests/frameworks/Files.expected
diff --git a/ql/test/library-tests/frameworks/Files.ql b/ruby/ql/test/library-tests/frameworks/Files.ql
similarity index 100%
rename from ql/test/library-tests/frameworks/Files.ql
rename to ruby/ql/test/library-tests/frameworks/Files.ql
diff --git a/ql/test/library-tests/frameworks/Files.rb b/ruby/ql/test/library-tests/frameworks/Files.rb
similarity index 100%
rename from ql/test/library-tests/frameworks/Files.rb
rename to ruby/ql/test/library-tests/frameworks/Files.rb
diff --git a/ql/test/library-tests/frameworks/StandardLibrary.expected b/ruby/ql/test/library-tests/frameworks/StandardLibrary.expected
similarity index 100%
rename from ql/test/library-tests/frameworks/StandardLibrary.expected
rename to ruby/ql/test/library-tests/frameworks/StandardLibrary.expected
diff --git a/ql/test/library-tests/frameworks/StandardLibrary.ql b/ruby/ql/test/library-tests/frameworks/StandardLibrary.ql
similarity index 100%
rename from ql/test/library-tests/frameworks/StandardLibrary.ql
rename to ruby/ql/test/library-tests/frameworks/StandardLibrary.ql
diff --git a/ql/test/library-tests/frameworks/app/components/DummyComponent.rb b/ruby/ql/test/library-tests/frameworks/app/components/DummyComponent.rb
similarity index 100%
rename from ql/test/library-tests/frameworks/app/components/DummyComponent.rb
rename to ruby/ql/test/library-tests/frameworks/app/components/DummyComponent.rb
diff --git a/ql/test/library-tests/frameworks/app/controllers/foo/bars_controller.rb b/ruby/ql/test/library-tests/frameworks/app/controllers/foo/bars_controller.rb
similarity index 100%
rename from ql/test/library-tests/frameworks/app/controllers/foo/bars_controller.rb
rename to ruby/ql/test/library-tests/frameworks/app/controllers/foo/bars_controller.rb
diff --git a/ql/test/library-tests/frameworks/app/views/foo/bars/_widget.html.erb b/ruby/ql/test/library-tests/frameworks/app/views/foo/bars/_widget.html.erb
similarity index 100%
rename from ql/test/library-tests/frameworks/app/views/foo/bars/_widget.html.erb
rename to ruby/ql/test/library-tests/frameworks/app/views/foo/bars/_widget.html.erb
diff --git a/ql/test/library-tests/frameworks/app/views/foo/bars/show.html.erb b/ruby/ql/test/library-tests/frameworks/app/views/foo/bars/show.html.erb
similarity index 100%
rename from ql/test/library-tests/frameworks/app/views/foo/bars/show.html.erb
rename to ruby/ql/test/library-tests/frameworks/app/views/foo/bars/show.html.erb
diff --git a/ql/test/library-tests/frameworks/http_clients/Excon.expected b/ruby/ql/test/library-tests/frameworks/http_clients/Excon.expected
similarity index 100%
rename from ql/test/library-tests/frameworks/http_clients/Excon.expected
rename to ruby/ql/test/library-tests/frameworks/http_clients/Excon.expected
diff --git a/ql/test/library-tests/frameworks/http_clients/Excon.ql b/ruby/ql/test/library-tests/frameworks/http_clients/Excon.ql
similarity index 100%
rename from ql/test/library-tests/frameworks/http_clients/Excon.ql
rename to ruby/ql/test/library-tests/frameworks/http_clients/Excon.ql
diff --git a/ql/test/library-tests/frameworks/http_clients/Excon.rb b/ruby/ql/test/library-tests/frameworks/http_clients/Excon.rb
similarity index 100%
rename from ql/test/library-tests/frameworks/http_clients/Excon.rb
rename to ruby/ql/test/library-tests/frameworks/http_clients/Excon.rb
diff --git a/ql/test/library-tests/frameworks/http_clients/Faraday.expected b/ruby/ql/test/library-tests/frameworks/http_clients/Faraday.expected
similarity index 100%
rename from ql/test/library-tests/frameworks/http_clients/Faraday.expected
rename to ruby/ql/test/library-tests/frameworks/http_clients/Faraday.expected
diff --git a/ql/test/library-tests/frameworks/http_clients/Faraday.ql b/ruby/ql/test/library-tests/frameworks/http_clients/Faraday.ql
similarity index 100%
rename from ql/test/library-tests/frameworks/http_clients/Faraday.ql
rename to ruby/ql/test/library-tests/frameworks/http_clients/Faraday.ql
diff --git a/ql/test/library-tests/frameworks/http_clients/Faraday.rb b/ruby/ql/test/library-tests/frameworks/http_clients/Faraday.rb
similarity index 100%
rename from ql/test/library-tests/frameworks/http_clients/Faraday.rb
rename to ruby/ql/test/library-tests/frameworks/http_clients/Faraday.rb
diff --git a/ql/test/library-tests/frameworks/http_clients/NetHTTP.expected b/ruby/ql/test/library-tests/frameworks/http_clients/NetHTTP.expected
similarity index 100%
rename from ql/test/library-tests/frameworks/http_clients/NetHTTP.expected
rename to ruby/ql/test/library-tests/frameworks/http_clients/NetHTTP.expected
diff --git a/ql/test/library-tests/frameworks/http_clients/NetHTTP.ql b/ruby/ql/test/library-tests/frameworks/http_clients/NetHTTP.ql
similarity index 100%
rename from ql/test/library-tests/frameworks/http_clients/NetHTTP.ql
rename to ruby/ql/test/library-tests/frameworks/http_clients/NetHTTP.ql
diff --git a/ql/test/library-tests/frameworks/http_clients/NetHTTP.rb b/ruby/ql/test/library-tests/frameworks/http_clients/NetHTTP.rb
similarity index 100%
rename from ql/test/library-tests/frameworks/http_clients/NetHTTP.rb
rename to ruby/ql/test/library-tests/frameworks/http_clients/NetHTTP.rb
diff --git a/ql/test/library-tests/frameworks/http_clients/RestClient.expected b/ruby/ql/test/library-tests/frameworks/http_clients/RestClient.expected
similarity index 100%
rename from ql/test/library-tests/frameworks/http_clients/RestClient.expected
rename to ruby/ql/test/library-tests/frameworks/http_clients/RestClient.expected
diff --git a/ql/test/library-tests/frameworks/http_clients/RestClient.ql b/ruby/ql/test/library-tests/frameworks/http_clients/RestClient.ql
similarity index 100%
rename from ql/test/library-tests/frameworks/http_clients/RestClient.ql
rename to ruby/ql/test/library-tests/frameworks/http_clients/RestClient.ql
diff --git a/ql/test/library-tests/frameworks/http_clients/RestClient.rb b/ruby/ql/test/library-tests/frameworks/http_clients/RestClient.rb
similarity index 100%
rename from ql/test/library-tests/frameworks/http_clients/RestClient.rb
rename to ruby/ql/test/library-tests/frameworks/http_clients/RestClient.rb
diff --git a/ql/test/library-tests/modules/ancestors.expected b/ruby/ql/test/library-tests/modules/ancestors.expected
similarity index 100%
rename from ql/test/library-tests/modules/ancestors.expected
rename to ruby/ql/test/library-tests/modules/ancestors.expected
diff --git a/ql/test/library-tests/modules/ancestors.ql b/ruby/ql/test/library-tests/modules/ancestors.ql
similarity index 100%
rename from ql/test/library-tests/modules/ancestors.ql
rename to ruby/ql/test/library-tests/modules/ancestors.ql
diff --git a/ql/test/library-tests/modules/callgraph.expected b/ruby/ql/test/library-tests/modules/callgraph.expected
similarity index 100%
rename from ql/test/library-tests/modules/callgraph.expected
rename to ruby/ql/test/library-tests/modules/callgraph.expected
diff --git a/ql/test/library-tests/modules/callgraph.ql b/ruby/ql/test/library-tests/modules/callgraph.ql
similarity index 100%
rename from ql/test/library-tests/modules/callgraph.ql
rename to ruby/ql/test/library-tests/modules/callgraph.ql
diff --git a/ql/test/library-tests/modules/calls.rb b/ruby/ql/test/library-tests/modules/calls.rb
similarity index 100%
rename from ql/test/library-tests/modules/calls.rb
rename to ruby/ql/test/library-tests/modules/calls.rb
diff --git a/ql/test/library-tests/modules/hello.rb b/ruby/ql/test/library-tests/modules/hello.rb
similarity index 100%
rename from ql/test/library-tests/modules/hello.rb
rename to ruby/ql/test/library-tests/modules/hello.rb
diff --git a/ql/test/library-tests/modules/methods.expected b/ruby/ql/test/library-tests/modules/methods.expected
similarity index 100%
rename from ql/test/library-tests/modules/methods.expected
rename to ruby/ql/test/library-tests/modules/methods.expected
diff --git a/ql/test/library-tests/modules/methods.ql b/ruby/ql/test/library-tests/modules/methods.ql
similarity index 100%
rename from ql/test/library-tests/modules/methods.ql
rename to ruby/ql/test/library-tests/modules/methods.ql
diff --git a/ql/test/library-tests/modules/modules.expected b/ruby/ql/test/library-tests/modules/modules.expected
similarity index 100%
rename from ql/test/library-tests/modules/modules.expected
rename to ruby/ql/test/library-tests/modules/modules.expected
diff --git a/ql/test/library-tests/modules/modules.ql b/ruby/ql/test/library-tests/modules/modules.ql
similarity index 100%
rename from ql/test/library-tests/modules/modules.ql
rename to ruby/ql/test/library-tests/modules/modules.ql
diff --git a/ql/test/library-tests/modules/modules.rb b/ruby/ql/test/library-tests/modules/modules.rb
similarity index 100%
rename from ql/test/library-tests/modules/modules.rb
rename to ruby/ql/test/library-tests/modules/modules.rb
diff --git a/ql/test/library-tests/modules/private.rb b/ruby/ql/test/library-tests/modules/private.rb
similarity index 100%
rename from ql/test/library-tests/modules/private.rb
rename to ruby/ql/test/library-tests/modules/private.rb
diff --git a/ql/test/library-tests/modules/superclasses.expected b/ruby/ql/test/library-tests/modules/superclasses.expected
similarity index 100%
rename from ql/test/library-tests/modules/superclasses.expected
rename to ruby/ql/test/library-tests/modules/superclasses.expected
diff --git a/ql/test/library-tests/modules/superclasses.ql b/ruby/ql/test/library-tests/modules/superclasses.ql
similarity index 100%
rename from ql/test/library-tests/modules/superclasses.ql
rename to ruby/ql/test/library-tests/modules/superclasses.ql
diff --git a/ql/test/library-tests/regexp/parse.expected b/ruby/ql/test/library-tests/regexp/parse.expected
similarity index 100%
rename from ql/test/library-tests/regexp/parse.expected
rename to ruby/ql/test/library-tests/regexp/parse.expected
diff --git a/ql/test/library-tests/regexp/parse.ql b/ruby/ql/test/library-tests/regexp/parse.ql
similarity index 100%
rename from ql/test/library-tests/regexp/parse.ql
rename to ruby/ql/test/library-tests/regexp/parse.ql
diff --git a/ql/test/library-tests/regexp/regexp.rb b/ruby/ql/test/library-tests/regexp/regexp.rb
similarity index 100%
rename from ql/test/library-tests/regexp/regexp.rb
rename to ruby/ql/test/library-tests/regexp/regexp.rb
diff --git a/ql/test/library-tests/variables/class_variables.rb b/ruby/ql/test/library-tests/variables/class_variables.rb
similarity index 100%
rename from ql/test/library-tests/variables/class_variables.rb
rename to ruby/ql/test/library-tests/variables/class_variables.rb
diff --git a/ql/test/library-tests/variables/instance_variables.rb b/ruby/ql/test/library-tests/variables/instance_variables.rb
similarity index 100%
rename from ql/test/library-tests/variables/instance_variables.rb
rename to ruby/ql/test/library-tests/variables/instance_variables.rb
diff --git a/ql/test/library-tests/variables/nested_scopes.rb b/ruby/ql/test/library-tests/variables/nested_scopes.rb
similarity index 100%
rename from ql/test/library-tests/variables/nested_scopes.rb
rename to ruby/ql/test/library-tests/variables/nested_scopes.rb
diff --git a/ql/test/library-tests/variables/parameter.expected b/ruby/ql/test/library-tests/variables/parameter.expected
similarity index 100%
rename from ql/test/library-tests/variables/parameter.expected
rename to ruby/ql/test/library-tests/variables/parameter.expected
diff --git a/ql/test/library-tests/variables/parameter.ql b/ruby/ql/test/library-tests/variables/parameter.ql
similarity index 100%
rename from ql/test/library-tests/variables/parameter.ql
rename to ruby/ql/test/library-tests/variables/parameter.ql
diff --git a/ql/test/library-tests/variables/parameters.rb b/ruby/ql/test/library-tests/variables/parameters.rb
similarity index 100%
rename from ql/test/library-tests/variables/parameters.rb
rename to ruby/ql/test/library-tests/variables/parameters.rb
diff --git a/ql/test/library-tests/variables/scopes.rb b/ruby/ql/test/library-tests/variables/scopes.rb
similarity index 100%
rename from ql/test/library-tests/variables/scopes.rb
rename to ruby/ql/test/library-tests/variables/scopes.rb
diff --git a/ql/test/library-tests/variables/ssa.expected b/ruby/ql/test/library-tests/variables/ssa.expected
similarity index 100%
rename from ql/test/library-tests/variables/ssa.expected
rename to ruby/ql/test/library-tests/variables/ssa.expected
diff --git a/ql/test/library-tests/variables/ssa.ql b/ruby/ql/test/library-tests/variables/ssa.ql
similarity index 100%
rename from ql/test/library-tests/variables/ssa.ql
rename to ruby/ql/test/library-tests/variables/ssa.ql
diff --git a/ql/test/library-tests/variables/ssa.rb b/ruby/ql/test/library-tests/variables/ssa.rb
similarity index 100%
rename from ql/test/library-tests/variables/ssa.rb
rename to ruby/ql/test/library-tests/variables/ssa.rb
diff --git a/ql/test/library-tests/variables/varaccess.expected b/ruby/ql/test/library-tests/variables/varaccess.expected
similarity index 100%
rename from ql/test/library-tests/variables/varaccess.expected
rename to ruby/ql/test/library-tests/variables/varaccess.expected
diff --git a/ql/test/library-tests/variables/varaccess.ql b/ruby/ql/test/library-tests/variables/varaccess.ql
similarity index 100%
rename from ql/test/library-tests/variables/varaccess.ql
rename to ruby/ql/test/library-tests/variables/varaccess.ql
diff --git a/ql/test/library-tests/variables/variable.expected b/ruby/ql/test/library-tests/variables/variable.expected
similarity index 100%
rename from ql/test/library-tests/variables/variable.expected
rename to ruby/ql/test/library-tests/variables/variable.expected
diff --git a/ql/test/library-tests/variables/variable.ql b/ruby/ql/test/library-tests/variables/variable.ql
similarity index 100%
rename from ql/test/library-tests/variables/variable.ql
rename to ruby/ql/test/library-tests/variables/variable.ql
diff --git a/ql/test/library-tests/variables/varscopes.expected b/ruby/ql/test/library-tests/variables/varscopes.expected
similarity index 100%
rename from ql/test/library-tests/variables/varscopes.expected
rename to ruby/ql/test/library-tests/variables/varscopes.expected
diff --git a/ql/test/library-tests/variables/varscopes.ql b/ruby/ql/test/library-tests/variables/varscopes.ql
similarity index 100%
rename from ql/test/library-tests/variables/varscopes.ql
rename to ruby/ql/test/library-tests/variables/varscopes.ql
diff --git a/ql/test/qlpack.lock.yml b/ruby/ql/test/qlpack.lock.yml
similarity index 100%
rename from ql/test/qlpack.lock.yml
rename to ruby/ql/test/qlpack.lock.yml
diff --git a/ql/test/qlpack.yml b/ruby/ql/test/qlpack.yml
similarity index 100%
rename from ql/test/qlpack.yml
rename to ruby/ql/test/qlpack.yml
diff --git a/ql/test/query-tests/AlertSuppression/.gitattributes b/ruby/ql/test/query-tests/AlertSuppression/.gitattributes
similarity index 100%
rename from ql/test/query-tests/AlertSuppression/.gitattributes
rename to ruby/ql/test/query-tests/AlertSuppression/.gitattributes
diff --git a/ql/test/query-tests/AlertSuppression/AlertSuppression.expected b/ruby/ql/test/query-tests/AlertSuppression/AlertSuppression.expected
similarity index 100%
rename from ql/test/query-tests/AlertSuppression/AlertSuppression.expected
rename to ruby/ql/test/query-tests/AlertSuppression/AlertSuppression.expected
diff --git a/ql/test/query-tests/AlertSuppression/AlertSuppression.qlref b/ruby/ql/test/query-tests/AlertSuppression/AlertSuppression.qlref
similarity index 100%
rename from ql/test/query-tests/AlertSuppression/AlertSuppression.qlref
rename to ruby/ql/test/query-tests/AlertSuppression/AlertSuppression.qlref
diff --git a/ql/test/query-tests/AlertSuppression/Test.rb b/ruby/ql/test/query-tests/AlertSuppression/Test.rb
similarity index 100%
rename from ql/test/query-tests/AlertSuppression/Test.rb
rename to ruby/ql/test/query-tests/AlertSuppression/Test.rb
diff --git a/ql/test/query-tests/AlertSuppression/TestWindows.rb b/ruby/ql/test/query-tests/AlertSuppression/TestWindows.rb
similarity index 100%
rename from ql/test/query-tests/AlertSuppression/TestWindows.rb
rename to ruby/ql/test/query-tests/AlertSuppression/TestWindows.rb
diff --git a/ql/test/query-tests/analysis/Definitions.expected b/ruby/ql/test/query-tests/analysis/Definitions.expected
similarity index 100%
rename from ql/test/query-tests/analysis/Definitions.expected
rename to ruby/ql/test/query-tests/analysis/Definitions.expected
diff --git a/ql/test/query-tests/analysis/Definitions.qlref b/ruby/ql/test/query-tests/analysis/Definitions.qlref
similarity index 100%
rename from ql/test/query-tests/analysis/Definitions.qlref
rename to ruby/ql/test/query-tests/analysis/Definitions.qlref
diff --git a/ql/test/query-tests/analysis/Definitions.rb b/ruby/ql/test/query-tests/analysis/Definitions.rb
similarity index 100%
rename from ql/test/query-tests/analysis/Definitions.rb
rename to ruby/ql/test/query-tests/analysis/Definitions.rb
diff --git a/ql/test/query-tests/diagnostics/ExtractionErrors.expected b/ruby/ql/test/query-tests/diagnostics/ExtractionErrors.expected
similarity index 100%
rename from ql/test/query-tests/diagnostics/ExtractionErrors.expected
rename to ruby/ql/test/query-tests/diagnostics/ExtractionErrors.expected
diff --git a/ql/test/query-tests/diagnostics/ExtractionErrors.qlref b/ruby/ql/test/query-tests/diagnostics/ExtractionErrors.qlref
similarity index 100%
rename from ql/test/query-tests/diagnostics/ExtractionErrors.qlref
rename to ruby/ql/test/query-tests/diagnostics/ExtractionErrors.qlref
diff --git a/ql/test/query-tests/diagnostics/NumberOfFilesExtractedWithErrors.expected b/ruby/ql/test/query-tests/diagnostics/NumberOfFilesExtractedWithErrors.expected
similarity index 100%
rename from ql/test/query-tests/diagnostics/NumberOfFilesExtractedWithErrors.expected
rename to ruby/ql/test/query-tests/diagnostics/NumberOfFilesExtractedWithErrors.expected
diff --git a/ql/test/query-tests/diagnostics/NumberOfFilesExtractedWithErrors.qlref b/ruby/ql/test/query-tests/diagnostics/NumberOfFilesExtractedWithErrors.qlref
similarity index 100%
rename from ql/test/query-tests/diagnostics/NumberOfFilesExtractedWithErrors.qlref
rename to ruby/ql/test/query-tests/diagnostics/NumberOfFilesExtractedWithErrors.qlref
diff --git a/ql/test/query-tests/diagnostics/NumberOfSuccessfullyExtractedFiles.expected b/ruby/ql/test/query-tests/diagnostics/NumberOfSuccessfullyExtractedFiles.expected
similarity index 100%
rename from ql/test/query-tests/diagnostics/NumberOfSuccessfullyExtractedFiles.expected
rename to ruby/ql/test/query-tests/diagnostics/NumberOfSuccessfullyExtractedFiles.expected
diff --git a/ql/test/query-tests/diagnostics/NumberOfSuccessfullyExtractedFiles.qlref b/ruby/ql/test/query-tests/diagnostics/NumberOfSuccessfullyExtractedFiles.qlref
similarity index 100%
rename from ql/test/query-tests/diagnostics/NumberOfSuccessfullyExtractedFiles.qlref
rename to ruby/ql/test/query-tests/diagnostics/NumberOfSuccessfullyExtractedFiles.qlref
diff --git a/ql/test/query-tests/diagnostics/SuccessfullyExtractedFiles.expected b/ruby/ql/test/query-tests/diagnostics/SuccessfullyExtractedFiles.expected
similarity index 100%
rename from ql/test/query-tests/diagnostics/SuccessfullyExtractedFiles.expected
rename to ruby/ql/test/query-tests/diagnostics/SuccessfullyExtractedFiles.expected
diff --git a/ql/test/query-tests/diagnostics/SuccessfullyExtractedFiles.qlref b/ruby/ql/test/query-tests/diagnostics/SuccessfullyExtractedFiles.qlref
similarity index 100%
rename from ql/test/query-tests/diagnostics/SuccessfullyExtractedFiles.qlref
rename to ruby/ql/test/query-tests/diagnostics/SuccessfullyExtractedFiles.qlref
diff --git a/ql/test/query-tests/diagnostics/src/bar.erb b/ruby/ql/test/query-tests/diagnostics/src/bar.erb
similarity index 100%
rename from ql/test/query-tests/diagnostics/src/bar.erb
rename to ruby/ql/test/query-tests/diagnostics/src/bar.erb
diff --git a/ql/test/query-tests/diagnostics/src/foo.rb b/ruby/ql/test/query-tests/diagnostics/src/foo.rb
similarity index 100%
rename from ql/test/query-tests/diagnostics/src/foo.rb
rename to ruby/ql/test/query-tests/diagnostics/src/foo.rb
diff --git a/ql/test/query-tests/diagnostics/src/not_ruby.rb b/ruby/ql/test/query-tests/diagnostics/src/not_ruby.rb
similarity index 100%
rename from ql/test/query-tests/diagnostics/src/not_ruby.rb
rename to ruby/ql/test/query-tests/diagnostics/src/not_ruby.rb
diff --git a/ql/test/query-tests/diagnostics/src/unsupported_feature.rb b/ruby/ql/test/query-tests/diagnostics/src/unsupported_feature.rb
similarity index 100%
rename from ql/test/query-tests/diagnostics/src/unsupported_feature.rb
rename to ruby/ql/test/query-tests/diagnostics/src/unsupported_feature.rb
diff --git a/ql/test/query-tests/diagnostics/src/vendor/cache/lib.rb b/ruby/ql/test/query-tests/diagnostics/src/vendor/cache/lib.rb
similarity index 100%
rename from ql/test/query-tests/diagnostics/src/vendor/cache/lib.rb
rename to ruby/ql/test/query-tests/diagnostics/src/vendor/cache/lib.rb
diff --git a/ql/test/query-tests/metrics/FLines/Empty.rb b/ruby/ql/test/query-tests/metrics/FLines/Empty.rb
similarity index 100%
rename from ql/test/query-tests/metrics/FLines/Empty.rb
rename to ruby/ql/test/query-tests/metrics/FLines/Empty.rb
diff --git a/ql/test/query-tests/metrics/FLines/FLines.expected b/ruby/ql/test/query-tests/metrics/FLines/FLines.expected
similarity index 100%
rename from ql/test/query-tests/metrics/FLines/FLines.expected
rename to ruby/ql/test/query-tests/metrics/FLines/FLines.expected
diff --git a/ql/test/query-tests/metrics/FLines/FLines.qlref b/ruby/ql/test/query-tests/metrics/FLines/FLines.qlref
similarity index 100%
rename from ql/test/query-tests/metrics/FLines/FLines.qlref
rename to ruby/ql/test/query-tests/metrics/FLines/FLines.qlref
diff --git a/ql/test/query-tests/metrics/FLines/FLines.rb b/ruby/ql/test/query-tests/metrics/FLines/FLines.rb
similarity index 100%
rename from ql/test/query-tests/metrics/FLines/FLines.rb
rename to ruby/ql/test/query-tests/metrics/FLines/FLines.rb
diff --git a/ql/test/query-tests/metrics/FLines/FLinesOfCode.expected b/ruby/ql/test/query-tests/metrics/FLines/FLinesOfCode.expected
similarity index 100%
rename from ql/test/query-tests/metrics/FLines/FLinesOfCode.expected
rename to ruby/ql/test/query-tests/metrics/FLines/FLinesOfCode.expected
diff --git a/ql/test/query-tests/metrics/FLines/FLinesOfCode.qlref b/ruby/ql/test/query-tests/metrics/FLines/FLinesOfCode.qlref
similarity index 100%
rename from ql/test/query-tests/metrics/FLines/FLinesOfCode.qlref
rename to ruby/ql/test/query-tests/metrics/FLines/FLinesOfCode.qlref
diff --git a/ql/test/query-tests/metrics/FLines/FLinesOfComments.expected b/ruby/ql/test/query-tests/metrics/FLines/FLinesOfComments.expected
similarity index 100%
rename from ql/test/query-tests/metrics/FLines/FLinesOfComments.expected
rename to ruby/ql/test/query-tests/metrics/FLines/FLinesOfComments.expected
diff --git a/ql/test/query-tests/metrics/FLines/FLinesOfComments.qlref b/ruby/ql/test/query-tests/metrics/FLines/FLinesOfComments.qlref
similarity index 100%
rename from ql/test/query-tests/metrics/FLines/FLinesOfComments.qlref
rename to ruby/ql/test/query-tests/metrics/FLines/FLinesOfComments.qlref
diff --git a/ql/test/query-tests/performance/UseDetect/UseDetect.expected b/ruby/ql/test/query-tests/performance/UseDetect/UseDetect.expected
similarity index 100%
rename from ql/test/query-tests/performance/UseDetect/UseDetect.expected
rename to ruby/ql/test/query-tests/performance/UseDetect/UseDetect.expected
diff --git a/ql/test/query-tests/performance/UseDetect/UseDetect.qlref b/ruby/ql/test/query-tests/performance/UseDetect/UseDetect.qlref
similarity index 100%
rename from ql/test/query-tests/performance/UseDetect/UseDetect.qlref
rename to ruby/ql/test/query-tests/performance/UseDetect/UseDetect.qlref
diff --git a/ql/test/query-tests/performance/UseDetect/UseDetect.rb b/ruby/ql/test/query-tests/performance/UseDetect/UseDetect.rb
similarity index 100%
rename from ql/test/query-tests/performance/UseDetect/UseDetect.rb
rename to ruby/ql/test/query-tests/performance/UseDetect/UseDetect.rb
diff --git a/ql/test/query-tests/security/cwe-078/CommandInjection.expected b/ruby/ql/test/query-tests/security/cwe-078/CommandInjection.expected
similarity index 100%
rename from ql/test/query-tests/security/cwe-078/CommandInjection.expected
rename to ruby/ql/test/query-tests/security/cwe-078/CommandInjection.expected
diff --git a/ql/test/query-tests/security/cwe-078/CommandInjection.qlref b/ruby/ql/test/query-tests/security/cwe-078/CommandInjection.qlref
similarity index 100%
rename from ql/test/query-tests/security/cwe-078/CommandInjection.qlref
rename to ruby/ql/test/query-tests/security/cwe-078/CommandInjection.qlref
diff --git a/ql/test/query-tests/security/cwe-078/CommandInjection.rb b/ruby/ql/test/query-tests/security/cwe-078/CommandInjection.rb
similarity index 100%
rename from ql/test/query-tests/security/cwe-078/CommandInjection.rb
rename to ruby/ql/test/query-tests/security/cwe-078/CommandInjection.rb
diff --git a/ql/test/query-tests/security/cwe-079/ReflectedXSS.expected b/ruby/ql/test/query-tests/security/cwe-079/ReflectedXSS.expected
similarity index 100%
rename from ql/test/query-tests/security/cwe-079/ReflectedXSS.expected
rename to ruby/ql/test/query-tests/security/cwe-079/ReflectedXSS.expected
diff --git a/ql/test/query-tests/security/cwe-079/ReflectedXSS.qlref b/ruby/ql/test/query-tests/security/cwe-079/ReflectedXSS.qlref
similarity index 100%
rename from ql/test/query-tests/security/cwe-079/ReflectedXSS.qlref
rename to ruby/ql/test/query-tests/security/cwe-079/ReflectedXSS.qlref
diff --git a/ql/test/query-tests/security/cwe-079/app/controllers/foo/bars_controller.rb b/ruby/ql/test/query-tests/security/cwe-079/app/controllers/foo/bars_controller.rb
similarity index 100%
rename from ql/test/query-tests/security/cwe-079/app/controllers/foo/bars_controller.rb
rename to ruby/ql/test/query-tests/security/cwe-079/app/controllers/foo/bars_controller.rb
diff --git a/ql/test/query-tests/security/cwe-079/app/views/foo/bars/_widget.html.erb b/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/bars/_widget.html.erb
similarity index 100%
rename from ql/test/query-tests/security/cwe-079/app/views/foo/bars/_widget.html.erb
rename to ruby/ql/test/query-tests/security/cwe-079/app/views/foo/bars/_widget.html.erb
diff --git a/ql/test/query-tests/security/cwe-079/app/views/foo/bars/show.html.erb b/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/bars/show.html.erb
similarity index 100%
rename from ql/test/query-tests/security/cwe-079/app/views/foo/bars/show.html.erb
rename to ruby/ql/test/query-tests/security/cwe-079/app/views/foo/bars/show.html.erb
diff --git a/ql/test/query-tests/security/cwe-089/ActiveRecordInjection.rb b/ruby/ql/test/query-tests/security/cwe-089/ActiveRecordInjection.rb
similarity index 100%
rename from ql/test/query-tests/security/cwe-089/ActiveRecordInjection.rb
rename to ruby/ql/test/query-tests/security/cwe-089/ActiveRecordInjection.rb
diff --git a/ql/test/query-tests/security/cwe-089/SqlInjection.expected b/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected
similarity index 100%
rename from ql/test/query-tests/security/cwe-089/SqlInjection.expected
rename to ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected
diff --git a/ql/test/query-tests/security/cwe-089/SqlInjection.qlref b/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.qlref
similarity index 100%
rename from ql/test/query-tests/security/cwe-089/SqlInjection.qlref
rename to ruby/ql/test/query-tests/security/cwe-089/SqlInjection.qlref
diff --git a/ql/test/query-tests/security/cwe-094/CodeInjection.expected b/ruby/ql/test/query-tests/security/cwe-094/CodeInjection.expected
similarity index 100%
rename from ql/test/query-tests/security/cwe-094/CodeInjection.expected
rename to ruby/ql/test/query-tests/security/cwe-094/CodeInjection.expected
diff --git a/ql/test/query-tests/security/cwe-094/CodeInjection.qlref b/ruby/ql/test/query-tests/security/cwe-094/CodeInjection.qlref
similarity index 100%
rename from ql/test/query-tests/security/cwe-094/CodeInjection.qlref
rename to ruby/ql/test/query-tests/security/cwe-094/CodeInjection.qlref
diff --git a/ql/test/query-tests/security/cwe-094/CodeInjection.rb b/ruby/ql/test/query-tests/security/cwe-094/CodeInjection.rb
similarity index 100%
rename from ql/test/query-tests/security/cwe-094/CodeInjection.rb
rename to ruby/ql/test/query-tests/security/cwe-094/CodeInjection.rb
diff --git a/ql/test/query-tests/security/cwe-1333-exponential-redos/ANodeBlog-LICENSE b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/ANodeBlog-LICENSE
similarity index 100%
rename from ql/test/query-tests/security/cwe-1333-exponential-redos/ANodeBlog-LICENSE
rename to ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/ANodeBlog-LICENSE
diff --git a/ql/test/query-tests/security/cwe-1333-exponential-redos/CodeMirror-LICENSE b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/CodeMirror-LICENSE
similarity index 100%
rename from ql/test/query-tests/security/cwe-1333-exponential-redos/CodeMirror-LICENSE
rename to ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/CodeMirror-LICENSE
diff --git a/ql/test/query-tests/security/cwe-1333-exponential-redos/Prism-LICENSE b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/Prism-LICENSE
similarity index 100%
rename from ql/test/query-tests/security/cwe-1333-exponential-redos/Prism-LICENSE
rename to ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/Prism-LICENSE
diff --git a/ql/test/query-tests/security/cwe-1333-exponential-redos/Prototype.js-LICENSE b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/Prototype.js-LICENSE
similarity index 100%
rename from ql/test/query-tests/security/cwe-1333-exponential-redos/Prototype.js-LICENSE
rename to ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/Prototype.js-LICENSE
diff --git a/ql/test/query-tests/security/cwe-1333-exponential-redos/ReDoS.expected b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/ReDoS.expected
similarity index 100%
rename from ql/test/query-tests/security/cwe-1333-exponential-redos/ReDoS.expected
rename to ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/ReDoS.expected
diff --git a/ql/test/query-tests/security/cwe-1333-exponential-redos/ReDoS.qlref b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/ReDoS.qlref
similarity index 100%
rename from ql/test/query-tests/security/cwe-1333-exponential-redos/ReDoS.qlref
rename to ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/ReDoS.qlref
diff --git a/ql/test/query-tests/security/cwe-1333-exponential-redos/brace-expansion-LICENSE b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/brace-expansion-LICENSE
similarity index 100%
rename from ql/test/query-tests/security/cwe-1333-exponential-redos/brace-expansion-LICENSE
rename to ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/brace-expansion-LICENSE
diff --git a/ql/test/query-tests/security/cwe-1333-exponential-redos/jest-LICENSE b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/jest-LICENSE
similarity index 100%
rename from ql/test/query-tests/security/cwe-1333-exponential-redos/jest-LICENSE
rename to ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/jest-LICENSE
diff --git a/ql/test/query-tests/security/cwe-1333-exponential-redos/knockout-LICENSE b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/knockout-LICENSE
similarity index 100%
rename from ql/test/query-tests/security/cwe-1333-exponential-redos/knockout-LICENSE
rename to ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/knockout-LICENSE
diff --git a/ql/test/query-tests/security/cwe-1333-exponential-redos/marked-LICENSE b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/marked-LICENSE
similarity index 100%
rename from ql/test/query-tests/security/cwe-1333-exponential-redos/marked-LICENSE
rename to ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/marked-LICENSE
diff --git a/ql/test/query-tests/security/cwe-1333-exponential-redos/tst.rb b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/tst.rb
similarity index 100%
rename from ql/test/query-tests/security/cwe-1333-exponential-redos/tst.rb
rename to ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/tst.rb
diff --git a/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.expected b/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.expected
similarity index 100%
rename from ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.expected
rename to ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.expected
diff --git a/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.qlref b/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.qlref
similarity index 100%
rename from ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.qlref
rename to ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.qlref
diff --git a/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.rb b/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.rb
similarity index 100%
rename from ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.rb
rename to ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.rb
diff --git a/ql/test/query-tests/security/cwe-502/UnsafeDeserialization.expected b/ruby/ql/test/query-tests/security/cwe-502/UnsafeDeserialization.expected
similarity index 100%
rename from ql/test/query-tests/security/cwe-502/UnsafeDeserialization.expected
rename to ruby/ql/test/query-tests/security/cwe-502/UnsafeDeserialization.expected
diff --git a/ql/test/query-tests/security/cwe-502/UnsafeDeserialization.qlref b/ruby/ql/test/query-tests/security/cwe-502/UnsafeDeserialization.qlref
similarity index 100%
rename from ql/test/query-tests/security/cwe-502/UnsafeDeserialization.qlref
rename to ruby/ql/test/query-tests/security/cwe-502/UnsafeDeserialization.qlref
diff --git a/ql/test/query-tests/security/cwe-502/UnsafeDeserialization.rb b/ruby/ql/test/query-tests/security/cwe-502/UnsafeDeserialization.rb
similarity index 100%
rename from ql/test/query-tests/security/cwe-502/UnsafeDeserialization.rb
rename to ruby/ql/test/query-tests/security/cwe-502/UnsafeDeserialization.rb
diff --git a/ql/test/query-tests/security/cwe-601/UrlRedirect.expected b/ruby/ql/test/query-tests/security/cwe-601/UrlRedirect.expected
similarity index 100%
rename from ql/test/query-tests/security/cwe-601/UrlRedirect.expected
rename to ruby/ql/test/query-tests/security/cwe-601/UrlRedirect.expected
diff --git a/ql/test/query-tests/security/cwe-601/UrlRedirect.qlref b/ruby/ql/test/query-tests/security/cwe-601/UrlRedirect.qlref
similarity index 100%
rename from ql/test/query-tests/security/cwe-601/UrlRedirect.qlref
rename to ruby/ql/test/query-tests/security/cwe-601/UrlRedirect.qlref
diff --git a/ql/test/query-tests/security/cwe-601/UrlRedirect.rb b/ruby/ql/test/query-tests/security/cwe-601/UrlRedirect.rb
similarity index 100%
rename from ql/test/query-tests/security/cwe-601/UrlRedirect.rb
rename to ruby/ql/test/query-tests/security/cwe-601/UrlRedirect.rb
diff --git a/ql/test/query-tests/security/cwe-732/FilePermissions.rb b/ruby/ql/test/query-tests/security/cwe-732/FilePermissions.rb
similarity index 100%
rename from ql/test/query-tests/security/cwe-732/FilePermissions.rb
rename to ruby/ql/test/query-tests/security/cwe-732/FilePermissions.rb
diff --git a/ql/test/query-tests/security/cwe-732/WeakFilePermissions.expected b/ruby/ql/test/query-tests/security/cwe-732/WeakFilePermissions.expected
similarity index 100%
rename from ql/test/query-tests/security/cwe-732/WeakFilePermissions.expected
rename to ruby/ql/test/query-tests/security/cwe-732/WeakFilePermissions.expected
diff --git a/ql/test/query-tests/security/cwe-732/WeakFilePermissions.qlref b/ruby/ql/test/query-tests/security/cwe-732/WeakFilePermissions.qlref
similarity index 100%
rename from ql/test/query-tests/security/cwe-732/WeakFilePermissions.qlref
rename to ruby/ql/test/query-tests/security/cwe-732/WeakFilePermissions.qlref
diff --git a/ql/test/query-tests/security/cwe-798/HardcodedCredentials.expected b/ruby/ql/test/query-tests/security/cwe-798/HardcodedCredentials.expected
similarity index 100%
rename from ql/test/query-tests/security/cwe-798/HardcodedCredentials.expected
rename to ruby/ql/test/query-tests/security/cwe-798/HardcodedCredentials.expected
diff --git a/ql/test/query-tests/security/cwe-798/HardcodedCredentials.qlref b/ruby/ql/test/query-tests/security/cwe-798/HardcodedCredentials.qlref
similarity index 100%
rename from ql/test/query-tests/security/cwe-798/HardcodedCredentials.qlref
rename to ruby/ql/test/query-tests/security/cwe-798/HardcodedCredentials.qlref
diff --git a/ql/test/query-tests/security/cwe-798/HardcodedCredentials.rb b/ruby/ql/test/query-tests/security/cwe-798/HardcodedCredentials.rb
similarity index 100%
rename from ql/test/query-tests/security/cwe-798/HardcodedCredentials.rb
rename to ruby/ql/test/query-tests/security/cwe-798/HardcodedCredentials.rb
diff --git a/ql/test/query-tests/summary/LinesOfCode.expected b/ruby/ql/test/query-tests/summary/LinesOfCode.expected
similarity index 100%
rename from ql/test/query-tests/summary/LinesOfCode.expected
rename to ruby/ql/test/query-tests/summary/LinesOfCode.expected
diff --git a/ql/test/query-tests/summary/LinesOfCode.qlref b/ruby/ql/test/query-tests/summary/LinesOfCode.qlref
similarity index 100%
rename from ql/test/query-tests/summary/LinesOfCode.qlref
rename to ruby/ql/test/query-tests/summary/LinesOfCode.qlref
diff --git a/ql/test/query-tests/summary/LinesOfUserCode.expected b/ruby/ql/test/query-tests/summary/LinesOfUserCode.expected
similarity index 100%
rename from ql/test/query-tests/summary/LinesOfUserCode.expected
rename to ruby/ql/test/query-tests/summary/LinesOfUserCode.expected
diff --git a/ql/test/query-tests/summary/LinesOfUserCode.qlref b/ruby/ql/test/query-tests/summary/LinesOfUserCode.qlref
similarity index 100%
rename from ql/test/query-tests/summary/LinesOfUserCode.qlref
rename to ruby/ql/test/query-tests/summary/LinesOfUserCode.qlref
diff --git a/ql/test/query-tests/summary/src/foo.rb b/ruby/ql/test/query-tests/summary/src/foo.rb
similarity index 100%
rename from ql/test/query-tests/summary/src/foo.rb
rename to ruby/ql/test/query-tests/summary/src/foo.rb
diff --git a/ql/test/query-tests/summary/src/vendor/cache/lib.rb b/ruby/ql/test/query-tests/summary/src/vendor/cache/lib.rb
similarity index 100%
rename from ql/test/query-tests/summary/src/vendor/cache/lib.rb
rename to ruby/ql/test/query-tests/summary/src/vendor/cache/lib.rb
diff --git a/scripts/create-extractor-pack.ps1 b/ruby/scripts/create-extractor-pack.ps1
similarity index 100%
rename from scripts/create-extractor-pack.ps1
rename to ruby/scripts/create-extractor-pack.ps1
diff --git a/scripts/create-extractor-pack.sh b/ruby/scripts/create-extractor-pack.sh
similarity index 100%
rename from scripts/create-extractor-pack.sh
rename to ruby/scripts/create-extractor-pack.sh
diff --git a/scripts/identical-files.json b/ruby/scripts/identical-files.json
similarity index 100%
rename from scripts/identical-files.json
rename to ruby/scripts/identical-files.json
diff --git a/scripts/merge_stats.py b/ruby/scripts/merge_stats.py
similarity index 100%
rename from scripts/merge_stats.py
rename to ruby/scripts/merge_stats.py
diff --git a/scripts/prepare-db-upgrade.sh b/ruby/scripts/prepare-db-upgrade.sh
similarity index 100%
rename from scripts/prepare-db-upgrade.sh
rename to ruby/scripts/prepare-db-upgrade.sh
diff --git a/scripts/sync-identical-files.py b/ruby/scripts/sync-identical-files.py
similarity index 100%
rename from scripts/sync-identical-files.py
rename to ruby/scripts/sync-identical-files.py
diff --git a/tools/autobuild.cmd b/ruby/tools/autobuild.cmd
similarity index 100%
rename from tools/autobuild.cmd
rename to ruby/tools/autobuild.cmd
diff --git a/tools/autobuild.sh b/ruby/tools/autobuild.sh
similarity index 100%
rename from tools/autobuild.sh
rename to ruby/tools/autobuild.sh
diff --git a/tools/index-files.cmd b/ruby/tools/index-files.cmd
similarity index 100%
rename from tools/index-files.cmd
rename to ruby/tools/index-files.cmd
diff --git a/tools/index-files.sh b/ruby/tools/index-files.sh
similarity index 100%
rename from tools/index-files.sh
rename to ruby/tools/index-files.sh
diff --git a/tools/qltest.cmd b/ruby/tools/qltest.cmd
similarity index 100%
rename from tools/qltest.cmd
rename to ruby/tools/qltest.cmd
diff --git a/tools/qltest.sh b/ruby/tools/qltest.sh
similarity index 100%
rename from tools/qltest.sh
rename to ruby/tools/qltest.sh
From d2ea7325399377fe8c22f4c5e26db54a16c2cd2a Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Thu, 14 Oct 2021 21:24:00 +0200
Subject: [PATCH 104/471] Remove CodeSpaces configuration
---
.devcontainer/Dockerfile | 15 -------------
.devcontainer/devcontainer.json | 39 ---------------------------------
.devcontainer/post_attach.sh | 37 -------------------------------
.devcontainer/post_create.sh | 4 ----
4 files changed, 95 deletions(-)
delete mode 100644 .devcontainer/Dockerfile
delete mode 100644 .devcontainer/devcontainer.json
delete mode 100755 .devcontainer/post_attach.sh
delete mode 100755 .devcontainer/post_create.sh
diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
deleted file mode 100644
index a5776e19806..00000000000
--- a/.devcontainer/Dockerfile
+++ /dev/null
@@ -1,15 +0,0 @@
-# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.162.0/containers/rust/.devcontainer/base.Dockerfile
-
-FROM mcr.microsoft.com/vscode/devcontainers/rust:0-1
-
-RUN apt-key --keyring /usr/share/keyrings/githubcli-archive-keyring.gpg adv \
- --keyserver keyserver.ubuntu.com --recv-key C99B11DEB97541F0 && \
- echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages $(lsb_release -cs) main" \
- | tee /etc/apt/sources.list.d/github-cli2.list > /dev/null
-
-
-RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
- && apt-get -y install --no-install-recommends gh
-
-COPY post_create.sh /bin/post_create.sh
-COPY post_attach.sh /bin/post_attach.sh
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
deleted file mode 100644
index b7824f45cfe..00000000000
--- a/.devcontainer/devcontainer.json
+++ /dev/null
@@ -1,39 +0,0 @@
-// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
-// https://github.com/microsoft/vscode-dev-containers/tree/v0.162.0/containers/rust
-{
- "name": "Rust",
- "build": {
- "dockerfile": "Dockerfile"
- },
- "runArgs": [
- "--cap-add=SYS_PTRACE",
- "--security-opt",
- "seccomp=unconfined"
- ],
- // Set *default* container specific settings.json values on container create.
- "settings": {
- "terminal.integrated.shell.linux": "/bin/bash",
- "lldb.executable": "/usr/bin/lldb",
- // VS Code don't watch files under ./target
- "files.watcherExclude": {
- "**/target/**": true
- }
- },
- // Add the IDs of extensions you want installed when the container is created.
- "extensions": [
- "rust-lang.rust",
- "bungcip.better-toml",
- "vadimcn.vscode-lldb",
- "mutantdino.resourcemonitor",
- "ms-azuretools.vscode-docker",
- "github.vscode-codeql"
- ],
- // Use 'forwardPorts' to make a list of ports inside the container available locally.
- // "forwardPorts": [],
- // Use 'postCreateCommand' to run commands after the container is created.
- // "postCreateCommand": "rustc --version",
- // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
- "remoteUser": "vscode",
- "postCreateCommand": [ "/bin/post_create.sh" ],
- "postAttachCommand": [ "flock", "-E", "0", "-n", "/var/lock/post_attach.lock", "/bin/post_attach.sh" ]
-}
\ No newline at end of file
diff --git a/.devcontainer/post_attach.sh b/.devcontainer/post_attach.sh
deleted file mode 100755
index 8b923e31ad0..00000000000
--- a/.devcontainer/post_attach.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#! /bin/bash
-set -xe
-
-echo "Check installed CodeQL version"
-CURRENT_CODEQL_BIN=$(readlink -e /usr/local/bin/codeql || echo "")
-LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | grep -v beta | sort --version-sort | tail -1)
-
-BASE_DIR=/home/vscode/codeql-binaries
-mkdir -p "${BASE_DIR}"
-LATEST_CODEQL_DIR="${BASE_DIR}/codeql-${LATEST}"
-LATEST_CODEQL_BIN="${LATEST_CODEQL_DIR}/codeql/codeql"
-
-if [ "${CURRENT_CODEQL_BIN}" != "${LATEST_CODEQL_BIN}" ]; then
- echo "Installing CodeQL ${LATEST}"
- TMPDIR=$(mktemp -d -p "$(dirname ${LATEST_CODEQL_DIR})")
- gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip -D "${TMPDIR}" "$LATEST"
- unzip -oq "${TMPDIR}/codeql-linux64.zip" -d "${TMPDIR}"
- rm -f "${TMPDIR}/codeql-linux64.zip"
- mv "${TMPDIR}" "${LATEST_CODEQL_DIR}"
- test -x "${LATEST_CODEQL_BIN}" && sudo ln -sf "${LATEST_CODEQL_BIN}" /usr/local/bin/codeql
- if [[ "${CURRENT_CODEQL_BIN}" =~ .*/codeql/codeql ]]; then
- rm -rf "$(dirname $(dirname ${CURRENT_CODEQL_BIN}))"
- fi
-fi
-
-echo "Build the Ruby extractor"
-
-# clone the git dependencies using "git clone" because cargo's builtin git support is rather slow
-REPO_DIR="${CARGO_HOME:-/home/vscode/.cargo}/git/db"
-REPO_DIR_ERB="${REPO_DIR}/tree-sitter-embedded-template-4c796e3340c233b6"
-REPO_DIR_RUBY="${REPO_DIR}/tree-sitter-ruby-666a40ce046f8e7a"
-
-mkdir -p "${REPO_DIR}"
-test -e "${REPO_DIR_ERB}" || git clone -q --bare https://github.com/tree-sitter/tree-sitter-embedded-template "${REPO_DIR_ERB}"
-test -e "${REPO_DIR_RUBY}" || git clone -q --bare https://github.com/tree-sitter/tree-sitter-ruby.git "${REPO_DIR_RUBY}"
-
-scripts/create-extractor-pack.sh
diff --git a/.devcontainer/post_create.sh b/.devcontainer/post_create.sh
deleted file mode 100755
index a1af653024b..00000000000
--- a/.devcontainer/post_create.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#! /bin/bash
-
-mkdir -p /home/vscode/.config/codeql
-echo '--search-path /workspaces/codeql-ruby' >> /home/vscode/.config/codeql/config
From 068beeff56d60bfd801a481e0c02ca006f47e10c Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Thu, 14 Oct 2021 13:17:32 +0200
Subject: [PATCH 105/471] Move create-extractor-pack Action
---
.github/actions/fetch-codeql/action.yml | 1 +
.github/workflows/dataset_measure.yml | 2 +-
.github/workflows/qltest.yml | 2 +-
{.github => ruby}/actions/create-extractor-pack/action.yml | 7 ++++---
4 files changed, 7 insertions(+), 5 deletions(-)
rename {.github => ruby}/actions/create-extractor-pack/action.yml (58%)
diff --git a/.github/actions/fetch-codeql/action.yml b/.github/actions/fetch-codeql/action.yml
index d6cca840241..de6d50dc12f 100644
--- a/.github/actions/fetch-codeql/action.yml
+++ b/.github/actions/fetch-codeql/action.yml
@@ -9,5 +9,6 @@ runs:
LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | grep -v beta | sort --version-sort | tail -1)
gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip "$LATEST"
unzip -q codeql-linux64.zip
+ echo "${{ github.workspace }}/codeql" >> $GITHUB_PATH
env:
GITHUB_TOKEN: ${{ github.token }}
diff --git a/.github/workflows/dataset_measure.yml b/.github/workflows/dataset_measure.yml
index 6e2f9f4b7af..7997d7f9240 100644
--- a/.github/workflows/dataset_measure.yml
+++ b/.github/workflows/dataset_measure.yml
@@ -25,7 +25,7 @@ jobs:
- uses: ./.github/actions/fetch-codeql
- - uses: ./.github/actions/create-extractor-pack
+ - uses: ./ruby/actions/create-extractor-pack
- name: Checkout ${{ matrix.repo }}
uses: actions/checkout@v2
diff --git a/.github/workflows/qltest.yml b/.github/workflows/qltest.yml
index b9ab622ae0d..1a055574b0b 100644
--- a/.github/workflows/qltest.yml
+++ b/.github/workflows/qltest.yml
@@ -15,7 +15,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/fetch-codeql
- - uses: ./.github/actions/create-extractor-pack
+ - uses: ./ruby/actions/create-extractor-pack
- name: Run QL tests
run: |
codeql/codeql pack install ql/test
diff --git a/.github/actions/create-extractor-pack/action.yml b/ruby/actions/create-extractor-pack/action.yml
similarity index 58%
rename from .github/actions/create-extractor-pack/action.yml
rename to ruby/actions/create-extractor-pack/action.yml
index b15565cffa5..75faf327c13 100644
--- a/.github/actions/create-extractor-pack/action.yml
+++ b/ruby/actions/create-extractor-pack/action.yml
@@ -8,8 +8,9 @@ runs:
path: |
~/.cargo/registry
~/.cargo/git
- target
- key: ${{ runner.os }}-qltest-cargo-${{ hashFiles('**/Cargo.lock') }}
+ ruby/target
+ key: ${{ runner.os }}-qltest-cargo-${{ hashFiles('ruby/**/Cargo.lock') }}
- name: Build Extractor
shell: bash
- run: env "PATH=$PATH:${{ github.workspace }}/codeql" scripts/create-extractor-pack.sh
+ run: scripts/create-extractor-pack.sh
+ working-directory: ruby
From 2de757335f01df709d8ca3a7b05a80870d184dd8 Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Thu, 14 Oct 2021 13:32:50 +0200
Subject: [PATCH 106/471] Update Ruby workflows
---
.../{qhelp.yml => qhelp-pr-preview.yml} | 4 +-
.github/workflows/qltest.yml | 38 ------------
.../workflows/{build.yml => ruby-build.yml} | 61 +++++++++++--------
...t_measure.yml => ruby-dataset-measure.yml} | 16 ++---
.github/workflows/ruby-qltest.yml | 50 +++++++++++++++
.../{sync_files.yml => sync-files.yml} | 2 +-
6 files changed, 95 insertions(+), 76 deletions(-)
rename .github/workflows/{qhelp.yml => qhelp-pr-preview.yml} (92%)
delete mode 100644 .github/workflows/qltest.yml
rename .github/workflows/{build.yml => ruby-build.yml} (88%)
rename .github/workflows/{dataset_measure.yml => ruby-dataset-measure.yml} (73%)
create mode 100644 .github/workflows/ruby-qltest.yml
rename .github/workflows/{sync_files.yml => sync-files.yml} (84%)
diff --git a/.github/workflows/qhelp.yml b/.github/workflows/qhelp-pr-preview.yml
similarity index 92%
rename from .github/workflows/qhelp.yml
rename to .github/workflows/qhelp-pr-preview.yml
index f4f2d2877cb..99ff16a232a 100644
--- a/.github/workflows/qhelp.yml
+++ b/.github/workflows/qhelp-pr-preview.yml
@@ -4,7 +4,7 @@ on:
pull_request:
branches: [main]
paths:
- - "**/*.qhelp"
+ - "ruby/**/*.qhelp"
jobs:
qhelp:
@@ -30,7 +30,7 @@ jobs:
for path in ${{ steps.changes.outputs.qhelp_files }} ; do
echo " ${path}
"
echo
- codeql/codeql generate query-help --format=markdown ${path}
+ codeql generate query-help --format=markdown ${path}
echo " "
done) | gh pr comment "${{ github.event.pull_request.number }}" -F -
env:
diff --git a/.github/workflows/qltest.yml b/.github/workflows/qltest.yml
deleted file mode 100644
index 1a055574b0b..00000000000
--- a/.github/workflows/qltest.yml
+++ /dev/null
@@ -1,38 +0,0 @@
-name: Run QL Tests
-
-on:
- push:
- branches: [main]
- pull_request:
- branches: [main]
-
-env:
- CARGO_TERM_COLOR: always
-
-jobs:
- qltest:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- - uses: ./.github/actions/fetch-codeql
- - uses: ./ruby/actions/create-extractor-pack
- - name: Run QL tests
- run: |
- codeql/codeql pack install ql/test
- codeql/codeql test run --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --search-path "${{ github.workspace }}" --additional-packs "${HOME}/.codeql/packages/codeql/suite-helpers/0.0.1" --consistency-queries ql/consistency-queries ql/test
- env:
- GITHUB_TOKEN: ${{ github.token }}
- - name: Check QL formatting
- run: find ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql/codeql query format --check-only
- - name: Check QL compilation
- run: |
- codeql/codeql pack install ql/src
- codeql/codeql query compile --check-only --threads=4 --warnings=error --search-path "${{ github.workspace }}" "ql/src" "ql/examples"
- env:
- GITHUB_TOKEN: ${{ github.token }}
- - name: Check DB upgrade scripts
- run: |
- echo >empty.trap
- codeql/codeql dataset import -S ql/lib/upgrades/initial/ruby.dbscheme testdb empty.trap
- codeql/codeql dataset upgrade testdb --additional-packs ql/lib/upgrades
- diff -q testdb/ruby.dbscheme ql/lib/ruby.dbscheme
diff --git a/.github/workflows/build.yml b/.github/workflows/ruby-build.yml
similarity index 88%
rename from .github/workflows/build.yml
rename to .github/workflows/ruby-build.yml
index e9fc6349e87..4361ce5a1cc 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/ruby-build.yml
@@ -1,10 +1,18 @@
-name: Build / Release
+name: "Ruby: Build"
on:
push:
- branches: [main]
+ paths:
+ - 'ruby/**'
+ branches:
+ - main
+ - 'rc/*'
pull_request:
- branches: [main]
+ paths:
+ - 'ruby/**'
+ branches:
+ - main
+ - 'rc/*'
workflow_dispatch:
inputs:
tag:
@@ -14,6 +22,10 @@ on:
env:
CARGO_TERM_COLOR: always
+defaults:
+ run:
+ working-directory: ruby
+
jobs:
build:
strategy:
@@ -35,7 +47,7 @@ jobs:
path: |
~/.cargo/registry
~/.cargo/git
- target
+ ruby/target
key: ${{ runner.os }}-rust-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Check formatting
run: cargo fmt --all -- --check
@@ -52,20 +64,20 @@ jobs:
if: ${{ matrix.os == 'ubuntu-latest' }}
with:
name: ruby.dbscheme
- path: ql/lib/ruby.dbscheme
+ path: ruby/ql/lib/ruby.dbscheme
- uses: actions/upload-artifact@v2
if: ${{ matrix.os == 'ubuntu-latest' }}
with:
name: TreeSitter.qll
- path: ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
+ path: ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
- uses: actions/upload-artifact@v2
with:
name: extractor-${{ matrix.os }}
path: |
- target/release/ruby-autobuilder
- target/release/ruby-autobuilder.exe
- target/release/ruby-extractor
- target/release/ruby-extractor.exe
+ ruby/target/release/ruby-autobuilder
+ ruby/target/release/ruby-autobuilder.exe
+ ruby/target/release/ruby-extractor
+ ruby/target/release/ruby-extractor.exe
retention-days: 1
compile-queries:
runs-on: ubuntu-latest
@@ -102,7 +114,7 @@ jobs:
with:
name: codeql-ruby-queries
path: |
- target/packs/*
+ ruby/target/packs/*
retention-days: 1
package:
@@ -113,19 +125,19 @@ jobs:
- uses: actions/download-artifact@v2
with:
name: ruby.dbscheme
- path: ruby
+ path: ruby/ruby
- uses: actions/download-artifact@v2
with:
name: extractor-ubuntu-latest
- path: linux64
+ path: ruby/linux64
- uses: actions/download-artifact@v2
with:
name: extractor-windows-latest
- path: win64
+ path: ruby/win64
- uses: actions/download-artifact@v2
with:
name: extractor-macos-latest
- path: osx64
+ path: ruby/osx64
- run: |
mkdir -p ruby
cp -r codeql-extractor.yml tools ql/lib/ruby.dbscheme.stats ruby/
@@ -141,12 +153,12 @@ jobs:
- uses: actions/upload-artifact@v2
with:
name: codeql-ruby-pack
- path: codeql-ruby.zip
+ path: ruby/codeql-ruby.zip
retention-days: 1
- uses: actions/download-artifact@v2
with:
name: codeql-ruby-queries
- path: qlpacks
+ path: ruby/qlpacks
- run: |
echo '{
"provide": [
@@ -158,18 +170,13 @@ jobs:
- uses: actions/upload-artifact@v2
with:
name: codeql-ruby-bundle
- path: codeql-ruby-bundle.zip
+ path: ruby/codeql-ruby-bundle.zip
retention-days: 1
- - if: ${{ github.event.inputs.tag }}
- run: |
- gh release create --prerelease \
- --title "CodeQL Ruby (${{ github.event.inputs.tag }})" \
- --target "${{ github.sha }}" \
- "${{ github.event.inputs.tag }}" \
- codeql-ruby-bundle.zip
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
test:
+ defaults:
+ run:
+ working-directory: ${{ github.workspace }}
strategy:
fail-fast: false
matrix:
diff --git a/.github/workflows/dataset_measure.yml b/.github/workflows/ruby-dataset-measure.yml
similarity index 73%
rename from .github/workflows/dataset_measure.yml
rename to .github/workflows/ruby-dataset-measure.yml
index 7997d7f9240..71eca47d54c 100644
--- a/.github/workflows/dataset_measure.yml
+++ b/.github/workflows/ruby-dataset-measure.yml
@@ -1,14 +1,14 @@
-name: Collect database stats
+name: "Ruby: Collect database stats"
on:
push:
branches: [main]
paths:
- - ql/lib/ruby.dbscheme
+ - ruby/ql/lib/ruby.dbscheme
pull_request:
branches: [main]
paths:
- - ql/lib/ruby.dbscheme
+ - ruby/ql/lib/ruby.dbscheme
workflow_dispatch:
jobs:
@@ -34,15 +34,15 @@ jobs:
path: ${{ github.workspace }}/repo
- name: Create database
run: |
- codeql/codeql database create \
- --search-path "${{ github.workspace }}" \
+ codeql database create \
+ --search-path "${{ github.workspace }}/ruby" \
--threads 4 \
--language ruby --source-root "${{ github.workspace }}/repo" \
"${{ runner.temp }}/database"
- name: Measure database
run: |
mkdir -p "stats/${{ matrix.repo }}"
- codeql/codeql dataset measure --threads 4 --output "stats/${{ matrix.repo }}/stats.xml" "${{ runner.temp }}/database/db-ruby"
+ codeql dataset measure --threads 4 --output "stats/${{ matrix.repo }}/stats.xml" "${{ runner.temp }}/database/db-ruby"
- uses: actions/upload-artifact@v2
with:
name: measurements
@@ -60,8 +60,8 @@ jobs:
path: stats
- run: |
python -m pip install --user lxml
- find stats -name 'stats.xml' | sort | xargs python scripts/merge_stats.py --output ql/lib/ruby.dbscheme.stats --normalise ruby_tokeninfo
+ find stats -name 'stats.xml' | sort | xargs python ruby/scripts/merge_stats.py --output ruby/ql/lib/ruby.dbscheme.stats --normalise ruby_tokeninfo
- uses: actions/upload-artifact@v2
with:
name: ruby.dbscheme.stats
- path: ql/lib/ruby.dbscheme.stats
+ path: ruby/ql/lib/ruby.dbscheme.stats
diff --git a/.github/workflows/ruby-qltest.yml b/.github/workflows/ruby-qltest.yml
new file mode 100644
index 00000000000..ece2286ad34
--- /dev/null
+++ b/.github/workflows/ruby-qltest.yml
@@ -0,0 +1,50 @@
+name: "Ruby: Run QL Tests"
+
+on:
+ push:
+ paths:
+ - 'ruby/**'
+ branches:
+ - main
+ - 'rc/*'
+ pull_request:
+ paths:
+ - 'ruby/**'
+ branches:
+ - main
+ - 'rc/*'
+
+env:
+ CARGO_TERM_COLOR: always
+
+defaults:
+ run:
+ working-directory: ruby
+
+jobs:
+ qltest:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ submodules: true
+ - uses: ./.github/actions/fetch-codeql
+ - uses: ./ruby/actions/create-extractor-pack
+ - name: Run QL tests
+ run: |
+ codeql test run --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --search-path "${{ github.workspace }}/ruby" --additional-packs "${{ github.workspace }}/ruby/codeql" --consistency-queries ql/consistency-queries ql/test
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ - name: Check QL formatting
+ run: find ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only
+ - name: Check QL compilation
+ run: |
+ codeql query compile --check-only --threads=4 --warnings=error --search-path "${{ github.workspace }}/ruby" --additional-packs "${{ github.workspace }}/ruby/codeql" "ql/src" "ql/examples"
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ - name: Check DB upgrade scripts
+ run: |
+ echo >empty.trap
+ codeql dataset import -S ql/lib/upgrades/initial/ruby.dbscheme testdb empty.trap
+ codeql dataset upgrade testdb --additional-packs ql/lib/upgrades
+ diff -q testdb/ruby.dbscheme ql/lib/ruby.dbscheme
diff --git a/.github/workflows/sync_files.yml b/.github/workflows/sync-files.yml
similarity index 84%
rename from .github/workflows/sync_files.yml
rename to .github/workflows/sync-files.yml
index a102f822330..07160b67de3 100644
--- a/.github/workflows/sync_files.yml
+++ b/.github/workflows/sync-files.yml
@@ -14,4 +14,4 @@ jobs:
with:
submodules: true
- name: Check synchronized files
- run: scripts/sync-identical-files.py
+ run: ruby/scripts/sync-identical-files.py
From 3554e8d105026a3cce4ab7f116dcf07567dac09a Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Thu, 14 Oct 2021 20:27:44 +0200
Subject: [PATCH 107/471] Drop LICENSE and CODE_OF_CONDUCT.md
---
ruby/CODE_OF_CONDUCT.md | 76 -----------------------------------------
ruby/LICENSE | 21 ------------
2 files changed, 97 deletions(-)
delete mode 100644 ruby/CODE_OF_CONDUCT.md
delete mode 100644 ruby/LICENSE
diff --git a/ruby/CODE_OF_CONDUCT.md b/ruby/CODE_OF_CONDUCT.md
deleted file mode 100644
index 3a64696bc25..00000000000
--- a/ruby/CODE_OF_CONDUCT.md
+++ /dev/null
@@ -1,76 +0,0 @@
-# Contributor Covenant Code of Conduct
-
-## Our Pledge
-
-In the interest of fostering an open and welcoming environment, we as
-contributors and maintainers pledge to make participation in our project and
-our community a harassment-free experience for everyone, regardless of age, body
-size, disability, ethnicity, sex characteristics, gender identity and expression,
-level of experience, education, socio-economic status, nationality, personal
-appearance, race, religion, or sexual identity and orientation.
-
-## Our Standards
-
-Examples of behavior that contributes to creating a positive environment
-include:
-
-* Using welcoming and inclusive language
-* Being respectful of differing viewpoints and experiences
-* Gracefully accepting constructive criticism
-* Focusing on what is best for the community
-* Showing empathy towards other community members
-
-Examples of unacceptable behavior by participants include:
-
-* The use of sexualized language or imagery and unwelcome sexual attention or
- advances
-* Trolling, insulting/derogatory comments, and personal or political attacks
-* Public or private harassment
-* Publishing others' private information, such as a physical or electronic
- address, without explicit permission
-* Other conduct which could reasonably be considered inappropriate in a
- professional setting
-
-## Our Responsibilities
-
-Project maintainers are responsible for clarifying the standards of acceptable
-behavior and are expected to take appropriate and fair corrective action in
-response to any instances of unacceptable behavior.
-
-Project maintainers have the right and responsibility to remove, edit, or
-reject comments, commits, code, wiki edits, issues, and other contributions
-that are not aligned to this Code of Conduct, or to ban temporarily or
-permanently any contributor for other behaviors that they deem inappropriate,
-threatening, offensive, or harmful.
-
-## Scope
-
-This Code of Conduct applies within all project spaces, and it also applies when
-an individual is representing the project or its community in public spaces.
-Examples of representing a project or community include using an official
-project e-mail address, posting via an official social media account, or acting
-as an appointed representative at an online or offline event. Representation of
-a project may be further defined and clarified by project maintainers.
-
-## Enforcement
-
-Instances of abusive, harassing, or otherwise unacceptable behavior may be
-reported by contacting the project team at opensource@github.com. All
-complaints will be reviewed and investigated and will result in a response that
-is deemed necessary and appropriate to the circumstances. The project team is
-obligated to maintain confidentiality with regard to the reporter of an incident.
-Further details of specific enforcement policies may be posted separately.
-
-Project maintainers who do not follow or enforce the Code of Conduct in good
-faith may face temporary or permanent repercussions as determined by other
-members of the project's leadership.
-
-## Attribution
-
-This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
-available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
-
-[homepage]: https://www.contributor-covenant.org
-
-For answers to common questions about this code of conduct, see
-https://www.contributor-covenant.org/faq
diff --git a/ruby/LICENSE b/ruby/LICENSE
deleted file mode 100644
index eeaa7263358..00000000000
--- a/ruby/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2020-2021 GitHub
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
From 8ce7b287d1efa320a8a5da367e3d756ab260159e Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Thu, 14 Oct 2021 13:18:27 +0200
Subject: [PATCH 108/471] Update dependabot config
---
.github/dependabot.yml | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 7d08545a991..613f9287146 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -1,18 +1,18 @@
version: 2
updates:
- package-ecosystem: "cargo"
- directory: "/node-types"
+ directory: "ruby/node-types"
schedule:
interval: "daily"
- package-ecosystem: "cargo"
- directory: "/generator"
+ directory: "ruby/generator"
schedule:
interval: "daily"
- package-ecosystem: "cargo"
- directory: "/extractor"
+ directory: "ruby/extractor"
schedule:
interval: "daily"
- package-ecosystem: "cargo"
- directory: "/autobuilder"
+ directory: "ruby/autobuilder"
schedule:
interval: "daily"
From aeb9ace6945e52450bdc59ab95566e18d31c480d Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Thu, 14 Oct 2021 20:26:48 +0200
Subject: [PATCH 109/471] Add ruby to CODEOWNERS
---
CODEOWNERS | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/CODEOWNERS b/CODEOWNERS
index 89529f95924..d5f3362a7ca 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -3,6 +3,7 @@
/java/ @github/codeql-java
/javascript/ @github/codeql-javascript
/python/ @github/codeql-python
+/ruby/ @github/codeql-ruby
# Make @xcorail (GitHub Security Lab) a code owner for experimental queries so he gets pinged when we promote a query out of experimental
/cpp/**/experimental/**/* @github/codeql-c-analysis @xcorail
@@ -10,6 +11,7 @@
/java/**/experimental/**/* @github/codeql-java @xcorail
/javascript/**/experimental/**/* @github/codeql-javascript @xcorail
/python/**/experimental/**/* @github/codeql-python @xcorail
+/ruby/**/experimental/**/* @github/codeql-ruby @xcorail
# Notify members of codeql-go about PRs to the shared data-flow library files
/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @github/codeql-java @github/codeql-go
@@ -22,4 +24,4 @@
/docs/codeql-cli/ @github/codeql-cli-reviewers
/docs/codeql-for-visual-studio-code/ @github/codeql-vscode-reviewers
/docs/ql-language-reference/ @github/codeql-frontend-reviewers
-/docs/query-*-style-guide.md @github/codeql-analysis-reviewers
\ No newline at end of file
+/docs/query-*-style-guide.md @github/codeql-analysis-reviewers
From ddbba403f8c05b777973388ec71d0f249de1b3fb Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Thu, 14 Oct 2021 21:26:08 +0200
Subject: [PATCH 110/471] Update CodeSpaces configuration
---
.devcontainer/devcontainer.json | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 8f9ffdde4de..ff73bcb4e7b 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -1,9 +1,14 @@
{
"extensions": [
+ "rust-lang.rust",
+ "bungcip.better-toml",
"github.vscode-codeql",
"slevesque.vscode-zipexplorer"
],
"settings": {
+ "files.watcherExclude": {
+ "**/target/**": true
+ },
"codeQL.runningQueries.memory": 2048
}
}
From 1bf4542c895dbdcb8806f17b16419fbe1f82634f Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Fri, 15 Oct 2021 12:46:50 +0200
Subject: [PATCH 111/471] Remove github/codeql submodule
---
.github/workflows/ruby-qltest.yml | 6 ++----
.github/workflows/sync-files.yml | 2 --
.gitmodules | 3 ---
ruby/codeql | 1 -
4 files changed, 2 insertions(+), 10 deletions(-)
delete mode 100644 .gitmodules
delete mode 160000 ruby/codeql
diff --git a/.github/workflows/ruby-qltest.yml b/.github/workflows/ruby-qltest.yml
index ece2286ad34..fad38bf689e 100644
--- a/.github/workflows/ruby-qltest.yml
+++ b/.github/workflows/ruby-qltest.yml
@@ -26,20 +26,18 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- with:
- submodules: true
- uses: ./.github/actions/fetch-codeql
- uses: ./ruby/actions/create-extractor-pack
- name: Run QL tests
run: |
- codeql test run --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --search-path "${{ github.workspace }}/ruby" --additional-packs "${{ github.workspace }}/ruby/codeql" --consistency-queries ql/consistency-queries ql/test
+ codeql test run --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --search-path "${{ github.workspace }}/ruby" --additional-packs "${{ github.workspace }}" --consistency-queries ql/consistency-queries ql/test
env:
GITHUB_TOKEN: ${{ github.token }}
- name: Check QL formatting
run: find ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only
- name: Check QL compilation
run: |
- codeql query compile --check-only --threads=4 --warnings=error --search-path "${{ github.workspace }}/ruby" --additional-packs "${{ github.workspace }}/ruby/codeql" "ql/src" "ql/examples"
+ codeql query compile --check-only --threads=4 --warnings=error --search-path "${{ github.workspace }}/ruby" --additional-packs "${{ github.workspace }}" "ql/src" "ql/examples"
env:
GITHUB_TOKEN: ${{ github.token }}
- name: Check DB upgrade scripts
diff --git a/.github/workflows/sync-files.yml b/.github/workflows/sync-files.yml
index 07160b67de3..e7894ae4ead 100644
--- a/.github/workflows/sync-files.yml
+++ b/.github/workflows/sync-files.yml
@@ -11,7 +11,5 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- with:
- submodules: true
- name: Check synchronized files
run: ruby/scripts/sync-identical-files.py
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index 1372e928c4e..00000000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "codeql"]
- path = ruby/codeql
- url = https://github.com/github/codeql.git
diff --git a/ruby/codeql b/ruby/codeql
deleted file mode 160000
index 65f4ec403f3..00000000000
--- a/ruby/codeql
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 65f4ec403f3528671131ca83067cf23cb48c9738
From de38570424666c9bfd6c23808c52028447c35a95 Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Thu, 14 Oct 2021 12:51:06 +0200
Subject: [PATCH 112/471] Merge identical-files.json
---
.github/workflows/sync-files.yml | 3 +-
config/identical-files.json | 34 ++++++++++----
.../ruby/dataflow/internal/DataFlowImpl.qll | 9 ++--
ruby/scripts/identical-files.json | 43 -----------------
ruby/scripts/sync-identical-files.py | 46 -------------------
5 files changed, 32 insertions(+), 103 deletions(-)
delete mode 100644 ruby/scripts/identical-files.json
delete mode 100755 ruby/scripts/sync-identical-files.py
diff --git a/.github/workflows/sync-files.yml b/.github/workflows/sync-files.yml
index e7894ae4ead..1ba6c9c2340 100644
--- a/.github/workflows/sync-files.yml
+++ b/.github/workflows/sync-files.yml
@@ -12,4 +12,5 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Check synchronized files
- run: ruby/scripts/sync-identical-files.py
+ run: python config/sync-files.py
+
diff --git a/config/identical-files.json b/config/identical-files.json
index 74ef7b82323..f3907eb154b 100644
--- a/config/identical-files.json
+++ b/config/identical-files.json
@@ -24,14 +24,16 @@
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll",
- "python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll"
+ "python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll",
+ "ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll"
],
"DataFlow Java/C++/C#/Python Common": [
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll",
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll",
- "python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll"
+ "python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll",
+ "ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll"
],
"TaintTracking::Configuration Java/C++/C#/Python": [
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
@@ -49,18 +51,21 @@
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking1/TaintTrackingImpl.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking2/TaintTrackingImpl.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking3/TaintTrackingImpl.qll",
- "python/ql/lib/semmle/python/dataflow/new/internal/tainttracking4/TaintTrackingImpl.qll"
+ "python/ql/lib/semmle/python/dataflow/new/internal/tainttracking4/TaintTrackingImpl.qll",
+ "ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll"
],
"DataFlow Java/C++/C#/Python Consistency checks": [
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll",
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll",
- "python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll"
+ "python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll",
+ "ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll"
],
"DataFlow Java/C# Flow Summaries": [
"java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll",
- "csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll"
+ "csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll",
+ "ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll"
],
"SsaReadPosition Java/C#": [
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll",
@@ -368,7 +373,8 @@
"Inline Test Expectations": [
"cpp/ql/test/TestUtilities/InlineExpectationsTest.qll",
"java/ql/test/TestUtilities/InlineExpectationsTest.qll",
- "python/ql/test/TestUtilities/InlineExpectationsTest.qll"
+ "python/ql/test/TestUtilities/InlineExpectationsTest.qll",
+ "ruby/ql/test/TestUtilities/InlineExpectationsTest.qll"
],
"C++ ExternalAPIs": [
"cpp/ql/src/Security/CWE/CWE-020/ExternalAPIs.qll",
@@ -440,7 +446,8 @@
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll",
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll",
- "csharp/ql/lib/semmle/code/cil/internal/SsaImplCommon.qll"
+ "csharp/ql/lib/semmle/code/cil/internal/SsaImplCommon.qll",
+ "ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll"
],
"CryptoAlgorithms Python/JS": [
"javascript/ql/lib/semmle/javascript/security/CryptoAlgorithms.qll",
@@ -460,6 +467,15 @@
],
"ReDoS Polynomial Python/JS": [
"javascript/ql/lib/semmle/javascript/security/performance/SuperlinearBackTracking.qll",
- "python/ql/lib/semmle/python/security/performance/SuperlinearBackTracking.qll"
+ "python/ql/lib/semmle/python/security/performance/SuperlinearBackTracking.qll",
+ "ruby/ql/lib/codeql/ruby/regexp/SuperlinearBackTracking.qll"
+ ],
+ "CFG": [
+ "csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll",
+ "ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll"
+ ],
+ "TypeTracker": [
+ "python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll",
+ "ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll"
]
-}
\ No newline at end of file
+}
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll
index 758b70ec316..0c99a25ccc4 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll
@@ -3643,9 +3643,10 @@ private module Subpaths {
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
NodeEx out, AccessPath apout
) {
- pathThroughCallable(arg, out, _, apout) and
+ pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
pathIntoCallable(arg, par, _, innercc, sc, _) and
- paramFlowsThrough(kind, innercc, sc, apout, _, unbindConf(arg.getConfiguration()))
+ paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
+ unbindConf(arg.getConfiguration()))
}
/**
@@ -3690,8 +3691,8 @@ private module Subpaths {
*/
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
- arg.getASuccessor() = par and
- arg.getASuccessor() = out and
+ pragma[only_bind_into](arg).getASuccessor() = par and
+ pragma[only_bind_into](arg).getASuccessor() = out and
subpaths03(arg, p, ret, o, apout) and
par.getNodeEx() = p and
out.getNodeEx() = o and
diff --git a/ruby/scripts/identical-files.json b/ruby/scripts/identical-files.json
deleted file mode 100644
index b2d55458914..00000000000
--- a/ruby/scripts/identical-files.json
+++ /dev/null
@@ -1,43 +0,0 @@
-{
- "SSA": [
- "codeql/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll",
- "ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll"
- ],
- "DataFlow Common": [
- "codeql/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll",
- "ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll"
- ],
- "DataFlow": [
- "codeql/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll",
- "ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll"
- ],
- "DataFlow Consistency": [
- "codeql/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll",
- "ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll"
- ],
- "DataFlow Summaries": [
- "codeql/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll",
- "ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll"
- ],
- "TaintTracking": [
- "codeql/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
- "ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll"
- ],
- "TypeTracker": [
- "codeql/python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll",
- "ql/lib/codeql/ruby/typetracking/TypeTracker.qll"
- ],
- "Inline Test Expectations": [
- "codeql/python/ql/test/TestUtilities/InlineExpectationsTest.qll",
- "ql/test/TestUtilities/InlineExpectationsTest.qll"
- ],
- "CFG": [
- "codeql/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll",
- "ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll"
- ],
- "ReDoS Polynomial Ruby/Python/JS": [
- "codeql/javascript/ql/lib/semmle/javascript/security/performance/SuperlinearBackTracking.qll",
- "codeql/python/ql/lib/semmle/python/security/performance/SuperlinearBackTracking.qll",
- "ql/lib/codeql/ruby/regexp/SuperlinearBackTracking.qll"
- ]
-}
diff --git a/ruby/scripts/sync-identical-files.py b/ruby/scripts/sync-identical-files.py
deleted file mode 100755
index 17fe22808e9..00000000000
--- a/ruby/scripts/sync-identical-files.py
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/usr/bin/env python3
-
-# Due to various technical limitations, we sometimes have files that need to be
-# kept identical in the repository. This script loads a database of such
-# files and can perform two functions: check whether they are still identical,
-# and overwrite the others with a master copy if needed.
-# The script that does the actual work is `sync-files.py`, which lives in the `codeql` submodule.
-import sys
-import os
-
-sys.path.append(os.path.realpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../codeql/config')))
-
-import importlib
-syncfiles = importlib.import_module('sync-files')
-
-def chdir_repo_root():
- root_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')
- os.chdir(root_path)
-
-def sync_identical_files():
- if len(sys.argv) == 1:
- master_file_picker = lambda files: None
- elif len(sys.argv) == 2:
- if sys.argv[1] == "--latest":
- master_file_picker = syncfiles.choose_latest_file
- elif os.path.isfile(sys.argv[1]):
- master_file_picker = lambda files: syncfiles.choose_master_file(sys.argv[1], files)
- else:
- raise Exception("File not found")
- else:
- raise Exception("Bad command line or file not found")
- chdir_repo_root()
- syncfiles.load_if_exists('.', 'scripts/identical-files.json')
- for group_name, files in syncfiles.file_groups.items():
- syncfiles.check_group(group_name, files, master_file_picker, syncfiles.emit_local_error)
-
-def main():
- sync_identical_files()
-
- if syncfiles.local_error_count > 0:
- exit(1)
- else:
- print(__file__ +": All checks OK.")
-
-if __name__ == "__main__":
- main()
From b23b3c33f64199cd8794a7946534f4a592d47f56 Mon Sep 17 00:00:00 2001
From: shati-patel <42641846+shati-patel@users.noreply.github.com>
Date: Fri, 15 Oct 2021 15:33:33 +0100
Subject: [PATCH 113/471] Add a queries.xml file (for CWE coverage) docs
---
ruby/ql/lib/queries.xml | 1 +
1 file changed, 1 insertion(+)
create mode 100644 ruby/ql/lib/queries.xml
diff --git a/ruby/ql/lib/queries.xml b/ruby/ql/lib/queries.xml
new file mode 100644
index 00000000000..a7ce9735ef4
--- /dev/null
+++ b/ruby/ql/lib/queries.xml
@@ -0,0 +1 @@
+
From 8cd86ae8f515a5d8a7ec674ffc0d731be0d01eef Mon Sep 17 00:00:00 2001
From: shati-patel <42641846+shati-patel@users.noreply.github.com>
Date: Mon, 18 Oct 2021 11:00:56 +0100
Subject: [PATCH 114/471] Move queries.xml to `src`
---
ruby/ql/{lib => src}/queries.xml | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename ruby/ql/{lib => src}/queries.xml (100%)
diff --git a/ruby/ql/lib/queries.xml b/ruby/ql/src/queries.xml
similarity index 100%
rename from ruby/ql/lib/queries.xml
rename to ruby/ql/src/queries.xml
From b79f8f18901e5ba5b67db1e28ad53300e2d05e9c Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Mon, 25 Oct 2021 16:25:44 +0200
Subject: [PATCH 115/471] Fix CI jobs
---
.codeqlmanifest.json | 3 ++-
.github/workflows/qhelp-pr-preview.yml | 4 +++-
.github/workflows/ruby-dataset-measure.yml | 8 ++++++--
.github/workflows/sync-files.yml | 8 ++++++--
4 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/.codeqlmanifest.json b/.codeqlmanifest.json
index 4efe12f6d2b..97e6df87f14 100644
--- a/.codeqlmanifest.json
+++ b/.codeqlmanifest.json
@@ -1,4 +1,5 @@
-{ "provide": [ "*/ql/src/qlpack.yml",
+{ "provide": [ "ruby/.codeqlmanifest.json",
+ "*/ql/src/qlpack.yml",
"*/ql/lib/qlpack.yml",
"*/ql/test/qlpack.yml",
"cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml",
diff --git a/.github/workflows/qhelp-pr-preview.yml b/.github/workflows/qhelp-pr-preview.yml
index 99ff16a232a..7e2ea6a10f8 100644
--- a/.github/workflows/qhelp-pr-preview.yml
+++ b/.github/workflows/qhelp-pr-preview.yml
@@ -2,7 +2,9 @@ name: Query help preview
on:
pull_request:
- branches: [main]
+ branches:
+ - main
+ - 'rc/*'
paths:
- "ruby/**/*.qhelp"
diff --git a/.github/workflows/ruby-dataset-measure.yml b/.github/workflows/ruby-dataset-measure.yml
index 71eca47d54c..39e5a8486d6 100644
--- a/.github/workflows/ruby-dataset-measure.yml
+++ b/.github/workflows/ruby-dataset-measure.yml
@@ -2,11 +2,15 @@ name: "Ruby: Collect database stats"
on:
push:
- branches: [main]
+ branches:
+ - main
+ - 'rc/*'
paths:
- ruby/ql/lib/ruby.dbscheme
pull_request:
- branches: [main]
+ branches:
+ - main
+ - 'rc/*'
paths:
- ruby/ql/lib/ruby.dbscheme
workflow_dispatch:
diff --git a/.github/workflows/sync-files.yml b/.github/workflows/sync-files.yml
index 1ba6c9c2340..e41f4b75dc6 100644
--- a/.github/workflows/sync-files.yml
+++ b/.github/workflows/sync-files.yml
@@ -2,9 +2,13 @@ name: Check synchronized files
on:
push:
- branches: [main]
+ branches:
+ - main
+ - 'rc/*'
pull_request:
- branches: [main]
+ branches:
+ - main
+ - 'rc/*'
jobs:
sync:
From dbd1148bd681503bffb9a40d1b1085bad793e4ba Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Mon, 25 Oct 2021 08:32:15 +0200
Subject: [PATCH 116/471] apply range pattern patch to javascript
---
javascript/ql/lib/semmle/javascript/DOM.qll | 30 +++++++----------
.../lib/semmle/javascript/dataflow/Nodes.qll | 32 +++++++------------
.../javascript/dataflow/Refinements.qll | 11 +++----
.../PolynomialReDoSCustomizations.qll | 6 ++--
.../CWE-090/LdapInjectionCustomizations.qll | 12 +++----
5 files changed, 34 insertions(+), 57 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/DOM.qll b/javascript/ql/lib/semmle/javascript/DOM.qll
index 5d721858de8..79a15c2a219 100644
--- a/javascript/ql/lib/semmle/javascript/DOM.qll
+++ b/javascript/ql/lib/semmle/javascript/DOM.qll
@@ -63,29 +63,25 @@ module DOM {
/**
* An HTML element, viewed as an `ElementDefinition`.
*/
- private class HtmlElementDefinition extends ElementDefinition, @xmlelement {
- HtmlElementDefinition() { this instanceof HTML::Element }
-
- override string getName() { result = this.(HTML::Element).getName() }
+ private class HtmlElementDefinition extends ElementDefinition, @xmlelement instanceof HTML::Element {
+ override string getName() { result = HTML::Element.super.getName() }
override AttributeDefinition getAttribute(int i) {
- result = this.(HTML::Element).getAttribute(i)
+ result = HTML::Element.super.getAttribute(i)
}
- override ElementDefinition getParent() { result = this.(HTML::Element).getParent() }
+ override ElementDefinition getParent() { result = HTML::Element.super.getParent() }
}
/**
* A JSX element, viewed as an `ElementDefinition`.
*/
- private class JsxElementDefinition extends ElementDefinition, @jsx_element {
- JsxElementDefinition() { this instanceof JSXElement }
+ private class JsxElementDefinition extends ElementDefinition, @jsx_element instanceof JSXElement {
+ override string getName() { result = JSXElement.super.getName() }
- override string getName() { result = this.(JSXElement).getName() }
+ override AttributeDefinition getAttribute(int i) { result = JSXElement.super.getAttribute(i) }
- override AttributeDefinition getAttribute(int i) { result = this.(JSXElement).getAttribute(i) }
-
- override ElementDefinition getParent() { result = this.(JSXElement).getJsxParent() }
+ override ElementDefinition getParent() { result = super.getJsxParent() }
}
/**
@@ -131,14 +127,12 @@ module DOM {
/**
* An HTML attribute, viewed as an `AttributeDefinition`.
*/
- private class HtmlAttributeDefinition extends AttributeDefinition, @xmlattribute {
- HtmlAttributeDefinition() { this instanceof HTML::Attribute }
+ private class HtmlAttributeDefinition extends AttributeDefinition, @xmlattribute instanceof HTML::Attribute {
+ override string getName() { result = HTML::Attribute.super.getName() }
- override string getName() { result = this.(HTML::Attribute).getName() }
+ override string getStringValue() { result = super.getValue() }
- override string getStringValue() { result = this.(HTML::Attribute).getValue() }
-
- override ElementDefinition getElement() { result = this.(HTML::Attribute).getElement() }
+ override ElementDefinition getElement() { result = HTML::Attribute.super.getElement() }
}
/**
diff --git a/javascript/ql/lib/semmle/javascript/dataflow/Nodes.qll b/javascript/ql/lib/semmle/javascript/dataflow/Nodes.qll
index c697c43dcce..d4dbfe43ec8 100644
--- a/javascript/ql/lib/semmle/javascript/dataflow/Nodes.qll
+++ b/javascript/ql/lib/semmle/javascript/dataflow/Nodes.qll
@@ -61,17 +61,15 @@ class ParameterNode extends DataFlow::SourceNode {
* new Array(16)
* ```
*/
-class InvokeNode extends DataFlow::SourceNode {
- InvokeNode() { this instanceof DataFlow::Impl::InvokeNodeDef }
-
+class InvokeNode extends DataFlow::SourceNode instanceof DataFlow::Impl::InvokeNodeDef {
/** Gets the syntactic invoke expression underlying this function invocation. */
- InvokeExpr getInvokeExpr() { result = this.(DataFlow::Impl::InvokeNodeDef).getInvokeExpr() }
+ InvokeExpr getInvokeExpr() { result = super.getInvokeExpr() }
/** Gets the name of the function or method being invoked, if it can be determined. */
- string getCalleeName() { result = this.(DataFlow::Impl::InvokeNodeDef).getCalleeName() }
+ string getCalleeName() { result = super.getCalleeName() }
/** Gets the data flow node specifying the function to be called. */
- DataFlow::Node getCalleeNode() { result = this.(DataFlow::Impl::InvokeNodeDef).getCalleeNode() }
+ DataFlow::Node getCalleeNode() { result = super.getCalleeNode() }
/**
* Gets the data flow node corresponding to the `i`th argument of this invocation.
@@ -92,10 +90,10 @@ class InvokeNode extends DataFlow::SourceNode {
* but the position of `z` cannot be determined, hence there are no first and second
* argument nodes.
*/
- DataFlow::Node getArgument(int i) { result = this.(DataFlow::Impl::InvokeNodeDef).getArgument(i) }
+ DataFlow::Node getArgument(int i) { result = super.getArgument(i) }
/** Gets the data flow node corresponding to an argument of this invocation. */
- DataFlow::Node getAnArgument() { result = this.(DataFlow::Impl::InvokeNodeDef).getAnArgument() }
+ DataFlow::Node getAnArgument() { result = super.getAnArgument() }
/** Gets the data flow node corresponding to the last argument of this invocation. */
DataFlow::Node getLastArgument() { result = getArgument(getNumArgument() - 1) }
@@ -112,12 +110,10 @@ class InvokeNode extends DataFlow::SourceNode {
* ```
* .
*/
- DataFlow::Node getASpreadArgument() {
- result = this.(DataFlow::Impl::InvokeNodeDef).getASpreadArgument()
- }
+ DataFlow::Node getASpreadArgument() { result = super.getASpreadArgument() }
/** Gets the number of arguments of this invocation, if it can be determined. */
- int getNumArgument() { result = this.(DataFlow::Impl::InvokeNodeDef).getNumArgument() }
+ int getNumArgument() { result = super.getNumArgument() }
Function getEnclosingFunction() { result = getBasicBlock().getContainer() }
@@ -258,15 +254,13 @@ class InvokeNode extends DataFlow::SourceNode {
* Math.abs(x)
* ```
*/
-class CallNode extends InvokeNode {
- CallNode() { this instanceof DataFlow::Impl::CallNodeDef }
-
+class CallNode extends InvokeNode instanceof DataFlow::Impl::CallNodeDef {
/**
* Gets the data flow node corresponding to the receiver expression of this method call.
*
* For example, the receiver of `x.m()` is `x`.
*/
- DataFlow::Node getReceiver() { result = this.(DataFlow::Impl::CallNodeDef).getReceiver() }
+ DataFlow::Node getReceiver() { result = super.getReceiver() }
}
/**
@@ -279,11 +273,9 @@ class CallNode extends InvokeNode {
* Math.abs(x)
* ```
*/
-class MethodCallNode extends CallNode {
- MethodCallNode() { this instanceof DataFlow::Impl::MethodCallNodeDef }
-
+class MethodCallNode extends CallNode instanceof DataFlow::Impl::MethodCallNodeDef {
/** Gets the name of the invoked method, if it can be determined. */
- string getMethodName() { result = this.(DataFlow::Impl::MethodCallNodeDef).getMethodName() }
+ string getMethodName() { result = super.getMethodName() }
/**
* Holds if this data flow node calls method `methodName` on receiver node `receiver`.
diff --git a/javascript/ql/lib/semmle/javascript/dataflow/Refinements.qll b/javascript/ql/lib/semmle/javascript/dataflow/Refinements.qll
index 4cfc9099254..91ed08e4a44 100644
--- a/javascript/ql/lib/semmle/javascript/dataflow/Refinements.qll
+++ b/javascript/ql/lib/semmle/javascript/dataflow/Refinements.qll
@@ -53,21 +53,18 @@ abstract class RefinementCandidate extends Expr {
* A refinement candidate that references at most one variable, and hence
* can be used to refine the abstract values inferred for that variable.
*/
-class Refinement extends Expr {
- Refinement() {
- this instanceof RefinementCandidate and
- count(this.(RefinementCandidate).getARefinedVar()) <= 1
- }
+class Refinement extends Expr instanceof RefinementCandidate {
+ Refinement() { count(this.(RefinementCandidate).getARefinedVar()) <= 1 }
/**
* Gets the variable refined by this expression, if any.
*/
- SsaSourceVariable getRefinedVar() { result = this.(RefinementCandidate).getARefinedVar() }
+ SsaSourceVariable getRefinedVar() { result = super.getARefinedVar() }
/**
* Gets a refinement value inferred for this expression in context `ctxt`.
*/
- RefinementValue eval(RefinementContext ctxt) { result = this.(RefinementCandidate).eval(ctxt) }
+ RefinementValue eval(RefinementContext ctxt) { result = super.eval(ctxt) }
}
/** A literal, viewed as a refinement expression. */
diff --git a/javascript/ql/lib/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll
index 9574e6e6376..2a63447b561 100644
--- a/javascript/ql/lib/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll
+++ b/javascript/ql/lib/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll
@@ -47,10 +47,8 @@ module PolynomialReDoS {
* A remote input to a server, seen as a source for polynomial
* regular expression denial-of-service vulnerabilities.
*/
- class RequestInputAccessAsSource extends Source {
- RequestInputAccessAsSource() { this instanceof HTTP::RequestInputAccess }
-
- override string getKind() { result = this.(HTTP::RequestInputAccess).getKind() }
+ class RequestInputAccessAsSource extends Source instanceof HTTP::RequestInputAccess {
+ override string getKind() { result = HTTP::RequestInputAccess.super.getKind() }
}
/**
diff --git a/javascript/ql/src/experimental/Security/CWE-090/LdapInjectionCustomizations.qll b/javascript/ql/src/experimental/Security/CWE-090/LdapInjectionCustomizations.qll
index cb0292a5c4d..beb52c7cf45 100644
--- a/javascript/ql/src/experimental/Security/CWE-090/LdapInjectionCustomizations.qll
+++ b/javascript/ql/src/experimental/Security/CWE-090/LdapInjectionCustomizations.qll
@@ -39,21 +39,17 @@ module LdapInjection {
/**
* An LDAP filter for an API call that executes an operation against the LDAP server.
*/
- class LdapjsSearchFilterAsSink extends Sink {
- LdapjsSearchFilterAsSink() { this instanceof LdapjsSearchFilter }
-
+ class LdapjsSearchFilterAsSink extends Sink instanceof LdapjsSearchFilter {
override DataFlow::InvokeNode getQueryCall() {
- result = this.(LdapjsSearchFilter).getQueryCall()
+ result = LdapjsSearchFilter.super.getQueryCall()
}
}
/**
* An LDAP DN argument for an API call that executes an operation against the LDAP server.
*/
- class LdapjsDNArgumentAsSink extends Sink {
- LdapjsDNArgumentAsSink() { this instanceof LdapjsDNArgument }
-
- override DataFlow::InvokeNode getQueryCall() { result = this.(LdapjsDNArgument).getQueryCall() }
+ class LdapjsDNArgumentAsSink extends Sink instanceof LdapjsDNArgument {
+ override DataFlow::InvokeNode getQueryCall() { result = LdapjsDNArgument.super.getQueryCall() }
}
/**
From d2d6b2ca7c1b57db7bb3a547468bbf8ccf682e71 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Mon, 25 Oct 2021 19:34:43 +0200
Subject: [PATCH 117/471] apply range pattern patch to cpp
---
cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql b/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql
index 8d016751f39..d1d39fdf59a 100644
--- a/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql
+++ b/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql
@@ -324,10 +324,8 @@ abstract class DataOutput extends Element {
/**
* Data that is output via standard output or standard error.
*/
-class StandardOutput extends DataOutput {
- StandardOutput() { this instanceof OutputWrite }
-
- override Expr getASource() { result = this.(OutputWrite).getASource() }
+class StandardOutput extends DataOutput instanceof OutputWrite {
+ override Expr getASource() { result = OutputWrite.super.getASource() }
}
private predicate socketCallOrIndirect(FunctionCall call) {
From f4a054ea0105d06413c403b45075de654a2d7cbe Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Mon, 25 Oct 2021 19:37:45 +0200
Subject: [PATCH 118/471] apply range pattern patch to python
---
.../dataflow/new/internal/DataFlowPrivate.qll | 6 +--
.../python/dataflow/old/TaintTracking.qll | 8 ++--
.../python/dependencies/TechInventory.qll | 8 ++--
.../lib/semmle/python/objects/ObjectAPI.qll | 48 +++++++------------
.../ql/lib/semmle/python/types/Extensions.qll | 6 +--
5 files changed, 28 insertions(+), 48 deletions(-)
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll
index b1a9ea7aa3a..307d275b319 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll
@@ -1345,10 +1345,8 @@ module IterableUnpacking {
}
/** A (possibly recursive) target of an unpacking assignment which is also a sequence. */
- class UnpackingAssignmentSequenceTarget extends UnpackingAssignmentTarget {
- UnpackingAssignmentSequenceTarget() { this instanceof SequenceNode }
-
- ControlFlowNode getElement(int i) { result = this.(SequenceNode).getElement(i) }
+ class UnpackingAssignmentSequenceTarget extends UnpackingAssignmentTarget instanceof SequenceNode {
+ ControlFlowNode getElement(int i) { result = super.getElement(i) }
ControlFlowNode getAnElement() { result = this.getElement(_) }
}
diff --git a/python/ql/lib/semmle/python/dataflow/old/TaintTracking.qll b/python/ql/lib/semmle/python/dataflow/old/TaintTracking.qll
index e7ee6a3ee08..8ed6711ba38 100755
--- a/python/ql/lib/semmle/python/dataflow/old/TaintTracking.qll
+++ b/python/ql/lib/semmle/python/dataflow/old/TaintTracking.qll
@@ -639,16 +639,14 @@ module DataFlow {
}
}
- deprecated private class ConfigurationAdapter extends TaintTracking::Configuration {
- ConfigurationAdapter() { this instanceof Configuration }
-
+ deprecated private class ConfigurationAdapter extends TaintTracking::Configuration instanceof Configuration {
override predicate isSource(DataFlow::Node node, TaintKind kind) {
- this.(Configuration).isSource(node.asCfgNode()) and
+ Configuration.super.isSource(node.asCfgNode()) and
kind instanceof DataFlowType
}
override predicate isSink(DataFlow::Node node, TaintKind kind) {
- this.(Configuration).isSink(node.asCfgNode()) and
+ Configuration.super.isSink(node.asCfgNode()) and
kind instanceof DataFlowType
}
}
diff --git a/python/ql/lib/semmle/python/dependencies/TechInventory.qll b/python/ql/lib/semmle/python/dependencies/TechInventory.qll
index 0c48b5ecfbb..20a938b86d0 100644
--- a/python/ql/lib/semmle/python/dependencies/TechInventory.qll
+++ b/python/ql/lib/semmle/python/dependencies/TechInventory.qll
@@ -14,16 +14,14 @@ string munge(File sourceFile, ExternalPackage package) {
result = "/" + sourceFile.getRelativePath() + "<|>" + package.getName() + "<|>unknown"
}
-abstract class ExternalPackage extends Object {
- ExternalPackage() { this instanceof ModuleObject }
-
+abstract class ExternalPackage extends Object instanceof ModuleObject {
abstract string getName();
abstract string getVersion();
- Object getAttribute(string name) { result = this.(ModuleObject).attr(name) }
+ Object getAttribute(string name) { result = super.attr(name) }
- PackageObject getPackage() { result = this.(ModuleObject).getPackage() }
+ PackageObject getPackage() { result = super.getPackage() }
}
bindingset[text]
diff --git a/python/ql/lib/semmle/python/objects/ObjectAPI.qll b/python/ql/lib/semmle/python/objects/ObjectAPI.qll
index 946eb1de04d..c01650e6da7 100644
--- a/python/ql/lib/semmle/python/objects/ObjectAPI.qll
+++ b/python/ql/lib/semmle/python/objects/ObjectAPI.qll
@@ -147,9 +147,7 @@ class Value extends TObject {
* Class representing modules in the Python program
* Each `ModuleValue` represents a module object in the Python program.
*/
-class ModuleValue extends Value {
- ModuleValue() { this instanceof ModuleObjectInternal }
-
+class ModuleValue extends Value instanceof ModuleObjectInternal {
/**
* Holds if this module "exports" name.
* That is, does it define `name` in `__all__` or is
@@ -159,7 +157,7 @@ class ModuleValue extends Value {
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() }
+ ModuleScope getScope() { result = super.getSourceModule() }
/**
* Gets the container path for this module. Will be the file for a Python module,
@@ -181,7 +179,7 @@ class ModuleValue extends Value {
predicate isPackage() { this instanceof PackageObjectInternal }
/** Whether the complete set of names "exported" by this module can be accurately determined */
- predicate hasCompleteExportInfo() { this.(ModuleObjectInternal).hasCompleteExportInfo() }
+ predicate hasCompleteExportInfo() { super.hasCompleteExportInfo() }
/** Get a module that this module imports */
ModuleValue getAnImportedModule() { result.importedAs(this.getScope().getAnImportedModuleName()) }
@@ -345,25 +343,21 @@ module Value {
* Callables include Python functions, built-in functions and bound-methods,
* but not classes.
*/
-class CallableValue extends Value {
- CallableValue() { this instanceof CallableObjectInternal }
-
+class CallableValue extends Value instanceof CallableObjectInternal {
/**
* Holds if this callable never returns once called.
* For example, `sys.exit`
*/
- predicate neverReturns() { this.(CallableObjectInternal).neverReturns() }
+ predicate neverReturns() { super.neverReturns() }
/** 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) }
+ NameNode getParameter(int n) { result = super.getParameter(n) }
/** Gets the `name`d parameter node of this callable. */
- NameNode getParameterByName(string name) {
- result = this.(CallableObjectInternal).getParameterByName(name)
- }
+ NameNode getParameterByName(string name) { result = super.getParameterByName(name) }
/**
* Gets the argument in `call` corresponding to the `n`'th positional parameter of this callable.
@@ -452,23 +446,21 @@ class CallableValue extends Value {
* Class representing bound-methods, such as `o.func`, where `o` is an instance
* of a class that has a callable attribute `func`.
*/
-class BoundMethodValue extends CallableValue {
- BoundMethodValue() { this instanceof BoundMethodObjectInternal }
-
+class BoundMethodValue extends CallableValue 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() }
+ CallableValue getFunction() { result = super.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() }
+ Value getSelf() { result = super.getSelf() }
/** Gets the parameter node that will be used for `self`. */
- NameNode getSelfParameter() { result = this.(BoundMethodObjectInternal).getSelfParameter() }
+ NameNode getSelfParameter() { result = super.getSelfParameter() }
}
/**
@@ -831,12 +823,10 @@ class BuiltinMethodValue extends FunctionValue {
/**
* A class representing sequence objects with a length and tracked items.
*/
-class SequenceValue extends Value {
- SequenceValue() { this instanceof SequenceObjectInternal }
+class SequenceValue extends Value instanceof SequenceObjectInternal {
+ Value getItem(int n) { result = super.getItem(n) }
- Value getItem(int n) { result = this.(SequenceObjectInternal).getItem(n) }
-
- int length() { result = this.(SequenceObjectInternal).length() }
+ int length() { result = super.length() }
}
/** A class representing tuple objects */
@@ -887,14 +877,12 @@ class NumericValue extends Value {
* https://docs.python.org/3/howto/descriptor.html#properties
* https://docs.python.org/3/library/functions.html#property
*/
-class PropertyValue extends Value {
- PropertyValue() { this instanceof PropertyInternal }
+class PropertyValue extends Value instanceof PropertyInternal {
+ CallableValue getGetter() { result = super.getGetter() }
- CallableValue getGetter() { result = this.(PropertyInternal).getGetter() }
+ CallableValue getSetter() { result = super.getSetter() }
- CallableValue getSetter() { result = this.(PropertyInternal).getSetter() }
-
- CallableValue getDeleter() { result = this.(PropertyInternal).getDeleter() }
+ CallableValue getDeleter() { result = super.getDeleter() }
}
/** A method-resolution-order sequence of classes */
diff --git a/python/ql/lib/semmle/python/types/Extensions.qll b/python/ql/lib/semmle/python/types/Extensions.qll
index 9c067ed7e4a..6053fc0e034 100644
--- a/python/ql/lib/semmle/python/types/Extensions.qll
+++ b/python/ql/lib/semmle/python/types/Extensions.qll
@@ -151,12 +151,10 @@ class ReModulePointToExtension extends PointsToExtension {
private predicate pointsTo_helper(Context context) { context.appliesTo(this) }
}
-deprecated private class BackwardCompatiblePointToExtension extends PointsToExtension {
- BackwardCompatiblePointToExtension() { this instanceof CustomPointsToFact }
-
+deprecated private class BackwardCompatiblePointToExtension extends PointsToExtension instanceof CustomPointsToFact {
override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) {
exists(Object obj, ClassObject cls |
- this.(CustomPointsToFact).pointsTo(context, obj, cls, origin)
+ CustomPointsToFact.super.pointsTo(context, obj, cls, origin)
|
value.getBuiltin() = obj
or
From e117659dce164ade9865036f1b5a8e0de9e5ab3a Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Mon, 25 Oct 2021 20:50:18 +0200
Subject: [PATCH 119/471] revert a thing for python
---
python/ql/lib/semmle/python/objects/ObjectAPI.qll | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/python/ql/lib/semmle/python/objects/ObjectAPI.qll b/python/ql/lib/semmle/python/objects/ObjectAPI.qll
index c01650e6da7..d3082d26130 100644
--- a/python/ql/lib/semmle/python/objects/ObjectAPI.qll
+++ b/python/ql/lib/semmle/python/objects/ObjectAPI.qll
@@ -343,21 +343,25 @@ module Value {
* Callables include Python functions, built-in functions and bound-methods,
* but not classes.
*/
-class CallableValue extends Value instanceof CallableObjectInternal {
+class CallableValue extends Value {
+ CallableValue() { this instanceof CallableObjectInternal }
+
/**
* Holds if this callable never returns once called.
* For example, `sys.exit`
*/
- predicate neverReturns() { super.neverReturns() }
+ 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 `n`th parameter node of this callable. */
- NameNode getParameter(int n) { result = super.getParameter(n) }
+ NameNode getParameter(int n) { result = this.(CallableObjectInternal).getParameter(n) }
/** Gets the `name`d parameter node of this callable. */
- NameNode getParameterByName(string name) { result = super.getParameterByName(name) }
+ NameNode getParameterByName(string name) {
+ result = this.(CallableObjectInternal).getParameterByName(name)
+ }
/**
* Gets the argument in `call` corresponding to the `n`'th positional parameter of this callable.
From a8a181a32f2ce26337775236a54518b2c9f9c36e Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Tue, 26 Oct 2021 09:55:47 +0200
Subject: [PATCH 120/471] Python: adjust logic and add tests Due to the way
paths a re printed, the tests look surprising
---
python/ql/lib/semmle/python/ApiGraphs.qll | 19 +++++++++++--------
.../lib/semmle/python/frameworks/Asyncpg.qll | 6 +++---
.../dataflow/ApiGraphs/async_test.py | 12 ++++++++++++
3 files changed, 26 insertions(+), 11 deletions(-)
diff --git a/python/ql/lib/semmle/python/ApiGraphs.qll b/python/ql/lib/semmle/python/ApiGraphs.qll
index 4fd39e41491..8d585defa5c 100644
--- a/python/ql/lib/semmle/python/ApiGraphs.qll
+++ b/python/ql/lib/semmle/python/ApiGraphs.qll
@@ -502,20 +502,23 @@ module API {
// - `awaitedValue` is `l`
// - `result` is `l` (should perhaps be `x`, but that should really be a read)
exists(AsyncFor asyncFor |
- result.asExpr() = asyncFor.getTarget() and
- // Morally, we should perhaps use asyncFor.getIter() = awaitedValue.asExpr(),
- // but that is actually behind a read step rather than a flow step.
- asyncFor.getTarget() = awaitedValue.asExpr()
+ result.asExpr() = asyncFor.getIter() and
+ // To consider `x` the result of awaiting, we would use asyncFor.getTarget() = awaitedValue.asExpr(),
+ // but that is behind a read step rather than a flow step.
+ asyncFor.getIter() = awaitedValue.asExpr()
)
or
// `async with x as y`
// - `awaitedValue` is `x`
- // - `result` is `x` (should probably be `y` but it might not exist)
+ // - `result` is `x` and `y` if it exists
exists(AsyncWith asyncWith |
result.asExpr() = asyncWith.getContextExpr() and
- // Morally, we should perhaps use asyncWith.getOptionalVars() = awaitedValue.asExpr(),
- // but that might not exist.
- asyncWith.getContextExpr() = awaitedValue.asExpr()
+ awaitedValue.asExpr() in [
+ // `x`
+ asyncWith.getContextExpr(),
+ // `y`, if it exists
+ asyncWith.getOptionalVars()
+ ]
)
}
diff --git a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
index 58d725f21a4..13af1bdde20 100644
--- a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
+++ b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
@@ -83,17 +83,17 @@ private module Asyncpg {
// - `awaitedValue` is local source of `l`
// - `result` is `l`
exists(AsyncFor asyncFor, DataFlow::Node awaited |
- result.asExpr() = asyncFor.getTarget() and
+ result.asExpr() = asyncFor.getIter() and
asyncFor.getIter() = awaited.asExpr() and
awaited.getALocalSource() = awaitedValue
)
or
// `async with x as y`
// - `awaitedValue` is local source of `x`
- // - `result` is `x`
+ // - `result` is `x` and `y`
exists(AsyncWith asyncWith, DataFlow::Node awaited |
result.asExpr() = asyncWith.getContextExpr() and
- asyncWith.getOptionalVars() = awaited.asExpr() and
+ awaited.asExpr() in [asyncWith.getContextExpr(), asyncWith.getOptionalVars()] and
awaited.getALocalSource() = awaitedValue
)
}
diff --git a/python/ql/test/experimental/dataflow/ApiGraphs/async_test.py b/python/ql/test/experimental/dataflow/ApiGraphs/async_test.py
index 85b14c86644..ba6073d0703 100644
--- a/python/ql/test/experimental/dataflow/ApiGraphs/async_test.py
+++ b/python/ql/test/experimental/dataflow/ApiGraphs/async_test.py
@@ -11,6 +11,18 @@ async def bar():
result = await pkg.async_func() # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited()
return result # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited()
+async def test_async_with():
+ async with pkg.async_func() as result: # $ use=moduleImport("pkg").getMember("async_func").getReturn()
+ return result # $ use=moduleImport("pkg").getMember("async_func").getReturn()
+
+async def test_async_for():
+ async for _ in pkg.async_func(): # $ use=moduleImport("pkg").getMember("async_func").getReturn()
+ pass
+
+ coro = pkg.async_func() # $ use=moduleImport("pkg").getMember("async_func").getReturn()
+ async for _ in coro: # $ use=moduleImport("pkg").getMember("async_func").getReturn()
+ pass
+
def check_annotations():
# Just to make sure how annotations should look like :)
result = pkg.sync_func() # $ use=moduleImport("pkg").getMember("sync_func").getReturn()
From f91e43c068f22a2346bc9b46c34a2bc504490c36 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Tue, 26 Oct 2021 10:43:06 +0200
Subject: [PATCH 121/471] Python: Add more honest test for `awaited`
---
.../dataflow/ApiGraphs/async_test.py | 18 ++++++-------
.../dataflow/ApiGraphs/awaited.expected | 0
.../dataflow/ApiGraphs/awaited.ql | 26 +++++++++++++++++++
3 files changed, 35 insertions(+), 9 deletions(-)
create mode 100644 python/ql/test/experimental/dataflow/ApiGraphs/awaited.expected
create mode 100644 python/ql/test/experimental/dataflow/ApiGraphs/awaited.ql
diff --git a/python/ql/test/experimental/dataflow/ApiGraphs/async_test.py b/python/ql/test/experimental/dataflow/ApiGraphs/async_test.py
index ba6073d0703..03ab6b6ca64 100644
--- a/python/ql/test/experimental/dataflow/ApiGraphs/async_test.py
+++ b/python/ql/test/experimental/dataflow/ApiGraphs/async_test.py
@@ -3,24 +3,24 @@ import pkg # $ use=moduleImport("pkg")
async def foo():
coro = pkg.async_func() # $ use=moduleImport("pkg").getMember("async_func").getReturn()
coro # $ use=moduleImport("pkg").getMember("async_func").getReturn()
- result = await coro # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited()
- result # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited()
- return result # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited()
+ result = await coro # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited() awaited=moduleImport("pkg").getMember("async_func").getReturn()
+ result # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited() awaited=moduleImport("pkg").getMember("async_func").getReturn()
+ return result # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited() awaited=moduleImport("pkg").getMember("async_func").getReturn()
async def bar():
- result = await pkg.async_func() # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited()
- return result # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited()
+ result = await pkg.async_func() # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited() awaited=moduleImport("pkg").getMember("async_func").getReturn()
+ return result # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited() awaited=moduleImport("pkg").getMember("async_func").getReturn()
async def test_async_with():
- async with pkg.async_func() as result: # $ use=moduleImport("pkg").getMember("async_func").getReturn()
- return result # $ use=moduleImport("pkg").getMember("async_func").getReturn()
+ async with pkg.async_func() as result: # $ use=moduleImport("pkg").getMember("async_func").getReturn() awaited=moduleImport("pkg").getMember("async_func").getReturn()
+ return result # $ use=moduleImport("pkg").getMember("async_func").getReturn() awaited=moduleImport("pkg").getMember("async_func").getReturn()
async def test_async_for():
- async for _ in pkg.async_func(): # $ use=moduleImport("pkg").getMember("async_func").getReturn()
+ async for _ in pkg.async_func(): # $ use=moduleImport("pkg").getMember("async_func").getReturn() awaited=moduleImport("pkg").getMember("async_func").getReturn()
pass
coro = pkg.async_func() # $ use=moduleImport("pkg").getMember("async_func").getReturn()
- async for _ in coro: # $ use=moduleImport("pkg").getMember("async_func").getReturn()
+ async for _ in coro: # $ use=moduleImport("pkg").getMember("async_func").getReturn() MISSING: awaited=moduleImport("pkg").getMember("async_func").getReturn()
pass
def check_annotations():
diff --git a/python/ql/test/experimental/dataflow/ApiGraphs/awaited.expected b/python/ql/test/experimental/dataflow/ApiGraphs/awaited.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/python/ql/test/experimental/dataflow/ApiGraphs/awaited.ql b/python/ql/test/experimental/dataflow/ApiGraphs/awaited.ql
new file mode 100644
index 00000000000..aa140431d2f
--- /dev/null
+++ b/python/ql/test/experimental/dataflow/ApiGraphs/awaited.ql
@@ -0,0 +1,26 @@
+import python
+import semmle.python.dataflow.new.DataFlow
+import TestUtilities.InlineExpectationsTest
+import semmle.python.ApiGraphs
+
+class AwaitedTest extends InlineExpectationsTest {
+ AwaitedTest() { this = "AwaitedTest" }
+
+ override string getARelevantTag() { result = "awaited" }
+
+ override predicate hasActualResult(Location location, string element, string tag, string value) {
+ exists(API::Node a, DataFlow::Node n, API::Node pred |
+ a = pred.getAwaited() and
+ n = a.getAUse() and
+ location = n.getLocation() and
+ // Module variable nodes have no suitable location, so it's best to simply exclude them entirely
+ // from the inline tests.
+ not n instanceof DataFlow::ModuleVariableNode and
+ exists(location.getFile().getRelativePath())
+ |
+ tag = "awaited" and
+ value = pred.getPath() and
+ element = n.toString()
+ )
+ }
+}
From 8a81d42e6fad0ed98726a3652b02e9c8737f689c Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Tue, 26 Oct 2021 10:57:27 +0200
Subject: [PATCH 122/471] Python: more logic adjustment Not sure why the
missing result is missing. There is and edge with label `getAwaited` from
`pkg.async_func` on line 22 to `coro` on line 23.
---
python/ql/lib/semmle/python/ApiGraphs.qll | 14 +++++++-------
python/ql/lib/semmle/python/frameworks/Asyncpg.qll | 14 +++++++-------
.../experimental/dataflow/ApiGraphs/async_test.py | 2 +-
3 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/python/ql/lib/semmle/python/ApiGraphs.qll b/python/ql/lib/semmle/python/ApiGraphs.qll
index 8d585defa5c..a93c56f94dc 100644
--- a/python/ql/lib/semmle/python/ApiGraphs.qll
+++ b/python/ql/lib/semmle/python/ApiGraphs.qll
@@ -494,26 +494,26 @@ module API {
// - `awaitedValue` is `x`
// - `result` is `await x`
exists(Await await |
- result.asExpr() = await and
- await.getValue() = awaitedValue.asExpr()
+ await.getValue() = awaitedValue.asExpr() and
+ result.asExpr() = await
)
or
// `async for x in l`
// - `awaitedValue` is `l`
- // - `result` is `l` (should perhaps be `x`, but that should really be a read)
+ // - `result` is `l` (`x` is behind a read step)
exists(AsyncFor asyncFor |
- result.asExpr() = asyncFor.getIter() and
// To consider `x` the result of awaiting, we would use asyncFor.getTarget() = awaitedValue.asExpr(),
// but that is behind a read step rather than a flow step.
- asyncFor.getIter() = awaitedValue.asExpr()
+ asyncFor.getIter() = awaitedValue.asExpr() and
+ result.asExpr() = asyncFor.getIter()
)
or
// `async with x as y`
// - `awaitedValue` is `x`
// - `result` is `x` and `y` if it exists
exists(AsyncWith asyncWith |
- result.asExpr() = asyncWith.getContextExpr() and
- awaitedValue.asExpr() in [
+ awaitedValue.asExpr() = asyncWith.getContextExpr() and
+ result.asExpr() in [
// `x`
asyncWith.getContextExpr(),
// `y`, if it exists
diff --git a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
index 13af1bdde20..7e8e94e0897 100644
--- a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
+++ b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
@@ -75,26 +75,26 @@ private module Asyncpg {
// - `awaitedValue` is `x`
// - `result` is `await x`
exists(Await await |
- result.asExpr() = await and
- await.getValue() = awaitedValue.asExpr()
+ await.getValue() = awaitedValue.asExpr() and
+ result.asExpr() = await
)
or
// `async for x in l`
// - `awaitedValue` is local source of `l`
// - `result` is `l`
exists(AsyncFor asyncFor, DataFlow::Node awaited |
- result.asExpr() = asyncFor.getIter() and
asyncFor.getIter() = awaited.asExpr() and
- awaited.getALocalSource() = awaitedValue
+ awaited.getALocalSource() = awaitedValue and
+ result.asExpr() = asyncFor.getIter()
)
or
// `async with x as y`
// - `awaitedValue` is local source of `x`
// - `result` is `x` and `y`
exists(AsyncWith asyncWith, DataFlow::Node awaited |
- result.asExpr() = asyncWith.getContextExpr() and
- awaited.asExpr() in [asyncWith.getContextExpr(), asyncWith.getOptionalVars()] and
- awaited.getALocalSource() = awaitedValue
+ awaited.asExpr() = asyncWith.getContextExpr() and
+ awaited.getALocalSource() = awaitedValue and
+ result.asExpr() in [asyncWith.getContextExpr(), asyncWith.getOptionalVars()]
)
}
diff --git a/python/ql/test/experimental/dataflow/ApiGraphs/async_test.py b/python/ql/test/experimental/dataflow/ApiGraphs/async_test.py
index 03ab6b6ca64..b962a0eae49 100644
--- a/python/ql/test/experimental/dataflow/ApiGraphs/async_test.py
+++ b/python/ql/test/experimental/dataflow/ApiGraphs/async_test.py
@@ -12,7 +12,7 @@ async def bar():
return result # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited() awaited=moduleImport("pkg").getMember("async_func").getReturn()
async def test_async_with():
- async with pkg.async_func() as result: # $ use=moduleImport("pkg").getMember("async_func").getReturn() awaited=moduleImport("pkg").getMember("async_func").getReturn()
+ async with pkg.async_func() as result: # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited() awaited=moduleImport("pkg").getMember("async_func").getReturn()
return result # $ use=moduleImport("pkg").getMember("async_func").getReturn() awaited=moduleImport("pkg").getMember("async_func").getReturn()
async def test_async_for():
From 44db920f106edf0a4ae8ba566f7632959d9f7c75 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Mon, 4 Oct 2021 16:04:43 +0200
Subject: [PATCH 123/471] refactor, cleanup, and improvements in experimental
cookie queries
---
.../CWE-1004/CookieWithoutHttpOnly.ql | 4 +-
.../Security/CWE-614/InsecureCookie.ql | 2 +-
.../javascript/security/InsecureCookie.qll | 201 +++++-------------
.../CWE-1004/CookieWithoutHttpOnly.expected | 7 +-
.../Security/CWE-1004/test_httpserver.js | 10 +-
.../Security/CWE-1004/test_responseCookie.js | 32 +--
.../Security/CWE-614/InsecureCookies.expected | 5 +-
7 files changed, 88 insertions(+), 173 deletions(-)
diff --git a/javascript/ql/src/experimental/Security/CWE-1004/CookieWithoutHttpOnly.ql b/javascript/ql/src/experimental/Security/CWE-1004/CookieWithoutHttpOnly.ql
index bcce52ed8ec..31207f6a578 100644
--- a/javascript/ql/src/experimental/Security/CWE-1004/CookieWithoutHttpOnly.ql
+++ b/javascript/ql/src/experimental/Security/CWE-1004/CookieWithoutHttpOnly.ql
@@ -15,6 +15,6 @@
import javascript
import experimental.semmle.javascript.security.InsecureCookie::Cookie
-from Cookie cookie
-where cookie.isAuthNotHttpOnly()
+from CookieWrite cookie
+where cookie.isSensitive() and not cookie.isHttpOnly()
select cookie, "Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie."
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql
index 789a7ec53e2..093535d0bbe 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql
@@ -13,6 +13,6 @@
import javascript
import experimental.semmle.javascript.security.InsecureCookie::Cookie
-from Cookie cookie
+from CookieWrite cookie
where not cookie.isSecure()
select cookie, "Cookie is added to response without the 'secure' flag being set to true"
diff --git a/javascript/ql/src/experimental/semmle/javascript/security/InsecureCookie.qll b/javascript/ql/src/experimental/semmle/javascript/security/InsecureCookie.qll
index 0b3406c9dd9..94f8a8df9a7 100644
--- a/javascript/ql/src/experimental/semmle/javascript/security/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/semmle/javascript/security/InsecureCookie.qll
@@ -5,7 +5,12 @@
*/
import javascript
+private import semmle.javascript.security.SensitiveActions
+// TODO: Move this entire file into stdlib.
+// TODO: make "session", "auth", a sensitive name.
+// TODO: Have helper predicate that selects the relevant Sensitive Classifications.
+// TODO: Look for more cookie libraries.
module Cookie {
/**
* `secure` property of the cookie options.
@@ -18,70 +23,32 @@ module Cookie {
string httpOnlyFlag() { result = "httpOnly" }
/**
- * Abstract class to represent different cases of insecure cookie settings.
+ * A write to a cookie.
*/
- abstract class Cookie extends DataFlow::Node {
+ abstract class CookieWrite extends DataFlow::Node {
/**
- * Gets the name of the middleware/library used to set the cookie.
- */
- abstract string getKind();
-
- /**
- * Gets the options used to set this cookie, if any.
- */
- abstract DataFlow::Node getCookieOptionsArgument();
-
- /**
- * Holds if this cookie is secure.
+ * Holds if this cookie is secure, i.e. only transmitted over SSL.
*/
abstract predicate isSecure();
/**
- * Holds if this cookie is HttpOnly.
+ * Holds if this cookie is HttpOnly, i.e. not accessible by JavaScript.
*/
abstract predicate isHttpOnly();
/**
- * Holds if the cookie is authentication sensitive and lacks HttpOnly.
+ * Holds if the cookie is likely an authentication cookie or otherwise sensitive.
*/
- abstract predicate isAuthNotHttpOnly();
- }
-
- /**
- * Holds if the expression is a variable with a sensitive name.
- */
- private predicate isAuthVariable(DataFlow::Node expr) {
- exists(string val |
- (
- val = expr.getStringValue() or
- val = expr.asExpr().(VarAccess).getName() or
- val = expr.(DataFlow::PropRead).getPropertyName()
- ) and
- regexpMatchAuth(val)
- )
- or
- isAuthVariable(expr.getAPredecessor())
- }
-
- /**
- * Holds if `val` looks related to authentication, without being an anti-forgery token.
- */
- bindingset[val]
- private predicate regexpMatchAuth(string val) {
- val.regexpMatch("(?i).*(session|login|token|user|auth|credential).*") and
- not val.regexpMatch("(?i).*(xsrf|csrf|forgery).*")
+ abstract predicate isSensitive();
}
/**
* A cookie set using the `express` module `cookie-session` (https://github.com/expressjs/cookie-session).
*/
- class InsecureCookieSession extends ExpressLibraries::CookieSession::MiddlewareInstance, Cookie {
- override string getKind() { result = "cookie-session" }
-
- override DataFlow::SourceNode getCookieOptionsArgument() { result.flowsTo(getArgument(0)) }
-
+ class InsecureCookieSession extends ExpressLibraries::CookieSession::MiddlewareInstance,
+ CookieWrite {
private DataFlow::Node getCookieFlagValue(string flag) {
- result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
+ result = this.getOptionArgument(0, flag)
}
override predicate isSecure() {
@@ -90,8 +57,8 @@ module Cookie {
not getCookieFlagValue(secureFlag()).mayHaveBooleanValue(false)
}
- override predicate isAuthNotHttpOnly() {
- not isHttpOnly() // It is a session cookie, likely auth sensitive
+ override predicate isSensitive() {
+ any() // It is a session cookie, likely auth sensitive
}
override predicate isHttpOnly() {
@@ -105,13 +72,9 @@ module Cookie {
* A cookie set using the `express` module `express-session` (https://github.com/expressjs/session).
*/
class InsecureExpressSessionCookie extends ExpressLibraries::ExpressSession::MiddlewareInstance,
- Cookie {
- override string getKind() { result = "express-session" }
-
- override DataFlow::SourceNode getCookieOptionsArgument() { result = this.getOption("cookie") }
-
+ CookieWrite {
private DataFlow::Node getCookieFlagValue(string flag) {
- result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
+ result = this.getOption("cookie").getALocalSource().getAPropertyWrite(flag).getRhs()
}
override predicate isSecure() {
@@ -122,8 +85,8 @@ module Cookie {
getCookieFlagValue(secureFlag()).mayHaveStringValue("auto")
}
- override predicate isAuthNotHttpOnly() {
- not isHttpOnly() // It is a session cookie, likely auth sensitive
+ override predicate isSensitive() {
+ any() // It is a session cookie, likely auth sensitive
}
override predicate isHttpOnly() {
@@ -137,17 +100,11 @@ module Cookie {
/**
* A cookie set using `response.cookie` from `express` module (https://expressjs.com/en/api.html#res.cookie).
*/
- class InsecureExpressCookieResponse extends Cookie, DataFlow::MethodCallNode {
+ class InsecureExpressCookieResponse extends CookieWrite, DataFlow::MethodCallNode {
InsecureExpressCookieResponse() { this.calls(any(Express::ResponseExpr r).flow(), "cookie") }
- override string getKind() { result = "response.cookie" }
-
- override DataFlow::SourceNode getCookieOptionsArgument() {
- result = this.getLastArgument().getALocalSource()
- }
-
private DataFlow::Node getCookieFlagValue(string flag) {
- result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
+ result = this.getOptionArgument(this.getNumArgument() - 1, flag)
}
override predicate isSecure() {
@@ -156,9 +113,12 @@ module Cookie {
getCookieFlagValue(secureFlag()).mayHaveBooleanValue(true)
}
- override predicate isAuthNotHttpOnly() {
- isAuthVariable(this.getArgument(0)) and
- not isHttpOnly()
+ override predicate isSensitive() {
+ HeuristicNames::nameIndicatesSensitiveData(any(string s |
+ this.getArgument(0).mayHaveStringValue(s)
+ ), _)
+ or
+ this.getArgument(0).asExpr() instanceof SensitiveExpr
}
override predicate isHttpOnly() {
@@ -168,101 +128,56 @@ module Cookie {
}
}
- private class AttributeToSetCookieHeaderTrackingConfig extends TaintTracking::Configuration {
- AttributeToSetCookieHeaderTrackingConfig() { this = "AttributeToSetCookieHeaderTrackingConfig" }
-
- override predicate isSource(DataFlow::Node source) {
- exists(string s | source.mayHaveStringValue(s))
- }
-
- override predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof TemplateLiteral }
- }
-
- private class SensitiveNameToSetCookieHeaderTrackingConfig extends TaintTracking::Configuration {
- SensitiveNameToSetCookieHeaderTrackingConfig() {
- this = "SensitiveNameToSetCookieHeaderTrackingConfig"
- }
-
- override predicate isSource(DataFlow::Node source) { isAuthVariable(source) }
-
- override predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof TemplateLiteral }
- }
-
/**
* A cookie set using `Set-Cookie` header of an `HTTP` response.
* (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie).
* In case an array is passed `setHeader("Set-Cookie", [...]` it sets multiple cookies.
* Each array element has its own attributes.
*/
- class InsecureSetCookieHeader extends Cookie {
+ class InsecureSetCookieHeader extends CookieWrite {
InsecureSetCookieHeader() {
- this.asExpr() = any(HTTP::SetCookieHeader setCookie).getHeaderArgument()
- }
-
- override string getKind() { result = "set-cookie header" }
-
- override DataFlow::Node getCookieOptionsArgument() {
- if this.asExpr() instanceof ArrayExpr
- then result.asExpr() = this.asExpr().(ArrayExpr).getAnElement()
- else result.asExpr() = this.asExpr()
+ this.asExpr() = any(HTTP::SetCookieHeader setCookie).getHeaderArgument() and
+ not this instanceof DataFlow::ArrayCreationNode
+ or
+ this =
+ any(HTTP::SetCookieHeader setCookie)
+ .getHeaderArgument()
+ .flow()
+ .(DataFlow::ArrayCreationNode)
+ .getAnElement()
}
/**
* A cookie is secure if the `secure` flag is specified in the cookie definition.
* The default is `false`.
*/
- override predicate isSecure() { allHaveCookieAttribute("secure") }
+ override predicate isSecure() { hasCookieAttribute("secure") }
/**
* A cookie is httpOnly if the `httpOnly` flag is specified in the cookie definition.
* The default is `false`.
*/
- override predicate isHttpOnly() { allHaveCookieAttribute(httpOnlyFlag()) }
+ override predicate isHttpOnly() { hasCookieAttribute(httpOnlyFlag()) }
/**
- * The predicate holds only if all elements have the specified attribute.
+ * The predicate holds only if any element have the specified attribute.
*/
bindingset[attribute]
- private predicate allHaveCookieAttribute(string attribute) {
- forall(DataFlow::Node n | n = getCookieOptionsArgument() |
- exists(string s |
- n.mayHaveStringValue(s) and
- hasCookieAttribute(s, attribute)
- )
- or
- exists(AttributeToSetCookieHeaderTrackingConfig cfg, DataFlow::Node source |
- cfg.hasFlow(source, n) and
- exists(string attr |
- source.mayHaveStringValue(attr) and
- attr.regexpMatch("(?i).*\\b" + attribute + "\\b.*")
- )
- )
+ private predicate hasCookieAttribute(string attribute) {
+ exists(string s |
+ this.mayHaveStringValue(s) and
+ hasCookieAttribute(s, attribute)
)
}
/**
- * The predicate holds only if any element has a sensitive name and
- * doesn't have the `httpOnly` flag.
+ * The predicate holds only if any element has a sensitive name.
*/
- override predicate isAuthNotHttpOnly() {
- exists(DataFlow::Node n | n = getCookieOptionsArgument() |
- exists(string s |
- n.mayHaveStringValue(s) and
- (
- not hasCookieAttribute(s, httpOnlyFlag()) and
- regexpMatchAuth(getCookieName(s))
- )
- )
- or
- not exists(AttributeToSetCookieHeaderTrackingConfig cfg, DataFlow::Node source |
- cfg.hasFlow(source, n) and
- exists(string attr |
- source.mayHaveStringValue(attr) and
- attr.regexpMatch("(?i).*\\b" + httpOnlyFlag() + "\\b.*")
- )
- ) and
- exists(SensitiveNameToSetCookieHeaderTrackingConfig cfg | cfg.hasFlow(_, n))
- )
+ override predicate isSensitive() {
+ HeuristicNames::nameIndicatesSensitiveData(getCookieName([
+ any(string s | this.mayHaveStringValue(s)),
+ this.(StringOps::ConcatenationRoot).getConstantStringParts()
+ ]), _)
}
/**
@@ -271,7 +186,9 @@ module Cookie {
* `=; Domain=; Secure; HttpOnly`
*/
bindingset[s]
- private string getCookieName(string s) { result = s.regexpCapture("\\s*([^=\\s]*)\\s*=.*", 1) }
+ private string getCookieName(string s) {
+ result = s.regexpCapture("\\s*\\b([^=\\s]*)\\b\\s*=.*", 1)
+ }
/**
* Holds if the `Set-Cookie` header value contains the specified attribute
@@ -284,14 +201,14 @@ module Cookie {
*/
bindingset[s, attribute]
private predicate hasCookieAttribute(string s, string attribute) {
- s.regexpMatch("(?i).*;\\s*" + attribute + "\\s*;?.*$")
+ s.regexpMatch("(?i).*;\\s*" + attribute + "\\b\\s*;?.*$")
}
}
/**
* A cookie set using `js-cookie` library (https://github.com/js-cookie/js-cookie).
*/
- class InsecureJsCookie extends Cookie {
+ class InsecureJsCookie extends CookieWrite {
InsecureJsCookie() {
this =
[
@@ -301,9 +218,7 @@ module Cookie {
].getAMemberCall("set")
}
- override string getKind() { result = "js-cookie" }
-
- override DataFlow::SourceNode getCookieOptionsArgument() {
+ DataFlow::SourceNode getCookieOptionsArgument() {
result = this.(DataFlow::CallNode).getAnArgument().getALocalSource()
}
@@ -316,7 +231,7 @@ module Cookie {
getCookieFlagValue(secureFlag()).mayHaveBooleanValue(true)
}
- override predicate isAuthNotHttpOnly() { none() }
+ override predicate isSensitive() { none() }
override predicate isHttpOnly() { none() } // js-cookie is browser side library and doesn't support HttpOnly
}
diff --git a/javascript/ql/test/query-tests/Security/CWE-1004/CookieWithoutHttpOnly.expected b/javascript/ql/test/query-tests/Security/CWE-1004/CookieWithoutHttpOnly.expected
index fb523cbb315..27ab7e48b0e 100644
--- a/javascript/ql/test/query-tests/Security/CWE-1004/CookieWithoutHttpOnly.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-1004/CookieWithoutHttpOnly.expected
@@ -5,10 +5,9 @@
| test_cookie-session.js:52:9:56:2 | session ... BAD\\n}) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
| test_express-session.js:11:9:15:2 | session ... BAD\\n}) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
| test_express-session.js:28:9:32:2 | session ... tter\\n}) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| test_httpserver.js:7:37:7:48 | "auth=ninja" | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| test_httpserver.js:27:37:27:70 | ["auth= ... cript"] | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| test_httpserver.js:57:37:57:80 | ["auth= ... cript"] | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| test_httpserver.js:87:37:87:59 | `sessio ... {attr}` | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
+| test_httpserver.js:7:37:7:51 | "authKey=ninja" | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
+| test_httpserver.js:27:38:27:52 | "authKey=ninja" | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
+| test_httpserver.js:87:37:87:59 | `authKe ... {attr}` | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
| test_responseCookie.js:15:5:20:10 | res.coo ... }) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
| test_responseCookie.js:25:5:28:10 | res.coo ... }) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
| test_responseCookie.js:48:5:48:43 | res.coo ... ptions) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
diff --git a/javascript/ql/test/query-tests/Security/CWE-1004/test_httpserver.js b/javascript/ql/test/query-tests/Security/CWE-1004/test_httpserver.js
index 721a24a55e8..2f15de83cc7 100644
--- a/javascript/ql/test/query-tests/Security/CWE-1004/test_httpserver.js
+++ b/javascript/ql/test/query-tests/Security/CWE-1004/test_httpserver.js
@@ -4,7 +4,7 @@ function test1() {
const server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'text/html');
// BAD
- res.setHeader("Set-Cookie", "auth=ninja");
+ res.setHeader("Set-Cookie", "authKey=ninja");
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('ok');
});
@@ -24,7 +24,7 @@ function test3() {
const server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'text/html');
// BAD
- res.setHeader("Set-Cookie", ["auth=ninja", "token=javascript"]);
+ res.setHeader("Set-Cookie", ["authKey=ninja", "token=javascript"]);
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('ok');
});
@@ -53,8 +53,8 @@ function test5() {
function test6() {
const server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'text/html');
- // BAD
- res.setHeader("Set-Cookie", ["auth=ninja; httponly", "token=javascript"]);
+ // OK - the sensitive cookie has httpOnly set
+ res.setHeader("Set-Cookie", ["authKey=ninja; httponly", "token=javascript"]);
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('ok');
});
@@ -84,7 +84,7 @@ function test9() {
const server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'text/html');
let attr = "; secure"
- res.setHeader("Set-Cookie", `session=ninja ${attr}`); // Bad, not httponly string expression
+ res.setHeader("Set-Cookie", `authKey=ninja ${attr}`); // Bad, not httponly string expression
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('ok');
});
diff --git a/javascript/ql/test/query-tests/Security/CWE-1004/test_responseCookie.js b/javascript/ql/test/query-tests/Security/CWE-1004/test_responseCookie.js
index b27e8d6a568..406703243f8 100644
--- a/javascript/ql/test/query-tests/Security/CWE-1004/test_responseCookie.js
+++ b/javascript/ql/test/query-tests/Security/CWE-1004/test_responseCookie.js
@@ -2,7 +2,7 @@ const express = require('express')
const app = express()
app.get('/a', function (req, res, next) {
- res.cookie('session', 'value',
+ res.cookie('authkey', 'value',
{
maxAge: 9000000000,
httpOnly: true, // GOOD
@@ -12,7 +12,7 @@ app.get('/a', function (req, res, next) {
})
app.get('/a', function (req, res, next) {
- res.cookie('session', 'value',
+ res.cookie('authkey', 'value',
{
maxAge: 9000000000,
httpOnly: false, // BAD
@@ -22,7 +22,7 @@ app.get('/a', function (req, res, next) {
})
app.get('/a', function (req, res, next) {
- res.cookie('session', 'value',
+ res.cookie('authkey', 'value',
{
maxAge: 9000000000
});
@@ -35,7 +35,7 @@ app.get('/a', function (req, res, next) {
httpOnly: true, // GOOD
secure: false
}
- res.cookie('session', 'value', options);
+ res.cookie('authkey', 'value', options);
res.end('ok')
})
@@ -45,7 +45,7 @@ app.get('/a', function (req, res, next) {
httpOnly: false, // BAD
secure: false
}
- res.cookie('session', 'value', options);
+ res.cookie('authkey', 'value', options);
res.end('ok')
})
@@ -53,7 +53,7 @@ app.get('/a', function (req, res, next) {
let options = {
maxAge: 9000000000
}
- res.cookie('session', 'value', options); // BAD
+ res.cookie('authkey', 'value', options); // BAD
res.end('ok')
})
@@ -62,7 +62,7 @@ app.get('/a', function (req, res, next) {
maxAge: 9000000000
}
options.httpOnly = false;
- res.cookie('session', 'value', options); // BAD
+ res.cookie('authkey', 'value', options); // BAD
res.end('ok')
})
@@ -71,7 +71,7 @@ app.get('/a', function (req, res, next) {
maxAge: 9000000000
}
options.httpOnly = true;
- res.cookie('session', 'value', options); // GOOD
+ res.cookie('authkey', 'value', options); // GOOD
res.end('ok')
})
@@ -81,7 +81,7 @@ app.get('/a', function (req, res, next) {
httpOnly: false,
}
options.httpOnly = false;
- res.cookie('session', 'value', options); // BAD
+ res.cookie('authkey', 'value', options); // BAD
res.end('ok')
})
@@ -91,8 +91,8 @@ app.get('/a', function (req, res, next) {
httpOnly: false,
}
options.httpOnly = false;
- let session = "blabla"
- res.cookie(session, 'value', options); // BAD, var name likely auth related
+ let authKey = "blabla"
+ res.cookie(authKey, 'value', options); // BAD, var name likely auth related
res.end('ok')
})
@@ -102,8 +102,8 @@ app.get('/a', function (req, res, next) {
httpOnly: false,
}
options.httpOnly = false;
- let o = { session: "blabla" }
- res.cookie(o.session, 'value', options); // BAD, var name likely auth related
+ let o = { authKey: "blabla" }
+ res.cookie(o.authKey, 'value', options); // BAD, var name likely auth related
res.end('ok')
})
@@ -113,7 +113,7 @@ app.get('/a', function (req, res, next) {
httpOnly: false,
}
options.httpOnly = false;
- let blabla = "session"
+ let blabla = "authKey"
res.cookie(blabla, 'value', options); // BAD, var name likely auth related
res.end('ok')
})
@@ -124,7 +124,7 @@ app.get('/a', function (req, res, next) {
httpOnly: true,
}
options.httpOnly = true;
- res.cookie('session', 'value', options); // GOOD
+ res.cookie('authkey', 'value', options); // GOOD
res.end('ok')
})
@@ -134,7 +134,7 @@ app.get('/a', function (req, res, next) {
httpOnly: false,
}
options.httpOnly = true;
- res.cookie('session', 'value', options); // GOOD
+ res.cookie('authkey', 'value', options); // GOOD
res.end('ok')
})
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.expected b/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.expected
index 6ea91cc8f94..18d0c7ca02b 100644
--- a/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.expected
@@ -4,8 +4,9 @@
| test_express-session.js:15:9:18:2 | session ... T OK\\n}) | Cookie is added to response without the 'secure' flag being set to true |
| test_express-session.js:25:9:25:21 | session(sess) | Cookie is added to response without the 'secure' flag being set to true |
| test_httpserver.js:7:37:7:48 | "type=ninja" | Cookie is added to response without the 'secure' flag being set to true |
-| test_httpserver.js:27:37:27:73 | ["type= ... cript"] | Cookie is added to response without the 'secure' flag being set to true |
-| test_httpserver.js:57:37:57:81 | ["type= ... cript"] | Cookie is added to response without the 'secure' flag being set to true |
+| test_httpserver.js:27:38:27:49 | "type=ninja" | Cookie is added to response without the 'secure' flag being set to true |
+| test_httpserver.js:27:52:27:72 | "langua ... script" | Cookie is added to response without the 'secure' flag being set to true |
+| test_httpserver.js:57:60:57:80 | "langua ... script" | Cookie is added to response without the 'secure' flag being set to true |
| test_jscookie.js:2:1:2:48 | js_cook ... alse }) | Cookie is added to response without the 'secure' flag being set to true |
| test_responseCookie.js:5:5:10:10 | res.coo ... }) | Cookie is added to response without the 'secure' flag being set to true |
| test_responseCookie.js:20:5:20:40 | res.coo ... ptions) | Cookie is added to response without the 'secure' flag being set to true |
From 26a24a38954c645fa0ab5526709694c8a8cd77cd Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Mon, 4 Oct 2021 22:41:48 +0200
Subject: [PATCH 124/471] prepare move to non-experimental
---
.../javascript/frameworks/CookieLibraries.qll | 28 +++++++++++++++++++
.../CWE-1004/CookieWithoutHttpOnly.ql | 15 +++++++---
.../Security/CWE-614/InsecureCookie.ql | 11 +++++---
3 files changed, 46 insertions(+), 8 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
index 11b23858ae0..8f261c39ec9 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
@@ -4,6 +4,31 @@
import javascript
+/**
+ * Classes and predicates for reasoning about writes to cookies.
+ */
+module CookieWrites {
+ /**
+ * A write to a cookie.
+ */
+ abstract class CookieWrite extends DataFlow::Node {
+ /**
+ * Holds if this cookie is secure, i.e. only transmitted over SSL.
+ */
+ abstract predicate isSecure();
+
+ /**
+ * Holds if this cookie is HttpOnly, i.e. not accessible by JavaScript.
+ */
+ abstract predicate isHttpOnly();
+
+ /**
+ * Holds if the cookie is likely an authentication cookie or otherwise sensitive.
+ */
+ abstract predicate isSensitive();
+ }
+}
+
/**
* A model of the `js-cookie` library (https://github.com/js-cookie/js-cookie).
*/
@@ -26,6 +51,7 @@ private module JsCookie {
}
class WriteAccess extends PersistentWriteAccess, DataFlow::CallNode {
+ // TODO: CookieWrite
WriteAccess() { this = libMemberCall("set") }
string getKey() { getArgument(0).mayHaveStringValue(result) }
@@ -54,6 +80,7 @@ private module BrowserCookies {
}
class WriteAccess extends PersistentWriteAccess, DataFlow::CallNode {
+ // TODO: CookieWrite
WriteAccess() { this = libMemberCall("set") }
string getKey() { getArgument(0).mayHaveStringValue(result) }
@@ -82,6 +109,7 @@ private module LibCookie {
}
class WriteAccess extends PersistentWriteAccess, DataFlow::CallNode {
+ // TODO: CookieWrite
WriteAccess() { this = libMemberCall("serialize") }
string getKey() { getArgument(0).mayHaveStringValue(result) }
diff --git a/javascript/ql/src/experimental/Security/CWE-1004/CookieWithoutHttpOnly.ql b/javascript/ql/src/experimental/Security/CWE-1004/CookieWithoutHttpOnly.ql
index 31207f6a578..e70254473ca 100644
--- a/javascript/ql/src/experimental/Security/CWE-1004/CookieWithoutHttpOnly.ql
+++ b/javascript/ql/src/experimental/Security/CWE-1004/CookieWithoutHttpOnly.ql
@@ -13,8 +13,15 @@
*/
import javascript
-import experimental.semmle.javascript.security.InsecureCookie::Cookie
+import experimental.semmle.javascript.security.InsecureCookie::Cookie as ExperimentalCookie
-from CookieWrite cookie
-where cookie.isSensitive() and not cookie.isHttpOnly()
-select cookie, "Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie."
+from DataFlow::Node node
+where
+ exists(ExperimentalCookie::CookieWrite cookie | cookie = node |
+ cookie.isSensitive() and not cookie.isHttpOnly()
+ )
+ or
+ exists(CookieWrites::CookieWrite cookie | cookie = node |
+ cookie.isSensitive() and not cookie.isHttpOnly()
+ )
+select node, "Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie."
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql
index 093535d0bbe..f3ab11952a0 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql
+++ b/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql
@@ -11,8 +11,11 @@
*/
import javascript
-import experimental.semmle.javascript.security.InsecureCookie::Cookie
+import experimental.semmle.javascript.security.InsecureCookie::Cookie as ExperimentalCookie
-from CookieWrite cookie
-where not cookie.isSecure()
-select cookie, "Cookie is added to response without the 'secure' flag being set to true"
+from DataFlow::Node node
+where
+ exists(ExperimentalCookie::CookieWrite cookie | cookie = node | not cookie.isSecure())
+ or
+ exists(CookieWrites::CookieWrite cookie | cookie = node | not cookie.isSecure())
+select node, "Cookie is added to response without the 'secure' flag being set to true"
From 6858acc6a9c782c8b447dd4734386e5b6dbd5914 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Mon, 4 Oct 2021 22:47:25 +0200
Subject: [PATCH 125/471] port experimental cookie models to non-experimental
---
.../javascript/frameworks/CookieLibraries.qll | 178 +++++++++++++++-
.../CWE-1004/CookieWithoutHttpOnly.qhelp | 0
.../CWE-1004/CookieWithoutHttpOnly.ql | 4 +-
.../Security/CWE-614/InsecureCookie.qhelp | 0
.../Security/CWE-614/InsecureCookie.ql | 2 +-
.../javascript/security/InsecureCookie.qll | 194 ------------------
.../CWE-1004/CookieWithoutHttpOnly.qlref | 2 +-
.../Security/CWE-614/InsecureCookies.qlref | 2 +-
8 files changed, 182 insertions(+), 200 deletions(-)
rename javascript/ql/src/{experimental => }/Security/CWE-1004/CookieWithoutHttpOnly.qhelp (100%)
rename javascript/ql/src/{experimental => }/Security/CWE-1004/CookieWithoutHttpOnly.ql (82%)
rename javascript/ql/src/{experimental => }/Security/CWE-614/InsecureCookie.qhelp (100%)
rename javascript/ql/src/{experimental => }/Security/CWE-614/InsecureCookie.ql (93%)
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
index 8f261c39ec9..d1fc50e95ea 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
@@ -24,9 +24,20 @@ module CookieWrites {
/**
* Holds if the cookie is likely an authentication cookie or otherwise sensitive.
+ * Can never hold for client-side cookies. TODO: Or can it...?
*/
abstract predicate isSensitive();
}
+
+ /**
+ * The flag that indicates that a cookie is secure.
+ */
+ string secure() { result = "secure" }
+
+ /**
+ * The flag that indicates that a cookie is HttpOnly.
+ */
+ string httpOnly() { result = "httpOnly" }
}
/**
@@ -50,13 +61,21 @@ private module JsCookie {
}
}
- class WriteAccess extends PersistentWriteAccess, DataFlow::CallNode {
- // TODO: CookieWrite
+ class WriteAccess extends PersistentWriteAccess, DataFlow::CallNode, CookieWrites::CookieWrite {
WriteAccess() { this = libMemberCall("set") }
string getKey() { getArgument(0).mayHaveStringValue(result) }
override DataFlow::Node getValue() { result = getArgument(1) }
+
+ override predicate isSecure() {
+ // A cookie is secure if there are cookie options with the `secure` flag set to `true`.
+ this.getOptionArgument(2, CookieWrites::secure()).mayHaveBooleanValue(true)
+ }
+
+ override predicate isSensitive() { none() } // TODO: Maybe it can be sensitive?
+
+ override predicate isHttpOnly() { none() } // js-cookie is browser side library and doesn't support HttpOnly
}
}
@@ -117,3 +136,158 @@ private module LibCookie {
override DataFlow::Node getValue() { result = getArgument(1) }
}
}
+
+/**
+ * A model of cookies in an express application.
+ */
+private module ExpressCookies {
+ /**
+ * A cookie set using `response.cookie` from `express` module (https://expressjs.com/en/api.html#res.cookie).
+ */
+ private class InsecureExpressCookieResponse extends CookieWrites::CookieWrite,
+ DataFlow::MethodCallNode {
+ InsecureExpressCookieResponse() { this.asExpr() instanceof Express::SetCookie }
+
+ override predicate isSecure() {
+ // A cookie is secure if there are cookie options with the `secure` flag set to `true`.
+ // The default is `false`.
+ this.getOptionArgument(2, CookieWrites::secure()).mayHaveBooleanValue(true)
+ }
+
+ override predicate isSensitive() {
+ HeuristicNames::nameIndicatesSensitiveData(any(string s |
+ this.getArgument(0).mayHaveStringValue(s)
+ ), _)
+ or
+ this.getArgument(0).asExpr() instanceof SensitiveExpr
+ }
+
+ override predicate isHttpOnly() {
+ // A cookie is httpOnly if there are cookie options with the `httpOnly` flag set to `true`.
+ // The default is `false`.
+ this.getOptionArgument(2, CookieWrites::httpOnly()).mayHaveBooleanValue(true)
+ }
+ }
+
+ /**
+ * A cookie set using the `express` module `cookie-session` (https://github.com/expressjs/cookie-session).
+ */
+ class InsecureCookieSession extends ExpressLibraries::CookieSession::MiddlewareInstance,
+ CookieWrites::CookieWrite {
+ private DataFlow::Node getCookieFlagValue(string flag) {
+ result = this.getOptionArgument(0, flag)
+ }
+
+ override predicate isSecure() {
+ // The flag `secure` is set to `false` by default for HTTP, `true` by default for HTTPS (https://github.com/expressjs/cookie-session#cookie-options).
+ // A cookie is secure if the `secure` flag is not explicitly set to `false`.
+ not getCookieFlagValue(CookieWrites::secure()).mayHaveBooleanValue(false)
+ }
+
+ override predicate isSensitive() {
+ any() // It is a session cookie, likely auth sensitive
+ }
+
+ override predicate isHttpOnly() {
+ // The flag `httpOnly` is set to `true` by default (https://github.com/expressjs/cookie-session#cookie-options).
+ // A cookie is httpOnly if the `httpOnly` flag is not explicitly set to `false`.
+ not getCookieFlagValue(CookieWrites::httpOnly()).mayHaveBooleanValue(false)
+ }
+ }
+
+ /**
+ * A cookie set using the `express` module `express-session` (https://github.com/expressjs/session).
+ */
+ class InsecureExpressSessionCookie extends ExpressLibraries::ExpressSession::MiddlewareInstance,
+ CookieWrites::CookieWrite {
+ private DataFlow::Node getCookieFlagValue(string flag) {
+ result = this.getOption("cookie").getALocalSource().getAPropertyWrite(flag).getRhs()
+ }
+
+ override predicate isSecure() {
+ // The flag `secure` is not set by default (https://github.com/expressjs/session#Cookiesecure).
+ // The default value for cookie options is { path: '/', httpOnly: true, secure: false, maxAge: null }.
+ // A cookie is secure if there are the cookie options with the `secure` flag set to `true` or to `auto`.
+ getCookieFlagValue(CookieWrites::secure()).mayHaveBooleanValue(true) or
+ getCookieFlagValue(CookieWrites::secure()).mayHaveStringValue("auto")
+ }
+
+ override predicate isSensitive() {
+ any() // It is a session cookie, likely auth sensitive
+ }
+
+ override predicate isHttpOnly() {
+ // The flag `httpOnly` is set by default (https://github.com/expressjs/session#Cookiesecure).
+ // The default value for cookie options is { path: '/', httpOnly: true, secure: false, maxAge: null }.
+ // A cookie is httpOnly if the `httpOnly` flag is not explicitly set to `false`.
+ not getCookieFlagValue(CookieWrites::httpOnly()).mayHaveBooleanValue(false)
+ }
+ }
+}
+
+/**
+ * A cookie set using `Set-Cookie` header of an `HTTP` response, where a raw header is used.
+ * (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie).
+ * This class does not model the Express implementation of `HTTP::CookieDefintion`
+ * as the express implementation does not use raw headers.
+ *
+ * In case an array is passed `setHeader("Set-Cookie", [...]` it sets multiple cookies.
+ * We model a `CookieWrite` for each array element.
+ */
+private class HTTPCookieWrite extends CookieWrites::CookieWrite {
+ string header;
+
+ HTTPCookieWrite() {
+ exists(HTTP::CookieDefinition setCookie |
+ this.asExpr() = setCookie.getHeaderArgument() and
+ not this instanceof DataFlow::ArrayCreationNode
+ or
+ this = setCookie.getHeaderArgument().flow().(DataFlow::ArrayCreationNode).getAnElement()
+ ) and
+ header =
+ [
+ any(string s | this.mayHaveStringValue(s)),
+ this.(StringOps::ConcatenationRoot).getConstantStringParts()
+ ]
+ }
+
+ override predicate isSecure() {
+ // A cookie is secure if the `secure` flag is specified in the cookie definition.
+ // The default is `false`.
+ hasCookieAttribute(header, CookieWrites::secure())
+ }
+
+ override predicate isHttpOnly() {
+ // A cookie is httpOnly if the `httpOnly` flag is specified in the cookie definition.
+ // The default is `false`.
+ hasCookieAttribute(header, CookieWrites::httpOnly())
+ }
+
+ override predicate isSensitive() {
+ HeuristicNames::nameIndicatesSensitiveData(getCookieName(header), _)
+ }
+
+ /**
+ * Gets cookie name from a `Set-Cookie` header value.
+ * The header value always starts with `=` optionally followed by attributes:
+ * `=; Domain=; Secure; HttpOnly`
+ */
+ bindingset[s]
+ private string getCookieName(string s) {
+ result = s.regexpCapture("\\s*\\b([^=\\s]*)\\b\\s*=.*", 1)
+ }
+
+ /**
+ * Holds if the `Set-Cookie` header value contains the specified attribute
+ * 1. The attribute is case insensitive
+ * 2. It always starts with a pair `=`.
+ * If the attribute is present there must be `;` after the pair.
+ * Other attributes like `Domain=`, `Path=`, etc. may come after the pair:
+ * `=; Domain=; Secure; HttpOnly`
+ * See `https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie`
+ */
+ bindingset[s, attribute]
+ private predicate hasCookieAttribute(string s, string attribute) {
+ s.regexpMatch("(?i).*;\\s*" + attribute + "\\b\\s*;?.*$")
+ }
+}
diff --git a/javascript/ql/src/experimental/Security/CWE-1004/CookieWithoutHttpOnly.qhelp b/javascript/ql/src/Security/CWE-1004/CookieWithoutHttpOnly.qhelp
similarity index 100%
rename from javascript/ql/src/experimental/Security/CWE-1004/CookieWithoutHttpOnly.qhelp
rename to javascript/ql/src/Security/CWE-1004/CookieWithoutHttpOnly.qhelp
diff --git a/javascript/ql/src/experimental/Security/CWE-1004/CookieWithoutHttpOnly.ql b/javascript/ql/src/Security/CWE-1004/CookieWithoutHttpOnly.ql
similarity index 82%
rename from javascript/ql/src/experimental/Security/CWE-1004/CookieWithoutHttpOnly.ql
rename to javascript/ql/src/Security/CWE-1004/CookieWithoutHttpOnly.ql
index e70254473ca..58ea21dbb55 100644
--- a/javascript/ql/src/experimental/Security/CWE-1004/CookieWithoutHttpOnly.ql
+++ b/javascript/ql/src/Security/CWE-1004/CookieWithoutHttpOnly.ql
@@ -13,10 +13,12 @@
*/
import javascript
-import experimental.semmle.javascript.security.InsecureCookie::Cookie as ExperimentalCookie
+import experimental.semmle.javascript.security.InsecureCookie::Cookie as ExperimentalCookie // TODO: Remove.
from DataFlow::Node node
where
+// TODO: Only for sensitive cookies? (e.g. auth cookies)
+// TODO: Give all descriptions, qlhelp, qldocs, an overhaul. Consider precisions, severity, cwes.
exists(ExperimentalCookie::CookieWrite cookie | cookie = node |
cookie.isSensitive() and not cookie.isHttpOnly()
)
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp b/javascript/ql/src/Security/CWE-614/InsecureCookie.qhelp
similarity index 100%
rename from javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp
rename to javascript/ql/src/Security/CWE-614/InsecureCookie.qhelp
diff --git a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql b/javascript/ql/src/Security/CWE-614/InsecureCookie.ql
similarity index 93%
rename from javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql
rename to javascript/ql/src/Security/CWE-614/InsecureCookie.ql
index f3ab11952a0..77be2bd106d 100644
--- a/javascript/ql/src/experimental/Security/CWE-614/InsecureCookie.ql
+++ b/javascript/ql/src/Security/CWE-614/InsecureCookie.ql
@@ -11,7 +11,7 @@
*/
import javascript
-import experimental.semmle.javascript.security.InsecureCookie::Cookie as ExperimentalCookie
+import experimental.semmle.javascript.security.InsecureCookie::Cookie as ExperimentalCookie // TODO: Remove
from DataFlow::Node node
where
diff --git a/javascript/ql/src/experimental/semmle/javascript/security/InsecureCookie.qll b/javascript/ql/src/experimental/semmle/javascript/security/InsecureCookie.qll
index 94f8a8df9a7..cde8d3f3047 100644
--- a/javascript/ql/src/experimental/semmle/javascript/security/InsecureCookie.qll
+++ b/javascript/ql/src/experimental/semmle/javascript/security/InsecureCookie.qll
@@ -41,198 +41,4 @@ module Cookie {
*/
abstract predicate isSensitive();
}
-
- /**
- * A cookie set using the `express` module `cookie-session` (https://github.com/expressjs/cookie-session).
- */
- class InsecureCookieSession extends ExpressLibraries::CookieSession::MiddlewareInstance,
- CookieWrite {
- private DataFlow::Node getCookieFlagValue(string flag) {
- result = this.getOptionArgument(0, flag)
- }
-
- override predicate isSecure() {
- // The flag `secure` is set to `false` by default for HTTP, `true` by default for HTTPS (https://github.com/expressjs/cookie-session#cookie-options).
- // A cookie is secure if the `secure` flag is not explicitly set to `false`.
- not getCookieFlagValue(secureFlag()).mayHaveBooleanValue(false)
- }
-
- override predicate isSensitive() {
- any() // It is a session cookie, likely auth sensitive
- }
-
- override predicate isHttpOnly() {
- // The flag `httpOnly` is set to `true` by default (https://github.com/expressjs/cookie-session#cookie-options).
- // A cookie is httpOnly if the `httpOnly` flag is not explicitly set to `false`.
- not getCookieFlagValue(httpOnlyFlag()).mayHaveBooleanValue(false)
- }
- }
-
- /**
- * A cookie set using the `express` module `express-session` (https://github.com/expressjs/session).
- */
- class InsecureExpressSessionCookie extends ExpressLibraries::ExpressSession::MiddlewareInstance,
- CookieWrite {
- private DataFlow::Node getCookieFlagValue(string flag) {
- result = this.getOption("cookie").getALocalSource().getAPropertyWrite(flag).getRhs()
- }
-
- override predicate isSecure() {
- // The flag `secure` is not set by default (https://github.com/expressjs/session#Cookiesecure).
- // The default value for cookie options is { path: '/', httpOnly: true, secure: false, maxAge: null }.
- // A cookie is secure if there are the cookie options with the `secure` flag set to `true` or to `auto`.
- getCookieFlagValue(secureFlag()).mayHaveBooleanValue(true) or
- getCookieFlagValue(secureFlag()).mayHaveStringValue("auto")
- }
-
- override predicate isSensitive() {
- any() // It is a session cookie, likely auth sensitive
- }
-
- override predicate isHttpOnly() {
- // The flag `httpOnly` is set by default (https://github.com/expressjs/session#Cookiesecure).
- // The default value for cookie options is { path: '/', httpOnly: true, secure: false, maxAge: null }.
- // A cookie is httpOnly if the `httpOnly` flag is not explicitly set to `false`.
- not getCookieFlagValue(httpOnlyFlag()).mayHaveBooleanValue(false)
- }
- }
-
- /**
- * A cookie set using `response.cookie` from `express` module (https://expressjs.com/en/api.html#res.cookie).
- */
- class InsecureExpressCookieResponse extends CookieWrite, DataFlow::MethodCallNode {
- InsecureExpressCookieResponse() { this.calls(any(Express::ResponseExpr r).flow(), "cookie") }
-
- private DataFlow::Node getCookieFlagValue(string flag) {
- result = this.getOptionArgument(this.getNumArgument() - 1, flag)
- }
-
- override predicate isSecure() {
- // A cookie is secure if there are cookie options with the `secure` flag set to `true`.
- // The default is `false`.
- getCookieFlagValue(secureFlag()).mayHaveBooleanValue(true)
- }
-
- override predicate isSensitive() {
- HeuristicNames::nameIndicatesSensitiveData(any(string s |
- this.getArgument(0).mayHaveStringValue(s)
- ), _)
- or
- this.getArgument(0).asExpr() instanceof SensitiveExpr
- }
-
- override predicate isHttpOnly() {
- // A cookie is httpOnly if there are cookie options with the `httpOnly` flag set to `true`.
- // The default is `false`.
- getCookieFlagValue(httpOnlyFlag()).mayHaveBooleanValue(true)
- }
- }
-
- /**
- * A cookie set using `Set-Cookie` header of an `HTTP` response.
- * (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie).
- * In case an array is passed `setHeader("Set-Cookie", [...]` it sets multiple cookies.
- * Each array element has its own attributes.
- */
- class InsecureSetCookieHeader extends CookieWrite {
- InsecureSetCookieHeader() {
- this.asExpr() = any(HTTP::SetCookieHeader setCookie).getHeaderArgument() and
- not this instanceof DataFlow::ArrayCreationNode
- or
- this =
- any(HTTP::SetCookieHeader setCookie)
- .getHeaderArgument()
- .flow()
- .(DataFlow::ArrayCreationNode)
- .getAnElement()
- }
-
- /**
- * A cookie is secure if the `secure` flag is specified in the cookie definition.
- * The default is `false`.
- */
- override predicate isSecure() { hasCookieAttribute("secure") }
-
- /**
- * A cookie is httpOnly if the `httpOnly` flag is specified in the cookie definition.
- * The default is `false`.
- */
- override predicate isHttpOnly() { hasCookieAttribute(httpOnlyFlag()) }
-
- /**
- * The predicate holds only if any element have the specified attribute.
- */
- bindingset[attribute]
- private predicate hasCookieAttribute(string attribute) {
- exists(string s |
- this.mayHaveStringValue(s) and
- hasCookieAttribute(s, attribute)
- )
- }
-
- /**
- * The predicate holds only if any element has a sensitive name.
- */
- override predicate isSensitive() {
- HeuristicNames::nameIndicatesSensitiveData(getCookieName([
- any(string s | this.mayHaveStringValue(s)),
- this.(StringOps::ConcatenationRoot).getConstantStringParts()
- ]), _)
- }
-
- /**
- * Gets cookie name from a `Set-Cookie` header value.
- * The header value always starts with `=` optionally followed by attributes:
- * `=; Domain=; Secure; HttpOnly`
- */
- bindingset[s]
- private string getCookieName(string s) {
- result = s.regexpCapture("\\s*\\b([^=\\s]*)\\b\\s*=.*", 1)
- }
-
- /**
- * Holds if the `Set-Cookie` header value contains the specified attribute
- * 1. The attribute is case insensitive
- * 2. It always starts with a pair `=`.
- * If the attribute is present there must be `;` after the pair.
- * Other attributes like `Domain=`, `Path=`, etc. may come after the pair:
- * `=; Domain=; Secure; HttpOnly`
- * See `https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie`
- */
- bindingset[s, attribute]
- private predicate hasCookieAttribute(string s, string attribute) {
- s.regexpMatch("(?i).*;\\s*" + attribute + "\\b\\s*;?.*$")
- }
- }
-
- /**
- * A cookie set using `js-cookie` library (https://github.com/js-cookie/js-cookie).
- */
- class InsecureJsCookie extends CookieWrite {
- InsecureJsCookie() {
- this =
- [
- DataFlow::globalVarRef("Cookie"),
- DataFlow::globalVarRef("Cookie").getAMemberCall("noConflict"),
- DataFlow::moduleImport("js-cookie")
- ].getAMemberCall("set")
- }
-
- DataFlow::SourceNode getCookieOptionsArgument() {
- result = this.(DataFlow::CallNode).getAnArgument().getALocalSource()
- }
-
- DataFlow::Node getCookieFlagValue(string flag) {
- result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
- }
-
- override predicate isSecure() {
- // A cookie is secure if there are cookie options with the `secure` flag set to `true`.
- getCookieFlagValue(secureFlag()).mayHaveBooleanValue(true)
- }
-
- override predicate isSensitive() { none() }
-
- override predicate isHttpOnly() { none() } // js-cookie is browser side library and doesn't support HttpOnly
- }
}
diff --git a/javascript/ql/test/query-tests/Security/CWE-1004/CookieWithoutHttpOnly.qlref b/javascript/ql/test/query-tests/Security/CWE-1004/CookieWithoutHttpOnly.qlref
index 97de8ad9476..f97b5920b84 100644
--- a/javascript/ql/test/query-tests/Security/CWE-1004/CookieWithoutHttpOnly.qlref
+++ b/javascript/ql/test/query-tests/Security/CWE-1004/CookieWithoutHttpOnly.qlref
@@ -1 +1 @@
-experimental/Security/CWE-1004/CookieWithoutHttpOnly.ql
+Security/CWE-1004/CookieWithoutHttpOnly.ql
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.qlref b/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.qlref
index 378d5dcae1a..dde95c5ad05 100644
--- a/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.qlref
+++ b/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.qlref
@@ -1 +1 @@
-experimental/Security/CWE-614/InsecureCookie.ql
+Security/CWE-614/InsecureCookie.ql
From 9193984f1b912c075dbb287583eb7edab3df2a34 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 6 Oct 2021 09:39:14 +0200
Subject: [PATCH 126/471] delete the experimental query library for cookie
queries
---
.../CWE-1004/CookieWithoutHttpOnly.ql | 9 +---
.../ql/src/Security/CWE-614/InsecureCookie.ql | 6 +--
.../javascript/security/InsecureCookie.qll | 44 -------------------
3 files changed, 3 insertions(+), 56 deletions(-)
delete mode 100644 javascript/ql/src/experimental/semmle/javascript/security/InsecureCookie.qll
diff --git a/javascript/ql/src/Security/CWE-1004/CookieWithoutHttpOnly.ql b/javascript/ql/src/Security/CWE-1004/CookieWithoutHttpOnly.ql
index 58ea21dbb55..059c3b25aa6 100644
--- a/javascript/ql/src/Security/CWE-1004/CookieWithoutHttpOnly.ql
+++ b/javascript/ql/src/Security/CWE-1004/CookieWithoutHttpOnly.ql
@@ -13,16 +13,11 @@
*/
import javascript
-import experimental.semmle.javascript.security.InsecureCookie::Cookie as ExperimentalCookie // TODO: Remove.
from DataFlow::Node node
where
-// TODO: Only for sensitive cookies? (e.g. auth cookies)
-// TODO: Give all descriptions, qlhelp, qldocs, an overhaul. Consider precisions, severity, cwes.
- exists(ExperimentalCookie::CookieWrite cookie | cookie = node |
- cookie.isSensitive() and not cookie.isHttpOnly()
- )
- or
+ // TODO: Only for sensitive cookies? (e.g. auth cookies)
+ // TODO: Give all descriptions, qlhelp, qldocs, an overhaul. Consider precisions, severity, cwes.
exists(CookieWrites::CookieWrite cookie | cookie = node |
cookie.isSensitive() and not cookie.isHttpOnly()
)
diff --git a/javascript/ql/src/Security/CWE-614/InsecureCookie.ql b/javascript/ql/src/Security/CWE-614/InsecureCookie.ql
index 77be2bd106d..fa6b8a9c84f 100644
--- a/javascript/ql/src/Security/CWE-614/InsecureCookie.ql
+++ b/javascript/ql/src/Security/CWE-614/InsecureCookie.ql
@@ -11,11 +11,7 @@
*/
import javascript
-import experimental.semmle.javascript.security.InsecureCookie::Cookie as ExperimentalCookie // TODO: Remove
from DataFlow::Node node
-where
- exists(ExperimentalCookie::CookieWrite cookie | cookie = node | not cookie.isSecure())
- or
- exists(CookieWrites::CookieWrite cookie | cookie = node | not cookie.isSecure())
+where exists(CookieWrites::CookieWrite cookie | cookie = node | not cookie.isSecure())
select node, "Cookie is added to response without the 'secure' flag being set to true"
diff --git a/javascript/ql/src/experimental/semmle/javascript/security/InsecureCookie.qll b/javascript/ql/src/experimental/semmle/javascript/security/InsecureCookie.qll
deleted file mode 100644
index cde8d3f3047..00000000000
--- a/javascript/ql/src/experimental/semmle/javascript/security/InsecureCookie.qll
+++ /dev/null
@@ -1,44 +0,0 @@
-/**
- * Provides classes for reasoning about cookies added to response without the 'secure' or 'httponly' flag being set.
- * - A cookie without the 'secure' flag being set can be intercepted and read by a malicious user.
- * - A cookie without the 'httponly' flag being set can be read by maliciously injected JavaScript.
- */
-
-import javascript
-private import semmle.javascript.security.SensitiveActions
-
-// TODO: Move this entire file into stdlib.
-// TODO: make "session", "auth", a sensitive name.
-// TODO: Have helper predicate that selects the relevant Sensitive Classifications.
-// TODO: Look for more cookie libraries.
-module Cookie {
- /**
- * `secure` property of the cookie options.
- */
- string secureFlag() { result = "secure" }
-
- /**
- * `httpOnly` property of the cookie options.
- */
- string httpOnlyFlag() { result = "httpOnly" }
-
- /**
- * A write to a cookie.
- */
- abstract class CookieWrite extends DataFlow::Node {
- /**
- * Holds if this cookie is secure, i.e. only transmitted over SSL.
- */
- abstract predicate isSecure();
-
- /**
- * Holds if this cookie is HttpOnly, i.e. not accessible by JavaScript.
- */
- abstract predicate isHttpOnly();
-
- /**
- * Holds if the cookie is likely an authentication cookie or otherwise sensitive.
- */
- abstract predicate isSensitive();
- }
-}
From 53b433779570a194e33007f7b10aced3b2e1d4d6 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 6 Oct 2021 09:57:45 +0200
Subject: [PATCH 127/471] combine test files
---
.../CWE-1004/CookieWithoutHttpOnly.ql | 1 -
.../ql/src/Security/CWE-614/InsecureCookie.ql | 1 +
.../CWE-1004/CookieWithoutHttpOnly.expected | 38 +-
.../Security/CWE-1004/test_cookie-session.js | 56 ---
.../Security/CWE-1004/test_express-session.js | 32 --
.../Security/CWE-1004/test_httpserver.js | 91 -----
.../Security/CWE-1004/test_responseCookie.js | 148 --------
.../Security/CWE-1004/tst-httpOnly.js | 324 ++++++++++++++++++
.../Security/CWE-614/InsecureCookies.expected | 24 +-
.../Security/CWE-614/test_cookie-session.js | 24 --
.../Security/CWE-614/test_express-session.js | 33 --
.../Security/CWE-614/test_httpserver.js | 61 ----
.../Security/CWE-614/test_jscookie.js | 3 -
.../Security/CWE-614/test_responseCookie.js | 33 --
.../Security/CWE-614/tst-cleartextCookie.js | 156 +++++++++
15 files changed, 512 insertions(+), 513 deletions(-)
delete mode 100644 javascript/ql/test/query-tests/Security/CWE-1004/test_cookie-session.js
delete mode 100644 javascript/ql/test/query-tests/Security/CWE-1004/test_express-session.js
delete mode 100644 javascript/ql/test/query-tests/Security/CWE-1004/test_httpserver.js
delete mode 100644 javascript/ql/test/query-tests/Security/CWE-1004/test_responseCookie.js
create mode 100644 javascript/ql/test/query-tests/Security/CWE-1004/tst-httpOnly.js
delete mode 100644 javascript/ql/test/query-tests/Security/CWE-614/test_cookie-session.js
delete mode 100644 javascript/ql/test/query-tests/Security/CWE-614/test_express-session.js
delete mode 100644 javascript/ql/test/query-tests/Security/CWE-614/test_httpserver.js
delete mode 100644 javascript/ql/test/query-tests/Security/CWE-614/test_jscookie.js
delete mode 100644 javascript/ql/test/query-tests/Security/CWE-614/test_responseCookie.js
create mode 100644 javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js
diff --git a/javascript/ql/src/Security/CWE-1004/CookieWithoutHttpOnly.ql b/javascript/ql/src/Security/CWE-1004/CookieWithoutHttpOnly.ql
index 059c3b25aa6..40198969b4f 100644
--- a/javascript/ql/src/Security/CWE-1004/CookieWithoutHttpOnly.ql
+++ b/javascript/ql/src/Security/CWE-1004/CookieWithoutHttpOnly.ql
@@ -16,7 +16,6 @@ import javascript
from DataFlow::Node node
where
- // TODO: Only for sensitive cookies? (e.g. auth cookies)
// TODO: Give all descriptions, qlhelp, qldocs, an overhaul. Consider precisions, severity, cwes.
exists(CookieWrites::CookieWrite cookie | cookie = node |
cookie.isSensitive() and not cookie.isHttpOnly()
diff --git a/javascript/ql/src/Security/CWE-614/InsecureCookie.ql b/javascript/ql/src/Security/CWE-614/InsecureCookie.ql
index fa6b8a9c84f..345c9555cb6 100644
--- a/javascript/ql/src/Security/CWE-614/InsecureCookie.ql
+++ b/javascript/ql/src/Security/CWE-614/InsecureCookie.ql
@@ -13,5 +13,6 @@
import javascript
from DataFlow::Node node
+// TODO: Only for sensitive cookies? (e.g. auth cookies)
where exists(CookieWrites::CookieWrite cookie | cookie = node | not cookie.isSecure())
select node, "Cookie is added to response without the 'secure' flag being set to true"
diff --git a/javascript/ql/test/query-tests/Security/CWE-1004/CookieWithoutHttpOnly.expected b/javascript/ql/test/query-tests/Security/CWE-1004/CookieWithoutHttpOnly.expected
index 27ab7e48b0e..6a3af033c67 100644
--- a/javascript/ql/test/query-tests/Security/CWE-1004/CookieWithoutHttpOnly.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-1004/CookieWithoutHttpOnly.expected
@@ -1,19 +1,19 @@
-| test_cookie-session.js:12:9:16:2 | session ... BAD\\n}) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| test_cookie-session.js:30:9:30:21 | session(sess) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| test_cookie-session.js:39:9:39:22 | session(sess2) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| test_cookie-session.js:48:9:48:22 | session(sess3) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| test_cookie-session.js:52:9:56:2 | session ... BAD\\n}) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| test_express-session.js:11:9:15:2 | session ... BAD\\n}) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| test_express-session.js:28:9:32:2 | session ... tter\\n}) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| test_httpserver.js:7:37:7:51 | "authKey=ninja" | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| test_httpserver.js:27:38:27:52 | "authKey=ninja" | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| test_httpserver.js:87:37:87:59 | `authKe ... {attr}` | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| test_responseCookie.js:15:5:20:10 | res.coo ... }) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| test_responseCookie.js:25:5:28:10 | res.coo ... }) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| test_responseCookie.js:48:5:48:43 | res.coo ... ptions) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| test_responseCookie.js:56:5:56:43 | res.coo ... ptions) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| test_responseCookie.js:65:5:65:43 | res.coo ... ptions) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| test_responseCookie.js:84:5:84:43 | res.coo ... ptions) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| test_responseCookie.js:95:5:95:41 | res.coo ... ptions) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| test_responseCookie.js:106:5:106:43 | res.coo ... ptions) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| test_responseCookie.js:117:5:117:40 | res.coo ... ptions) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
+| tst-httpOnly.js:11:9:15:2 | session ... BAD\\n}) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
+| tst-httpOnly.js:29:9:29:21 | session(sess) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
+| tst-httpOnly.js:38:9:38:22 | session(sess2) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
+| tst-httpOnly.js:47:9:47:22 | session(sess3) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
+| tst-httpOnly.js:51:9:55:2 | session ... BAD\\n}) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
+| tst-httpOnly.js:68:5:73:10 | res.coo ... }) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
+| tst-httpOnly.js:78:5:81:10 | res.coo ... }) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
+| tst-httpOnly.js:101:5:101:43 | res.coo ... ptions) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
+| tst-httpOnly.js:109:5:109:43 | res.coo ... ptions) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
+| tst-httpOnly.js:118:5:118:43 | res.coo ... ptions) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
+| tst-httpOnly.js:137:5:137:43 | res.coo ... ptions) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
+| tst-httpOnly.js:148:5:148:41 | res.coo ... ptions) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
+| tst-httpOnly.js:159:5:159:43 | res.coo ... ptions) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
+| tst-httpOnly.js:170:5:170:40 | res.coo ... ptions) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
+| tst-httpOnly.js:209:37:209:51 | "authKey=ninja" | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
+| tst-httpOnly.js:229:38:229:52 | "authKey=ninja" | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
+| tst-httpOnly.js:289:37:289:59 | `authKe ... {attr}` | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
+| tst-httpOnly.js:303:9:307:2 | session ... BAD\\n}) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
+| tst-httpOnly.js:320:9:324:2 | session ... tter\\n}) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
diff --git a/javascript/ql/test/query-tests/Security/CWE-1004/test_cookie-session.js b/javascript/ql/test/query-tests/Security/CWE-1004/test_cookie-session.js
deleted file mode 100644
index b29faed1cd9..00000000000
--- a/javascript/ql/test/query-tests/Security/CWE-1004/test_cookie-session.js
+++ /dev/null
@@ -1,56 +0,0 @@
-const express = require('express')
-const app = express()
-const session = require('cookie-session')
-const expiryDate = new Date(Date.now() + 60 * 60 * 1000)
-
-app.use(session({
- name: 'session',
- keys: ['key1', 'key2'],
- httpOnly: true, // GOOD
-}))
-
-app.use(session({
- name: 'session',
- keys: ['key1', 'key2'],
- httpOnly: false // BAD
-}))
-
-app.use(session({
- name: 'session',
- keys: ['key1', 'key2'],
- secure: true // GOOD, httpOnly is true by default
-}))
-
-var sess = {
- name: 'session',
- keys: ['key1', 'key2'],
-}
-
-sess.httpOnly = false;
-app.use(session(sess)) // BAD
-
-var sess2 = {
- name: 'session',
- keys: ['key1', 'key2'],
- httpOnly: true,
-}
-
-sess2.httpOnly = false;
-app.use(session(sess2)) // BAD
-
-var sess3 = {
- name: 'mycookie',
- keys: ['key1', 'key2'],
- httpOnly: true,
-}
-
-sess3.httpOnly = false;
-app.use(session(sess3)) // BAD, It is a session cookie, name doesn't matter
-
-var flag = false
-var flag2 = flag
-app.use(session({
- name: 'session',
- keys: ['key1', 'key2'],
- httpOnly: flag2 // BAD
-}))
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Security/CWE-1004/test_express-session.js b/javascript/ql/test/query-tests/Security/CWE-1004/test_express-session.js
deleted file mode 100644
index 1810055456b..00000000000
--- a/javascript/ql/test/query-tests/Security/CWE-1004/test_express-session.js
+++ /dev/null
@@ -1,32 +0,0 @@
-const express = require('express')
-const app = express()
-const session = require('express-session')
-
-app.use(session({
- name: 'session',
- keys: ['key1', 'key2'],
- cookie: { httpOnly: true }, // GOOD
-}))
-
-app.use(session({
- name: 'session',
- keys: ['key1', 'key2'],
- cookie: { httpOnly: false } // BAD
-}))
-
-app.use(session({
- name: 'session',
- keys: ['key1', 'key2'],
- cookie: { secure: true } // GOOD, httpOnly is true by default
-}))
-
-app.use(session({ // GOOD, httpOnly is true by default
- name: 'session',
- keys: ['key1', 'key2']
-}))
-
-app.use(session({
- name: 'mycookie',
- keys: ['key1', 'key2'],
- cookie: { httpOnly: false } // BAD, It is a session cookie, name doesn't matter
-}))
diff --git a/javascript/ql/test/query-tests/Security/CWE-1004/test_httpserver.js b/javascript/ql/test/query-tests/Security/CWE-1004/test_httpserver.js
deleted file mode 100644
index 2f15de83cc7..00000000000
--- a/javascript/ql/test/query-tests/Security/CWE-1004/test_httpserver.js
+++ /dev/null
@@ -1,91 +0,0 @@
-const http = require('http');
-
-function test1() {
- const server = http.createServer((req, res) => {
- res.setHeader('Content-Type', 'text/html');
- // BAD
- res.setHeader("Set-Cookie", "authKey=ninja");
- res.writeHead(200, { 'Content-Type': 'text/plain' });
- res.end('ok');
- });
-}
-
-function test2() {
- const server = http.createServer((req, res) => {
- res.setHeader('Content-Type', 'text/html');
- // GOOD
- res.setHeader("Set-Cookie", "auth=ninja; HttpOnly");
- res.writeHead(200, { 'Content-Type': 'text/plain' });
- res.end('ok');
- });
-}
-
-function test3() {
- const server = http.createServer((req, res) => {
- res.setHeader('Content-Type', 'text/html');
- // BAD
- res.setHeader("Set-Cookie", ["authKey=ninja", "token=javascript"]);
- res.writeHead(200, { 'Content-Type': 'text/plain' });
- res.end('ok');
- });
-}
-
-function test4() {
- const server = http.createServer((req, res) => {
- res.setHeader('Content-Type', 'text/html');
- // GOOD
- res.setHeader("Set-Cookie", ["auth=ninja; HttpOnly"]);
- res.writeHead(200, { 'Content-Type': 'text/plain' });
- res.end('ok');
- });
-}
-
-function test5() {
- const server = http.createServer((req, res) => {
- res.setHeader('Content-Type', 'text/html');
- // GOOD, case insensitive
- res.setHeader("Set-Cookie", ["auth=ninja; httponly"]);
- res.writeHead(200, { 'Content-Type': 'text/plain' });
- res.end('ok');
- });
-}
-
-function test6() {
- const server = http.createServer((req, res) => {
- res.setHeader('Content-Type', 'text/html');
- // OK - the sensitive cookie has httpOnly set
- res.setHeader("Set-Cookie", ["authKey=ninja; httponly", "token=javascript"]);
- res.writeHead(200, { 'Content-Type': 'text/plain' });
- res.end('ok');
- });
-}
-
-function test7() {
- const server = http.createServer((req, res) => {
- res.setHeader('Content-Type', 'text/html');
- // Good, not auth related
- res.setHeader("Set-Cookie", ["foo=ninja", "bar=javascript"]);
- res.writeHead(200, { 'Content-Type': 'text/plain' });
- res.end('ok');
- });
-}
-
-function test8() {
- const server = http.createServer((req, res) => {
- res.setHeader('Content-Type', 'text/html');
- let attr = "; httponly"
- res.setHeader("Set-Cookie", `session=ninja ${attr}`); // Good, httponly string expression
- res.writeHead(200, { 'Content-Type': 'text/plain' });
- res.end('ok');
- });
-}
-
-function test9() {
- const server = http.createServer((req, res) => {
- res.setHeader('Content-Type', 'text/html');
- let attr = "; secure"
- res.setHeader("Set-Cookie", `authKey=ninja ${attr}`); // Bad, not httponly string expression
- res.writeHead(200, { 'Content-Type': 'text/plain' });
- res.end('ok');
- });
-}
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Security/CWE-1004/test_responseCookie.js b/javascript/ql/test/query-tests/Security/CWE-1004/test_responseCookie.js
deleted file mode 100644
index 406703243f8..00000000000
--- a/javascript/ql/test/query-tests/Security/CWE-1004/test_responseCookie.js
+++ /dev/null
@@ -1,148 +0,0 @@
-const express = require('express')
-const app = express()
-
-app.get('/a', function (req, res, next) {
- res.cookie('authkey', 'value',
- {
- maxAge: 9000000000,
- httpOnly: true, // GOOD
- secure: false
- });
- res.end('ok')
-})
-
-app.get('/a', function (req, res, next) {
- res.cookie('authkey', 'value',
- {
- maxAge: 9000000000,
- httpOnly: false, // BAD
- secure: false
- });
- res.end('ok')
-})
-
-app.get('/a', function (req, res, next) {
- res.cookie('authkey', 'value',
- {
- maxAge: 9000000000
- });
- res.end('ok') // BAD
-})
-
-app.get('/a', function (req, res, next) {
- let options = {
- maxAge: 9000000000,
- httpOnly: true, // GOOD
- secure: false
- }
- res.cookie('authkey', 'value', options);
- res.end('ok')
-})
-
-app.get('/a', function (req, res, next) {
- let options = {
- maxAge: 9000000000,
- httpOnly: false, // BAD
- secure: false
- }
- res.cookie('authkey', 'value', options);
- res.end('ok')
-})
-
-app.get('/a', function (req, res, next) {
- let options = {
- maxAge: 9000000000
- }
- res.cookie('authkey', 'value', options); // BAD
- res.end('ok')
-})
-
-app.get('/a', function (req, res, next) {
- let options = {
- maxAge: 9000000000
- }
- options.httpOnly = false;
- res.cookie('authkey', 'value', options); // BAD
- res.end('ok')
-})
-
-app.get('/a', function (req, res, next) {
- let options = {
- maxAge: 9000000000
- }
- options.httpOnly = true;
- res.cookie('authkey', 'value', options); // GOOD
- res.end('ok')
-})
-
-app.get('/a', function (req, res, next) {
- let options = {
- maxAge: 9000000000,
- httpOnly: false,
- }
- options.httpOnly = false;
- res.cookie('authkey', 'value', options); // BAD
- res.end('ok')
-})
-
-app.get('/a', function (req, res, next) {
- let options = {
- maxAge: 9000000000,
- httpOnly: false,
- }
- options.httpOnly = false;
- let authKey = "blabla"
- res.cookie(authKey, 'value', options); // BAD, var name likely auth related
- res.end('ok')
-})
-
-app.get('/a', function (req, res, next) {
- let options = {
- maxAge: 9000000000,
- httpOnly: false,
- }
- options.httpOnly = false;
- let o = { authKey: "blabla" }
- res.cookie(o.authKey, 'value', options); // BAD, var name likely auth related
- res.end('ok')
-})
-
-app.get('/a', function (req, res, next) {
- let options = {
- maxAge: 9000000000,
- httpOnly: false,
- }
- options.httpOnly = false;
- let blabla = "authKey"
- res.cookie(blabla, 'value', options); // BAD, var name likely auth related
- res.end('ok')
-})
-
-app.get('/a', function (req, res, next) {
- let options = {
- maxAge: 9000000000,
- httpOnly: true,
- }
- options.httpOnly = true;
- res.cookie('authkey', 'value', options); // GOOD
- res.end('ok')
-})
-
-app.get('/a', function (req, res, next) {
- let options = {
- maxAge: 9000000000,
- httpOnly: false,
- }
- options.httpOnly = true;
- res.cookie('authkey', 'value', options); // GOOD
- res.end('ok')
-})
-
-app.get('/a', function (req, res, next) {
- let options = {
- maxAge: 9000000000,
- httpOnly: false,
- }
- res.cookie('mycookie', 'value', options); // GOOD, name likely is not auth sensitive
- res.end('ok')
-})
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Security/CWE-1004/tst-httpOnly.js b/javascript/ql/test/query-tests/Security/CWE-1004/tst-httpOnly.js
new file mode 100644
index 00000000000..c0835fe570f
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-1004/tst-httpOnly.js
@@ -0,0 +1,324 @@
+const express = require('express')
+const app = express()
+const session = require('cookie-session')
+
+app.use(session({
+ name: 'session',
+ keys: ['key1', 'key2'],
+ httpOnly: true, // GOOD
+}))
+
+app.use(session({
+ name: 'session',
+ keys: ['key1', 'key2'],
+ httpOnly: false // BAD
+}))
+
+app.use(session({
+ name: 'session',
+ keys: ['key1', 'key2'],
+ secure: true // GOOD, httpOnly is true by default
+}))
+
+var sess = {
+ name: 'session',
+ keys: ['key1', 'key2'],
+}
+
+sess.httpOnly = false;
+app.use(session(sess)) // BAD
+
+var sess2 = {
+ name: 'session',
+ keys: ['key1', 'key2'],
+ httpOnly: true,
+}
+
+sess2.httpOnly = false;
+app.use(session(sess2)) // BAD
+
+var sess3 = {
+ name: 'mycookie',
+ keys: ['key1', 'key2'],
+ httpOnly: true,
+}
+
+sess3.httpOnly = false;
+app.use(session(sess3)) // BAD, It is a session cookie, name doesn't matter
+
+var flag = false
+var flag2 = flag
+app.use(session({
+ name: 'session',
+ keys: ['key1', 'key2'],
+ httpOnly: flag2 // BAD
+}))
+
+app.get('/a', function (req, res, next) {
+ res.cookie('authkey', 'value',
+ {
+ maxAge: 9000000000,
+ httpOnly: true, // GOOD
+ secure: false
+ });
+ res.end('ok')
+})
+
+app.get('/a', function (req, res, next) {
+ res.cookie('authkey', 'value',
+ {
+ maxAge: 9000000000,
+ httpOnly: false, // BAD
+ secure: false
+ });
+ res.end('ok')
+})
+
+app.get('/a', function (req, res, next) {
+ res.cookie('authkey', 'value',
+ {
+ maxAge: 9000000000
+ });
+ res.end('ok') // BAD
+})
+
+app.get('/a', function (req, res, next) {
+ let options = {
+ maxAge: 9000000000,
+ httpOnly: true, // GOOD
+ secure: false
+ }
+ res.cookie('authkey', 'value', options);
+ res.end('ok')
+})
+
+app.get('/a', function (req, res, next) {
+ let options = {
+ maxAge: 9000000000,
+ httpOnly: false, // BAD
+ secure: false
+ }
+ res.cookie('authkey', 'value', options);
+ res.end('ok')
+})
+
+app.get('/a', function (req, res, next) {
+ let options = {
+ maxAge: 9000000000
+ }
+ res.cookie('authkey', 'value', options); // BAD
+ res.end('ok')
+})
+
+app.get('/a', function (req, res, next) {
+ let options = {
+ maxAge: 9000000000
+ }
+ options.httpOnly = false;
+ res.cookie('authkey', 'value', options); // BAD
+ res.end('ok')
+})
+
+app.get('/a', function (req, res, next) {
+ let options = {
+ maxAge: 9000000000
+ }
+ options.httpOnly = true;
+ res.cookie('authkey', 'value', options); // GOOD
+ res.end('ok')
+})
+
+app.get('/a', function (req, res, next) {
+ let options = {
+ maxAge: 9000000000,
+ httpOnly: false,
+ }
+ options.httpOnly = false;
+ res.cookie('authkey', 'value', options); // BAD
+ res.end('ok')
+})
+
+app.get('/a', function (req, res, next) {
+ let options = {
+ maxAge: 9000000000,
+ httpOnly: false,
+ }
+ options.httpOnly = false;
+ let authKey = "blabla"
+ res.cookie(authKey, 'value', options); // BAD, var name likely auth related
+ res.end('ok')
+})
+
+app.get('/a', function (req, res, next) {
+ let options = {
+ maxAge: 9000000000,
+ httpOnly: false,
+ }
+ options.httpOnly = false;
+ let o = { authKey: "blabla" }
+ res.cookie(o.authKey, 'value', options); // BAD, var name likely auth related
+ res.end('ok')
+})
+
+app.get('/a', function (req, res, next) {
+ let options = {
+ maxAge: 9000000000,
+ httpOnly: false,
+ }
+ options.httpOnly = false;
+ let blabla = "authKey"
+ res.cookie(blabla, 'value', options); // BAD, var name likely auth related
+ res.end('ok')
+})
+
+app.get('/a', function (req, res, next) {
+ let options = {
+ maxAge: 9000000000,
+ httpOnly: true,
+ }
+ options.httpOnly = true;
+ res.cookie('authkey', 'value', options); // GOOD
+ res.end('ok')
+})
+
+app.get('/a', function (req, res, next) {
+ let options = {
+ maxAge: 9000000000,
+ httpOnly: false,
+ }
+ options.httpOnly = true;
+ res.cookie('authkey', 'value', options); // GOOD
+ res.end('ok')
+})
+
+app.get('/a', function (req, res, next) {
+ let options = {
+ maxAge: 9000000000,
+ httpOnly: false,
+ }
+ res.cookie('mycookie', 'value', options); // GOOD, name likely is not auth sensitive
+ res.end('ok')
+})
+
+const http = require('http');
+
+function test1() {
+ const server = http.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ // BAD
+ res.setHeader("Set-Cookie", "authKey=ninja");
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+ });
+}
+
+function test2() {
+ const server = http.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ // GOOD
+ res.setHeader("Set-Cookie", "auth=ninja; HttpOnly");
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+ });
+}
+
+function test3() {
+ const server = http.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ // BAD
+ res.setHeader("Set-Cookie", ["authKey=ninja", "token=javascript"]);
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+ });
+}
+
+function test4() {
+ const server = http.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ // GOOD
+ res.setHeader("Set-Cookie", ["auth=ninja; HttpOnly"]);
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+ });
+}
+
+function test5() {
+ const server = http.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ // GOOD, case insensitive
+ res.setHeader("Set-Cookie", ["auth=ninja; httponly"]);
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+ });
+}
+
+function test6() {
+ const server = http.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ // OK - the sensitive cookie has httpOnly set
+ res.setHeader("Set-Cookie", ["authKey=ninja; httponly", "token=javascript"]);
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+ });
+}
+
+function test7() {
+ const server = http.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ // Good, not auth related
+ res.setHeader("Set-Cookie", ["foo=ninja", "bar=javascript"]);
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+ });
+}
+
+function test8() {
+ const server = http.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ let attr = "; httponly"
+ res.setHeader("Set-Cookie", `session=ninja ${attr}`); // Good, httponly string expression
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+ });
+}
+
+function test9() {
+ const server = http.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ let attr = "; secure"
+ res.setHeader("Set-Cookie", `authKey=ninja ${attr}`); // Bad, not httponly string expression
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+ });
+}
+
+const session = require('express-session')
+
+app.use(session({
+ name: 'session',
+ keys: ['key1', 'key2'],
+ cookie: { httpOnly: true }, // GOOD
+}))
+
+app.use(session({
+ name: 'session',
+ keys: ['key1', 'key2'],
+ cookie: { httpOnly: false } // BAD
+}))
+
+app.use(session({
+ name: 'session',
+ keys: ['key1', 'key2'],
+ cookie: { secure: true } // GOOD, httpOnly is true by default
+}))
+
+app.use(session({ // GOOD, httpOnly is true by default
+ name: 'session',
+ keys: ['key1', 'key2']
+}))
+
+app.use(session({
+ name: 'mycookie',
+ keys: ['key1', 'key2'],
+ cookie: { httpOnly: false } // BAD, It is a session cookie, name doesn't matter
+}))
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.expected b/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.expected
index 18d0c7ca02b..5f474956fa6 100644
--- a/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.expected
@@ -1,12 +1,12 @@
-| test_cookie-session.js:16:9:24:2 | session ... Date\\n}) | Cookie is added to response without the 'secure' flag being set to true |
-| test_express-session.js:5:9:8:2 | session ... T OK\\n}) | Cookie is added to response without the 'secure' flag being set to true |
-| test_express-session.js:10:9:13:2 | session ... T OK\\n}) | Cookie is added to response without the 'secure' flag being set to true |
-| test_express-session.js:15:9:18:2 | session ... T OK\\n}) | Cookie is added to response without the 'secure' flag being set to true |
-| test_express-session.js:25:9:25:21 | session(sess) | Cookie is added to response without the 'secure' flag being set to true |
-| test_httpserver.js:7:37:7:48 | "type=ninja" | Cookie is added to response without the 'secure' flag being set to true |
-| test_httpserver.js:27:38:27:49 | "type=ninja" | Cookie is added to response without the 'secure' flag being set to true |
-| test_httpserver.js:27:52:27:72 | "langua ... script" | Cookie is added to response without the 'secure' flag being set to true |
-| test_httpserver.js:57:60:57:80 | "langua ... script" | Cookie is added to response without the 'secure' flag being set to true |
-| test_jscookie.js:2:1:2:48 | js_cook ... alse }) | Cookie is added to response without the 'secure' flag being set to true |
-| test_responseCookie.js:5:5:10:10 | res.coo ... }) | Cookie is added to response without the 'secure' flag being set to true |
-| test_responseCookie.js:20:5:20:40 | res.coo ... ptions) | Cookie is added to response without the 'secure' flag being set to true |
+| tst-cleartextCookie.js:5:5:10:10 | res.coo ... }) | Cookie is added to response without the 'secure' flag being set to true |
+| tst-cleartextCookie.js:20:5:20:40 | res.coo ... ptions) | Cookie is added to response without the 'secure' flag being set to true |
+| tst-cleartextCookie.js:35:1:35:48 | js_cook ... alse }) | Cookie is added to response without the 'secure' flag being set to true |
+| tst-cleartextCookie.js:44:37:44:48 | "type=ninja" | Cookie is added to response without the 'secure' flag being set to true |
+| tst-cleartextCookie.js:64:38:64:49 | "type=ninja" | Cookie is added to response without the 'secure' flag being set to true |
+| tst-cleartextCookie.js:64:52:64:72 | "langua ... script" | Cookie is added to response without the 'secure' flag being set to true |
+| tst-cleartextCookie.js:94:60:94:80 | "langua ... script" | Cookie is added to response without the 'secure' flag being set to true |
+| tst-cleartextCookie.js:104:9:107:2 | session ... T OK\\n}) | Cookie is added to response without the 'secure' flag being set to true |
+| tst-cleartextCookie.js:109:9:112:2 | session ... T OK\\n}) | Cookie is added to response without the 'secure' flag being set to true |
+| tst-cleartextCookie.js:114:9:117:2 | session ... T OK\\n}) | Cookie is added to response without the 'secure' flag being set to true |
+| tst-cleartextCookie.js:124:9:124:21 | session(sess) | Cookie is added to response without the 'secure' flag being set to true |
+| tst-cleartextCookie.js:148:9:156:2 | session ... Date\\n}) | Cookie is added to response without the 'secure' flag being set to true |
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/test_cookie-session.js b/javascript/ql/test/query-tests/Security/CWE-614/test_cookie-session.js
deleted file mode 100644
index 18ee1a78055..00000000000
--- a/javascript/ql/test/query-tests/Security/CWE-614/test_cookie-session.js
+++ /dev/null
@@ -1,24 +0,0 @@
-const express = require('express')
-const app = express()
-const session = require('cookie-session')
-const expiryDate = new Date(Date.now() + 60 * 60 * 1000)
-
-app.use(session({
- name: 'session',
- keys: ['key1', 'key2'],
- secure: true, // OK
- httpOnly: true,
- domain: 'example.com',
- path: 'foo/bar',
- expires: expiryDate
-}))
-
-app.use(session({
- name: 'session',
- keys: ['key1', 'key2'],
- secure: false, // NOT OK
- httpOnly: true,
- domain: 'example.com',
- path: 'foo/bar',
- expires: expiryDate
-}))
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/test_express-session.js b/javascript/ql/test/query-tests/Security/CWE-614/test_express-session.js
deleted file mode 100644
index 873fe2162ca..00000000000
--- a/javascript/ql/test/query-tests/Security/CWE-614/test_express-session.js
+++ /dev/null
@@ -1,33 +0,0 @@
-const express = require('express')
-const app = express()
-const session = require('express-session')
-
-app.use(session({
- secret: 'secret',
- cookie: { secure: false } // NOT OK
-}))
-
-app.use(session({
- secret: 'secret'
- // NOT OK
-}))
-
-app.use(session({
- secret: 'secret',
- cookie: {} // NOT OK
-}))
-
-const sess = {
- secret: 'secret',
- cookie: { secure: false } // NOT OK
-}
-
-app.use(session(sess))
-
-
-app.set('trust proxy', 1)
-app.use(session({
- secret: 'secret',
- cookie: { secure: true } // OK
-}))
-
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/test_httpserver.js b/javascript/ql/test/query-tests/Security/CWE-614/test_httpserver.js
deleted file mode 100644
index f490cd5c838..00000000000
--- a/javascript/ql/test/query-tests/Security/CWE-614/test_httpserver.js
+++ /dev/null
@@ -1,61 +0,0 @@
-const http = require('http');
-
-function test1() {
- const server = http.createServer((req, res) => {
- res.setHeader('Content-Type', 'text/html');
- // BAD
- res.setHeader("Set-Cookie", "type=ninja");
- res.writeHead(200, { 'Content-Type': 'text/plain' });
- res.end('ok');
- });
-}
-
-function test2() {
- const server = http.createServer((req, res) => {
- res.setHeader('Content-Type', 'text/html');
- // GOOD
- res.setHeader("Set-Cookie", "type=ninja; Secure");
- res.writeHead(200, { 'Content-Type': 'text/plain' });
- res.end('ok');
- });
-}
-
-function test3() {
- const server = http.createServer((req, res) => {
- res.setHeader('Content-Type', 'text/html');
- // BAD
- res.setHeader("Set-Cookie", ["type=ninja", "language=javascript"]);
- res.writeHead(200, { 'Content-Type': 'text/plain' });
- res.end('ok');
- });
-}
-
-function test4() {
- const server = http.createServer((req, res) => {
- res.setHeader('Content-Type', 'text/html');
- // GOOD
- res.setHeader("Set-Cookie", ["type=ninja; Secure"]);
- res.writeHead(200, { 'Content-Type': 'text/plain' });
- res.end('ok');
- });
-}
-
-function test5() {
- const server = http.createServer((req, res) => {
- res.setHeader('Content-Type', 'text/html');
- // GOOD, case insensitive
- res.setHeader("Set-Cookie", ["type=ninja; secure"]);
- res.writeHead(200, { 'Content-Type': 'text/plain' });
- res.end('ok');
- });
-}
-
-function test6() {
- const server = http.createServer((req, res) => {
- res.setHeader('Content-Type', 'text/html');
- // BAD
- res.setHeader("Set-Cookie", ["type=ninja; secure", "language=javascript"]);
- res.writeHead(200, { 'Content-Type': 'text/plain' });
- res.end('ok');
- });
-}
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/test_jscookie.js b/javascript/ql/test/query-tests/Security/CWE-614/test_jscookie.js
deleted file mode 100644
index d53fcecf84b..00000000000
--- a/javascript/ql/test/query-tests/Security/CWE-614/test_jscookie.js
+++ /dev/null
@@ -1,3 +0,0 @@
-const js_cookie = require('js-cookie')
-js_cookie.set('key', 'value', { secure: false }); // NOT OK
-js_cookie.set('key', 'value', { secure: true }); // OK
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/test_responseCookie.js b/javascript/ql/test/query-tests/Security/CWE-614/test_responseCookie.js
deleted file mode 100644
index 8d002d594f3..00000000000
--- a/javascript/ql/test/query-tests/Security/CWE-614/test_responseCookie.js
+++ /dev/null
@@ -1,33 +0,0 @@
-const express = require('express')
-const app = express()
-
-app.get('/a', function (req, res, next) {
- res.cookie('name', 'value',
- {
- maxAge: 9000000000,
- httpOnly: true,
- secure: false // NOT OK
- });
- res.end('ok')
-})
-
-app.get('/b', function (req, res, next) {
- let options = {
- maxAge: 9000000000,
- httpOnly: true,
- secure: false // NOT OK
- }
- res.cookie('name', 'value', options);
- res.end('ok')
-})
-
-app.get('/c', function (req, res, next) {
- res.cookie('name', 'value',
- {
- maxAge: 9000000000,
- httpOnly: true,
- secure: true // OK
- });
- res.end('ok')
-})
-
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js b/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js
new file mode 100644
index 00000000000..f87c9d3ee02
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js
@@ -0,0 +1,156 @@
+const express = require('express')
+const app = express()
+
+app.get('/a', function (req, res, next) {
+ res.cookie('name', 'value',
+ {
+ maxAge: 9000000000,
+ httpOnly: true,
+ secure: false // NOT OK
+ });
+ res.end('ok')
+})
+
+app.get('/b', function (req, res, next) {
+ let options = {
+ maxAge: 9000000000,
+ httpOnly: true,
+ secure: false // NOT OK
+ }
+ res.cookie('name', 'value', options);
+ res.end('ok')
+})
+
+app.get('/c', function (req, res, next) {
+ res.cookie('name', 'value',
+ {
+ maxAge: 9000000000,
+ httpOnly: true,
+ secure: true // OK
+ });
+ res.end('ok')
+})
+
+const js_cookie = require('js-cookie')
+js_cookie.set('key', 'value', { secure: false }); // NOT OK
+js_cookie.set('key', 'value', { secure: true }); // OK
+
+const http = require('http');
+
+function test1() {
+ const server = http.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ // BAD
+ res.setHeader("Set-Cookie", "type=ninja");
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+ });
+}
+
+function test2() {
+ const server = http.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ // GOOD
+ res.setHeader("Set-Cookie", "type=ninja; Secure");
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+ });
+}
+
+function test3() {
+ const server = http.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ // BAD
+ res.setHeader("Set-Cookie", ["type=ninja", "language=javascript"]);
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+ });
+}
+
+function test4() {
+ const server = http.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ // GOOD
+ res.setHeader("Set-Cookie", ["type=ninja; Secure"]);
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+ });
+}
+
+function test5() {
+ const server = http.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ // GOOD, case insensitive
+ res.setHeader("Set-Cookie", ["type=ninja; secure"]);
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+ });
+}
+
+function test6() {
+ const server = http.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ // BAD
+ res.setHeader("Set-Cookie", ["type=ninja; secure", "language=javascript"]);
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+ });
+}
+
+const express = require('express')
+const app = express()
+const session = require('express-session')
+
+app.use(session({
+ secret: 'secret',
+ cookie: { secure: false } // NOT OK
+}))
+
+app.use(session({
+ secret: 'secret'
+ // NOT OK
+}))
+
+app.use(session({
+ secret: 'secret',
+ cookie: {} // NOT OK
+}))
+
+const sess = {
+ secret: 'secret',
+ cookie: { secure: false } // NOT OK
+}
+
+app.use(session(sess))
+
+
+app.set('trust proxy', 1)
+app.use(session({
+ secret: 'secret',
+ cookie: { secure: true } // OK
+}))
+
+const express = require('express')
+const app = express()
+const session = require('cookie-session')
+const expiryDate = new Date(Date.now() + 60 * 60 * 1000)
+
+app.use(session({
+ name: 'session',
+ keys: ['key1', 'key2'],
+ secure: true, // OK
+ httpOnly: true,
+ domain: 'example.com',
+ path: 'foo/bar',
+ expires: expiryDate
+}))
+
+app.use(session({
+ name: 'session',
+ keys: ['key1', 'key2'],
+ secure: false, // NOT OK
+ httpOnly: true,
+ domain: 'example.com',
+ path: 'foo/bar',
+ expires: expiryDate
+}))
From f36accf3e6f55aa6970580090bd2f9adac8b10db Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 6 Oct 2021 10:08:11 +0200
Subject: [PATCH 128/471] only report clear-text cookies for sensitive cookies
---
.../javascript/frameworks/CookieLibraries.qll | 6 +++++-
.../ql/src/Security/CWE-614/InsecureCookie.ql | 7 +++++--
.../Security/CWE-614/InsecureCookies.expected | 11 +++++------
.../Security/CWE-614/tst-cleartextCookie.js | 18 +++++++++---------
4 files changed, 24 insertions(+), 18 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
index d1fc50e95ea..b6cbcc2e48f 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
@@ -73,7 +73,11 @@ private module JsCookie {
this.getOptionArgument(2, CookieWrites::secure()).mayHaveBooleanValue(true)
}
- override predicate isSensitive() { none() } // TODO: Maybe it can be sensitive?
+ override predicate isSensitive() {
+ HeuristicNames::nameIndicatesSensitiveData(any(string s |
+ this.getArgument(0).mayHaveStringValue(s)
+ ), _)
+ }
override predicate isHttpOnly() { none() } // js-cookie is browser side library and doesn't support HttpOnly
}
diff --git a/javascript/ql/src/Security/CWE-614/InsecureCookie.ql b/javascript/ql/src/Security/CWE-614/InsecureCookie.ql
index 345c9555cb6..f6900f424bd 100644
--- a/javascript/ql/src/Security/CWE-614/InsecureCookie.ql
+++ b/javascript/ql/src/Security/CWE-614/InsecureCookie.ql
@@ -12,7 +12,10 @@
import javascript
+// TODO: Rename to ClearTextCookie?
from DataFlow::Node node
-// TODO: Only for sensitive cookies? (e.g. auth cookies)
-where exists(CookieWrites::CookieWrite cookie | cookie = node | not cookie.isSecure())
+where
+ exists(CookieWrites::CookieWrite cookie | cookie = node |
+ cookie.isSensitive() and not cookie.isSecure()
+ )
select node, "Cookie is added to response without the 'secure' flag being set to true"
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.expected b/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.expected
index 5f474956fa6..bc858269883 100644
--- a/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.expected
@@ -1,10 +1,9 @@
| tst-cleartextCookie.js:5:5:10:10 | res.coo ... }) | Cookie is added to response without the 'secure' flag being set to true |
-| tst-cleartextCookie.js:20:5:20:40 | res.coo ... ptions) | Cookie is added to response without the 'secure' flag being set to true |
-| tst-cleartextCookie.js:35:1:35:48 | js_cook ... alse }) | Cookie is added to response without the 'secure' flag being set to true |
-| tst-cleartextCookie.js:44:37:44:48 | "type=ninja" | Cookie is added to response without the 'secure' flag being set to true |
-| tst-cleartextCookie.js:64:38:64:49 | "type=ninja" | Cookie is added to response without the 'secure' flag being set to true |
-| tst-cleartextCookie.js:64:52:64:72 | "langua ... script" | Cookie is added to response without the 'secure' flag being set to true |
-| tst-cleartextCookie.js:94:60:94:80 | "langua ... script" | Cookie is added to response without the 'secure' flag being set to true |
+| tst-cleartextCookie.js:20:5:20:43 | res.coo ... ptions) | Cookie is added to response without the 'secure' flag being set to true |
+| tst-cleartextCookie.js:35:1:35:52 | js_cook ... alse }) | Cookie is added to response without the 'secure' flag being set to true |
+| tst-cleartextCookie.js:44:37:44:51 | "authKey=ninja" | Cookie is added to response without the 'secure' flag being set to true |
+| tst-cleartextCookie.js:64:38:64:52 | "authKey=ninja" | Cookie is added to response without the 'secure' flag being set to true |
+| tst-cleartextCookie.js:94:60:94:72 | "authKey=foo" | Cookie is added to response without the 'secure' flag being set to true |
| tst-cleartextCookie.js:104:9:107:2 | session ... T OK\\n}) | Cookie is added to response without the 'secure' flag being set to true |
| tst-cleartextCookie.js:109:9:112:2 | session ... T OK\\n}) | Cookie is added to response without the 'secure' flag being set to true |
| tst-cleartextCookie.js:114:9:117:2 | session ... T OK\\n}) | Cookie is added to response without the 'secure' flag being set to true |
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js b/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js
index f87c9d3ee02..bdfe322f56f 100644
--- a/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js
+++ b/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js
@@ -2,7 +2,7 @@ const express = require('express')
const app = express()
app.get('/a', function (req, res, next) {
- res.cookie('name', 'value',
+ res.cookie('authkey', 'value',
{
maxAge: 9000000000,
httpOnly: true,
@@ -17,7 +17,7 @@ app.get('/b', function (req, res, next) {
httpOnly: true,
secure: false // NOT OK
}
- res.cookie('name', 'value', options);
+ res.cookie('authKey', 'value', options);
res.end('ok')
})
@@ -32,8 +32,8 @@ app.get('/c', function (req, res, next) {
})
const js_cookie = require('js-cookie')
-js_cookie.set('key', 'value', { secure: false }); // NOT OK
-js_cookie.set('key', 'value', { secure: true }); // OK
+js_cookie.set('authKey', 'value', { secure: false }); // NOT OK
+js_cookie.set('authKey', 'value', { secure: true }); // OK
const http = require('http');
@@ -41,7 +41,7 @@ function test1() {
const server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'text/html');
// BAD
- res.setHeader("Set-Cookie", "type=ninja");
+ res.setHeader("Set-Cookie", "authKey=ninja");
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('ok');
});
@@ -60,8 +60,8 @@ function test2() {
function test3() {
const server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'text/html');
- // BAD
- res.setHeader("Set-Cookie", ["type=ninja", "language=javascript"]);
+ // BAD (and good, TODO: Move to separate lines)
+ res.setHeader("Set-Cookie", ["authKey=ninja", "language=javascript"]);
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('ok');
});
@@ -90,8 +90,8 @@ function test5() {
function test6() {
const server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'text/html');
- // BAD
- res.setHeader("Set-Cookie", ["type=ninja; secure", "language=javascript"]);
+ // BAD (and good. TODO: Move to separate lines)
+ res.setHeader("Set-Cookie", ["type=ninja; secure", "authKey=foo"]);
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('ok');
});
From ab23ffff3d3e13e0c1b8a04b9164fff3c2a62830 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 6 Oct 2021 11:53:38 +0200
Subject: [PATCH 129/471] documentation overhaul for clear-text-cookie
---
.../Security/CWE-614/ClearTextCookie.qhelp | 38 +++++++++++++++++++
.../src/Security/CWE-614/ClearTextCookie.ql | 20 ++++++++++
.../src/Security/CWE-614/InsecureCookie.qhelp | 26 -------------
.../ql/src/Security/CWE-614/InsecureCookie.ql | 21 ----------
.../CWE-614/examples/ClearTextCookieBad.js | 7 ++++
.../CWE-614/examples/ClearTextCookieGood.js | 7 ++++
.../Security/CWE-614/ClearTextCookie.expected | 12 ++++++
.../Security/CWE-614/ClearTextCookie.qlref | 1 +
.../Security/CWE-614/InsecureCookies.expected | 11 ------
.../Security/CWE-614/InsecureCookies.qlref | 1 -
.../Security/CWE-614/tst-cleartextCookie.js | 13 +++++++
11 files changed, 98 insertions(+), 59 deletions(-)
create mode 100644 javascript/ql/src/Security/CWE-614/ClearTextCookie.qhelp
create mode 100644 javascript/ql/src/Security/CWE-614/ClearTextCookie.ql
delete mode 100644 javascript/ql/src/Security/CWE-614/InsecureCookie.qhelp
delete mode 100644 javascript/ql/src/Security/CWE-614/InsecureCookie.ql
create mode 100644 javascript/ql/src/Security/CWE-614/examples/ClearTextCookieBad.js
create mode 100644 javascript/ql/src/Security/CWE-614/examples/ClearTextCookieGood.js
create mode 100644 javascript/ql/test/query-tests/Security/CWE-614/ClearTextCookie.expected
create mode 100644 javascript/ql/test/query-tests/Security/CWE-614/ClearTextCookie.qlref
delete mode 100644 javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.expected
delete mode 100644 javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.qlref
diff --git a/javascript/ql/src/Security/CWE-614/ClearTextCookie.qhelp b/javascript/ql/src/Security/CWE-614/ClearTextCookie.qhelp
new file mode 100644
index 00000000000..d97ee493b64
--- /dev/null
+++ b/javascript/ql/src/Security/CWE-614/ClearTextCookie.qhelp
@@ -0,0 +1,38 @@
+
+
+
+
+Cookies that are transmitted in clear text can be intercepted by an attacker.
+If sensitive cookies are intercepted, the attacker can read the cookie and
+use it to perform actions on the user's behalf.
+
+
+
+
+
+Always transmit sensitive cookies using SSL by setting the secure
+attribute on the cookie.
+
+
+
+
+
+The following example stores an authentication token in a cookie that can
+be transmitted in clear text.
+
+
+
+To force the cookie to be transmitted using SSL, set the secure
+attribute on the cookie.
+
+
+
+
+
+ExpressJS: Use cookies securely.
+OWASP: Set cookie flags appropriately.
+Set-Cookie.
+
+
diff --git a/javascript/ql/src/Security/CWE-614/ClearTextCookie.ql b/javascript/ql/src/Security/CWE-614/ClearTextCookie.ql
new file mode 100644
index 00000000000..910e196c7fc
--- /dev/null
+++ b/javascript/ql/src/Security/CWE-614/ClearTextCookie.ql
@@ -0,0 +1,20 @@
+/**
+ * @name Clear text transmission of sensitive cookie
+ * @description Sending sensitive information in a cookie without requring SSL encryption
+ * can expose the cookie to an attacker.
+ * @kind problem
+ * @problem.severity warning
+ * @security-severity 5.0
+ * @precision high
+ * @id js/clear-text-cookie
+ * @tags security
+ * external/cwe/cwe-614
+ * external/cwe/cwe-311
+ * external/cwe/cwe-312
+ */
+
+import javascript
+
+from CookieWrites::CookieWrite cookie
+where cookie.isSensitive() and not cookie.isSecure()
+select cookie, "Sensitive cookie sent without enforcing SSL encryption"
diff --git a/javascript/ql/src/Security/CWE-614/InsecureCookie.qhelp b/javascript/ql/src/Security/CWE-614/InsecureCookie.qhelp
deleted file mode 100644
index 06e920b44bf..00000000000
--- a/javascript/ql/src/Security/CWE-614/InsecureCookie.qhelp
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-Failing to set the 'secure' flag on a cookie can cause it to be sent in cleartext.
-This makes it easier for an attacker to intercept.
-
-
-
-Always set the secure flag to `true` on a cookie before adding it
-to an HTTP response (if the default value is `false`).
-
-
-
-
-
-Production Best Practices: Security:Use cookies securely.
-NodeJS security cheat sheet:Set cookie flags appropriately.
-express-session:cookie.secure.
-cookie-session:Cookie Options.
-express response.cookie.
-Set-Cookie.
-js-cookie.
-
-
diff --git a/javascript/ql/src/Security/CWE-614/InsecureCookie.ql b/javascript/ql/src/Security/CWE-614/InsecureCookie.ql
deleted file mode 100644
index f6900f424bd..00000000000
--- a/javascript/ql/src/Security/CWE-614/InsecureCookie.ql
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * @name Failure to set secure cookies
- * @description Insecure cookies may be sent in cleartext, which makes them vulnerable to
- * interception.
- * @kind problem
- * @problem.severity error
- * @precision high
- * @id js/insecure-cookie
- * @tags security
- * external/cwe/cwe-614
- */
-
-import javascript
-
-// TODO: Rename to ClearTextCookie?
-from DataFlow::Node node
-where
- exists(CookieWrites::CookieWrite cookie | cookie = node |
- cookie.isSensitive() and not cookie.isSecure()
- )
-select node, "Cookie is added to response without the 'secure' flag being set to true"
diff --git a/javascript/ql/src/Security/CWE-614/examples/ClearTextCookieBad.js b/javascript/ql/src/Security/CWE-614/examples/ClearTextCookieBad.js
new file mode 100644
index 00000000000..330bf86456a
--- /dev/null
+++ b/javascript/ql/src/Security/CWE-614/examples/ClearTextCookieBad.js
@@ -0,0 +1,7 @@
+const http = require('http');
+
+const server = http.createServer((req, res) => {
+ res.setHeader("Set-Cookie", `authKey=${makeAuthkey()}`);
+ res.writeHead(200, { 'Content-Type': 'text/html' });
+ res.end('Hello world
');
+});
\ No newline at end of file
diff --git a/javascript/ql/src/Security/CWE-614/examples/ClearTextCookieGood.js b/javascript/ql/src/Security/CWE-614/examples/ClearTextCookieGood.js
new file mode 100644
index 00000000000..09fd0b52fec
--- /dev/null
+++ b/javascript/ql/src/Security/CWE-614/examples/ClearTextCookieGood.js
@@ -0,0 +1,7 @@
+const http = require('http');
+
+const server = http.createServer((req, res) => {
+ res.setHeader("Set-Cookie", `authKey=${makeAuthkey()}; secure; httpOnly`);
+ res.writeHead(200, { 'Content-Type': 'text/html' });
+ res.end('Hello world
');
+});
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/ClearTextCookie.expected b/javascript/ql/test/query-tests/Security/CWE-614/ClearTextCookie.expected
new file mode 100644
index 00000000000..b39ccbc514d
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-614/ClearTextCookie.expected
@@ -0,0 +1,12 @@
+| tst-cleartextCookie.js:5:5:10:10 | res.coo ... }) | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:20:5:20:43 | res.coo ... ptions) | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:35:1:35:52 | js_cook ... alse }) | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:44:37:44:51 | "authKey=ninja" | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:64:38:64:52 | "authKey=ninja" | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:94:60:94:72 | "authKey=foo" | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:104:9:107:2 | session ... T OK\\n}) | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:109:9:112:2 | session ... T OK\\n}) | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:114:9:117:2 | session ... T OK\\n}) | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:124:9:124:21 | session(sess) | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:148:9:156:2 | session ... Date\\n}) | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:160:33:160:58 | `authKe ... key()}` | Sensitive cookie sent without enforcing SSL encryption |
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/ClearTextCookie.qlref b/javascript/ql/test/query-tests/Security/CWE-614/ClearTextCookie.qlref
new file mode 100644
index 00000000000..357e5d75faf
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-614/ClearTextCookie.qlref
@@ -0,0 +1 @@
+Security/CWE-614/ClearTextCookie.ql
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.expected b/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.expected
deleted file mode 100644
index bc858269883..00000000000
--- a/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.expected
+++ /dev/null
@@ -1,11 +0,0 @@
-| tst-cleartextCookie.js:5:5:10:10 | res.coo ... }) | Cookie is added to response without the 'secure' flag being set to true |
-| tst-cleartextCookie.js:20:5:20:43 | res.coo ... ptions) | Cookie is added to response without the 'secure' flag being set to true |
-| tst-cleartextCookie.js:35:1:35:52 | js_cook ... alse }) | Cookie is added to response without the 'secure' flag being set to true |
-| tst-cleartextCookie.js:44:37:44:51 | "authKey=ninja" | Cookie is added to response without the 'secure' flag being set to true |
-| tst-cleartextCookie.js:64:38:64:52 | "authKey=ninja" | Cookie is added to response without the 'secure' flag being set to true |
-| tst-cleartextCookie.js:94:60:94:72 | "authKey=foo" | Cookie is added to response without the 'secure' flag being set to true |
-| tst-cleartextCookie.js:104:9:107:2 | session ... T OK\\n}) | Cookie is added to response without the 'secure' flag being set to true |
-| tst-cleartextCookie.js:109:9:112:2 | session ... T OK\\n}) | Cookie is added to response without the 'secure' flag being set to true |
-| tst-cleartextCookie.js:114:9:117:2 | session ... T OK\\n}) | Cookie is added to response without the 'secure' flag being set to true |
-| tst-cleartextCookie.js:124:9:124:21 | session(sess) | Cookie is added to response without the 'secure' flag being set to true |
-| tst-cleartextCookie.js:148:9:156:2 | session ... Date\\n}) | Cookie is added to response without the 'secure' flag being set to true |
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.qlref b/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.qlref
deleted file mode 100644
index dde95c5ad05..00000000000
--- a/javascript/ql/test/query-tests/Security/CWE-614/InsecureCookies.qlref
+++ /dev/null
@@ -1 +0,0 @@
-Security/CWE-614/InsecureCookie.ql
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js b/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js
index bdfe322f56f..2d913691889 100644
--- a/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js
+++ b/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js
@@ -154,3 +154,16 @@ app.use(session({
path: 'foo/bar',
expires: expiryDate
}))
+
+http.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ res.setHeader("Set-Cookie", `authKey=${makeAuthkey()}`); // NOT OK
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+});
+
+http.createServer((req, res) => {
+ res.setHeader("Set-Cookie", `authKey=${makeAuthkey()}; secure; httpOnly`); // OK
+ res.writeHead(200, { 'Content-Type': 'text/html' });
+ res.end('Hello world
');
+});
\ No newline at end of file
From 2cb3d2c53fe2840496786b0ee9ebf1fb5312f383 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 6 Oct 2021 15:01:25 +0200
Subject: [PATCH 130/471] documentation overhaul on client-exposed-cookie (and
restricting it to server-side)
---
.../javascript/frameworks/CookieLibraries.qll | 24 +++++++++--
.../CWE-1004/ClientExposedCookie.qhelp | 42 +++++++++++++++++++
.../Security/CWE-1004/ClientExposedCookie.ql | 20 +++++++++
.../CWE-1004/CookieWithoutHttpOnly.qhelp | 25 -----------
.../CWE-1004/CookieWithoutHttpOnly.ql | 23 ----------
.../examples/ClientExposedCookieBad.js | 7 ++++
.../examples/ClientExposedCookieGood.js | 7 ++++
.../CWE-1004/ClientExposedCookie.expected | 19 +++++++++
.../CWE-1004/ClientExposedCookie.qlref | 1 +
.../CWE-1004/CookieWithoutHttpOnly.expected | 19 ---------
.../CWE-1004/CookieWithoutHttpOnly.qlref | 1 -
11 files changed, 116 insertions(+), 72 deletions(-)
create mode 100644 javascript/ql/src/Security/CWE-1004/ClientExposedCookie.qhelp
create mode 100644 javascript/ql/src/Security/CWE-1004/ClientExposedCookie.ql
delete mode 100644 javascript/ql/src/Security/CWE-1004/CookieWithoutHttpOnly.qhelp
delete mode 100644 javascript/ql/src/Security/CWE-1004/CookieWithoutHttpOnly.ql
create mode 100644 javascript/ql/src/Security/CWE-1004/examples/ClientExposedCookieBad.js
create mode 100644 javascript/ql/src/Security/CWE-1004/examples/ClientExposedCookieGood.js
create mode 100644 javascript/ql/test/query-tests/Security/CWE-1004/ClientExposedCookie.expected
create mode 100644 javascript/ql/test/query-tests/Security/CWE-1004/ClientExposedCookie.qlref
delete mode 100644 javascript/ql/test/query-tests/Security/CWE-1004/CookieWithoutHttpOnly.expected
delete mode 100644 javascript/ql/test/query-tests/Security/CWE-1004/CookieWithoutHttpOnly.qlref
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
index b6cbcc2e48f..444dc1c8daa 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
@@ -24,9 +24,25 @@ module CookieWrites {
/**
* Holds if the cookie is likely an authentication cookie or otherwise sensitive.
- * Can never hold for client-side cookies. TODO: Or can it...?
+ * Can never hold for client-side cookies.
*/
abstract predicate isSensitive();
+
+ /**
+ * Holds if the cookie write happens on a server, that is `httpOnly` flag is relevant.
+ */
+ predicate isServerSide() {
+ any() // holds by default. Client-side cookie writes should extend ClientSideCookieWrite.
+ }
+ }
+
+ /**
+ * A client-side write to a cookie.
+ */
+ abstract class ClientSideCookieWrite extends CookieWrite {
+ final override predicate isHttpOnly() { none() }
+
+ final override predicate isServerSide() { none() }
}
/**
@@ -43,6 +59,7 @@ module CookieWrites {
/**
* A model of the `js-cookie` library (https://github.com/js-cookie/js-cookie).
*/
+// TODO: Writes to document.cookie.
private module JsCookie {
/**
* Gets a function call that invokes method `name` of the `js-cookie` library.
@@ -61,7 +78,8 @@ private module JsCookie {
}
}
- class WriteAccess extends PersistentWriteAccess, DataFlow::CallNode, CookieWrites::CookieWrite {
+ class WriteAccess extends PersistentWriteAccess, DataFlow::CallNode,
+ CookieWrites::ClientSideCookieWrite {
WriteAccess() { this = libMemberCall("set") }
string getKey() { getArgument(0).mayHaveStringValue(result) }
@@ -78,8 +96,6 @@ private module JsCookie {
this.getArgument(0).mayHaveStringValue(s)
), _)
}
-
- override predicate isHttpOnly() { none() } // js-cookie is browser side library and doesn't support HttpOnly
}
}
diff --git a/javascript/ql/src/Security/CWE-1004/ClientExposedCookie.qhelp b/javascript/ql/src/Security/CWE-1004/ClientExposedCookie.qhelp
new file mode 100644
index 00000000000..d343c298000
--- /dev/null
+++ b/javascript/ql/src/Security/CWE-1004/ClientExposedCookie.qhelp
@@ -0,0 +1,42 @@
+
+
+
+
+
+Authentication cookies stored by a server can be accessed by a client if the httpOnly flag is not set.
+
+
+An attacker that manages a cross-site scripting (XSS) attack can read the cookie and hijack the session.
+
+
+
+
+
+Set the httpOnly flag on all cookies that are not needed by the client.
+
+
+
+
+
+
+
+The following example stores an authentication token in a cookie that can
+viewed by the client.
+
+
+
+To force the cookie to be transmitted using SSL, set the secure
+attribute on the cookie.
+
+
+
+
+
+ExpressJS: Use cookies securely.
+OWASP: Set cookie flags appropriately.
+Set-Cookie.
+
+
+
diff --git a/javascript/ql/src/Security/CWE-1004/ClientExposedCookie.ql b/javascript/ql/src/Security/CWE-1004/ClientExposedCookie.ql
new file mode 100644
index 00000000000..ad7b3f7f0b0
--- /dev/null
+++ b/javascript/ql/src/Security/CWE-1004/ClientExposedCookie.ql
@@ -0,0 +1,20 @@
+/**
+ * @name Sensitive server cookie exposed to the client
+ * @description Sensitive cookies set by a server can be read by the client if the `httpOnly` flag is not set.
+ * @kind problem
+ * @problem.severity warning
+ * @security-severity 5.0
+ * @precision high
+ * @id js/client-exposed-cookie
+ * @tags security
+ * external/cwe/cwe-1004
+ */
+
+import javascript
+
+from CookieWrites::CookieWrite cookie
+where
+ cookie.isSensitive() and
+ cookie.isServerSide() and
+ not cookie.isHttpOnly()
+select cookie, "Sensitive server cookie is missing 'httpOnly' flag."
diff --git a/javascript/ql/src/Security/CWE-1004/CookieWithoutHttpOnly.qhelp b/javascript/ql/src/Security/CWE-1004/CookieWithoutHttpOnly.qhelp
deleted file mode 100644
index d674c97aef0..00000000000
--- a/javascript/ql/src/Security/CWE-1004/CookieWithoutHttpOnly.qhelp
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-Cookies without HttpOnly flag are accessible to JavaScript running in the same origin. In case of
-Cross-Site Scripting (XSS) vulnerability the cookie can be stolen by malicious script.
-
-
-
-Protect sensitive cookies, such as those related to authentication, by setting HttpOnly to true to make
-them not accessible to JavaScript.
-
-
-
-
-
-Production Best Practices: Security:Use cookies securely.
-NodeJS security cheat sheet:Set cookie flags appropriately.
-express-session:cookie.httpOnly.
-cookie-session:Cookie Options.
-express response.cookie.
-Set-Cookie.
-
-
diff --git a/javascript/ql/src/Security/CWE-1004/CookieWithoutHttpOnly.ql b/javascript/ql/src/Security/CWE-1004/CookieWithoutHttpOnly.ql
deleted file mode 100644
index 40198969b4f..00000000000
--- a/javascript/ql/src/Security/CWE-1004/CookieWithoutHttpOnly.ql
+++ /dev/null
@@ -1,23 +0,0 @@
-/**
- * @name 'HttpOnly' attribute is not set to true
- * @description Omitting the 'HttpOnly' attribute for security sensitive cookie data allows
- * malicious JavaScript to steal it in case of XSS vulnerabilities. Always set
- * 'HttpOnly' to 'true' for authentication related cookies to make them
- * inaccessible from JavaScript.
- * @kind problem
- * @problem.severity warning
- * @precision high
- * @id js/cookie-httponly-not-set
- * @tags security
- * external/cwe/cwe-1004
- */
-
-import javascript
-
-from DataFlow::Node node
-where
- // TODO: Give all descriptions, qlhelp, qldocs, an overhaul. Consider precisions, severity, cwes.
- exists(CookieWrites::CookieWrite cookie | cookie = node |
- cookie.isSensitive() and not cookie.isHttpOnly()
- )
-select node, "Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie."
diff --git a/javascript/ql/src/Security/CWE-1004/examples/ClientExposedCookieBad.js b/javascript/ql/src/Security/CWE-1004/examples/ClientExposedCookieBad.js
new file mode 100644
index 00000000000..330bf86456a
--- /dev/null
+++ b/javascript/ql/src/Security/CWE-1004/examples/ClientExposedCookieBad.js
@@ -0,0 +1,7 @@
+const http = require('http');
+
+const server = http.createServer((req, res) => {
+ res.setHeader("Set-Cookie", `authKey=${makeAuthkey()}`);
+ res.writeHead(200, { 'Content-Type': 'text/html' });
+ res.end('Hello world
');
+});
\ No newline at end of file
diff --git a/javascript/ql/src/Security/CWE-1004/examples/ClientExposedCookieGood.js b/javascript/ql/src/Security/CWE-1004/examples/ClientExposedCookieGood.js
new file mode 100644
index 00000000000..09fd0b52fec
--- /dev/null
+++ b/javascript/ql/src/Security/CWE-1004/examples/ClientExposedCookieGood.js
@@ -0,0 +1,7 @@
+const http = require('http');
+
+const server = http.createServer((req, res) => {
+ res.setHeader("Set-Cookie", `authKey=${makeAuthkey()}; secure; httpOnly`);
+ res.writeHead(200, { 'Content-Type': 'text/html' });
+ res.end('Hello world
');
+});
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Security/CWE-1004/ClientExposedCookie.expected b/javascript/ql/test/query-tests/Security/CWE-1004/ClientExposedCookie.expected
new file mode 100644
index 00000000000..5616e132578
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-1004/ClientExposedCookie.expected
@@ -0,0 +1,19 @@
+| tst-httpOnly.js:11:9:15:2 | session ... BAD\\n}) | Sensitive server cookie is missing 'httpOnly' flag. |
+| tst-httpOnly.js:29:9:29:21 | session(sess) | Sensitive server cookie is missing 'httpOnly' flag. |
+| tst-httpOnly.js:38:9:38:22 | session(sess2) | Sensitive server cookie is missing 'httpOnly' flag. |
+| tst-httpOnly.js:47:9:47:22 | session(sess3) | Sensitive server cookie is missing 'httpOnly' flag. |
+| tst-httpOnly.js:51:9:55:2 | session ... BAD\\n}) | Sensitive server cookie is missing 'httpOnly' flag. |
+| tst-httpOnly.js:68:5:73:10 | res.coo ... }) | Sensitive server cookie is missing 'httpOnly' flag. |
+| tst-httpOnly.js:78:5:81:10 | res.coo ... }) | Sensitive server cookie is missing 'httpOnly' flag. |
+| tst-httpOnly.js:101:5:101:43 | res.coo ... ptions) | Sensitive server cookie is missing 'httpOnly' flag. |
+| tst-httpOnly.js:109:5:109:43 | res.coo ... ptions) | Sensitive server cookie is missing 'httpOnly' flag. |
+| tst-httpOnly.js:118:5:118:43 | res.coo ... ptions) | Sensitive server cookie is missing 'httpOnly' flag. |
+| tst-httpOnly.js:137:5:137:43 | res.coo ... ptions) | Sensitive server cookie is missing 'httpOnly' flag. |
+| tst-httpOnly.js:148:5:148:41 | res.coo ... ptions) | Sensitive server cookie is missing 'httpOnly' flag. |
+| tst-httpOnly.js:159:5:159:43 | res.coo ... ptions) | Sensitive server cookie is missing 'httpOnly' flag. |
+| tst-httpOnly.js:170:5:170:40 | res.coo ... ptions) | Sensitive server cookie is missing 'httpOnly' flag. |
+| tst-httpOnly.js:209:37:209:51 | "authKey=ninja" | Sensitive server cookie is missing 'httpOnly' flag. |
+| tst-httpOnly.js:229:38:229:52 | "authKey=ninja" | Sensitive server cookie is missing 'httpOnly' flag. |
+| tst-httpOnly.js:289:37:289:59 | `authKe ... {attr}` | Sensitive server cookie is missing 'httpOnly' flag. |
+| tst-httpOnly.js:303:9:307:2 | session ... BAD\\n}) | Sensitive server cookie is missing 'httpOnly' flag. |
+| tst-httpOnly.js:320:9:324:2 | session ... tter\\n}) | Sensitive server cookie is missing 'httpOnly' flag. |
diff --git a/javascript/ql/test/query-tests/Security/CWE-1004/ClientExposedCookie.qlref b/javascript/ql/test/query-tests/Security/CWE-1004/ClientExposedCookie.qlref
new file mode 100644
index 00000000000..683e1cd4698
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-1004/ClientExposedCookie.qlref
@@ -0,0 +1 @@
+Security/CWE-1004/ClientExposedCookie.ql
diff --git a/javascript/ql/test/query-tests/Security/CWE-1004/CookieWithoutHttpOnly.expected b/javascript/ql/test/query-tests/Security/CWE-1004/CookieWithoutHttpOnly.expected
deleted file mode 100644
index 6a3af033c67..00000000000
--- a/javascript/ql/test/query-tests/Security/CWE-1004/CookieWithoutHttpOnly.expected
+++ /dev/null
@@ -1,19 +0,0 @@
-| tst-httpOnly.js:11:9:15:2 | session ... BAD\\n}) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| tst-httpOnly.js:29:9:29:21 | session(sess) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| tst-httpOnly.js:38:9:38:22 | session(sess2) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| tst-httpOnly.js:47:9:47:22 | session(sess3) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| tst-httpOnly.js:51:9:55:2 | session ... BAD\\n}) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| tst-httpOnly.js:68:5:73:10 | res.coo ... }) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| tst-httpOnly.js:78:5:81:10 | res.coo ... }) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| tst-httpOnly.js:101:5:101:43 | res.coo ... ptions) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| tst-httpOnly.js:109:5:109:43 | res.coo ... ptions) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| tst-httpOnly.js:118:5:118:43 | res.coo ... ptions) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| tst-httpOnly.js:137:5:137:43 | res.coo ... ptions) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| tst-httpOnly.js:148:5:148:41 | res.coo ... ptions) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| tst-httpOnly.js:159:5:159:43 | res.coo ... ptions) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| tst-httpOnly.js:170:5:170:40 | res.coo ... ptions) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| tst-httpOnly.js:209:37:209:51 | "authKey=ninja" | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| tst-httpOnly.js:229:38:229:52 | "authKey=ninja" | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| tst-httpOnly.js:289:37:289:59 | `authKe ... {attr}` | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| tst-httpOnly.js:303:9:307:2 | session ... BAD\\n}) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
-| tst-httpOnly.js:320:9:324:2 | session ... tter\\n}) | Cookie attribute 'HttpOnly' is not set to true for this sensitive cookie. |
diff --git a/javascript/ql/test/query-tests/Security/CWE-1004/CookieWithoutHttpOnly.qlref b/javascript/ql/test/query-tests/Security/CWE-1004/CookieWithoutHttpOnly.qlref
deleted file mode 100644
index f97b5920b84..00000000000
--- a/javascript/ql/test/query-tests/Security/CWE-1004/CookieWithoutHttpOnly.qlref
+++ /dev/null
@@ -1 +0,0 @@
-Security/CWE-1004/CookieWithoutHttpOnly.ql
From 283b8231cb8c3de180ea58af7958d789af5758a7 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 6 Oct 2021 15:18:43 +0200
Subject: [PATCH 131/471] add more cookie models
---
.../javascript/frameworks/CookieLibraries.qll | 102 +++++++++++++-----
.../Security/CWE-614/ClearTextCookie.expected | 3 +
.../Security/CWE-614/tst-cleartextCookie.js | 17 ++-
3 files changed, 96 insertions(+), 26 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
index 444dc1c8daa..ef4f877d31e 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
@@ -59,7 +59,6 @@ module CookieWrites {
/**
* A model of the `js-cookie` library (https://github.com/js-cookie/js-cookie).
*/
-// TODO: Writes to document.cookie.
private module JsCookie {
/**
* Gets a function call that invokes method `name` of the `js-cookie` library.
@@ -118,13 +117,27 @@ private module BrowserCookies {
}
}
- class WriteAccess extends PersistentWriteAccess, DataFlow::CallNode {
- // TODO: CookieWrite
+ class WriteAccess extends PersistentWriteAccess, DataFlow::CallNode,
+ CookieWrites::ClientSideCookieWrite {
WriteAccess() { this = libMemberCall("set") }
string getKey() { getArgument(0).mayHaveStringValue(result) }
override DataFlow::Node getValue() { result = getArgument(1) }
+
+ override predicate isSecure() {
+ // A cookie is secure if there are cookie options with the `secure` flag set to `true`.
+ this.getOptionArgument(2, CookieWrites::secure()).mayHaveBooleanValue(true)
+ or
+ // or, an explicit default has been set
+ exists(DataFlow::moduleMember("browser-cookies", "defaults").getAPropertyWrite("secure"))
+ }
+
+ override predicate isSensitive() {
+ HeuristicNames::nameIndicatesSensitiveData(any(string s |
+ this.getArgument(0).mayHaveStringValue(s)
+ ), _)
+ }
}
}
@@ -147,13 +160,24 @@ private module LibCookie {
override PersistentWriteAccess getAWrite() { key = result.(WriteAccess).getKey() }
}
- class WriteAccess extends PersistentWriteAccess, DataFlow::CallNode {
- // TODO: CookieWrite
+ class WriteAccess extends PersistentWriteAccess, DataFlow::CallNode,
+ CookieWrites::ClientSideCookieWrite {
WriteAccess() { this = libMemberCall("serialize") }
string getKey() { getArgument(0).mayHaveStringValue(result) }
override DataFlow::Node getValue() { result = getArgument(1) }
+
+ override predicate isSecure() {
+ // A cookie is secure if there are cookie options with the `secure` flag set to `true`.
+ this.getOptionArgument(2, CookieWrites::secure()).mayHaveBooleanValue(true)
+ }
+
+ override predicate isSensitive() {
+ HeuristicNames::nameIndicatesSensitiveData(any(string s |
+ this.getArgument(0).mayHaveStringValue(s)
+ ), _)
+ }
}
}
@@ -286,28 +310,56 @@ private class HTTPCookieWrite extends CookieWrites::CookieWrite {
override predicate isSensitive() {
HeuristicNames::nameIndicatesSensitiveData(getCookieName(header), _)
}
+}
- /**
- * Gets cookie name from a `Set-Cookie` header value.
- * The header value always starts with `=` optionally followed by attributes:
- * `=; Domain=; Secure; HttpOnly`
- */
- bindingset[s]
- private string getCookieName(string s) {
- result = s.regexpCapture("\\s*\\b([^=\\s]*)\\b\\s*=.*", 1)
+/**
+ * Gets cookie name from a `Set-Cookie` header value.
+ * The header value always starts with `=` optionally followed by attributes:
+ * `=; Domain=; Secure; HttpOnly`
+ */
+bindingset[s]
+private string getCookieName(string s) {
+ result = s.regexpCapture("\\s*\\b([^=\\s]*)\\b\\s*=.*", 1)
+}
+
+/**
+ * Holds if the `Set-Cookie` header value contains the specified attribute
+ * 1. The attribute is case insensitive
+ * 2. It always starts with a pair `=`.
+ * If the attribute is present there must be `;` after the pair.
+ * Other attributes like `Domain=`, `Path=`, etc. may come after the pair:
+ * `=; Domain=; Secure; HttpOnly`
+ * See `https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie`
+ */
+bindingset[s, attribute]
+private predicate hasCookieAttribute(string s, string attribute) {
+ s.regexpMatch("(?i).*;\\s*" + attribute + "\\b\\s*;?.*$")
+}
+
+/**
+ * A write to `document.cookie`.
+ */
+private class DocumentCookieWrite extends CookieWrites::ClientSideCookieWrite {
+ string cookie;
+
+ DocumentCookieWrite() {
+ exists(DataFlow::PropWrite write | this = write |
+ write = DOM::documentRef().getAPropertyWrite("cookie") and
+ cookie =
+ [
+ any(string s | write.getRhs().mayHaveStringValue(s)),
+ write.getRhs().(StringOps::ConcatenationRoot).getConstantStringParts()
+ ]
+ )
}
- /**
- * Holds if the `Set-Cookie` header value contains the specified attribute
- * 1. The attribute is case insensitive
- * 2. It always starts with a pair `=`.
- * If the attribute is present there must be `;` after the pair.
- * Other attributes like `Domain=`, `Path=`, etc. may come after the pair:
- * `=; Domain=; Secure; HttpOnly`
- * See `https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie`
- */
- bindingset[s, attribute]
- private predicate hasCookieAttribute(string s, string attribute) {
- s.regexpMatch("(?i).*;\\s*" + attribute + "\\b\\s*;?.*$")
+ override predicate isSecure() {
+ // A cookie is secure if the `secure` flag is specified in the cookie definition.
+ // The default is `false`.
+ hasCookieAttribute(cookie, CookieWrites::secure())
+ }
+
+ override predicate isSensitive() {
+ HeuristicNames::nameIndicatesSensitiveData(getCookieName(cookie), _)
}
}
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/ClearTextCookie.expected b/javascript/ql/test/query-tests/Security/CWE-614/ClearTextCookie.expected
index b39ccbc514d..2f30145bbc9 100644
--- a/javascript/ql/test/query-tests/Security/CWE-614/ClearTextCookie.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-614/ClearTextCookie.expected
@@ -10,3 +10,6 @@
| tst-cleartextCookie.js:124:9:124:21 | session(sess) | Sensitive cookie sent without enforcing SSL encryption |
| tst-cleartextCookie.js:148:9:156:2 | session ... Date\\n}) | Sensitive cookie sent without enforcing SSL encryption |
| tst-cleartextCookie.js:160:33:160:58 | `authKe ... key()}` | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:173:5:173:19 | document.cookie | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:177:5:177:41 | cookies ... hkey()) | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:182:5:182:46 | cookie. ... hkey()) | Sensitive cookie sent without enforcing SSL encryption |
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js b/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js
index 2d913691889..dcc0a412c2e 100644
--- a/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js
+++ b/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js
@@ -166,4 +166,19 @@ http.createServer((req, res) => {
res.setHeader("Set-Cookie", `authKey=${makeAuthkey()}; secure; httpOnly`); // OK
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('Hello world
');
-});
\ No newline at end of file
+});
+
+function clientCookies() {
+ document.cookie = `authKey=${makeAuthkey()}; secure`; // OK
+ document.cookie = `authKey=${makeAuthkey()}`; // NOT OK
+
+ var cookies = require('browser-cookies');
+
+ cookies.set('authKey', makeAuthkey()); // NOT OK
+ cookies.set('authKey', makeAuthkey(), { secure: true, expires: 7 }); // OK
+
+ const cookie = require('cookie');
+
+ cookie.serialize('authKey', makeAuthkey()); // NOT OK
+ cookie.serialize('authKey', makeAuthkey(), { secure: true, expires: 7 }); // OK
+}
\ No newline at end of file
From 1e1e54984717b1c092301b80ebc96c42e9c746fe Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 6 Oct 2021 15:32:28 +0200
Subject: [PATCH 132/471] update tests so it's clear which cookies are insecure
---
.../Security/CWE-614/ClearTextCookie.expected | 22 +++++++++----------
.../Security/CWE-614/tst-cleartextCookie.js | 12 ++++++----
2 files changed, 19 insertions(+), 15 deletions(-)
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/ClearTextCookie.expected b/javascript/ql/test/query-tests/Security/CWE-614/ClearTextCookie.expected
index 2f30145bbc9..3b0a63421a1 100644
--- a/javascript/ql/test/query-tests/Security/CWE-614/ClearTextCookie.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-614/ClearTextCookie.expected
@@ -2,14 +2,14 @@
| tst-cleartextCookie.js:20:5:20:43 | res.coo ... ptions) | Sensitive cookie sent without enforcing SSL encryption |
| tst-cleartextCookie.js:35:1:35:52 | js_cook ... alse }) | Sensitive cookie sent without enforcing SSL encryption |
| tst-cleartextCookie.js:44:37:44:51 | "authKey=ninja" | Sensitive cookie sent without enforcing SSL encryption |
-| tst-cleartextCookie.js:64:38:64:52 | "authKey=ninja" | Sensitive cookie sent without enforcing SSL encryption |
-| tst-cleartextCookie.js:94:60:94:72 | "authKey=foo" | Sensitive cookie sent without enforcing SSL encryption |
-| tst-cleartextCookie.js:104:9:107:2 | session ... T OK\\n}) | Sensitive cookie sent without enforcing SSL encryption |
-| tst-cleartextCookie.js:109:9:112:2 | session ... T OK\\n}) | Sensitive cookie sent without enforcing SSL encryption |
-| tst-cleartextCookie.js:114:9:117:2 | session ... T OK\\n}) | Sensitive cookie sent without enforcing SSL encryption |
-| tst-cleartextCookie.js:124:9:124:21 | session(sess) | Sensitive cookie sent without enforcing SSL encryption |
-| tst-cleartextCookie.js:148:9:156:2 | session ... Date\\n}) | Sensitive cookie sent without enforcing SSL encryption |
-| tst-cleartextCookie.js:160:33:160:58 | `authKe ... key()}` | Sensitive cookie sent without enforcing SSL encryption |
-| tst-cleartextCookie.js:173:5:173:19 | document.cookie | Sensitive cookie sent without enforcing SSL encryption |
-| tst-cleartextCookie.js:177:5:177:41 | cookies ... hkey()) | Sensitive cookie sent without enforcing SSL encryption |
-| tst-cleartextCookie.js:182:5:182:46 | cookie. ... hkey()) | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:64:13:64:27 | "authKey=ninja" | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:97:13:97:25 | "authKey=foo" | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:108:9:111:2 | session ... T OK\\n}) | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:113:9:116:2 | session ... T OK\\n}) | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:118:9:121:2 | session ... T OK\\n}) | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:128:9:128:21 | session(sess) | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:152:9:160:2 | session ... Date\\n}) | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:164:33:164:58 | `authKe ... key()}` | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:177:5:177:19 | document.cookie | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:181:5:181:41 | cookies ... hkey()) | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:186:5:186:46 | cookie. ... hkey()) | Sensitive cookie sent without enforcing SSL encryption |
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js b/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js
index dcc0a412c2e..0e48411fe24 100644
--- a/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js
+++ b/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js
@@ -60,8 +60,10 @@ function test2() {
function test3() {
const server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'text/html');
- // BAD (and good, TODO: Move to separate lines)
- res.setHeader("Set-Cookie", ["authKey=ninja", "language=javascript"]);
+ res.setHeader("Set-Cookie", [
+ "authKey=ninja", // NOT OK
+ "language=javascript" // OK
+ ]);
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('ok');
});
@@ -90,8 +92,10 @@ function test5() {
function test6() {
const server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'text/html');
- // BAD (and good. TODO: Move to separate lines)
- res.setHeader("Set-Cookie", ["type=ninja; secure", "authKey=foo"]);
+ res.setHeader("Set-Cookie", [
+ "type=ninja; secure", // OK
+ "authKey=foo" // NOT OK
+ ]);
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('ok');
});
From 834d5ec6ad72c883647af06d138838d83cb829d4 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 6 Oct 2021 15:46:28 +0200
Subject: [PATCH 133/471] add session{key,id} as sensitive info
---
.../security/internal/SensitiveDataHeuristics.qll | 2 +-
.../Security/CWE-1004/ClientExposedCookie.expected | 1 +
.../test/query-tests/Security/CWE-1004/tst-httpOnly.js | 10 ++++++++++
.../security/internal/SensitiveDataHeuristics.qll | 2 +-
4 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/security/internal/SensitiveDataHeuristics.qll b/javascript/ql/lib/semmle/javascript/security/internal/SensitiveDataHeuristics.qll
index 589c37120b9..2adce57db11 100644
--- a/javascript/ql/lib/semmle/javascript/security/internal/SensitiveDataHeuristics.qll
+++ b/javascript/ql/lib/semmle/javascript/security/internal/SensitiveDataHeuristics.qll
@@ -58,7 +58,7 @@ module HeuristicNames {
*/
string maybeAccountInfo() {
result = "(?is).*acc(ou)?nt.*" or
- result = "(?is).*(puid|username|userid).*" or
+ result = "(?is).*(puid|username|userid|session(id|key)).*" or
result = "(?s).*([uU]|^|_|[a-z](?=U))([uU][iI][dD]).*"
}
diff --git a/javascript/ql/test/query-tests/Security/CWE-1004/ClientExposedCookie.expected b/javascript/ql/test/query-tests/Security/CWE-1004/ClientExposedCookie.expected
index 5616e132578..b8b29a028c2 100644
--- a/javascript/ql/test/query-tests/Security/CWE-1004/ClientExposedCookie.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-1004/ClientExposedCookie.expected
@@ -17,3 +17,4 @@
| tst-httpOnly.js:289:37:289:59 | `authKe ... {attr}` | Sensitive server cookie is missing 'httpOnly' flag. |
| tst-httpOnly.js:303:9:307:2 | session ... BAD\\n}) | Sensitive server cookie is missing 'httpOnly' flag. |
| tst-httpOnly.js:320:9:324:2 | session ... tter\\n}) | Sensitive server cookie is missing 'httpOnly' flag. |
+| tst-httpOnly.js:330:37:330:68 | "sessio ... onKey() | Sensitive server cookie is missing 'httpOnly' flag. |
diff --git a/javascript/ql/test/query-tests/Security/CWE-1004/tst-httpOnly.js b/javascript/ql/test/query-tests/Security/CWE-1004/tst-httpOnly.js
index c0835fe570f..b91f82c781f 100644
--- a/javascript/ql/test/query-tests/Security/CWE-1004/tst-httpOnly.js
+++ b/javascript/ql/test/query-tests/Security/CWE-1004/tst-httpOnly.js
@@ -322,3 +322,13 @@ app.use(session({
keys: ['key1', 'key2'],
cookie: { httpOnly: false } // BAD, It is a session cookie, name doesn't matter
}))
+
+const http = require('http');
+function test10() {
+ const server = http.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ res.setHeader("Set-Cookie", "sessionKey=" + makeSessionKey()); // BAD
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+ });
+}
\ No newline at end of file
diff --git a/python/ql/lib/semmle/python/security/internal/SensitiveDataHeuristics.qll b/python/ql/lib/semmle/python/security/internal/SensitiveDataHeuristics.qll
index 589c37120b9..2adce57db11 100644
--- a/python/ql/lib/semmle/python/security/internal/SensitiveDataHeuristics.qll
+++ b/python/ql/lib/semmle/python/security/internal/SensitiveDataHeuristics.qll
@@ -58,7 +58,7 @@ module HeuristicNames {
*/
string maybeAccountInfo() {
result = "(?is).*acc(ou)?nt.*" or
- result = "(?is).*(puid|username|userid).*" or
+ result = "(?is).*(puid|username|userid|session(id|key)).*" or
result = "(?s).*([uU]|^|_|[a-z](?=U))([uU][iI][dD]).*"
}
From 92d59aa11cab23048267930cac0851b5bc623019 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Mon, 11 Oct 2021 13:15:54 +0200
Subject: [PATCH 134/471] refactor most of the `isSensitive` predicates into a
common helper predicate
---
.../javascript/frameworks/CookieLibraries.qll | 114 +++++++++---------
1 file changed, 54 insertions(+), 60 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
index ef4f877d31e..84631eb31d1 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
@@ -56,6 +56,46 @@ module CookieWrites {
string httpOnly() { result = "httpOnly" }
}
+/**
+ * Holds if `node` looks like it can contain a sensitive cookie.
+ * Either from `node` being a sensitive expression, or from `node` containing
+ * a string value that looks like a sensitive cookie name.
+ */
+private predicate canHaveSensitiveCookie(DataFlow::Node node) {
+ exists(string s |
+ node.mayHaveStringValue(s) or
+ s = node.(StringOps::ConcatenationRoot).getConstantStringParts()
+ |
+ HeuristicNames::nameIndicatesSensitiveData([s, getCookieName(s)], _)
+ )
+ or
+ node.asExpr() instanceof SensitiveExpr
+}
+
+/**
+ * Gets cookie name from a `Set-Cookie` header value.
+ * The header value always starts with `=` optionally followed by attributes:
+ * `=; Domain=; Secure; HttpOnly`
+ */
+bindingset[s]
+private string getCookieName(string s) {
+ result = s.regexpCapture("\\s*\\b([^=\\s]*)\\b\\s*=.*", 1)
+}
+
+/**
+ * Holds if the `Set-Cookie` header value contains the specified attribute
+ * 1. The attribute is case insensitive
+ * 2. It always starts with a pair `=`.
+ * If the attribute is present there must be `;` after the pair.
+ * Other attributes like `Domain=`, `Path=`, etc. may come after the pair:
+ * `=; Domain=; Secure; HttpOnly`
+ * See `https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie`
+ */
+bindingset[s, attribute]
+private predicate hasCookieAttribute(string s, string attribute) {
+ s.regexpMatch("(?i).*;\\s*" + attribute + "\\b\\s*;?.*$")
+}
+
/**
* A model of the `js-cookie` library (https://github.com/js-cookie/js-cookie).
*/
@@ -90,11 +130,7 @@ private module JsCookie {
this.getOptionArgument(2, CookieWrites::secure()).mayHaveBooleanValue(true)
}
- override predicate isSensitive() {
- HeuristicNames::nameIndicatesSensitiveData(any(string s |
- this.getArgument(0).mayHaveStringValue(s)
- ), _)
- }
+ override predicate isSensitive() { canHaveSensitiveCookie(this.getArgument(0)) }
}
}
@@ -133,11 +169,7 @@ private module BrowserCookies {
exists(DataFlow::moduleMember("browser-cookies", "defaults").getAPropertyWrite("secure"))
}
- override predicate isSensitive() {
- HeuristicNames::nameIndicatesSensitiveData(any(string s |
- this.getArgument(0).mayHaveStringValue(s)
- ), _)
- }
+ override predicate isSensitive() { canHaveSensitiveCookie(this.getArgument(0)) }
}
}
@@ -173,11 +205,7 @@ private module LibCookie {
this.getOptionArgument(2, CookieWrites::secure()).mayHaveBooleanValue(true)
}
- override predicate isSensitive() {
- HeuristicNames::nameIndicatesSensitiveData(any(string s |
- this.getArgument(0).mayHaveStringValue(s)
- ), _)
- }
+ override predicate isSensitive() { canHaveSensitiveCookie(this.getArgument(0)) }
}
}
@@ -198,13 +226,7 @@ private module ExpressCookies {
this.getOptionArgument(2, CookieWrites::secure()).mayHaveBooleanValue(true)
}
- override predicate isSensitive() {
- HeuristicNames::nameIndicatesSensitiveData(any(string s |
- this.getArgument(0).mayHaveStringValue(s)
- ), _)
- or
- this.getArgument(0).asExpr() instanceof SensitiveExpr
- }
+ override predicate isSensitive() { canHaveSensitiveCookie(this.getArgument(0)) }
override predicate isHttpOnly() {
// A cookie is httpOnly if there are cookie options with the `httpOnly` flag set to `true`.
@@ -307,33 +329,7 @@ private class HTTPCookieWrite extends CookieWrites::CookieWrite {
hasCookieAttribute(header, CookieWrites::httpOnly())
}
- override predicate isSensitive() {
- HeuristicNames::nameIndicatesSensitiveData(getCookieName(header), _)
- }
-}
-
-/**
- * Gets cookie name from a `Set-Cookie` header value.
- * The header value always starts with `=` optionally followed by attributes:
- * `=; Domain=; Secure; HttpOnly`
- */
-bindingset[s]
-private string getCookieName(string s) {
- result = s.regexpCapture("\\s*\\b([^=\\s]*)\\b\\s*=.*", 1)
-}
-
-/**
- * Holds if the `Set-Cookie` header value contains the specified attribute
- * 1. The attribute is case insensitive
- * 2. It always starts with a pair `=`.
- * If the attribute is present there must be `;` after the pair.
- * Other attributes like `Domain=`, `Path=`, etc. may come after the pair:
- * `=; Domain=; Secure; HttpOnly`
- * See `https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie`
- */
-bindingset[s, attribute]
-private predicate hasCookieAttribute(string s, string attribute) {
- s.regexpMatch("(?i).*;\\s*" + attribute + "\\b\\s*;?.*$")
+ override predicate isSensitive() { canHaveSensitiveCookie(this) }
}
/**
@@ -341,16 +337,16 @@ private predicate hasCookieAttribute(string s, string attribute) {
*/
private class DocumentCookieWrite extends CookieWrites::ClientSideCookieWrite {
string cookie;
+ DataFlow::PropWrite write;
DocumentCookieWrite() {
- exists(DataFlow::PropWrite write | this = write |
- write = DOM::documentRef().getAPropertyWrite("cookie") and
- cookie =
- [
- any(string s | write.getRhs().mayHaveStringValue(s)),
- write.getRhs().(StringOps::ConcatenationRoot).getConstantStringParts()
- ]
- )
+ this = write and
+ write = DOM::documentRef().getAPropertyWrite("cookie") and
+ cookie =
+ [
+ any(string s | write.getRhs().mayHaveStringValue(s)),
+ write.getRhs().(StringOps::ConcatenationRoot).getConstantStringParts()
+ ]
}
override predicate isSecure() {
@@ -359,7 +355,5 @@ private class DocumentCookieWrite extends CookieWrites::ClientSideCookieWrite {
hasCookieAttribute(cookie, CookieWrites::secure())
}
- override predicate isSensitive() {
- HeuristicNames::nameIndicatesSensitiveData(getCookieName(cookie), _)
- }
+ override predicate isSensitive() { canHaveSensitiveCookie(write.getRhs()) }
}
From 311df4d2b7a5175fe3b58b1760323b11dd2e0c48 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Mon, 11 Oct 2021 13:36:08 +0200
Subject: [PATCH 135/471] add test for the `cookie` npm package
---
.../Security/CWE-614/ClearTextCookie.expected | 1 +
.../Security/CWE-614/tst-cleartextCookie.js | 12 +++++++++++-
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/ClearTextCookie.expected b/javascript/ql/test/query-tests/Security/CWE-614/ClearTextCookie.expected
index 3b0a63421a1..09206efe0f3 100644
--- a/javascript/ql/test/query-tests/Security/CWE-614/ClearTextCookie.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-614/ClearTextCookie.expected
@@ -13,3 +13,4 @@
| tst-cleartextCookie.js:177:5:177:19 | document.cookie | Sensitive cookie sent without enforcing SSL encryption |
| tst-cleartextCookie.js:181:5:181:41 | cookies ... hkey()) | Sensitive cookie sent without enforcing SSL encryption |
| tst-cleartextCookie.js:186:5:186:46 | cookie. ... hkey()) | Sensitive cookie sent without enforcing SSL encryption |
+| tst-cleartextCookie.js:195:33:195:74 | cookie. ... hkey()) | Sensitive cookie sent without enforcing SSL encryption |
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js b/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js
index 0e48411fe24..49bb26d6678 100644
--- a/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js
+++ b/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js
@@ -185,4 +185,14 @@ function clientCookies() {
cookie.serialize('authKey', makeAuthkey()); // NOT OK
cookie.serialize('authKey', makeAuthkey(), { secure: true, expires: 7 }); // OK
-}
\ No newline at end of file
+}
+
+const cookie = require('cookie');
+
+http.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ res.setHeader("Set-Cookie", cookie.serialize("authKey", makeAuthkey(), {secure: true,httpOnly: true})); // OK
+ res.setHeader("Set-Cookie", cookie.serialize("authKey", makeAuthkey())); // NOT OK
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+});
From 5228196f79aa9680dd5a5cc439c7352c4886b462 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Tue, 12 Oct 2021 12:33:41 +0200
Subject: [PATCH 136/471] fix typos and update docs
---
.../ql/lib/semmle/javascript/frameworks/CookieLibraries.qll | 3 +--
javascript/ql/src/Security/CWE-1004/ClientExposedCookie.qhelp | 4 +---
javascript/ql/src/Security/CWE-614/ClearTextCookie.qhelp | 4 ++--
3 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
index 84631eb31d1..6d09df8348b 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
@@ -24,12 +24,11 @@ module CookieWrites {
/**
* Holds if the cookie is likely an authentication cookie or otherwise sensitive.
- * Can never hold for client-side cookies.
*/
abstract predicate isSensitive();
/**
- * Holds if the cookie write happens on a server, that is `httpOnly` flag is relevant.
+ * Holds if the cookie write happens on a server, i.e. the `httpOnly` flag is relevant.
*/
predicate isServerSide() {
any() // holds by default. Client-side cookie writes should extend ClientSideCookieWrite.
diff --git a/javascript/ql/src/Security/CWE-1004/ClientExposedCookie.qhelp b/javascript/ql/src/Security/CWE-1004/ClientExposedCookie.qhelp
index d343c298000..54c689f8731 100644
--- a/javascript/ql/src/Security/CWE-1004/ClientExposedCookie.qhelp
+++ b/javascript/ql/src/Security/CWE-1004/ClientExposedCookie.qhelp
@@ -6,7 +6,7 @@
Authentication cookies stored by a server can be accessed by a client if the httpOnly flag is not set.
-
+
An attacker that manages a cross-site scripting (XSS) attack can read the cookie and hijack the session.
@@ -18,8 +18,6 @@ Set the httpOnly flag on all cookies that are not needed by the cli
-
-
The following example stores an authentication token in a cookie that can
diff --git a/javascript/ql/src/Security/CWE-614/ClearTextCookie.qhelp b/javascript/ql/src/Security/CWE-614/ClearTextCookie.qhelp
index d97ee493b64..8b0716d0a4f 100644
--- a/javascript/ql/src/Security/CWE-614/ClearTextCookie.qhelp
+++ b/javascript/ql/src/Security/CWE-614/ClearTextCookie.qhelp
@@ -22,12 +22,12 @@ attribute on the cookie.
The following example stores an authentication token in a cookie that can
be transmitted in clear text.
-
+
To force the cookie to be transmitted using SSL, set the secure
attribute on the cookie.
-
+
From 038438edcaa12003e1630343876ddb1e1d14cea2 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Tue, 12 Oct 2021 13:26:18 +0200
Subject: [PATCH 137/471] assume that setting the secure/httpOnly flag to some
unknown value is good
---
.../javascript/frameworks/CookieLibraries.qll | 26 +++++++++++++------
.../Security/CWE-614/tst-cleartextCookie.js | 15 +++++++++++
2 files changed, 33 insertions(+), 8 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
index 6d09df8348b..b9df9f227e4 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
@@ -126,7 +126,9 @@ private module JsCookie {
override predicate isSecure() {
// A cookie is secure if there are cookie options with the `secure` flag set to `true`.
- this.getOptionArgument(2, CookieWrites::secure()).mayHaveBooleanValue(true)
+ exists(DataFlow::Node value | value = this.getOptionArgument(2, CookieWrites::secure()) |
+ not value.mayHaveBooleanValue(false) // anything but `false` is accepted as being maybe true
+ )
}
override predicate isSensitive() { canHaveSensitiveCookie(this.getArgument(0)) }
@@ -162,7 +164,9 @@ private module BrowserCookies {
override predicate isSecure() {
// A cookie is secure if there are cookie options with the `secure` flag set to `true`.
- this.getOptionArgument(2, CookieWrites::secure()).mayHaveBooleanValue(true)
+ exists(DataFlow::Node value | value = this.getOptionArgument(2, CookieWrites::secure()) |
+ not value.mayHaveBooleanValue(false) // anything but `false` is accepted as being maybe true
+ )
or
// or, an explicit default has been set
exists(DataFlow::moduleMember("browser-cookies", "defaults").getAPropertyWrite("secure"))
@@ -201,7 +205,9 @@ private module LibCookie {
override predicate isSecure() {
// A cookie is secure if there are cookie options with the `secure` flag set to `true`.
- this.getOptionArgument(2, CookieWrites::secure()).mayHaveBooleanValue(true)
+ exists(DataFlow::Node value | value = this.getOptionArgument(2, CookieWrites::secure()) |
+ not value.mayHaveBooleanValue(false) // anything but `false` is accepted as being maybe true
+ )
}
override predicate isSensitive() { canHaveSensitiveCookie(this.getArgument(0)) }
@@ -222,7 +228,9 @@ private module ExpressCookies {
override predicate isSecure() {
// A cookie is secure if there are cookie options with the `secure` flag set to `true`.
// The default is `false`.
- this.getOptionArgument(2, CookieWrites::secure()).mayHaveBooleanValue(true)
+ exists(DataFlow::Node value | value = this.getOptionArgument(2, CookieWrites::secure()) |
+ not value.mayHaveBooleanValue(false) // anything but `false` is accepted as being maybe true
+ )
}
override predicate isSensitive() { canHaveSensitiveCookie(this.getArgument(0)) }
@@ -230,7 +238,9 @@ private module ExpressCookies {
override predicate isHttpOnly() {
// A cookie is httpOnly if there are cookie options with the `httpOnly` flag set to `true`.
// The default is `false`.
- this.getOptionArgument(2, CookieWrites::httpOnly()).mayHaveBooleanValue(true)
+ exists(DataFlow::Node value | value = this.getOptionArgument(2, CookieWrites::httpOnly()) |
+ not value.mayHaveBooleanValue(false) // anything but `false` is accepted as being maybe true
+ )
}
}
@@ -272,9 +282,9 @@ private module ExpressCookies {
override predicate isSecure() {
// The flag `secure` is not set by default (https://github.com/expressjs/session#Cookiesecure).
// The default value for cookie options is { path: '/', httpOnly: true, secure: false, maxAge: null }.
- // A cookie is secure if there are the cookie options with the `secure` flag set to `true` or to `auto`.
- getCookieFlagValue(CookieWrites::secure()).mayHaveBooleanValue(true) or
- getCookieFlagValue(CookieWrites::secure()).mayHaveStringValue("auto")
+ exists(DataFlow::Node value | value = getCookieFlagValue(CookieWrites::secure()) |
+ not value.mayHaveBooleanValue(false) // anything but `false` is accepted as being maybe true
+ )
}
override predicate isSensitive() {
diff --git a/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js b/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js
index 49bb26d6678..7d0828a7c77 100644
--- a/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js
+++ b/javascript/ql/test/query-tests/Security/CWE-614/tst-cleartextCookie.js
@@ -196,3 +196,18 @@ http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('ok');
});
+
+(function mightBeSecures() {
+ const express = require('express')
+ const app = express()
+ const session = require('express-session')
+
+ app.use(session({
+ secret: config.sessionSecret,
+ cookie: {
+ httpOnly: config.sessionCookie.httpOnly,
+ secure: config.sessionCookie.secure && config.secure.ssl
+ },
+ name: config.sessionKey
+ }));
+})();
\ No newline at end of file
From 9c8a51bca6cafe1a3fdb596cd1b7dca03f4899a6 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Tue, 12 Oct 2021 16:26:08 +0200
Subject: [PATCH 138/471] cache `SensitiveExpr`
---
.../ql/lib/semmle/javascript/security/SensitiveActions.qll | 3 +++
1 file changed, 3 insertions(+)
diff --git a/javascript/ql/lib/semmle/javascript/security/SensitiveActions.qll b/javascript/ql/lib/semmle/javascript/security/SensitiveActions.qll
index eb692c3b440..54803fcf857 100644
--- a/javascript/ql/lib/semmle/javascript/security/SensitiveActions.qll
+++ b/javascript/ql/lib/semmle/javascript/security/SensitiveActions.qll
@@ -14,11 +14,14 @@ import semmle.javascript.security.internal.SensitiveDataHeuristics
private import HeuristicNames
/** An expression that might contain sensitive data. */
+cached
abstract class SensitiveExpr extends Expr {
/** Gets a human-readable description of this expression for use in alert messages. */
+ cached
abstract string describe();
/** Gets a classification of the kind of sensitive data this expression might contain. */
+ cached
abstract SensitiveDataClassification getClassification();
}
From 8ba545999e38db01c40657a2160fcdc5bd9c2a77 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Tue, 26 Oct 2021 14:13:56 +0200
Subject: [PATCH 139/471] add change-note
---
javascript/change-notes/2021-10-26-cookie-queries.md | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 javascript/change-notes/2021-10-26-cookie-queries.md
diff --git a/javascript/change-notes/2021-10-26-cookie-queries.md b/javascript/change-notes/2021-10-26-cookie-queries.md
new file mode 100644
index 00000000000..04c987bd475
--- /dev/null
+++ b/javascript/change-notes/2021-10-26-cookie-queries.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* The `js/clear-text-cookie` and `js/client-exposed-cookie` queries have been renamed and moved into non-experimental.
From fe398239424148c5f6d55201a84317cb55f3c3e5 Mon Sep 17 00:00:00 2001
From: Edoardo Pirovano
Date: Tue, 26 Oct 2021 14:18:35 +0100
Subject: [PATCH 140/471] Fix LGTM version number in language reference
---
docs/codeql/ql-language-reference/ql-language-specification.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/codeql/ql-language-reference/ql-language-specification.rst b/docs/codeql/ql-language-reference/ql-language-specification.rst
index 26795bd844c..d9492f04021 100644
--- a/docs/codeql/ql-language-reference/ql-language-specification.rst
+++ b/docs/codeql/ql-language-reference/ql-language-specification.rst
@@ -1333,7 +1333,7 @@ The values of a set literal expression are all the values of all the contained e
Set literals are supported from release 2.1.0 of the CodeQL CLI, and release 1.24 of LGTM Enterprise.
-Since release 2.7.1 of the CodeQL CLI, and release 1.28 of LGTM Enterprise, a trailing comma is allowed in a set literal.
+Since release 2.7.1 of the CodeQL CLI, and release 1.30 of LGTM Enterprise, a trailing comma is allowed in a set literal.
Disambiguation of expressions
-----------------------------
From 29e3abc977bb8065db9bcd16952b69ad61073f0f Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Tue, 26 Oct 2021 15:34:16 +0200
Subject: [PATCH 141/471] Python: FastAPI: Add HTTP header taint example
---
.../library-tests/frameworks/fastapi/taint_test.py | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/python/ql/test/library-tests/frameworks/fastapi/taint_test.py b/python/ql/test/library-tests/frameworks/fastapi/taint_test.py
index 591d74a887b..27dc1882a77 100644
--- a/python/ql/test/library-tests/frameworks/fastapi/taint_test.py
+++ b/python/ql/test/library-tests/frameworks/fastapi/taint_test.py
@@ -73,6 +73,17 @@ async def form_example(username: str = Form(None)): # $ requestHandler routedPar
return "ok" # $ HttpResponse
+# --- HTTP headers ---
+# see https://fastapi.tiangolo.com/tutorial/header-params/
+
+from fastapi import Header
+
+@app.get("/header-example") # $ routeSetup="/header-example"
+async def header_example(user_agent: Optional[str] = Header(None)): # $ requestHandler routedParameter=user_agent
+ ensure_tainted(user_agent) # $ tainted
+ return "ok" # $ HttpResponse
+
+
# --- file upload ---
# see https://fastapi.tiangolo.com/tutorial/request-files/
# see https://fastapi.tiangolo.com/tutorial/request-files/#uploadfile
From 6f7d0b62d7bb45e6150792fe06cafd8b6e232292 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Tue, 26 Oct 2021 17:11:27 +0200
Subject: [PATCH 142/471] Add ExplicitIntentSanitizer and
allowIntentExtrasImplicitRead
---
.../code/java/frameworks/android/Intent.qll | 55 +++++++++++++++++++
.../AndroidSensitiveCommunicationQuery.qll | 31 +----------
.../CWE/CWE-200/AndroidFileIntentSource.qll | 6 +-
3 files changed, 57 insertions(+), 35 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll b/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll
index 296d6b39b2b..3d526ef47da 100644
--- a/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll
+++ b/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll
@@ -77,6 +77,61 @@ class IntentGetParcelableExtraMethod extends Method {
}
}
+/** The class `android.os.BaseBundle`, or a class that extends it. */
+class AndroidBundle extends Class {
+ AndroidBundle() { this.getASupertype*().hasQualifiedName("android.os", "BaseBundle") }
+}
+
+/** An `Intent` that explicitly sets a destination component. */
+class ExplicitIntent extends Expr {
+ ExplicitIntent() {
+ exists(MethodAccess ma, Method m |
+ ma.getMethod() = m and
+ m.getDeclaringType() instanceof TypeIntent and
+ m.hasName(["setPackage", "setClass", "setClassName", "setComponent"]) and
+ ma.getQualifier() = this
+ )
+ or
+ exists(ConstructorCall cc, Argument classArg |
+ cc.getConstructedType() instanceof TypeIntent and
+ cc.getAnArgument() = classArg and
+ classArg.getType() instanceof TypeClass and
+ not exists(NullLiteral nullLiteral | DataFlow::localExprFlow(nullLiteral, classArg)) and
+ cc = this
+ )
+ }
+}
+
+/**
+ * A sanitizer for explicit intents.
+ *
+ * Use this when you want to work only with implicit intents
+ * in a `DataFlow` or `TaintTracking` configuration.
+ */
+class ExplicitIntentSanitizer extends DataFlow::Node {
+ ExplicitIntentSanitizer() {
+ exists(ExplicitIntent explIntent | DataFlow::localExprFlow(explIntent, this.asExpr()))
+ }
+}
+
+private class BundleExtrasSyntheticField extends SyntheticField {
+ BundleExtrasSyntheticField() { this = "android.content.Intent.extras" }
+
+ override RefType getType() { result instanceof AndroidBundle }
+}
+
+/**
+ * Holds if extras may be implicitly read from the Intent `node`.
+ */
+predicate allowIntentExtrasImplicitRead(DataFlow::Node node, DataFlow::Content c) {
+ node.getType() instanceof TypeIntent and
+ (
+ c instanceof DataFlow::MapValueContent
+ or
+ c.(DataFlow::SyntheticFieldContent).getType() instanceof AndroidBundle
+ )
+}
+
private class IntentBundleFlowSteps extends SummaryModelCsv {
override predicate row(string row) {
row =
diff --git a/java/ql/lib/semmle/code/java/security/AndroidSensitiveCommunicationQuery.qll b/java/ql/lib/semmle/code/java/security/AndroidSensitiveCommunicationQuery.qll
index a03f74d5044..2d5f5b0fd85 100644
--- a/java/ql/lib/semmle/code/java/security/AndroidSensitiveCommunicationQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/AndroidSensitiveCommunicationQuery.qll
@@ -121,31 +121,6 @@ private predicate isStartActivityOrServiceSink(DataFlow::Node arg) {
)
}
-private predicate isCleanIntent(Expr intent) {
- intent.getType() instanceof TypeIntent and
- (
- exists(MethodAccess setRecieverMa |
- setRecieverMa.getQualifier() = intent and
- setRecieverMa.getMethod().hasName(["setPackage", "setClass", "setClassName", "setComponent"])
- )
- or
- // Handle the cases where the PackageContext and Class are set at construction time
- // Intent(Context packageContext, Class> cls)
- // Intent(String action, Uri uri, Context packageContext, Class> cls)
- exists(ConstructorCall cc | cc = intent |
- cc.getConstructedType() instanceof TypeIntent and
- cc.getNumArgument() > 1 and
- (
- cc.getArgument(0).getType() instanceof TypeContext and
- not maybeNullArg(cc.getArgument(1))
- or
- cc.getArgument(2).getType() instanceof TypeContext and
- not maybeNullArg(cc.getArgument(3))
- )
- )
- )
-}
-
/**
* Taint configuration tracking flow from variables containing sensitive information to broadcast Intents.
*/
@@ -165,11 +140,7 @@ class SensitiveCommunicationConfig extends TaintTracking::Configuration {
/**
* Holds if broadcast doesn't specify receiving package name of the 3rd party app
*/
- override predicate isSanitizer(DataFlow::Node node) {
- exists(DataFlow::Node intent | isCleanIntent(intent.asExpr()) |
- DataFlow::localFlow(intent, node)
- )
- }
+ override predicate isSanitizer(DataFlow::Node node) { node instanceof ExplicitIntentSanitizer }
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::Content c) {
super.allowImplicitRead(node, c)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-200/AndroidFileIntentSource.qll b/java/ql/src/experimental/Security/CWE/CWE-200/AndroidFileIntentSource.qll
index 22935997afe..15eefaaeb63 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-200/AndroidFileIntentSource.qll
+++ b/java/ql/src/experimental/Security/CWE/CWE-200/AndroidFileIntentSource.qll
@@ -48,11 +48,7 @@ class GetContentIntentConfig extends TaintTracking2::Configuration {
// Allow the wrapped intent created by Intent.getChooser to be consumed
// by at the sink:
isSink(node) and
- (
- content.(DataFlow::SyntheticFieldContent).getField() = "android.content.Intent.extras"
- or
- content instanceof DataFlow::MapValueContent
- )
+ allowIntentExtrasImplicitRead(node, content)
}
}
From c850554467456faeaab66ec19a48bc53a66b898d Mon Sep 17 00:00:00 2001
From: yoff
Date: Wed, 27 Oct 2021 11:09:37 +0200
Subject: [PATCH 143/471] Update
python/ql/lib/semmle/python/frameworks/SqlAlchemy.qll
Co-authored-by: Rasmus Wriedt Larsen
---
python/ql/lib/semmle/python/frameworks/SqlAlchemy.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/lib/semmle/python/frameworks/SqlAlchemy.qll b/python/ql/lib/semmle/python/frameworks/SqlAlchemy.qll
index 6f8bca5aa7e..7e168524b89 100644
--- a/python/ql/lib/semmle/python/frameworks/SqlAlchemy.qll
+++ b/python/ql/lib/semmle/python/frameworks/SqlAlchemy.qll
@@ -315,7 +315,7 @@ module SqlAlchemy {
*/
abstract class TextClauseConstruction extends SqlConstruction::Range, DataFlow::CallCfgNode {
/** Gets the argument that specifies the SQL text. */
- final override DataFlow::Node getSql() {
+ override DataFlow::Node getSql() {
result in [this.getArg(0), this.getArgByName("text")]
}
}
From 01ad19b82bb46c500d97d5f29f423570618caeee Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Wed, 27 Oct 2021 11:40:57 +0200
Subject: [PATCH 144/471] Python: correct qldoc
---
python/ql/lib/semmle/python/Concepts.qll | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/python/ql/lib/semmle/python/Concepts.qll b/python/ql/lib/semmle/python/Concepts.qll
index 99d7e8ac038..c1d3063151f 100644
--- a/python/ql/lib/semmle/python/Concepts.qll
+++ b/python/ql/lib/semmle/python/Concepts.qll
@@ -341,7 +341,7 @@ class SqlConstruction extends DataFlow::Node {
SqlConstruction() { this = range }
- /** Gets the argument that specifies the SQL statements to be executed. */
+ /** Gets the argument that specifies the SQL statements to be constructed. */
DataFlow::Node getSql() { result = range.getSql() }
}
@@ -356,7 +356,7 @@ module SqlConstruction {
* extend `SqlExecution` instead.
*/
abstract class Range extends DataFlow::Node {
- /** Gets the argument that specifies the SQL statements to be executed. */
+ /** Gets the argument that specifies the SQL statements to be constructed. */
abstract DataFlow::Node getSql();
}
}
From 826f44d98eaea1a791ea21401b0312b25e5ecccb Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Wed, 27 Oct 2021 11:41:18 +0200
Subject: [PATCH 145/471] Python: Share implementation of `awaited`
---
python/ql/lib/semmle/python/ApiGraphs.qll | 39 +---------------
.../lib/semmle/python/frameworks/Asyncpg.qll | 37 +--------------
.../ql/lib/semmle/python/internal/Awaited.qll | 45 +++++++++++++++++++
.../dataflow/ApiGraphs/awaited.ql | 12 ++---
4 files changed, 55 insertions(+), 78 deletions(-)
create mode 100644 python/ql/lib/semmle/python/internal/Awaited.qll
diff --git a/python/ql/lib/semmle/python/ApiGraphs.qll b/python/ql/lib/semmle/python/ApiGraphs.qll
index a93c56f94dc..ef655a5f0d5 100644
--- a/python/ql/lib/semmle/python/ApiGraphs.qll
+++ b/python/ql/lib/semmle/python/ApiGraphs.qll
@@ -302,6 +302,8 @@ module API {
* API graph node for the prefix `foo`), in accordance with the usual semantics of Python.
*/
+ private import semmle.python.internal.Awaited
+
cached
newtype TApiNode =
/** The root of the API graph. */
@@ -485,43 +487,6 @@ module API {
)
}
- /**
- * Holds if `result` is the result of awaiting `awaitedValue`.
- */
- cached
- DataFlow::Node awaited(DataFlow::Node awaitedValue) {
- // `await` x
- // - `awaitedValue` is `x`
- // - `result` is `await x`
- exists(Await await |
- await.getValue() = awaitedValue.asExpr() and
- result.asExpr() = await
- )
- or
- // `async for x in l`
- // - `awaitedValue` is `l`
- // - `result` is `l` (`x` is behind a read step)
- exists(AsyncFor asyncFor |
- // To consider `x` the result of awaiting, we would use asyncFor.getTarget() = awaitedValue.asExpr(),
- // but that is behind a read step rather than a flow step.
- asyncFor.getIter() = awaitedValue.asExpr() and
- result.asExpr() = asyncFor.getIter()
- )
- or
- // `async with x as y`
- // - `awaitedValue` is `x`
- // - `result` is `x` and `y` if it exists
- exists(AsyncWith asyncWith |
- awaitedValue.asExpr() = asyncWith.getContextExpr() and
- result.asExpr() in [
- // `x`
- asyncWith.getContextExpr(),
- // `y`, if it exists
- asyncWith.getOptionalVars()
- ]
- )
- }
-
/**
* Holds if `ref` is a use of a node that should have an incoming edge from `base` labeled
* `lbl` in the API graph.
diff --git a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
index 7e8e94e0897..031342a4c23 100644
--- a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
+++ b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll
@@ -10,6 +10,8 @@ private import semmle.python.ApiGraphs
/** Provides models for the `asyncpg` PyPI package. */
private module Asyncpg {
+ private import semmle.python.internal.Awaited
+
/** A `ConectionPool` is created when the result of `asyncpg.create_pool()` is awaited. */
API::Node connectionPool() {
result = API::moduleImport("asyncpg").getMember("create_pool").getReturn().getAwaited()
@@ -63,41 +65,6 @@ private module Asyncpg {
}
}
- /**
- * Holds if `result` is the result of awaiting `awaitedValue`.
- *
- * Internal helper predicate to achieve the same as `.awaited()` does for API graphs,
- * but sutiable for use with type-tracking.
- */
- pragma[inline]
- DataFlow::Node awaited(DataFlow::Node awaitedValue) {
- // `await` x
- // - `awaitedValue` is `x`
- // - `result` is `await x`
- exists(Await await |
- await.getValue() = awaitedValue.asExpr() and
- result.asExpr() = await
- )
- or
- // `async for x in l`
- // - `awaitedValue` is local source of `l`
- // - `result` is `l`
- exists(AsyncFor asyncFor, DataFlow::Node awaited |
- asyncFor.getIter() = awaited.asExpr() and
- awaited.getALocalSource() = awaitedValue and
- result.asExpr() = asyncFor.getIter()
- )
- or
- // `async with x as y`
- // - `awaitedValue` is local source of `x`
- // - `result` is `x` and `y`
- exists(AsyncWith asyncWith, DataFlow::Node awaited |
- awaited.asExpr() = asyncWith.getContextExpr() and
- awaited.getALocalSource() = awaitedValue and
- result.asExpr() in [asyncWith.getContextExpr(), asyncWith.getOptionalVars()]
- )
- }
-
/**
* Provides models of the `PreparedStatement` class in `asyncpg`.
* `PreparedStatement`s are created when the result of calling `prepare(query)` on a connection is awaited.
diff --git a/python/ql/lib/semmle/python/internal/Awaited.qll b/python/ql/lib/semmle/python/internal/Awaited.qll
new file mode 100644
index 00000000000..bca318913d5
--- /dev/null
+++ b/python/ql/lib/semmle/python/internal/Awaited.qll
@@ -0,0 +1,45 @@
+/**
+ * INTERNAL: Do not use.
+ *
+ * Provides helper class for defining additional API graph edges.
+ */
+
+private import python
+private import semmle.python.dataflow.new.DataFlow
+
+/**
+ * Holds if `result` is the result of awaiting `awaitedValue`.
+ */
+cached
+DataFlow::Node awaited(DataFlow::Node awaitedValue) {
+ // `await` x
+ // - `awaitedValue` is `x`
+ // - `result` is `await x`
+ exists(Await await |
+ await.getValue() = awaitedValue.asExpr() and
+ result.asExpr() = await
+ )
+ or
+ // `async for x in l`
+ // - `awaitedValue` is `l`
+ // - `result` is `l` (`x` is behind a read step)
+ exists(AsyncFor asyncFor |
+ // To consider `x` the result of awaiting, we would use asyncFor.getTarget() = awaitedValue.asExpr(),
+ // but that is behind a read step rather than a flow step.
+ asyncFor.getIter() = awaitedValue.asExpr() and
+ result.asExpr() = asyncFor.getIter()
+ )
+ or
+ // `async with x as y`
+ // - `awaitedValue` is `x`
+ // - `result` is `x` and `y` if it exists
+ exists(AsyncWith asyncWith |
+ awaitedValue.asExpr() = asyncWith.getContextExpr() and
+ result.asExpr() in [
+ // `x`
+ asyncWith.getContextExpr(),
+ // `y`, if it exists
+ asyncWith.getOptionalVars()
+ ]
+ )
+}
diff --git a/python/ql/test/experimental/dataflow/ApiGraphs/awaited.ql b/python/ql/test/experimental/dataflow/ApiGraphs/awaited.ql
index aa140431d2f..a786ff4fef9 100644
--- a/python/ql/test/experimental/dataflow/ApiGraphs/awaited.ql
+++ b/python/ql/test/experimental/dataflow/ApiGraphs/awaited.ql
@@ -9,18 +9,18 @@ class AwaitedTest extends InlineExpectationsTest {
override string getARelevantTag() { result = "awaited" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
- exists(API::Node a, DataFlow::Node n, API::Node pred |
- a = pred.getAwaited() and
- n = a.getAUse() and
- location = n.getLocation() and
+ exists(API::Node awaited, DataFlow::Node use, API::Node pred |
+ awaited = pred.getAwaited() and
+ use = awaited.getAUse() and
+ location = use.getLocation() and
// Module variable nodes have no suitable location, so it's best to simply exclude them entirely
// from the inline tests.
- not n instanceof DataFlow::ModuleVariableNode and
+ not use instanceof DataFlow::ModuleVariableNode and
exists(location.getFile().getRelativePath())
|
tag = "awaited" and
value = pred.getPath() and
- element = n.toString()
+ element = use.toString()
)
}
}
From 06586a13a35d04f46cb00b750e524d9db954c475 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Wed, 27 Oct 2021 11:55:04 +0200
Subject: [PATCH 146/471] Python: merge tests files
---
.../frameworks/asyncpg/FileSystemAccess.py | 29 -------------------
.../asyncpg/{SqlExecution.py => asyncpg.py} | 11 +++++++
2 files changed, 11 insertions(+), 29 deletions(-)
delete mode 100644 python/ql/test/library-tests/frameworks/asyncpg/FileSystemAccess.py
rename python/ql/test/library-tests/frameworks/asyncpg/{SqlExecution.py => asyncpg.py} (78%)
diff --git a/python/ql/test/library-tests/frameworks/asyncpg/FileSystemAccess.py b/python/ql/test/library-tests/frameworks/asyncpg/FileSystemAccess.py
deleted file mode 100644
index e835f4becdc..00000000000
--- a/python/ql/test/library-tests/frameworks/asyncpg/FileSystemAccess.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import asyncio
-import asyncpg
-
-async def test_connection():
- conn = await asyncpg.connect()
-
- try:
- # The file-like object is passed in as a keyword-only argument.
- # See https://magicstack.github.io/asyncpg/current/api/index.html#asyncpg.connection.Connection.copy_from_query
- await conn.copy_from_query("sql", output="filepath") # $ getSql="sql" getAPathArgument="filepath"
- await conn.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ getSql="sql" getAPathArgument="filepath"
-
- await conn.copy_from_table("table", output="filepath") # $ getAPathArgument="filepath"
- await conn.copy_to_table("table", source="filepath") # $ getAPathArgument="filepath"
-
- finally:
- await conn.close()
-
-async def test_connection_pool():
- pool = await asyncpg.create_pool()
-
- try:
- await pool.copy_from_query("sql", output="filepath") # $ getSql="sql" getAPathArgument="filepath"
- await pool.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ getSql="sql" getAPathArgument="filepath"
- await pool.copy_from_table("table", output="filepath") # $ getAPathArgument="filepath"
- await pool.copy_to_table("table", source="filepath") # $ getAPathArgument="filepath"
-
- finally:
- await pool.close()
diff --git a/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py b/python/ql/test/library-tests/frameworks/asyncpg/asyncpg.py
similarity index 78%
rename from python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py
rename to python/ql/test/library-tests/frameworks/asyncpg/asyncpg.py
index 0ffaaacba62..e4b3c895ece 100644
--- a/python/ql/test/library-tests/frameworks/asyncpg/SqlExecution.py
+++ b/python/ql/test/library-tests/frameworks/asyncpg/asyncpg.py
@@ -5,7 +5,14 @@ async def test_connection():
conn = await asyncpg.connect()
try:
+ # The file-like object is passed in as a keyword-only argument.
+ # See https://magicstack.github.io/asyncpg/current/api/index.html#asyncpg.connection.Connection.copy_from_query
await conn.copy_from_query("sql", output="filepath") # $ getSql="sql" getAPathArgument="filepath"
+ await conn.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ getSql="sql" getAPathArgument="filepath"
+
+ await conn.copy_from_table("table", output="filepath") # $ getAPathArgument="filepath"
+ await conn.copy_to_table("table", source="filepath") # $ getAPathArgument="filepath"
+
await conn.execute("sql") # $ getSql="sql"
await conn.executemany("sql") # $ getSql="sql"
await conn.fetch("sql") # $ getSql="sql"
@@ -63,6 +70,10 @@ async def test_connection_pool():
try:
await pool.copy_from_query("sql", output="filepath") # $ getSql="sql" getAPathArgument="filepath"
+ await pool.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ getSql="sql" getAPathArgument="filepath"
+ await pool.copy_from_table("table", output="filepath") # $ getAPathArgument="filepath"
+ await pool.copy_to_table("table", source="filepath") # $ getAPathArgument="filepath"
+
await pool.execute("sql") # $ getSql="sql"
await pool.executemany("sql") # $ getSql="sql"
await pool.fetch("sql") # $ getSql="sql"
From 8077a49109a3a2e31c686ea2d1d60d7bd2bab7f9 Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Wed, 27 Oct 2021 10:31:41 +0200
Subject: [PATCH 147/471] Switch qhelp-pr-preview.yml to pull_request_target
---
.github/workflows/qhelp-pr-preview.yml | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/qhelp-pr-preview.yml b/.github/workflows/qhelp-pr-preview.yml
index 7e2ea6a10f8..6e1a9e53ceb 100644
--- a/.github/workflows/qhelp-pr-preview.yml
+++ b/.github/workflows/qhelp-pr-preview.yml
@@ -1,10 +1,14 @@
name: Query help preview
+permissions:
+ contents: read
+ pull-requests: write
+
on:
- pull_request:
+ pull_request_target:
branches:
- main
- - 'rc/*'
+ - "rc/*"
paths:
- "ruby/**/*.qhelp"
@@ -14,14 +18,14 @@ jobs:
steps:
- uses: actions/checkout@v2
with:
+ ref: refs/pull/${{ github.event.number }}/merge
fetch-depth: 2
- name: Determine changed files
id: changes
run: |
- echo -n "::set-output name=qhelp_files::"
- (git diff --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep .qhelp$ | grep -v .inc.qhelp;
- git diff --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep .inc.qhelp$ | xargs -d '\n' -rn1 basename | xargs -d '\n' -rn1 git grep -l) |
- sort -u | xargs -d '\n' -n1 printf "'%s' "
+ (git diff --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep '.qhelp$' | grep -v '.inc.qhelp';
+ git diff --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep '.inc.qhelp$' | xargs -d '\n' -rn1 basename | xargs -d '\n' -rn1 git grep -l) |
+ sort -u > "${{ runner.temp }}/paths.txt"
- uses: ./.github/actions/fetch-codeql
@@ -29,10 +33,10 @@ jobs:
if: ${{ steps.changes.outputs.qhelp_files }}
run: |
( echo "QHelp previews:";
- for path in ${{ steps.changes.outputs.qhelp_files }} ; do
+ cat "${{ runner.temp }}/paths.txt" | while read path; do
echo " ${path}
"
echo
- codeql generate query-help --format=markdown ${path}
+ codeql generate query-help --format=markdown "${path}"
echo " "
done) | gh pr comment "${{ github.event.pull_request.number }}" -F -
env:
From 54e946918a25f6476de78584d773ccbbb8fd6df6 Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Wed, 27 Oct 2021 12:47:39 +0200
Subject: [PATCH 148/471] QHelp preview: run if paths.txt is non-empty
---
.github/workflows/qhelp-pr-preview.yml | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/qhelp-pr-preview.yml b/.github/workflows/qhelp-pr-preview.yml
index 6e1a9e53ceb..91ab157fd0a 100644
--- a/.github/workflows/qhelp-pr-preview.yml
+++ b/.github/workflows/qhelp-pr-preview.yml
@@ -30,14 +30,15 @@ jobs:
- uses: ./.github/actions/fetch-codeql
- name: QHelp preview
- if: ${{ steps.changes.outputs.qhelp_files }}
run: |
- ( echo "QHelp previews:";
- cat "${{ runner.temp }}/paths.txt" | while read path; do
- echo " ${path}
"
- echo
- codeql generate query-help --format=markdown "${path}"
- echo " "
- done) | gh pr comment "${{ github.event.pull_request.number }}" -F -
+ if [ -s "${{ runner.temp }}/paths.txt" ]; then
+ ( echo "QHelp previews:";
+ cat "${{ runner.temp }}/paths.txt" | while read path; do
+ echo " ${path}
"
+ echo
+ codeql generate query-help --format=markdown "${path}"
+ echo " "
+ done) | gh pr comment "${{ github.event.pull_request.number }}" -F -
+ fi
env:
GITHUB_TOKEN: ${{ github.token }}
From ce3a19458da9dfd13cc7f069158f4b138acd7c99 Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Wed, 27 Oct 2021 13:30:22 +0200
Subject: [PATCH 149/471] Set persist-credentials: false
---
.github/workflows/qhelp-pr-preview.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/qhelp-pr-preview.yml b/.github/workflows/qhelp-pr-preview.yml
index 91ab157fd0a..16d0a50e1d4 100644
--- a/.github/workflows/qhelp-pr-preview.yml
+++ b/.github/workflows/qhelp-pr-preview.yml
@@ -20,6 +20,7 @@ jobs:
with:
ref: refs/pull/${{ github.event.number }}/merge
fetch-depth: 2
+ persist-credentials: false
- name: Determine changed files
id: changes
run: |
From 51cebdce83697924d06c9ac9efca68ad09fb19f5 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Thu, 21 Oct 2021 15:33:11 +0200
Subject: [PATCH 150/471] Dataflow: Add support for call context restrictions
on sources/sinks.
---
.../java/dataflow/internal/DataFlowImpl.qll | 115 +++++++++++++-----
.../dataflow/internal/DataFlowImplCommon.qll | 36 ++++++
2 files changed, 119 insertions(+), 32 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll
index e11244c42b0..209944587e5 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll
@@ -2,6 +2,42 @@ private import DataFlowImplSpecific::Private
private import DataFlowImplSpecific::Public
import Cached
+module DataFlowImplCommonPublic {
+ private newtype TFlowFeature =
+ TFeatureHasSourceCallContext() or
+ TFeatureHasSinkCallContext() or
+ TFeatureEqualSourceSinkCallContext()
+
+ /** A flow configuration feature for use in `Configuration::getAFeature()`. */
+ class FlowFeature extends TFlowFeature {
+ abstract string toString();
+ }
+
+ /**
+ * A flow configuration feature that implies that sources have some existing
+ * call context.
+ */
+ class FeatureHasSourceCallContext extends FlowFeature, TFeatureHasSourceCallContext {
+ override string toString() { result = "FeatureHasSourceCallContext" }
+ }
+
+ /**
+ * A flow configuration feature that implies that sinks have some existing
+ * call context.
+ */
+ class FeatureHasSinkCallContext extends FlowFeature, TFeatureHasSinkCallContext {
+ override string toString() { result = "FeatureHasSinkCallContext" }
+ }
+
+ /**
+ * A flow configuration feature that implies that source-sink pairs have some
+ * shared existing call context.
+ */
+ class FeatureEqualSourceSinkCallContext extends FlowFeature, TFeatureEqualSourceSinkCallContext {
+ override string toString() { result = "FeatureEqualSourceSinkCallContext" }
+ }
+}
+
/**
* The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion.
*
From 034c7f3538a5f6dd17e2f156b7ceecdad2562f5b Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Thu, 21 Oct 2021 15:33:58 +0200
Subject: [PATCH 151/471] Dataflow: Sync.
---
.../cpp/dataflow/internal/DataFlowImpl.qll | 115 +++++++++++++-----
.../cpp/dataflow/internal/DataFlowImpl2.qll | 115 +++++++++++++-----
.../cpp/dataflow/internal/DataFlowImpl3.qll | 115 +++++++++++++-----
.../cpp/dataflow/internal/DataFlowImpl4.qll | 115 +++++++++++++-----
.../dataflow/internal/DataFlowImplCommon.qll | 36 ++++++
.../dataflow/internal/DataFlowImplLocal.qll | 115 +++++++++++++-----
.../cpp/ir/dataflow/internal/DataFlowImpl.qll | 115 +++++++++++++-----
.../ir/dataflow/internal/DataFlowImpl2.qll | 115 +++++++++++++-----
.../ir/dataflow/internal/DataFlowImpl3.qll | 115 +++++++++++++-----
.../ir/dataflow/internal/DataFlowImpl4.qll | 115 +++++++++++++-----
.../dataflow/internal/DataFlowImplCommon.qll | 36 ++++++
.../csharp/dataflow/internal/DataFlowImpl.qll | 115 +++++++++++++-----
.../dataflow/internal/DataFlowImpl2.qll | 115 +++++++++++++-----
.../dataflow/internal/DataFlowImpl3.qll | 115 +++++++++++++-----
.../dataflow/internal/DataFlowImpl4.qll | 115 +++++++++++++-----
.../dataflow/internal/DataFlowImpl5.qll | 115 +++++++++++++-----
.../dataflow/internal/DataFlowImplCommon.qll | 36 ++++++
.../java/dataflow/internal/DataFlowImpl2.qll | 115 +++++++++++++-----
.../java/dataflow/internal/DataFlowImpl3.qll | 115 +++++++++++++-----
.../java/dataflow/internal/DataFlowImpl4.qll | 115 +++++++++++++-----
.../java/dataflow/internal/DataFlowImpl5.qll | 115 +++++++++++++-----
.../java/dataflow/internal/DataFlowImpl6.qll | 115 +++++++++++++-----
.../DataFlowImplForSerializability.qll | 115 +++++++++++++-----
.../dataflow/new/internal/DataFlowImpl.qll | 115 +++++++++++++-----
.../dataflow/new/internal/DataFlowImpl2.qll | 115 +++++++++++++-----
.../dataflow/new/internal/DataFlowImpl3.qll | 115 +++++++++++++-----
.../dataflow/new/internal/DataFlowImpl4.qll | 115 +++++++++++++-----
.../new/internal/DataFlowImplCommon.qll | 36 ++++++
28 files changed, 2136 insertions(+), 768 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
index e11244c42b0..209944587e5 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
@@ -2,6 +2,42 @@ private import DataFlowImplSpecific::Private
private import DataFlowImplSpecific::Public
import Cached
+module DataFlowImplCommonPublic {
+ private newtype TFlowFeature =
+ TFeatureHasSourceCallContext() or
+ TFeatureHasSinkCallContext() or
+ TFeatureEqualSourceSinkCallContext()
+
+ /** A flow configuration feature for use in `Configuration::getAFeature()`. */
+ class FlowFeature extends TFlowFeature {
+ abstract string toString();
+ }
+
+ /**
+ * A flow configuration feature that implies that sources have some existing
+ * call context.
+ */
+ class FeatureHasSourceCallContext extends FlowFeature, TFeatureHasSourceCallContext {
+ override string toString() { result = "FeatureHasSourceCallContext" }
+ }
+
+ /**
+ * A flow configuration feature that implies that sinks have some existing
+ * call context.
+ */
+ class FeatureHasSinkCallContext extends FlowFeature, TFeatureHasSinkCallContext {
+ override string toString() { result = "FeatureHasSinkCallContext" }
+ }
+
+ /**
+ * A flow configuration feature that implies that source-sink pairs have some
+ * shared existing call context.
+ */
+ class FeatureEqualSourceSinkCallContext extends FlowFeature, TFeatureEqualSourceSinkCallContext {
+ override string toString() { result = "FeatureEqualSourceSinkCallContext" }
+ }
+}
+
/**
* The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion.
*
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
index e11244c42b0..209944587e5 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
@@ -2,6 +2,42 @@ private import DataFlowImplSpecific::Private
private import DataFlowImplSpecific::Public
import Cached
+module DataFlowImplCommonPublic {
+ private newtype TFlowFeature =
+ TFeatureHasSourceCallContext() or
+ TFeatureHasSinkCallContext() or
+ TFeatureEqualSourceSinkCallContext()
+
+ /** A flow configuration feature for use in `Configuration::getAFeature()`. */
+ class FlowFeature extends TFlowFeature {
+ abstract string toString();
+ }
+
+ /**
+ * A flow configuration feature that implies that sources have some existing
+ * call context.
+ */
+ class FeatureHasSourceCallContext extends FlowFeature, TFeatureHasSourceCallContext {
+ override string toString() { result = "FeatureHasSourceCallContext" }
+ }
+
+ /**
+ * A flow configuration feature that implies that sinks have some existing
+ * call context.
+ */
+ class FeatureHasSinkCallContext extends FlowFeature, TFeatureHasSinkCallContext {
+ override string toString() { result = "FeatureHasSinkCallContext" }
+ }
+
+ /**
+ * A flow configuration feature that implies that source-sink pairs have some
+ * shared existing call context.
+ */
+ class FeatureEqualSourceSinkCallContext extends FlowFeature, TFeatureEqualSourceSinkCallContext {
+ override string toString() { result = "FeatureEqualSourceSinkCallContext" }
+ }
+}
+
/**
* The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion.
*
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll
index e11244c42b0..209944587e5 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll
@@ -2,6 +2,42 @@ private import DataFlowImplSpecific::Private
private import DataFlowImplSpecific::Public
import Cached
+module DataFlowImplCommonPublic {
+ private newtype TFlowFeature =
+ TFeatureHasSourceCallContext() or
+ TFeatureHasSinkCallContext() or
+ TFeatureEqualSourceSinkCallContext()
+
+ /** A flow configuration feature for use in `Configuration::getAFeature()`. */
+ class FlowFeature extends TFlowFeature {
+ abstract string toString();
+ }
+
+ /**
+ * A flow configuration feature that implies that sources have some existing
+ * call context.
+ */
+ class FeatureHasSourceCallContext extends FlowFeature, TFeatureHasSourceCallContext {
+ override string toString() { result = "FeatureHasSourceCallContext" }
+ }
+
+ /**
+ * A flow configuration feature that implies that sinks have some existing
+ * call context.
+ */
+ class FeatureHasSinkCallContext extends FlowFeature, TFeatureHasSinkCallContext {
+ override string toString() { result = "FeatureHasSinkCallContext" }
+ }
+
+ /**
+ * A flow configuration feature that implies that source-sink pairs have some
+ * shared existing call context.
+ */
+ class FeatureEqualSourceSinkCallContext extends FlowFeature, TFeatureEqualSourceSinkCallContext {
+ override string toString() { result = "FeatureEqualSourceSinkCallContext" }
+ }
+}
+
/**
* The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion.
*
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll
index b3d03ea4e26..97ae7eb973b 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,36 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3663,8 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll
index e11244c42b0..209944587e5 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll
@@ -2,6 +2,42 @@ private import DataFlowImplSpecific::Private
private import DataFlowImplSpecific::Public
import Cached
+module DataFlowImplCommonPublic {
+ private newtype TFlowFeature =
+ TFeatureHasSourceCallContext() or
+ TFeatureHasSinkCallContext() or
+ TFeatureEqualSourceSinkCallContext()
+
+ /** A flow configuration feature for use in `Configuration::getAFeature()`. */
+ class FlowFeature extends TFlowFeature {
+ abstract string toString();
+ }
+
+ /**
+ * A flow configuration feature that implies that sources have some existing
+ * call context.
+ */
+ class FeatureHasSourceCallContext extends FlowFeature, TFeatureHasSourceCallContext {
+ override string toString() { result = "FeatureHasSourceCallContext" }
+ }
+
+ /**
+ * A flow configuration feature that implies that sinks have some existing
+ * call context.
+ */
+ class FeatureHasSinkCallContext extends FlowFeature, TFeatureHasSinkCallContext {
+ override string toString() { result = "FeatureHasSinkCallContext" }
+ }
+
+ /**
+ * A flow configuration feature that implies that source-sink pairs have some
+ * shared existing call context.
+ */
+ class FeatureEqualSourceSinkCallContext extends FlowFeature, TFeatureEqualSourceSinkCallContext {
+ override string toString() { result = "FeatureEqualSourceSinkCallContext" }
+ }
+}
+
/**
* The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion.
*
From 92e4a1ed1704a01f854975481d09641080808ae7 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Mon, 25 Oct 2021 11:09:00 +0200
Subject: [PATCH 152/471] Dataflow: Review fixes.
---
.../code/java/dataflow/internal/DataFlowImpl.qll | 13 +++++++++++++
.../java/dataflow/internal/DataFlowImplCommon.qll | 2 +-
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll
index 209944587e5..ae9e81d42c3 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll
@@ -10,7 +10,7 @@ module DataFlowImplCommonPublic {
/** A flow configuration feature for use in `Configuration::getAFeature()`. */
class FlowFeature extends TFlowFeature {
- abstract string toString();
+ string toString() { none() }
}
/**
From 699630af54136f10db46c2307a826344c4e217dc Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Mon, 25 Oct 2021 11:09:17 +0200
Subject: [PATCH 153/471] Dataflow: Sync.
---
.../code/cpp/dataflow/internal/DataFlowImpl.qll | 13 +++++++++++++
.../code/cpp/dataflow/internal/DataFlowImpl2.qll | 13 +++++++++++++
.../code/cpp/dataflow/internal/DataFlowImpl3.qll | 13 +++++++++++++
.../code/cpp/dataflow/internal/DataFlowImpl4.qll | 13 +++++++++++++
.../cpp/dataflow/internal/DataFlowImplCommon.qll | 2 +-
.../cpp/dataflow/internal/DataFlowImplLocal.qll | 13 +++++++++++++
.../code/cpp/ir/dataflow/internal/DataFlowImpl.qll | 13 +++++++++++++
.../code/cpp/ir/dataflow/internal/DataFlowImpl2.qll | 13 +++++++++++++
.../code/cpp/ir/dataflow/internal/DataFlowImpl3.qll | 13 +++++++++++++
.../code/cpp/ir/dataflow/internal/DataFlowImpl4.qll | 13 +++++++++++++
.../cpp/ir/dataflow/internal/DataFlowImplCommon.qll | 2 +-
.../code/csharp/dataflow/internal/DataFlowImpl.qll | 13 +++++++++++++
.../code/csharp/dataflow/internal/DataFlowImpl2.qll | 13 +++++++++++++
.../code/csharp/dataflow/internal/DataFlowImpl3.qll | 13 +++++++++++++
.../code/csharp/dataflow/internal/DataFlowImpl4.qll | 13 +++++++++++++
.../code/csharp/dataflow/internal/DataFlowImpl5.qll | 13 +++++++++++++
.../csharp/dataflow/internal/DataFlowImplCommon.qll | 2 +-
.../code/java/dataflow/internal/DataFlowImpl2.qll | 13 +++++++++++++
.../code/java/dataflow/internal/DataFlowImpl3.qll | 13 +++++++++++++
.../code/java/dataflow/internal/DataFlowImpl4.qll | 13 +++++++++++++
.../code/java/dataflow/internal/DataFlowImpl5.qll | 13 +++++++++++++
.../code/java/dataflow/internal/DataFlowImpl6.qll | 13 +++++++++++++
.../internal/DataFlowImplForSerializability.qll | 13 +++++++++++++
.../python/dataflow/new/internal/DataFlowImpl.qll | 13 +++++++++++++
.../python/dataflow/new/internal/DataFlowImpl2.qll | 13 +++++++++++++
.../python/dataflow/new/internal/DataFlowImpl3.qll | 13 +++++++++++++
.../python/dataflow/new/internal/DataFlowImpl4.qll | 13 +++++++++++++
.../dataflow/new/internal/DataFlowImplCommon.qll | 2 +-
28 files changed, 316 insertions(+), 4 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
index 209944587e5..ae9e81d42c3 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
@@ -10,7 +10,7 @@ module DataFlowImplCommonPublic {
/** A flow configuration feature for use in `Configuration::getAFeature()`. */
class FlowFeature extends TFlowFeature {
- abstract string toString();
+ string toString() { none() }
}
/**
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
index 209944587e5..ae9e81d42c3 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
@@ -10,7 +10,7 @@ module DataFlowImplCommonPublic {
/** A flow configuration feature for use in `Configuration::getAFeature()`. */
class FlowFeature extends TFlowFeature {
- abstract string toString();
+ string toString() { none() }
}
/**
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll
index 209944587e5..ae9e81d42c3 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll
@@ -10,7 +10,7 @@ module DataFlowImplCommonPublic {
/** A flow configuration feature for use in `Configuration::getAFeature()`. */
class FlowFeature extends TFlowFeature {
- abstract string toString();
+ string toString() { none() }
}
/**
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll
index 97ae7eb973b..5f0a5b225d3 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll
@@ -3459,6 +3459,16 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
ap instanceof AccessPathNil and
if hasSinkCallCtx(config)
then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
sc instanceof SummaryCtxNone or
cc instanceof CallContextNoCall
else any()
@@ -3664,6 +3674,9 @@ private predicate pathIntoCallable(
or
not exists(TSummaryCtxSome(p, ap)) and
sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll
index 209944587e5..ae9e81d42c3 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll
@@ -10,7 +10,7 @@ module DataFlowImplCommonPublic {
/** A flow configuration feature for use in `Configuration::getAFeature()`. */
class FlowFeature extends TFlowFeature {
- abstract string toString();
+ string toString() { none() }
}
/**
From 6eabb610b49dd83690a6e2bf21d86e759633133c Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Wed, 27 Oct 2021 13:58:30 +0200
Subject: [PATCH 154/471] Dataflow: Sync Ruby
---
.../ruby/dataflow/internal/DataFlowImpl.qll | 128 +++++++++++++-----
.../ruby/dataflow/internal/DataFlowImpl2.qll | 128 +++++++++++++-----
.../dataflow/internal/DataFlowImplCommon.qll | 36 +++++
3 files changed, 228 insertions(+), 64 deletions(-)
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll
index b3d03ea4e26..5f0a5b225d3 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,46 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3673,11 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll
index b3d03ea4e26..5f0a5b225d3 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll
@@ -10,6 +10,7 @@
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
+import DataFlowImplCommonPublic
/**
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
*/
int fieldFlowBranchLimit() { result = 2 }
+ /**
+ * Gets a data flow configuration feature to add restrictions to the set of
+ * valid flow paths.
+ *
+ * - `FeatureHasSourceCallContext`:
+ * Assume that sources have some existing call context to disallow
+ * conflicting return-flow directly following the source.
+ * - `FeatureHasSinkCallContext`:
+ * Assume that sinks have some existing call context to disallow
+ * conflicting argument-to-parameter flow directly preceding the sink.
+ * - `FeatureEqualSourceSinkCallContext`:
+ * Implies both of the above and additionally ensures that the entire flow
+ * path preserves the call context.
+ */
+ FlowFeature getAFeature() { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+ not fullBarrier(node2, config) and
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
+private predicate hasSourceCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSourceCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
+private predicate hasSinkCallCtx(Configuration config) {
+ exists(FlowFeature feature | feature = config.getAFeature() |
+ feature instanceof FeatureHasSinkCallContext or
+ feature instanceof FeatureEqualSourceSinkCallContext
+ )
+}
+
private module Stage1 {
class ApApprox = Unit;
@@ -421,7 +454,7 @@ private module Stage1 {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
- cc = false
+ if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
sinkNode(node, config) and
- toReturn = false
+ if hasSinkCallCtx(config) then toReturn = true else toReturn = false
or
exists(NodeEx mid |
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
Cc ccNone() { result = false }
+ CcCall ccSomeCall() { result = true }
+
private class LocalCc = Unit;
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
Cc ccNone() { result instanceof CallContextAny }
+ CcCall ccSomeCall() { result instanceof CallContextSomeCall }
+
private class LocalCc = LocalCallContext;
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
- cc = ccNone() and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
ap = getApNil(node)
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
) {
fwdFlow(node, _, _, ap, config) and
sinkNode(node, config) and
- toReturn = false and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
returnAp = apNone() and
ap instanceof ApNil
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap = TAccessPathNil(node.getDataFlowType())
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
)
} or
TPathNodeSink(NodeEx node, Configuration config) {
- sinkNode(node, pragma[only_bind_into](config)) and
- Stage4::revFlow(node, pragma[only_bind_into](config)) and
- (
- // A sink that is also a source ...
- sourceNode(node, config)
- or
- // ... or a sink that can be reached from a source
- exists(PathNodeMid mid |
- pathStep(mid, node, _, _, TAccessPathNil(_)) and
- pragma[only_bind_into](config) = mid.getConfiguration()
- )
+ exists(PathNodeMid sink |
+ sink.isAtSink() and
+ node = sink.getNodeEx() and
+ config = sink.getConfiguration()
)
}
@@ -3403,22 +3439,46 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// an intermediate step to another intermediate node
result = this.getSuccMid()
or
- // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
- exists(PathNodeMid mid, PathNodeSink sink |
- mid = this.getSuccMid() and
- mid.getNodeEx() = sink.getNodeEx() and
- mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
- result = sink
- )
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
sourceNode(node, config) and
- cc instanceof CallContextAny and
+ (
+ if hasSourceCallCtx(config)
+ then cc instanceof CallContextSomeCall
+ else cc instanceof CallContextAny
+ ) and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
}
+
+ predicate isAtSink() {
+ sinkNode(node, config) and
+ ap instanceof AccessPathNil and
+ if hasSinkCallCtx(config)
+ then
+ // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
+ // is exactly what we need to check. This also implies
+ // `sc instanceof SummaryCtxNone`.
+ // For `FeatureEqualSourceSinkCallContext` the initial call context was
+ // set to `CallContextSomeCall` and jumps are disallowed, so
+ // `cc instanceof CallContextNoCall` never holds. On the other hand,
+ // in this case there's never any need to enter a call except to identify
+ // a summary, so the condition in `pathIntoCallable` enforces this, which
+ // means that `sc instanceof SummaryCtxNone` holds if and only if we are
+ // in the call context of the source.
+ sc instanceof SummaryCtxNone or
+ cc instanceof CallContextNoCall
+ else any()
+ }
+
+ PathNodeSink projectToSink() {
+ this.isAtSink() and
+ result.getNodeEx() = node and
+ result.getConfiguration() = unbindConf(config)
+ }
}
/**
@@ -3613,7 +3673,11 @@ private predicate pathIntoCallable(
sc = TSummaryCtxSome(p, ap)
or
not exists(TSummaryCtxSome(p, ap)) and
- sc = TSummaryCtxNone()
+ sc = TSummaryCtxNone() and
+ // When the call contexts of source and sink needs to match then there's
+ // never any reason to enter a callable except to find a summary. See also
+ // the comment in `PathNodeMid::isAtSink`.
+ not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
|
if recordDataFlowCallSite(call, callable)
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll
index e11244c42b0..ae9e81d42c3 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll
@@ -2,6 +2,42 @@ private import DataFlowImplSpecific::Private
private import DataFlowImplSpecific::Public
import Cached
+module DataFlowImplCommonPublic {
+ private newtype TFlowFeature =
+ TFeatureHasSourceCallContext() or
+ TFeatureHasSinkCallContext() or
+ TFeatureEqualSourceSinkCallContext()
+
+ /** A flow configuration feature for use in `Configuration::getAFeature()`. */
+ class FlowFeature extends TFlowFeature {
+ string toString() { none() }
+ }
+
+ /**
+ * A flow configuration feature that implies that sources have some existing
+ * call context.
+ */
+ class FeatureHasSourceCallContext extends FlowFeature, TFeatureHasSourceCallContext {
+ override string toString() { result = "FeatureHasSourceCallContext" }
+ }
+
+ /**
+ * A flow configuration feature that implies that sinks have some existing
+ * call context.
+ */
+ class FeatureHasSinkCallContext extends FlowFeature, TFeatureHasSinkCallContext {
+ override string toString() { result = "FeatureHasSinkCallContext" }
+ }
+
+ /**
+ * A flow configuration feature that implies that source-sink pairs have some
+ * shared existing call context.
+ */
+ class FeatureEqualSourceSinkCallContext extends FlowFeature, TFeatureEqualSourceSinkCallContext {
+ override string toString() { result = "FeatureEqualSourceSinkCallContext" }
+ }
+}
+
/**
* The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion.
*
From 19e010e6fe7912a49735e52d7e9e146d9e77cb44 Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Wed, 27 Oct 2021 14:30:00 +0200
Subject: [PATCH 155/471] fetch-codeql action: unzip in runner.temp
---
.github/actions/fetch-codeql/action.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/actions/fetch-codeql/action.yml b/.github/actions/fetch-codeql/action.yml
index de6d50dc12f..6cefa7cd06f 100644
--- a/.github/actions/fetch-codeql/action.yml
+++ b/.github/actions/fetch-codeql/action.yml
@@ -8,7 +8,7 @@ runs:
run: |
LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | grep -v beta | sort --version-sort | tail -1)
gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip "$LATEST"
- unzip -q codeql-linux64.zip
- echo "${{ github.workspace }}/codeql" >> $GITHUB_PATH
+ unzip -q -d "${{ runner.temp }}" codeql-linux64.zip
+ echo "${{ runner.temp }}/codeql" >> $GITHUB_PATH
env:
GITHUB_TOKEN: ${{ github.token }}
From b128c7ca00469446f05dc474fccdc2299e445a4f Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Wed, 27 Oct 2021 14:10:16 +0200
Subject: [PATCH 156/471] Don't use local actions
---
.github/workflows/qhelp-pr-preview.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/qhelp-pr-preview.yml b/.github/workflows/qhelp-pr-preview.yml
index 16d0a50e1d4..5632a14719f 100644
--- a/.github/workflows/qhelp-pr-preview.yml
+++ b/.github/workflows/qhelp-pr-preview.yml
@@ -16,11 +16,13 @@ jobs:
qhelp:
runs-on: ubuntu-latest
steps:
+ - uses: github/codeql/.github/actions/fetch-codeql@main
- uses: actions/checkout@v2
with:
ref: refs/pull/${{ github.event.number }}/merge
fetch-depth: 2
persist-credentials: false
+
- name: Determine changed files
id: changes
run: |
@@ -28,8 +30,6 @@ jobs:
git diff --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep '.inc.qhelp$' | xargs -d '\n' -rn1 basename | xargs -d '\n' -rn1 git grep -l) |
sort -u > "${{ runner.temp }}/paths.txt"
- - uses: ./.github/actions/fetch-codeql
-
- name: QHelp preview
run: |
if [ -s "${{ runner.temp }}/paths.txt" ]; then
From aeedfd998753de95befe438cfb831534c34d18fe Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Wed, 27 Oct 2021 16:00:19 +0200
Subject: [PATCH 157/471] Filter out non-qhelp files
---
.github/workflows/qhelp-pr-preview.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/qhelp-pr-preview.yml b/.github/workflows/qhelp-pr-preview.yml
index 5632a14719f..7446a5f4429 100644
--- a/.github/workflows/qhelp-pr-preview.yml
+++ b/.github/workflows/qhelp-pr-preview.yml
@@ -28,7 +28,7 @@ jobs:
run: |
(git diff --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep '.qhelp$' | grep -v '.inc.qhelp';
git diff --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep '.inc.qhelp$' | xargs -d '\n' -rn1 basename | xargs -d '\n' -rn1 git grep -l) |
- sort -u > "${{ runner.temp }}/paths.txt"
+ grep '.qhelp$' | sort -u > "${{ runner.temp }}/paths.txt"
- name: QHelp preview
run: |
From e0e18c6587973738c47e4178183a319a067b7300 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Wed, 27 Oct 2021 17:24:46 +0100
Subject: [PATCH 158/471] C++: Drop the precision tags again, for now.
---
.../src/Likely Bugs/Memory Management/ImproperNullTermination.ql | 1 -
.../src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql | 1 -
2 files changed, 2 deletions(-)
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.ql b/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.ql
index 5c92b0a3db7..ed378dce60a 100644
--- a/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.ql
+++ b/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.ql
@@ -5,7 +5,6 @@
* @kind problem
* @id cpp/improper-null-termination
* @problem.severity warning
- * @precision medium
* @security-severity 7.8
* @tags security
* external/cwe/cwe-170
diff --git a/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql b/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql
index 597a762a2bb..3b0e3f5198d 100644
--- a/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql
+++ b/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql
@@ -5,7 +5,6 @@
* @kind problem
* @id cpp/user-controlled-null-termination-tainted
* @problem.severity warning
- * @precision medium
* @security-severity 10.0
* @tags security
* external/cwe/cwe-170
From c1ab49fe8af87e0b402496bf9d20ea0cbc5f49ff Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 27 Oct 2021 18:56:04 +0200
Subject: [PATCH 159/471] rename LDapFilterStep to
TaintPreservingLDapFilterStep
---
javascript/ql/lib/semmle/javascript/frameworks/LDAPjs.qll | 6 +++---
.../javascript/security/dataflow/SqlInjectionQuery.qll | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/LDAPjs.qll b/javascript/ql/lib/semmle/javascript/frameworks/LDAPjs.qll
index b027d23e020..216ca72d911 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/LDAPjs.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/LDAPjs.qll
@@ -35,7 +35,7 @@ module LDAPjs {
}
/** A creation of an LDAPjs filter, or object containing a filter, that doesn't sanitizes the input. */
- abstract class LDAPFilterStep extends DataFlow::Node {
+ abstract class TaintPreservingLdapFilterStep extends DataFlow::Node {
/** The input that creates (part of) an LDAPjs filter. */
abstract DataFlow::Node getInput();
@@ -44,7 +44,7 @@ module LDAPjs {
}
/** A call to the ldap utility method "parseFilter". */
- private class ParseFilter extends LDAPFilterStep, API::CallNode {
+ private class ParseFilter extends TaintPreservingLdapFilterStep, API::CallNode {
ParseFilter() { this = ldapjs().getMember("parseFilter").getACall() }
override DataFlow::Node getInput() { result = this.getArgument(0) }
@@ -56,7 +56,7 @@ module LDAPjs {
* A filter used in call to "search" on an LDAPjs client.
* We model that as a step from the ".filter" write to the options object itself.
*/
- private class SearchFilter extends LDAPFilterStep {
+ private class SearchFilter extends TaintPreservingLdapFilterStep {
SearchOptions options;
SearchFilter() {
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionQuery.qll
index 26fece12073..349d99d1123 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionQuery.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionQuery.qll
@@ -26,7 +26,7 @@ class Configuration extends TaintTracking::Configuration {
}
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
- exists(LDAPjs::LDAPFilterStep filter |
+ exists(LDAPjs::TaintPreservingLdapFilterStep filter |
pred = filter.getInput() and
succ = filter.getOutput()
)
From 2e912ee28ec0bf1bc9bafb13f5304a334ef7a40f Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 27 Oct 2021 19:03:36 +0200
Subject: [PATCH 160/471] rename LDAP to Ldap
---
javascript/ql/lib/javascript.qll | 2 +-
.../javascript/frameworks/{LDAPjs.qll => LdapJS.qll} | 4 ++--
.../security/dataflow/SqlInjectionCustomizations.qll | 10 +++++-----
.../javascript/security/dataflow/SqlInjectionQuery.qll | 2 +-
4 files changed, 9 insertions(+), 9 deletions(-)
rename javascript/ql/lib/semmle/javascript/frameworks/{LDAPjs.qll => LdapJS.qll} (96%)
diff --git a/javascript/ql/lib/javascript.qll b/javascript/ql/lib/javascript.qll
index 268920ff782..52299b1c245 100644
--- a/javascript/ql/lib/javascript.qll
+++ b/javascript/ql/lib/javascript.qll
@@ -99,7 +99,7 @@ import semmle.javascript.frameworks.History
import semmle.javascript.frameworks.Immutable
import semmle.javascript.frameworks.Knex
import semmle.javascript.frameworks.LazyCache
-import semmle.javascript.frameworks.LDAPjs
+import semmle.javascript.frameworks.LdapJS
import semmle.javascript.frameworks.LodashUnderscore
import semmle.javascript.frameworks.Logging
import semmle.javascript.frameworks.HttpFrameworks
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/LDAPjs.qll b/javascript/ql/lib/semmle/javascript/frameworks/LdapJS.qll
similarity index 96%
rename from javascript/ql/lib/semmle/javascript/frameworks/LDAPjs.qll
rename to javascript/ql/lib/semmle/javascript/frameworks/LdapJS.qll
index 216ca72d911..311eff87136 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/LDAPjs.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/LdapJS.qll
@@ -7,7 +7,7 @@ import javascript
/**
* A module providing sinks and sanitizers for LDAP injection.
*/
-module LDAPjs {
+module LdapJS {
/** Gets a reference to the ldapjs library. */
API::Node ldapjs() { result = API::moduleImport("ldapjs") }
@@ -43,7 +43,7 @@ module LDAPjs {
abstract DataFlow::Node getOutput();
}
- /** A call to the ldap utility method "parseFilter". */
+ /** A call to the LDAPjs utility method "parseFilter". */
private class ParseFilter extends TaintPreservingLdapFilterStep, API::CallNode {
ParseFilter() { this = ldapjs().getMember("parseFilter").getACall() }
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll
index 82e5101bf45..e3bf02362a4 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll
@@ -48,13 +48,13 @@ module SqlInjection {
class LdapJSSink extends Sink {
LdapJSSink() {
// A distinguished name (DN) used in a call to the client API.
- this = any(LDAPjs::ClientCall call).getArgument(0)
+ this = any(LdapJS::ClientCall call).getArgument(0)
or
// A search options object, which contains a filter and a baseDN.
- this = any(LDAPjs::SearchOptions opt).getARhs()
+ this = any(LdapJS::SearchOptions opt).getARhs()
or
// A call to "parseDN", which parses a DN from a string.
- this = LDAPjs::ldapjs().getMember("parseDN").getACall().getArgument(0)
+ this = LdapJS::ldapjs().getMember("parseDN").getACall().getArgument(0)
}
}
@@ -64,9 +64,9 @@ module SqlInjection {
* A chain of replace calls that replaces all unsafe chars for ldap injection.
* For simplicity it's used as a sanitizer for all of `js/sql-injection`.
*/
- class LDAPStringSanitizer extends Sanitizer,
+ class LdapStringSanitizer extends Sanitizer,
IncompleteBlacklistSanitizer::StringReplaceCallSequence {
- LDAPStringSanitizer() {
+ LdapStringSanitizer() {
forall(string char | char = ["*", "(", ")", "\\", "/"] |
this.getAMember().getAReplacedString() = char
)
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionQuery.qll
index 349d99d1123..35d838cbff8 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionQuery.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionQuery.qll
@@ -26,7 +26,7 @@ class Configuration extends TaintTracking::Configuration {
}
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
- exists(LDAPjs::TaintPreservingLdapFilterStep filter |
+ exists(LdapJS::TaintPreservingLdapFilterStep filter |
pred = filter.getInput() and
succ = filter.getOutput()
)
From 5e2cab4fb128ba252ade07b0ac92b4e694811984 Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Wed, 27 Oct 2021 18:56:44 +0200
Subject: [PATCH 161/471] Split workflow into separate jobs
---
.github/workflows/qhelp-pr-preview.yml | 35 ++++++++++++++++++--------
1 file changed, 25 insertions(+), 10 deletions(-)
diff --git a/.github/workflows/qhelp-pr-preview.yml b/.github/workflows/qhelp-pr-preview.yml
index 7446a5f4429..ddfb5c957a5 100644
--- a/.github/workflows/qhelp-pr-preview.yml
+++ b/.github/workflows/qhelp-pr-preview.yml
@@ -2,7 +2,6 @@ name: Query help preview
permissions:
contents: read
- pull-requests: write
on:
pull_request_target:
@@ -14,6 +13,8 @@ on:
jobs:
qhelp:
+ permissions:
+ contents: read
runs-on: ubuntu-latest
steps:
- uses: github/codeql/.github/actions/fetch-codeql@main
@@ -32,14 +33,28 @@ jobs:
- name: QHelp preview
run: |
- if [ -s "${{ runner.temp }}/paths.txt" ]; then
- ( echo "QHelp previews:";
- cat "${{ runner.temp }}/paths.txt" | while read path; do
- echo " ${path}
"
- echo
- codeql generate query-help --format=markdown "${path}"
- echo " "
- done) | gh pr comment "${{ github.event.pull_request.number }}" -F -
- fi
+ cat "${{ runner.temp }}/paths.txt" | while read path; do
+ echo " ${path}
"
+ echo
+ codeql generate query-help --format=markdown "${path}"
+ echo " "
+ done > comment.txt
+
+ - uses: actions/upload-artifact@v2
+ with:
+ name: comment.txt
+ path: comment.txt
+
+ post_comment:
+ permissions:
+ pull-requests: write
+ runs-on: ubuntu-latest
+ needs: qhelp
+ steps:
+ - uses: actions/download-artifact@v2
+ with:
+ name: comment.txt
+ - run: |
+ (echo "QHelp previews:"; cat comment.txt) | gh pr comment "${{ github.event.pull_request.number }}" --repo "${{ github.repository }}" -F -
env:
GITHUB_TOKEN: ${{ github.token }}
From af64b319ee7188cbde16fdeff793856eef714684 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 27 Oct 2021 19:54:52 +0200
Subject: [PATCH 162/471] update documentation strings
Co-authored-by: Esben Sparre Andreasen
---
.../semmle/javascript/frameworks/CookieLibraries.qll | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
index b9df9f227e4..b190f407e3d 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
@@ -23,7 +23,7 @@ module CookieWrites {
abstract predicate isHttpOnly();
/**
- * Holds if the cookie is likely an authentication cookie or otherwise sensitive.
+ * Holds if the cookie likely is an authentication cookie or otherwise sensitive.
*/
abstract predicate isSensitive();
@@ -57,8 +57,10 @@ module CookieWrites {
/**
* Holds if `node` looks like it can contain a sensitive cookie.
- * Either from `node` being a sensitive expression, or from `node` containing
- * a string value that looks like a sensitive cookie name.
+ *
+ * Heuristics:
+ * - `node` contains a string value that looks like a sensitive cookie name
+ * - `node` is a sensitive expression
*/
private predicate canHaveSensitiveCookie(DataFlow::Node node) {
exists(string s |
@@ -72,7 +74,7 @@ private predicate canHaveSensitiveCookie(DataFlow::Node node) {
}
/**
- * Gets cookie name from a `Set-Cookie` header value.
+ * Gets the cookie name of a `Set-Cookie` header value.
* The header value always starts with `=` optionally followed by attributes:
* `=; Domain=; Secure; HttpOnly`
*/
From 0372ccce02f312a9bf8efa4c747a383d89f1ca2a Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 27 Oct 2021 20:04:24 +0200
Subject: [PATCH 163/471] simplify regexp
Co-authored-by: Esben Sparre Andreasen
---
.../ql/lib/semmle/javascript/frameworks/CookieLibraries.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
index b190f407e3d..83ca03335c9 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
@@ -80,7 +80,7 @@ private predicate canHaveSensitiveCookie(DataFlow::Node node) {
*/
bindingset[s]
private string getCookieName(string s) {
- result = s.regexpCapture("\\s*\\b([^=\\s]*)\\b\\s*=.*", 1)
+ result = s.regexpCapture("([^=]*)=.*", 1).trim()
}
/**
From 78774233c7b817bc96876bcf18eee8988fba9581 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Sun, 16 May 2021 23:01:03 +0200
Subject: [PATCH 164/471] add library input as source to
`js/prototype-polluting-assignment`
---
...otypePollutingAssignmentCustomizations.qll | 9 ++++
.../PrototypePollutingAssignment.expected | 43 +++++++++++++++++++
.../PrototypePollutingAssignment/lib.js | 12 ++++++
.../PrototypePollutingAssignment/package.json | 5 +++
4 files changed, 69 insertions(+)
create mode 100644 javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
create mode 100644 javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/package.json
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentCustomizations.qll
index df48d455eb8..96b8edf11e4 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentCustomizations.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentCustomizations.qll
@@ -53,4 +53,13 @@ module PrototypePollutingAssignment {
private class DefaultSource extends Source {
DefaultSource() { this instanceof RemoteFlowSource }
}
+
+ import semmle.javascript.PackageExports as Exports
+
+ /**
+ * A parameter of an exported function, seen as a source prototype-polluting assignment.
+ */
+ class ExternalInputSource extends Source, DataFlow::SourceNode {
+ ExternalInputSource() { this = Exports::getALibraryInputParameter() }
+ }
}
diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
index 3ef5681278c..e8e27fc4728 100644
--- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
@@ -1,4 +1,24 @@
nodes
+| lib.js:1:38:1:40 | obj |
+| lib.js:1:43:1:46 | path |
+| lib.js:1:43:1:46 | path |
+| lib.js:1:43:1:46 | path |
+| lib.js:2:7:2:27 | currentPath |
+| lib.js:2:7:2:27 | currentPath |
+| lib.js:2:21:2:24 | path |
+| lib.js:2:21:2:24 | path |
+| lib.js:2:21:2:27 | path[0] |
+| lib.js:2:21:2:27 | path[0] |
+| lib.js:6:7:6:9 | obj |
+| lib.js:6:7:6:9 | obj |
+| lib.js:11:17:11:32 | obj[currentPath] |
+| lib.js:11:17:11:32 | obj[currentPath] |
+| lib.js:11:21:11:31 | currentPath |
+| lib.js:11:21:11:31 | currentPath |
+| lib.js:11:35:11:38 | path |
+| lib.js:11:35:11:38 | path |
+| lib.js:11:35:11:47 | path.slice(1) |
+| lib.js:11:35:11:47 | path.slice(1) |
| tst.js:5:9:5:38 | taint |
| tst.js:5:17:5:38 | String( ... y.data) |
| tst.js:5:24:5:37 | req.query.data |
@@ -24,6 +44,28 @@ nodes
| tst.js:48:9:48:11 | obj |
| tst.js:48:9:48:11 | obj |
edges
+| lib.js:1:38:1:40 | obj | lib.js:6:7:6:9 | obj |
+| lib.js:1:38:1:40 | obj | lib.js:6:7:6:9 | obj |
+| lib.js:1:43:1:46 | path | lib.js:2:21:2:24 | path |
+| lib.js:1:43:1:46 | path | lib.js:2:21:2:24 | path |
+| lib.js:1:43:1:46 | path | lib.js:2:21:2:24 | path |
+| lib.js:1:43:1:46 | path | lib.js:11:35:11:38 | path |
+| lib.js:1:43:1:46 | path | lib.js:11:35:11:38 | path |
+| lib.js:1:43:1:46 | path | lib.js:11:35:11:38 | path |
+| lib.js:2:7:2:27 | currentPath | lib.js:11:21:11:31 | currentPath |
+| lib.js:2:7:2:27 | currentPath | lib.js:11:21:11:31 | currentPath |
+| lib.js:2:21:2:24 | path | lib.js:2:21:2:27 | path[0] |
+| lib.js:2:21:2:24 | path | lib.js:2:21:2:27 | path[0] |
+| lib.js:2:21:2:27 | path[0] | lib.js:2:7:2:27 | currentPath |
+| lib.js:2:21:2:27 | path[0] | lib.js:2:7:2:27 | currentPath |
+| lib.js:11:17:11:32 | obj[currentPath] | lib.js:1:38:1:40 | obj |
+| lib.js:11:17:11:32 | obj[currentPath] | lib.js:1:38:1:40 | obj |
+| lib.js:11:21:11:31 | currentPath | lib.js:11:17:11:32 | obj[currentPath] |
+| lib.js:11:21:11:31 | currentPath | lib.js:11:17:11:32 | obj[currentPath] |
+| lib.js:11:35:11:38 | path | lib.js:11:35:11:47 | path.slice(1) |
+| lib.js:11:35:11:38 | path | lib.js:11:35:11:47 | path.slice(1) |
+| lib.js:11:35:11:47 | path.slice(1) | lib.js:1:43:1:46 | path |
+| lib.js:11:35:11:47 | path.slice(1) | lib.js:1:43:1:46 | path |
| tst.js:5:9:5:38 | taint | tst.js:8:12:8:16 | taint |
| tst.js:5:9:5:38 | taint | tst.js:9:12:9:16 | taint |
| tst.js:5:9:5:38 | taint | tst.js:12:25:12:29 | taint |
@@ -48,6 +90,7 @@ edges
| tst.js:33:23:33:25 | obj | tst.js:48:9:48:11 | obj |
| tst.js:33:23:33:25 | obj | tst.js:48:9:48:11 | obj |
#select
+| lib.js:6:7:6:9 | obj | lib.js:1:43:1:46 | path | lib.js:6:7:6:9 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:1:43:1:46 | path | here |
| tst.js:8:5:8:17 | object[taint] | tst.js:5:24:5:37 | req.query.data | tst.js:8:5:8:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | here |
| tst.js:9:5:9:17 | object[taint] | tst.js:5:24:5:37 | req.query.data | tst.js:9:5:9:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | here |
| tst.js:14:5:14:32 | unsafeG ... taint) | tst.js:5:24:5:37 | req.query.data | tst.js:14:5:14:32 | unsafeG ... taint) | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | here |
diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
new file mode 100644
index 00000000000..d3af18193e6
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
@@ -0,0 +1,12 @@
+module.exports.set = function recSet(obj, path, value) {
+ var currentPath = path[0];
+ var currentValue = obj[currentPath];
+ if (path.length === 1) {
+ if (currentValue === void 0) {
+ obj[currentPath] = value; // NOT OK
+ }
+ return currentValue;
+ }
+
+ return recSet(obj[currentPath], path.slice(1), value);
+}
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/package.json b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/package.json
new file mode 100644
index 00000000000..a163d396180
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/package.json
@@ -0,0 +1,5 @@
+{
+ "name": "my-lib",
+ "version": "0.0.7",
+ "main": "./lib.js"
+}
\ No newline at end of file
From 2d65aa17db2cd170aa94f357c15dfc6d955a7f3c Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Sun, 16 May 2021 23:07:50 +0200
Subject: [PATCH 165/471] recognize exported functions that use the `arguments`
object
---
.../lib/semmle/javascript/PackageExports.qll | 11 ++++++--
...ShellCommandConstructionCustomizations.qll | 6 ++---
.../PolynomialReDoSCustomizations.qll | 2 +-
.../PrototypePollutingAssignment.expected | 26 +++++++++++++++++++
.../PrototypePollutingAssignment/lib.js | 11 ++++++++
5 files changed, 50 insertions(+), 6 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/PackageExports.qll b/javascript/ql/lib/semmle/javascript/PackageExports.qll
index 5d67b958d28..51affaabdb7 100644
--- a/javascript/ql/lib/semmle/javascript/PackageExports.qll
+++ b/javascript/ql/lib/semmle/javascript/PackageExports.qll
@@ -11,11 +11,18 @@ private import semmle.javascript.internal.CachedStages
* Gets a parameter that is a library input to a top-level package.
*/
cached
-DataFlow::ParameterNode getALibraryInputParameter() {
+DataFlow::SourceNode getALibraryInputParameter() {
Stages::Taint::ref() and
exists(int bound, DataFlow::FunctionNode func |
- func = getAValueExportedByPackage().getABoundFunctionValue(bound) and
+ func = getAValueExportedByPackage().getABoundFunctionValue(bound)
+ |
result = func.getParameter(any(int arg | arg >= bound))
+ or
+ exists(DataFlow::PropRead read |
+ not read.getPropertyName() = "length" and
+ result = read and
+ read.getBase() = func.getFunction().getArgumentsVariable().getAnAccess().flow()
+ )
)
}
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll
index 20b69168474..79ec58e6b82 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll
@@ -50,14 +50,14 @@ module UnsafeShellCommandConstruction {
/**
* A parameter of an exported function, seen as a source for shell command constructed from library input.
*/
- class ExternalInputSource extends Source, DataFlow::ParameterNode {
+ class ExternalInputSource extends Source, DataFlow::SourceNode {
ExternalInputSource() {
this = Exports::getALibraryInputParameter() and
not (
// looks to be on purpose.
- this.getName() = ["cmd", "command"]
+ this.(DataFlow::ParameterNode).getName() = ["cmd", "command"]
or
- this.getName().regexpMatch(".*(Cmd|Command)$") // ends with "Cmd" or "Command"
+ this.(DataFlow::ParameterNode).getName().regexpMatch(".*(Cmd|Command)$") // ends with "Cmd" or "Command"
)
}
}
diff --git a/javascript/ql/lib/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll
index 98783c5b9e4..27c6516079b 100644
--- a/javascript/ql/lib/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll
+++ b/javascript/ql/lib/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll
@@ -126,7 +126,7 @@ module PolynomialReDoS {
/**
* A parameter of an exported function, seen as a source for polynomial-redos.
*/
- class ExternalInputSource extends Source, DataFlow::ParameterNode {
+ class ExternalInputSource extends Source, DataFlow::SourceNode {
ExternalInputSource() { this = Exports::getALibraryInputParameter() }
override string getKind() { result = "library" }
diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
index e8e27fc4728..d58c63254d2 100644
--- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
@@ -19,6 +19,19 @@ nodes
| lib.js:11:35:11:38 | path |
| lib.js:11:35:11:47 | path.slice(1) |
| lib.js:11:35:11:47 | path.slice(1) |
+| lib.js:14:38:14:41 | path |
+| lib.js:14:38:14:41 | path |
+| lib.js:15:3:15:14 | obj[path[0]] |
+| lib.js:15:3:15:14 | obj[path[0]] |
+| lib.js:15:7:15:10 | path |
+| lib.js:15:7:15:13 | path[0] |
+| lib.js:20:7:20:25 | path |
+| lib.js:20:14:20:25 | arguments[1] |
+| lib.js:20:14:20:25 | arguments[1] |
+| lib.js:22:3:22:14 | obj[path[0]] |
+| lib.js:22:3:22:14 | obj[path[0]] |
+| lib.js:22:7:22:10 | path |
+| lib.js:22:7:22:13 | path[0] |
| tst.js:5:9:5:38 | taint |
| tst.js:5:17:5:38 | String( ... y.data) |
| tst.js:5:24:5:37 | req.query.data |
@@ -66,6 +79,17 @@ edges
| lib.js:11:35:11:38 | path | lib.js:11:35:11:47 | path.slice(1) |
| lib.js:11:35:11:47 | path.slice(1) | lib.js:1:43:1:46 | path |
| lib.js:11:35:11:47 | path.slice(1) | lib.js:1:43:1:46 | path |
+| lib.js:14:38:14:41 | path | lib.js:15:7:15:10 | path |
+| lib.js:14:38:14:41 | path | lib.js:15:7:15:10 | path |
+| lib.js:15:7:15:10 | path | lib.js:15:7:15:13 | path[0] |
+| lib.js:15:7:15:13 | path[0] | lib.js:15:3:15:14 | obj[path[0]] |
+| lib.js:15:7:15:13 | path[0] | lib.js:15:3:15:14 | obj[path[0]] |
+| lib.js:20:7:20:25 | path | lib.js:22:7:22:10 | path |
+| lib.js:20:14:20:25 | arguments[1] | lib.js:20:7:20:25 | path |
+| lib.js:20:14:20:25 | arguments[1] | lib.js:20:7:20:25 | path |
+| lib.js:22:7:22:10 | path | lib.js:22:7:22:13 | path[0] |
+| lib.js:22:7:22:13 | path[0] | lib.js:22:3:22:14 | obj[path[0]] |
+| lib.js:22:7:22:13 | path[0] | lib.js:22:3:22:14 | obj[path[0]] |
| tst.js:5:9:5:38 | taint | tst.js:8:12:8:16 | taint |
| tst.js:5:9:5:38 | taint | tst.js:9:12:9:16 | taint |
| tst.js:5:9:5:38 | taint | tst.js:12:25:12:29 | taint |
@@ -91,6 +115,8 @@ edges
| tst.js:33:23:33:25 | obj | tst.js:48:9:48:11 | obj |
#select
| lib.js:6:7:6:9 | obj | lib.js:1:43:1:46 | path | lib.js:6:7:6:9 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:1:43:1:46 | path | here |
+| lib.js:15:3:15:14 | obj[path[0]] | lib.js:14:38:14:41 | path | lib.js:15:3:15:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:14:38:14:41 | path | here |
+| lib.js:22:3:22:14 | obj[path[0]] | lib.js:20:14:20:25 | arguments[1] | lib.js:22:3:22:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:20:14:20:25 | arguments[1] | here |
| tst.js:8:5:8:17 | object[taint] | tst.js:5:24:5:37 | req.query.data | tst.js:8:5:8:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | here |
| tst.js:9:5:9:17 | object[taint] | tst.js:5:24:5:37 | req.query.data | tst.js:9:5:9:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | here |
| tst.js:14:5:14:32 | unsafeG ... taint) | tst.js:5:24:5:37 | req.query.data | tst.js:14:5:14:32 | unsafeG ... taint) | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | here |
diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
index d3af18193e6..741683248c3 100644
--- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
+++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
@@ -9,4 +9,15 @@ module.exports.set = function recSet(obj, path, value) {
}
return recSet(obj[currentPath], path.slice(1), value);
+}
+
+module.exports.set2 = function (obj, path, value) {
+ obj[path[0]][path[1]] = value; // NOT OK
+}
+
+module.exports.setWithArgs = function() {
+ var obj = arguments[0];
+ var path = arguments[1];
+ var value = arguments[2];
+ obj[path[0]][path[1]] = value; // NOT OK
}
\ No newline at end of file
From 2a808b2cd6140d336990f91328eab8fc07b57940 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Sun, 16 May 2021 23:10:08 +0200
Subject: [PATCH 166/471] track taint through string coercions for
`js/prototype-polluting-assignment`
---
.../PrototypePollutingAssignmentQuery.qll | 8 ++++++-
.../PrototypePollutingAssignment.expected | 23 +++++++++++++++++++
.../PrototypePollutingAssignment/tst.js | 11 +++++++++
3 files changed, 41 insertions(+), 1 deletion(-)
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
index 2c55bc6a8eb..5e4fbbb6cea 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
@@ -31,7 +31,13 @@ class Configuration extends TaintTracking::Configuration {
node instanceof Sanitizer
or
// Concatenating with a string will in practice prevent the string `__proto__` from arising.
- node instanceof StringOps::ConcatenationRoot
+ exists(StringOps::ConcatenationRoot root | node = root |
+ // Exclude the string coercion `"" + node` from this filter.
+ not (
+ strictcount(root.getALeaf()) = 2 and
+ root.getALeaf().getStringValue() = ""
+ )
+ )
}
override predicate isAdditionalFlowStep(
diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
index d58c63254d2..c171838a166 100644
--- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
@@ -56,6 +56,17 @@ nodes
| tst.js:45:9:45:11 | obj |
| tst.js:48:9:48:11 | obj |
| tst.js:48:9:48:11 | obj |
+| tst.js:77:9:77:38 | taint |
+| tst.js:77:17:77:38 | String( ... y.data) |
+| tst.js:77:24:77:37 | req.query.data |
+| tst.js:77:24:77:37 | req.query.data |
+| tst.js:80:5:80:17 | object[taint] |
+| tst.js:80:5:80:17 | object[taint] |
+| tst.js:80:12:80:16 | taint |
+| tst.js:82:5:82:22 | object["" + taint] |
+| tst.js:82:5:82:22 | object["" + taint] |
+| tst.js:82:12:82:21 | "" + taint |
+| tst.js:82:17:82:21 | taint |
edges
| lib.js:1:38:1:40 | obj | lib.js:6:7:6:9 | obj |
| lib.js:1:38:1:40 | obj | lib.js:6:7:6:9 | obj |
@@ -113,6 +124,16 @@ edges
| tst.js:33:23:33:25 | obj | tst.js:45:9:45:11 | obj |
| tst.js:33:23:33:25 | obj | tst.js:48:9:48:11 | obj |
| tst.js:33:23:33:25 | obj | tst.js:48:9:48:11 | obj |
+| tst.js:77:9:77:38 | taint | tst.js:80:12:80:16 | taint |
+| tst.js:77:9:77:38 | taint | tst.js:82:17:82:21 | taint |
+| tst.js:77:17:77:38 | String( ... y.data) | tst.js:77:9:77:38 | taint |
+| tst.js:77:24:77:37 | req.query.data | tst.js:77:17:77:38 | String( ... y.data) |
+| tst.js:77:24:77:37 | req.query.data | tst.js:77:17:77:38 | String( ... y.data) |
+| tst.js:80:12:80:16 | taint | tst.js:80:5:80:17 | object[taint] |
+| tst.js:80:12:80:16 | taint | tst.js:80:5:80:17 | object[taint] |
+| tst.js:82:12:82:21 | "" + taint | tst.js:82:5:82:22 | object["" + taint] |
+| tst.js:82:12:82:21 | "" + taint | tst.js:82:5:82:22 | object["" + taint] |
+| tst.js:82:17:82:21 | taint | tst.js:82:12:82:21 | "" + taint |
#select
| lib.js:6:7:6:9 | obj | lib.js:1:43:1:46 | path | lib.js:6:7:6:9 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:1:43:1:46 | path | here |
| lib.js:15:3:15:14 | obj[path[0]] | lib.js:14:38:14:41 | path | lib.js:15:3:15:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:14:38:14:41 | path | here |
@@ -124,3 +145,5 @@ edges
| tst.js:39:9:39:11 | obj | tst.js:5:24:5:37 | req.query.data | tst.js:39:9:39:11 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | here |
| tst.js:45:9:45:11 | obj | tst.js:5:24:5:37 | req.query.data | tst.js:45:9:45:11 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | here |
| tst.js:48:9:48:11 | obj | tst.js:5:24:5:37 | req.query.data | tst.js:48:9:48:11 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | here |
+| tst.js:80:5:80:17 | object[taint] | tst.js:77:24:77:37 | req.query.data | tst.js:80:5:80:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:77:24:77:37 | req.query.data | here |
+| tst.js:82:5:82:22 | object["" + taint] | tst.js:77:24:77:37 | req.query.data | tst.js:82:5:82:22 | object["" + taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:77:24:77:37 | req.query.data | here |
diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/tst.js b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/tst.js
index bc64d93e225..2031dd8a51a 100644
--- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/tst.js
+++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/tst.js
@@ -71,3 +71,14 @@ class Box {
this.foo = 'bar'; // OK - 'this' won't refer to Object.prototype
}
}
+
+
+app.get('/', (req, res) => {
+ let taint = String(req.query.data);
+
+ let object = {};
+ object[taint][taint] = taint; // NOT OK
+
+ object["" + taint]["" + taint] = taint; // NOT OK
+});
+
From e94b0f5913a9972ad573fe8a918b50ef887abf07 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Sun, 16 May 2021 23:12:07 +0200
Subject: [PATCH 167/471] recognize inclusion based sanitizers for
`js/prototype-polluting-assignment`
---
.../PrototypePollutingAssignmentQuery.qll | 15 ++++++++++++++-
.../PrototypePollutingAssignment.expected | 7 +++++++
.../CWE-915/PrototypePollutingAssignment/tst.js | 6 ++++++
3 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
index 5e4fbbb6cea..e809e29b490 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
@@ -84,7 +84,8 @@ class Configuration extends TaintTracking::Configuration {
guard instanceof InstanceofCheck or
guard instanceof IsArrayCheck or
guard instanceof TypeofCheck or
- guard instanceof EqualityCheck
+ guard instanceof EqualityCheck or
+ guard instanceof IncludesCheck
}
}
@@ -204,3 +205,15 @@ private class EqualityCheck extends TaintTracking::SanitizerGuardNode, DataFlow:
outcome = astNode.getPolarity().booleanNot()
}
}
+
+/**
+ * Sanitizer guard of the form `x.includes("__proto__")`.
+ */
+private class IncludesCheck extends TaintTracking::LabeledSanitizerGuardNode, InclusionTest {
+ IncludesCheck() { this.getContainedNode().mayHaveStringValue("__proto__") }
+
+ override predicate sanitizes(boolean outcome, Expr e) {
+ e = getContainerNode().asExpr() and
+ outcome = getPolarity().booleanNot()
+ }
+}
diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
index c171838a166..d43da6c250c 100644
--- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
@@ -67,6 +67,9 @@ nodes
| tst.js:82:5:82:22 | object["" + taint] |
| tst.js:82:12:82:21 | "" + taint |
| tst.js:82:17:82:21 | taint |
+| tst.js:87:9:87:21 | object[taint] |
+| tst.js:87:9:87:21 | object[taint] |
+| tst.js:87:16:87:20 | taint |
edges
| lib.js:1:38:1:40 | obj | lib.js:6:7:6:9 | obj |
| lib.js:1:38:1:40 | obj | lib.js:6:7:6:9 | obj |
@@ -126,6 +129,7 @@ edges
| tst.js:33:23:33:25 | obj | tst.js:48:9:48:11 | obj |
| tst.js:77:9:77:38 | taint | tst.js:80:12:80:16 | taint |
| tst.js:77:9:77:38 | taint | tst.js:82:17:82:21 | taint |
+| tst.js:77:9:77:38 | taint | tst.js:87:16:87:20 | taint |
| tst.js:77:17:77:38 | String( ... y.data) | tst.js:77:9:77:38 | taint |
| tst.js:77:24:77:37 | req.query.data | tst.js:77:17:77:38 | String( ... y.data) |
| tst.js:77:24:77:37 | req.query.data | tst.js:77:17:77:38 | String( ... y.data) |
@@ -134,6 +138,8 @@ edges
| tst.js:82:12:82:21 | "" + taint | tst.js:82:5:82:22 | object["" + taint] |
| tst.js:82:12:82:21 | "" + taint | tst.js:82:5:82:22 | object["" + taint] |
| tst.js:82:17:82:21 | taint | tst.js:82:12:82:21 | "" + taint |
+| tst.js:87:16:87:20 | taint | tst.js:87:9:87:21 | object[taint] |
+| tst.js:87:16:87:20 | taint | tst.js:87:9:87:21 | object[taint] |
#select
| lib.js:6:7:6:9 | obj | lib.js:1:43:1:46 | path | lib.js:6:7:6:9 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:1:43:1:46 | path | here |
| lib.js:15:3:15:14 | obj[path[0]] | lib.js:14:38:14:41 | path | lib.js:15:3:15:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:14:38:14:41 | path | here |
@@ -147,3 +153,4 @@ edges
| tst.js:48:9:48:11 | obj | tst.js:5:24:5:37 | req.query.data | tst.js:48:9:48:11 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | here |
| tst.js:80:5:80:17 | object[taint] | tst.js:77:24:77:37 | req.query.data | tst.js:80:5:80:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:77:24:77:37 | req.query.data | here |
| tst.js:82:5:82:22 | object["" + taint] | tst.js:77:24:77:37 | req.query.data | tst.js:82:5:82:22 | object["" + taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:77:24:77:37 | req.query.data | here |
+| tst.js:87:9:87:21 | object[taint] | tst.js:77:24:77:37 | req.query.data | tst.js:87:9:87:21 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:77:24:77:37 | req.query.data | here |
diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/tst.js b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/tst.js
index 2031dd8a51a..84208bd6573 100644
--- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/tst.js
+++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/tst.js
@@ -80,5 +80,11 @@ app.get('/', (req, res) => {
object[taint][taint] = taint; // NOT OK
object["" + taint]["" + taint] = taint; // NOT OK
+
+ if (!taint.includes("__proto__")) {
+ object[taint][taint] = taint; // OK
+ } else {
+ object[taint][taint] = taint; // NOT OK
+ }
});
From 6e183af383360df8fe800c83ec7e3399e50ce575 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Sun, 16 May 2021 23:40:33 +0200
Subject: [PATCH 168/471] ignore test files for the `prototypeLessObject'
predicate
---
.../dataflow/PrototypePollutingAssignmentQuery.qll | 12 +++++++++++-
.../PrototypePollutingAssignment.expected | 12 ++++++++++++
.../CWE-915/PrototypePollutingAssignment/lib.js | 6 +++++-
.../CWE-915/PrototypePollutingAssignment/lib.spec.js | 9 +++++++++
4 files changed, 37 insertions(+), 2 deletions(-)
create mode 100644 javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.spec.js
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
index e809e29b490..1aa61eecb71 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
@@ -11,6 +11,7 @@ private import javascript
private import semmle.javascript.DynamicPropertyAccess
private import semmle.javascript.dataflow.InferredTypes
private import PrototypePollutingAssignmentCustomizations::PrototypePollutingAssignment
+private import filters.ClassifyFiles as ClassifyFiles
// Materialize flow labels
private class ConcreteObjectPrototype extends ObjectPrototype {
@@ -98,7 +99,8 @@ private DataFlow::SourceNode prototypeLessObject(DataFlow::TypeTracker t) {
t.start() and
// We assume the argument to Object.create is not Object.prototype, since most
// users wouldn't bother to call Object.create in that case.
- result = DataFlow::globalVarRef("Object").getAMemberCall("create")
+ result = DataFlow::globalVarRef("Object").getAMemberCall("create") and
+ not result.getFile() instanceof TestFile
or
// Allow use of SharedFlowSteps to track a bit further
exists(DataFlow::Node mid |
@@ -109,6 +111,14 @@ private DataFlow::SourceNode prototypeLessObject(DataFlow::TypeTracker t) {
exists(DataFlow::TypeTracker t2 | result = prototypeLessObject(t2).track(t2, t))
}
+/**
+ * A test file.
+ * Objects created in such files are ignored in the `prototypeLessObject` predicate.
+ */
+private class TestFile extends File {
+ TestFile() { ClassifyFiles::classify(this, "test") }
+}
+
/** Holds if `Object.prototype` has a member named `prop`. */
private predicate isPropertyPresentOnObjectPrototype(string prop) {
exists(ExternalInstanceMemberDecl decl |
diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
index d43da6c250c..dc0f07adb32 100644
--- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
@@ -32,6 +32,12 @@ nodes
| lib.js:22:3:22:14 | obj[path[0]] |
| lib.js:22:7:22:10 | path |
| lib.js:22:7:22:13 | path[0] |
+| lib.js:25:44:25:47 | path |
+| lib.js:25:44:25:47 | path |
+| lib.js:26:10:26:21 | obj[path[0]] |
+| lib.js:26:10:26:21 | obj[path[0]] |
+| lib.js:26:14:26:17 | path |
+| lib.js:26:14:26:20 | path[0] |
| tst.js:5:9:5:38 | taint |
| tst.js:5:17:5:38 | String( ... y.data) |
| tst.js:5:24:5:37 | req.query.data |
@@ -104,6 +110,11 @@ edges
| lib.js:22:7:22:10 | path | lib.js:22:7:22:13 | path[0] |
| lib.js:22:7:22:13 | path[0] | lib.js:22:3:22:14 | obj[path[0]] |
| lib.js:22:7:22:13 | path[0] | lib.js:22:3:22:14 | obj[path[0]] |
+| lib.js:25:44:25:47 | path | lib.js:26:14:26:17 | path |
+| lib.js:25:44:25:47 | path | lib.js:26:14:26:17 | path |
+| lib.js:26:14:26:17 | path | lib.js:26:14:26:20 | path[0] |
+| lib.js:26:14:26:20 | path[0] | lib.js:26:10:26:21 | obj[path[0]] |
+| lib.js:26:14:26:20 | path[0] | lib.js:26:10:26:21 | obj[path[0]] |
| tst.js:5:9:5:38 | taint | tst.js:8:12:8:16 | taint |
| tst.js:5:9:5:38 | taint | tst.js:9:12:9:16 | taint |
| tst.js:5:9:5:38 | taint | tst.js:12:25:12:29 | taint |
@@ -144,6 +155,7 @@ edges
| lib.js:6:7:6:9 | obj | lib.js:1:43:1:46 | path | lib.js:6:7:6:9 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:1:43:1:46 | path | here |
| lib.js:15:3:15:14 | obj[path[0]] | lib.js:14:38:14:41 | path | lib.js:15:3:15:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:14:38:14:41 | path | here |
| lib.js:22:3:22:14 | obj[path[0]] | lib.js:20:14:20:25 | arguments[1] | lib.js:22:3:22:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:20:14:20:25 | arguments[1] | here |
+| lib.js:26:10:26:21 | obj[path[0]] | lib.js:25:44:25:47 | path | lib.js:26:10:26:21 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:25:44:25:47 | path | here |
| tst.js:8:5:8:17 | object[taint] | tst.js:5:24:5:37 | req.query.data | tst.js:8:5:8:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | here |
| tst.js:9:5:9:17 | object[taint] | tst.js:5:24:5:37 | req.query.data | tst.js:9:5:9:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | here |
| tst.js:14:5:14:32 | unsafeG ... taint) | tst.js:5:24:5:37 | req.query.data | tst.js:14:5:14:32 | unsafeG ... taint) | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | here |
diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
index 741683248c3..4456163b75e 100644
--- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
+++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
@@ -20,4 +20,8 @@ module.exports.setWithArgs = function() {
var path = arguments[1];
var value = arguments[2];
obj[path[0]][path[1]] = value; // NOT OK
-}
\ No newline at end of file
+}
+
+module.exports.usedInTest = function (obj, path, value) {
+ return obj[path[0]][path[1]] = value; // NOT OK
+}
diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.spec.js b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.spec.js
new file mode 100644
index 00000000000..704a16dc48c
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.spec.js
@@ -0,0 +1,9 @@
+const lib = require("./lib");
+
+describe("lib", () => {
+ it("should work", () => {
+ const obj = Object.create(null);
+
+ lib.usedInTest(obj, "foo", "my-value");
+ });
+});
\ No newline at end of file
From d1238dfd8bf22a4cd1cee37df10863721f68dbcf Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Mon, 17 May 2021 12:17:48 +0200
Subject: [PATCH 169/471] update alert message to distinguish between library
input and remote flow
---
...otypePollutingAssignmentCustomizations.qll | 11 +++++++-
.../PrototypePollutingAssignmentQuery.qll | 2 +-
.../CWE-915/PrototypePollutingAssignment.ql | 2 +-
.../PrototypePollutingAssignment.expected | 28 +++++++++----------
4 files changed, 26 insertions(+), 17 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentCustomizations.qll
index 96b8edf11e4..4d1bfbebbf7 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentCustomizations.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentCustomizations.qll
@@ -13,7 +13,12 @@ module PrototypePollutingAssignment {
/**
* A data flow source for untrusted data from which the special `__proto__` property name may be arise.
*/
- abstract class Source extends DataFlow::Node { }
+ abstract class Source extends DataFlow::Node {
+ /**
+ * Gets a string that describes the type of source.
+ */
+ abstract string describe();
+ }
/**
* A data flow sink for prototype-polluting assignments or untrusted property names.
@@ -52,6 +57,8 @@ module PrototypePollutingAssignment {
/** A remote flow source or location.{hash,search} as a taint source. */
private class DefaultSource extends Source {
DefaultSource() { this instanceof RemoteFlowSource }
+
+ override string describe() { result = "user controlled input" }
}
import semmle.javascript.PackageExports as Exports
@@ -61,5 +68,7 @@ module PrototypePollutingAssignment {
*/
class ExternalInputSource extends Source, DataFlow::SourceNode {
ExternalInputSource() { this = Exports::getALibraryInputParameter() }
+
+ override string describe() { result = "library input" }
}
}
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
index 1aa61eecb71..f50b9170abe 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
@@ -10,7 +10,7 @@
private import javascript
private import semmle.javascript.DynamicPropertyAccess
private import semmle.javascript.dataflow.InferredTypes
-private import PrototypePollutingAssignmentCustomizations::PrototypePollutingAssignment
+import PrototypePollutingAssignmentCustomizations::PrototypePollutingAssignment
private import filters.ClassifyFiles as ClassifyFiles
// Materialize flow labels
diff --git a/javascript/ql/src/Security/CWE-915/PrototypePollutingAssignment.ql b/javascript/ql/src/Security/CWE-915/PrototypePollutingAssignment.ql
index b68a75b5b3b..0557b575adb 100644
--- a/javascript/ql/src/Security/CWE-915/PrototypePollutingAssignment.ql
+++ b/javascript/ql/src/Security/CWE-915/PrototypePollutingAssignment.ql
@@ -24,4 +24,4 @@ from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
select sink, source, sink,
"This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@.",
- source.getNode(), "here"
+ source.getNode(), source.getNode().(Source).describe()
diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
index dc0f07adb32..522e74d2a32 100644
--- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
@@ -152,17 +152,17 @@ edges
| tst.js:87:16:87:20 | taint | tst.js:87:9:87:21 | object[taint] |
| tst.js:87:16:87:20 | taint | tst.js:87:9:87:21 | object[taint] |
#select
-| lib.js:6:7:6:9 | obj | lib.js:1:43:1:46 | path | lib.js:6:7:6:9 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:1:43:1:46 | path | here |
-| lib.js:15:3:15:14 | obj[path[0]] | lib.js:14:38:14:41 | path | lib.js:15:3:15:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:14:38:14:41 | path | here |
-| lib.js:22:3:22:14 | obj[path[0]] | lib.js:20:14:20:25 | arguments[1] | lib.js:22:3:22:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:20:14:20:25 | arguments[1] | here |
-| lib.js:26:10:26:21 | obj[path[0]] | lib.js:25:44:25:47 | path | lib.js:26:10:26:21 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:25:44:25:47 | path | here |
-| tst.js:8:5:8:17 | object[taint] | tst.js:5:24:5:37 | req.query.data | tst.js:8:5:8:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | here |
-| tst.js:9:5:9:17 | object[taint] | tst.js:5:24:5:37 | req.query.data | tst.js:9:5:9:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | here |
-| tst.js:14:5:14:32 | unsafeG ... taint) | tst.js:5:24:5:37 | req.query.data | tst.js:14:5:14:32 | unsafeG ... taint) | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | here |
-| tst.js:34:5:34:7 | obj | tst.js:5:24:5:37 | req.query.data | tst.js:34:5:34:7 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | here |
-| tst.js:39:9:39:11 | obj | tst.js:5:24:5:37 | req.query.data | tst.js:39:9:39:11 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | here |
-| tst.js:45:9:45:11 | obj | tst.js:5:24:5:37 | req.query.data | tst.js:45:9:45:11 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | here |
-| tst.js:48:9:48:11 | obj | tst.js:5:24:5:37 | req.query.data | tst.js:48:9:48:11 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | here |
-| tst.js:80:5:80:17 | object[taint] | tst.js:77:24:77:37 | req.query.data | tst.js:80:5:80:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:77:24:77:37 | req.query.data | here |
-| tst.js:82:5:82:22 | object["" + taint] | tst.js:77:24:77:37 | req.query.data | tst.js:82:5:82:22 | object["" + taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:77:24:77:37 | req.query.data | here |
-| tst.js:87:9:87:21 | object[taint] | tst.js:77:24:77:37 | req.query.data | tst.js:87:9:87:21 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:77:24:77:37 | req.query.data | here |
+| lib.js:6:7:6:9 | obj | lib.js:1:43:1:46 | path | lib.js:6:7:6:9 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:1:43:1:46 | path | library input |
+| lib.js:15:3:15:14 | obj[path[0]] | lib.js:14:38:14:41 | path | lib.js:15:3:15:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:14:38:14:41 | path | library input |
+| lib.js:22:3:22:14 | obj[path[0]] | lib.js:20:14:20:25 | arguments[1] | lib.js:22:3:22:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:20:14:20:25 | arguments[1] | library input |
+| lib.js:26:10:26:21 | obj[path[0]] | lib.js:25:44:25:47 | path | lib.js:26:10:26:21 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:25:44:25:47 | path | library input |
+| tst.js:8:5:8:17 | object[taint] | tst.js:5:24:5:37 | req.query.data | tst.js:8:5:8:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | user controlled input |
+| tst.js:9:5:9:17 | object[taint] | tst.js:5:24:5:37 | req.query.data | tst.js:9:5:9:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | user controlled input |
+| tst.js:14:5:14:32 | unsafeG ... taint) | tst.js:5:24:5:37 | req.query.data | tst.js:14:5:14:32 | unsafeG ... taint) | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | user controlled input |
+| tst.js:34:5:34:7 | obj | tst.js:5:24:5:37 | req.query.data | tst.js:34:5:34:7 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | user controlled input |
+| tst.js:39:9:39:11 | obj | tst.js:5:24:5:37 | req.query.data | tst.js:39:9:39:11 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | user controlled input |
+| tst.js:45:9:45:11 | obj | tst.js:5:24:5:37 | req.query.data | tst.js:45:9:45:11 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | user controlled input |
+| tst.js:48:9:48:11 | obj | tst.js:5:24:5:37 | req.query.data | tst.js:48:9:48:11 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | user controlled input |
+| tst.js:80:5:80:17 | object[taint] | tst.js:77:24:77:37 | req.query.data | tst.js:80:5:80:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:77:24:77:37 | req.query.data | user controlled input |
+| tst.js:82:5:82:22 | object["" + taint] | tst.js:77:24:77:37 | req.query.data | tst.js:82:5:82:22 | object["" + taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:77:24:77:37 | req.query.data | user controlled input |
+| tst.js:87:9:87:21 | object[taint] | tst.js:77:24:77:37 | req.query.data | tst.js:87:9:87:21 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:77:24:77:37 | req.query.data | user controlled input |
From 3d124cf95e44432d494469c78732f62d3527fb61 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Mon, 17 May 2021 15:31:54 +0200
Subject: [PATCH 170/471] add change-note
---
javascript/change-notes/2020-05-17-prototype-assignment.md | 3 +++
1 file changed, 3 insertions(+)
create mode 100644 javascript/change-notes/2020-05-17-prototype-assignment.md
diff --git a/javascript/change-notes/2020-05-17-prototype-assignment.md b/javascript/change-notes/2020-05-17-prototype-assignment.md
new file mode 100644
index 00000000000..e73ecda809b
--- /dev/null
+++ b/javascript/change-notes/2020-05-17-prototype-assignment.md
@@ -0,0 +1,3 @@
+lgtm,codescanning
+* The `js/prototype-polluting-assignment` query now flags assignments that may modify
+ the build-in Object prototype where the property name originates from library input.
From fa9e9dd84782b05ee149942f5cc07bdd1efbdcdd Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Fri, 11 Jun 2021 10:36:51 +0200
Subject: [PATCH 171/471] split out predicates in `ClassifyFiles` to avoid
unnecessary computations
---
.../javascript/filters/ClassifyFiles.qll | 73 +++++++++++++------
.../PrototypePollutingAssignmentQuery.qll | 2 +-
2 files changed, 50 insertions(+), 25 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/filters/ClassifyFiles.qll b/javascript/ql/lib/semmle/javascript/filters/ClassifyFiles.qll
index 8e8831d45b2..aa3aa81bffe 100644
--- a/javascript/ql/lib/semmle/javascript/filters/ClassifyFiles.qll
+++ b/javascript/ql/lib/semmle/javascript/filters/ClassifyFiles.qll
@@ -45,6 +45,49 @@ private predicate looksLikeExterns(TopLevel tl) {
)
}
+/**
+ * Holds if `f` contains generated or minified code.
+ */
+predicate isGeneratedCodeFile(File f) { isGenerated(f.getATopLevel()) }
+
+/**
+ * Holds if `f` contains test code.
+ */
+predicate isTestFile(File f) {
+ exists(Test t | t.getFile() = f)
+ or
+ exists(string stemExt | stemExt = "test" or stemExt = "spec" |
+ f = getTestFile(any(File orig), stemExt)
+ )
+ or
+ f.getAbsolutePath().regexpMatch(".*/__(mocks|tests)__/.*")
+}
+
+/**
+ * Holds if `f` contains externs declarations.
+ */
+predicate isExternsFile(File f) {
+ (f.getATopLevel().isExterns() or looksLikeExterns(f.getATopLevel()))
+}
+
+/**
+ * Holds if `f` contains library code.
+ */
+predicate isLibaryFile(File f) { f.getATopLevel() instanceof FrameworkLibraryInstance }
+
+/**
+ * Holds if `f` contains template code.
+ */
+predicate isTemplateFile(File f) {
+ exists(JSParseError err | maybeCausedByTemplate(err) | f = err.getFile())
+ or
+ // Polymer templates
+ exists(HTML::Element elt | elt.getName() = "template" |
+ f = elt.getFile() and
+ not f.getExtension() = "vue"
+ )
+}
+
/**
* Holds if `f` is classified as belonging to `category`.
*
@@ -55,33 +98,15 @@ private predicate looksLikeExterns(TopLevel tl) {
* - `"library"`: `f` contains library code;
* - `"template"`: `f` contains template code.
*/
+pragma[inline]
predicate classify(File f, string category) {
- isGenerated(f.getATopLevel()) and category = "generated"
+ isGeneratedCodeFile(f) and category = "generated"
or
- (
- exists(Test t | t.getFile() = f)
- or
- exists(string stemExt | stemExt = "test" or stemExt = "spec" |
- f = getTestFile(any(File orig), stemExt)
- )
- or
- f.getAbsolutePath().regexpMatch(".*/__(mocks|tests)__/.*")
- ) and
- category = "test"
+ isTestFile(f) and category = "test"
or
- (f.getATopLevel().isExterns() or looksLikeExterns(f.getATopLevel())) and
- category = "externs"
+ isExternsFile(f) and category = "externs"
or
- f.getATopLevel() instanceof FrameworkLibraryInstance and category = "library"
+ isLibaryFile(f) and category = "library"
or
- exists(JSParseError err | maybeCausedByTemplate(err) |
- f = err.getFile() and category = "template"
- )
- or
- // Polymer templates
- exists(HTML::Element elt | elt.getName() = "template" |
- f = elt.getFile() and
- category = "template" and
- not f.getExtension() = "vue"
- )
+ isTemplateFile(f) and category = "template"
}
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
index f50b9170abe..90aa77cd2fd 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
@@ -116,7 +116,7 @@ private DataFlow::SourceNode prototypeLessObject(DataFlow::TypeTracker t) {
* Objects created in such files are ignored in the `prototypeLessObject` predicate.
*/
private class TestFile extends File {
- TestFile() { ClassifyFiles::classify(this, "test") }
+ TestFile() { ClassifyFiles::isTestFile(this) }
}
/** Holds if `Object.prototype` has a member named `prop`. */
From 0c9c9bbde7aa32e9a044d50f6783ad028400c188 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 2 Sep 2021 12:59:41 +0200
Subject: [PATCH 172/471] detect library input when the arguments object is
converted to an array
---
.../lib/semmle/javascript/PackageExports.qll | 26 ++++++++++++++---
.../PrototypePollutingAssignment.expected | 28 +++++++++++++++++++
.../PrototypePollutingAssignment/lib.js | 16 +++++++++++
3 files changed, 66 insertions(+), 4 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/PackageExports.qll b/javascript/ql/lib/semmle/javascript/PackageExports.qll
index 51affaabdb7..88577a71c5b 100644
--- a/javascript/ql/lib/semmle/javascript/PackageExports.qll
+++ b/javascript/ql/lib/semmle/javascript/PackageExports.qll
@@ -18,10 +18,28 @@ DataFlow::SourceNode getALibraryInputParameter() {
|
result = func.getParameter(any(int arg | arg >= bound))
or
- exists(DataFlow::PropRead read |
- not read.getPropertyName() = "length" and
- result = read and
- read.getBase() = func.getFunction().getArgumentsVariable().getAnAccess().flow()
+ result = getAnArgumentsRead(func.getFunction())
+ )
+}
+
+private DataFlow::SourceNode getAnArgumentsRead(Function func) {
+ exists(DataFlow::PropRead read |
+ not read.getPropertyName() = "length" and
+ result = read
+ |
+ read.getBase() = func.getArgumentsVariable().getAnAccess().flow()
+ or
+ exists(DataFlow::MethodCallNode call |
+ call =
+ DataFlow::globalVarRef("Array")
+ .getAPropertyRead("prototype")
+ .getAPropertyRead("slice")
+ .getAMethodCall("call")
+ or
+ call = DataFlow::globalVarRef("Array").getAMethodCall("from")
+ |
+ call.getArgument(0) = func.getArgumentsVariable().getAnAccess().flow() and
+ call.flowsTo(read.getBase())
)
)
}
diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
index 522e74d2a32..9f29c850275 100644
--- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
@@ -38,6 +38,20 @@ nodes
| lib.js:26:10:26:21 | obj[path[0]] |
| lib.js:26:14:26:17 | path |
| lib.js:26:14:26:20 | path[0] |
+| lib.js:32:7:32:20 | path |
+| lib.js:32:14:32:20 | args[1] |
+| lib.js:32:14:32:20 | args[1] |
+| lib.js:34:3:34:14 | obj[path[0]] |
+| lib.js:34:3:34:14 | obj[path[0]] |
+| lib.js:34:7:34:10 | path |
+| lib.js:34:7:34:13 | path[0] |
+| lib.js:40:7:40:20 | path |
+| lib.js:40:14:40:20 | args[1] |
+| lib.js:40:14:40:20 | args[1] |
+| lib.js:42:3:42:14 | obj[path[0]] |
+| lib.js:42:3:42:14 | obj[path[0]] |
+| lib.js:42:7:42:10 | path |
+| lib.js:42:7:42:13 | path[0] |
| tst.js:5:9:5:38 | taint |
| tst.js:5:17:5:38 | String( ... y.data) |
| tst.js:5:24:5:37 | req.query.data |
@@ -115,6 +129,18 @@ edges
| lib.js:26:14:26:17 | path | lib.js:26:14:26:20 | path[0] |
| lib.js:26:14:26:20 | path[0] | lib.js:26:10:26:21 | obj[path[0]] |
| lib.js:26:14:26:20 | path[0] | lib.js:26:10:26:21 | obj[path[0]] |
+| lib.js:32:7:32:20 | path | lib.js:34:7:34:10 | path |
+| lib.js:32:14:32:20 | args[1] | lib.js:32:7:32:20 | path |
+| lib.js:32:14:32:20 | args[1] | lib.js:32:7:32:20 | path |
+| lib.js:34:7:34:10 | path | lib.js:34:7:34:13 | path[0] |
+| lib.js:34:7:34:13 | path[0] | lib.js:34:3:34:14 | obj[path[0]] |
+| lib.js:34:7:34:13 | path[0] | lib.js:34:3:34:14 | obj[path[0]] |
+| lib.js:40:7:40:20 | path | lib.js:42:7:42:10 | path |
+| lib.js:40:14:40:20 | args[1] | lib.js:40:7:40:20 | path |
+| lib.js:40:14:40:20 | args[1] | lib.js:40:7:40:20 | path |
+| lib.js:42:7:42:10 | path | lib.js:42:7:42:13 | path[0] |
+| lib.js:42:7:42:13 | path[0] | lib.js:42:3:42:14 | obj[path[0]] |
+| lib.js:42:7:42:13 | path[0] | lib.js:42:3:42:14 | obj[path[0]] |
| tst.js:5:9:5:38 | taint | tst.js:8:12:8:16 | taint |
| tst.js:5:9:5:38 | taint | tst.js:9:12:9:16 | taint |
| tst.js:5:9:5:38 | taint | tst.js:12:25:12:29 | taint |
@@ -156,6 +182,8 @@ edges
| lib.js:15:3:15:14 | obj[path[0]] | lib.js:14:38:14:41 | path | lib.js:15:3:15:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:14:38:14:41 | path | library input |
| lib.js:22:3:22:14 | obj[path[0]] | lib.js:20:14:20:25 | arguments[1] | lib.js:22:3:22:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:20:14:20:25 | arguments[1] | library input |
| lib.js:26:10:26:21 | obj[path[0]] | lib.js:25:44:25:47 | path | lib.js:26:10:26:21 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:25:44:25:47 | path | library input |
+| lib.js:34:3:34:14 | obj[path[0]] | lib.js:32:14:32:20 | args[1] | lib.js:34:3:34:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:32:14:32:20 | args[1] | library input |
+| lib.js:42:3:42:14 | obj[path[0]] | lib.js:40:14:40:20 | args[1] | lib.js:42:3:42:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:40:14:40:20 | args[1] | library input |
| tst.js:8:5:8:17 | object[taint] | tst.js:5:24:5:37 | req.query.data | tst.js:8:5:8:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | user controlled input |
| tst.js:9:5:9:17 | object[taint] | tst.js:5:24:5:37 | req.query.data | tst.js:9:5:9:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | user controlled input |
| tst.js:14:5:14:32 | unsafeG ... taint) | tst.js:5:24:5:37 | req.query.data | tst.js:14:5:14:32 | unsafeG ... taint) | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | user controlled input |
diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
index 4456163b75e..023ef1fb9a6 100644
--- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
+++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
@@ -25,3 +25,19 @@ module.exports.setWithArgs = function() {
module.exports.usedInTest = function (obj, path, value) {
return obj[path[0]][path[1]] = value; // NOT OK
}
+
+module.exports.setWithArgs2 = function() {
+ const args = Array.prototype.slice.call(arguments);
+ var obj = args[0];
+ var path = args[1];
+ var value = args[2];
+ obj[path[0]][path[1]] = value; // NOT OK
+}
+
+module.exports.setWithArgs3 = function() {
+ const args = Array.from(arguments);
+ var obj = args[0];
+ var path = args[1];
+ var value = args[2];
+ obj[path[0]][path[1]] = value; // NOT OK
+}
\ No newline at end of file
From 2dedfb302abd3dc5799e4f8889bd73fde37fb708 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 8 Sep 2021 13:13:00 +0200
Subject: [PATCH 173/471] remove paths without unmatched returns from
`js/prototype-polluting-assignment`
---
.../PrototypePollutingAssignmentQuery.qll | 8 +++++
.../PrototypePollutingAssignment.expected | 35 +++++++++++++++++++
.../PrototypePollutingAssignment/lib.js | 33 ++++++++++++++++-
3 files changed, 75 insertions(+), 1 deletion(-)
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
index 90aa77cd2fd..571135f978c 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
@@ -69,6 +69,14 @@ class Configuration extends TaintTracking::Configuration {
inlbl.isTaint() and
outlbl instanceof ObjectPrototype
)
+ or
+ DataFlow::localFieldStep(pred, succ) and inlbl = outlbl
+ }
+
+ // override to require that there is a path without unmatched return steps
+ override predicate hasFlowPath(DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink) {
+ super.hasFlowPath(source, sink) and
+ DataFlow::hasPathWithoutUnmatchedReturn(source, sink)
}
override predicate isLabeledBarrier(DataFlow::Node node, DataFlow::FlowLabel lbl) {
diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
index 9f29c850275..6e2ca7874be 100644
--- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
@@ -52,6 +52,24 @@ nodes
| lib.js:42:3:42:14 | obj[path[0]] |
| lib.js:42:7:42:10 | path |
| lib.js:42:7:42:13 | path[0] |
+| lib.js:45:13:45:13 | s |
+| lib.js:45:13:45:13 | s |
+| lib.js:46:10:46:10 | s |
+| lib.js:52:9:52:22 | path |
+| lib.js:52:16:52:22 | id("x") |
+| lib.js:55:11:55:22 | obj[path[0]] |
+| lib.js:55:11:55:22 | obj[path[0]] |
+| lib.js:55:15:55:18 | path |
+| lib.js:55:15:55:21 | path[0] |
+| lib.js:59:18:59:18 | s |
+| lib.js:59:18:59:18 | s |
+| lib.js:61:17:61:17 | s |
+| lib.js:68:11:68:26 | path |
+| lib.js:68:18:68:26 | this.path |
+| lib.js:70:13:70:24 | obj[path[0]] |
+| lib.js:70:13:70:24 | obj[path[0]] |
+| lib.js:70:17:70:20 | path |
+| lib.js:70:17:70:23 | path[0] |
| tst.js:5:9:5:38 | taint |
| tst.js:5:17:5:38 | String( ... y.data) |
| tst.js:5:24:5:37 | req.query.data |
@@ -141,6 +159,22 @@ edges
| lib.js:42:7:42:10 | path | lib.js:42:7:42:13 | path[0] |
| lib.js:42:7:42:13 | path[0] | lib.js:42:3:42:14 | obj[path[0]] |
| lib.js:42:7:42:13 | path[0] | lib.js:42:3:42:14 | obj[path[0]] |
+| lib.js:45:13:45:13 | s | lib.js:46:10:46:10 | s |
+| lib.js:45:13:45:13 | s | lib.js:46:10:46:10 | s |
+| lib.js:46:10:46:10 | s | lib.js:52:16:52:22 | id("x") |
+| lib.js:52:9:52:22 | path | lib.js:55:15:55:18 | path |
+| lib.js:52:16:52:22 | id("x") | lib.js:52:9:52:22 | path |
+| lib.js:55:15:55:18 | path | lib.js:55:15:55:21 | path[0] |
+| lib.js:55:15:55:21 | path[0] | lib.js:55:11:55:22 | obj[path[0]] |
+| lib.js:55:15:55:21 | path[0] | lib.js:55:11:55:22 | obj[path[0]] |
+| lib.js:59:18:59:18 | s | lib.js:61:17:61:17 | s |
+| lib.js:59:18:59:18 | s | lib.js:61:17:61:17 | s |
+| lib.js:61:17:61:17 | s | lib.js:68:18:68:26 | this.path |
+| lib.js:68:11:68:26 | path | lib.js:70:17:70:20 | path |
+| lib.js:68:18:68:26 | this.path | lib.js:68:11:68:26 | path |
+| lib.js:70:17:70:20 | path | lib.js:70:17:70:23 | path[0] |
+| lib.js:70:17:70:23 | path[0] | lib.js:70:13:70:24 | obj[path[0]] |
+| lib.js:70:17:70:23 | path[0] | lib.js:70:13:70:24 | obj[path[0]] |
| tst.js:5:9:5:38 | taint | tst.js:8:12:8:16 | taint |
| tst.js:5:9:5:38 | taint | tst.js:9:12:9:16 | taint |
| tst.js:5:9:5:38 | taint | tst.js:12:25:12:29 | taint |
@@ -184,6 +218,7 @@ edges
| lib.js:26:10:26:21 | obj[path[0]] | lib.js:25:44:25:47 | path | lib.js:26:10:26:21 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:25:44:25:47 | path | library input |
| lib.js:34:3:34:14 | obj[path[0]] | lib.js:32:14:32:20 | args[1] | lib.js:34:3:34:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:32:14:32:20 | args[1] | library input |
| lib.js:42:3:42:14 | obj[path[0]] | lib.js:40:14:40:20 | args[1] | lib.js:42:3:42:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:40:14:40:20 | args[1] | library input |
+| lib.js:70:13:70:24 | obj[path[0]] | lib.js:59:18:59:18 | s | lib.js:70:13:70:24 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:59:18:59:18 | s | library input |
| tst.js:8:5:8:17 | object[taint] | tst.js:5:24:5:37 | req.query.data | tst.js:8:5:8:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | user controlled input |
| tst.js:9:5:9:17 | object[taint] | tst.js:5:24:5:37 | req.query.data | tst.js:9:5:9:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | user controlled input |
| tst.js:14:5:14:32 | unsafeG ... taint) | tst.js:5:24:5:37 | req.query.data | tst.js:14:5:14:32 | unsafeG ... taint) | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | user controlled input |
diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
index 023ef1fb9a6..16f95ea609a 100644
--- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
+++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
@@ -40,4 +40,35 @@ module.exports.setWithArgs3 = function() {
var path = args[1];
var value = args[2];
obj[path[0]][path[1]] = value; // NOT OK
-}
\ No newline at end of file
+}
+
+function id(s) {
+ return s;
+}
+
+module.exports.id = id;
+
+module.exports.notVulnerable = function () {
+ const path = id("x");
+ const value = id("y");
+ const obj = id("z");
+ return (obj[path[0]][path[1]] = value); // OK
+}
+
+class Foo {
+ constructor(o, s, v) {
+ this.obj = o;
+ this.path = s;
+ this.value = v;
+ }
+
+ doXss() {
+ // not called here, but still bad.
+ const obj = this.obj;
+ const path = this.path;
+ const value = this.value;
+ return (obj[path[0]][path[1]] = value); // NOT OK
+ }
+}
+
+module.exports.Foo = Foo;
From 1243c736dda8c9ee71ac0fd74163c68ad2b2a4a6 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 8 Sep 2021 13:15:17 +0200
Subject: [PATCH 174/471] use ConcatenationNode::isCoercion
---
.../security/dataflow/PrototypePollutingAssignmentQuery.qll | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
index 571135f978c..40a06fe2d55 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
@@ -34,10 +34,7 @@ class Configuration extends TaintTracking::Configuration {
// Concatenating with a string will in practice prevent the string `__proto__` from arising.
exists(StringOps::ConcatenationRoot root | node = root |
// Exclude the string coercion `"" + node` from this filter.
- not (
- strictcount(root.getALeaf()) = 2 and
- root.getALeaf().getStringValue() = ""
- )
+ not node.(StringOps::ConcatenationNode).isCoercion()
)
}
From a9a9e34265ef8112574689430ecc89056adfbd27 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 22 Sep 2021 18:57:54 +0200
Subject: [PATCH 175/471] recognize delete expresssions as a sink for
js/prototype-polluting-assignment
---
...totypePollutingAssignmentCustomizations.qll | 2 ++
.../PrototypePollutingAssignment.expected | 18 ++++++++++++++++++
.../PrototypePollutingAssignment/lib.js | 14 ++++++++++++++
3 files changed, 34 insertions(+)
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentCustomizations.qll
index 4d1bfbebbf7..f7868c290aa 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentCustomizations.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentCustomizations.qll
@@ -49,6 +49,8 @@ module PrototypePollutingAssignment {
this = any(DataFlow::PropWrite write).getBase()
or
this = any(ExtendCall c).getDestinationOperand()
+ or
+ this = any(DeleteExpr del).getOperand().flow().(DataFlow::PropRef).getBase()
}
override DataFlow::FlowLabel getAFlowLabel() { result instanceof ObjectPrototype }
diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
index 6e2ca7874be..1ae74c62e1b 100644
--- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
@@ -70,6 +70,15 @@ nodes
| lib.js:70:13:70:24 | obj[path[0]] |
| lib.js:70:17:70:20 | path |
| lib.js:70:17:70:23 | path[0] |
+| lib.js:83:7:83:25 | path |
+| lib.js:83:14:83:25 | arguments[1] |
+| lib.js:83:14:83:25 | arguments[1] |
+| lib.js:86:7:86:26 | proto |
+| lib.js:86:15:86:26 | obj[path[0]] |
+| lib.js:86:19:86:22 | path |
+| lib.js:86:19:86:25 | path[0] |
+| lib.js:87:10:87:14 | proto |
+| lib.js:87:10:87:14 | proto |
| tst.js:5:9:5:38 | taint |
| tst.js:5:17:5:38 | String( ... y.data) |
| tst.js:5:24:5:37 | req.query.data |
@@ -175,6 +184,14 @@ edges
| lib.js:70:17:70:20 | path | lib.js:70:17:70:23 | path[0] |
| lib.js:70:17:70:23 | path[0] | lib.js:70:13:70:24 | obj[path[0]] |
| lib.js:70:17:70:23 | path[0] | lib.js:70:13:70:24 | obj[path[0]] |
+| lib.js:83:7:83:25 | path | lib.js:86:19:86:22 | path |
+| lib.js:83:14:83:25 | arguments[1] | lib.js:83:7:83:25 | path |
+| lib.js:83:14:83:25 | arguments[1] | lib.js:83:7:83:25 | path |
+| lib.js:86:7:86:26 | proto | lib.js:87:10:87:14 | proto |
+| lib.js:86:7:86:26 | proto | lib.js:87:10:87:14 | proto |
+| lib.js:86:15:86:26 | obj[path[0]] | lib.js:86:7:86:26 | proto |
+| lib.js:86:19:86:22 | path | lib.js:86:19:86:25 | path[0] |
+| lib.js:86:19:86:25 | path[0] | lib.js:86:15:86:26 | obj[path[0]] |
| tst.js:5:9:5:38 | taint | tst.js:8:12:8:16 | taint |
| tst.js:5:9:5:38 | taint | tst.js:9:12:9:16 | taint |
| tst.js:5:9:5:38 | taint | tst.js:12:25:12:29 | taint |
@@ -219,6 +236,7 @@ edges
| lib.js:34:3:34:14 | obj[path[0]] | lib.js:32:14:32:20 | args[1] | lib.js:34:3:34:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:32:14:32:20 | args[1] | library input |
| lib.js:42:3:42:14 | obj[path[0]] | lib.js:40:14:40:20 | args[1] | lib.js:42:3:42:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:40:14:40:20 | args[1] | library input |
| lib.js:70:13:70:24 | obj[path[0]] | lib.js:59:18:59:18 | s | lib.js:70:13:70:24 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:59:18:59:18 | s | library input |
+| lib.js:87:10:87:14 | proto | lib.js:83:14:83:25 | arguments[1] | lib.js:87:10:87:14 | proto | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:83:14:83:25 | arguments[1] | library input |
| tst.js:8:5:8:17 | object[taint] | tst.js:5:24:5:37 | req.query.data | tst.js:8:5:8:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | user controlled input |
| tst.js:9:5:9:17 | object[taint] | tst.js:5:24:5:37 | req.query.data | tst.js:9:5:9:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | user controlled input |
| tst.js:14:5:14:32 | unsafeG ... taint) | tst.js:5:24:5:37 | req.query.data | tst.js:14:5:14:32 | unsafeG ... taint) | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | user controlled input |
diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
index 16f95ea609a..73e34407fcc 100644
--- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
+++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
@@ -69,6 +69,20 @@ class Foo {
const value = this.value;
return (obj[path[0]][path[1]] = value); // NOT OK
}
+
+ safe() {
+ const obj = this.obj;
+ obj[path[0]] = this.value; // OK
+ }
}
module.exports.Foo = Foo;
+
+module.exports.delete = function() {
+ var obj = arguments[0];
+ var path = arguments[1];
+ delete obj[path[0]]; // OK
+ var prop = arguments[2];
+ var proto = obj[path[0]];
+ delete proto[prop]; // NOT
+}
From 78371894f443166f4be52393ffe570dff762eb7b Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 27 Oct 2021 20:47:06 +0200
Subject: [PATCH 176/471] update import after rebasing on main
---
.../security/dataflow/PrototypePollutingAssignmentQuery.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
index 40a06fe2d55..51dfa8a38a3 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
@@ -11,7 +11,7 @@ private import javascript
private import semmle.javascript.DynamicPropertyAccess
private import semmle.javascript.dataflow.InferredTypes
import PrototypePollutingAssignmentCustomizations::PrototypePollutingAssignment
-private import filters.ClassifyFiles as ClassifyFiles
+private import semmle.javascript.filters.ClassifyFiles as ClassifyFiles
// Materialize flow labels
private class ConcreteObjectPrototype extends ObjectPrototype {
From 96b6f670d9a764af23af2d008014f3d2d864880f Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Wed, 27 Oct 2021 21:01:11 +0200
Subject: [PATCH 177/471] filter away paths that start with libary inputs and
end with a fixed-property write
---
.../dataflow/PrototypePollutingAssignmentQuery.qll | 11 +++++++++--
.../PrototypePollutingAssignment.expected | 13 +++++++++++++
.../CWE-915/PrototypePollutingAssignment/lib.js | 7 ++++++-
3 files changed, 28 insertions(+), 3 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
index 51dfa8a38a3..826fbe0828d 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
@@ -70,10 +70,17 @@ class Configuration extends TaintTracking::Configuration {
DataFlow::localFieldStep(pred, succ) and inlbl = outlbl
}
- // override to require that there is a path without unmatched return steps
override predicate hasFlowPath(DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink) {
super.hasFlowPath(source, sink) and
- DataFlow::hasPathWithoutUnmatchedReturn(source, sink)
+ // require that there is a path without unmatched return steps
+ DataFlow::hasPathWithoutUnmatchedReturn(source, sink) and
+ // filter away paths that start with library inputs and end with a write to a fixed property.
+ not exists(ExternalInputSource src, Sink snk, DataFlow::PropWrite write |
+ source.getNode() = src and sink.getNode() = snk
+ |
+ snk = write.getBase() and
+ exists(write.getPropertyName())
+ )
}
override predicate isLabeledBarrier(DataFlow::Node node, DataFlow::FlowLabel lbl) {
diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
index 1ae74c62e1b..f8832cbdf4e 100644
--- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
@@ -79,6 +79,13 @@ nodes
| lib.js:86:19:86:25 | path[0] |
| lib.js:87:10:87:14 | proto |
| lib.js:87:10:87:14 | proto |
+| lib.js:90:43:90:46 | path |
+| lib.js:90:43:90:46 | path |
+| lib.js:91:7:91:28 | maybeProto |
+| lib.js:91:20:91:28 | obj[path] |
+| lib.js:91:24:91:27 | path |
+| lib.js:92:3:92:12 | maybeProto |
+| lib.js:92:3:92:12 | maybeProto |
| tst.js:5:9:5:38 | taint |
| tst.js:5:17:5:38 | String( ... y.data) |
| tst.js:5:24:5:37 | req.query.data |
@@ -192,6 +199,12 @@ edges
| lib.js:86:15:86:26 | obj[path[0]] | lib.js:86:7:86:26 | proto |
| lib.js:86:19:86:22 | path | lib.js:86:19:86:25 | path[0] |
| lib.js:86:19:86:25 | path[0] | lib.js:86:15:86:26 | obj[path[0]] |
+| lib.js:90:43:90:46 | path | lib.js:91:24:91:27 | path |
+| lib.js:90:43:90:46 | path | lib.js:91:24:91:27 | path |
+| lib.js:91:7:91:28 | maybeProto | lib.js:92:3:92:12 | maybeProto |
+| lib.js:91:7:91:28 | maybeProto | lib.js:92:3:92:12 | maybeProto |
+| lib.js:91:20:91:28 | obj[path] | lib.js:91:7:91:28 | maybeProto |
+| lib.js:91:24:91:27 | path | lib.js:91:20:91:28 | obj[path] |
| tst.js:5:9:5:38 | taint | tst.js:8:12:8:16 | taint |
| tst.js:5:9:5:38 | taint | tst.js:9:12:9:16 | taint |
| tst.js:5:9:5:38 | taint | tst.js:12:25:12:29 | taint |
diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
index 73e34407fcc..2e9ecabe089 100644
--- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
+++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
@@ -84,5 +84,10 @@ module.exports.delete = function() {
delete obj[path[0]]; // OK
var prop = arguments[2];
var proto = obj[path[0]];
- delete proto[prop]; // NOT
+ delete proto[prop]; // NOT OK
}
+
+module.exports.fixedProp = function (obj, path, value) {
+ var maybeProto = obj[path];
+ maybeProto.foo = value; // OK - fixed properties from library inputs are OK.
+}
\ No newline at end of file
From c3b1d7e5c82a7a709199d5e0983599fa0777ca1e Mon Sep 17 00:00:00 2001
From: ihsinme
Date: Thu, 28 Oct 2021 10:17:13 +0300
Subject: [PATCH 178/471] Apply suggestions from code review
Co-authored-by: Mathias Vorreiter Pedersen
---
.../CWE/CWE-243/IncorrectChangingWorkingDirectory.ql | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.ql b/cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.ql
index a6026adbce3..b1a066256a8 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.ql
@@ -13,7 +13,7 @@
import cpp
-/** Holds if a `fc` function call is available before or before a `chdir` function call. */
+/** Holds if a `fc` function call is available before or after a `chdir` function call. */
predicate inExistsChdir(FunctionCall fcp) {
exists(FunctionCall fctmp |
(
@@ -54,7 +54,7 @@ where
not inExistsChdir(fctmp) and
not outExistsChdir(fctmp)
) and
- msg = "Creation of chroot Jail Without Changing Working Directory out"
+ msg = "Creation of 'chroot' jail without changing the working directory"
or
(
fc.getTarget().hasGlobalOrStdName("chdir") or
@@ -65,6 +65,6 @@ where
not exists(ReturnStmt rttmp | rttmp.getExpr().getAChild*() = fc) and
not exists(Assignment astmp | astmp.getAChild*() = fc) and
not exists(Initializer ittmp | ittmp.getExpr().getAChild*() = fc) and
- not fc.isInMacroExpansion() and
- msg = fc.getTarget().getName() + " unchecked return value."
+ not isFromMacroDefinition(fc)
+ msg = "Unchecked return value for call to '" + fc.getTarget().getName() + "'."
select fc, msg
From 0addb2d1eabb39207e474d2a45de4c16a3d36de0 Mon Sep 17 00:00:00 2001
From: ihsinme
Date: Thu, 28 Oct 2021 10:17:48 +0300
Subject: [PATCH 179/471] Update IncorrectChangingWorkingDirectory.ql
---
.../Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.ql | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.ql b/cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.ql
index b1a066256a8..d731c70a8e2 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.ql
@@ -12,6 +12,7 @@
*/
import cpp
+import semmle.code.cpp.commons.Exclusions
/** Holds if a `fc` function call is available before or after a `chdir` function call. */
predicate inExistsChdir(FunctionCall fcp) {
@@ -27,7 +28,7 @@ predicate inExistsChdir(FunctionCall fcp) {
)
}
-/** Holds if a `fc` function call is available before or before a function call containing a `chdir` call. */
+/** Holds if a `fc` function call is available before or after a function call containing a `chdir` call. */
predicate outExistsChdir(FunctionCall fcp) {
exists(FunctionCall fctmp |
exists(FunctionCall fctmp2 |
From 235a3ec23289d15c276dba3b3d75d6ac1be721c4 Mon Sep 17 00:00:00 2001
From: ihsinme
Date: Thu, 28 Oct 2021 10:34:42 +0300
Subject: [PATCH 180/471] Update InsecureTemporaryFile.qhelp
---
.../Security/CWE/CWE-377/InsecureTemporaryFile.qhelp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.qhelp
index 3c488cf1cc5..248394525d8 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.qhelp
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.qhelp
@@ -5,7 +5,8 @@
Working with a file, without checking its existence and its rights, as well as working with names that can be predicted, may not be safe. Requires the attention of developers.
-
+
+
The following example demonstrates erroneous and corrected work with file.
From 432fc7445552d3d256e3890f84da031e1113c50a Mon Sep 17 00:00:00 2001
From: ihsinme
Date: Thu, 28 Oct 2021 10:37:01 +0300
Subject: [PATCH 181/471] Apply suggestions from code review
Co-authored-by: Mathias Vorreiter Pedersen
---
.../Security/CWE/CWE-377/InsecureTemporaryFile.ql | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.ql b/cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.ql
index 83314d3cb79..e039f7a7e62 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.ql
@@ -1,8 +1,8 @@
/**
- * @name Find insecure work with the file name.
+ * @name Insecure generation of filenames.
* @description The file can be used to influence the correct operation of the program.
* @kind problem
- * @id cpp/insecure-work-with-file-name
+ * @id cpp/insecure-generation-of-filename
* @problem.severity warning
* @precision medium
* @tags correctness
From cca675a161794fe834aca8abd16de435e6bc4f9e Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Wed, 27 Oct 2021 15:33:54 +0200
Subject: [PATCH 182/471] Python: Add test for async taint (which we belive we
have just broken)
---
.../dataflow/ApiGraphs/async_test.py | 2 +-
.../dataflow/tainttracking/TestTaintLib.qll | 7 +++
.../defaultAdditionalTaintStep/test_async.py | 57 +++++++++++++++++++
.../dataflow/tainttracking/taintlib.py | 5 ++
4 files changed, 70 insertions(+), 1 deletion(-)
create mode 100644 python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_async.py
diff --git a/python/ql/test/experimental/dataflow/ApiGraphs/async_test.py b/python/ql/test/experimental/dataflow/ApiGraphs/async_test.py
index b962a0eae49..faee160cc68 100644
--- a/python/ql/test/experimental/dataflow/ApiGraphs/async_test.py
+++ b/python/ql/test/experimental/dataflow/ApiGraphs/async_test.py
@@ -13,7 +13,7 @@ async def bar():
async def test_async_with():
async with pkg.async_func() as result: # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited() awaited=moduleImport("pkg").getMember("async_func").getReturn()
- return result # $ use=moduleImport("pkg").getMember("async_func").getReturn() awaited=moduleImport("pkg").getMember("async_func").getReturn()
+ return result # $ awaited=moduleImport("pkg").getMember("async_func").getReturn()
async def test_async_for():
async for _ in pkg.async_func(): # $ use=moduleImport("pkg").getMember("async_func").getReturn() awaited=moduleImport("pkg").getMember("async_func").getReturn()
diff --git a/python/ql/test/experimental/dataflow/tainttracking/TestTaintLib.qll b/python/ql/test/experimental/dataflow/tainttracking/TestTaintLib.qll
index 40c7b245870..02754a3cac4 100644
--- a/python/ql/test/experimental/dataflow/tainttracking/TestTaintLib.qll
+++ b/python/ql/test/experimental/dataflow/tainttracking/TestTaintLib.qll
@@ -7,9 +7,16 @@ class TestTaintTrackingConfiguration extends TaintTracking::Configuration {
TestTaintTrackingConfiguration() { this = "TestTaintTrackingConfiguration" }
override predicate isSource(DataFlow::Node source) {
+ // Standard sources
source.(DataFlow::CfgNode).getNode().(NameNode).getId() in [
"TAINTED_STRING", "TAINTED_BYTES", "TAINTED_LIST", "TAINTED_DICT"
]
+ or
+ // User defined sources
+ exists(CallNode call |
+ call.getFunction().(NameNode).getId() = "taint" and
+ source.(DataFlow::CfgNode).getNode() = call.getAnArg()
+ )
}
override predicate isSink(DataFlow::Node sink) {
diff --git a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_async.py b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_async.py
new file mode 100644
index 00000000000..509a20c9010
--- /dev/null
+++ b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_async.py
@@ -0,0 +1,57 @@
+# Add taintlib to PATH so it can be imported during runtime without any hassle
+import sys; import os; sys.path.append(os.path.dirname(os.path.dirname((__file__))))
+from taintlib import *
+
+# This has no runtime impact, but allows autocomplete to work
+from typing import TYPE_CHECKING
+if TYPE_CHECKING:
+ from ..taintlib import *
+
+
+# Actual tests
+
+async def tainted_coro():
+ return TAINTED_STRING
+
+async def test_await():
+ coro = tainted_coro()
+ taint(coro)
+ s = await coro
+ ensure_tainted(coro, s) # $ tainted
+
+
+class AsyncContext:
+ async def __aenter__(self):
+ return TAINTED_STRING
+
+ async def __aexit__(self, exc_type, exc, tb):
+ pass
+
+async def test_async_with():
+ ctx = AsyncContext()
+ taint(ctx)
+ async with ctx as tainted:
+ ensure_tainted(tainted) # $ MISSING: tainted
+
+
+class AsyncIter:
+ def __aiter__(self):
+ return self
+
+ async def __anext__(self):
+ raise StopAsyncIteration
+
+async def test_async_for():
+ iter = AsyncIter()
+ taint(iter)
+ async for tainted in iter:
+ ensure_tainted(tainted) # $ MISSING: tainted
+
+
+
+# Make tests runable
+import asyncio
+
+asyncio.run(test_await())
+asyncio.run(test_async_with())
+asyncio.run(test_async_for())
diff --git a/python/ql/test/experimental/dataflow/tainttracking/taintlib.py b/python/ql/test/experimental/dataflow/tainttracking/taintlib.py
index 04996e156aa..a5ae33e1ba7 100644
--- a/python/ql/test/experimental/dataflow/tainttracking/taintlib.py
+++ b/python/ql/test/experimental/dataflow/tainttracking/taintlib.py
@@ -5,6 +5,11 @@ TAINTED_DICT = {"name": TAINTED_STRING, "some key": "foo"}
NOT_TAINTED = "NOT_TAINTED"
+# Use this to force expressions to be tainted
+def taint(*args):
+ pass
+
+
def ensure_tainted(*args):
print("- ensure_tainted")
for i, arg in enumerate(args):
From 56dab252c9f6a88e1ba1ab202cb9ac2b45af6192 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Wed, 27 Oct 2021 15:33:27 +0200
Subject: [PATCH 183/471] Python: remove spurious dataflow step
---
.../lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll | 1 +
1 file changed, 1 insertion(+)
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll
index 44c64234b75..933b82b56e1 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll
@@ -171,6 +171,7 @@ module EssaFlow {
// see `with_flow` in `python/ql/src/semmle/python/dataflow/Implementation.qll`
with.getContextExpr() = contextManager.getNode() and
with.getOptionalVars() = var.getNode() and
+ not with.isAsync() and
contextManager.strictlyDominates(var)
)
or
From 2574aa89805e65f8115a009f0dd38ad49df98529 Mon Sep 17 00:00:00 2001
From: ihsinme
Date: Thu, 28 Oct 2021 10:51:48 +0300
Subject: [PATCH 184/471] Update InsecureTemporaryFile.ql
From 3fb01394302dc375335c7ea55a19070895763403 Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Thu, 28 Oct 2021 09:58:10 +0200
Subject: [PATCH 185/471] Protect against flag injection
---
.github/workflows/qhelp-pr-preview.yml | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/qhelp-pr-preview.yml b/.github/workflows/qhelp-pr-preview.yml
index ddfb5c957a5..580564c3752 100644
--- a/.github/workflows/qhelp-pr-preview.yml
+++ b/.github/workflows/qhelp-pr-preview.yml
@@ -29,14 +29,17 @@ jobs:
run: |
(git diff --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep '.qhelp$' | grep -v '.inc.qhelp';
git diff --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep '.inc.qhelp$' | xargs -d '\n' -rn1 basename | xargs -d '\n' -rn1 git grep -l) |
- grep '.qhelp$' | sort -u > "${{ runner.temp }}/paths.txt"
+ grep '.qhelp$' | grep -v '^-' | sort -u > "${{ runner.temp }}/paths.txt"
- name: QHelp preview
run: |
cat "${{ runner.temp }}/paths.txt" | while read path; do
+ if [ ! -f "${path}" ]; then
+ exit 1
+ fi
echo " ${path}
"
echo
- codeql generate query-help --format=markdown "${path}"
+ codeql generate query-help --format=markdown -- "./${path}"
echo " "
done > comment.txt
From bccd4e9e93defda4c7a2100ed8d2234552cc7ef4 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Thu, 28 Oct 2021 10:51:49 +0100
Subject: [PATCH 186/471] C++: Add 'getReturnAddress' and
'getReturnAddressOperand' predicates to 'ReturnValueInstruction'.
---
.../ir/implementation/aliased_ssa/Instruction.qll | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll
index 2fb3edad602..f2cafede5fe 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll
@@ -753,11 +753,21 @@ class ReturnValueInstruction extends ReturnInstruction {
*/
final LoadOperand getReturnValueOperand() { result = getAnOperand() }
+ /**
+ * Gets the operand that provides the address of the value being returned by the function.
+ */
+ final AddressOperand getReturnAddressOperand() { result = this.getAnOperand() }
+
/**
* Gets the instruction whose result provides the value being returned by the function, if an
* exact definition is available.
*/
- final Instruction getReturnValue() { result = getReturnValueOperand().getDef() }
+ final Instruction getReturnValue() { result = this.getReturnValueOperand().getDef() }
+
+ /**
+ * Gets the instruction whose result provides the address of the value being returned by the function.
+ */
+ final Instruction getReturnAddress() { result = this.getReturnAddressOperand().getDef() }
}
/**
From 13ce2569d7f95bd37b220edad9d430a6bae325c7 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Thu, 28 Oct 2021 10:52:00 +0100
Subject: [PATCH 187/471] =?UTF-8?q?C++/C#:=20Sync=20identical=20IR=20files?=
=?UTF-8?q?=C2=B7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../code/cpp/ir/implementation/raw/Instruction.qll | 12 +++++++++++-
.../ir/implementation/unaliased_ssa/Instruction.qll | 12 +++++++++++-
.../ir/implementation/raw/Instruction.qll | 12 +++++++++++-
.../ir/implementation/unaliased_ssa/Instruction.qll | 12 +++++++++++-
4 files changed, 44 insertions(+), 4 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll
index 2fb3edad602..f2cafede5fe 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll
@@ -753,11 +753,21 @@ class ReturnValueInstruction extends ReturnInstruction {
*/
final LoadOperand getReturnValueOperand() { result = getAnOperand() }
+ /**
+ * Gets the operand that provides the address of the value being returned by the function.
+ */
+ final AddressOperand getReturnAddressOperand() { result = this.getAnOperand() }
+
/**
* Gets the instruction whose result provides the value being returned by the function, if an
* exact definition is available.
*/
- final Instruction getReturnValue() { result = getReturnValueOperand().getDef() }
+ final Instruction getReturnValue() { result = this.getReturnValueOperand().getDef() }
+
+ /**
+ * Gets the instruction whose result provides the address of the value being returned by the function.
+ */
+ final Instruction getReturnAddress() { result = this.getReturnAddressOperand().getDef() }
}
/**
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll
index 2fb3edad602..f2cafede5fe 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll
@@ -753,11 +753,21 @@ class ReturnValueInstruction extends ReturnInstruction {
*/
final LoadOperand getReturnValueOperand() { result = getAnOperand() }
+ /**
+ * Gets the operand that provides the address of the value being returned by the function.
+ */
+ final AddressOperand getReturnAddressOperand() { result = this.getAnOperand() }
+
/**
* Gets the instruction whose result provides the value being returned by the function, if an
* exact definition is available.
*/
- final Instruction getReturnValue() { result = getReturnValueOperand().getDef() }
+ final Instruction getReturnValue() { result = this.getReturnValueOperand().getDef() }
+
+ /**
+ * Gets the instruction whose result provides the address of the value being returned by the function.
+ */
+ final Instruction getReturnAddress() { result = this.getReturnAddressOperand().getDef() }
}
/**
diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll
index 2fb3edad602..f2cafede5fe 100644
--- a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll
+++ b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll
@@ -753,11 +753,21 @@ class ReturnValueInstruction extends ReturnInstruction {
*/
final LoadOperand getReturnValueOperand() { result = getAnOperand() }
+ /**
+ * Gets the operand that provides the address of the value being returned by the function.
+ */
+ final AddressOperand getReturnAddressOperand() { result = this.getAnOperand() }
+
/**
* Gets the instruction whose result provides the value being returned by the function, if an
* exact definition is available.
*/
- final Instruction getReturnValue() { result = getReturnValueOperand().getDef() }
+ final Instruction getReturnValue() { result = this.getReturnValueOperand().getDef() }
+
+ /**
+ * Gets the instruction whose result provides the address of the value being returned by the function.
+ */
+ final Instruction getReturnAddress() { result = this.getReturnAddressOperand().getDef() }
}
/**
diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll
index 2fb3edad602..f2cafede5fe 100644
--- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll
+++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll
@@ -753,11 +753,21 @@ class ReturnValueInstruction extends ReturnInstruction {
*/
final LoadOperand getReturnValueOperand() { result = getAnOperand() }
+ /**
+ * Gets the operand that provides the address of the value being returned by the function.
+ */
+ final AddressOperand getReturnAddressOperand() { result = this.getAnOperand() }
+
/**
* Gets the instruction whose result provides the value being returned by the function, if an
* exact definition is available.
*/
- final Instruction getReturnValue() { result = getReturnValueOperand().getDef() }
+ final Instruction getReturnValue() { result = this.getReturnValueOperand().getDef() }
+
+ /**
+ * Gets the instruction whose result provides the address of the value being returned by the function.
+ */
+ final Instruction getReturnAddress() { result = this.getReturnAddressOperand().getDef() }
}
/**
From 1842fed7a2dbd6d6cfa4582362a2cec757e7a54f Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Wed, 20 Oct 2021 15:03:35 +0100
Subject: [PATCH 188/471] C++: Add shared SSA library and instantiate it with
the IR.
---
.../code/cpp/ir/dataflow/internal/Ssa.qll | 303 +++++++++
.../ir/dataflow/internal/SsaImplCommon.qll | 637 ++++++++++++++++++
.../ir/dataflow/internal/SsaImplSpecific.qll | 64 ++
3 files changed, 1004 insertions(+)
create mode 100644 cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
create mode 100644 cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll
create mode 100644 cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplSpecific.qll
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
new file mode 100644
index 00000000000..57ba19f71f0
--- /dev/null
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
@@ -0,0 +1,303 @@
+import SsaImplCommon
+import SsaImplSpecific
+private import cpp as Cpp
+private import semmle.code.cpp.ir.IR
+private import DataFlowUtil
+private import DataFlowPrivate
+private import semmle.code.cpp.models.interfaces.Allocation as Alloc
+private import semmle.code.cpp.models.interfaces.DataFlow as DataFlow
+
+cached
+private newtype TDefOrUse =
+ TExplicitDef(Instruction store) { explicitWrite(_, store, _) } or
+ TInitializeParam(Instruction instr) {
+ instr instanceof InitializeParameterInstruction
+ or
+ instr instanceof InitializeIndirectionInstruction
+ } or
+ TExplicitUse(Operand op) { isExplicitUse(op) } or
+ TReturnParamIndirection(Operand op) { returnParameterIndirection(op, _) }
+
+pragma[nomagic]
+private int getRank(DefOrUse defOrUse, IRBlock block) {
+ defOrUse =
+ rank[result](int i, DefOrUse cand |
+ block.getInstruction(i) = toInstruction(cand)
+ |
+ cand order by i
+ )
+}
+
+private class DefOrUse extends TDefOrUse {
+ /** Gets the instruction associated with this definition, if any. */
+ Instruction asDef() { none() }
+
+ /** Gets the operand associated with this use, if any. */
+ Operand asUse() { none() }
+
+ /** Gets a textual representation of this element. */
+ abstract string toString();
+
+ /** Gets the block of this definition or use. */
+ abstract IRBlock getBlock();
+
+ /** Holds if this definition or use has rank `rank` in block `block`. */
+ cached
+ final predicate hasRankInBlock(IRBlock block, int rnk) {
+ block = getBlock() and
+ rnk = getRank(this, block)
+ }
+
+ /**
+ * 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://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+ abstract predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ );
+}
+
+private Instruction toInstruction(DefOrUse defOrUse) {
+ result = defOrUse.asDef()
+ or
+ result = defOrUse.asUse().getUse()
+}
+
+abstract class Def extends DefOrUse {
+ Instruction store;
+
+ /** Gets the instruction of this definition. */
+ Instruction getInstruction() { result = store }
+
+ /** Gets the variable that is defined by this definition. */
+ abstract SourceVariable getVariable();
+
+ /** Holds if this definition is guaranteed to happen. */
+ abstract predicate isCertain();
+
+ override Instruction asDef() { result = this.getInstruction() }
+
+ override string toString() { result = "Def" }
+
+ override IRBlock getBlock() { result = this.getInstruction().getBlock() }
+
+ override predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ store.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ }
+}
+
+private class ExplicitDef extends Def, TExplicitDef {
+ ExplicitDef() { this = TExplicitDef(store) }
+
+ override SourceVariable getVariable() {
+ exists(VariableInstruction var |
+ explicitWrite(_, this.getInstruction(), var) and
+ result.getVariable() = var.getIRVariable() and
+ not result.isIndirection()
+ )
+ }
+
+ override predicate isCertain() { explicitWrite(true, this.getInstruction(), _) }
+}
+
+private class ParameterDef extends Def, TInitializeParam {
+ ParameterDef() { this = TInitializeParam(store) }
+
+ override SourceVariable getVariable() {
+ result.getVariable() = store.(InitializeParameterInstruction).getIRVariable() and
+ not result.isIndirection()
+ or
+ result.getVariable() = store.(InitializeIndirectionInstruction).getIRVariable() and
+ result.isIndirection()
+ }
+
+ override predicate isCertain() { any() }
+}
+
+abstract class Use extends DefOrUse {
+ Operand use;
+
+ override Operand asUse() { result = use }
+
+ /** Gets the underlying operand of this use. */
+ Operand getOperand() { result = use }
+
+ override string toString() { result = "Use" }
+
+ /** Gets the variable that is used by this use. */
+ abstract SourceVariable getVariable();
+
+ override IRBlock getBlock() { result = use.getUse().getBlock() }
+
+ override predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ use.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ }
+}
+
+private class ExplicitUse extends Use, TExplicitUse {
+ ExplicitUse() { this = TExplicitUse(use) }
+
+ override SourceVariable getVariable() {
+ exists(VariableInstruction var |
+ use.getDef() = var and
+ result.getVariable() = var.getIRVariable() and
+ (
+ if use.getUse() instanceof ReadSideEffectInstruction
+ then result.isIndirection()
+ else not result.isIndirection()
+ )
+ )
+ }
+}
+
+private class ReturnParameterIndirection extends Use, TReturnParamIndirection {
+ ReturnParameterIndirection() { this = TReturnParamIndirection(use) }
+
+ override SourceVariable getVariable() {
+ exists(ReturnIndirectionInstruction ret |
+ returnParameterIndirection(use, ret) and
+ result.getVariable() = ret.getIRVariable() and
+ result.isIndirection()
+ )
+ }
+}
+
+private predicate isExplicitUse(Operand op) {
+ op.getDef() instanceof VariableAddressInstruction and
+ not exists(LoadInstruction load |
+ load.getSourceAddressOperand() = op and
+ load.getAUse().getUse() instanceof InitializeIndirectionInstruction
+ )
+}
+
+private predicate returnParameterIndirection(Operand op, ReturnIndirectionInstruction ret) {
+ ret.getSourceAddressOperand() = op
+}
+
+/**
+ * Holds if `iFrom` computes an address that is used by `iTo`.
+ */
+predicate addressFlow(Instruction iFrom, Instruction iTo) {
+ iTo.(CopyValueInstruction).getSourceValue() = iFrom
+ or
+ iTo.(ConvertInstruction).getUnary() = iFrom
+ or
+ iTo.(CheckedConvertOrNullInstruction).getUnary() = iFrom
+ or
+ iTo.(InheritanceConversionInstruction).getUnary() = iFrom
+ or
+ iTo.(PointerArithmeticInstruction).getLeft() = iFrom
+ or
+ iTo.(FieldAddressInstruction).getObjectAddress() = iFrom
+ or
+ iTo.(LoadInstruction).getSourceAddress() = iFrom
+ or
+ exists(WriteSideEffectInstruction write |
+ write.getPrimaryInstruction() = iTo and
+ write.getDestinationAddress() = iFrom
+ )
+}
+
+/**
+ * The reflexive, transitive closure of `addressFlow` that ends as the address of a
+ * store or read operation.
+ */
+cached
+predicate addressFlowTC(Instruction iFrom, Instruction iTo) {
+ iTo = [getDestinationAddress(_), getSourceAddress(_)] and
+ addressFlow*(iFrom, iTo)
+}
+
+/**
+ * Gets the destination address of `instr` if it is a `StoreInstruction` or
+ * a `WriteSideEffectInstruction`. The destination address of a `WriteSideEffectInstruction` is adjusted
+ * in the case of calls to operator `new` to give the destination address of a subsequent store (if any).
+ */
+Instruction getDestinationAddress(Instruction instr) {
+ result =
+ [
+ instr.(StoreInstruction).getDestinationAddress(),
+ instr.(WriteSideEffectInstruction).getDestinationAddress()
+ ]
+}
+
+class ReferenceToInstruction extends CopyValueInstruction {
+ ReferenceToInstruction() {
+ this.getResultType() instanceof Cpp::ReferenceType and
+ not this.getUnary().getResultType() instanceof Cpp::ReferenceType
+ }
+
+ Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() }
+
+ Operand getSourceAddressOperand() { result = this.getUnaryOperand() }
+}
+
+/** Gets the source address of `instr` if it is an instruction that behaves like a `LoadInstruction`. */
+Instruction getSourceAddress(Instruction instr) { result = getSourceAddressOperand(instr).getDef() }
+
+/**
+ * Gets the operand that represents the source address of `instr` if it is an
+ * instruction that behaves like a `LoadInstruction`.
+ */
+Operand getSourceAddressOperand(Instruction instr) {
+ result =
+ [
+ instr.(LoadInstruction).getSourceAddressOperand(),
+ instr.(ReadSideEffectInstruction).getArgumentOperand(),
+ instr.(ReferenceToInstruction).getSourceAddressOperand()
+ ]
+}
+
+/**
+ * Gets the source address of `node` if it's an instruction or operand that
+ * behaves like a `LoadInstruction`.
+ */
+Instruction getSourceAddressFromNode(Node node) {
+ result = getSourceAddress(node.asInstruction())
+ or
+ result = getSourceAddress(node.asOperand().(SideEffectOperand).getUse())
+}
+
+/** Gets the source value of `instr` if it's an instruction that behaves like a `LoadInstruction`. */
+Instruction getSourceValue(Instruction instr) { result = getSourceValueOperand(instr).getDef() }
+
+/**
+ * Gets the operand that represents the source value of `instr` if it's an instruction
+ * that behaves like a `LoadInstruction`.
+ */
+Operand getSourceValueOperand(Instruction instr) {
+ result = instr.(LoadInstruction).getSourceValueOperand()
+ or
+ result = instr.(ReadSideEffectInstruction).getSideEffectOperand()
+ or
+ result = instr.(ReferenceToInstruction).getSourceValueOperand()
+}
+
+/**
+ * Holds if `instr` is a `StoreInstruction` or a `WriteSideEffectInstruction` that writes to an address.
+ * The addresses is computed using `address`, and `certain` is `true` if the write is guaranteed to overwrite
+ * the entire variable.
+ */
+cached
+predicate explicitWrite(boolean certain, Instruction instr, Instruction address) {
+ exists(StoreInstruction store |
+ store = instr and addressFlowTC(address, store.getDestinationAddress())
+ |
+ if
+ addressFlowTC(any(Instruction i |
+ i instanceof FieldAddressInstruction or i instanceof PointerArithmeticInstruction
+ ), store.getDestinationAddress())
+ then certain = false
+ else certain = true
+ )
+ or
+ addressFlowTC(address, instr.(WriteSideEffectInstruction).getDestinationAddress()) and
+ certain = false
+}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll
new file mode 100644
index 00000000000..884f4406d01
--- /dev/null
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll
@@ -0,0 +1,637 @@
+/**
+ * Provides a language-independent implementation of static single assignment
+ * (SSA) form.
+ */
+
+private import SsaImplSpecific
+
+private BasicBlock getABasicBlockPredecessor(BasicBlock bb) { getABasicBlockSuccessor(result) = bb }
+
+/**
+ * Liveness analysis (based on source variables) to restrict the size of the
+ * SSA representation.
+ */
+private module Liveness {
+ /**
+ * A classification of variable references into reads (of a given kind) and
+ * (certain or uncertain) writes.
+ */
+ private newtype TRefKind =
+ Read(boolean certain) { certain in [false, true] } or
+ Write(boolean certain) { certain in [false, true] }
+
+ private class RefKind extends TRefKind {
+ string toString() {
+ exists(boolean certain | this = Read(certain) and result = "read (" + certain + ")")
+ or
+ exists(boolean certain | this = Write(certain) and result = "write (" + certain + ")")
+ }
+
+ int getOrder() {
+ this = Read(_) and
+ result = 0
+ or
+ this = Write(_) and
+ result = 1
+ }
+ }
+
+ /**
+ * Holds if the `i`th node of basic block `bb` is a reference to `v` of kind `k`.
+ */
+ private predicate ref(BasicBlock bb, int i, SourceVariable v, RefKind k) {
+ exists(boolean certain | variableRead(bb, i, v, certain) | k = Read(certain))
+ or
+ exists(boolean certain | variableWrite(bb, i, v, certain) | k = Write(certain))
+ }
+
+ private newtype OrderedRefIndex =
+ MkOrderedRefIndex(int i, int tag) {
+ exists(RefKind rk | ref(_, i, _, rk) | tag = rk.getOrder())
+ }
+
+ private OrderedRefIndex refOrd(BasicBlock bb, int i, SourceVariable v, RefKind k, int ord) {
+ ref(bb, i, v, k) and
+ result = MkOrderedRefIndex(i, ord) and
+ ord = k.getOrder()
+ }
+
+ /**
+ * Gets the (1-based) rank of the reference to `v` at the `i`th node of
+ * basic block `bb`, which has the given reference kind `k`.
+ *
+ * Reads are considered before writes when they happen at the same index.
+ */
+ private int refRank(BasicBlock bb, int i, SourceVariable v, RefKind k) {
+ refOrd(bb, i, v, k, _) =
+ rank[result](int j, int ord, OrderedRefIndex res |
+ res = refOrd(bb, j, v, _, ord)
+ |
+ res order by j, ord
+ )
+ }
+
+ private int maxRefRank(BasicBlock bb, SourceVariable v) {
+ result = refRank(bb, _, v, _) and
+ not result + 1 = refRank(bb, _, v, _)
+ }
+
+ /**
+ * Gets the (1-based) rank of the first reference to `v` inside basic block `bb`
+ * that is either a read or a certain write.
+ */
+ private int firstReadOrCertainWrite(BasicBlock bb, SourceVariable v) {
+ result =
+ min(int r, RefKind k |
+ r = refRank(bb, _, v, k) and
+ k != Write(false)
+ |
+ r
+ )
+ }
+
+ /**
+ * Holds if source variable `v` is live at the beginning of basic block `bb`.
+ */
+ predicate liveAtEntry(BasicBlock bb, SourceVariable v) {
+ // The first read or certain write to `v` inside `bb` is a read
+ refRank(bb, _, v, Read(_)) = firstReadOrCertainWrite(bb, v)
+ or
+ // There is no certain write to `v` inside `bb`, but `v` is live at entry
+ // to a successor basic block of `bb`
+ not exists(firstReadOrCertainWrite(bb, v)) and
+ liveAtExit(bb, v)
+ }
+
+ /**
+ * Holds if source variable `v` is live at the end of basic block `bb`.
+ */
+ predicate liveAtExit(BasicBlock bb, SourceVariable v) {
+ liveAtEntry(getABasicBlockSuccessor(bb), v)
+ }
+
+ /**
+ * Holds if variable `v` is live in basic block `bb` at index `i`.
+ * The rank of `i` is `rnk` as defined by `refRank()`.
+ */
+ private predicate liveAtRank(BasicBlock bb, int i, SourceVariable v, int rnk) {
+ exists(RefKind kind | rnk = refRank(bb, i, v, kind) |
+ rnk = maxRefRank(bb, v) and
+ liveAtExit(bb, v)
+ or
+ ref(bb, i, v, kind) and
+ kind = Read(_)
+ or
+ exists(RefKind nextKind |
+ liveAtRank(bb, _, v, rnk + 1) and
+ rnk + 1 = refRank(bb, _, v, nextKind) and
+ nextKind != Write(true)
+ )
+ )
+ }
+
+ /**
+ * Holds if variable `v` is live after the (certain or uncertain) write at
+ * index `i` inside basic block `bb`.
+ */
+ predicate liveAfterWrite(BasicBlock bb, int i, SourceVariable v) {
+ exists(int rnk | rnk = refRank(bb, i, v, Write(_)) | liveAtRank(bb, i, v, rnk))
+ }
+}
+
+private import Liveness
+
+/** Holds if `bb1` strictly dominates `bb2`. */
+private predicate strictlyDominates(BasicBlock bb1, BasicBlock bb2) {
+ bb1 = getImmediateBasicBlockDominator+(bb2)
+}
+
+/** Holds if `bb1` dominates a predecessor of `bb2`. */
+private predicate dominatesPredecessor(BasicBlock bb1, BasicBlock bb2) {
+ exists(BasicBlock pred | pred = getABasicBlockPredecessor(bb2) |
+ bb1 = pred
+ or
+ strictlyDominates(bb1, pred)
+ )
+}
+
+/** Holds if `df` is in the dominance frontier of `bb`. */
+private predicate inDominanceFrontier(BasicBlock bb, BasicBlock df) {
+ dominatesPredecessor(bb, df) and
+ not strictlyDominates(bb, df)
+}
+
+/**
+ * Holds if `bb` is in the dominance frontier of a block containing a
+ * definition of `v`.
+ */
+pragma[noinline]
+private predicate inDefDominanceFrontier(BasicBlock bb, SourceVariable v) {
+ exists(BasicBlock defbb, Definition def |
+ def.definesAt(v, defbb, _) and
+ inDominanceFrontier(defbb, bb)
+ )
+}
+
+cached
+newtype TDefinition =
+ TWriteDef(SourceVariable v, BasicBlock bb, int i) {
+ variableWrite(bb, i, v, _) and
+ liveAfterWrite(bb, i, v)
+ } or
+ TPhiNode(SourceVariable v, BasicBlock bb) {
+ inDefDominanceFrontier(bb, v) and
+ liveAtEntry(bb, v)
+ }
+
+private module SsaDefReaches {
+ newtype TSsaRefKind =
+ SsaRead() or
+ SsaDef()
+
+ /**
+ * A classification of SSA variable references into reads and definitions.
+ */
+ class SsaRefKind extends TSsaRefKind {
+ string toString() {
+ this = SsaRead() and
+ result = "SsaRead"
+ or
+ this = SsaDef() and
+ result = "SsaDef"
+ }
+
+ int getOrder() {
+ this = SsaRead() and
+ result = 0
+ or
+ this = SsaDef() and
+ result = 1
+ }
+ }
+
+ /**
+ * Holds if the `i`th node of basic block `bb` is a reference to `v`,
+ * either a read (when `k` is `SsaRead()`) or an SSA definition (when `k`
+ * is `SsaDef()`).
+ *
+ * Unlike `Liveness::ref`, this includes `phi` nodes.
+ */
+ predicate ssaRef(BasicBlock bb, int i, SourceVariable v, SsaRefKind k) {
+ variableRead(bb, i, v, _) and
+ k = SsaRead()
+ or
+ exists(Definition def | def.definesAt(v, bb, i)) and
+ k = SsaDef()
+ }
+
+ private newtype OrderedSsaRefIndex =
+ MkOrderedSsaRefIndex(int i, SsaRefKind k) { ssaRef(_, i, _, k) }
+
+ private OrderedSsaRefIndex ssaRefOrd(BasicBlock bb, int i, SourceVariable v, SsaRefKind k, int ord) {
+ ssaRef(bb, i, v, k) and
+ result = MkOrderedSsaRefIndex(i, k) and
+ ord = k.getOrder()
+ }
+
+ /**
+ * Gets the (1-based) rank of the reference to `v` at the `i`th node of basic
+ * block `bb`, which has the given reference kind `k`.
+ *
+ * For example, if `bb` is a basic block with a phi node for `v` (considered
+ * to be at index -1), reads `v` at node 2, and defines it at node 5, we have:
+ *
+ * ```ql
+ * ssaRefRank(bb, -1, v, SsaDef()) = 1 // phi node
+ * ssaRefRank(bb, 2, v, Read()) = 2 // read at node 2
+ * ssaRefRank(bb, 5, v, SsaDef()) = 3 // definition at node 5
+ * ```
+ *
+ * Reads are considered before writes when they happen at the same index.
+ */
+ int ssaRefRank(BasicBlock bb, int i, SourceVariable v, SsaRefKind k) {
+ ssaRefOrd(bb, i, v, k, _) =
+ rank[result](int j, int ord, OrderedSsaRefIndex res |
+ res = ssaRefOrd(bb, j, v, _, ord)
+ |
+ res order by j, ord
+ )
+ }
+
+ int maxSsaRefRank(BasicBlock bb, SourceVariable v) {
+ result = ssaRefRank(bb, _, v, _) and
+ not result + 1 = ssaRefRank(bb, _, v, _)
+ }
+
+ /**
+ * Holds if the SSA definition `def` reaches rank index `rnk` in its own
+ * basic block `bb`.
+ */
+ predicate ssaDefReachesRank(BasicBlock bb, Definition def, int rnk, SourceVariable v) {
+ exists(int i |
+ rnk = ssaRefRank(bb, i, v, SsaDef()) and
+ def.definesAt(v, bb, i)
+ )
+ or
+ ssaDefReachesRank(bb, def, rnk - 1, v) and
+ rnk = ssaRefRank(bb, _, v, SsaRead())
+ }
+
+ /**
+ * Holds if the SSA definition of `v` at `def` reaches index `i` in the same
+ * basic block `bb`, without crossing another SSA definition of `v`.
+ */
+ predicate ssaDefReachesReadWithinBlock(SourceVariable v, Definition def, BasicBlock bb, int i) {
+ exists(int rnk |
+ ssaDefReachesRank(bb, def, rnk, v) and
+ rnk = ssaRefRank(bb, i, v, SsaRead())
+ )
+ }
+
+ /**
+ * Holds if the SSA definition of `v` at `def` reaches uncertain SSA definition
+ * `redef` in the same basic block, without crossing another SSA definition of `v`.
+ */
+ predicate ssaDefReachesUncertainDefWithinBlock(
+ SourceVariable v, Definition def, UncertainWriteDefinition redef
+ ) {
+ exists(BasicBlock bb, int rnk, int i |
+ ssaDefReachesRank(bb, def, rnk, v) and
+ rnk = ssaRefRank(bb, i, v, SsaDef()) - 1 and
+ redef.definesAt(v, bb, i)
+ )
+ }
+
+ /**
+ * Same as `ssaRefRank()`, but restricted to a particular SSA definition `def`.
+ */
+ int ssaDefRank(Definition def, SourceVariable v, BasicBlock bb, int i, SsaRefKind k) {
+ v = def.getSourceVariable() and
+ result = ssaRefRank(bb, i, v, k) and
+ (
+ ssaDefReachesRead(_, def, bb, i)
+ or
+ def.definesAt(_, bb, i)
+ )
+ }
+
+ /**
+ * Holds if the reference to `def` at index `i` in basic block `bb` is the
+ * last reference to `v` inside `bb`.
+ */
+ pragma[noinline]
+ predicate lastSsaRef(Definition def, SourceVariable v, BasicBlock bb, int i) {
+ ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v)
+ }
+
+ predicate defOccursInBlock(Definition def, BasicBlock bb, SourceVariable v) {
+ exists(ssaDefRank(def, v, bb, _, _))
+ }
+
+ pragma[noinline]
+ private predicate ssaDefReachesThroughBlock(Definition def, BasicBlock bb) {
+ ssaDefReachesEndOfBlock(bb, def, _) and
+ not defOccursInBlock(_, bb, def.getSourceVariable())
+ }
+
+ /**
+ * Holds if `def` is accessed in basic block `bb1` (either a read or a write),
+ * `bb2` is a transitive successor of `bb1`, `def` is live at the end of `bb1`,
+ * and the underlying variable for `def` is neither read nor written in any block
+ * on the path between `bb1` and `bb2`.
+ */
+ predicate varBlockReaches(Definition def, BasicBlock bb1, BasicBlock bb2) {
+ defOccursInBlock(def, bb1, _) and
+ bb2 = getABasicBlockSuccessor(bb1)
+ or
+ exists(BasicBlock mid |
+ varBlockReaches(def, bb1, mid) and
+ ssaDefReachesThroughBlock(def, mid) and
+ bb2 = getABasicBlockSuccessor(mid)
+ )
+ }
+
+ /**
+ * Holds if `def` is accessed in basic block `bb1` (either a read or a write),
+ * `def` is read at index `i2` in basic block `bb2`, `bb2` is in a transitive
+ * successor block of `bb1`, and `def` is neither read nor written in any block
+ * on a path between `bb1` and `bb2`.
+ */
+ predicate defAdjacentRead(Definition def, BasicBlock bb1, BasicBlock bb2, int i2) {
+ varBlockReaches(def, bb1, bb2) and
+ ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1
+ }
+}
+
+private import SsaDefReaches
+
+pragma[nomagic]
+predicate liveThrough(BasicBlock bb, SourceVariable v) {
+ liveAtExit(bb, v) and
+ not ssaRef(bb, _, v, SsaDef())
+}
+
+/**
+ * NB: If this predicate is exposed, it should be cached.
+ *
+ * Holds if the SSA definition of `v` at `def` reaches the end of basic
+ * block `bb`, at which point it is still live, without crossing another
+ * SSA definition of `v`.
+ */
+pragma[nomagic]
+predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable v) {
+ exists(int last | last = maxSsaRefRank(bb, v) |
+ ssaDefReachesRank(bb, def, last, v) and
+ liveAtExit(bb, v)
+ )
+ or
+ // The construction of SSA form ensures that each read of a variable is
+ // dominated by its definition. An SSA definition therefore reaches a
+ // control flow node if it is the _closest_ SSA 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.
+ ssaDefReachesEndOfBlock(getImmediateBasicBlockDominator(bb), def, pragma[only_bind_into](v)) and
+ liveThrough(bb, pragma[only_bind_into](v))
+}
+
+/**
+ * NB: If this predicate is exposed, it should be cached.
+ *
+ * Holds if `inp` is an input to the phi node `phi` along the edge originating in `bb`.
+ */
+pragma[nomagic]
+predicate phiHasInputFromBlock(PhiNode phi, Definition inp, BasicBlock bb) {
+ exists(SourceVariable v, BasicBlock bbDef |
+ phi.definesAt(v, bbDef, _) and
+ getABasicBlockPredecessor(bbDef) = bb and
+ ssaDefReachesEndOfBlock(bb, inp, v)
+ )
+}
+
+/**
+ * NB: If this predicate is exposed, it should be cached.
+ *
+ * Holds if the SSA definition of `v` at `def` reaches a read at index `i` in
+ * basic block `bb`, without crossing another SSA definition of `v`. The read
+ * is of kind `rk`.
+ */
+pragma[nomagic]
+predicate ssaDefReachesRead(SourceVariable v, Definition def, BasicBlock bb, int i) {
+ ssaDefReachesReadWithinBlock(v, def, bb, i)
+ or
+ variableRead(bb, i, v, _) and
+ ssaDefReachesEndOfBlock(getABasicBlockPredecessor(bb), def, v) and
+ not ssaDefReachesReadWithinBlock(v, _, bb, i)
+}
+
+/**
+ * NB: If this predicate is exposed, it should be cached.
+ *
+ * Holds if `def` is accessed at index `i1` in basic block `bb1` (either a read
+ * or a write), `def` is read at index `i2` in basic block `bb2`, and there is a
+ * path between them without any read of `def`.
+ */
+pragma[nomagic]
+predicate adjacentDefRead(Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2) {
+ exists(int rnk |
+ rnk = ssaDefRank(def, _, bb1, i1, _) and
+ rnk + 1 = ssaDefRank(def, _, bb1, i2, SsaRead()) and
+ variableRead(bb1, i2, _, _) and
+ bb2 = bb1
+ )
+ or
+ lastSsaRef(def, _, bb1, i1) and
+ defAdjacentRead(def, bb1, bb2, i2)
+}
+
+pragma[noinline]
+private predicate adjacentDefRead(
+ Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2, SourceVariable v
+) {
+ adjacentDefRead(def, bb1, i1, bb2, i2) and
+ v = def.getSourceVariable()
+}
+
+private predicate adjacentDefReachesRead(
+ Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2
+) {
+ exists(SourceVariable v | adjacentDefRead(def, bb1, i1, bb2, i2, v) |
+ ssaRef(bb1, i1, v, SsaDef())
+ or
+ variableRead(bb1, i1, v, true)
+ )
+ or
+ exists(BasicBlock bb3, int i3 |
+ adjacentDefReachesRead(def, bb1, i1, bb3, i3) and
+ variableRead(bb3, i3, _, false) and
+ adjacentDefRead(def, bb3, i3, bb2, i2)
+ )
+}
+
+/**
+ * NB: If this predicate is exposed, it should be cached.
+ *
+ * Same as `adjacentDefRead`, but ignores uncertain reads.
+ */
+pragma[nomagic]
+predicate adjacentDefNoUncertainReads(Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2) {
+ adjacentDefReachesRead(def, bb1, i1, bb2, i2) and
+ variableRead(bb2, i2, _, true)
+}
+
+/**
+ * NB: If this predicate is exposed, it should be cached.
+ *
+ * Holds if the node at index `i` in `bb` is a last reference to SSA definition
+ * `def`. The reference is last because it can reach another write `next`,
+ * without passing through another read or write.
+ */
+pragma[nomagic]
+predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) {
+ exists(SourceVariable v |
+ // Next reference to `v` inside `bb` is a write
+ exists(int rnk, int j |
+ rnk = ssaDefRank(def, v, bb, i, _) and
+ next.definesAt(v, bb, j) and
+ rnk + 1 = ssaRefRank(bb, j, v, SsaDef())
+ )
+ or
+ // Can reach a write using one or more steps
+ lastSsaRef(def, v, bb, i) and
+ exists(BasicBlock bb2 |
+ varBlockReaches(def, bb, bb2) and
+ 1 = ssaDefRank(next, v, bb2, _, SsaDef())
+ )
+ )
+}
+
+/**
+ * NB: If this predicate is exposed, it should be cached.
+ *
+ * Holds if `inp` is an immediately preceding definition of uncertain definition
+ * `def`. Since `def` is uncertain, the value from the preceding definition might
+ * still be valid.
+ */
+pragma[nomagic]
+predicate uncertainWriteDefinitionInput(UncertainWriteDefinition def, Definition inp) {
+ lastRefRedef(inp, _, _, def)
+}
+
+private predicate adjacentDefReachesUncertainRead(
+ Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2
+) {
+ adjacentDefReachesRead(def, bb1, i1, bb2, i2) and
+ variableRead(bb2, i2, _, false)
+}
+
+/**
+ * NB: If this predicate is exposed, it should be cached.
+ *
+ * Same as `lastRefRedef`, but ignores uncertain reads.
+ */
+pragma[nomagic]
+predicate lastRefRedefNoUncertainReads(Definition def, BasicBlock bb, int i, Definition next) {
+ lastRefRedef(def, bb, i, next) and
+ not variableRead(bb, i, def.getSourceVariable(), false)
+ or
+ exists(BasicBlock bb0, int i0 |
+ lastRefRedef(def, bb0, i0, next) and
+ adjacentDefReachesUncertainRead(def, bb, i, bb0, i0)
+ )
+}
+
+/**
+ * NB: If this predicate is exposed, it should be cached.
+ *
+ * Holds if the node at index `i` in `bb` is a last reference to SSA
+ * definition `def`.
+ *
+ * That is, the node can reach the end of the enclosing callable, or another
+ * SSA definition for the underlying source variable, without passing through
+ * another read.
+ */
+pragma[nomagic]
+predicate lastRef(Definition def, BasicBlock bb, int i) {
+ lastRefRedef(def, bb, i, _)
+ or
+ lastSsaRef(def, _, bb, i) and
+ (
+ // Can reach exit directly
+ bb instanceof ExitBasicBlock
+ or
+ // Can reach a block using one or more steps, where `def` is no longer live
+ exists(BasicBlock bb2 | varBlockReaches(def, bb, bb2) |
+ not defOccursInBlock(def, bb2, _) and
+ not ssaDefReachesEndOfBlock(bb2, def, _)
+ )
+ )
+}
+
+/**
+ * NB: If this predicate is exposed, it should be cached.
+ *
+ * Same as `lastRefRedef`, but ignores uncertain reads.
+ */
+pragma[nomagic]
+predicate lastRefNoUncertainReads(Definition def, BasicBlock bb, int i) {
+ lastRef(def, bb, i) and
+ not variableRead(bb, i, def.getSourceVariable(), false)
+ or
+ exists(BasicBlock bb0, int i0 |
+ lastRef(def, bb0, i0) and
+ adjacentDefReachesUncertainRead(def, bb, i, bb0, i0)
+ )
+}
+
+/** A static single assignment (SSA) definition. */
+class Definition extends TDefinition {
+ /** Gets the source variable underlying this SSA definition. */
+ SourceVariable getSourceVariable() { this.definesAt(result, _, _) }
+
+ /**
+ * Holds if this SSA definition defines `v` at index `i` in basic block `bb`.
+ * Phi nodes are considered to be at index `-1`, while normal variable writes
+ * are at the index of the control flow node they wrap.
+ */
+ final predicate definesAt(SourceVariable v, BasicBlock bb, int i) {
+ this = TWriteDef(v, bb, i)
+ or
+ this = TPhiNode(v, bb) and i = -1
+ }
+
+ /** Gets the basic block to which this SSA definition belongs. */
+ final BasicBlock getBasicBlock() { this.definesAt(_, result, _) }
+
+ /** Gets a textual representation of this SSA definition. */
+ string toString() { none() }
+}
+
+/** An SSA definition that corresponds to a write. */
+class WriteDefinition extends Definition, TWriteDef {
+ private SourceVariable v;
+ private BasicBlock bb;
+ private int i;
+
+ WriteDefinition() { this = TWriteDef(v, bb, i) }
+
+ override string toString() { result = "WriteDef" }
+}
+
+/** A phi node. */
+class PhiNode extends Definition, TPhiNode {
+ override string toString() { result = "Phi" }
+}
+
+/**
+ * An SSA definition that represents an uncertain update of the underlying
+ * source variable.
+ */
+class UncertainWriteDefinition extends WriteDefinition {
+ UncertainWriteDefinition() {
+ exists(SourceVariable v, BasicBlock bb, int i |
+ this.definesAt(v, bb, i) and
+ variableWrite(bb, i, v, false)
+ )
+ }
+}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplSpecific.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplSpecific.qll
new file mode 100644
index 00000000000..196d32205ad
--- /dev/null
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplSpecific.qll
@@ -0,0 +1,64 @@
+private import semmle.code.cpp.ir.IR
+private import DataFlowUtil
+private import DataFlowPrivate
+private import DataFlowImplCommon as DataFlowImplCommon
+private import Ssa as Ssa
+
+class BasicBlock = IRBlock;
+
+BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.immediatelyDominates(bb) }
+
+BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
+
+class ExitBasicBlock extends IRBlock {
+ ExitBasicBlock() { this.getLastInstruction() instanceof ExitFunctionInstruction }
+}
+
+private newtype TSourceVariable =
+ TSourceIRVariable(IRVariable var) or
+ TSourceIRVariableIndirection(InitializeIndirectionInstruction init)
+
+abstract class SourceVariable extends TSourceVariable {
+ IRVariable var;
+
+ IRVariable getVariable() { result = var }
+
+ abstract string toString();
+
+ predicate isIndirection() { none() }
+}
+
+class SourceIRVariable extends SourceVariable, TSourceIRVariable {
+ SourceIRVariable() { this = TSourceIRVariable(var) }
+
+ override string toString() { result = this.getVariable().toString() }
+}
+
+class SourceIRVariableIndirection extends SourceVariable, TSourceIRVariableIndirection {
+ InitializeIndirectionInstruction init;
+
+ SourceIRVariableIndirection() {
+ this = TSourceIRVariableIndirection(init) and var = init.getIRVariable()
+ }
+
+ override string toString() { result = "*" + this.getVariable().toString() }
+
+ override predicate isIndirection() { any() }
+}
+
+predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
+ DataFlowImplCommon::forceCachingInSameStage() and
+ exists(Ssa::Def def |
+ def.hasRankInBlock(bb, i) and
+ v = def.getVariable() and
+ (if def.isCertain() then certain = true else certain = false)
+ )
+}
+
+predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
+ exists(Ssa::Use use |
+ use.hasRankInBlock(bb, i) and
+ v = use.getVariable() and
+ certain = true
+ )
+}
From 436152a46da23d5bbecad45396c4ba7aa819042a Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 28 Oct 2021 12:37:07 +0200
Subject: [PATCH 189/471] Python: Refactor flask file sending tests
---
.../library-tests/frameworks/flask/file_sending.py | 7 +++++++
.../frameworks/flask/save_uploaded_file.py | 13 +------------
2 files changed, 8 insertions(+), 12 deletions(-)
create mode 100644 python/ql/test/library-tests/frameworks/flask/file_sending.py
diff --git a/python/ql/test/library-tests/frameworks/flask/file_sending.py b/python/ql/test/library-tests/frameworks/flask/file_sending.py
new file mode 100644
index 00000000000..54cece7071b
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/flask/file_sending.py
@@ -0,0 +1,7 @@
+from flask import send_from_directory, send_file
+
+send_from_directory("filepath", "file") # $ getAPathArgument="filepath" getAPathArgument="file"
+send_from_directory(directory="filepath", filename="file") # $ getAPathArgument="filepath" getAPathArgument="file"
+
+send_file("file") # $ getAPathArgument="file"
+send_file(filename_or_fp="file") # $ getAPathArgument="file"
diff --git a/python/ql/test/library-tests/frameworks/flask/save_uploaded_file.py b/python/ql/test/library-tests/frameworks/flask/save_uploaded_file.py
index b61de94a365..8aba97d1876 100644
--- a/python/ql/test/library-tests/frameworks/flask/save_uploaded_file.py
+++ b/python/ql/test/library-tests/frameworks/flask/save_uploaded_file.py
@@ -1,17 +1,6 @@
-from flask import Flask, request, send_from_directory, send_file
+from flask import Flask, request
app = Flask(__name__)
@app.route("/save-uploaded-file") # $routeSetup="/save-uploaded-file"
def test_taint(): # $requestHandler
request.files['key'].save("path") # $ getAPathArgument="path"
-
-
-@app.route("/path-injection") # $routeSetup="/path-injection"
-def test_path(): # $requestHandler
-
- send_from_directory("filepath","file") # $ getAPathArgument="filepath" getAPathArgument="file"
- send_file("file") # $ getAPathArgument="file"
-
- send_from_directory(directory="filepath","file") # $ getAPathArgument="filepath" getAPathArgument="file"
- send_from_directory(filename="filepath","file") # $ getAPathArgument="filepath" getAPathArgument="file"
- send_file(filename_or_fp="file") # $ getAPathArgument="file"
From 205989688268d2febde03878ed1973172ccffaa3 Mon Sep 17 00:00:00 2001
From: Nick Rolfe
Date: Thu, 28 Oct 2021 12:04:48 +0100
Subject: [PATCH 190/471] Ruby: clean up docs
---
CONTRIBUTING.md | 3 ++-
ruby/CONTRIBUTING.md | 64 --------------------------------------------
ruby/README.md | 52 ++++++-----------------------------
3 files changed, 10 insertions(+), 109 deletions(-)
delete mode 100644 ruby/CONTRIBUTING.md
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8b885094185..7e465e09e2a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -11,13 +11,14 @@ If you have an idea for a query that you would like to share with other CodeQL u
1. **Directory structure**
- There are five language-specific query directories in this repository:
+ There are six language-specific query directories in this repository:
* C/C++: `cpp/ql/src`
* C#: `csharp/ql/src`
* Java: `java/ql/src`
* JavaScript: `javascript/ql/src`
* Python: `python/ql/src`
+ * Ruby: `ruby/ql/src`
Each language-specific directory contains further subdirectories that group queries based on their `@tags` or purpose.
- Experimental queries and libraries are stored in the `experimental` subdirectory within each language-specific directory in the [CodeQL repository](https://github.com/github/codeql). For example, experimental Java queries and libraries are stored in `java/ql/src/experimental` and any corresponding tests in `java/ql/test/experimental`.
diff --git a/ruby/CONTRIBUTING.md b/ruby/CONTRIBUTING.md
deleted file mode 100644
index 792385e766e..00000000000
--- a/ruby/CONTRIBUTING.md
+++ /dev/null
@@ -1,64 +0,0 @@
-## Contributing
-
-Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great.
-
-Contributions to this project are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [project's open source license](LICENSE).
-
-Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
-
-## Building and testing
-
-See [Developer information](docs/HOWTO.md) for information on building the Ruby extractor. There is no need to rebuild the extractor if you are only developing queries.
-
-1. Install the CodeQL CLI as described in [Getting started with the CodeQL CLI](https://codeql.github.com/docs/codeql-cli/getting-started-with-the-codeql-cli/).
-
-2. Ensure that `/codeql` is in your `PATH`.
-
-3. Clone this repository into `/codeql-ruby` and change to this directory.
-
-4. To run all tests in a directory and its subdirectories, run `codeql test run `, for example `codeql test run ql/test/query-tests/security`.
-
-6. To run an individual test, run `codeql test run `, where `` is a `.ql` or `.qlref` file, for example `codeql test run ql/test/query-tests/security/cwe-078/CommandInjection.qlref`.
-
-## Adding a new query
-
-If you have an idea for a query that you would like to share with other CodeQL users, please open a pull request to add it to this repository.
-Follow the steps below to help other users understand what your query does, and to ensure that your query is consistent with the other CodeQL queries.
-
-1. **Consult the documentation for query writers**
-
- There is lots of useful documentation to help you write CodeQL queries, ranging from information about query file structure to language-specific tutorials. For more information on the documentation available, see [Writing CodeQL queries](https://codeql.github.com/docs/writing-codeql-queries/) and the [CodeQL documentation](https://codeql.github.com/docs).
-
-2. **Format your code correctly**
-
- All of the standard CodeQL queries and libraries are uniformly formatted for clarity and consistency, so we strongly recommend that all contributions follow the same formatting guidelines. If you use the CodeQL extension for Visual Studio Code, you can auto-format your query using the [Format Document command](https://code.visualstudio.com/docs/editor/codebasics#_formatting). For more information, see the [QL style guide](https://github.com/github/codeql/blob/main/docs/ql-style-guide.md).
-
-3. **Make sure your query has the correct metadata**
-
- Query metadata is used to identify your query and make sure the query results are displayed properly.
- The most important metadata to include are the `@name`, `@description`, and the `@kind`.
- Other metadata properties (`@precision`, `@severity`, and `@tags`) are usually added after the query has been reviewed by the maintainers.
- For more information on writing query metadata, see the [Query metadata style guide](https://github.com/github/codeql/blob/main/docs/query-metadata-style-guide.md).
-
-4. **Make sure the `select` statement is compatible with the query type**
-
- The `select` statement of your query must be compatible with the query type (determined by the `@kind` metadata property) for alert or path results to be displayed correctly in LGTM and Visual Studio Code.
- For more information on `select` statement format, see [About CodeQL queries](https://codeql.github.com/docs/writing-codeql-queries/about-codeql-queries/#select-clause) on the [CodeQL documentation](https://codeql.github.com/docs) site.
-
-5. **Write a query help file**
-
- Query help files explain the purpose of your query to other users. Write your query help in a `.qhelp` file and save it in the same directory as your new query.
- For more information on writing query help, see the [Query help style guide](https://github.com/github/codeql/blob/main/docs/query-help-style-guide.md).
-
-6. **Maintain backwards compatibility**
-
-The standard CodeQL libraries must evolve in a backwards compatible manner. If any backwards incompatible changes need to be made, the existing API must first be marked as deprecated. This is done by adding a `deprecated` annotation along with a QLDoc reference to the replacement API. Only after at least one full release cycle has elapsed may the old API be removed.
-
-In addition to contributions to our standard queries and libraries, we also welcome contributions of a more experimental nature, which do not need to fulfill all the requirements listed above. See the guidelines for [experimental queries and libraries](ql/docs/experimental.md) for details.
-
-## Resources
-
-- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
-- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
-- [GitHub Help](https://help.github.com)
-- [A Note About Git Commit Messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
diff --git a/ruby/README.md b/ruby/README.md
index 8ce739089a4..312a339b5b2 100644
--- a/ruby/README.md
+++ b/ruby/README.md
@@ -1,50 +1,14 @@
# Ruby analysis support for CodeQL
-This open-source repository contains the extractor, CodeQL libraries, and queries that power Ruby
+This directory contains the extractor, CodeQL libraries, and queries that power Ruby
support in [LGTM](https://lgtm.com) and the other CodeQL products that [GitHub](https://github.com)
makes available to its customers worldwide.
It contains two major components:
- - an extractor, written in Rust, that parses Ruby source code and converts it into a database
- that can be queried using CodeQL.
- - static analysis libraries and queries written in [CodeQL](https://codeql.github.com/docs/) that can be
- used to analyze such a database to find coding mistakes or security vulnerabilities.
-
-The goal of this project is to provide comprehensive static analysis support for Ruby in CodeQL.
-
-For the queries and libraries that power CodeQL support for other languages, visit [the CodeQL
-repository](https://github.com/github/codeql).
-
-## Installation
-
-Simply clone this repository. There are no external dependencies.
-
-If you want to use the CodeQL extension for Visual Studio Code, import this repository into your VS
-Code workspace.
-
-## Usage
-
-To analyze a Ruby codebase, either use the [CodeQL command-line
-interface](https://codeql.github.com/docs/codeql-cli/) to create a database yourself, or
-download a pre-built database from [LGTM.com](https://lgtm.com/). You can then run any of the
-queries contained in this repository either on the command line or using the VS Code extension.
-
-Note that the [lgtm.com](https://github.com/github/codeql-ruby/tree/lgtm.com) branch of this
-repository corresponds to the version of the queries that is currently deployed on LGTM.com.
-The [main](https://github.com/github/codeql-ruby/tree/main) branch may contain changes that
-have not been deployed yet, so you may need to upgrade databases downloaded from [LGTM.com](https://lgtm.com) before
-running queries on them.
-
-## Contributions
-
-Contributions are welcome! Please see our [contribution guidelines](CONTRIBUTING.md) and our
-[code of conduct](CODE_OF_CONDUCT.md) for details on how to participate in our community.
-
-## Licensing
-
-The code in this repository is licensed under the [MIT license](LICENSE).
-
-## Resources
-
-- [Writing CodeQL queries](https://codeql.github.com/docs/writing-codeql-queries/)
-- [CodeQL documentation](https://codeql.github.com/docs/)
+ 1. static analysis libraries and queries written in
+ [CodeQL](https://codeql.github.com/docs/) that can be used to analyze such
+ a database to find coding mistakes or security vulnerabilities.
+ 2. an extractor, written in Rust, that parses Ruby source code and converts it
+ into a database that can be queried using CodeQL. See [Developer
+ information](doc/HOWTO.md) for information on building the extractor (you
+ do not need to do this if you are only developing queries).
\ No newline at end of file
From b3ba75a00f30ec0012befa2a69897e8fa2deaac6 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Thu, 28 Oct 2021 13:22:59 +0200
Subject: [PATCH 191/471] Python: Fix tests by managing local sources
`API::Node::getAwaited` is restriced to local sources
---
.../dataflow/new/internal/DataFlowPrivate.qll | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll
index 933b82b56e1..5e9fded4932 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll
@@ -175,6 +175,19 @@ module EssaFlow {
contextManager.strictlyDominates(var)
)
or
+ // Async with var definition
+ // `async with f(42) as x:`
+ // nodeFrom is `x`, cfg node
+ // nodeTo is `x`, essa var
+ //
+ // This makes the cfg node the local source of the awaited value.
+ exists(With with, ControlFlowNode var |
+ nodeFrom.(CfgNode).getNode() = var and
+ nodeTo.(EssaNode).getVar().getDefinition().(WithDefinition).getDefiningNode() = var and
+ with.getOptionalVars() = var.getNode() and
+ with.isAsync()
+ )
+ or
// Parameter definition
// `def foo(x):`
// nodeFrom is `x`, cfgNode
From 6648a695eb29e0c06095811cad41e351a0ed8991 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 28 Oct 2021 13:32:51 +0200
Subject: [PATCH 192/471] Python: Add flask specific path-injection test
---
.../PathInjection.expected | 21 +++++++++++++++++++
.../flask_path_injection.py | 21 +++++++++++++++++++
2 files changed, 42 insertions(+)
create mode 100644 python/ql/test/query-tests/Security/CWE-022-PathInjection/flask_path_injection.py
diff --git a/python/ql/test/query-tests/Security/CWE-022-PathInjection/PathInjection.expected b/python/ql/test/query-tests/Security/CWE-022-PathInjection/PathInjection.expected
index 446e80f33f1..9f5f0200400 100644
--- a/python/ql/test/query-tests/Security/CWE-022-PathInjection/PathInjection.expected
+++ b/python/ql/test/query-tests/Security/CWE-022-PathInjection/PathInjection.expected
@@ -1,4 +1,12 @@
edges
+| flask_path_injection.py:11:16:11:22 | ControlFlowNode for request | flask_path_injection.py:11:16:11:27 | ControlFlowNode for Attribute |
+| flask_path_injection.py:11:16:11:27 | ControlFlowNode for Attribute | flask_path_injection.py:13:44:13:51 | ControlFlowNode for filename |
+| flask_path_injection.py:19:15:19:21 | ControlFlowNode for request | flask_path_injection.py:19:15:19:26 | ControlFlowNode for Attribute |
+| flask_path_injection.py:19:15:19:21 | ControlFlowNode for request | flask_path_injection.py:20:16:20:22 | ControlFlowNode for request |
+| flask_path_injection.py:19:15:19:21 | ControlFlowNode for request | flask_path_injection.py:20:16:20:27 | ControlFlowNode for Attribute |
+| flask_path_injection.py:19:15:19:26 | ControlFlowNode for Attribute | flask_path_injection.py:21:32:21:38 | ControlFlowNode for dirname |
+| flask_path_injection.py:20:16:20:22 | ControlFlowNode for request | flask_path_injection.py:20:16:20:27 | ControlFlowNode for Attribute |
+| flask_path_injection.py:20:16:20:27 | ControlFlowNode for Attribute | flask_path_injection.py:21:41:21:48 | ControlFlowNode for filename |
| path_injection.py:12:16:12:22 | ControlFlowNode for request | path_injection.py:12:16:12:27 | ControlFlowNode for Attribute |
| path_injection.py:12:16:12:27 | ControlFlowNode for Attribute | path_injection.py:13:14:13:47 | ControlFlowNode for Attribute() |
| path_injection.py:19:16:19:22 | ControlFlowNode for request | path_injection.py:19:16:19:27 | ControlFlowNode for Attribute |
@@ -68,6 +76,15 @@ edges
| test_chaining.py:41:9:41:16 | ControlFlowNode for source() | test_chaining.py:42:9:42:19 | ControlFlowNode for normpath() |
| test_chaining.py:44:13:44:23 | ControlFlowNode for normpath() | test_chaining.py:45:14:45:14 | ControlFlowNode for z |
nodes
+| flask_path_injection.py:11:16:11:22 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| flask_path_injection.py:11:16:11:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| flask_path_injection.py:13:44:13:51 | ControlFlowNode for filename | semmle.label | ControlFlowNode for filename |
+| flask_path_injection.py:19:15:19:21 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| flask_path_injection.py:19:15:19:26 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| flask_path_injection.py:20:16:20:22 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| flask_path_injection.py:20:16:20:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| flask_path_injection.py:21:32:21:38 | ControlFlowNode for dirname | semmle.label | ControlFlowNode for dirname |
+| flask_path_injection.py:21:41:21:48 | ControlFlowNode for filename | semmle.label | ControlFlowNode for filename |
| path_injection.py:12:16:12:22 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| path_injection.py:12:16:12:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| path_injection.py:13:14:13:47 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
@@ -153,6 +170,10 @@ nodes
| test_chaining.py:44:13:44:23 | ControlFlowNode for normpath() | semmle.label | ControlFlowNode for normpath() |
| test_chaining.py:45:14:45:14 | ControlFlowNode for z | semmle.label | ControlFlowNode for z |
#select
+| flask_path_injection.py:13:44:13:51 | ControlFlowNode for filename | flask_path_injection.py:11:16:11:22 | ControlFlowNode for request | flask_path_injection.py:13:44:13:51 | ControlFlowNode for filename | This path depends on $@. | flask_path_injection.py:11:16:11:22 | ControlFlowNode for request | a user-provided value |
+| flask_path_injection.py:21:32:21:38 | ControlFlowNode for dirname | flask_path_injection.py:19:15:19:21 | ControlFlowNode for request | flask_path_injection.py:21:32:21:38 | ControlFlowNode for dirname | This path depends on $@. | flask_path_injection.py:19:15:19:21 | ControlFlowNode for request | a user-provided value |
+| flask_path_injection.py:21:41:21:48 | ControlFlowNode for filename | flask_path_injection.py:19:15:19:21 | ControlFlowNode for request | flask_path_injection.py:21:41:21:48 | ControlFlowNode for filename | This path depends on $@. | flask_path_injection.py:19:15:19:21 | ControlFlowNode for request | a user-provided value |
+| flask_path_injection.py:21:41:21:48 | ControlFlowNode for filename | flask_path_injection.py:20:16:20:22 | ControlFlowNode for request | flask_path_injection.py:21:41:21:48 | ControlFlowNode for filename | This path depends on $@. | flask_path_injection.py:20:16:20:22 | ControlFlowNode for request | a user-provided value |
| path_injection.py:13:14:13:47 | ControlFlowNode for Attribute() | path_injection.py:12:16:12:22 | ControlFlowNode for request | path_injection.py:13:14:13:47 | ControlFlowNode for Attribute() | This path depends on $@. | path_injection.py:12:16:12:22 | ControlFlowNode for request | a user-provided value |
| path_injection.py:21:14:21:18 | ControlFlowNode for npath | path_injection.py:19:16:19:22 | ControlFlowNode for request | path_injection.py:21:14:21:18 | ControlFlowNode for npath | This path depends on $@. | path_injection.py:19:16:19:22 | ControlFlowNode for request | a user-provided value |
| path_injection.py:31:14:31:18 | ControlFlowNode for npath | path_injection.py:27:16:27:22 | ControlFlowNode for request | path_injection.py:31:14:31:18 | ControlFlowNode for npath | This path depends on $@. | path_injection.py:27:16:27:22 | ControlFlowNode for request | a user-provided value |
diff --git a/python/ql/test/query-tests/Security/CWE-022-PathInjection/flask_path_injection.py b/python/ql/test/query-tests/Security/CWE-022-PathInjection/flask_path_injection.py
new file mode 100644
index 00000000000..ee531fead46
--- /dev/null
+++ b/python/ql/test/query-tests/Security/CWE-022-PathInjection/flask_path_injection.py
@@ -0,0 +1,21 @@
+from flask import Flask, request, send_from_directory
+app = Flask(__name__)
+
+
+STATIC_DIR = "/server/static/"
+
+
+# see https://flask.palletsprojects.com/en/1.1.x/api/#flask.send_from_directory
+@app.route("/provide-filename")
+def download_file():
+ filename = request.args.get('filename', '')
+ # ok since `send_from_directory` ensure this stays within `STATIC_DIR`
+ return send_from_directory(STATIC_DIR, filename) # OK
+
+
+# see https://flask.palletsprojects.com/en/1.1.x/api/#flask.send_from_directory
+@app.route("/also-provide-dirname")
+def download_file():
+ dirname = request.args.get('dirname', '')
+ filename = request.args.get('filename', '')
+ return send_from_directory(dirname, filename) # NOT OK
From 5ebefe2d3000a8a1ea1b809107b003e889f82355 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Wed, 20 Oct 2021 15:14:03 +0100
Subject: [PATCH 193/471] C++: Throw away the old way of doing store steps
using memory edges. Instead, we introduce a StoreNode IPA branch that does
store steps and instead use the shared SSA library to transfer flow into
these nodes before a store step, and out of them following a sequence of
store steps.
---
.../ir/dataflow/internal/DataFlowPrivate.qll | 103 +---------
.../cpp/ir/dataflow/internal/DataFlowUtil.qll | 183 +++++++++++++++---
.../code/cpp/ir/dataflow/internal/Ssa.qll | 111 +++++++++++
3 files changed, 268 insertions(+), 129 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
index 73bf72a3643..4d4271941b4 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
@@ -184,108 +184,17 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
*/
predicate jumpStep(Node n1, Node n2) { none() }
-private predicate fieldStoreStepNoChi(Node node1, FieldContent f, PostUpdateNode node2) {
- exists(StoreInstruction store, Class c |
- store = node2.asInstruction() and
- store.getSourceValueOperand() = node1.asOperand() and
- getWrittenField(store, f.(FieldContent).getAField(), c) and
- f.hasOffset(c, _, _)
- )
-}
-
-private FieldAddressInstruction getFieldInstruction(Instruction instr) {
- result = instr or
- result = instr.(CopyValueInstruction).getUnary()
-}
-
-pragma[noinline]
-private predicate getWrittenField(Instruction instr, Field f, Class c) {
- exists(FieldAddressInstruction fa |
- fa =
- getFieldInstruction([
- instr.(StoreInstruction).getDestinationAddress(),
- instr.(WriteSideEffectInstruction).getDestinationAddress()
- ]) and
- f = fa.getField() and
- c = f.getDeclaringType()
- )
-}
-
-private predicate fieldStoreStepChi(Node node1, FieldContent f, PostUpdateNode node2) {
- exists(ChiPartialOperand operand, ChiInstruction chi |
- chi.getPartialOperand() = operand and
- node1.asOperand() = operand and
- node2.asInstruction() = chi and
- exists(Class c |
- c = chi.getResultType() and
- exists(int startBit, int endBit |
- chi.getUpdatedInterval(startBit, endBit) and
- f.hasOffset(c, startBit, endBit)
- )
- or
- getWrittenField(operand.getDef(), f.getAField(), c) and
- f.hasOffset(c, _, _)
- )
- )
-}
-
-private predicate arrayStoreStepChi(Node node1, ArrayContent a, PostUpdateNode node2) {
- exists(a) and
- exists(ChiPartialOperand operand, ChiInstruction chi, StoreInstruction store |
- chi.getPartialOperand() = operand and
- store = operand.getDef() and
- node1.asOperand() = operand and
- // This `ChiInstruction` will always have a non-conflated result because both `ArrayStoreNode`
- // and `PointerStoreNode` require it in their characteristic predicates.
- node2.asInstruction() = chi and
- (
- // `x[i] = taint()`
- // This matches the characteristic predicate in `ArrayStoreNode`.
- store.getDestinationAddress() instanceof PointerAddInstruction
- or
- // `*p = taint()`
- // This matches the characteristic predicate in `PointerStoreNode`.
- store.getDestinationAddress().(CopyValueInstruction).getUnary() instanceof LoadInstruction
- )
- )
-}
-
/**
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
* Thus, `node2` references an object with a field `f` that contains the
* value of `node1`.
*/
-predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
- fieldStoreStepNoChi(node1, f, node2) or
- fieldStoreStepChi(node1, f, node2) or
- arrayStoreStepChi(node1, f, node2) or
- fieldStoreStepAfterArraySuppression(node1, f, node2)
-}
-
-// This predicate pushes the correct `FieldContent` onto the access path when the
-// `suppressArrayRead` predicate has popped off an `ArrayContent`.
-private predicate fieldStoreStepAfterArraySuppression(
- Node node1, FieldContent f, PostUpdateNode node2
-) {
- exists(WriteSideEffectInstruction write, ChiInstruction chi, Class c |
- not chi.isResultConflated() and
- node1.asInstruction() = chi and
- node2.asInstruction() = chi and
- chi.getPartial() = write and
- getWrittenField(write, f.getAField(), c) and
- f.hasOffset(c, _, _)
- )
-}
-
-bindingset[result, i]
-private int unbindInt(int i) { i <= result and i >= result }
-
-pragma[noinline]
-private predicate getLoadedField(LoadInstruction load, Field f, Class c) {
- exists(FieldAddressInstruction fa |
- fa = load.getSourceAddress() and
- f = fa.getField() and
- c = f.getDeclaringType()
+predicate storeStep(StoreNode node1, FieldContent f, StoreNode node2) {
+ exists(FieldAddressInstruction fai |
+ not fai.getObjectAddress().getResultType().stripType() instanceof Union and
+ node1.getInstruction() = fai and
+ node2.getInstruction() = fai.getObjectAddress() and
+ f.getField() = fai.getField()
)
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
index 9e7a95e010d..449e680b850 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
@@ -10,6 +10,8 @@ private import semmle.code.cpp.ir.ValueNumbering
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.controlflow.IRGuards
private import semmle.code.cpp.models.interfaces.DataFlow
+private import DataFlowPrivate
+private import Ssa as Ssa
cached
private module Cached {
@@ -17,12 +19,28 @@ private module Cached {
newtype TIRDataFlowNode =
TInstructionNode(Instruction i) or
TOperandNode(Operand op) or
- TVariableNode(Variable var)
+ TVariableNode(Variable var) or
+ TStoreNodeInstr(Instruction i) { Ssa::explicitWrite(_, _, i) } or
+ TStoreNodeOperand(ArgumentOperand op) { Ssa::explicitWrite(_, _, op.getDef()) }
cached
predicate localFlowStepCached(Node nodeFrom, Node nodeTo) {
simpleLocalFlowStep(nodeFrom, nodeTo)
}
+
+ private predicate needsPostReadNode(Instruction iFrom) {
+ // If the instruction generates an address that flows to a load.
+ Ssa::addressFlowTC(iFrom, Ssa::getSourceAddress(_)) and
+ (
+ // And it is either a field address
+ iFrom instanceof FieldAddressInstruction
+ or
+ // Or it is instruction that either uses or is used for an address that needs a post read node.
+ exists(Instruction mid | needsPostReadNode(mid) |
+ Ssa::addressFlow(mid, iFrom) or Ssa::addressFlow(iFrom, mid)
+ )
+ )
+ }
}
private import Cached
@@ -180,6 +198,99 @@ class OperandNode extends Node, TOperandNode {
override string toString() { result = this.getOperand().toString() }
}
+/**
+ * INTERNAL: do not use.
+ *
+ * A `StoreNode` is a node that has been (or is about to be) the
+ * source or target of a `storeStep`.
+ */
+abstract class StoreNode extends Node {
+ /** Gets the underlying instruction, if any. */
+ Instruction getInstruction() { none() }
+
+ /** Gets the underlying operand, if any. */
+ Operand getOperand() { none() }
+
+ /** Holds if this node should receive flow from `addr`. */
+ abstract predicate flowInto(Instruction addr);
+
+ override Declaration getEnclosingCallable() { result = this.getFunction() }
+
+ /** Holds if this `StoreNode` is the root of the address computation used by a store operation. */
+ predicate isTerminal() {
+ not exists(this.getAPredecessor()) and
+ not storeStep(this, _, _)
+ }
+
+ /** Gets the store operation that uses the address computed by this `StoreNode`. */
+ abstract Instruction getStoreInstruction();
+
+ /** Holds if the store operation associated with this `StoreNode` overwrites the entire variable. */
+ final predicate isCertain() { Ssa::explicitWrite(true, this.getStoreInstruction(), _) }
+
+ /**
+ * Gets the `StoreNode` that computes the address used by this `StoreNode`.
+ * The boolean `readEffect` is `true` if the predecessor is accessed through the
+ * address of a `ReadSideEffectInstruction`.
+ */
+ abstract StoreNode getAPredecessor();
+
+ /** The inverse of `StoreNode.getAPredecessor`. */
+ final StoreNode getASuccessor() { result.getAPredecessor() = this }
+}
+
+private class StoreNodeInstr extends StoreNode, TStoreNodeInstr {
+ Instruction instr;
+
+ StoreNodeInstr() { this = TStoreNodeInstr(instr) }
+
+ override predicate flowInto(Instruction addr) { this.getInstruction() = addr }
+
+ override Instruction getInstruction() { result = instr }
+
+ override Function getFunction() { result = this.getInstruction().getEnclosingFunction() }
+
+ override IRType getType() { result = this.getInstruction().getResultIRType() }
+
+ override Location getLocation() { result = this.getInstruction().getLocation() }
+
+ override string toString() {
+ result = instructionNode(this.getInstruction()).toString() + " [store]"
+ }
+
+ override Instruction getStoreInstruction() {
+ Ssa::explicitWrite(_, result, this.getInstruction())
+ }
+
+ override StoreNode getAPredecessor() {
+ Ssa::addressFlow(result.getInstruction(), this.getInstruction())
+ }
+}
+
+private class StoreNodeOperand extends StoreNode, TStoreNodeOperand {
+ ArgumentOperand operand;
+
+ StoreNodeOperand() { this = TStoreNodeOperand(operand) }
+
+ override predicate flowInto(Instruction addr) { this.getOperand().getDef() = addr }
+
+ override Operand getOperand() { result = operand }
+
+ override Function getFunction() { result = operand.getDef().getEnclosingFunction() }
+
+ override IRType getType() { result = operand.getIRType() }
+
+ override Location getLocation() { result = operand.getLocation() }
+
+ override string toString() { result = operandNode(this.getOperand()).toString() + " [store]" }
+
+ override WriteSideEffectInstruction getStoreInstruction() {
+ Ssa::explicitWrite(_, result, operand.getDef())
+ }
+
+ override StoreNode getAPredecessor() { operand.getDef() = result.getInstruction() }
+}
+
/**
* An expression, viewed as a node in a data flow graph.
*/
@@ -313,15 +424,14 @@ deprecated class UninitializedNode extends Node {
* Nodes corresponding to AST elements, for example `ExprNode`, usually refer
* to the value before the update with the exception of `ClassInstanceExpr`,
* which represents the value after the constructor has run.
- *
- * This class exists to match the interface used by Java. There are currently no non-abstract
- * classes that extend it. When we implement field flow, we can revisit this.
*/
-abstract class PostUpdateNode extends InstructionNode {
+abstract class PostUpdateNode extends Node {
/**
* Gets the node before the state update.
*/
abstract Node getPreUpdateNode();
+
+ override string toString() { result = this.getPreUpdateNode() + " [post update]" }
}
/**
@@ -614,6 +724,13 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
or
// Instruction -> Operand flow
simpleOperandLocalFlowStep(nodeFrom.asInstruction(), nodeTo.asOperand())
+ or
+ // Flow into, through, and out of store nodes
+ StoreNodeFlow::flowInto(nodeFrom, nodeTo)
+ or
+ StoreNodeFlow::flowThrough(nodeFrom, nodeTo)
+ or
+ StoreNodeFlow::flowOutOf(nodeFrom, nodeTo)
}
pragma[noinline]
@@ -631,6 +748,28 @@ private predicate isSingleFieldClass(Type type, Operand op) {
c.getSize() = size and
getFieldSizeOfClass(c, type, size)
)
+private module StoreNodeFlow {
+ /** Holds if the store node `nodeTo` should receive flow from `nodeFrom`. */
+ predicate flowInto(Node nodeFrom, StoreNode nodeTo) {
+ nodeTo.flowInto(Ssa::getDestinationAddress(nodeFrom.asInstruction()))
+ }
+
+ /** Holds if the store node `nodeTo` should receive flow from `nodeFom`. */
+ predicate flowThrough(StoreNode nFrom, StoreNode nodeTo) {
+ // Flow through a post update node that doesn't need a store step.
+ not storeStep(nFrom, _, _) and
+ nodeTo.getASuccessor() = nFrom
+ }
+
+ /**
+ * Holds if flow should leave the store node `nodeFrom` and enter the node `nodeTo`.
+ * This happens because we have traversed an entire chain of field dereferences
+ * after a store operation.
+ */
+ predicate flowOutOf(StoreNode nFrom, Node nodeTo) {
+ nFrom.isTerminal() and
+ Ssa::ssaFlow(nFrom, nodeTo)
+ }
}
private predicate simpleOperandLocalFlowStep(Instruction iFrom, Operand opTo) {
@@ -788,25 +927,10 @@ predicate localInstructionFlow(Instruction e1, Instruction e2) {
*/
predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)) }
-/**
- * Gets a field corresponding to the bit range `[startBit..endBit)` of class `c`, if any.
- */
-private Field getAField(Class c, int startBit, int endBit) {
- result.getDeclaringType() = c and
- startBit = 8 * result.getByteOffset() and
- endBit = 8 * result.getType().getSize() + startBit
- or
- exists(Field f, Class cInner |
- f = c.getAField() and
- cInner = f.getUnderlyingType() and
- result = getAField(cInner, startBit - 8 * f.getByteOffset(), endBit - 8 * f.getByteOffset())
- )
-}
-
private newtype TContent =
- TFieldContent(Class c, int startBit, int endBit) { exists(getAField(c, startBit, endBit)) } or
- TCollectionContent() or
- TArrayContent()
+ TFieldContent(Field f) or
+ TCollectionContent() or // Not used in C/C++
+ TArrayContent() // Not used in C/C++.
/**
* A description of the way data may be stored inside an object. Examples
@@ -824,18 +948,13 @@ class Content extends TContent {
/** A reference through an instance field. */
class FieldContent extends Content, TFieldContent {
- Class c;
- int startBit;
- int endBit;
+ Field f;
- FieldContent() { this = TFieldContent(c, startBit, endBit) }
+ FieldContent() { this = TFieldContent(f) }
- // Ensure that there's just 1 result for `toString`.
- override string toString() { result = min(Field f | f = getAField() | f.toString()) }
+ override string toString() { result = f.toString() }
- predicate hasOffset(Class cl, int start, int end) { cl = c and start = startBit and end = endBit }
-
- Field getAField() { result = getAField(c, startBit, endBit) }
+ Field getField() { result = f }
}
/** A reference through an array. */
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
index 57ba19f71f0..b4cfa89c22f 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
@@ -301,3 +301,114 @@ predicate explicitWrite(boolean certain, Instruction instr, Instruction address)
addressFlowTC(address, instr.(WriteSideEffectInstruction).getDestinationAddress()) and
certain = false
}
+
+cached
+private module Cached {
+ private predicate defUseFlow(Node nodeFrom, Node nodeTo) {
+ exists(IRBlock bb1, int i1, IRBlock bb2, int i2, DefOrUse defOrUse, Use use |
+ defOrUse.hasRankInBlock(bb1, i1) and
+ use.hasRankInBlock(bb2, i2) and
+ adjacentDefRead(_, bb1, i1, bb2, i2) and
+ nodeFrom.asInstruction() = toInstruction(defOrUse) and
+ flowOutOfAddressStep(use.getOperand(), nodeTo)
+ )
+ }
+
+ private predicate fromStoreNode(StoreNode nodeFrom, Node nodeTo) {
+ // Def-use flow from a `StoreNode`.
+ exists(IRBlock bb1, int i1, IRBlock bb2, int i2, Def def, Use use |
+ nodeFrom.isTerminal() and
+ def.getInstruction() = nodeFrom.getStoreInstruction() and
+ def.hasRankInBlock(bb1, i1) and
+ adjacentDefRead(_, bb1, i1, bb2, i2) and
+ use.hasRankInBlock(bb2, i2) and
+ flowOutOfAddressStep(use.getOperand(), nodeTo)
+ )
+ }
+
+ /**
+ * Holds if `nodeFrom` is a read or write, and `nTo` is the next subsequent read of the variable
+ * written (or read) by `storeOrRead`.
+ */
+ cached
+ predicate ssaFlow(Node nodeFrom, Node nodeTo) {
+ // Def-use/use-use flow from an `InstructionNode` to an `OperandNode`.
+ defUseFlow(nodeFrom, nodeTo)
+ or
+ // Def-use flow from a `StoreNode` to an `OperandNode`.
+ fromStoreNode(nodeFrom, nodeTo)
+ }
+
+ private predicate flowOutOfAddressStep(Operand operand, Node nTo) {
+ exists(StoreNode storeNode, Instruction def |
+ storeNode = nTo and
+ def = operand.getDef()
+ |
+ storeNode.isTerminal() and
+ not addressFlow(def, _) and
+ // Only transfer flow to a store node if it doesn't immediately overwrite the address
+ // we've just written to.
+ explicitWrite(false, storeNode.getStoreInstruction(), def)
+ )
+ or
+ operand = getSourceAddressOperand(nTo.asInstruction())
+ or
+ exists(ReturnIndirectionInstruction ret |
+ ret.getSourceAddressOperand() = operand and
+ ret = nTo.asInstruction()
+ )
+ or
+ exists(ReturnValueInstruction ret |
+ ret.getReturnAddressOperand() = operand and
+ nTo.asInstruction() = ret
+ )
+ or
+ exists(CallInstruction call, int index, ReadSideEffectInstruction read |
+ call.getArgumentOperand(index) = operand and
+ read = getSideEffectFor(call, index) and
+ nTo.asOperand() = read.getSideEffectOperand()
+ )
+ or
+ exists(CopyInstruction copy |
+ not exists(getSourceAddressOperand(copy)) and
+ copy.getSourceValueOperand() = operand and
+ flowOutOfAddressStep(copy.getAUse(), nTo)
+ )
+ or
+ exists(ConvertInstruction convert |
+ convert.getUnaryOperand() = operand and
+ flowOutOfAddressStep(convert.getAUse(), nTo)
+ )
+ or
+ exists(CheckedConvertOrNullInstruction convert |
+ convert.getUnaryOperand() = operand and
+ flowOutOfAddressStep(convert.getAUse(), nTo)
+ )
+ or
+ exists(InheritanceConversionInstruction convert |
+ convert.getUnaryOperand() = operand and
+ flowOutOfAddressStep(convert.getAUse(), nTo)
+ )
+ or
+ exists(PointerArithmeticInstruction arith |
+ arith.getLeftOperand() = operand and
+ flowOutOfAddressStep(arith.getAUse(), nTo)
+ )
+ or
+ // Flow through a modelled function that has parameter -> return value flow.
+ exists(
+ CallInstruction call, DataFlow::DataFlowFunction func, int index,
+ DataFlow::FunctionInput input, DataFlow::FunctionOutput output
+ |
+ call.getStaticCallTarget() = func and
+ call.getArgumentOperand(index) = operand and
+ not getSideEffectFor(call, index) instanceof ReadSideEffectInstruction and
+ func.hasDataFlow(input, output) and
+ input.isParameter(index) and
+ output.isReturnValue() and
+ flowOutOfAddressStep(call.getAUse(), nTo)
+ )
+ }
+}
+
+import Cached
From 8bef79502fda8a83544509a4b0ca0e6ac3e9cc15 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Wed, 20 Oct 2021 15:18:08 +0100
Subject: [PATCH 194/471] C++: Similarly to the previous commit, we throw away
the old memory-edges based way of doing read steps. Instead, we use the
shared SSA library to transfer flow into a new ReadNode IPA branch, perform
the necessary read steps, and then use the shared SSA library to transfer
flow out of the ReadNode again.
---
.../ir/dataflow/internal/DataFlowPrivate.qll | 119 +---------------
.../cpp/ir/dataflow/internal/DataFlowUtil.qll | 130 +++++++++++++++++-
.../code/cpp/ir/dataflow/internal/Ssa.qll | 19 +++
3 files changed, 148 insertions(+), 120 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
index 4d4271941b4..9de1da31772 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
@@ -203,122 +203,15 @@ predicate storeStep(StoreNode node1, FieldContent f, StoreNode node2) {
* Thus, `node1` references an object with a field `f` whose value ends up in
* `node2`.
*/
-private predicate fieldReadStep(Node node1, FieldContent f, Node node2) {
- exists(LoadOperand operand |
- node2.asOperand() = operand and
- node1.asInstruction() = operand.getAnyDef() and
- exists(Class c |
- c = operand.getAnyDef().getResultType() and
- exists(int startBit, int endBit |
- operand.getUsedInterval(unbindInt(startBit), unbindInt(endBit)) and
- f.hasOffset(c, startBit, endBit)
- )
- or
- getLoadedField(operand.getUse(), f.getAField(), c) and
- f.hasOffset(c, _, _)
- )
+predicate readStep(ReadNode node1, FieldContent f, ReadNode node2) {
+ exists(FieldAddressInstruction fai |
+ not fai.getObjectAddress().getResultType().stripType() instanceof Union and
+ node1.getInstruction() = fai.getObjectAddress() and
+ node2.getInstruction() = fai and
+ f.getField() = fai.getField()
)
}
-/**
- * When a store step happens in a function that looks like an array write such as:
- * ```cpp
- * void f(int* pa) {
- * pa = source();
- * }
- * ```
- * it can be a write to an array, but it can also happen that `f` is called as `f(&a.x)`. If that is
- * the case, the `ArrayContent` that was written by the call to `f` should be popped off the access
- * path, and a `FieldContent` containing `x` should be pushed instead.
- * So this case pops `ArrayContent` off the access path, and the `fieldStoreStepAfterArraySuppression`
- * predicate in `storeStep` ensures that we push the right `FieldContent` onto the access path.
- */
-predicate suppressArrayRead(Node node1, ArrayContent a, Node node2) {
- exists(a) and
- exists(WriteSideEffectInstruction write, ChiInstruction chi |
- node1.asInstruction() = write and
- node2.asInstruction() = chi and
- chi.getPartial() = write and
- getWrittenField(write, _, _)
- )
-}
-
-private class ArrayToPointerConvertInstruction extends ConvertInstruction {
- ArrayToPointerConvertInstruction() {
- this.getUnary().getResultType() instanceof ArrayType and
- this.getResultType() instanceof PointerType
- }
-}
-
-private Instruction skipOneCopyValueInstructionRec(CopyValueInstruction copy) {
- copy.getUnary() = result and not result instanceof CopyValueInstruction
- or
- result = skipOneCopyValueInstructionRec(copy.getUnary())
-}
-
-private Instruction skipCopyValueInstructions(Operand op) {
- not result instanceof CopyValueInstruction and result = op.getDef()
- or
- result = skipOneCopyValueInstructionRec(op.getDef())
-}
-
-private predicate arrayReadStep(Node node1, ArrayContent a, Node node2) {
- exists(a) and
- // Explicit dereferences such as `*p` or `p[i]` where `p` is a pointer or array.
- exists(LoadOperand operand, Instruction address |
- operand.isDefinitionInexact() and
- node1.asInstruction() = operand.getAnyDef() and
- operand = node2.asOperand() and
- address = skipCopyValueInstructions(operand.getAddressOperand()) and
- (
- address instanceof LoadInstruction or
- address instanceof ArrayToPointerConvertInstruction or
- address instanceof PointerOffsetInstruction
- )
- )
-}
-
-/**
- * In cases such as:
- * ```cpp
- * void f(int* pa) {
- * *pa = source();
- * }
- * ...
- * int x;
- * f(&x);
- * use(x);
- * ```
- * the load on `x` in `use(x)` will exactly overlap with its definition (in this case the definition
- * is a `WriteSideEffect`). This predicate pops the `ArrayContent` (pushed by the store in `f`)
- * from the access path.
- */
-private predicate exactReadStep(Node node1, ArrayContent a, Node node2) {
- exists(a) and
- exists(WriteSideEffectInstruction write, ChiInstruction chi |
- not chi.isResultConflated() and
- chi.getPartial() = write and
- node1.asInstruction() = write and
- node2.asInstruction() = chi and
- // To distinquish this case from the `arrayReadStep` case we require that the entire variable was
- // overwritten by the `WriteSideEffectInstruction` (i.e., there is a load that reads the
- // entire variable).
- exists(LoadInstruction load | load.getSourceValue() = chi)
- )
-}
-
-/**
- * Holds if data can flow from `node1` to `node2` via a read of `f`.
- * Thus, `node1` references an object with a field `f` whose value ends up in
- * `node2`.
- */
-predicate readStep(Node node1, Content f, Node node2) {
- fieldReadStep(node1, f, node2) or
- arrayReadStep(node1, f, node2) or
- exactReadStep(node1, f, node2) or
- suppressArrayRead(node1, f, node2)
-}
-
/**
* Holds if values stored inside content `c` are cleared at node `n`.
*/
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
index 449e680b850..6291362855c 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
@@ -21,7 +21,8 @@ private module Cached {
TOperandNode(Operand op) or
TVariableNode(Variable var) or
TStoreNodeInstr(Instruction i) { Ssa::explicitWrite(_, _, i) } or
- TStoreNodeOperand(ArgumentOperand op) { Ssa::explicitWrite(_, _, op.getDef()) }
+ TStoreNodeOperand(ArgumentOperand op) { Ssa::explicitWrite(_, _, op.getDef()) } or
+ TReadNode(Instruction i) { needsPostReadNode(i) }
cached
predicate localFlowStepCached(Node nodeFrom, Node nodeTo) {
@@ -291,6 +292,61 @@ private class StoreNodeOperand extends StoreNode, TStoreNodeOperand {
override StoreNode getAPredecessor() { operand.getDef() = result.getInstruction() }
}
+/**
+ * INTERNAL: do not use.
+ *
+ * A `ReadNode` is a node that has been (or is about to be) the
+ * source or target of a `readStep`.
+ */
+class ReadNode extends Node, TReadNode {
+ Instruction i;
+
+ ReadNode() { this = TReadNode(i) }
+
+ /** Gets the underlying instruction. */
+ Instruction getInstruction() { result = i }
+
+ override Declaration getEnclosingCallable() { result = this.getFunction() }
+
+ override Function getFunction() { result = this.getInstruction().getEnclosingFunction() }
+
+ override IRType getType() { result = this.getInstruction().getResultIRType() }
+
+ override Location getLocation() { result = this.getInstruction().getLocation() }
+
+ override string toString() {
+ result = instructionNode(this.getInstruction()).toString() + " [read]"
+ }
+
+ /** Gets a load instruction that uses the address computed by this read node. */
+ final Instruction getALoadInstruction() {
+ Ssa::addressFlowTC(this.getInstruction(), Ssa::getSourceAddress(result))
+ }
+
+ /**
+ * Gets a read node with an underlying instruction that is used by this
+ * underlying instruction to compute an address of a load instruction.
+ */
+ final ReadNode getAPredecessor() {
+ Ssa::addressFlow(result.getInstruction(), this.getInstruction())
+ }
+
+ /** The inverse of `ReadNode.getAPredecessor`. */
+ final ReadNode getASuccessor() { result.getAPredecessor() = this }
+
+ /** Holds if this read node computes a value that will not be used for any future read nodes. */
+ final predicate isTerminal() {
+ not exists(this.getASuccessor()) and
+ not readStep(this, _, _)
+ }
+
+ /** Holds if this read node computes a value that has not yet been used for any read operations. */
+ final predicate isInitial() {
+ not exists(this.getAPredecessor()) and
+ not readStep(_, _, this)
+ }
+}
+
/**
* An expression, viewed as a node in a data flow graph.
*/
@@ -731,6 +787,13 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
StoreNodeFlow::flowThrough(nodeFrom, nodeTo)
or
StoreNodeFlow::flowOutOf(nodeFrom, nodeTo)
+ or
+ // Flow into, through, and out of read nodes
+ ReadNodeFlow::flowInto(nodeFrom, nodeTo)
+ or
+ ReadNodeFlow::flowThrough(nodeFrom, nodeTo)
+ or
+ ReadNodeFlow::flowOutOf(nodeFrom, nodeTo)
}
pragma[noinline]
@@ -742,12 +805,65 @@ private predicate getFieldSizeOfClass(Class c, Type type, int size) {
)
}
-private predicate isSingleFieldClass(Type type, Operand op) {
- exists(int size, Class c |
- c = op.getType().getUnderlyingType() and
- c.getSize() = size and
- getFieldSizeOfClass(c, type, size)
- )
+private module ReadNodeFlow {
+ /** Holds if the read node `nodeTo` should receive flow from `nodeFrom`. */
+ predicate flowInto(Node nodeFrom, ReadNode nodeTo) {
+ nodeTo.isInitial() and
+ (
+ // If we entered through an address operand.
+ nodeFrom.asOperand().getDef() = nodeTo.getInstruction()
+ or
+ // If we entered flow through a memory-producing instruction.
+ // This can happen if we have flow to an `InitializeParameterIndirection` through
+ // a `ReadSideEffectInstruction`.
+ exists(Instruction load, Instruction def |
+ def = nodeFrom.asInstruction() and
+ def = Ssa::getSourceValueOperand(load).getAnyDef() and
+ not def = any(StoreNode store).getStoreInstruction() and
+ pragma[only_bind_into](nodeTo).getALoadInstruction() = load
+ )
+ )
+ }
+
+ /** Holds if the read node `nodeTo` should receive flow from the read node `nodeFrom`. */
+ predicate flowThrough(ReadNode nodeFrom, ReadNode nodeTo) {
+ not readStep(nodeFrom, _, _) and
+ nodeFrom.getASuccessor() = nodeTo
+ }
+
+ /**
+ * Holds if flow should leave the read node `nFrom` and enter the node `nodeTo`.
+ * This happens either because there is use-use flow from one of the variables used in
+ * the read operation, or because we have traversed all the field dereferences in the
+ * read operation.
+ */
+ predicate flowOutOf(ReadNode nFrom, Node nodeTo) {
+ // Use-use flow to another use of the same variable instruction
+ Ssa::ssaFlow(nFrom, nodeTo)
+ or
+ not exists(nFrom.getAPredecessor()) and
+ exists(Node store |
+ Ssa::explicitWrite(_, store.asInstruction(), nFrom.getInstruction()) and
+ Ssa::ssaFlow(store, nodeTo)
+ )
+ or
+ // Flow out of read nodes and into memory instructions if we cannot move any further through
+ // read nodes.
+ nFrom.isTerminal() and
+ (
+ exists(Instruction load |
+ load = nodeTo.asInstruction() and
+ Ssa::getSourceAddress(load) = nFrom.getInstruction()
+ )
+ or
+ exists(CallInstruction call, int i |
+ call.getArgument(i) = nodeTo.asInstruction() and
+ call.getArgument(i) = nFrom.getInstruction()
+ )
+ )
+ }
+}
+
private module StoreNodeFlow {
/** Holds if the store node `nodeTo` should receive flow from `nodeFrom`. */
predicate flowInto(Node nodeFrom, StoreNode nodeTo) {
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
index b4cfa89c22f..bb6e3addf81 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
@@ -326,6 +326,16 @@ private module Cached {
)
}
+ private predicate fromReadNode(ReadNode nodeFrom, Node nodeTo) {
+ exists(IRBlock bb1, int i1, IRBlock bb2, int i2, Use use1, Use use2 |
+ use1.hasRankInBlock(bb1, i1) and
+ use2.hasRankInBlock(bb2, i2) and
+ use1.getOperand().getDef() = nodeFrom.getInstruction() and
+ adjacentDefRead(_, bb1, i1, bb2, i2) and
+ flowOutOfAddressStep(use2.getOperand(), nodeTo)
+ )
+ }
+
/**
* Holds if `nodeFrom` is a read or write, and `nTo` is the next subsequent read of the variable
* written (or read) by `storeOrRead`.
@@ -337,9 +347,18 @@ private module Cached {
or
// Def-use flow from a `StoreNode` to an `OperandNode`.
fromStoreNode(nodeFrom, nodeTo)
+ or
+ // Use-use flow from a `ReadNode` to an `OperandNode`
+ fromReadNode(nodeFrom, nodeTo)
}
private predicate flowOutOfAddressStep(Operand operand, Node nTo) {
+ // Flow into a read node
+ exists(ReadNode readNode | readNode = nTo |
+ readNode.isInitial() and
+ operand.getDef() = readNode.getInstruction()
+ )
+ or
exists(StoreNode storeNode, Instruction def |
storeNode = nTo and
def = operand.getDef()
From 3a488574e59624e8e6301ff824a81139c1f58206 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Wed, 20 Oct 2021 15:19:31 +0100
Subject: [PATCH 195/471] C++: Rewrite the PartialDefinitionNode classes to
match the new StoreNodes.
---
.../cpp/ir/dataflow/internal/DataFlowUtil.qll | 147 +-----
.../fields/partial-definition-diff.expected | 448 +++---------------
.../fields/partial-definition-ir.expected | 439 ++++++++++++++++-
.../fields/partial-definition.expected | 4 +
4 files changed, 540 insertions(+), 498 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
index 6291362855c..f8b4f3b8df0 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
@@ -498,7 +498,7 @@ abstract class PostUpdateNode extends Node {
* value, but does not necessarily replace it entirely. For example:
* ```
* x.y = 1; // a partial definition of the object `x`.
- * x.y.z = 1; // a partial definition of the object `x.y`.
+ * x.y.z = 1; // a partial definition of the object `x.y` and `x`.
* x.setY(1); // a partial definition of the object `x`.
* setY(&x); // a partial definition of the object `x`.
* ```
@@ -507,135 +507,34 @@ abstract private class PartialDefinitionNode extends PostUpdateNode {
abstract Expr getDefinedExpr();
}
-private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode {
- override ChiInstruction instr;
- StoreInstruction store;
-
- ExplicitFieldStoreQualifierNode() {
- not instr.isResultConflated() and
- instr.getPartial() = store and
- (
- instr.getUpdatedInterval(_, _) or
- store.getDestinationAddress() instanceof FieldAddressInstruction
- )
+private class FieldPartialDefinitionNode extends PartialDefinitionNode, StoreNodeInstr {
+ FieldPartialDefinitionNode() {
+ this.getInstruction() = any(FieldAddressInstruction fai).getObjectAddress()
}
- // By using an operand as the result of this predicate we avoid the dataflow inconsistency errors
- // caused by having multiple nodes sharing the same pre update node. This inconsistency error can cause
- // a tuple explosion in the big step dataflow relation since it can make many nodes be the entry node
- // into a big step.
- override Node getPreUpdateNode() { result.asOperand() = instr.getTotalOperand() }
+ override Node getPreUpdateNode() { result.asInstruction() = this.getInstruction() }
+
+ override Expr getDefinedExpr() { result = this.getInstruction().getUnconvertedResultExpression() }
+
+ override string toString() { result = PartialDefinitionNode.super.toString() }
+}
+
+private class NonPartialDefinitionPostUpdate extends PostUpdateNode, StoreNodeInstr {
+ NonPartialDefinitionPostUpdate() { not this instanceof PartialDefinitionNode }
+
+ override Node getPreUpdateNode() { result.asInstruction() = this.getInstruction() }
+
+ override string toString() { result = PostUpdateNode.super.toString() }
+}
+
+private class ArgumentPostUpdateNode extends PartialDefinitionNode, StoreNodeOperand {
+ override ArgumentNode getPreUpdateNode() { result.asOperand() = operand }
override Expr getDefinedExpr() {
- result =
- store
- .getDestinationAddress()
- .(FieldAddressInstruction)
- .getObjectAddress()
- .getUnconvertedResultExpression()
- }
-}
-
-/**
- * Not every store instruction generates a chi instruction that we can attach a PostUpdateNode to.
- * For instance, an update to a field of a struct containing only one field. Even if the store does
- * have a chi instruction, a subsequent use of the result of the store may be linked directly to the
- * result of the store as an inexact definition if the store totally overlaps the use. For these
- * cases we attach the PostUpdateNode to the store instruction. There's no obvious pre update node
- * for this case (as the entire memory is updated), so `getPreUpdateNode` is implemented as
- * `none()`.
- */
-private class ExplicitSingleFieldStoreQualifierNode extends PartialDefinitionNode {
- override StoreInstruction instr;
-
- ExplicitSingleFieldStoreQualifierNode() {
- (
- instr.getAUse().isDefinitionInexact()
- or
- not exists(ChiInstruction chi | chi.getPartial() = instr)
- ) and
- // Without this condition any store would create a `PostUpdateNode`.
- instr.getDestinationAddress() instanceof FieldAddressInstruction
+ result = this.getOperand().getDef().getUnconvertedResultExpression()
}
- override Node getPreUpdateNode() { none() }
-
- override Expr getDefinedExpr() {
- result =
- instr
- .getDestinationAddress()
- .(FieldAddressInstruction)
- .getObjectAddress()
- .getUnconvertedResultExpression()
- }
-}
-
-private FieldAddressInstruction getFieldInstruction(Instruction instr) {
- result = instr or
- result = instr.(CopyValueInstruction).getUnary()
-}
-
-/**
- * The target of a `fieldStoreStepAfterArraySuppression` store step, which is used to convert
- * an `ArrayContent` to a `FieldContent` when the `WriteSideEffect` instruction stores
- * into a field. See the QLDoc for `suppressArrayRead` for an example of where such a conversion
- * is inserted.
- */
-private class WriteSideEffectFieldStoreQualifierNode extends PartialDefinitionNode {
- override ChiInstruction instr;
- WriteSideEffectInstruction write;
- FieldAddressInstruction field;
-
- WriteSideEffectFieldStoreQualifierNode() {
- not instr.isResultConflated() and
- instr.getPartial() = write and
- field = getFieldInstruction(write.getDestinationAddress())
- }
-
- override Node getPreUpdateNode() { result.asOperand() = instr.getTotalOperand() }
-
- override Expr getDefinedExpr() {
- result = field.getObjectAddress().getUnconvertedResultExpression()
- }
-}
-
-/**
- * The `PostUpdateNode` that is the target of a `arrayStoreStepChi` store step. The overriden
- * `ChiInstruction` corresponds to the instruction represented by `node2` in `arrayStoreStepChi`.
- */
-private class ArrayStoreNode extends PartialDefinitionNode {
- override ChiInstruction instr;
- PointerAddInstruction add;
-
- ArrayStoreNode() {
- not instr.isResultConflated() and
- exists(StoreInstruction store |
- instr.getPartial() = store and
- add = store.getDestinationAddress()
- )
- }
-
- override Node getPreUpdateNode() { result.asOperand() = instr.getTotalOperand() }
-
- override Expr getDefinedExpr() { result = add.getLeft().getUnconvertedResultExpression() }
-}
-
-/**
- * The `PostUpdateNode` that is the target of a `arrayStoreStepChi` store step. The overriden
- * `ChiInstruction` corresponds to the instruction represented by `node2` in `arrayStoreStepChi`.
- */
-private class PointerStoreNode extends PostUpdateNode {
- override ChiInstruction instr;
-
- PointerStoreNode() {
- not instr.isResultConflated() and
- exists(StoreInstruction store |
- instr.getPartial() = store and
- store.getDestinationAddress().(CopyValueInstruction).getUnary() instanceof LoadInstruction
- )
- }
-
- override Node getPreUpdateNode() { result.asOperand() = instr.getTotalOperand() }
+ override string toString() { result = PartialDefinitionNode.super.toString() }
}
/**
diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected
index bd670c38ba5..a2eb9f15991 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected
+++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected
@@ -1,152 +1,93 @@
| A.cpp:25:13:25:13 | c | AST only |
| A.cpp:27:28:27:28 | c | AST only |
-| A.cpp:31:20:31:20 | c | AST only |
-| A.cpp:40:5:40:6 | cc | AST only |
-| A.cpp:41:5:41:6 | ct | AST only |
-| A.cpp:42:10:42:12 | & ... | AST only |
-| A.cpp:43:10:43:12 | & ... | AST only |
-| A.cpp:48:20:48:20 | c | AST only |
-| A.cpp:49:10:49:10 | b | AST only |
-| A.cpp:49:13:49:13 | c | AST only |
-| A.cpp:55:5:55:5 | b | AST only |
-| A.cpp:56:10:56:10 | b | AST only |
-| A.cpp:56:13:56:15 | call to get | AST only |
-| A.cpp:57:28:57:30 | call to get | AST only |
-| A.cpp:64:10:64:15 | this | AST only |
-| A.cpp:64:17:64:18 | b1 | AST only |
-| A.cpp:65:10:65:11 | b1 | AST only |
-| A.cpp:65:14:65:14 | c | AST only |
-| A.cpp:66:10:66:11 | b2 | AST only |
-| A.cpp:66:14:66:14 | c | AST only |
-| A.cpp:73:10:73:19 | this | AST only |
-| A.cpp:73:21:73:22 | b1 | AST only |
-| A.cpp:74:10:74:11 | b1 | AST only |
-| A.cpp:74:14:74:14 | c | AST only |
-| A.cpp:75:10:75:11 | b2 | AST only |
-| A.cpp:75:14:75:14 | c | AST only |
-| A.cpp:81:10:81:15 | this | AST only |
-| A.cpp:81:17:81:18 | b1 | AST only |
-| A.cpp:81:21:81:21 | c | AST only |
-| A.cpp:82:12:82:12 | this | AST only |
-| A.cpp:87:9:87:9 | this | AST only |
-| A.cpp:90:7:90:8 | b2 | AST only |
-| A.cpp:90:15:90:15 | c | AST only |
+| A.cpp:31:14:31:21 | new | IR only |
+| A.cpp:40:8:40:13 | 0 | IR only |
+| A.cpp:41:8:41:13 | new | IR only |
+| A.cpp:41:15:41:21 | new | IR only |
+| A.cpp:47:12:47:18 | new | IR only |
+| A.cpp:54:12:54:18 | new | IR only |
+| A.cpp:55:8:55:10 | new | IR only |
+| A.cpp:55:12:55:19 | new | IR only |
+| A.cpp:57:11:57:24 | new | IR only |
+| A.cpp:57:11:57:24 | new | IR only |
+| A.cpp:57:17:57:23 | new | IR only |
+| A.cpp:57:28:57:30 | new | IR only |
+| A.cpp:62:13:62:19 | new | IR only |
+| A.cpp:64:10:64:15 | new | IR only |
+| A.cpp:64:21:64:28 | new | IR only |
+| A.cpp:71:13:71:19 | new | IR only |
+| A.cpp:73:10:73:19 | new | IR only |
+| A.cpp:73:25:73:32 | new | IR only |
+| A.cpp:89:15:89:21 | new | IR only |
+| A.cpp:99:14:99:21 | new | IR only |
| A.cpp:100:9:100:9 | a | AST only |
-| A.cpp:101:5:101:6 | this | AST only |
-| A.cpp:101:8:101:9 | c1 | AST only |
-| A.cpp:107:12:107:13 | c1 | AST only |
-| A.cpp:107:16:107:16 | a | AST only |
-| A.cpp:120:12:120:13 | c1 | AST only |
-| A.cpp:120:16:120:16 | a | AST only |
-| A.cpp:126:5:126:5 | b | AST only |
-| A.cpp:131:5:131:6 | this | AST only |
-| A.cpp:131:8:131:8 | b | AST only |
-| A.cpp:132:10:132:10 | b | AST only |
-| A.cpp:132:13:132:13 | c | AST only |
+| A.cpp:116:12:116:19 | new | IR only |
+| A.cpp:126:8:126:10 | new | IR only |
+| A.cpp:126:12:126:18 | new | IR only |
+| A.cpp:130:12:130:18 | new | IR only |
| A.cpp:142:10:142:10 | c | AST only |
+| A.cpp:142:14:142:20 | new | IR only |
| A.cpp:143:13:143:13 | b | AST only |
-| A.cpp:151:18:151:18 | b | AST only |
-| A.cpp:151:21:151:21 | this | AST only |
-| A.cpp:152:10:152:10 | d | AST only |
-| A.cpp:152:13:152:13 | b | AST only |
-| A.cpp:153:10:153:10 | d | AST only |
-| A.cpp:153:13:153:13 | b | AST only |
-| A.cpp:153:16:153:16 | c | AST only |
-| A.cpp:154:10:154:10 | b | AST only |
-| A.cpp:154:13:154:13 | c | AST only |
-| A.cpp:160:29:160:29 | b | AST only |
-| A.cpp:161:38:161:39 | l1 | AST only |
-| A.cpp:162:38:162:39 | l2 | AST only |
-| A.cpp:163:10:163:11 | l3 | AST only |
-| A.cpp:163:14:163:17 | head | AST only |
-| A.cpp:164:10:164:11 | l3 | AST only |
-| A.cpp:164:14:164:17 | next | AST only |
-| A.cpp:164:20:164:23 | head | AST only |
-| A.cpp:165:10:165:11 | l3 | AST only |
-| A.cpp:165:14:165:17 | next | AST only |
-| A.cpp:165:20:165:23 | next | AST only |
-| A.cpp:165:26:165:29 | head | AST only |
-| A.cpp:166:10:166:11 | l3 | AST only |
-| A.cpp:166:14:166:17 | next | AST only |
-| A.cpp:166:20:166:23 | next | AST only |
-| A.cpp:166:26:166:29 | next | AST only |
-| A.cpp:166:32:166:35 | head | AST only |
-| A.cpp:169:12:169:12 | l | AST only |
-| A.cpp:169:15:169:18 | head | AST only |
+| A.cpp:143:25:143:31 | new | IR only |
+| A.cpp:150:12:150:18 | new | IR only |
+| A.cpp:151:12:151:24 | new | IR only |
+| A.cpp:159:12:159:18 | new | IR only |
+| A.cpp:160:18:160:60 | new | IR only |
+| A.cpp:160:18:160:60 | new | IR only |
+| A.cpp:160:32:160:59 | 0 | IR only |
+| A.cpp:160:32:160:59 | 0 | IR only |
+| A.cpp:160:32:160:59 | new | IR only |
+| A.cpp:161:18:161:40 | 0 | IR only |
+| A.cpp:161:18:161:40 | new | IR only |
+| A.cpp:162:18:162:40 | 0 | IR only |
+| A.cpp:162:18:162:40 | new | IR only |
| A.cpp:183:7:183:10 | head | AST only |
| A.cpp:184:13:184:16 | next | AST only |
-| B.cpp:7:25:7:25 | e | AST only |
-| B.cpp:8:25:8:26 | b1 | AST only |
-| B.cpp:9:10:9:11 | b2 | AST only |
-| B.cpp:9:14:9:17 | box1 | AST only |
-| B.cpp:9:20:9:24 | elem1 | AST only |
-| B.cpp:10:10:10:11 | b2 | AST only |
-| B.cpp:10:14:10:17 | box1 | AST only |
-| B.cpp:10:20:10:24 | elem2 | AST only |
-| B.cpp:16:37:16:37 | e | AST only |
-| B.cpp:17:25:17:26 | b1 | AST only |
-| B.cpp:18:10:18:11 | b2 | AST only |
-| B.cpp:18:14:18:17 | box1 | AST only |
-| B.cpp:18:20:18:24 | elem1 | AST only |
-| B.cpp:19:10:19:11 | b2 | AST only |
-| B.cpp:19:14:19:17 | box1 | AST only |
-| B.cpp:19:20:19:24 | elem2 | AST only |
+| B.cpp:7:16:7:35 | 0 | IR only |
+| B.cpp:7:16:7:35 | new | IR only |
+| B.cpp:8:16:8:27 | new | IR only |
+| B.cpp:16:16:16:38 | 0 | IR only |
+| B.cpp:16:16:16:38 | new | IR only |
+| B.cpp:17:16:17:27 | new | IR only |
| B.cpp:35:13:35:17 | elem1 | AST only |
| B.cpp:36:13:36:17 | elem2 | AST only |
| B.cpp:46:13:46:16 | box1 | AST only |
-| C.cpp:19:5:19:5 | c | AST only |
+| C.cpp:18:12:18:18 | new | IR only |
| C.cpp:24:11:24:12 | s3 | AST only |
+| C.cpp:30:5:30:8 | s2 | IR only |
+| C.cpp:30:10:30:11 | this | IR only |
+| C.cpp:32:5:32:8 | s4 | IR only |
| D.cpp:9:21:9:24 | elem | AST only |
| D.cpp:11:29:11:32 | elem | AST only |
| D.cpp:16:21:16:23 | box | AST only |
| D.cpp:18:29:18:31 | box | AST only |
-| D.cpp:22:10:22:11 | b2 | AST only |
-| D.cpp:22:14:22:20 | call to getBox1 | AST only |
-| D.cpp:22:25:22:31 | call to getElem | AST only |
-| D.cpp:30:5:30:5 | b | AST only |
-| D.cpp:30:8:30:10 | box | AST only |
+| D.cpp:29:15:29:41 | new | IR only |
+| D.cpp:29:15:29:41 | new | IR only |
+| D.cpp:29:24:29:40 | 0 | IR only |
+| D.cpp:29:24:29:40 | new | IR only |
| D.cpp:30:13:30:16 | elem | AST only |
-| D.cpp:31:14:31:14 | b | AST only |
-| D.cpp:37:5:37:5 | b | AST only |
-| D.cpp:37:8:37:10 | box | AST only |
-| D.cpp:37:21:37:21 | e | AST only |
-| D.cpp:38:14:38:14 | b | AST only |
-| D.cpp:44:5:44:5 | b | AST only |
-| D.cpp:44:8:44:14 | call to getBox1 | AST only |
+| D.cpp:36:15:36:41 | new | IR only |
+| D.cpp:36:15:36:41 | new | IR only |
+| D.cpp:36:24:36:40 | 0 | IR only |
+| D.cpp:36:24:36:40 | new | IR only |
+| D.cpp:43:15:43:41 | new | IR only |
+| D.cpp:43:15:43:41 | new | IR only |
+| D.cpp:43:24:43:40 | 0 | IR only |
+| D.cpp:43:24:43:40 | new | IR only |
| D.cpp:44:19:44:22 | elem | AST only |
-| D.cpp:45:14:45:14 | b | AST only |
-| D.cpp:51:5:51:5 | b | AST only |
-| D.cpp:51:8:51:14 | call to getBox1 | AST only |
-| D.cpp:51:27:51:27 | e | AST only |
-| D.cpp:52:14:52:14 | b | AST only |
+| D.cpp:50:15:50:41 | new | IR only |
+| D.cpp:50:15:50:41 | new | IR only |
+| D.cpp:50:24:50:40 | 0 | IR only |
+| D.cpp:50:24:50:40 | new | IR only |
| D.cpp:57:5:57:12 | boxfield | AST only |
-| D.cpp:58:5:58:12 | boxfield | AST only |
-| D.cpp:58:5:58:12 | this | AST only |
-| D.cpp:58:15:58:17 | box | AST only |
+| D.cpp:57:16:57:42 | new | IR only |
+| D.cpp:57:16:57:42 | new | IR only |
+| D.cpp:57:25:57:41 | 0 | IR only |
+| D.cpp:57:25:57:41 | new | IR only |
| D.cpp:58:20:58:23 | elem | AST only |
-| D.cpp:59:5:59:7 | this | AST only |
-| D.cpp:64:10:64:17 | boxfield | AST only |
-| D.cpp:64:10:64:17 | this | AST only |
-| D.cpp:64:20:64:22 | box | AST only |
-| D.cpp:64:25:64:28 | elem | AST only |
-| E.cpp:21:10:21:10 | p | AST only |
-| E.cpp:21:13:21:16 | data | AST only |
-| E.cpp:21:18:21:23 | buffer | AST only |
-| E.cpp:28:21:28:23 | raw | AST only |
-| E.cpp:29:21:29:21 | b | AST only |
-| E.cpp:29:24:29:29 | buffer | AST only |
-| E.cpp:30:21:30:21 | p | AST only |
-| E.cpp:30:23:30:26 | data | AST only |
-| E.cpp:30:28:30:33 | buffer | AST only |
-| E.cpp:31:10:31:12 | raw | AST only |
-| E.cpp:32:10:32:10 | b | AST only |
-| E.cpp:32:13:32:18 | buffer | AST only |
-| E.cpp:33:18:33:19 | & ... | AST only |
| aliasing.cpp:9:6:9:7 | m1 | AST only |
| aliasing.cpp:13:5:13:6 | m1 | AST only |
| aliasing.cpp:17:5:17:6 | m1 | AST only |
-| aliasing.cpp:25:17:25:19 | & ... | AST only |
-| aliasing.cpp:26:19:26:20 | s2 | AST only |
| aliasing.cpp:37:8:37:9 | m1 | AST only |
| aliasing.cpp:42:6:42:7 | m1 | AST only |
| aliasing.cpp:49:9:49:10 | m1 | AST only |
@@ -155,291 +96,52 @@
| aliasing.cpp:72:5:72:6 | m1 | AST only |
| aliasing.cpp:79:6:79:7 | m1 | AST only |
| aliasing.cpp:86:5:86:6 | m1 | AST only |
-| aliasing.cpp:92:3:92:3 | w | AST only |
| aliasing.cpp:92:7:92:8 | m1 | AST only |
| aliasing.cpp:98:5:98:6 | m1 | AST only |
| aliasing.cpp:106:3:106:5 | * ... | AST only |
-| aliasing.cpp:111:15:111:19 | & ... | AST only |
-| aliasing.cpp:121:15:121:16 | xs | AST only |
-| aliasing.cpp:126:15:126:20 | ... - ... | AST only |
-| aliasing.cpp:131:15:131:16 | xs | AST only |
-| aliasing.cpp:136:15:136:17 | + ... | AST only |
-| aliasing.cpp:141:15:141:15 | s | AST only |
-| aliasing.cpp:141:17:141:20 | data | AST only |
-| aliasing.cpp:147:15:147:22 | & ... | AST only |
-| aliasing.cpp:158:15:158:15 | s | AST only |
-| aliasing.cpp:158:17:158:20 | data | AST only |
-| aliasing.cpp:164:15:164:15 | s | AST only |
-| aliasing.cpp:164:17:164:20 | data | AST only |
-| aliasing.cpp:175:15:175:22 | & ... | AST only |
-| aliasing.cpp:175:16:175:17 | s2 | AST only |
-| aliasing.cpp:181:15:181:22 | & ... | AST only |
-| aliasing.cpp:181:16:181:17 | s2 | AST only |
-| aliasing.cpp:187:15:187:22 | & ... | AST only |
-| aliasing.cpp:187:16:187:17 | s2 | AST only |
-| aliasing.cpp:194:15:194:22 | & ... | AST only |
-| aliasing.cpp:194:16:194:17 | s2 | AST only |
-| aliasing.cpp:200:15:200:24 | & ... | AST only |
-| aliasing.cpp:200:16:200:18 | ps2 | AST only |
-| aliasing.cpp:205:15:205:24 | & ... | AST only |
-| aliasing.cpp:205:16:205:18 | ps2 | AST only |
| arrays.cpp:6:3:6:8 | access to array | AST only |
-| arrays.cpp:6:3:6:23 | arr | IR only |
+| arrays.cpp:7:3:7:6 | access to array | IR only |
+| arrays.cpp:8:3:8:6 | access to array | IR only |
+| arrays.cpp:9:3:9:6 | * ... | IR only |
+| arrays.cpp:10:3:10:6 | * ... | IR only |
| arrays.cpp:15:3:15:10 | * ... | AST only |
-| arrays.cpp:36:3:36:3 | o | AST only |
-| arrays.cpp:36:5:36:10 | nested | AST only |
+| arrays.cpp:16:3:16:6 | access to array | IR only |
+| arrays.cpp:17:3:17:6 | access to array | IR only |
| arrays.cpp:36:19:36:22 | data | AST only |
-| arrays.cpp:37:8:37:8 | o | AST only |
-| arrays.cpp:37:8:37:22 | access to array | AST only |
-| arrays.cpp:37:10:37:15 | nested | AST only |
-| arrays.cpp:37:24:37:27 | data | AST only |
-| arrays.cpp:38:8:38:8 | o | AST only |
-| arrays.cpp:38:8:38:22 | access to array | AST only |
-| arrays.cpp:38:10:38:15 | nested | AST only |
-| arrays.cpp:38:24:38:27 | data | AST only |
-| arrays.cpp:42:3:42:3 | o | AST only |
-| arrays.cpp:42:3:42:20 | access to array | AST only |
-| arrays.cpp:42:5:42:12 | indirect | AST only |
| arrays.cpp:42:22:42:25 | data | AST only |
-| arrays.cpp:43:8:43:8 | o | AST only |
-| arrays.cpp:43:8:43:25 | access to array | AST only |
-| arrays.cpp:43:10:43:17 | indirect | AST only |
-| arrays.cpp:43:27:43:30 | data | AST only |
-| arrays.cpp:44:8:44:8 | o | AST only |
-| arrays.cpp:44:8:44:25 | access to array | AST only |
-| arrays.cpp:44:10:44:17 | indirect | AST only |
-| arrays.cpp:44:27:44:30 | data | AST only |
-| arrays.cpp:48:3:48:3 | o | AST only |
-| arrays.cpp:48:3:48:20 | access to array | AST only |
-| arrays.cpp:48:5:48:12 | indirect | AST only |
| arrays.cpp:48:22:48:25 | data | AST only |
-| arrays.cpp:49:8:49:8 | o | AST only |
-| arrays.cpp:49:8:49:25 | access to array | AST only |
-| arrays.cpp:49:10:49:17 | indirect | AST only |
-| arrays.cpp:49:27:49:30 | data | AST only |
-| arrays.cpp:50:8:50:8 | o | AST only |
-| arrays.cpp:50:8:50:25 | access to array | AST only |
-| arrays.cpp:50:10:50:17 | indirect | AST only |
-| arrays.cpp:50:27:50:30 | data | AST only |
| by_reference.cpp:12:8:12:8 | a | AST only |
| by_reference.cpp:16:11:16:11 | a | AST only |
-| by_reference.cpp:20:5:20:8 | this | AST only |
-| by_reference.cpp:20:23:20:27 | value | AST only |
-| by_reference.cpp:24:19:24:22 | this | AST only |
-| by_reference.cpp:24:25:24:29 | value | AST only |
| by_reference.cpp:40:12:40:15 | this | AST only |
-| by_reference.cpp:50:3:50:3 | s | AST only |
-| by_reference.cpp:50:17:50:26 | call to user_input | AST only |
| by_reference.cpp:51:8:51:8 | s | AST only |
-| by_reference.cpp:51:10:51:20 | call to getDirectly | AST only |
-| by_reference.cpp:56:3:56:3 | s | AST only |
-| by_reference.cpp:56:19:56:28 | call to user_input | AST only |
| by_reference.cpp:57:8:57:8 | s | AST only |
-| by_reference.cpp:57:10:57:22 | call to getIndirectly | AST only |
-| by_reference.cpp:62:3:62:3 | s | AST only |
-| by_reference.cpp:62:25:62:34 | call to user_input | AST only |
| by_reference.cpp:63:8:63:8 | s | AST only |
-| by_reference.cpp:63:10:63:28 | call to getThroughNonMember | AST only |
-| by_reference.cpp:68:17:68:18 | & ... | AST only |
-| by_reference.cpp:68:21:68:30 | call to user_input | AST only |
-| by_reference.cpp:69:8:69:20 | call to nonMemberGetA | AST only |
| by_reference.cpp:84:10:84:10 | a | AST only |
| by_reference.cpp:88:9:88:9 | a | AST only |
| by_reference.cpp:92:3:92:5 | * ... | AST only |
| by_reference.cpp:96:3:96:4 | pa | AST only |
-| by_reference.cpp:102:21:102:39 | & ... | AST only |
-| by_reference.cpp:103:21:103:25 | outer | AST only |
-| by_reference.cpp:103:27:103:35 | inner_ptr | AST only |
-| by_reference.cpp:104:15:104:22 | & ... | AST only |
-| by_reference.cpp:106:21:106:41 | & ... | AST only |
-| by_reference.cpp:107:21:107:26 | pouter | AST only |
-| by_reference.cpp:107:29:107:37 | inner_ptr | AST only |
-| by_reference.cpp:108:15:108:24 | & ... | AST only |
-| by_reference.cpp:110:8:110:12 | outer | AST only |
-| by_reference.cpp:110:14:110:25 | inner_nested | AST only |
-| by_reference.cpp:110:27:110:27 | a | AST only |
-| by_reference.cpp:111:8:111:12 | outer | AST only |
-| by_reference.cpp:111:14:111:22 | inner_ptr | AST only |
-| by_reference.cpp:111:25:111:25 | a | AST only |
-| by_reference.cpp:112:8:112:12 | outer | AST only |
-| by_reference.cpp:112:14:112:14 | a | AST only |
-| by_reference.cpp:114:8:114:13 | pouter | AST only |
-| by_reference.cpp:114:16:114:27 | inner_nested | AST only |
-| by_reference.cpp:114:29:114:29 | a | AST only |
-| by_reference.cpp:115:8:115:13 | pouter | AST only |
-| by_reference.cpp:115:16:115:24 | inner_ptr | AST only |
-| by_reference.cpp:115:27:115:27 | a | AST only |
-| by_reference.cpp:116:8:116:13 | pouter | AST only |
-| by_reference.cpp:116:16:116:16 | a | AST only |
-| by_reference.cpp:122:27:122:38 | inner_nested | AST only |
-| by_reference.cpp:123:21:123:36 | * ... | AST only |
-| by_reference.cpp:123:22:123:26 | outer | AST only |
-| by_reference.cpp:124:21:124:21 | a | AST only |
-| by_reference.cpp:126:29:126:40 | inner_nested | AST only |
-| by_reference.cpp:127:21:127:38 | * ... | AST only |
-| by_reference.cpp:127:22:127:27 | pouter | AST only |
-| by_reference.cpp:128:23:128:23 | a | AST only |
-| by_reference.cpp:130:8:130:12 | outer | AST only |
-| by_reference.cpp:130:14:130:25 | inner_nested | AST only |
-| by_reference.cpp:130:27:130:27 | a | AST only |
-| by_reference.cpp:131:8:131:12 | outer | AST only |
-| by_reference.cpp:131:14:131:22 | inner_ptr | AST only |
-| by_reference.cpp:131:25:131:25 | a | AST only |
-| by_reference.cpp:132:8:132:12 | outer | AST only |
-| by_reference.cpp:132:14:132:14 | a | AST only |
-| by_reference.cpp:134:8:134:13 | pouter | AST only |
-| by_reference.cpp:134:16:134:27 | inner_nested | AST only |
-| by_reference.cpp:134:29:134:29 | a | AST only |
-| by_reference.cpp:135:8:135:13 | pouter | AST only |
-| by_reference.cpp:135:16:135:24 | inner_ptr | AST only |
-| by_reference.cpp:135:27:135:27 | a | AST only |
-| by_reference.cpp:136:8:136:13 | pouter | AST only |
-| by_reference.cpp:136:16:136:16 | a | AST only |
| complex.cpp:11:22:11:23 | a_ | AST only |
| complex.cpp:12:22:12:23 | b_ | AST only |
-| complex.cpp:42:8:42:8 | b | AST only |
-| complex.cpp:42:16:42:16 | f | AST only |
-| complex.cpp:43:8:43:8 | b | AST only |
-| complex.cpp:43:16:43:16 | f | AST only |
-| complex.cpp:53:3:53:4 | b1 | AST only |
-| complex.cpp:53:12:53:12 | f | AST only |
-| complex.cpp:54:3:54:4 | b2 | AST only |
-| complex.cpp:54:12:54:12 | f | AST only |
-| complex.cpp:55:3:55:4 | b3 | AST only |
-| complex.cpp:55:12:55:12 | f | AST only |
-| complex.cpp:56:3:56:4 | b3 | AST only |
-| complex.cpp:56:12:56:12 | f | AST only |
-| complex.cpp:59:7:59:8 | b1 | AST only |
-| complex.cpp:62:7:62:8 | b2 | AST only |
-| complex.cpp:65:7:65:8 | b3 | AST only |
-| complex.cpp:68:7:68:8 | b4 | AST only |
| conflated.cpp:10:3:10:7 | * ... | AST only |
-| conflated.cpp:10:4:10:5 | ra | AST only |
-| conflated.cpp:19:19:19:21 | raw | AST only |
-| conflated.cpp:20:8:20:10 | raw | AST only |
-| conflated.cpp:29:3:29:4 | pa | AST only |
| conflated.cpp:29:7:29:7 | x | AST only |
-| conflated.cpp:36:3:36:4 | pa | AST only |
| conflated.cpp:36:7:36:7 | x | AST only |
| conflated.cpp:53:7:53:10 | next | AST only |
-| conflated.cpp:54:3:54:4 | ll | AST only |
-| conflated.cpp:54:7:54:10 | next | AST only |
| conflated.cpp:54:13:54:13 | y | AST only |
-| conflated.cpp:59:35:59:38 | next | AST only |
-| conflated.cpp:60:3:60:4 | ll | AST only |
-| conflated.cpp:60:7:60:10 | next | AST only |
+| conflated.cpp:59:20:59:39 | new | IR only |
| conflated.cpp:60:13:60:13 | y | AST only |
| constructors.cpp:20:24:20:25 | a_ | AST only |
| constructors.cpp:21:24:21:25 | b_ | AST only |
-| constructors.cpp:28:10:28:10 | f | AST only |
-| constructors.cpp:29:10:29:10 | f | AST only |
-| constructors.cpp:40:9:40:9 | f | AST only |
-| constructors.cpp:43:9:43:9 | g | AST only |
-| constructors.cpp:46:9:46:9 | h | AST only |
-| constructors.cpp:49:9:49:9 | i | AST only |
| qualifiers.cpp:9:36:9:36 | a | AST only |
| qualifiers.cpp:12:56:12:56 | a | AST only |
| qualifiers.cpp:13:57:13:57 | a | AST only |
-| qualifiers.cpp:22:5:22:9 | outer | AST only |
-| qualifiers.cpp:22:11:22:18 | call to getInner | AST only |
| qualifiers.cpp:22:23:22:23 | a | AST only |
-| qualifiers.cpp:23:10:23:14 | outer | AST only |
-| qualifiers.cpp:23:16:23:20 | inner | AST only |
-| qualifiers.cpp:23:23:23:23 | a | AST only |
-| qualifiers.cpp:27:5:27:9 | outer | AST only |
-| qualifiers.cpp:27:11:27:18 | call to getInner | AST only |
-| qualifiers.cpp:27:28:27:37 | call to user_input | AST only |
-| qualifiers.cpp:28:10:28:14 | outer | AST only |
-| qualifiers.cpp:28:16:28:20 | inner | AST only |
-| qualifiers.cpp:28:23:28:23 | a | AST only |
-| qualifiers.cpp:32:17:32:21 | outer | AST only |
-| qualifiers.cpp:32:23:32:30 | call to getInner | AST only |
-| qualifiers.cpp:32:35:32:44 | call to user_input | AST only |
-| qualifiers.cpp:33:10:33:14 | outer | AST only |
-| qualifiers.cpp:33:16:33:20 | inner | AST only |
-| qualifiers.cpp:33:23:33:23 | a | AST only |
-| qualifiers.cpp:37:19:37:35 | * ... | AST only |
-| qualifiers.cpp:37:20:37:24 | outer | AST only |
-| qualifiers.cpp:37:38:37:47 | call to user_input | AST only |
-| qualifiers.cpp:38:10:38:14 | outer | AST only |
-| qualifiers.cpp:38:16:38:20 | inner | AST only |
-| qualifiers.cpp:38:23:38:23 | a | AST only |
-| qualifiers.cpp:42:6:42:22 | * ... | AST only |
-| qualifiers.cpp:42:7:42:11 | outer | AST only |
| qualifiers.cpp:42:25:42:25 | a | AST only |
-| qualifiers.cpp:43:10:43:14 | outer | AST only |
-| qualifiers.cpp:43:16:43:20 | inner | AST only |
-| qualifiers.cpp:43:23:43:23 | a | AST only |
-| qualifiers.cpp:47:6:47:11 | & ... | AST only |
-| qualifiers.cpp:47:15:47:22 | call to getInner | AST only |
| qualifiers.cpp:47:27:47:27 | a | AST only |
-| qualifiers.cpp:48:10:48:14 | outer | AST only |
-| qualifiers.cpp:48:16:48:20 | inner | AST only |
-| qualifiers.cpp:48:23:48:23 | a | AST only |
| realistic.cpp:26:5:26:10 | offset | AST only |
-| realistic.cpp:42:20:42:20 | o | AST only |
-| realistic.cpp:49:9:49:11 | foo | AST only |
| realistic.cpp:49:20:49:22 | baz | AST only |
-| realistic.cpp:53:9:53:11 | foo | AST only |
-| realistic.cpp:53:9:53:18 | access to array | AST only |
-| realistic.cpp:53:20:53:22 | baz | AST only |
-| realistic.cpp:53:25:53:33 | userInput | AST only |
| realistic.cpp:53:35:53:43 | bufferLen | AST only |
-| realistic.cpp:54:16:54:18 | foo | AST only |
-| realistic.cpp:54:16:54:25 | access to array | AST only |
-| realistic.cpp:54:27:54:29 | baz | AST only |
-| realistic.cpp:54:32:54:40 | userInput | AST only |
-| realistic.cpp:54:42:54:47 | buffer | AST only |
-| realistic.cpp:60:16:60:18 | dst | AST only |
-| realistic.cpp:61:21:61:23 | foo | AST only |
-| realistic.cpp:61:21:61:30 | access to array | AST only |
-| realistic.cpp:61:32:61:34 | baz | AST only |
-| realistic.cpp:61:37:61:45 | userInput | AST only |
-| realistic.cpp:61:47:61:55 | bufferLen | AST only |
-| realistic.cpp:65:21:65:23 | foo | AST only |
-| realistic.cpp:65:21:65:30 | access to array | AST only |
-| realistic.cpp:65:32:65:34 | baz | AST only |
-| realistic.cpp:65:37:65:45 | userInput | AST only |
-| realistic.cpp:65:47:65:52 | buffer | AST only |
-| realistic.cpp:66:21:66:23 | dst | AST only |
| simple.cpp:20:24:20:25 | a_ | AST only |
| simple.cpp:21:24:21:25 | b_ | AST only |
-| simple.cpp:28:10:28:10 | f | AST only |
-| simple.cpp:29:10:29:10 | f | AST only |
-| simple.cpp:39:5:39:5 | f | AST only |
-| simple.cpp:40:5:40:5 | g | AST only |
-| simple.cpp:41:5:41:5 | h | AST only |
-| simple.cpp:42:5:42:5 | h | AST only |
-| simple.cpp:45:9:45:9 | f | AST only |
-| simple.cpp:48:9:48:9 | g | AST only |
-| simple.cpp:51:9:51:9 | h | AST only |
-| simple.cpp:54:9:54:9 | i | AST only |
| simple.cpp:65:7:65:7 | i | AST only |
-| simple.cpp:83:9:83:10 | this | AST only |
| simple.cpp:83:12:83:13 | f1 | AST only |
-| simple.cpp:84:14:84:20 | this | AST only |
| simple.cpp:92:7:92:7 | i | AST only |
-| struct_init.c:15:8:15:9 | ab | AST only |
-| struct_init.c:15:12:15:12 | a | AST only |
-| struct_init.c:16:8:16:9 | ab | AST only |
-| struct_init.c:16:12:16:12 | b | AST only |
-| struct_init.c:22:8:22:9 | ab | AST only |
-| struct_init.c:22:11:22:11 | a | AST only |
-| struct_init.c:23:8:23:9 | ab | AST only |
-| struct_init.c:23:11:23:11 | b | AST only |
-| struct_init.c:24:10:24:12 | & ... | AST only |
-| struct_init.c:31:8:31:12 | outer | AST only |
-| struct_init.c:31:14:31:21 | nestedAB | AST only |
-| struct_init.c:31:23:31:23 | a | AST only |
-| struct_init.c:32:8:32:12 | outer | AST only |
-| struct_init.c:32:14:32:21 | nestedAB | AST only |
-| struct_init.c:32:23:32:23 | b | AST only |
-| struct_init.c:33:8:33:12 | outer | AST only |
-| struct_init.c:33:14:33:22 | pointerAB | AST only |
-| struct_init.c:33:25:33:25 | a | AST only |
-| struct_init.c:34:8:34:12 | outer | AST only |
-| struct_init.c:34:14:34:22 | pointerAB | AST only |
-| struct_init.c:34:25:34:25 | b | AST only |
-| struct_init.c:36:10:36:24 | & ... | AST only |
-| struct_init.c:46:10:46:14 | outer | AST only |
-| struct_init.c:46:16:46:24 | pointerAB | AST only |
diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected
index 294c46a5694..8ed0e3c4032 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected
+++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected
@@ -1,22 +1,208 @@
| A.cpp:25:7:25:10 | this |
| A.cpp:27:22:27:25 | this |
+| A.cpp:31:14:31:21 | new |
+| A.cpp:31:20:31:20 | c |
+| A.cpp:40:5:40:6 | cc |
+| A.cpp:40:15:40:21 | 0 |
+| A.cpp:41:5:41:6 | ct |
+| A.cpp:41:15:41:21 | new |
+| A.cpp:42:10:42:12 | & ... |
+| A.cpp:43:10:43:12 | & ... |
+| A.cpp:47:12:47:18 | new |
+| A.cpp:48:20:48:20 | c |
+| A.cpp:49:10:49:10 | b |
+| A.cpp:49:13:49:13 | c |
+| A.cpp:54:12:54:18 | new |
+| A.cpp:55:5:55:5 | b |
+| A.cpp:55:12:55:19 | new |
+| A.cpp:56:10:56:10 | b |
+| A.cpp:56:13:56:15 | call to get |
+| A.cpp:57:11:57:24 | new |
+| A.cpp:57:17:57:23 | new |
+| A.cpp:57:28:57:30 | call to get |
+| A.cpp:62:13:62:19 | new |
+| A.cpp:64:10:64:15 | this |
+| A.cpp:64:17:64:18 | b1 |
+| A.cpp:64:21:64:28 | new |
+| A.cpp:65:10:65:11 | b1 |
+| A.cpp:65:14:65:14 | c |
+| A.cpp:66:10:66:11 | b2 |
+| A.cpp:66:14:66:14 | c |
+| A.cpp:71:13:71:19 | new |
+| A.cpp:73:10:73:19 | this |
+| A.cpp:73:21:73:22 | b1 |
+| A.cpp:73:25:73:32 | new |
+| A.cpp:74:10:74:11 | b1 |
+| A.cpp:74:14:74:14 | c |
+| A.cpp:75:10:75:11 | b2 |
+| A.cpp:75:14:75:14 | c |
+| A.cpp:81:10:81:15 | this |
+| A.cpp:81:17:81:18 | b1 |
+| A.cpp:81:21:81:21 | c |
+| A.cpp:82:12:82:12 | this |
+| A.cpp:87:9:87:9 | this |
+| A.cpp:89:15:89:21 | new |
+| A.cpp:90:7:90:8 | b2 |
+| A.cpp:90:15:90:15 | c |
+| A.cpp:99:14:99:21 | new |
| A.cpp:100:5:100:6 | c1 |
+| A.cpp:101:5:101:6 | this |
+| A.cpp:101:8:101:9 | c1 |
+| A.cpp:107:12:107:13 | c1 |
+| A.cpp:107:16:107:16 | a |
+| A.cpp:116:12:116:19 | new |
+| A.cpp:120:12:120:13 | c1 |
+| A.cpp:120:16:120:16 | a |
+| A.cpp:126:5:126:5 | b |
+| A.cpp:126:12:126:18 | new |
+| A.cpp:130:12:130:18 | new |
+| A.cpp:131:5:131:6 | this |
+| A.cpp:131:8:131:8 | b |
+| A.cpp:132:10:132:10 | b |
+| A.cpp:132:13:132:13 | c |
| A.cpp:142:7:142:7 | b |
+| A.cpp:142:14:142:20 | new |
| A.cpp:143:7:143:10 | this |
+| A.cpp:143:25:143:31 | new |
+| A.cpp:150:12:150:18 | new |
+| A.cpp:151:12:151:24 | new |
+| A.cpp:151:18:151:18 | b |
+| A.cpp:151:21:151:21 | this |
+| A.cpp:152:10:152:10 | d |
+| A.cpp:152:13:152:13 | b |
+| A.cpp:153:10:153:10 | d |
+| A.cpp:153:13:153:13 | b |
+| A.cpp:153:16:153:16 | c |
+| A.cpp:154:10:154:10 | b |
+| A.cpp:154:13:154:13 | c |
+| A.cpp:159:12:159:18 | new |
+| A.cpp:160:18:160:60 | new |
+| A.cpp:160:29:160:29 | b |
+| A.cpp:160:32:160:59 | new |
+| A.cpp:160:43:160:49 | 0 |
+| A.cpp:160:52:160:58 | 0 |
+| A.cpp:161:18:161:40 | new |
+| A.cpp:161:29:161:35 | 0 |
+| A.cpp:161:38:161:39 | l1 |
+| A.cpp:162:18:162:40 | new |
+| A.cpp:162:29:162:35 | 0 |
+| A.cpp:162:38:162:39 | l2 |
+| A.cpp:163:10:163:11 | l3 |
+| A.cpp:163:14:163:17 | head |
+| A.cpp:164:10:164:11 | l3 |
+| A.cpp:164:14:164:17 | next |
+| A.cpp:164:20:164:23 | head |
+| A.cpp:165:10:165:11 | l3 |
+| A.cpp:165:14:165:17 | next |
+| A.cpp:165:20:165:23 | next |
+| A.cpp:165:26:165:29 | head |
+| A.cpp:166:10:166:11 | l3 |
+| A.cpp:166:14:166:17 | next |
+| A.cpp:166:20:166:23 | next |
+| A.cpp:166:26:166:29 | next |
+| A.cpp:166:32:166:35 | head |
+| A.cpp:169:12:169:12 | l |
+| A.cpp:169:15:169:18 | head |
| A.cpp:183:7:183:10 | this |
| A.cpp:184:7:184:10 | this |
+| B.cpp:7:16:7:35 | new |
+| B.cpp:7:25:7:25 | e |
+| B.cpp:7:28:7:34 | 0 |
+| B.cpp:8:16:8:27 | new |
+| B.cpp:8:25:8:26 | b1 |
+| B.cpp:9:10:9:11 | b2 |
+| B.cpp:9:14:9:17 | box1 |
+| B.cpp:9:20:9:24 | elem1 |
+| B.cpp:10:10:10:11 | b2 |
+| B.cpp:10:14:10:17 | box1 |
+| B.cpp:10:20:10:24 | elem2 |
+| B.cpp:16:16:16:38 | new |
+| B.cpp:16:28:16:34 | 0 |
+| B.cpp:16:37:16:37 | e |
+| B.cpp:17:16:17:27 | new |
+| B.cpp:17:25:17:26 | b1 |
+| B.cpp:18:10:18:11 | b2 |
+| B.cpp:18:14:18:17 | box1 |
+| B.cpp:18:20:18:24 | elem1 |
+| B.cpp:19:10:19:11 | b2 |
+| B.cpp:19:14:19:17 | box1 |
+| B.cpp:19:20:19:24 | elem2 |
| B.cpp:35:7:35:10 | this |
| B.cpp:36:7:36:10 | this |
| B.cpp:46:7:46:10 | this |
+| C.cpp:18:12:18:18 | new |
+| C.cpp:19:5:19:5 | c |
| C.cpp:24:5:24:8 | this |
+| C.cpp:29:10:29:11 | s1 |
+| C.cpp:29:10:29:11 | this |
+| C.cpp:30:10:30:11 | s2 |
+| C.cpp:30:10:30:11 | this |
+| C.cpp:31:10:31:11 | s3 |
+| C.cpp:31:10:31:11 | this |
+| C.cpp:32:10:32:11 | s4 |
| D.cpp:9:21:9:24 | this |
| D.cpp:11:29:11:32 | this |
| D.cpp:16:21:16:23 | this |
| D.cpp:18:29:18:31 | this |
+| D.cpp:22:10:22:11 | b2 |
+| D.cpp:22:14:22:20 | call to getBox1 |
+| D.cpp:22:25:22:31 | call to getElem |
+| D.cpp:29:15:29:41 | new |
+| D.cpp:29:24:29:40 | new |
+| D.cpp:29:33:29:39 | 0 |
+| D.cpp:30:5:30:5 | b |
+| D.cpp:30:8:30:10 | box |
+| D.cpp:31:14:31:14 | b |
+| D.cpp:36:15:36:41 | new |
+| D.cpp:36:24:36:40 | new |
+| D.cpp:36:33:36:39 | 0 |
+| D.cpp:37:5:37:5 | b |
+| D.cpp:37:8:37:10 | box |
+| D.cpp:37:21:37:21 | e |
+| D.cpp:38:14:38:14 | b |
+| D.cpp:43:15:43:41 | new |
+| D.cpp:43:24:43:40 | new |
+| D.cpp:43:33:43:39 | 0 |
+| D.cpp:44:5:44:5 | b |
+| D.cpp:44:8:44:14 | call to getBox1 |
+| D.cpp:45:14:45:14 | b |
+| D.cpp:50:15:50:41 | new |
+| D.cpp:50:24:50:40 | new |
+| D.cpp:50:33:50:39 | 0 |
+| D.cpp:51:5:51:5 | b |
+| D.cpp:51:8:51:14 | call to getBox1 |
+| D.cpp:51:27:51:27 | e |
+| D.cpp:52:14:52:14 | b |
| D.cpp:57:5:57:12 | this |
+| D.cpp:57:16:57:42 | new |
+| D.cpp:57:25:57:41 | new |
+| D.cpp:57:34:57:40 | 0 |
+| D.cpp:58:5:58:12 | boxfield |
+| D.cpp:58:5:58:12 | this |
+| D.cpp:58:15:58:17 | box |
+| D.cpp:59:5:59:7 | this |
+| D.cpp:64:10:64:17 | boxfield |
+| D.cpp:64:10:64:17 | this |
+| D.cpp:64:20:64:22 | box |
+| D.cpp:64:25:64:28 | elem |
+| E.cpp:21:10:21:10 | p |
+| E.cpp:21:13:21:16 | data |
+| E.cpp:21:18:21:23 | buffer |
+| E.cpp:28:21:28:23 | raw |
+| E.cpp:29:21:29:21 | b |
+| E.cpp:29:24:29:29 | buffer |
+| E.cpp:30:21:30:21 | p |
+| E.cpp:30:23:30:26 | data |
+| E.cpp:30:28:30:33 | buffer |
+| E.cpp:31:10:31:12 | raw |
+| E.cpp:32:10:32:10 | b |
+| E.cpp:32:13:32:18 | buffer |
+| E.cpp:33:18:33:19 | & ... |
| aliasing.cpp:9:3:9:3 | s |
| aliasing.cpp:13:3:13:3 | s |
| aliasing.cpp:17:3:17:3 | s |
+| aliasing.cpp:25:17:25:19 | & ... |
+| aliasing.cpp:26:19:26:20 | s2 |
| aliasing.cpp:37:3:37:6 | ref1 |
| aliasing.cpp:42:3:42:4 | s2 |
| aliasing.cpp:49:3:49:7 | copy1 |
@@ -25,48 +211,299 @@
| aliasing.cpp:72:3:72:3 | s |
| aliasing.cpp:79:3:79:3 | s |
| aliasing.cpp:86:3:86:3 | s |
+| aliasing.cpp:92:3:92:3 | w |
| aliasing.cpp:92:5:92:5 | s |
| aliasing.cpp:98:3:98:3 | s |
+| aliasing.cpp:111:15:111:19 | & ... |
| aliasing.cpp:111:16:111:16 | s |
+| aliasing.cpp:121:15:121:16 | xs |
+| aliasing.cpp:126:15:126:20 | ... - ... |
+| aliasing.cpp:131:15:131:16 | xs |
+| aliasing.cpp:136:15:136:17 | + ... |
+| aliasing.cpp:141:15:141:15 | s |
+| aliasing.cpp:141:17:141:20 | data |
+| aliasing.cpp:147:15:147:22 | & ... |
| aliasing.cpp:147:16:147:19 | access to array |
+| aliasing.cpp:158:15:158:15 | s |
+| aliasing.cpp:158:17:158:20 | data |
+| aliasing.cpp:164:15:164:15 | s |
+| aliasing.cpp:164:17:164:20 | data |
+| aliasing.cpp:175:15:175:22 | & ... |
+| aliasing.cpp:175:16:175:17 | s2 |
| aliasing.cpp:175:19:175:19 | s |
+| aliasing.cpp:181:15:181:22 | & ... |
+| aliasing.cpp:181:16:181:17 | s2 |
| aliasing.cpp:181:19:181:19 | s |
+| aliasing.cpp:187:15:187:22 | & ... |
+| aliasing.cpp:187:16:187:17 | s2 |
| aliasing.cpp:187:19:187:19 | s |
+| aliasing.cpp:194:15:194:22 | & ... |
+| aliasing.cpp:194:16:194:17 | s2 |
| aliasing.cpp:194:19:194:19 | s |
+| aliasing.cpp:200:15:200:24 | & ... |
+| aliasing.cpp:200:16:200:18 | ps2 |
| aliasing.cpp:200:21:200:21 | s |
+| aliasing.cpp:205:15:205:24 | & ... |
+| aliasing.cpp:205:16:205:18 | ps2 |
| aliasing.cpp:205:21:205:21 | s |
-| arrays.cpp:6:3:6:5 | arr |
+| arrays.cpp:7:8:7:13 | access to array |
+| arrays.cpp:8:8:8:13 | access to array |
+| arrays.cpp:9:8:9:11 | * ... |
+| arrays.cpp:10:8:10:15 | * ... |
+| arrays.cpp:16:8:16:13 | access to array |
+| arrays.cpp:17:8:17:13 | access to array |
+| arrays.cpp:36:3:36:3 | o |
| arrays.cpp:36:3:36:17 | access to array |
+| arrays.cpp:36:5:36:10 | nested |
+| arrays.cpp:37:8:37:8 | o |
+| arrays.cpp:37:8:37:22 | access to array |
+| arrays.cpp:37:10:37:15 | nested |
+| arrays.cpp:37:24:37:27 | data |
+| arrays.cpp:38:8:38:8 | o |
+| arrays.cpp:38:8:38:22 | access to array |
+| arrays.cpp:38:10:38:15 | nested |
+| arrays.cpp:38:24:38:27 | data |
+| arrays.cpp:42:3:42:3 | o |
+| arrays.cpp:42:3:42:20 | access to array |
+| arrays.cpp:42:5:42:12 | indirect |
+| arrays.cpp:43:8:43:8 | o |
+| arrays.cpp:43:8:43:25 | access to array |
+| arrays.cpp:43:10:43:17 | indirect |
+| arrays.cpp:43:27:43:30 | data |
+| arrays.cpp:44:8:44:8 | o |
+| arrays.cpp:44:8:44:25 | access to array |
+| arrays.cpp:44:10:44:17 | indirect |
+| arrays.cpp:44:27:44:30 | data |
+| arrays.cpp:48:3:48:3 | o |
+| arrays.cpp:48:3:48:20 | access to array |
+| arrays.cpp:48:5:48:12 | indirect |
+| arrays.cpp:49:8:49:8 | o |
+| arrays.cpp:49:8:49:25 | access to array |
+| arrays.cpp:49:10:49:17 | indirect |
+| arrays.cpp:49:27:49:30 | data |
+| arrays.cpp:50:8:50:8 | o |
+| arrays.cpp:50:8:50:25 | access to array |
+| arrays.cpp:50:10:50:17 | indirect |
+| arrays.cpp:50:27:50:30 | data |
| by_reference.cpp:12:5:12:5 | s |
| by_reference.cpp:16:5:16:8 | this |
+| by_reference.cpp:20:5:20:8 | this |
+| by_reference.cpp:20:23:20:27 | value |
+| by_reference.cpp:24:19:24:22 | this |
+| by_reference.cpp:24:25:24:29 | value |
+| by_reference.cpp:50:3:50:3 | s |
+| by_reference.cpp:50:17:50:26 | call to user_input |
+| by_reference.cpp:51:10:51:20 | call to getDirectly |
+| by_reference.cpp:56:3:56:3 | s |
+| by_reference.cpp:56:19:56:28 | call to user_input |
+| by_reference.cpp:57:10:57:22 | call to getIndirectly |
+| by_reference.cpp:62:3:62:3 | s |
+| by_reference.cpp:62:25:62:34 | call to user_input |
+| by_reference.cpp:63:10:63:28 | call to getThroughNonMember |
+| by_reference.cpp:68:17:68:18 | & ... |
+| by_reference.cpp:68:21:68:30 | call to user_input |
+| by_reference.cpp:69:8:69:20 | call to nonMemberGetA |
| by_reference.cpp:84:3:84:7 | inner |
| by_reference.cpp:88:3:88:7 | inner |
+| by_reference.cpp:102:21:102:39 | & ... |
| by_reference.cpp:102:22:102:26 | outer |
+| by_reference.cpp:103:21:103:25 | outer |
+| by_reference.cpp:103:27:103:35 | inner_ptr |
+| by_reference.cpp:104:15:104:22 | & ... |
| by_reference.cpp:104:16:104:20 | outer |
+| by_reference.cpp:106:21:106:41 | & ... |
| by_reference.cpp:106:22:106:27 | pouter |
+| by_reference.cpp:107:21:107:26 | pouter |
+| by_reference.cpp:107:29:107:37 | inner_ptr |
+| by_reference.cpp:108:15:108:24 | & ... |
| by_reference.cpp:108:16:108:21 | pouter |
+| by_reference.cpp:110:8:110:12 | outer |
+| by_reference.cpp:110:14:110:25 | inner_nested |
+| by_reference.cpp:110:27:110:27 | a |
+| by_reference.cpp:111:8:111:12 | outer |
+| by_reference.cpp:111:14:111:22 | inner_ptr |
+| by_reference.cpp:111:25:111:25 | a |
+| by_reference.cpp:112:8:112:12 | outer |
+| by_reference.cpp:112:14:112:14 | a |
+| by_reference.cpp:114:8:114:13 | pouter |
+| by_reference.cpp:114:16:114:27 | inner_nested |
+| by_reference.cpp:114:29:114:29 | a |
+| by_reference.cpp:115:8:115:13 | pouter |
+| by_reference.cpp:115:16:115:24 | inner_ptr |
+| by_reference.cpp:115:27:115:27 | a |
+| by_reference.cpp:116:8:116:13 | pouter |
+| by_reference.cpp:116:16:116:16 | a |
| by_reference.cpp:122:21:122:25 | outer |
+| by_reference.cpp:122:27:122:38 | inner_nested |
+| by_reference.cpp:123:21:123:36 | * ... |
+| by_reference.cpp:123:22:123:26 | outer |
| by_reference.cpp:124:15:124:19 | outer |
+| by_reference.cpp:124:21:124:21 | a |
| by_reference.cpp:126:21:126:26 | pouter |
+| by_reference.cpp:126:29:126:40 | inner_nested |
+| by_reference.cpp:127:21:127:38 | * ... |
+| by_reference.cpp:127:22:127:27 | pouter |
| by_reference.cpp:128:15:128:20 | pouter |
+| by_reference.cpp:128:23:128:23 | a |
+| by_reference.cpp:130:8:130:12 | outer |
+| by_reference.cpp:130:14:130:25 | inner_nested |
+| by_reference.cpp:130:27:130:27 | a |
+| by_reference.cpp:131:8:131:12 | outer |
+| by_reference.cpp:131:14:131:22 | inner_ptr |
+| by_reference.cpp:131:25:131:25 | a |
+| by_reference.cpp:132:8:132:12 | outer |
+| by_reference.cpp:132:14:132:14 | a |
+| by_reference.cpp:134:8:134:13 | pouter |
+| by_reference.cpp:134:16:134:27 | inner_nested |
+| by_reference.cpp:134:29:134:29 | a |
+| by_reference.cpp:135:8:135:13 | pouter |
+| by_reference.cpp:135:16:135:24 | inner_ptr |
+| by_reference.cpp:135:27:135:27 | a |
+| by_reference.cpp:136:8:136:13 | pouter |
+| by_reference.cpp:136:16:136:16 | a |
| complex.cpp:11:22:11:23 | this |
| complex.cpp:12:22:12:23 | this |
+| complex.cpp:42:8:42:8 | b |
| complex.cpp:42:10:42:14 | inner |
+| complex.cpp:42:16:42:16 | f |
+| complex.cpp:43:8:43:8 | b |
| complex.cpp:43:10:43:14 | inner |
+| complex.cpp:43:16:43:16 | f |
+| complex.cpp:53:3:53:4 | b1 |
| complex.cpp:53:6:53:10 | inner |
+| complex.cpp:53:12:53:12 | f |
+| complex.cpp:54:3:54:4 | b2 |
| complex.cpp:54:6:54:10 | inner |
+| complex.cpp:54:12:54:12 | f |
+| complex.cpp:55:3:55:4 | b3 |
| complex.cpp:55:6:55:10 | inner |
+| complex.cpp:55:12:55:12 | f |
+| complex.cpp:56:3:56:4 | b3 |
| complex.cpp:56:6:56:10 | inner |
+| complex.cpp:56:12:56:12 | f |
+| complex.cpp:59:7:59:8 | b1 |
+| complex.cpp:62:7:62:8 | b2 |
+| complex.cpp:65:7:65:8 | b3 |
+| complex.cpp:68:7:68:8 | b4 |
+| conflated.cpp:10:4:10:5 | ra |
+| conflated.cpp:19:19:19:21 | raw |
+| conflated.cpp:20:8:20:10 | raw |
+| conflated.cpp:29:3:29:4 | pa |
+| conflated.cpp:36:3:36:4 | pa |
| conflated.cpp:53:3:53:4 | ll |
+| conflated.cpp:54:3:54:4 | ll |
+| conflated.cpp:54:7:54:10 | next |
+| conflated.cpp:59:20:59:39 | new |
+| conflated.cpp:59:35:59:38 | next |
+| conflated.cpp:60:3:60:4 | ll |
+| conflated.cpp:60:7:60:10 | next |
| constructors.cpp:20:24:20:25 | this |
| constructors.cpp:21:24:21:25 | this |
+| constructors.cpp:28:10:28:10 | f |
+| constructors.cpp:29:10:29:10 | f |
+| constructors.cpp:40:9:40:9 | f |
+| constructors.cpp:43:9:43:9 | g |
+| constructors.cpp:46:9:46:9 | h |
+| constructors.cpp:49:9:49:9 | i |
| qualifiers.cpp:9:30:9:33 | this |
| qualifiers.cpp:12:49:12:53 | inner |
| qualifiers.cpp:13:51:13:55 | inner |
+| qualifiers.cpp:22:5:22:9 | outer |
+| qualifiers.cpp:22:11:22:18 | call to getInner |
+| qualifiers.cpp:23:10:23:14 | outer |
+| qualifiers.cpp:23:16:23:20 | inner |
+| qualifiers.cpp:23:23:23:23 | a |
+| qualifiers.cpp:27:5:27:9 | outer |
+| qualifiers.cpp:27:11:27:18 | call to getInner |
+| qualifiers.cpp:27:28:27:37 | call to user_input |
+| qualifiers.cpp:28:10:28:14 | outer |
+| qualifiers.cpp:28:16:28:20 | inner |
+| qualifiers.cpp:28:23:28:23 | a |
+| qualifiers.cpp:32:17:32:21 | outer |
+| qualifiers.cpp:32:23:32:30 | call to getInner |
+| qualifiers.cpp:32:35:32:44 | call to user_input |
+| qualifiers.cpp:33:10:33:14 | outer |
+| qualifiers.cpp:33:16:33:20 | inner |
+| qualifiers.cpp:33:23:33:23 | a |
+| qualifiers.cpp:37:19:37:35 | * ... |
+| qualifiers.cpp:37:20:37:24 | outer |
+| qualifiers.cpp:37:38:37:47 | call to user_input |
+| qualifiers.cpp:38:10:38:14 | outer |
+| qualifiers.cpp:38:16:38:20 | inner |
+| qualifiers.cpp:38:23:38:23 | a |
+| qualifiers.cpp:42:6:42:22 | * ... |
+| qualifiers.cpp:42:7:42:11 | outer |
+| qualifiers.cpp:43:10:43:14 | outer |
+| qualifiers.cpp:43:16:43:20 | inner |
+| qualifiers.cpp:43:23:43:23 | a |
+| qualifiers.cpp:47:6:47:11 | & ... |
+| qualifiers.cpp:47:15:47:22 | call to getInner |
+| qualifiers.cpp:48:10:48:14 | outer |
+| qualifiers.cpp:48:16:48:20 | inner |
+| qualifiers.cpp:48:23:48:23 | a |
+| realistic.cpp:42:20:42:20 | o |
+| realistic.cpp:49:9:49:11 | foo |
| realistic.cpp:49:9:49:18 | access to array |
+| realistic.cpp:53:9:53:11 | foo |
+| realistic.cpp:53:9:53:18 | access to array |
+| realistic.cpp:53:20:53:22 | baz |
+| realistic.cpp:53:25:53:33 | userInput |
+| realistic.cpp:54:16:54:18 | foo |
+| realistic.cpp:54:16:54:25 | access to array |
+| realistic.cpp:54:27:54:29 | baz |
+| realistic.cpp:54:32:54:40 | userInput |
+| realistic.cpp:54:42:54:47 | buffer |
+| realistic.cpp:60:16:60:18 | dst |
+| realistic.cpp:61:21:61:23 | foo |
+| realistic.cpp:61:21:61:30 | access to array |
+| realistic.cpp:61:32:61:34 | baz |
+| realistic.cpp:61:37:61:45 | userInput |
+| realistic.cpp:61:47:61:55 | bufferLen |
+| realistic.cpp:65:21:65:23 | foo |
+| realistic.cpp:65:21:65:30 | access to array |
+| realistic.cpp:65:32:65:34 | baz |
+| realistic.cpp:65:37:65:45 | userInput |
+| realistic.cpp:65:47:65:52 | buffer |
+| realistic.cpp:66:21:66:23 | dst |
| simple.cpp:20:24:20:25 | this |
| simple.cpp:21:24:21:25 | this |
+| simple.cpp:28:10:28:10 | f |
+| simple.cpp:29:10:29:10 | f |
+| simple.cpp:39:5:39:5 | f |
+| simple.cpp:40:5:40:5 | g |
+| simple.cpp:41:5:41:5 | h |
+| simple.cpp:42:5:42:5 | h |
+| simple.cpp:45:9:45:9 | f |
+| simple.cpp:48:9:48:9 | g |
+| simple.cpp:51:9:51:9 | h |
+| simple.cpp:54:9:54:9 | i |
| simple.cpp:65:5:65:5 | a |
| simple.cpp:83:9:83:10 | f2 |
+| simple.cpp:83:9:83:10 | this |
+| simple.cpp:84:14:84:20 | this |
| simple.cpp:92:5:92:5 | a |
+| struct_init.c:15:8:15:9 | ab |
+| struct_init.c:15:12:15:12 | a |
+| struct_init.c:16:8:16:9 | ab |
+| struct_init.c:16:12:16:12 | b |
+| struct_init.c:22:8:22:9 | ab |
+| struct_init.c:22:11:22:11 | a |
+| struct_init.c:23:8:23:9 | ab |
+| struct_init.c:23:11:23:11 | b |
+| struct_init.c:24:10:24:12 | & ... |
+| struct_init.c:31:8:31:12 | outer |
+| struct_init.c:31:14:31:21 | nestedAB |
+| struct_init.c:31:23:31:23 | a |
+| struct_init.c:32:8:32:12 | outer |
+| struct_init.c:32:14:32:21 | nestedAB |
+| struct_init.c:32:23:32:23 | b |
+| struct_init.c:33:8:33:12 | outer |
+| struct_init.c:33:14:33:22 | pointerAB |
+| struct_init.c:33:25:33:25 | a |
+| struct_init.c:34:8:34:12 | outer |
+| struct_init.c:34:14:34:22 | pointerAB |
+| struct_init.c:34:25:34:25 | b |
+| struct_init.c:36:10:36:24 | & ... |
| struct_init.c:36:11:36:15 | outer |
+| struct_init.c:46:10:46:14 | outer |
+| struct_init.c:46:16:46:24 | pointerAB |
diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected
index 0a3ea441f93..373e357142a 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected
+++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected
@@ -107,6 +107,10 @@
| C.cpp:19:5:19:5 | c |
| C.cpp:24:5:24:8 | this |
| C.cpp:24:11:24:12 | s3 |
+| C.cpp:29:10:29:11 | s1 |
+| C.cpp:29:10:29:11 | this |
+| C.cpp:31:10:31:11 | s3 |
+| C.cpp:31:10:31:11 | this |
| D.cpp:9:21:9:24 | elem |
| D.cpp:9:21:9:24 | this |
| D.cpp:11:29:11:32 | elem |
From 8caff411387b3f64a57a8fbd55658827986e77a4 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Wed, 20 Oct 2021 15:22:51 +0100
Subject: [PATCH 196/471] C++: Throw away most of the usage of IR-computed
def-use information. Instead, we rely on the shared SSA library's use-use
edges.
---
.../ir/dataflow/internal/DataFlowPrivate.qll | 6 +-
.../cpp/ir/dataflow/internal/DataFlowUtil.qll | 130 ++++++++----------
2 files changed, 62 insertions(+), 74 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
index 9de1da31772..8852d54d7b8 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
@@ -106,11 +106,9 @@ class ReturnNode extends InstructionNode {
Instruction primary;
ReturnNode() {
- exists(ReturnValueInstruction ret | instr = ret.getReturnValue() and primary = ret)
+ exists(ReturnValueInstruction ret | instr = ret and primary = ret)
or
- exists(ReturnIndirectionInstruction rii |
- instr = rii.getSideEffectOperand().getAnyDef() and primary = rii
- )
+ exists(ReturnIndirectionInstruction rii | instr = rii and primary = rii)
}
/** Gets the kind of this returned value. */
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
index f8b4f3b8df0..a075831439e 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
@@ -613,6 +613,11 @@ class VariableNode extends Node, TVariableNode {
*/
InstructionNode instructionNode(Instruction instr) { result.getInstruction() = instr }
+/**
+ * Gets the node corresponding to `operand`.
+ */
+OperandNode operandNode(Operand operand) { result.getOperand() = operand }
+
/**
* DEPRECATED: use `definitionByReferenceNodeFromArgument` instead.
*
@@ -693,14 +698,32 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
ReadNodeFlow::flowThrough(nodeFrom, nodeTo)
or
ReadNodeFlow::flowOutOf(nodeFrom, nodeTo)
+ or
+ // Adjacent-def-use and adjacent-use-use flow
+ adjacentDefUseFlow(nodeFrom, nodeTo)
}
-pragma[noinline]
-private predicate getFieldSizeOfClass(Class c, Type type, int size) {
- exists(Field f |
- f.getDeclaringType() = c and
- f.getUnderlyingType() = type and
- type.getSize() = size
+private predicate adjacentDefUseFlow(Node nodeFrom, Node nodeTo) {
+ // Flow that isn't already covered by field flow out of store/read nodes.
+ not nodeFrom.asInstruction() = any(StoreNode pun).getStoreInstruction() and
+ not nodeFrom.asInstruction() = any(ReadNode pun).getALoadInstruction() and
+ (
+ //Def-use flow
+ Ssa::ssaFlow(nodeFrom, nodeTo)
+ or
+ exists(Instruction loadAddress | loadAddress = Ssa::getSourceAddressFromNode(nodeFrom) |
+ // Use-use flow through reads
+ exists(Node address |
+ Ssa::addressFlowTC(address.asInstruction(), loadAddress) and
+ Ssa::ssaFlow(address, nodeTo)
+ )
+ or
+ // Use-use flow through stores.
+ exists(Node store |
+ Ssa::explicitWrite(_, store.asInstruction(), loadAddress) and
+ Ssa::ssaFlow(store, nodeTo)
+ )
+ )
)
}
@@ -789,40 +812,39 @@ private module StoreNodeFlow {
private predicate simpleOperandLocalFlowStep(Instruction iFrom, Operand opTo) {
// Propagate flow from an instruction to its exact uses.
+ // We do this for all instruction/operand pairs, except when the operand is the
+ // side effect operand of a ReturnIndirectionInstruction, or the load operand of a LoadInstruction.
+ // This is because we get these flows through the shared SSA library already, and including this
+ // flow here will create multiple dataflow paths which creates a blowup in stage 3 of dataflow.
+ (
+ not any(ReturnIndirectionInstruction ret).getSideEffectOperand() = opTo and
+ not any(LoadInstruction load).getSourceValueOperand() = opTo and
+ not any(ReturnValueInstruction ret).getReturnValueOperand() = opTo
+ ) and
opTo.getDef() = iFrom
- or
- opTo = any(ReadSideEffectInstruction read).getSideEffectOperand() and
- not iFrom.isResultConflated() and
- iFrom = opTo.getAnyDef()
- or
- // Loading a single `int` from an `int *` parameter is not an exact load since
- // the parameter may point to an entire array rather than a single `int`. The
- // following rule ensures that any flow going into the
- // `InitializeIndirectionInstruction`, even if it's for a different array
- // element, will propagate to a load of the first element.
- //
- // Since we're linking `InitializeIndirectionInstruction` and
- // `LoadInstruction` together directly, this rule will break if there's any
- // reassignment of the parameter indirection, including a conditional one that
- // leads to a phi node.
- exists(InitializeIndirectionInstruction init |
- iFrom = init and
- opTo.(LoadOperand).getAnyDef() = init and
- // Check that the types match. Otherwise we can get flow from an object to
- // its fields, which leads to field conflation when there's flow from other
- // fields to the object elsewhere.
- init.getParameter().getType().getUnspecifiedType().(DerivedType).getBaseType() =
- opTo.getType().getUnspecifiedType()
- )
- or
- // Flow from stores to structs with a single field to a load of that field.
- exists(LoadInstruction load |
- load.getSourceValueOperand() = opTo and
- opTo.getAnyDef() = iFrom and
- isSingleFieldClass(pragma[only_bind_out](pragma[only_bind_out](iFrom).getResultType()), opTo)
+}
+
+pragma[noinline]
+private predicate getAddressType(LoadInstruction load, Type t) {
+ exists(Instruction address |
+ address = load.getSourceAddress() and
+ t = address.getResultType()
)
}
+/**
+ * Like the AST dataflow library, we want to conflate the address and value of a reference. This class
+ * represents the `LoadInstruction` that is generated from a reference dereference.
+ */
+private class ReferenceDereferenceInstruction extends LoadInstruction {
+ ReferenceDereferenceInstruction() {
+ exists(ReferenceType ref |
+ getAddressType(this, ref) and
+ this.getResultType() = ref.getBaseType()
+ )
+ }
+}
+
private predicate simpleInstructionLocalFlowStep(Operand opFrom, Instruction iTo) {
iTo.(CopyInstruction).getSourceValueOperand() = opFrom
or
@@ -835,40 +857,8 @@ private predicate simpleInstructionLocalFlowStep(Operand opFrom, Instruction iTo
or
iTo.(InheritanceConversionInstruction).getUnaryOperand() = opFrom
or
- // A chi instruction represents a point where a new value (the _partial_
- // operand) may overwrite an old value (the _total_ operand), but the alias
- // analysis couldn't determine that it surely will overwrite every bit of it or
- // that it surely will overwrite no bit of it.
- //
- // By allowing flow through the total operand, we ensure that flow is not lost
- // due to shortcomings of the alias analysis. We may get false flow in cases
- // where the data is indeed overwritten.
- //
- // Flow through the partial operand belongs in the taint-tracking libraries
- // for now.
- iTo.getAnOperand().(ChiTotalOperand) = opFrom
- or
- // Add flow from write side-effects to non-conflated chi instructions through their
- // partial operands. From there, a `readStep` will find subsequent reads of that field.
- // Consider the following example:
- // ```
- // void setX(Point* p, int new_x) {
- // p->x = new_x;
- // }
- // ...
- // setX(&p, taint());
- // ```
- // Here, a `WriteSideEffectInstruction` will provide a new definition for `p->x` after the call to
- // `setX`, which will be melded into `p` through a chi instruction.
- exists(ChiInstruction chi | chi = iTo |
- opFrom.getAnyDef() instanceof WriteSideEffectInstruction and
- chi.getPartialOperand() = opFrom and
- not chi.isResultConflated() and
- // In a call such as `set_value(&x->val);` we don't want the memory representing `x` to receive
- // dataflow by a simple step. Instead, this is handled by field flow. If we add a simple step here
- // we can get field-to-object flow.
- not chi.isPartialUpdate()
- )
+ // Conflate references and values like in AST dataflow.
+ iTo.(ReferenceDereferenceInstruction).getSourceAddressOperand() = opFrom
or
// Flow through modeled functions
modelFlow(opFrom, iTo)
From 710d0cfc3df64bb9a4dfc9f6d59e67dd00eab83d Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Wed, 20 Oct 2021 15:29:47 +0100
Subject: [PATCH 197/471] C++: Since we now no longer have flow from exact
memory operands to LoadInstructions, we no longer have flow from
PhiInstructions to LoadInstructions. We could allow flow in this particular
case, but we might as well use the shared SSA library's phi edges.
---
.../cpp/ir/dataflow/internal/DataFlowUtil.qll | 41 ++++++++++++++++++-
.../code/cpp/ir/dataflow/internal/Ssa.qll | 32 +++++++++++++++
2 files changed, 72 insertions(+), 1 deletion(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
index a075831439e..076ee494bc5 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
@@ -22,7 +22,8 @@ private module Cached {
TVariableNode(Variable var) or
TStoreNodeInstr(Instruction i) { Ssa::explicitWrite(_, _, i) } or
TStoreNodeOperand(ArgumentOperand op) { Ssa::explicitWrite(_, _, op.getDef()) } or
- TReadNode(Instruction i) { needsPostReadNode(i) }
+ TReadNode(Instruction i) { needsPostReadNode(i) } or
+ TSsaPhiNode(Ssa::PhiNode phi)
cached
predicate localFlowStepCached(Node nodeFrom, Node nodeTo) {
@@ -347,6 +348,44 @@ class ReadNode extends Node, TReadNode {
}
}
+/**
+ * INTERNAL: do not use.
+ *
+ * A phi node produced by the shared SSA library, viewed as a node in a data flow graph.
+ */
+class SsaPhiNode extends Node, TSsaPhiNode {
+ Ssa::PhiNode phi;
+
+ SsaPhiNode() { this = TSsaPhiNode(phi) }
+
+ /* Get the phi node associated with this node. */
+ Ssa::PhiNode getPhiNode() { result = phi }
+
+ override Declaration getEnclosingCallable() { result = this.getFunction() }
+
+ override Function getFunction() { result = phi.getBasicBlock().getEnclosingFunction() }
+
+ override IRType getType() { result instanceof IRVoidType }
+
+ override Location getLocation() { result = phi.getBasicBlock().getLocation() }
+
+ /** Holds if this phi node has input from the `rnk`'th write operation in block `block`. */
+ final predicate hasInputAtRankInBlock(IRBlock block, int rnk) {
+ hasInputAtRankInBlock(block, rnk, _)
+ }
+
+ /**
+ * Holds if this phi node has input from the definition `input` (which is the `rnk`'th write
+ * operation in block `block`).
+ */
+ cached
+ final predicate hasInputAtRankInBlock(IRBlock block, int rnk, Ssa::Definition input) {
+ Ssa::phiHasInputFromBlock(phi, input, _) and input.definesAt(_, block, rnk)
+ }
+
+ override string toString() { result = "Phi" }
+}
+
/**
* An expression, viewed as a node in a data flow graph.
*/
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
index bb6e3addf81..04ae62dbc35 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
@@ -336,6 +336,34 @@ private module Cached {
)
}
+ private predicate fromPhiNode(SsaPhiNode nodeFrom, Node nodeTo) {
+ exists(PhiNode phi, Use use, IRBlock block, int rnk |
+ phi = nodeFrom.getPhiNode() and
+ adjacentDefRead(phi, _, _, block, rnk) and
+ use.hasRankInBlock(block, rnk) and
+ flowOutOfAddressStep(use.getOperand(), nodeTo)
+ )
+ }
+
+ private predicate toPhiNode(Node nodeFrom, SsaPhiNode nodeTo) {
+ // Flow to phi nodes
+ exists(Def def, IRBlock block, int rnk |
+ def.hasRankInBlock(block, rnk) and
+ nodeTo.hasInputAtRankInBlock(block, rnk)
+ |
+ exists(StoreNode store |
+ store = nodeFrom and
+ store.isTerminal() and
+ def.getInstruction() = store.getStoreInstruction()
+ )
+ or
+ def.getInstruction() = nodeFrom.asInstruction()
+ )
+ or
+ // Phi -> phi flow
+ nodeTo.hasInputAtRankInBlock(_, _, nodeFrom.(SsaPhiNode).getPhiNode())
+ }
+
/**
* Holds if `nodeFrom` is a read or write, and `nTo` is the next subsequent read of the variable
* written (or read) by `storeOrRead`.
@@ -350,6 +378,10 @@ private module Cached {
or
// Use-use flow from a `ReadNode` to an `OperandNode`
fromReadNode(nodeFrom, nodeTo)
+ or
+ fromPhiNode(nodeFrom, nodeTo)
+ or
+ toPhiNode(nodeFrom, nodeTo)
}
private predicate flowOutOfAddressStep(Operand operand, Node nTo) {
From b1ea00fa85633f7c891b97a674002d91d27b8d0d Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Wed, 20 Oct 2021 15:34:26 +0100
Subject: [PATCH 198/471] C++: Remove the taintflow edges that gives
performance problems.
---
.../code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll | 4 ----
1 file changed, 4 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
index f563e47db9f..287958ebea4 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
@@ -44,8 +44,6 @@ private predicate instructionToOperandTaintStep(Instruction fromInstr, Operand t
fromInstr = readInstr.getArgumentDef() and
toOperand = readInstr.getSideEffectOperand()
)
- or
- toOperand.(LoadOperand).getAnyDef() = fromInstr
}
/**
@@ -84,8 +82,6 @@ private predicate operandToInstructionTaintStep(Operand opFrom, Instruction inst
instrTo.(FieldAddressInstruction).getField().getDeclaringType() instanceof Union
)
or
- instrTo.(LoadInstruction).getSourceAddressOperand() = opFrom
- or
// Flow from an element to an array or union that contains it.
instrTo.(ChiInstruction).getPartialOperand() = opFrom and
not instrTo.isResultConflated() and
From 5dbaea8b52b2eaca2acd71ea4fcee125447538c6 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Wed, 20 Oct 2021 15:35:34 +0100
Subject: [PATCH 199/471] C++: Add a special dataflow step from
InitializeIndirection instructions.
---
.../code/cpp/ir/dataflow/internal/Ssa.qll | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
index 04ae62dbc35..aba581452ea 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
@@ -382,6 +382,24 @@ private module Cached {
fromPhiNode(nodeFrom, nodeTo)
or
toPhiNode(nodeFrom, nodeTo)
+ or
+ // When we want to transfer flow out of a `StoreNode` we perform two steps:
+ // 1. Find the next use of the address being stored to
+ // 2. Find the `LoadInstruction` that loads the address
+ // When the address being stored into doesn't have a `LoadInstruction` associated with it because it's
+ // passed into a `CallInstruction` we transfer flow to the `ReadSideEffect`, which will then flow into
+ // the callee. We then pickup the flow from the `InitializeIndirectionInstruction` and use the shared
+ // SSA library to determine where the next use of the address that received the flow is.
+ exists(Node init, Node mid |
+ nodeFrom.asInstruction().(InitializeIndirectionInstruction).getIRVariable() =
+ init.asInstruction().(InitializeParameterInstruction).getIRVariable() and
+ // No need for the flow if the next use is the instruction that returns the flow out of the callee.
+ not mid.asInstruction() instanceof ReturnIndirectionInstruction and
+ // Find the next use of the address
+ ssaFlow(init, mid) and
+ // And flow to the next load of that address
+ flowOutOfAddressStep([mid.asInstruction().getAUse(), mid.asOperand()], nodeTo)
+ )
}
private predicate flowOutOfAddressStep(Operand operand, Node nTo) {
From 3efe60fdd2010255fac324ec467074d6f04b1457 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Wed, 20 Oct 2021 15:46:16 +0100
Subject: [PATCH 200/471] C++: Accept test changes.
---
.../defaulttainttracking.cpp | 8 +-
.../annotate_sinks_only/stl.cpp | 4 +-
.../dataflow-ir-consistency.expected | 635 ++++-
.../dataflow/dataflow-tests/lambdas.cpp | 2 +-
.../dataflow/dataflow-tests/test.cpp | 20 +-
.../test/library-tests/dataflow/fields/A.cpp | 10 +-
.../test/library-tests/dataflow/fields/C.cpp | 8 +-
.../test/library-tests/dataflow/fields/D.cpp | 4 +-
.../test/library-tests/dataflow/fields/E.cpp | 6 +-
.../dataflow/fields/IRConfiguration.qll | 2 +-
.../dataflow/fields/aliasing.cpp | 22 +-
.../library-tests/dataflow/fields/arrays.cpp | 14 +-
.../dataflow/fields/by_reference.cpp | 8 +-
.../dataflow/fields/conflated.cpp | 12 +-
.../fields/dataflow-consistency.expected | 4 +-
.../fields/dataflow-ir-consistency.expected | 1439 +++++++++--
.../dataflow/fields/ir-path-flow.expected | 2097 +++++++++++++----
.../dataflow/fields/qualifiers.cpp | 12 +-
.../dataflow/fields/realistic.cpp | 2 +-
.../security-taint/tainted_diff.expected | 4 +
.../security-taint/tainted_ir.expected | 4 -
.../dataflow/smart-pointers-taint/test.cpp | 8 +-
.../dataflow/taint-tests/arrayassignment.cpp | 22 +-
.../dataflow/taint-tests/map.cpp | 72 +-
.../dataflow/taint-tests/set.cpp | 16 +-
.../dataflow/taint-tests/smart_pointer.cpp | 2 +-
.../dataflow/taint-tests/string.cpp | 16 +-
.../dataflow/taint-tests/stringstream.cpp | 10 +-
.../dataflow/taint-tests/taint.cpp | 26 +-
.../dataflow/taint-tests/taint.ql | 6 +-
.../dataflow/taint-tests/vector.cpp | 58 +-
31 files changed, 3667 insertions(+), 886 deletions(-)
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp
index 843587b576a..834ef057b2b 100644
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp
@@ -210,8 +210,8 @@ void test_pointers2()
sink(buffer); // $ MISSING: ast,ir
sink(ptr1); // $ ast MISSING: ir
- sink(ptr2); // $ SPURIOUS: ast
- sink(*ptr2); // $ ast MISSING: ir
+ sink(ptr2); // $ SPURIOUS: ast,ir
+ sink(*ptr2); // $ ast,ir
sink(ptr3); // $ MISSING: ast,ir
sink(ptr4); // clean
sink(*ptr4); // $ MISSING: ast,ir
@@ -254,8 +254,8 @@ int test_readv_and_writev(iovec* iovs) {
sink(*iovs); // $ast,ir
char* p = (char*)iovs[1].iov_base;
- sink(p); // $ ir MISSING: ast
- sink(*p); // $ ir MISSING: ast
+ sink(p); // $ MISSING: ast,ir
+ sink(*p); // $ MISSING: ast,ir
writev(0, iovs, 16); // $ remote
}
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/stl.cpp b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/stl.cpp
index 8a15071ef3c..f22347ba744 100644
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/stl.cpp
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/stl.cpp
@@ -89,12 +89,12 @@ void test_stringstream()
sink(ss1);
sink(ss2); // $ ir MISSING: ast
- sink(ss3); // $ MISSING: ast,ir
+ sink(ss3); // $ ir MISSING: ast
sink(ss4); // $ ir MISSING: ast
sink(ss5); // $ ir MISSING: ast
sink(ss1.str());
sink(ss2.str()); // $ ir MISSING: ast
- sink(ss3.str()); // $ MISSING: ast,ir
+ sink(ss3.str()); // $ ir MISSING: ast
sink(ss4.str()); // $ ir MISSING: ast
sink(ss5.str()); // $ ir MISSING: ast
}
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected
index db9a86fbb57..a441de0d72a 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected
@@ -26,62 +26,589 @@ unreachableNodeCCtx
localCallNodes
postIsNotPre
postHasUniquePre
-| test.cpp:373:5:373:20 | Store | PostUpdateNode should have one pre-update node but has 0. |
uniquePostUpdate
postIsInSameCallable
reverseRead
argHasPostUpdate
postWithInFlow
-| BarrierGuard.cpp:49:3:49:17 | Chi | PostUpdateNode should not be the target of local flow. |
-| BarrierGuard.cpp:60:3:60:18 | Chi | PostUpdateNode should not be the target of local flow. |
-| clang.cpp:28:3:28:34 | Chi | PostUpdateNode should not be the target of local flow. |
-| clang.cpp:34:22:34:27 | Chi | PostUpdateNode should not be the target of local flow. |
-| clang.cpp:34:32:34:37 | Chi | PostUpdateNode should not be the target of local flow. |
-| clang.cpp:39:32:39:37 | Chi | PostUpdateNode should not be the target of local flow. |
-| clang.cpp:39:42:39:47 | Chi | PostUpdateNode should not be the target of local flow. |
-| clang.cpp:43:35:43:40 | Chi | PostUpdateNode should not be the target of local flow. |
-| clang.cpp:43:51:43:51 | Chi | PostUpdateNode should not be the target of local flow. |
-| clang.cpp:49:25:49:30 | Chi | PostUpdateNode should not be the target of local flow. |
-| clang.cpp:49:35:49:40 | Chi | PostUpdateNode should not be the target of local flow. |
-| clang.cpp:50:3:50:26 | Chi | PostUpdateNode should not be the target of local flow. |
-| example.c:17:19:17:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| example.c:17:21:17:21 | Chi | PostUpdateNode should not be the target of local flow. |
-| example.c:24:2:24:30 | Chi | PostUpdateNode should not be the target of local flow. |
-| example.c:24:13:24:30 | Chi | PostUpdateNode should not be the target of local flow. |
-| example.c:26:2:26:25 | Chi | PostUpdateNode should not be the target of local flow. |
-| file://:0:0:0:0 | Chi | PostUpdateNode should not be the target of local flow. |
-| file://:0:0:0:0 | Chi | PostUpdateNode should not be the target of local flow. |
-| file://:0:0:0:0 | Chi | PostUpdateNode should not be the target of local flow. |
-| lambdas.cpp:13:12:13:12 | Chi | PostUpdateNode should not be the target of local flow. |
-| lambdas.cpp:13:15:13:15 | Chi | PostUpdateNode should not be the target of local flow. |
-| lambdas.cpp:28:10:31:2 | Chi | PostUpdateNode should not be the target of local flow. |
-| lambdas.cpp:28:10:31:2 | Chi | PostUpdateNode should not be the target of local flow. |
-| lambdas.cpp:43:3:43:14 | Chi | PostUpdateNode should not be the target of local flow. |
-| ref.cpp:11:5:11:13 | Chi | PostUpdateNode should not be the target of local flow. |
-| ref.cpp:20:5:20:13 | Chi | PostUpdateNode should not be the target of local flow. |
-| ref.cpp:22:7:22:13 | Chi | PostUpdateNode should not be the target of local flow. |
-| ref.cpp:24:7:24:13 | Chi | PostUpdateNode should not be the target of local flow. |
-| ref.cpp:29:5:29:18 | Chi | PostUpdateNode should not be the target of local flow. |
-| ref.cpp:31:7:31:13 | Chi | PostUpdateNode should not be the target of local flow. |
-| ref.cpp:39:7:39:13 | Chi | PostUpdateNode should not be the target of local flow. |
-| ref.cpp:44:5:44:18 | Chi | PostUpdateNode should not be the target of local flow. |
-| ref.cpp:46:7:46:13 | Chi | PostUpdateNode should not be the target of local flow. |
-| ref.cpp:48:7:48:13 | Chi | PostUpdateNode should not be the target of local flow. |
-| ref.cpp:75:5:75:17 | Chi | PostUpdateNode should not be the target of local flow. |
-| ref.cpp:83:5:83:17 | Chi | PostUpdateNode should not be the target of local flow. |
-| ref.cpp:87:7:87:17 | Chi | PostUpdateNode should not be the target of local flow. |
-| ref.cpp:89:7:89:17 | Chi | PostUpdateNode should not be the target of local flow. |
-| ref.cpp:94:5:94:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| ref.cpp:96:7:96:17 | Chi | PostUpdateNode should not be the target of local flow. |
-| ref.cpp:104:7:104:17 | Chi | PostUpdateNode should not be the target of local flow. |
-| ref.cpp:109:5:109:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| ref.cpp:113:7:113:17 | Chi | PostUpdateNode should not be the target of local flow. |
-| ref.cpp:115:7:115:17 | Chi | PostUpdateNode should not be the target of local flow. |
-| test.cpp:91:3:91:18 | Chi | PostUpdateNode should not be the target of local flow. |
-| test.cpp:115:3:115:17 | Chi | PostUpdateNode should not be the target of local flow. |
-| test.cpp:120:3:120:10 | Chi | PostUpdateNode should not be the target of local flow. |
-| test.cpp:125:3:125:11 | Chi | PostUpdateNode should not be the target of local flow. |
-| test.cpp:359:5:359:20 | Chi | PostUpdateNode should not be the target of local flow. |
-| test.cpp:373:5:373:20 | Chi | PostUpdateNode should not be the target of local flow. |
-| test.cpp:373:5:373:20 | Store | PostUpdateNode should not be the target of local flow. |
-| test.cpp:465:3:465:15 | Chi | PostUpdateNode should not be the target of local flow. |
+| BarrierGuard.cpp:49:6:49:6 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| BarrierGuard.cpp:60:3:60:4 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| BarrierGuard.cpp:60:7:60:7 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:8:20:8:29 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:22:3:22:6 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:22:8:22:20 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:22:9:22:20 | sourceArray1 [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:26:8:26:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:26:8:26:24 | sourceStruct1_ptr [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:26:27:26:34 | sourceStruct1_ptr [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:28:3:28:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:28:22:28:23 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:30:8:30:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:30:8:30:24 | sourceStruct1_ptr [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:30:27:30:34 | sourceStruct1_ptr [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:34:19:34:41 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:34:19:34:41 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:39:16:39:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:39:30:39:51 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:39:30:39:51 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:43:26:43:53 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:43:26:43:53 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:49:7:49:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:49:22:49:44 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:49:22:49:44 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:50:3:50:12 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:50:3:50:12 | stackArray [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:50:3:50:15 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:6:29:6:37 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:7:29:7:37 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:9:30:9:45 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:10:30:10:45 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:15:8:15:8 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:15:8:15:8 | ConvertToNonVirtualBase [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:15:8:15:8 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:16:30:16:45 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:17:31:17:39 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:21:8:21:8 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:21:8:21:8 | ConvertToNonVirtualBase [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:21:8:21:8 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:22:30:22:45 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:24:31:24:39 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:29:8:29:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:29:29:29:34 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:31:8:31:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:31:8:31:13 | topPtr [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:31:16:31:24 | topPtr [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:32:8:32:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:32:8:32:13 | topPtr [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:32:16:32:24 | topPtr [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:33:3:33:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:33:3:33:8 | topPtr [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:33:11:33:16 | topPtr [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:35:8:35:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:35:8:35:13 | topPtr [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:35:16:35:25 | topPtr [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:36:8:36:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:36:8:36:13 | topPtr [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:36:16:36:25 | topPtr [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:37:3:37:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:37:3:37:8 | topPtr [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:37:11:37:17 | topPtr [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:39:8:39:13 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:39:8:39:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:39:8:39:13 | topRef [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:39:15:39:23 | topRef [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:40:8:40:13 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:40:8:40:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:40:8:40:13 | topRef [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:40:15:40:23 | topRef [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:41:3:41:8 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:41:3:41:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:41:3:41:8 | topRef [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:41:10:41:15 | topRef [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:43:8:43:13 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:43:8:43:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:43:8:43:13 | topRef [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:43:15:43:24 | topRef [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:44:8:44:13 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:44:8:44:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:44:8:44:13 | topRef [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:44:15:44:24 | topRef [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:45:3:45:8 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:45:3:45:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:45:3:45:8 | topRef [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:45:10:45:16 | topRef [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:51:3:51:22 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:55:8:55:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:55:8:55:19 | globalBottom [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:55:22:55:30 | globalBottom [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:56:8:56:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:56:8:56:19 | globalMiddle [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:56:22:56:30 | globalMiddle [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:58:8:58:23 | call to readGlobalBottom [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:58:28:58:36 | call to readGlobalBottom [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:60:3:60:14 | globalBottom [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:60:18:60:29 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:60:18:60:29 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:60:18:60:29 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:61:3:61:14 | globalMiddle [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:61:18:61:29 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:61:18:61:29 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:61:18:61:29 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:65:3:65:22 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:65:10:65:21 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:65:10:65:21 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:65:10:65:21 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:69:3:69:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:69:3:69:5 | top [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:69:8:69:13 | top [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:73:3:73:5 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:73:3:73:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:73:3:73:5 | top [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:73:7:73:12 | top [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:77:3:77:19 | call to allocateBottom [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:77:21:77:34 | call to allocateBottom [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:78:3:78:21 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:78:23:78:39 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:78:23:78:39 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:78:24:78:37 | call to allocateBottom [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:80:8:80:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:81:3:81:3 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:81:3:81:3 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:81:6:81:11 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:85:3:85:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:89:3:89:10 | bottom [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:89:3:89:10 | call to identity [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:89:12:89:17 | (Middle *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:89:12:89:17 | (Top *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:89:12:89:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:89:12:89:17 | bottom [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:89:21:89:26 | call to identity [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:90:3:90:10 | call to identity [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:90:3:90:10 | top [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:90:12:90:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:90:12:90:14 | top [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:90:18:90:23 | call to identity [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:100:3:100:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:105:5:105:17 | maybeCallSink [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:113:30:113:38 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:117:31:117:46 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:127:10:127:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:127:31:127:36 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:129:10:129:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:129:10:129:15 | topPtr [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:129:18:129:25 | topPtr [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:130:10:130:15 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:130:10:130:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:130:10:130:15 | topRef [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:130:17:130:24 | topRef [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:148:5:148:5 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:168:8:168:8 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| example.c:17:19:17:22 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| example.c:17:19:17:22 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| example.c:24:9:24:9 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| example.c:24:20:24:20 | y [post update] | PostUpdateNode should not be the target of local flow. |
+| example.c:26:9:26:9 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| example.c:26:13:26:16 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| example.c:26:18:26:24 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| example.c:26:19:26:24 | coords [post update] | PostUpdateNode should not be the target of local flow. |
+| example.c:28:2:28:12 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| example.c:28:14:28:25 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| example.c:28:22:28:25 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| example.c:28:23:28:25 | pos [post update] | PostUpdateNode should not be the target of local flow. |
+| globals.cpp:5:9:5:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| globals.cpp:13:5:13:19 | flowTestGlobal1 [post update] | PostUpdateNode should not be the target of local flow. |
+| globals.cpp:23:5:23:19 | flowTestGlobal2 [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:8:6:8:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:9:6:9:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:10:6:10:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:11:6:11:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:13:7:13:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:13:10:17:2 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:13:10:17:2 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:16:3:16:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:20:7:20:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:20:10:24:2 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:20:10:24:2 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:20:10:24:2 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:23:3:23:3 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:23:3:23:14 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:23:3:23:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:23:3:23:14 | v [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:28:7:28:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:28:10:31:2 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:28:10:31:2 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:34:7:34:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:34:13:34:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:40:7:40:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:40:13:40:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:43:3:43:3 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:43:3:43:3 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:43:3:43:3 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:45:3:45:3 | t [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:45:3:45:3 | u [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:45:3:45:3 | w [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:45:4:45:4 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:45:4:45:4 | t [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:45:7:45:7 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:45:7:45:7 | u [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:45:10:45:10 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:45:10:45:10 | w [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:11:5:11:7 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:11:5:11:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:11:5:11:7 | lhs [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:16:5:16:10 | lhs [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:16:12:16:14 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:16:12:16:14 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:16:12:16:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:16:12:16:14 | lhs [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:20:5:20:7 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:20:5:20:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:20:5:20:7 | lhs [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:22:7:22:9 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:22:7:22:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:22:7:22:9 | lhs [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:24:7:24:9 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:24:7:24:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:24:7:24:9 | lhs [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:29:5:29:7 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:29:5:29:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:29:5:29:7 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:31:7:31:9 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:31:7:31:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:31:7:31:9 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:37:7:37:19 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:37:21:37:23 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:37:21:37:23 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:37:21:37:23 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:37:21:37:23 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:39:7:39:9 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:39:7:39:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:39:7:39:9 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:44:5:44:7 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:44:5:44:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:44:5:44:7 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:46:7:46:9 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:46:7:46:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:46:7:46:9 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:48:7:48:9 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:48:7:48:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:48:7:48:9 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:55:5:55:17 | x1 [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:55:19:55:20 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:55:19:55:20 | x1 [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:58:5:58:13 | x2 [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:58:15:58:16 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:58:15:58:16 | x2 [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:61:5:61:24 | x3 [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:61:26:61:27 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:61:26:61:27 | x3 [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:64:5:64:13 | x4 [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:64:15:64:16 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:64:15:64:16 | x4 [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:75:5:75:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:75:5:75:7 | lhs [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:75:9:75:11 | val [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:79:5:79:10 | lhs [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:79:12:79:14 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:79:12:79:14 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:79:12:79:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:79:12:79:14 | lhs [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:83:5:83:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:83:5:83:7 | lhs [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:83:9:83:11 | val [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:87:7:87:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:87:7:87:9 | lhs [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:87:11:87:13 | val [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:89:7:89:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:89:7:89:9 | lhs [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:89:11:89:13 | val [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:94:5:94:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:94:5:94:7 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:94:9:94:11 | val [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:96:7:96:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:96:7:96:9 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:96:11:96:13 | val [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:102:7:102:19 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:102:21:102:23 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:102:21:102:23 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:102:21:102:23 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:102:21:102:23 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:104:7:104:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:104:7:104:9 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:104:11:104:13 | val [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:109:5:109:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:109:5:109:7 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:109:9:109:11 | val [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:113:7:113:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:113:7:113:9 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:113:11:113:13 | val [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:115:7:115:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:115:7:115:9 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:115:11:115:13 | val [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:122:5:122:17 | x1 [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:122:19:122:20 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:122:19:122:20 | x1 [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:125:5:125:13 | x2 [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:125:15:125:16 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:125:15:125:16 | x2 [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:128:5:128:24 | x3 [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:128:26:128:27 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:128:26:128:27 | x3 [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:131:5:131:13 | x4 [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:131:15:131:16 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:131:15:131:16 | x4 [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:6:7:6:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:8:3:8:4 | t2 [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:12:5:12:6 | t2 [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:17:3:17:4 | t1 [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:23:12:23:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:23:27:23:27 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:24:5:24:6 | t1 [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:41:9:41:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:41:17:41:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:42:9:42:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:43:10:43:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:43:10:43:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:45:5:45:5 | t [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:45:9:45:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:45:9:45:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:50:9:50:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:50:24:50:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:52:7:52:7 | t [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:54:7:54:7 | t [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:67:14:67:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:68:8:68:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:69:8:69:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:70:14:70:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:77:3:77:4 | u1 [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:80:7:80:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:84:8:84:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:85:3:85:4 | i1 [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:91:3:91:9 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:91:3:91:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:91:3:91:9 | source1 [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:100:9:100:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:101:10:101:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:102:5:102:5 | t [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:107:9:107:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:108:10:108:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:109:5:109:5 | t [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:115:3:115:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:115:4:115:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:115:4:115:6 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:116:3:116:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:120:3:120:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:120:4:120:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:120:4:120:6 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:121:3:121:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:125:3:125:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:125:4:125:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:125:4:125:6 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:126:3:126:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:134:3:134:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:138:7:138:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:139:7:139:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:145:3:145:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:149:7:149:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:151:7:151:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:156:7:156:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:158:3:158:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:162:7:162:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:164:7:164:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:171:7:171:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:172:3:172:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:176:7:176:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:177:7:177:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:190:5:190:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:194:9:194:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:194:13:194:27 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:194:13:194:27 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:194:13:194:27 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:195:9:195:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:196:9:196:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:201:9:201:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:203:5:203:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:207:9:207:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:207:13:207:33 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:207:13:207:33 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:207:13:207:33 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:209:9:209:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:209:13:209:33 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:209:13:209:33 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:209:13:209:33 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:214:9:214:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:215:9:215:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:217:5:217:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:221:9:221:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:221:13:221:34 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:221:13:221:34 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:221:13:221:34 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:223:9:223:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:223:13:223:34 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:223:13:223:34 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:223:13:223:34 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:230:9:230:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:231:9:231:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:232:5:232:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:236:9:236:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:236:13:236:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:236:13:236:24 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:236:13:236:24 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:237:9:237:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:245:7:245:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:245:7:245:12 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:245:7:245:12 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:246:7:246:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:246:7:246:16 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:246:7:246:16 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:250:11:250:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:251:7:251:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:251:7:251:12 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:251:7:251:12 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:255:11:255:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:256:7:256:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:256:7:256:12 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:256:7:256:12 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:265:11:265:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:265:15:265:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:265:15:265:20 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:265:15:265:20 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:267:7:267:7 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:267:11:267:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:267:11:267:20 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:267:11:267:20 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:272:11:272:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:273:7:273:23 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:273:14:273:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:273:14:273:19 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:273:14:273:19 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:277:11:277:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:278:7:278:29 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:278:14:278:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:278:14:278:19 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:278:14:278:19 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:282:11:282:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:283:7:283:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:288:13:288:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:288:17:288:22 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:288:17:288:22 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:288:17:288:22 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:290:9:290:9 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:290:13:290:22 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:290:13:290:22 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:290:13:290:22 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:295:13:295:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:295:17:295:22 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:295:17:295:22 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:295:17:295:22 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:296:9:296:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:300:13:300:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:300:23:300:28 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:300:23:300:28 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:300:23:300:28 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:301:9:301:23 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:305:13:305:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:306:9:306:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:314:2:314:2 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:314:2:314:2 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:314:2:314:2 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:317:6:317:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:317:10:317:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:317:10:317:10 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:317:10:317:10 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:319:6:319:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:319:10:319:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:319:10:319:10 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:319:10:319:10 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:321:2:321:2 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:321:2:321:2 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:321:2:321:2 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:324:2:324:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:333:5:333:13 | globalVar [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:347:5:347:13 | globalVar [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:359:5:359:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:359:5:359:9 | field [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:364:5:364:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:364:5:364:14 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:364:5:364:14 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:373:5:373:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:373:5:373:9 | field [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:374:5:374:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:374:5:374:20 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:374:5:374:20 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:383:7:383:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:384:3:384:8 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:384:10:384:13 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:384:10:384:13 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:384:11:384:13 | tmp [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:389:7:389:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:390:8:390:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:391:3:391:8 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:391:10:391:13 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:391:10:391:13 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:391:11:391:13 | tmp [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:400:3:400:8 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:400:10:400:13 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:400:10:400:13 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:400:11:400:13 | tmp [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:406:8:406:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:407:3:407:8 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:407:10:407:13 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:407:10:407:13 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:407:11:407:13 | tmp [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:417:3:417:14 | local [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:417:16:417:20 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:417:16:417:20 | local [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:423:3:423:18 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:423:20:423:25 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:423:21:423:25 | local [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:429:3:429:18 | local [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:429:20:429:24 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:429:20:429:24 | local [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:436:3:436:16 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:436:18:436:23 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:436:19:436:23 | local [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:442:3:442:16 | local [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:442:18:442:22 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:442:18:442:22 | local [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:453:7:453:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:456:7:456:9 | tmp [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:458:7:458:9 | tmp [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:465:3:465:4 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:465:4:465:4 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:465:4:465:4 | p [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:469:7:469:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:470:3:470:19 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:470:21:470:22 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:470:22:470:22 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:481:3:481:19 | content [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:481:21:481:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:481:21:481:30 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:481:24:481:30 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:481:24:481:30 | content [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:482:8:482:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:9:7:9:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:10:12:10:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:10:27:10:27 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:11:5:11:5 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:17:7:17:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:18:12:18:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:18:35:18:35 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:19:5:19:5 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:25:7:25:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:26:12:26:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:26:27:26:27 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:27:5:27:5 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:33:7:33:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:34:12:34:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:34:27:34:27 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:37:5:37:5 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:43:7:43:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:44:12:44:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:44:27:44:27 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:47:5:47:5 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:54:7:54:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:55:12:55:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:55:30:55:30 | y [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:55:38:55:38 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:62:7:62:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:63:12:63:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:63:30:63:30 | y [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:63:38:63:38 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:64:5:64:5 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:70:7:70:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:76:12:76:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:76:30:76:30 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:76:38:76:38 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:83:7:83:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:84:12:84:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:84:20:84:20 | y [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:84:38:84:38 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:90:7:90:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:91:12:91:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:91:20:91:20 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:91:38:91:38 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:97:7:97:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:98:7:98:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:101:18:101:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| true_upon_entry.cpp:102:5:102:5 | x [post update] | PostUpdateNode should not be the target of local flow. |
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/lambdas.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/lambdas.cpp
index 7bf140918ab..f48b50d705b 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/lambdas.cpp
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/lambdas.cpp
@@ -18,7 +18,7 @@ void test_lambdas()
sink(a()); // $ ast,ir
auto b = [&] {
- sink(t); // $ ast MISSING: ir
+ sink(t); // $ ast,ir
sink(u);
v = source(); // (v is reference captured)
};
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp
index afceddfabd0..e4a2eb4a0b8 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp
@@ -100,14 +100,14 @@ void local_references(int &source1, int clean1) {
int t = source();
int &ref = t;
t = clean1;
- sink(ref); // $ SPURIOUS: ast
+ sink(ref); // $ SPURIOUS: ast,ir
}
{
int t = clean1;
int &ref = t;
t = source();
- sink(ref); // $ ir MISSING: ast
+ sink(ref); // $ MISSING: ast,ir
}
}
@@ -346,7 +346,7 @@ namespace FlowThroughGlobals {
int taintAndCall() {
globalVar = source();
calledAfterTaint();
- sink(globalVar); // $ ast MISSING: ir
+ sink(globalVar); // $ ast,ir
}
}
@@ -355,21 +355,21 @@ namespace FlowThroughGlobals {
class FlowThroughFields {
int field = 0;
- int taintField() {
+ void taintField() {
field = source();
}
- int f() {
+ void f() {
sink(field); // tainted or clean? Not sure.
taintField();
- sink(field); // $ ast MISSING: ir
- }
-
- int calledAfterTaint() {
sink(field); // $ ast,ir
}
- int taintAndCall() {
+ void calledAfterTaint() {
+ sink(field); // $ ast,ir
+ }
+
+ void taintAndCall() {
field = source();
calledAfterTaint();
sink(field); // $ ast,ir
diff --git a/cpp/ql/test/library-tests/dataflow/fields/A.cpp b/cpp/ql/test/library-tests/dataflow/fields/A.cpp
index d52ca067c6f..395d32f1cd0 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/A.cpp
+++ b/cpp/ql/test/library-tests/dataflow/fields/A.cpp
@@ -54,7 +54,7 @@ public:
B *b = new B();
b->set(new C1());
sink(b->get()); // $ ast ir=55:12
- sink((new B(new C()))->get()); // $ ast ir
+ sink((new B(new C()))->get()); // $ ast MISSING: ir
}
void f3()
@@ -63,7 +63,7 @@ public:
B *b2;
b2 = setOnB(b1, new C2());
sink(b1->c); // no flow
- sink(b2->c); // $ ast MISSING: ir
+ sink(b2->c); // $ ast ir=64:21
}
void f4()
@@ -72,7 +72,7 @@ public:
B *b2;
b2 = setOnBWrap(b1, new C2());
sink(b1->c); // no flow
- sink(b2->c); // $ ast MISSING: ir
+ sink(b2->c); // $ ast ir=73:25
}
B *setOnBWrap(B *b1, C *c)
@@ -117,7 +117,7 @@ public:
}
if (C1 *c1 = dynamic_cast(cc))
{
- sink(c1->a); // $ SPURIOUS: ast
+ sink(c1->a); // $ SPURIOUS: ast,ir
}
}
@@ -149,7 +149,7 @@ public:
{
B *b = new B();
D *d = new D(b, r());
- sink(d->b); // $ ast,ir=143:25 ast,ir=150:12
+ sink(d->b); // $ ast MISSING: ir
sink(d->b->c); // $ ast MISSING: ir
sink(b->c); // $ ast,ir
}
diff --git a/cpp/ql/test/library-tests/dataflow/fields/C.cpp b/cpp/ql/test/library-tests/dataflow/fields/C.cpp
index 79b9ffefdea..bc1c662c3db 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/C.cpp
+++ b/cpp/ql/test/library-tests/dataflow/fields/C.cpp
@@ -1,10 +1,10 @@
+void sink(...);
class C
{
class Elem
{
};
-
private:
Elem *s1 = new Elem();
const Elem *s2 = new Elem();
@@ -26,12 +26,10 @@ public:
void func()
{
- sink(s1); // $ast ir
+ sink(s1); // $ast MISSING: ir
sink(s2); // $ MISSING: ast,ir
- sink(s3); // $ast ir
+ sink(s3); // $ast MISSING: ir
sink(s4); // $ MISSING: ast,ir
}
-
- static void sink(const void *o) {}
};
const C::Elem *C::s4 = new Elem();
diff --git a/cpp/ql/test/library-tests/dataflow/fields/D.cpp b/cpp/ql/test/library-tests/dataflow/fields/D.cpp
index 86c40838850..ee51e6e5428 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/D.cpp
+++ b/cpp/ql/test/library-tests/dataflow/fields/D.cpp
@@ -19,7 +19,7 @@ public:
};
static void sinkWrap(Box2* b2) {
- sink(b2->getBox1()->getElem()); // $ast=28:15 ast=35:15 ast=42:15 ast=49:15 MISSING: ir
+ sink(b2->getBox1()->getElem()); // $ast,ir=28:15 ast,ir=35:15 ast,ir=42:15 ast,ir=49:15
}
Box2* boxfield;
@@ -61,6 +61,6 @@ public:
private:
void f5b() {
- sink(boxfield->box->elem); // $ ast MISSING: ir
+ sink(boxfield->box->elem); // $ ast,ir
}
};
diff --git a/cpp/ql/test/library-tests/dataflow/fields/E.cpp b/cpp/ql/test/library-tests/dataflow/fields/E.cpp
index f2349e00794..d9b6c366ce3 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/E.cpp
+++ b/cpp/ql/test/library-tests/dataflow/fields/E.cpp
@@ -18,7 +18,7 @@ void sink(char *b);
void handlePacket(packet *p)
{
- sink(p->data.buffer); // $ ast MISSING: ir
+ sink(p->data.buffer); // $ ast,ir
}
void f(buf* b)
@@ -28,7 +28,7 @@ void f(buf* b)
argument_source(raw);
argument_source(b->buffer);
argument_source(p.data.buffer);
- sink(raw); // $ ast MISSING: ir
- sink(b->buffer); // $ ast MISSING: ir
+ sink(raw); // $ ast,ir
+ sink(b->buffer); // $ ast,ir
handlePacket(&p);
}
\ No newline at end of file
diff --git a/cpp/ql/test/library-tests/dataflow/fields/IRConfiguration.qll b/cpp/ql/test/library-tests/dataflow/fields/IRConfiguration.qll
index a9d98705ca4..652147c328b 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/IRConfiguration.qll
+++ b/cpp/ql/test/library-tests/dataflow/fields/IRConfiguration.qll
@@ -18,7 +18,7 @@ class IRConf extends Configuration {
override predicate isSink(Node sink) {
exists(Call c |
c.getTarget().hasName("sink") and
- c.getAnArgument() = sink.asConvertedExpr()
+ c.getAnArgument() = [sink.asExpr(), sink.asConvertedExpr()]
)
}
diff --git a/cpp/ql/test/library-tests/dataflow/fields/aliasing.cpp b/cpp/ql/test/library-tests/dataflow/fields/aliasing.cpp
index fcdfc53cfdb..16ff8546b15 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/aliasing.cpp
+++ b/cpp/ql/test/library-tests/dataflow/fields/aliasing.cpp
@@ -35,12 +35,12 @@ void assignAfterAlias() {
S s1 = { 0, 0 };
S &ref1 = s1;
ref1.m1 = user_input();
- sink(s1.m1); // $ ir MISSING: ast
+ sink(s1.m1); // $ MISSING: ast,ir
S s2 = { 0, 0 };
S &ref2 = s2;
s2.m1 = user_input();
- sink(ref2.m1); // $ ir MISSING: ast
+ sink(ref2.m1); // $ MISSING: ast,ir
}
void assignAfterCopy() {
@@ -77,14 +77,14 @@ void pointerIntermediate() {
Wrapper w = { { 0, 0 } };
S *s = &w.s;
s->m1 = user_input();
- sink(w.s.m1); // $ ir MISSING: ast
+ sink(w.s.m1); // $ MISSING: ast,ir
}
void referenceIntermediate() {
Wrapper w = { { 0, 0 } };
S &s = w.s;
s.m1 = user_input();
- sink(w.s.m1); // $ ir MISSING: ast
+ sink(w.s.m1); // $ MISSING: ast,ir
}
void nestedAssign() {
@@ -99,7 +99,7 @@ void addressOfField() {
S s_copy = s;
int* px = &s_copy.m1;
- sink(*px); // $ ir MISSING: ast
+ sink(*px); // $ MISSING: ast,ir
}
void taint_a_ptr(int* pa) {
@@ -119,7 +119,7 @@ struct S_with_pointer {
void pointer_deref(int* xs) {
taint_a_ptr(xs);
- sink(xs[0]); // $ ir MISSING: ast
+ sink(xs[0]); // $ MISSING: ast,ir
}
void pointer_deref_sub(int* xs) {
@@ -129,18 +129,18 @@ void pointer_deref_sub(int* xs) {
void pointer_many_addrof_and_deref(int* xs) {
taint_a_ptr(xs);
- sink(*&*&*xs); // $ ir MISSING: ast
+ sink(*&*&*xs); // $ MISSING: ast,ir
}
void pointer_unary_plus(int* xs) {
taint_a_ptr(+xs);
- sink(*+xs); // $ ir MISSING: ast
+ sink(*+xs); // $ MISSING: ast,ir
}
void pointer_member_index(S_with_pointer s) {
taint_a_ptr(s.data);
// `s.data` is points to all-aliased-memory
- sink(s.data[0]); // $ MISSING: ir,ast
+ sink(s.data[0]); // $ ir MISSING: ast
}
void member_array_different_field(S_with_pointer* s) {
@@ -156,13 +156,13 @@ struct S_with_array {
void pointer_member_deref() {
S_with_array s;
taint_a_ptr(s.data);
- sink(*s.data); // $ ast MISSING: ir
+ sink(*s.data); // $ ast,ir
}
void array_member_deref() {
S_with_array s;
taint_a_ptr(s.data);
- sink(s.data[0]); // $ ast MISSING: ir
+ sink(s.data[0]); // $ ast,ir
}
struct S2 {
diff --git a/cpp/ql/test/library-tests/dataflow/fields/arrays.cpp b/cpp/ql/test/library-tests/dataflow/fields/arrays.cpp
index 8c4f0b34c4d..08fc650085e 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/arrays.cpp
+++ b/cpp/ql/test/library-tests/dataflow/fields/arrays.cpp
@@ -5,7 +5,7 @@ void local_array() {
void *arr[10] = { 0 };
arr[0] = user_input();
sink(arr[0]); // $ ast,ir
- sink(arr[1]); // $ SPURIOUS: ast
+ sink(arr[1]); // $ SPURIOUS: ast,ir
sink(*arr); // $ ast,ir
sink(*&arr[0]); // $ ast,ir
}
@@ -14,7 +14,7 @@ void local_array_convoluted_assign() {
void *arr[10] = { 0 };
*&arr[0] = user_input();
sink(arr[0]); // $ ast,ir
- sink(arr[1]); // $ SPURIOUS: ast
+ sink(arr[1]); // $ SPURIOUS: ast,ir
}
struct inner {
@@ -35,17 +35,17 @@ struct outer {
void nested_array_1(outer o) {
o.nested.arr[1].data = user_input();
sink(o.nested.arr[1].data); // $ ast,ir
- sink(o.nested.arr[0].data); // $ SPURIOUS: ast
+ sink(o.nested.arr[0].data); // $ SPURIOUS: ast,ir
}
void nested_array_2(outer o) {
o.indirect->arr[1].data = user_input();
- sink(o.indirect->arr[1].data); // $ ast MISSING: ir
- sink(o.indirect->arr[0].data); // $ SPURIOUS: ast
+ sink(o.indirect->arr[1].data); // $ ast,ir
+ sink(o.indirect->arr[0].data); // $ SPURIOUS: ast,ir
}
void nested_array_3(outer o) {
o.indirect->ptr[1].data = user_input();
- sink(o.indirect->ptr[1].data); // $ MISSING: ir,ast
- sink(o.indirect->ptr[0].data);
+ sink(o.indirect->ptr[1].data); // $ ir MISSING: ast
+ sink(o.indirect->ptr[0].data); // $ SPURIOUS: ir
}
diff --git a/cpp/ql/test/library-tests/dataflow/fields/by_reference.cpp b/cpp/ql/test/library-tests/dataflow/fields/by_reference.cpp
index 8e84d39be3b..d8c6a194151 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/by_reference.cpp
+++ b/cpp/ql/test/library-tests/dataflow/fields/by_reference.cpp
@@ -108,11 +108,11 @@ void test_outer_with_ptr(Outer *pouter) {
taint_a_ptr(&pouter->a);
sink(outer.inner_nested.a); // $ ast,ir
- sink(outer.inner_ptr->a); // $ ast MISSING: ir
+ sink(outer.inner_ptr->a); // $ ast,ir
sink(outer.a); // $ ast,ir
sink(pouter->inner_nested.a); // $ ast,ir
- sink(pouter->inner_ptr->a); // $ast MISSING: ir
+ sink(pouter->inner_ptr->a); // $ast,ir
sink(pouter->a); // $ ast,ir
}
@@ -128,10 +128,10 @@ void test_outer_with_ref(Outer *pouter) {
taint_a_ref(pouter->a);
sink(outer.inner_nested.a); // $ ast,ir
- sink(outer.inner_ptr->a); // $ ast MISSING: ir
+ sink(outer.inner_ptr->a); // $ ast,ir
sink(outer.a); // $ ast,ir
sink(pouter->inner_nested.a); // $ ast,ir
- sink(pouter->inner_ptr->a); // $ ast MISSING: ir
+ sink(pouter->inner_ptr->a); // $ ast,ir
sink(pouter->a); // $ ast,ir
}
diff --git a/cpp/ql/test/library-tests/dataflow/fields/conflated.cpp b/cpp/ql/test/library-tests/dataflow/fields/conflated.cpp
index aa600a99a5e..1d166465710 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/conflated.cpp
+++ b/cpp/ql/test/library-tests/dataflow/fields/conflated.cpp
@@ -8,7 +8,7 @@ struct A {
void pointer_without_allocation(const A& ra) {
*ra.p = user_input();
- sink(*ra.p); // $ MISSING: ast,ir
+ sink(*ra.p); // $ ir MISSING: ast
}
void argument_source(void*);
@@ -17,7 +17,7 @@ void sink(void*);
void pointer_without_allocation_2() {
char *raw;
argument_source(raw);
- sink(raw); // $ ast MISSING: ir
+ sink(raw); // $ ast,ir
}
A* makeA() {
@@ -27,14 +27,14 @@ A* makeA() {
void no_InitializeDynamicAllocation_instruction() {
A* pa = makeA();
pa->x = user_input();
- sink(pa->x); // $ ast MISSING: ir
+ sink(pa->x); // $ ast,ir
}
void fresh_or_arg(A* arg, bool unknown) {
A* pa;
pa = unknown ? arg : new A;
pa->x = user_input();
- sink(pa->x); // $ ast MISSING: ir
+ sink(pa->x); // $ ast,ir
}
struct LinkedList {
@@ -52,11 +52,11 @@ void too_many_indirections() {
LinkedList* ll = new LinkedList;
ll->next = new LinkedList;
ll->next->y = user_input();
- sink(ll->next->y); // $ ast MISSING: ir
+ sink(ll->next->y); // $ ast,ir
}
void too_many_indirections_2(LinkedList* next) {
LinkedList* ll = new LinkedList(next);
ll->next->y = user_input();
- sink(ll->next->y); // $ ast MISSING: ir
+ sink(ll->next->y); // $ ast,ir
}
diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected
index 8333b9d5776..82ad497f1ec 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected
+++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected
@@ -3,8 +3,8 @@ uniqueEnclosingCallable
| C.cpp:9:14:9:23 | new | Node should have one enclosing callable but has 0. |
| C.cpp:10:20:10:29 | 0 | Node should have one enclosing callable but has 0. |
| C.cpp:10:20:10:29 | new | Node should have one enclosing callable but has 0. |
-| C.cpp:37:24:37:33 | 0 | Node should have one enclosing callable but has 0. |
-| C.cpp:37:24:37:33 | new | Node should have one enclosing callable but has 0. |
+| C.cpp:35:24:35:33 | 0 | Node should have one enclosing callable but has 0. |
+| C.cpp:35:24:35:33 | new | Node should have one enclosing callable but has 0. |
uniqueType
uniqueNodeLocation
missingLocation
diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected
index fe7d8360403..ad1bf5316cb 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected
+++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected
@@ -20,149 +20,1306 @@ unreachableNodeCCtx
localCallNodes
postIsNotPre
postHasUniquePre
-| D.cpp:57:5:57:42 | Store | PostUpdateNode should have one pre-update node but has 0. |
-| simple.cpp:65:5:65:22 | Store | PostUpdateNode should have one pre-update node but has 0. |
-| simple.cpp:83:9:83:28 | Store | PostUpdateNode should have one pre-update node but has 0. |
-| simple.cpp:92:5:92:22 | Store | PostUpdateNode should have one pre-update node but has 0. |
uniquePostUpdate
postIsInSameCallable
reverseRead
argHasPostUpdate
postWithInFlow
-| A.cpp:25:7:25:17 | Chi | PostUpdateNode should not be the target of local flow. |
-| A.cpp:27:22:27:32 | Chi | PostUpdateNode should not be the target of local flow. |
-| A.cpp:98:12:98:18 | Chi | PostUpdateNode should not be the target of local flow. |
-| A.cpp:100:5:100:13 | Chi | PostUpdateNode should not be the target of local flow. |
-| A.cpp:142:7:142:20 | Chi | PostUpdateNode should not be the target of local flow. |
-| A.cpp:143:7:143:31 | Chi | PostUpdateNode should not be the target of local flow. |
-| A.cpp:183:7:183:20 | Chi | PostUpdateNode should not be the target of local flow. |
-| A.cpp:184:7:184:23 | Chi | PostUpdateNode should not be the target of local flow. |
-| B.cpp:6:15:6:24 | Chi | PostUpdateNode should not be the target of local flow. |
-| B.cpp:15:15:15:27 | Chi | PostUpdateNode should not be the target of local flow. |
-| B.cpp:35:7:35:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| B.cpp:36:7:36:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| B.cpp:46:7:46:21 | Chi | PostUpdateNode should not be the target of local flow. |
-| C.cpp:22:12:22:21 | Chi | PostUpdateNode should not be the target of local flow. |
-| C.cpp:22:12:22:21 | Chi | PostUpdateNode should not be the target of local flow. |
-| C.cpp:24:5:24:25 | Chi | PostUpdateNode should not be the target of local flow. |
-| C.cpp:24:16:24:25 | Chi | PostUpdateNode should not be the target of local flow. |
-| D.cpp:9:21:9:28 | Chi | PostUpdateNode should not be the target of local flow. |
-| D.cpp:11:29:11:36 | Chi | PostUpdateNode should not be the target of local flow. |
-| D.cpp:16:21:16:27 | Chi | PostUpdateNode should not be the target of local flow. |
-| D.cpp:18:29:18:35 | Chi | PostUpdateNode should not be the target of local flow. |
-| D.cpp:28:15:28:24 | Chi | PostUpdateNode should not be the target of local flow. |
-| D.cpp:35:15:35:24 | Chi | PostUpdateNode should not be the target of local flow. |
-| D.cpp:42:15:42:24 | Chi | PostUpdateNode should not be the target of local flow. |
-| D.cpp:49:15:49:24 | Chi | PostUpdateNode should not be the target of local flow. |
-| D.cpp:56:15:56:24 | Chi | PostUpdateNode should not be the target of local flow. |
-| D.cpp:57:5:57:42 | Chi | PostUpdateNode should not be the target of local flow. |
-| D.cpp:57:5:57:42 | Store | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:9:3:9:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:13:3:13:21 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:17:3:17:21 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:21:12:21:12 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:21:15:21:15 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:22:12:22:12 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:22:15:22:15 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:23:12:23:12 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:23:15:23:15 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:35:12:35:12 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:35:15:35:15 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:37:3:37:24 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:40:12:40:12 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:40:15:40:15 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:42:3:42:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:47:12:47:12 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:47:15:47:15 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:49:3:49:25 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:52:12:52:12 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:52:15:52:15 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:54:3:54:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:59:12:59:12 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:59:15:59:15 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:60:3:60:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:70:19:70:19 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:70:22:70:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:72:3:72:21 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:77:19:77:19 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:77:22:77:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:79:3:79:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:84:19:84:19 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:84:22:84:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:86:3:86:21 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:91:19:91:19 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:91:22:91:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:92:3:92:23 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:98:3:98:21 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:106:3:106:20 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:111:15:111:19 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:147:15:147:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:175:15:175:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:181:15:181:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:187:15:187:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:194:15:194:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:200:15:200:24 | Chi | PostUpdateNode should not be the target of local flow. |
-| aliasing.cpp:205:15:205:24 | Chi | PostUpdateNode should not be the target of local flow. |
-| arrays.cpp:5:18:5:23 | Chi | PostUpdateNode should not be the target of local flow. |
-| arrays.cpp:5:21:5:21 | Chi | PostUpdateNode should not be the target of local flow. |
-| arrays.cpp:6:3:6:23 | Chi | PostUpdateNode should not be the target of local flow. |
-| arrays.cpp:14:18:14:23 | Chi | PostUpdateNode should not be the target of local flow. |
-| arrays.cpp:14:21:14:21 | Chi | PostUpdateNode should not be the target of local flow. |
-| arrays.cpp:15:3:15:25 | Chi | PostUpdateNode should not be the target of local flow. |
-| arrays.cpp:36:3:36:37 | Chi | PostUpdateNode should not be the target of local flow. |
-| by_reference.cpp:12:5:12:16 | Chi | PostUpdateNode should not be the target of local flow. |
-| by_reference.cpp:16:5:16:19 | Chi | PostUpdateNode should not be the target of local flow. |
-| by_reference.cpp:84:3:84:25 | Chi | PostUpdateNode should not be the target of local flow. |
-| by_reference.cpp:88:3:88:24 | Chi | PostUpdateNode should not be the target of local flow. |
-| by_reference.cpp:92:3:92:20 | Chi | PostUpdateNode should not be the target of local flow. |
-| by_reference.cpp:96:3:96:19 | Chi | PostUpdateNode should not be the target of local flow. |
-| by_reference.cpp:102:21:102:39 | Chi | PostUpdateNode should not be the target of local flow. |
-| by_reference.cpp:104:15:104:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| by_reference.cpp:106:21:106:41 | Chi | PostUpdateNode should not be the target of local flow. |
-| by_reference.cpp:108:15:108:24 | Chi | PostUpdateNode should not be the target of local flow. |
-| by_reference.cpp:122:21:122:38 | Chi | PostUpdateNode should not be the target of local flow. |
-| by_reference.cpp:124:15:124:21 | Chi | PostUpdateNode should not be the target of local flow. |
-| by_reference.cpp:126:21:126:40 | Chi | PostUpdateNode should not be the target of local flow. |
-| by_reference.cpp:128:15:128:23 | Chi | PostUpdateNode should not be the target of local flow. |
-| complex.cpp:11:22:11:27 | Chi | PostUpdateNode should not be the target of local flow. |
-| complex.cpp:12:22:12:27 | Chi | PostUpdateNode should not be the target of local flow. |
-| complex.cpp:14:26:14:26 | Chi | PostUpdateNode should not be the target of local flow. |
-| complex.cpp:14:33:14:33 | Chi | PostUpdateNode should not be the target of local flow. |
-| complex.cpp:22:11:22:17 | Chi | PostUpdateNode should not be the target of local flow. |
-| complex.cpp:25:7:25:7 | Chi | PostUpdateNode should not be the target of local flow. |
-| complex.cpp:42:16:42:16 | Chi | PostUpdateNode should not be the target of local flow. |
-| complex.cpp:43:16:43:16 | Chi | PostUpdateNode should not be the target of local flow. |
-| complex.cpp:53:12:53:12 | Chi | PostUpdateNode should not be the target of local flow. |
-| complex.cpp:54:12:54:12 | Chi | PostUpdateNode should not be the target of local flow. |
-| complex.cpp:55:12:55:12 | Chi | PostUpdateNode should not be the target of local flow. |
-| complex.cpp:56:12:56:12 | Chi | PostUpdateNode should not be the target of local flow. |
-| conflated.cpp:45:39:45:42 | Chi | PostUpdateNode should not be the target of local flow. |
-| conflated.cpp:53:3:53:27 | Chi | PostUpdateNode should not be the target of local flow. |
-| constructors.cpp:20:24:20:29 | Chi | PostUpdateNode should not be the target of local flow. |
-| constructors.cpp:21:24:21:29 | Chi | PostUpdateNode should not be the target of local flow. |
-| constructors.cpp:23:28:23:28 | Chi | PostUpdateNode should not be the target of local flow. |
-| constructors.cpp:23:35:23:35 | Chi | PostUpdateNode should not be the target of local flow. |
-| qualifiers.cpp:9:30:9:44 | Chi | PostUpdateNode should not be the target of local flow. |
-| qualifiers.cpp:12:49:12:64 | Chi | PostUpdateNode should not be the target of local flow. |
-| qualifiers.cpp:13:51:13:65 | Chi | PostUpdateNode should not be the target of local flow. |
-| realistic.cpp:39:12:39:95 | Chi | PostUpdateNode should not be the target of local flow. |
-| realistic.cpp:49:9:49:64 | Chi | PostUpdateNode should not be the target of local flow. |
-| simple.cpp:20:24:20:29 | Chi | PostUpdateNode should not be the target of local flow. |
-| simple.cpp:21:24:21:29 | Chi | PostUpdateNode should not be the target of local flow. |
-| simple.cpp:23:28:23:28 | Chi | PostUpdateNode should not be the target of local flow. |
-| simple.cpp:23:35:23:35 | Chi | PostUpdateNode should not be the target of local flow. |
-| simple.cpp:65:5:65:22 | Store | PostUpdateNode should not be the target of local flow. |
-| simple.cpp:83:9:83:28 | Chi | PostUpdateNode should not be the target of local flow. |
-| simple.cpp:83:9:83:28 | Store | PostUpdateNode should not be the target of local flow. |
-| simple.cpp:92:5:92:22 | Store | PostUpdateNode should not be the target of local flow. |
-| struct_init.c:20:20:20:29 | Chi | PostUpdateNode should not be the target of local flow. |
-| struct_init.c:20:34:20:34 | Chi | PostUpdateNode should not be the target of local flow. |
-| struct_init.c:27:7:27:16 | Chi | PostUpdateNode should not be the target of local flow. |
-| struct_init.c:27:21:27:21 | Chi | PostUpdateNode should not be the target of local flow. |
-| struct_init.c:28:5:28:7 | Chi | PostUpdateNode should not be the target of local flow. |
-| struct_init.c:36:10:36:24 | Chi | PostUpdateNode should not be the target of local flow. |
-| struct_init.c:40:20:40:29 | Chi | PostUpdateNode should not be the target of local flow. |
-| struct_init.c:40:34:40:34 | Chi | PostUpdateNode should not be the target of local flow. |
-| struct_init.c:42:7:42:16 | Chi | PostUpdateNode should not be the target of local flow. |
-| struct_init.c:42:21:42:21 | Chi | PostUpdateNode should not be the target of local flow. |
-| struct_init.c:43:5:43:7 | Chi | PostUpdateNode should not be the target of local flow. |
+| A.cpp:9:9:9:9 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:9:9:9:9 | ConvertToNonVirtualBase [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:9:9:9:9 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:14:9:14:9 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:14:9:14:9 | ConvertToNonVirtualBase [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:14:9:14:9 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:25:7:25:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:25:13:25:13 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:27:22:27:25 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:27:28:27:28 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:28:16:28:30 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:31:7:31:22 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:31:14:31:21 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:31:14:31:21 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:31:14:31:21 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:31:14:31:21 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:31:20:31:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:31:20:31:20 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:38:7:38:8 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:38:7:38:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:39:7:39:8 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:39:7:39:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:40:5:40:6 | cc [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:40:8:40:13 | 0 [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:40:8:40:13 | cc [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:40:15:40:21 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:41:5:41:6 | ct [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:41:8:41:13 | ct [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:41:8:41:13 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:41:15:41:21 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:41:15:41:21 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:41:15:41:21 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:41:15:41:21 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:42:5:42:8 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:42:10:42:12 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:42:10:42:12 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:42:11:42:12 | cc [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:43:5:43:8 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:43:10:43:12 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:43:10:43:12 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:43:11:43:12 | ct [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:47:8:47:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:47:12:47:18 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:47:12:47:18 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:47:12:47:18 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:48:8:48:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:48:12:48:18 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:48:20:48:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:48:20:48:20 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:49:5:49:8 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:49:10:49:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:49:10:49:13 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:49:13:49:13 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:49:13:49:13 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:54:8:54:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:54:12:54:18 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:54:12:54:18 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:54:12:54:18 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:55:5:55:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:55:5:55:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:55:8:55:10 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:55:8:55:10 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:55:12:55:19 | (C *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:55:12:55:19 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:55:12:55:19 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:55:12:55:19 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:56:5:56:8 | call to get [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:56:10:56:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:56:10:56:10 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:56:10:56:17 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:56:13:56:15 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:56:13:56:15 | call to get [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:57:5:57:8 | call to get [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:57:10:57:32 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:57:11:57:24 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:57:11:57:24 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:57:11:57:24 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:57:11:57:24 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:57:17:57:23 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:57:17:57:23 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:57:17:57:23 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:57:28:57:30 | call to get [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:57:28:57:30 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:62:8:62:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:62:13:62:19 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:62:13:62:19 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:62:13:62:19 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:64:5:64:6 | b2 [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:64:10:64:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:64:10:64:15 | b1 [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:64:10:64:15 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:64:10:64:15 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:64:10:64:15 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:64:17:64:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:64:17:64:18 | b1 [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:64:21:64:28 | (C *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:64:21:64:28 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:64:21:64:28 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:64:21:64:28 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:65:5:65:8 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:65:10:65:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:65:10:65:14 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:65:14:65:14 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:65:14:65:14 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:66:5:66:8 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:66:10:66:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:66:10:66:14 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:66:14:66:14 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:66:14:66:14 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:71:8:71:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:71:13:71:19 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:71:13:71:19 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:71:13:71:19 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:73:5:73:6 | b2 [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:73:10:73:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:73:10:73:19 | b1 [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:73:10:73:19 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:73:10:73:19 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:73:10:73:19 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:73:21:73:22 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:73:21:73:22 | b1 [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:73:25:73:32 | (C *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:73:25:73:32 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:73:25:73:32 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:73:25:73:32 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:74:5:74:8 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:74:10:74:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:74:10:74:14 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:74:14:74:14 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:74:14:74:14 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:75:5:75:8 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:75:10:75:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:75:10:75:14 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:75:14:75:14 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:75:14:75:14 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:81:5:81:6 | b2 [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:81:10:81:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:81:10:81:15 | b1 [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:81:10:81:15 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:81:10:81:15 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:81:10:81:15 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:81:17:81:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:81:17:81:18 | b1 [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:81:21:81:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:81:21:81:21 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:82:5:82:25 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:82:12:82:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:82:12:82:12 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:82:12:82:12 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:82:12:82:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:82:12:82:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:87:9:87:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:87:9:87:9 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:87:9:87:9 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:89:10:89:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:89:15:89:21 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:89:15:89:21 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:89:15:89:21 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:90:7:90:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:90:7:90:8 | b2 [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:90:11:90:13 | b2 [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:90:11:90:13 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:90:15:90:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:90:15:90:15 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:91:7:91:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:93:5:93:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:98:8:98:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:98:12:98:18 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:98:12:98:18 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:99:9:99:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:99:14:99:21 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:99:14:99:21 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:99:14:99:21 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:100:5:100:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:100:9:100:9 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:101:5:101:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:101:5:101:6 | c1 [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:101:5:101:6 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:101:5:101:6 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:101:8:101:9 | (C *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:101:8:101:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:101:8:101:9 | c1 [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:105:9:105:38 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:107:7:107:10 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:107:12:107:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:107:12:107:16 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:107:16:107:16 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:107:16:107:16 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:110:9:110:38 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:112:7:112:8 | cc [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:116:7:116:8 | cc [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:116:12:116:19 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:116:12:116:19 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:116:12:116:19 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:118:9:118:39 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:120:7:120:10 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:120:12:120:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:120:12:120:16 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:120:16:120:16 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:120:16:120:16 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:126:5:126:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:126:5:126:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:126:8:126:10 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:126:8:126:10 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:126:12:126:18 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:126:12:126:18 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:126:12:126:18 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:130:8:130:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:130:12:130:18 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:130:12:130:18 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:130:12:130:18 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:131:5:131:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:131:5:131:6 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:131:5:131:6 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:131:5:131:6 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:131:8:131:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:131:8:131:8 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:132:5:132:8 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:132:10:132:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:132:10:132:13 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:132:13:132:13 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:132:13:132:13 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:142:7:142:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:142:10:142:10 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:142:14:142:20 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:142:14:142:20 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:142:14:142:20 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:143:7:143:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:143:13:143:13 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:143:17:143:31 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:143:17:143:31 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:143:25:143:31 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:143:25:143:31 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:143:25:143:31 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:150:8:150:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:150:12:150:18 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:150:12:150:18 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:150:12:150:18 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:151:8:151:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:151:12:151:24 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:151:12:151:24 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:151:12:151:24 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:151:12:151:24 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:151:18:151:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:151:18:151:18 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:151:21:151:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:151:21:151:21 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:151:21:151:21 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:152:5:152:8 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:152:10:152:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:152:10:152:13 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:152:13:152:13 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:152:13:152:13 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:153:5:153:8 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:153:10:153:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:153:10:153:16 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:153:13:153:13 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:153:16:153:16 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:153:16:153:16 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:154:5:154:8 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:154:10:154:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:154:10:154:13 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:154:13:154:13 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:154:13:154:13 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:159:8:159:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:159:12:159:18 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:159:12:159:18 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:159:12:159:18 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:160:13:160:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:160:18:160:60 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:160:18:160:60 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:160:18:160:60 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:160:18:160:60 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:160:18:160:60 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:160:29:160:29 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:160:29:160:29 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:160:32:160:59 | 0 [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:160:32:160:59 | 0 [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:160:32:160:59 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:160:32:160:59 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:160:32:160:59 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:160:43:160:49 | (B *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:160:52:160:58 | (MyList *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:161:13:161:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:161:18:161:40 | 0 [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:161:18:161:40 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:161:18:161:40 | l1 [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:161:18:161:40 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:161:18:161:40 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:161:29:161:35 | (B *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:161:38:161:39 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:161:38:161:39 | l1 [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:162:13:162:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:162:18:162:40 | 0 [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:162:18:162:40 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:162:18:162:40 | l2 [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:162:18:162:40 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:162:18:162:40 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:162:29:162:35 | (B *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:162:38:162:39 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:162:38:162:39 | l2 [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:163:5:163:8 | head [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:163:10:163:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:163:10:163:17 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:163:14:163:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:163:14:163:17 | head [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:164:5:164:8 | head [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:164:10:164:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:164:10:164:23 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:164:14:164:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:164:20:164:23 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:164:20:164:23 | head [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:165:5:165:8 | head [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:165:10:165:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:165:10:165:29 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:165:14:165:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:165:20:165:23 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:165:26:165:29 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:165:26:165:29 | head [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:166:5:166:8 | head [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:166:10:166:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:166:10:166:35 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:166:14:166:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:166:20:166:23 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:166:26:166:29 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:166:32:166:35 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:166:32:166:35 | head [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:167:18:167:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:167:40:167:40 | l [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:169:7:169:10 | head [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:169:12:169:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:169:12:169:18 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:169:15:169:18 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:169:15:169:18 | head [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:174:14:174:63 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:183:7:183:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:183:7:183:10 | head [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:184:7:184:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:184:13:184:16 | next [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:6:11:6:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:6:15:6:24 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:6:15:6:24 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:7:11:7:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:7:16:7:35 | 0 [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:7:16:7:35 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:7:16:7:35 | e [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:7:16:7:35 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:7:16:7:35 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:7:25:7:25 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:7:25:7:25 | e [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:7:28:7:34 | (Elem *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:8:11:8:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:8:16:8:27 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:8:16:8:27 | b1 [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:8:16:8:27 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:8:16:8:27 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:8:25:8:26 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:8:25:8:26 | b1 [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:9:5:9:8 | elem1 [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:9:10:9:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:9:10:9:24 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:9:14:9:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:9:20:9:24 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:9:20:9:24 | elem1 [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:10:5:10:8 | elem2 [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:10:10:10:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:10:10:10:24 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:10:14:10:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:10:20:10:24 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:10:20:10:24 | elem2 [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:15:11:15:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:15:15:15:27 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:15:15:15:27 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:16:11:16:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:16:16:16:38 | 0 [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:16:16:16:38 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:16:16:16:38 | e [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:16:16:16:38 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:16:16:16:38 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:16:28:16:34 | (Elem *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:16:37:16:37 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:16:37:16:37 | e [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:17:11:17:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:17:16:17:27 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:17:16:17:27 | b1 [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:17:16:17:27 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:17:16:17:27 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:17:25:17:26 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:17:25:17:26 | b1 [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:18:5:18:8 | elem1 [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:18:10:18:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:18:10:18:24 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:18:14:18:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:18:20:18:24 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:18:20:18:24 | elem1 [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:19:5:19:8 | elem2 [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:19:10:19:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:19:10:19:24 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:19:14:19:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:19:20:19:24 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:19:20:19:24 | elem2 [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:35:7:35:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:35:13:35:17 | elem1 [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:36:7:36:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:36:13:36:17 | elem2 [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:46:7:46:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:46:13:46:16 | box1 [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:18:8:18:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:18:12:18:18 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:18:12:18:18 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:18:12:18:18 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:19:5:19:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:19:5:19:5 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:19:8:19:11 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:22:9:22:22 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:22:12:22:21 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:22:12:22:21 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:24:5:24:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:24:11:24:12 | s3 [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:24:16:24:25 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:24:16:24:25 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:29:5:29:8 | s1 [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:29:10:29:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:29:10:29:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:29:10:29:11 | s1 [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:30:5:30:8 | s2 [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:30:10:30:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:30:10:30:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:30:10:30:11 | s2 [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:31:5:31:8 | s3 [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:31:10:31:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:31:10:31:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:31:10:31:11 | s3 [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:32:5:32:8 | s4 [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:32:10:32:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:32:10:32:11 | s4 [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:9:21:9:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:9:21:9:24 | elem [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:10:23:10:34 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:11:29:11:32 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:11:29:11:32 | elem [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:16:21:16:23 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:16:21:16:23 | box [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:17:23:17:33 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:18:29:18:31 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:18:29:18:31 | box [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:22:5:22:8 | call to getElem [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:22:10:22:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:22:10:22:11 | b2 [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:22:10:22:33 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:22:14:22:20 | b2 [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:22:14:22:20 | call to getBox1 [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:22:25:22:31 | call to getBox1 [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:22:25:22:31 | call to getElem [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:28:11:28:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:28:15:28:24 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:28:15:28:24 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:29:11:29:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:29:15:29:41 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:29:15:29:41 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:29:15:29:41 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:29:15:29:41 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:29:24:29:40 | 0 [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:29:24:29:40 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:29:24:29:40 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:29:24:29:40 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:29:33:29:39 | (Elem *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:30:5:30:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:30:8:30:10 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:30:13:30:16 | elem [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:31:5:31:12 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:31:14:31:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:31:14:31:14 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:35:11:35:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:35:15:35:24 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:35:15:35:24 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:36:11:36:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:36:15:36:41 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:36:15:36:41 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:36:15:36:41 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:36:15:36:41 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:36:24:36:40 | 0 [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:36:24:36:40 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:36:24:36:40 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:36:24:36:40 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:36:33:36:39 | (Elem *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:37:5:37:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:37:8:37:10 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:37:8:37:10 | box [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:37:13:37:19 | box [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:37:13:37:19 | e [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:37:21:37:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:37:21:37:21 | e [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:38:5:38:12 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:38:14:38:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:38:14:38:14 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:42:11:42:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:42:15:42:24 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:42:15:42:24 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:43:11:43:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:43:15:43:41 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:43:15:43:41 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:43:15:43:41 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:43:15:43:41 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:43:24:43:40 | 0 [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:43:24:43:40 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:43:24:43:40 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:43:24:43:40 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:43:33:43:39 | (Elem *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:44:5:44:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:44:5:44:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:44:8:44:14 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:44:19:44:22 | elem [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:45:5:45:12 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:45:14:45:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:45:14:45:14 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:49:11:49:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:49:15:49:24 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:49:15:49:24 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:50:11:50:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:50:15:50:41 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:50:15:50:41 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:50:15:50:41 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:50:15:50:41 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:50:24:50:40 | 0 [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:50:24:50:40 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:50:24:50:40 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:50:24:50:40 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:50:33:50:39 | (Elem *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:51:5:51:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:51:5:51:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:51:8:51:14 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:51:8:51:14 | call to getBox1 [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:51:19:51:25 | call to getBox1 [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:51:19:51:25 | e [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:51:27:51:27 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:51:27:51:27 | e [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:52:5:52:12 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:52:14:52:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:52:14:52:14 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:56:11:56:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:56:15:56:24 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:56:15:56:24 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:57:5:57:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:57:5:57:12 | boxfield [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:57:16:57:42 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:57:16:57:42 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:57:16:57:42 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:57:16:57:42 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:57:25:57:41 | 0 [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:57:25:57:41 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:57:25:57:41 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:57:25:57:41 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:57:34:57:40 | (Elem *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:58:5:58:12 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:58:5:58:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:58:15:58:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:58:20:58:23 | elem [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:59:5:59:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:59:5:59:7 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:59:5:59:7 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:64:5:64:8 | elem [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:64:10:64:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:64:10:64:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:64:10:64:28 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:64:20:64:22 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:64:25:64:28 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:64:25:64:28 | elem [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:21:5:21:8 | buffer [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:21:10:21:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:21:18:21:23 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:21:18:21:23 | buffer [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:28:5:28:19 | raw [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:28:21:28:23 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:28:21:28:23 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:28:21:28:23 | raw [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:29:5:29:19 | buffer [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:29:21:29:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:29:21:29:29 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:29:24:29:29 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:29:24:29:29 | buffer [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:30:5:30:19 | buffer [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:30:21:30:33 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:30:28:30:33 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:30:28:30:33 | buffer [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:31:5:31:8 | raw [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:31:10:31:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:31:10:31:12 | raw [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:32:5:32:8 | buffer [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:32:10:32:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:32:13:32:18 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:32:13:32:18 | buffer [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:33:5:33:16 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:33:18:33:19 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:33:19:33:19 | p [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:9:3:9:3 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:9:6:9:7 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:13:3:13:3 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:13:3:13:3 | s [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:13:5:13:6 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:17:5:17:6 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:21:9:21:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:21:9:21:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:22:9:22:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:22:9:22:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:23:9:23:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:23:9:23:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:25:3:25:15 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:25:17:25:19 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:25:18:25:19 | s1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:26:3:26:17 | s2 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:26:19:26:20 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:26:19:26:20 | s2 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:35:9:35:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:35:9:35:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:36:6:36:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:37:3:37:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:37:3:37:6 | ref1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:37:8:37:9 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:40:9:40:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:40:9:40:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:41:6:41:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:42:6:42:7 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:47:9:47:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:47:9:47:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:48:5:48:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:49:9:49:10 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:52:9:52:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:52:9:52:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:53:5:53:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:54:6:54:7 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:59:9:59:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:59:9:59:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:60:6:60:7 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:61:5:61:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:70:17:70:24 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:70:17:70:24 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:71:5:71:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:72:5:72:6 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:77:17:77:24 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:77:17:77:24 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:78:6:78:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:79:3:79:3 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:79:6:79:7 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:84:17:84:24 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:84:17:84:24 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:85:6:85:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:86:3:86:3 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:86:3:86:3 | s [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:86:5:86:6 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:91:17:91:24 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:91:17:91:24 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:92:7:92:8 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:98:5:98:6 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:100:5:100:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:101:8:101:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:106:3:106:5 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:106:4:106:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:106:4:106:5 | pa [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:111:3:111:13 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:111:15:111:19 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:111:18:111:19 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:121:3:121:13 | xs [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:121:15:121:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:121:15:121:16 | xs [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:126:3:126:13 | ... - ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:126:15:126:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:126:15:126:16 | xs [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:126:15:126:20 | ... - ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:131:3:131:13 | xs [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:131:15:131:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:131:15:131:16 | xs [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:136:3:136:13 | + ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:136:15:136:17 | + ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:136:16:136:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:136:16:136:17 | xs [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:141:3:141:13 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:141:17:141:20 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:141:17:141:20 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:147:3:147:13 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:147:15:147:22 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:147:16:147:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:147:16:147:16 | s [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:147:21:147:22 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:158:3:158:13 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:158:15:158:20 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:158:17:158:20 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:164:3:164:13 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:164:15:164:20 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:164:17:164:20 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:175:3:175:13 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:175:15:175:22 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:175:21:175:22 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:181:3:181:13 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:181:15:181:22 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:181:21:181:22 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:187:3:187:13 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:187:15:187:22 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:187:21:187:22 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:188:6:188:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:194:3:194:13 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:194:15:194:22 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:194:21:194:22 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:195:6:195:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:200:3:200:13 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:200:15:200:24 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:200:16:200:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:200:23:200:24 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:205:3:205:13 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:205:15:205:24 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:205:16:205:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:205:23:205:24 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:5:9:5:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:5:18:5:23 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:5:18:5:23 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:6:3:6:5 | arr [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:6:3:6:5 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:6:3:6:8 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:7:3:7:6 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:7:8:7:10 | arr [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:7:8:7:10 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:7:8:7:13 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:7:8:7:13 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:8:3:8:6 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:8:8:8:10 | arr [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:8:8:8:10 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:8:8:8:13 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:8:8:8:13 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:9:3:9:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:9:8:9:11 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:9:9:9:11 | arr [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:9:9:9:11 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:10:3:10:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:10:8:10:15 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:10:9:10:15 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:10:10:10:12 | arr [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:10:10:10:12 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:10:10:10:15 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:14:9:14:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:14:18:14:23 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:14:18:14:23 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:15:3:15:10 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:15:4:15:10 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:15:5:15:7 | arr [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:15:5:15:7 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:15:5:15:10 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:16:3:16:6 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:16:8:16:10 | arr [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:16:8:16:10 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:16:8:16:13 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:16:8:16:13 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:17:3:17:6 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:17:8:17:10 | arr [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:17:8:17:10 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:17:8:17:13 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:17:8:17:13 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:36:3:36:14 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:36:12:36:14 | arr [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:36:19:36:22 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:37:3:37:6 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:37:8:37:19 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:37:17:37:19 | arr [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:37:24:37:27 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:37:24:37:27 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:38:3:38:6 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:38:8:38:19 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:38:17:38:19 | arr [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:38:24:38:27 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:38:24:38:27 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:42:3:42:17 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:42:5:42:12 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:42:15:42:17 | arr [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:42:22:42:25 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:43:3:43:6 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:43:8:43:22 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:43:10:43:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:43:20:43:22 | arr [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:43:27:43:30 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:43:27:43:30 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:44:3:44:6 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:44:8:44:22 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:44:10:44:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:44:20:44:22 | arr [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:44:27:44:30 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:44:27:44:30 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:48:5:48:12 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:48:15:48:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:48:15:48:17 | ptr [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:48:22:48:25 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:49:3:49:6 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:49:10:49:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:49:20:49:22 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:49:20:49:22 | ptr [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:49:27:49:30 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:49:27:49:30 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:50:3:50:6 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:50:10:50:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:50:20:50:22 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:50:20:50:22 | ptr [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:50:27:50:30 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:50:27:50:30 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:12:5:12:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:12:8:12:8 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:16:5:16:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:16:11:16:11 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:20:5:20:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:20:5:20:8 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:20:11:20:21 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:20:11:20:21 | value [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:20:23:20:27 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:20:23:20:27 | value [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:24:5:24:17 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:24:5:24:17 | value [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:24:19:24:22 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:24:19:24:22 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:24:25:24:29 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:24:25:24:29 | value [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:32:5:32:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:36:5:36:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:40:5:40:31 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:44:5:44:31 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:50:3:50:3 | s [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:50:5:50:15 | call to user_input [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:50:5:50:15 | s [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:50:17:50:26 | call to user_input [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:51:3:51:6 | call to getDirectly [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:51:10:51:20 | call to getDirectly [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:56:3:56:3 | s [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:56:5:56:17 | call to user_input [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:56:5:56:17 | s [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:56:19:56:28 | call to user_input [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:57:3:57:6 | call to getIndirectly [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:57:10:57:22 | call to getIndirectly [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:62:3:62:3 | s [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:62:5:62:23 | call to user_input [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:62:5:62:23 | s [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:62:25:62:34 | call to user_input [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:63:3:63:6 | call to getThroughNonMember [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:63:10:63:28 | call to getThroughNonMember [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:68:3:68:15 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:68:3:68:15 | call to user_input [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:68:17:68:18 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:68:18:68:18 | s [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:68:21:68:30 | call to user_input [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:69:3:69:6 | call to nonMemberGetA [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:69:8:69:20 | call to nonMemberGetA [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:84:3:84:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:84:10:84:10 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:88:3:88:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:88:3:88:7 | inner [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:88:9:88:9 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:92:3:92:5 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:92:4:92:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:92:4:92:5 | pa [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:96:3:96:4 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:96:3:96:4 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:96:3:96:4 | pa [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:102:3:102:19 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:102:21:102:39 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:102:28:102:39 | inner_nested [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:103:3:103:19 | inner_ptr [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:103:27:103:35 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:103:27:103:35 | inner_ptr [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:104:3:104:13 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:104:15:104:22 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:104:22:104:22 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:106:3:106:19 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:106:21:106:41 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:106:22:106:27 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:106:30:106:41 | inner_nested [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:107:3:107:19 | inner_ptr [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:107:21:107:26 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:107:29:107:37 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:107:29:107:37 | inner_ptr [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:108:3:108:13 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:108:15:108:24 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:108:16:108:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:108:24:108:24 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:110:3:110:6 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:110:27:110:27 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:110:27:110:27 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:111:3:111:6 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:111:14:111:22 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:111:25:111:25 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:111:25:111:25 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:112:3:112:6 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:112:14:112:14 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:112:14:112:14 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:114:3:114:6 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:114:8:114:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:114:29:114:29 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:114:29:114:29 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:115:3:115:6 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:115:8:115:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:115:16:115:24 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:115:27:115:27 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:115:27:115:27 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:116:3:116:6 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:116:8:116:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:116:16:116:16 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:116:16:116:16 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:122:3:122:19 | inner_nested [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:122:21:122:38 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:122:27:122:38 | inner_nested [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:123:3:123:19 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:123:21:123:36 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:123:21:123:36 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:123:28:123:36 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:123:28:123:36 | inner_ptr [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:124:3:124:13 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:124:15:124:21 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:124:21:124:21 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:126:3:126:19 | inner_nested [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:126:21:126:26 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:126:21:126:40 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:126:29:126:40 | inner_nested [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:127:3:127:19 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:127:21:127:38 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:127:21:127:38 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:127:22:127:27 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:127:30:127:38 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:127:30:127:38 | inner_ptr [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:128:3:128:13 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:128:15:128:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:128:15:128:23 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:128:23:128:23 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:130:3:130:6 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:130:27:130:27 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:130:27:130:27 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:131:3:131:6 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:131:14:131:22 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:131:25:131:25 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:131:25:131:25 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:132:3:132:6 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:132:14:132:14 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:132:14:132:14 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:134:3:134:6 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:134:8:134:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:134:29:134:29 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:134:29:134:29 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:135:3:135:6 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:135:8:135:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:135:16:135:24 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:135:27:135:27 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:135:27:135:27 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:136:3:136:6 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:136:8:136:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:136:16:136:16 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:136:16:136:16 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:9:13:9:22 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:10:13:10:22 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:11:22:11:23 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:11:22:11:23 | a_ [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:12:22:12:23 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:12:22:12:23 | b_ [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:14:23:14:27 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:14:30:14:34 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:22:11:22:17 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:22:11:22:17 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:25:7:25:7 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:25:7:25:7 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:33:3:33:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:42:8:42:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:42:8:42:8 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:42:16:42:16 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:42:18:42:18 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:43:8:43:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:43:8:43:8 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:43:16:43:16 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:43:18:43:18 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:48:9:48:10 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:48:9:48:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:49:9:49:10 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:49:9:49:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:50:9:50:10 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:50:9:50:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:51:9:51:10 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:51:9:51:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:53:12:53:12 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:53:14:53:17 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:54:12:54:12 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:54:14:54:17 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:55:12:55:12 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:55:14:55:17 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:56:12:56:12 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:56:14:56:17 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:59:3:59:5 | b1 [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:59:7:59:8 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:59:7:59:8 | b1 [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:62:3:62:5 | b2 [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:62:7:62:8 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:62:7:62:8 | b2 [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:65:3:65:5 | b3 [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:65:7:65:8 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:65:7:65:8 | b3 [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:68:3:68:5 | b4 [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:68:7:68:8 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:68:7:68:8 | b4 [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:10:3:10:7 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:10:4:10:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:10:4:10:5 | ra [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:10:7:10:7 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:10:7:10:7 | p [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:19:3:19:17 | raw [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:19:19:19:21 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:19:19:19:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:19:19:19:21 | raw [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:20:3:20:6 | raw [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:20:8:20:10 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:20:8:20:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:20:8:20:10 | raw [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:24:3:24:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:28:6:28:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:29:3:29:4 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:29:7:29:7 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:35:3:35:4 | pa [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:35:8:35:28 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:35:8:35:28 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:36:3:36:4 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:36:7:36:7 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:45:34:45:43 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:52:15:52:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:53:3:53:4 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:53:7:53:10 | next [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:54:3:54:4 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:54:7:54:10 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:54:13:54:13 | y [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:59:15:59:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:59:20:59:39 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:59:20:59:39 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:59:20:59:39 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:59:20:59:39 | next [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:59:35:59:38 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:59:35:59:38 | next [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:60:3:60:4 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:60:7:60:10 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| conflated.cpp:60:13:60:13 | y [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:5:5:5:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:18:15:18:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:19:15:19:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:20:24:20:25 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:20:24:20:25 | a_ [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:21:24:21:25 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:21:24:21:25 | b_ [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:23:25:23:29 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:23:32:23:36 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:28:10:28:10 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:28:10:28:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:28:10:28:10 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:28:12:28:12 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:29:10:29:10 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:29:10:29:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:29:10:29:10 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:29:12:29:12 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:34:9:34:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:34:11:34:26 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:35:9:35:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:35:11:35:26 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:36:9:36:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:36:11:36:37 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:37:9:37:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:37:11:37:15 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:40:5:40:7 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:40:9:40:9 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:40:9:40:9 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:43:5:43:7 | g [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:43:9:43:9 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:43:9:43:9 | g [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:46:5:46:7 | h [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:46:9:46:9 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:46:9:46:9 | h [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:49:5:49:7 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:49:9:49:9 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:49:9:49:9 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:9:30:9:33 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:9:36:9:36 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:12:49:12:53 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:12:56:12:56 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:13:51:13:55 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:13:51:13:55 | inner [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:13:57:13:57 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:18:25:18:37 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:22:5:22:9 | outer [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:22:11:22:18 | outer [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:22:23:22:23 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:23:5:23:8 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:23:16:23:20 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:23:23:23:23 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:23:23:23:23 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:27:5:27:9 | outer [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:27:11:27:18 | call to getInner [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:27:11:27:18 | outer [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:27:23:27:26 | call to getInner [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:27:23:27:26 | call to user_input [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:27:28:27:37 | call to user_input [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:28:5:28:8 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:28:16:28:20 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:28:23:28:23 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:28:23:28:23 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:32:5:32:15 | call to getInner [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:32:5:32:15 | call to user_input [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:32:17:32:21 | outer [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:32:23:32:30 | call to getInner [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:32:23:32:30 | outer [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:32:35:32:44 | call to user_input [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:33:5:33:8 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:33:16:33:20 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:33:23:33:23 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:33:23:33:23 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:37:5:37:17 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:37:5:37:17 | call to user_input [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:37:19:37:35 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:37:19:37:35 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:37:20:37:24 | outer [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:37:26:37:33 | call to getInner [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:37:26:37:33 | outer [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:37:38:37:47 | call to user_input [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:38:5:38:8 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:38:16:38:20 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:38:23:38:23 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:38:23:38:23 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:42:7:42:11 | outer [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:42:13:42:20 | call to getInner [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:42:13:42:20 | outer [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:42:25:42:25 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:43:5:43:8 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:43:16:43:20 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:43:23:43:23 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:43:23:43:23 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:47:6:47:11 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:47:7:47:11 | outer [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:47:15:47:22 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:47:27:47:27 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:48:5:48:8 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:48:16:48:20 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:48:23:48:23 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:48:23:48:23 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:24:42:24:56 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:25:11:25:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:26:5:26:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:27:5:27:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:30:9:30:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:31:9:31:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:32:9:32:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:34:9:34:12 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:34:10:34:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:34:10:34:10 | d [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:34:10:34:12 | ... ++ [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:34:17:34:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:36:5:36:23 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:39:5:39:96 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:42:5:42:10 | o [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:42:20:42:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:42:20:42:20 | o [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:48:14:48:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:48:34:48:34 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:49:9:49:15 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:49:13:49:15 | bar [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:49:20:49:22 | baz [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:51:9:51:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:53:9:53:15 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:53:13:53:15 | bar [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:53:20:53:22 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:53:35:53:43 | bufferLen [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:54:9:54:14 | buffer [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:54:16:54:22 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:54:16:54:47 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:54:16:54:47 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:54:20:54:22 | bar [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:54:27:54:29 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:54:42:54:47 | buffer [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:58:13:58:22 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:60:9:60:14 | dst [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:60:16:60:18 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:60:16:60:18 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:60:16:60:18 | dst [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:61:9:61:12 | bufferLen [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:61:14:61:55 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:61:21:61:27 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:61:25:61:27 | bar [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:61:32:61:34 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:61:47:61:55 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:61:47:61:55 | bufferLen [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:65:9:65:12 | buffer [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:65:14:65:52 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:65:21:65:27 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:65:21:65:52 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:65:25:65:27 | bar [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:65:32:65:34 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:65:47:65:52 | buffer [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:66:9:66:12 | dst [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:66:14:66:23 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:66:21:66:23 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:66:21:66:23 | dst [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:67:9:67:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:69:5:69:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:5:5:5:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:18:15:18:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:19:15:19:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:20:24:20:25 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:20:24:20:25 | a_ [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:21:24:21:25 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:21:24:21:25 | b_ [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:23:25:23:29 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:23:32:23:36 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:28:10:28:10 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:28:10:28:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:28:10:28:10 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:28:12:28:12 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:29:10:29:10 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:29:10:29:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:29:10:29:10 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:29:12:29:12 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:34:9:34:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:34:11:34:15 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:35:9:35:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:35:11:35:15 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:36:9:36:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:36:11:36:15 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:37:9:37:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:37:11:37:15 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:39:5:39:5 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:39:7:39:10 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:40:5:40:5 | g [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:40:7:40:10 | g [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:41:5:41:5 | h [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:41:7:41:10 | h [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:42:5:42:5 | h [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:42:7:42:10 | h [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:45:5:45:7 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:45:9:45:9 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:45:9:45:9 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:48:5:48:7 | g [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:48:9:48:9 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:48:9:48:9 | g [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:51:5:51:7 | h [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:51:9:51:9 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:51:9:51:9 | h [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:54:5:54:7 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:54:9:54:9 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:54:9:54:9 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:65:7:65:7 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:66:7:66:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:79:9:79:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:83:9:83:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:83:12:83:13 | f1 [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:84:14:84:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:84:14:84:20 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:84:14:84:20 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:92:7:92:7 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:93:15:93:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:15:3:15:6 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:15:8:15:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:15:12:15:12 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:15:12:15:12 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:16:3:16:6 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:16:8:16:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:16:12:16:12 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:16:12:16:12 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:20:17:20:36 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:20:17:20:36 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:22:3:22:6 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:22:11:22:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:22:11:22:11 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:23:3:23:6 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:23:11:23:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:23:11:23:11 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:24:3:24:8 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:24:10:24:12 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:24:11:24:12 | ab [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:26:23:29:3 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:27:5:27:23 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:27:5:27:23 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:31:3:31:6 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:31:23:31:23 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:31:23:31:23 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:32:3:32:6 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:32:23:32:23 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:32:23:32:23 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:33:3:33:6 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:33:14:33:22 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:33:25:33:25 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:33:25:33:25 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:34:3:34:6 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:34:14:34:22 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:34:25:34:25 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:34:25:34:25 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:36:3:36:8 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:36:10:36:24 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:36:17:36:24 | nestedAB [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:40:17:40:36 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:40:17:40:36 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:41:23:44:3 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:42:5:42:23 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:42:5:42:23 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:46:3:46:8 | pointerAB [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:46:16:46:24 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:46:16:46:24 | pointerAB [post update] | PostUpdateNode should not be the target of local flow. |
diff --git a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected
index 897d2e72fe5..703bf4d4eb1 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected
+++ b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected
@@ -1,733 +1,1832 @@
edges
-| A.cpp:23:10:23:10 | c | A.cpp:25:7:25:17 | Chi [c] |
-| A.cpp:27:17:27:17 | c | A.cpp:27:22:27:32 | Chi [c] |
-| A.cpp:28:8:28:10 | *#this [c] | A.cpp:28:29:28:29 | Store |
-| A.cpp:55:5:55:5 | set output argument [c] | A.cpp:56:10:56:10 | b indirection [c] |
+| A.cpp:23:10:23:10 | c | A.cpp:25:13:25:13 | c [post update] |
+| A.cpp:25:13:25:13 | c [post update] | A.cpp:25:7:25:10 | this [post update] [c] |
+| A.cpp:27:17:27:17 | c | A.cpp:27:28:27:28 | c [post update] |
+| A.cpp:27:28:27:28 | c [post update] | A.cpp:27:22:27:25 | this [post update] [c] |
+| A.cpp:28:8:28:10 | this [c] | A.cpp:28:23:28:26 | this [read] [c] |
+| A.cpp:28:23:28:26 | this [read] [c] | A.cpp:28:29:28:29 | FieldAddress [read] |
+| A.cpp:28:29:28:29 | FieldAddress [read] | A.cpp:28:8:28:10 | ReturnValue |
+| A.cpp:29:23:29:23 | c | A.cpp:31:14:31:21 | c |
+| A.cpp:31:14:31:21 | c | A.cpp:23:10:23:10 | c |
+| A.cpp:31:14:31:21 | c | A.cpp:31:14:31:21 | new [post update] [c] |
+| A.cpp:31:14:31:21 | new [post update] [c] | A.cpp:29:15:29:18 | ReturnValue [c] |
+| A.cpp:47:12:47:18 | new | A.cpp:48:12:48:18 | c |
+| A.cpp:48:12:48:18 | c | A.cpp:29:23:29:23 | c |
+| A.cpp:48:12:48:18 | c | A.cpp:48:12:48:18 | call to make [c] |
+| A.cpp:48:12:48:18 | call to make [c] | A.cpp:49:10:49:10 | b [read] [c] |
+| A.cpp:49:10:49:10 | b [read] [c] | A.cpp:49:13:49:13 | FieldAddress [read] |
+| A.cpp:49:13:49:13 | FieldAddress [read] | A.cpp:49:10:49:13 | (void *)... |
+| A.cpp:55:8:55:10 | b [post update] [c] | A.cpp:56:13:56:15 | b [c] |
| A.cpp:55:8:55:10 | new | A.cpp:27:17:27:17 | c |
-| A.cpp:55:8:55:10 | new | A.cpp:55:5:55:5 | set output argument [c] |
+| A.cpp:55:8:55:10 | new | A.cpp:55:8:55:10 | b [post update] [c] |
| A.cpp:55:12:55:19 | (C *)... | A.cpp:55:8:55:10 | new |
| A.cpp:55:12:55:19 | new | A.cpp:55:8:55:10 | new |
-| A.cpp:56:10:56:10 | b indirection [c] | A.cpp:28:8:28:10 | *#this [c] |
-| A.cpp:56:10:56:10 | b indirection [c] | A.cpp:56:13:56:15 | call to get |
-| A.cpp:57:10:57:25 | new indirection [c] | A.cpp:28:8:28:10 | *#this [c] |
-| A.cpp:57:10:57:25 | new indirection [c] | A.cpp:57:28:57:30 | call to get |
-| A.cpp:57:11:57:24 | B output argument [c] | A.cpp:57:10:57:25 | new indirection [c] |
+| A.cpp:56:13:56:15 | b [c] | A.cpp:28:8:28:10 | this [c] |
+| A.cpp:56:13:56:15 | b [c] | A.cpp:56:13:56:15 | call to get |
+| A.cpp:56:13:56:15 | b [c] | A.cpp:56:13:56:15 | call to get |
+| A.cpp:56:13:56:15 | call to get | A.cpp:56:10:56:17 | (void *)... |
| A.cpp:57:11:57:24 | new | A.cpp:23:10:23:10 | c |
-| A.cpp:57:11:57:24 | new | A.cpp:57:11:57:24 | B output argument [c] |
+| A.cpp:57:11:57:24 | new | A.cpp:57:11:57:24 | new [post update] [c] |
+| A.cpp:57:11:57:24 | new [post update] [c] | A.cpp:57:28:57:30 | new [c] |
| A.cpp:57:17:57:23 | new | A.cpp:57:11:57:24 | new |
-| A.cpp:98:12:98:18 | new | A.cpp:100:5:100:13 | Chi [a] |
-| A.cpp:100:5:100:13 | Chi [a] | A.cpp:101:8:101:9 | c1 indirection [a] |
-| A.cpp:101:8:101:9 | c1 indirection [a] | A.cpp:103:14:103:14 | *c [a] |
-| A.cpp:103:14:103:14 | *c [a] | A.cpp:107:16:107:16 | a |
-| A.cpp:126:5:126:5 | Chi [c] | A.cpp:131:8:131:8 | f7 output argument [c] |
-| A.cpp:126:5:126:5 | set output argument [c] | A.cpp:126:5:126:5 | Chi [c] |
+| A.cpp:57:28:57:30 | call to get | A.cpp:57:10:57:32 | (void *)... |
+| A.cpp:57:28:57:30 | new [c] | A.cpp:28:8:28:10 | this [c] |
+| A.cpp:57:28:57:30 | new [c] | A.cpp:57:28:57:30 | call to get |
+| A.cpp:57:28:57:30 | new [c] | A.cpp:57:28:57:30 | call to get |
+| A.cpp:64:10:64:15 | call to setOnB [c] | A.cpp:66:10:66:11 | b2 [read] [c] |
+| A.cpp:64:10:64:15 | new | A.cpp:64:10:64:15 | call to setOnB [c] |
+| A.cpp:64:10:64:15 | new | A.cpp:85:26:85:26 | c |
+| A.cpp:64:21:64:28 | (C *)... | A.cpp:64:10:64:15 | new |
+| A.cpp:64:21:64:28 | new | A.cpp:64:10:64:15 | new |
+| A.cpp:66:10:66:11 | b2 [read] [c] | A.cpp:66:14:66:14 | FieldAddress [read] |
+| A.cpp:66:14:66:14 | FieldAddress [read] | A.cpp:66:10:66:14 | (void *)... |
+| A.cpp:73:10:73:19 | call to setOnBWrap [c] | A.cpp:75:10:75:11 | b2 [read] [c] |
+| A.cpp:73:10:73:19 | new | A.cpp:73:10:73:19 | call to setOnBWrap [c] |
+| A.cpp:73:10:73:19 | new | A.cpp:78:27:78:27 | c |
+| A.cpp:73:25:73:32 | (C *)... | A.cpp:73:10:73:19 | new |
+| A.cpp:73:25:73:32 | new | A.cpp:73:10:73:19 | new |
+| A.cpp:75:10:75:11 | b2 [read] [c] | A.cpp:75:14:75:14 | FieldAddress [read] |
+| A.cpp:75:14:75:14 | FieldAddress [read] | A.cpp:75:10:75:14 | (void *)... |
+| A.cpp:78:27:78:27 | c | A.cpp:81:10:81:15 | c |
+| A.cpp:81:10:81:15 | c | A.cpp:81:10:81:15 | call to setOnB [c] |
+| A.cpp:81:10:81:15 | c | A.cpp:85:26:85:26 | c |
+| A.cpp:81:10:81:15 | call to setOnB [c] | A.cpp:78:6:78:15 | ReturnValue [c] |
+| A.cpp:85:26:85:26 | c | A.cpp:90:11:90:13 | c |
+| A.cpp:90:11:90:13 | b2 [post update] [c] | A.cpp:85:9:85:14 | ReturnValue [c] |
+| A.cpp:90:11:90:13 | c | A.cpp:27:17:27:17 | c |
+| A.cpp:90:11:90:13 | c | A.cpp:90:11:90:13 | b2 [post update] [c] |
+| A.cpp:98:12:98:18 | new | A.cpp:100:9:100:9 | a [post update] |
+| A.cpp:100:5:100:6 | c1 [post update] [a] | A.cpp:101:5:101:6 | c1 [a] |
+| A.cpp:100:9:100:9 | a [post update] | A.cpp:100:5:100:6 | c1 [post update] [a] |
+| A.cpp:101:5:101:6 | c1 [a] | A.cpp:103:14:103:14 | c [a] |
+| A.cpp:103:14:103:14 | c [a] | A.cpp:107:12:107:13 | c1 [read] [a] |
+| A.cpp:103:14:103:14 | c [a] | A.cpp:120:12:120:13 | c1 [read] [a] |
+| A.cpp:107:12:107:13 | c1 [read] [a] | A.cpp:107:16:107:16 | FieldAddress [read] |
+| A.cpp:107:16:107:16 | FieldAddress [read] | A.cpp:107:12:107:16 | (void *)... |
+| A.cpp:120:12:120:13 | c1 [read] [a] | A.cpp:120:16:120:16 | FieldAddress [read] |
+| A.cpp:120:16:120:16 | FieldAddress [read] | A.cpp:120:12:120:16 | (void *)... |
+| A.cpp:126:5:126:5 | b [post update] [c] | A.cpp:131:5:131:6 | b [post update] [c] |
+| A.cpp:126:8:126:10 | b [post update] [c] | A.cpp:126:5:126:5 | b [post update] [c] |
+| A.cpp:126:8:126:10 | b [post update] [c] | A.cpp:131:5:131:6 | b [post update] [c] |
| A.cpp:126:8:126:10 | new | A.cpp:27:17:27:17 | c |
-| A.cpp:126:8:126:10 | new | A.cpp:126:5:126:5 | set output argument [c] |
+| A.cpp:126:8:126:10 | new | A.cpp:126:8:126:10 | b [post update] [c] |
| A.cpp:126:12:126:18 | new | A.cpp:126:8:126:10 | new |
-| A.cpp:131:8:131:8 | Chi [c] | A.cpp:132:13:132:13 | c |
-| A.cpp:131:8:131:8 | f7 output argument [c] | A.cpp:131:8:131:8 | Chi [c] |
-| A.cpp:140:13:140:13 | b | A.cpp:143:7:143:31 | Chi [b] |
-| A.cpp:142:7:142:20 | Chi [c] | A.cpp:151:18:151:18 | D output argument [c] |
-| A.cpp:142:14:142:20 | new | A.cpp:142:7:142:20 | Chi [c] |
-| A.cpp:143:7:143:31 | Chi [b] | A.cpp:151:12:151:24 | D output argument [b] |
-| A.cpp:143:25:143:31 | new | A.cpp:143:7:143:31 | Chi [b] |
+| A.cpp:131:5:131:6 | b [post update] [c] | A.cpp:132:10:132:10 | b [read] [c] |
+| A.cpp:132:10:132:10 | b [read] [c] | A.cpp:132:13:132:13 | FieldAddress [read] |
+| A.cpp:132:13:132:13 | FieldAddress [read] | A.cpp:132:10:132:13 | (void *)... |
+| A.cpp:140:13:140:13 | b | A.cpp:143:13:143:13 | b [post update] |
+| A.cpp:142:7:142:7 | b [post update] [c] | A.cpp:143:13:143:13 | b [post update] [c] |
+| A.cpp:142:7:142:7 | b [post update] [c] | A.cpp:151:12:151:24 | b [post update] [c] |
+| A.cpp:142:10:142:10 | c [post update] | A.cpp:142:7:142:7 | b [post update] [c] |
+| A.cpp:142:14:142:20 | new | A.cpp:142:10:142:10 | c [post update] |
+| A.cpp:143:7:143:10 | this [post update] [b, c] | A.cpp:151:12:151:24 | new [post update] [b, c] |
+| A.cpp:143:7:143:10 | this [post update] [b] | A.cpp:151:12:151:24 | new [post update] [b] |
+| A.cpp:143:13:143:13 | b [post update] | A.cpp:143:7:143:10 | this [post update] [b] |
+| A.cpp:143:13:143:13 | b [post update] | A.cpp:143:7:143:10 | this [post update] [b] |
+| A.cpp:143:13:143:13 | b [post update] [c] | A.cpp:143:7:143:10 | this [post update] [b, c] |
+| A.cpp:143:25:143:31 | new | A.cpp:143:13:143:13 | b [post update] |
| A.cpp:150:12:150:18 | new | A.cpp:151:12:151:24 | b |
-| A.cpp:151:12:151:24 | Chi [b] | A.cpp:152:13:152:13 | b |
-| A.cpp:151:12:151:24 | D output argument [b] | A.cpp:151:12:151:24 | Chi [b] |
| A.cpp:151:12:151:24 | b | A.cpp:140:13:140:13 | b |
-| A.cpp:151:12:151:24 | b | A.cpp:151:12:151:24 | D output argument [b] |
-| A.cpp:151:18:151:18 | Chi [c] | A.cpp:154:13:154:13 | c |
-| A.cpp:151:18:151:18 | D output argument [c] | A.cpp:151:18:151:18 | Chi [c] |
-| C.cpp:18:12:18:18 | C output argument [s1] | C.cpp:19:5:19:5 | c indirection [s1] |
-| C.cpp:18:12:18:18 | C output argument [s3] | C.cpp:19:5:19:5 | c indirection [s3] |
-| C.cpp:19:5:19:5 | c indirection [s1] | C.cpp:27:8:27:11 | *#this [s1] |
-| C.cpp:19:5:19:5 | c indirection [s3] | C.cpp:27:8:27:11 | *#this [s3] |
-| C.cpp:22:12:22:21 | Chi [s1] | C.cpp:24:5:24:25 | Chi [s1] |
-| C.cpp:22:12:22:21 | new | C.cpp:22:12:22:21 | Chi [s1] |
-| C.cpp:24:5:24:25 | Chi [s1] | C.cpp:18:12:18:18 | C output argument [s1] |
-| C.cpp:24:5:24:25 | Chi [s3] | C.cpp:18:12:18:18 | C output argument [s3] |
-| C.cpp:24:16:24:25 | new | C.cpp:24:5:24:25 | Chi [s3] |
-| C.cpp:27:8:27:11 | *#this [s1] | C.cpp:29:10:29:11 | s1 |
-| C.cpp:27:8:27:11 | *#this [s3] | C.cpp:31:10:31:11 | s3 |
-| aliasing.cpp:9:3:9:22 | Chi [m1] | aliasing.cpp:25:17:25:19 | pointerSetter output argument [m1] |
-| aliasing.cpp:9:11:9:20 | call to user_input | aliasing.cpp:9:3:9:22 | Chi [m1] |
-| aliasing.cpp:13:3:13:21 | Chi [m1] | aliasing.cpp:26:19:26:20 | referenceSetter output argument [m1] |
-| aliasing.cpp:13:10:13:19 | call to user_input | aliasing.cpp:13:3:13:21 | Chi [m1] |
-| aliasing.cpp:25:17:25:19 | Chi [m1] | aliasing.cpp:29:11:29:12 | m1 |
-| aliasing.cpp:25:17:25:19 | pointerSetter output argument [m1] | aliasing.cpp:25:17:25:19 | Chi [m1] |
-| aliasing.cpp:26:19:26:20 | Chi [m1] | aliasing.cpp:30:11:30:12 | m1 |
-| aliasing.cpp:26:19:26:20 | referenceSetter output argument [m1] | aliasing.cpp:26:19:26:20 | Chi [m1] |
-| aliasing.cpp:37:13:37:22 | call to user_input | aliasing.cpp:38:11:38:12 | m1 |
-| aliasing.cpp:42:11:42:20 | call to user_input | aliasing.cpp:43:13:43:14 | m1 |
-| aliasing.cpp:60:3:60:22 | Chi [m1] | aliasing.cpp:61:13:61:14 | Store [m1] |
-| aliasing.cpp:60:11:60:20 | call to user_input | aliasing.cpp:60:3:60:22 | Chi [m1] |
-| aliasing.cpp:61:13:61:14 | Store [m1] | aliasing.cpp:62:14:62:15 | m1 |
-| aliasing.cpp:79:11:79:20 | call to user_input | aliasing.cpp:80:12:80:13 | m1 |
-| aliasing.cpp:86:10:86:19 | call to user_input | aliasing.cpp:87:12:87:13 | m1 |
-| aliasing.cpp:92:12:92:21 | call to user_input | aliasing.cpp:93:12:93:13 | m1 |
-| aliasing.cpp:98:3:98:21 | Chi [m1] | aliasing.cpp:100:14:100:14 | Store [m1] |
-| aliasing.cpp:98:10:98:19 | call to user_input | aliasing.cpp:98:3:98:21 | Chi [m1] |
-| aliasing.cpp:100:14:100:14 | Store [m1] | aliasing.cpp:102:8:102:10 | * ... |
-| aliasing.cpp:106:3:106:20 | Chi [[]] | aliasing.cpp:121:15:121:16 | taint_a_ptr output argument [[]] |
-| aliasing.cpp:106:3:106:20 | Chi [[]] | aliasing.cpp:131:15:131:16 | taint_a_ptr output argument [[]] |
-| aliasing.cpp:106:3:106:20 | Chi [[]] | aliasing.cpp:136:15:136:17 | taint_a_ptr output argument [[]] |
-| aliasing.cpp:106:3:106:20 | Chi [[]] | aliasing.cpp:175:15:175:22 | taint_a_ptr output argument [[]] |
-| aliasing.cpp:106:3:106:20 | Chi [[]] | aliasing.cpp:187:15:187:22 | taint_a_ptr output argument [[]] |
-| aliasing.cpp:106:3:106:20 | Chi [[]] | aliasing.cpp:200:15:200:24 | taint_a_ptr output argument [[]] |
-| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:106:3:106:20 | Chi [[]] |
-| aliasing.cpp:121:15:121:16 | Chi [[]] | aliasing.cpp:122:8:122:12 | access to array |
-| aliasing.cpp:121:15:121:16 | taint_a_ptr output argument [[]] | aliasing.cpp:121:15:121:16 | Chi [[]] |
-| aliasing.cpp:131:15:131:16 | Chi [[]] | aliasing.cpp:132:8:132:14 | * ... |
-| aliasing.cpp:131:15:131:16 | taint_a_ptr output argument [[]] | aliasing.cpp:131:15:131:16 | Chi [[]] |
-| aliasing.cpp:136:15:136:17 | Chi [[]] | aliasing.cpp:137:8:137:11 | * ... |
-| aliasing.cpp:136:15:136:17 | taint_a_ptr output argument [[]] | aliasing.cpp:136:15:136:17 | Chi [[]] |
-| aliasing.cpp:175:15:175:22 | Chi | aliasing.cpp:175:15:175:22 | Chi [m1] |
-| aliasing.cpp:175:15:175:22 | Chi [m1] | aliasing.cpp:176:13:176:14 | m1 |
-| aliasing.cpp:175:15:175:22 | taint_a_ptr output argument [[]] | aliasing.cpp:175:15:175:22 | Chi |
-| aliasing.cpp:187:15:187:22 | Chi | aliasing.cpp:187:15:187:22 | Chi [m1] |
-| aliasing.cpp:187:15:187:22 | Chi [m1] | aliasing.cpp:188:13:188:14 | Store [m1] |
-| aliasing.cpp:187:15:187:22 | taint_a_ptr output argument [[]] | aliasing.cpp:187:15:187:22 | Chi |
-| aliasing.cpp:188:13:188:14 | Store [m1] | aliasing.cpp:189:15:189:16 | m1 |
-| aliasing.cpp:200:15:200:24 | Chi | aliasing.cpp:200:15:200:24 | Chi [m1] |
-| aliasing.cpp:200:15:200:24 | Chi [m1] | aliasing.cpp:201:15:201:16 | m1 |
-| aliasing.cpp:200:15:200:24 | taint_a_ptr output argument [[]] | aliasing.cpp:200:15:200:24 | Chi |
+| A.cpp:151:12:151:24 | b | A.cpp:151:12:151:24 | new [post update] [b] |
+| A.cpp:151:12:151:24 | b [post update] [c] | A.cpp:154:10:154:10 | b [read] [c] |
+| A.cpp:151:12:151:24 | new [post update] [b, c] | A.cpp:153:10:153:10 | d [read] [b, c] |
+| A.cpp:151:12:151:24 | new [post update] [b] | A.cpp:152:10:152:10 | d [read] [b] |
+| A.cpp:152:10:152:10 | d [read] [b] | A.cpp:152:13:152:13 | FieldAddress [read] |
+| A.cpp:152:13:152:13 | FieldAddress [read] | A.cpp:152:10:152:13 | (void *)... |
+| A.cpp:153:10:153:10 | d [read] [b, c] | A.cpp:153:13:153:13 | FieldAddress [read] [c] |
+| A.cpp:153:13:153:13 | FieldAddress [read] [c] | A.cpp:153:13:153:13 | b [read] [c] |
+| A.cpp:153:13:153:13 | b [read] [c] | A.cpp:153:16:153:16 | FieldAddress [read] |
+| A.cpp:153:16:153:16 | FieldAddress [read] | A.cpp:153:10:153:16 | (void *)... |
+| A.cpp:154:10:154:10 | b [read] [c] | A.cpp:154:13:154:13 | FieldAddress [read] |
+| A.cpp:154:13:154:13 | FieldAddress [read] | A.cpp:154:10:154:13 | (void *)... |
+| A.cpp:159:12:159:18 | new | A.cpp:160:18:160:60 | b |
+| A.cpp:160:18:160:60 | b | A.cpp:160:18:160:60 | new [post update] [head] |
+| A.cpp:160:18:160:60 | b | A.cpp:181:15:181:21 | newHead |
+| A.cpp:160:18:160:60 | new [post update] [head] | A.cpp:161:18:161:40 | l1 [head] |
+| A.cpp:161:18:161:40 | l1 [head] | A.cpp:161:18:161:40 | new [post update] [next, head] |
+| A.cpp:161:18:161:40 | l1 [head] | A.cpp:181:32:181:35 | next [head] |
+| A.cpp:161:18:161:40 | new [post update] [next, head] | A.cpp:162:18:162:40 | l2 [next, head] |
+| A.cpp:162:18:162:40 | l2 [next, head] | A.cpp:162:18:162:40 | new [post update] [next, next, head] |
+| A.cpp:162:18:162:40 | l2 [next, head] | A.cpp:181:32:181:35 | next [next, head] |
+| A.cpp:162:18:162:40 | new [post update] [next, next, head] | A.cpp:165:10:165:11 | l3 [read] [next, next, head] |
+| A.cpp:162:18:162:40 | new [post update] [next, next, head] | A.cpp:167:44:167:44 | l [read] [next, next, head] |
+| A.cpp:165:10:165:11 | l3 [read] [next, next, head] | A.cpp:165:14:165:17 | FieldAddress [read] [next, head] |
+| A.cpp:165:14:165:17 | FieldAddress [read] [next, head] | A.cpp:165:14:165:17 | next [read] [next, head] |
+| A.cpp:165:14:165:17 | next [read] [next, head] | A.cpp:165:20:165:23 | FieldAddress [read] [head] |
+| A.cpp:165:20:165:23 | FieldAddress [read] [head] | A.cpp:165:20:165:23 | next [read] [head] |
+| A.cpp:165:20:165:23 | next [read] [head] | A.cpp:165:26:165:29 | FieldAddress [read] |
+| A.cpp:165:26:165:29 | FieldAddress [read] | A.cpp:165:10:165:29 | (void *)... |
+| A.cpp:167:44:167:44 | l [read] [next, head] | A.cpp:167:47:167:50 | FieldAddress [read] [head] |
+| A.cpp:167:44:167:44 | l [read] [next, next, head] | A.cpp:167:47:167:50 | FieldAddress [read] [next, head] |
+| A.cpp:167:47:167:50 | FieldAddress [read] [head] | A.cpp:169:12:169:12 | l [read] [head] |
+| A.cpp:167:47:167:50 | FieldAddress [read] [next, head] | A.cpp:167:44:167:44 | l [read] [next, head] |
+| A.cpp:169:12:169:12 | l [read] [head] | A.cpp:169:15:169:18 | FieldAddress [read] |
+| A.cpp:169:15:169:18 | FieldAddress [read] | A.cpp:169:12:169:18 | (void *)... |
+| A.cpp:181:15:181:21 | newHead | A.cpp:183:7:183:10 | head [post update] |
+| A.cpp:181:32:181:35 | next [head] | A.cpp:184:13:184:16 | next [post update] [head] |
+| A.cpp:181:32:181:35 | next [next, head] | A.cpp:184:13:184:16 | next [post update] [next, head] |
+| A.cpp:183:7:183:10 | head [post update] | A.cpp:183:7:183:10 | this [post update] [head] |
+| A.cpp:184:13:184:16 | next [post update] [head] | A.cpp:184:7:184:10 | this [post update] [next, head] |
+| A.cpp:184:13:184:16 | next [post update] [next, head] | A.cpp:184:7:184:10 | this [post update] [next, next, head] |
+| B.cpp:6:15:6:24 | new | B.cpp:7:16:7:35 | e |
+| B.cpp:7:16:7:35 | e | B.cpp:7:16:7:35 | new [post update] [elem1] |
+| B.cpp:7:16:7:35 | e | B.cpp:33:16:33:17 | e1 |
+| B.cpp:7:16:7:35 | new [post update] [elem1] | B.cpp:8:16:8:27 | b1 [elem1] |
+| B.cpp:8:16:8:27 | b1 [elem1] | B.cpp:8:16:8:27 | new [post update] [box1, elem1] |
+| B.cpp:8:16:8:27 | b1 [elem1] | B.cpp:44:16:44:17 | b1 [elem1] |
+| B.cpp:8:16:8:27 | new [post update] [box1, elem1] | B.cpp:9:10:9:11 | b2 [read] [box1, elem1] |
+| B.cpp:9:10:9:11 | b2 [read] [box1, elem1] | B.cpp:9:14:9:17 | FieldAddress [read] [elem1] |
+| B.cpp:9:14:9:17 | FieldAddress [read] [elem1] | B.cpp:9:14:9:17 | box1 [read] [elem1] |
+| B.cpp:9:14:9:17 | box1 [read] [elem1] | B.cpp:9:20:9:24 | FieldAddress [read] |
+| B.cpp:9:20:9:24 | FieldAddress [read] | B.cpp:9:10:9:24 | (void *)... |
+| B.cpp:15:15:15:27 | new | B.cpp:16:16:16:38 | e |
+| B.cpp:16:16:16:38 | e | B.cpp:16:16:16:38 | new [post update] [elem2] |
+| B.cpp:16:16:16:38 | e | B.cpp:33:26:33:27 | e2 |
+| B.cpp:16:16:16:38 | new [post update] [elem2] | B.cpp:17:16:17:27 | b1 [elem2] |
+| B.cpp:17:16:17:27 | b1 [elem2] | B.cpp:17:16:17:27 | new [post update] [box1, elem2] |
+| B.cpp:17:16:17:27 | b1 [elem2] | B.cpp:44:16:44:17 | b1 [elem2] |
+| B.cpp:17:16:17:27 | new [post update] [box1, elem2] | B.cpp:19:10:19:11 | b2 [read] [box1, elem2] |
+| B.cpp:19:10:19:11 | b2 [read] [box1, elem2] | B.cpp:19:14:19:17 | FieldAddress [read] [elem2] |
+| B.cpp:19:14:19:17 | FieldAddress [read] [elem2] | B.cpp:19:14:19:17 | box1 [read] [elem2] |
+| B.cpp:19:14:19:17 | box1 [read] [elem2] | B.cpp:19:20:19:24 | FieldAddress [read] |
+| B.cpp:19:20:19:24 | FieldAddress [read] | B.cpp:19:10:19:24 | (void *)... |
+| B.cpp:33:16:33:17 | e1 | B.cpp:35:13:35:17 | elem1 [post update] |
+| B.cpp:33:26:33:27 | e2 | B.cpp:36:13:36:17 | elem2 [post update] |
+| B.cpp:35:13:35:17 | elem1 [post update] | B.cpp:35:7:35:10 | this [post update] [elem1] |
+| B.cpp:36:13:36:17 | elem2 [post update] | B.cpp:36:7:36:10 | this [post update] [elem2] |
+| B.cpp:44:16:44:17 | b1 [elem1] | B.cpp:46:13:46:16 | box1 [post update] [elem1] |
+| B.cpp:44:16:44:17 | b1 [elem2] | B.cpp:46:13:46:16 | box1 [post update] [elem2] |
+| B.cpp:46:13:46:16 | box1 [post update] [elem1] | B.cpp:46:7:46:10 | this [post update] [box1, elem1] |
+| B.cpp:46:13:46:16 | box1 [post update] [elem2] | B.cpp:46:7:46:10 | this [post update] [box1, elem2] |
+| C.cpp:18:12:18:18 | new [post update] [s1] | C.cpp:19:8:19:11 | c [s1] |
+| C.cpp:19:8:19:11 | c [s1] | C.cpp:27:8:27:11 | this [s1] |
+| C.cpp:22:3:22:3 | this [post update] [s1] | C.cpp:18:12:18:18 | new [post update] [s1] |
+| C.cpp:22:9:22:22 | FieldAddress [post update] | C.cpp:22:3:22:3 | this [post update] [s1] |
+| C.cpp:22:12:22:21 | new | C.cpp:22:9:22:22 | FieldAddress [post update] |
+| C.cpp:27:8:27:11 | this [s1] | C.cpp:29:10:29:11 | this [read] [s1] |
+| C.cpp:29:10:29:11 | FieldAddress [read] | C.cpp:29:10:29:11 | s1 |
+| C.cpp:29:10:29:11 | this [read] [s1] | C.cpp:29:10:29:11 | FieldAddress [read] |
+| D.cpp:10:11:10:17 | this [elem] | D.cpp:10:30:10:33 | this [read] [elem] |
+| D.cpp:10:30:10:33 | FieldAddress [read] | D.cpp:10:11:10:17 | ReturnValue |
+| D.cpp:10:30:10:33 | this [read] [elem] | D.cpp:10:30:10:33 | FieldAddress [read] |
+| D.cpp:11:24:11:24 | e | D.cpp:11:29:11:32 | elem [post update] |
+| D.cpp:11:29:11:32 | elem [post update] | D.cpp:11:29:11:32 | this [post update] [elem] |
+| D.cpp:17:11:17:17 | this [box, elem] | D.cpp:17:30:17:32 | this [read] [box, elem] |
+| D.cpp:17:30:17:32 | FieldAddress [read] [elem] | D.cpp:17:11:17:17 | ReturnValue [elem] |
+| D.cpp:17:30:17:32 | this [read] [box, elem] | D.cpp:17:30:17:32 | FieldAddress [read] [elem] |
+| D.cpp:21:30:21:31 | b2 [box, elem] | D.cpp:22:14:22:20 | b2 [box, elem] |
+| D.cpp:22:14:22:20 | b2 [box, elem] | D.cpp:17:11:17:17 | this [box, elem] |
+| D.cpp:22:14:22:20 | b2 [box, elem] | D.cpp:22:14:22:20 | call to getBox1 [elem] |
+| D.cpp:22:14:22:20 | call to getBox1 [elem] | D.cpp:22:25:22:31 | call to getBox1 [elem] |
+| D.cpp:22:25:22:31 | call to getBox1 [elem] | D.cpp:10:11:10:17 | this [elem] |
+| D.cpp:22:25:22:31 | call to getBox1 [elem] | D.cpp:22:25:22:31 | call to getElem |
+| D.cpp:22:25:22:31 | call to getBox1 [elem] | D.cpp:22:25:22:31 | call to getElem |
+| D.cpp:22:25:22:31 | call to getElem | D.cpp:22:10:22:33 | (void *)... |
+| D.cpp:28:15:28:24 | new | D.cpp:30:13:30:16 | elem [post update] |
+| D.cpp:30:5:30:5 | b [post update] [box, elem] | D.cpp:31:5:31:12 | b [box, elem] |
+| D.cpp:30:8:30:10 | FieldAddress [post update] [elem] | D.cpp:30:5:30:5 | b [post update] [box, elem] |
+| D.cpp:30:8:30:10 | box [post update] [elem] | D.cpp:30:8:30:10 | FieldAddress [post update] [elem] |
+| D.cpp:30:13:30:16 | elem [post update] | D.cpp:30:8:30:10 | box [post update] [elem] |
+| D.cpp:31:5:31:12 | b [box, elem] | D.cpp:21:30:21:31 | b2 [box, elem] |
+| D.cpp:35:15:35:24 | new | D.cpp:37:13:37:19 | e |
+| D.cpp:37:5:37:5 | b [post update] [box, elem] | D.cpp:38:5:38:12 | b [box, elem] |
+| D.cpp:37:8:37:10 | FieldAddress [post update] [elem] | D.cpp:37:5:37:5 | b [post update] [box, elem] |
+| D.cpp:37:13:37:19 | box [post update] [elem] | D.cpp:37:8:37:10 | FieldAddress [post update] [elem] |
+| D.cpp:37:13:37:19 | e | D.cpp:11:24:11:24 | e |
+| D.cpp:37:13:37:19 | e | D.cpp:37:13:37:19 | box [post update] [elem] |
+| D.cpp:38:5:38:12 | b [box, elem] | D.cpp:21:30:21:31 | b2 [box, elem] |
+| D.cpp:42:15:42:24 | new | D.cpp:44:19:44:22 | elem [post update] |
+| D.cpp:44:8:44:14 | b [post update] [box, elem] | D.cpp:45:5:45:12 | b [box, elem] |
+| D.cpp:44:8:44:14 | call to getBox1 [post update] [elem] | D.cpp:44:8:44:14 | b [post update] [box, elem] |
+| D.cpp:44:19:44:22 | elem [post update] | D.cpp:44:8:44:14 | call to getBox1 [post update] [elem] |
+| D.cpp:45:5:45:12 | b [box, elem] | D.cpp:21:30:21:31 | b2 [box, elem] |
+| D.cpp:49:15:49:24 | new | D.cpp:51:19:51:25 | e |
+| D.cpp:51:8:51:14 | b [post update] [box, elem] | D.cpp:52:5:52:12 | b [box, elem] |
+| D.cpp:51:8:51:14 | call to getBox1 [post update] [elem] | D.cpp:51:8:51:14 | b [post update] [box, elem] |
+| D.cpp:51:19:51:25 | call to getBox1 [post update] [elem] | D.cpp:51:8:51:14 | call to getBox1 [post update] [elem] |
+| D.cpp:51:19:51:25 | e | D.cpp:11:24:11:24 | e |
+| D.cpp:51:19:51:25 | e | D.cpp:51:19:51:25 | call to getBox1 [post update] [elem] |
+| D.cpp:52:5:52:12 | b [box, elem] | D.cpp:21:30:21:31 | b2 [box, elem] |
+| D.cpp:56:15:56:24 | new | D.cpp:58:20:58:23 | elem [post update] |
+| D.cpp:58:5:58:12 | FieldAddress [post update] [box, elem] | D.cpp:58:5:58:12 | this [post update] [boxfield, box, elem] |
+| D.cpp:58:5:58:12 | boxfield [post update] [box, elem] | D.cpp:58:5:58:12 | FieldAddress [post update] [box, elem] |
+| D.cpp:58:5:58:12 | this [post update] [boxfield, box, elem] | D.cpp:59:5:59:7 | this [boxfield, box, elem] |
+| D.cpp:58:15:58:17 | FieldAddress [post update] [elem] | D.cpp:58:5:58:12 | boxfield [post update] [box, elem] |
+| D.cpp:58:15:58:17 | box [post update] [elem] | D.cpp:58:15:58:17 | FieldAddress [post update] [elem] |
+| D.cpp:58:20:58:23 | elem [post update] | D.cpp:58:15:58:17 | box [post update] [elem] |
+| D.cpp:59:5:59:7 | this [boxfield, box, elem] | D.cpp:63:8:63:10 | this [boxfield, box, elem] |
+| D.cpp:63:8:63:10 | this [boxfield, box, elem] | D.cpp:64:10:64:17 | this [read] [boxfield, box, elem] |
+| D.cpp:64:10:64:17 | FieldAddress [read] [box, elem] | D.cpp:64:10:64:17 | boxfield [read] [box, elem] |
+| D.cpp:64:10:64:17 | boxfield [read] [box, elem] | D.cpp:64:20:64:22 | FieldAddress [read] [elem] |
+| D.cpp:64:10:64:17 | this [read] [boxfield, box, elem] | D.cpp:64:10:64:17 | FieldAddress [read] [box, elem] |
+| D.cpp:64:20:64:22 | FieldAddress [read] [elem] | D.cpp:64:20:64:22 | box [read] [elem] |
+| D.cpp:64:20:64:22 | box [read] [elem] | D.cpp:64:25:64:28 | FieldAddress [read] |
+| D.cpp:64:25:64:28 | FieldAddress [read] | D.cpp:64:10:64:28 | (void *)... |
+| E.cpp:19:27:19:27 | *p [data, buffer] | E.cpp:21:10:21:10 | p [read] [data, buffer] |
+| E.cpp:21:10:21:10 | p [read] [data, buffer] | E.cpp:21:13:21:16 | data [read] [buffer] |
+| E.cpp:21:13:21:16 | data [read] [buffer] | E.cpp:21:18:21:23 | FieldAddress [read] |
+| E.cpp:21:18:21:23 | FieldAddress [read] | E.cpp:21:18:21:23 | buffer |
+| E.cpp:28:21:28:23 | argument_source output argument | E.cpp:31:10:31:12 | raw |
+| E.cpp:29:21:29:21 | b [post update] [buffer] | E.cpp:32:10:32:10 | b [read] [buffer] |
+| E.cpp:29:21:29:29 | argument_source output argument | E.cpp:29:24:29:29 | FieldAddress [post update] |
+| E.cpp:29:24:29:29 | FieldAddress [post update] | E.cpp:29:21:29:21 | b [post update] [buffer] |
+| E.cpp:30:21:30:21 | p [post update] [data, buffer] | E.cpp:33:18:33:19 | & ... indirection [data, buffer] |
+| E.cpp:30:21:30:33 | argument_source output argument | E.cpp:30:28:30:33 | FieldAddress [post update] |
+| E.cpp:30:23:30:26 | data [post update] [buffer] | E.cpp:30:21:30:21 | p [post update] [data, buffer] |
+| E.cpp:30:28:30:33 | FieldAddress [post update] | E.cpp:30:23:30:26 | data [post update] [buffer] |
+| E.cpp:32:10:32:10 | b [read] [buffer] | E.cpp:32:13:32:18 | FieldAddress [read] |
+| E.cpp:32:13:32:18 | FieldAddress [read] | E.cpp:32:13:32:18 | buffer |
+| E.cpp:33:18:33:19 | & ... indirection [data, buffer] | E.cpp:19:27:19:27 | *p [data, buffer] |
+| aliasing.cpp:9:3:9:3 | s [post update] [m1] | aliasing.cpp:25:3:25:15 | & ... [post update] [m1] |
+| aliasing.cpp:9:6:9:7 | m1 [post update] | aliasing.cpp:9:3:9:3 | s [post update] [m1] |
+| aliasing.cpp:9:11:9:20 | call to user_input | aliasing.cpp:9:6:9:7 | m1 [post update] |
+| aliasing.cpp:13:3:13:3 | (reference dereference) [post update] [m1] | aliasing.cpp:13:3:13:3 | s [post update] [m1] |
+| aliasing.cpp:13:3:13:3 | (reference dereference) [post update] [m1] | aliasing.cpp:26:3:26:17 | s2 [post update] [m1] |
+| aliasing.cpp:13:3:13:3 | s [post update] [m1] | aliasing.cpp:26:3:26:17 | s2 [post update] [m1] |
+| aliasing.cpp:13:5:13:6 | m1 [post update] | aliasing.cpp:13:3:13:3 | (reference dereference) [post update] [m1] |
+| aliasing.cpp:13:10:13:19 | call to user_input | aliasing.cpp:13:5:13:6 | m1 [post update] |
+| aliasing.cpp:25:3:25:15 | & ... [post update] [m1] | aliasing.cpp:29:8:29:9 | s1 [read] [m1] |
+| aliasing.cpp:26:3:26:17 | s2 [post update] [m1] | aliasing.cpp:30:8:30:9 | s2 [read] [m1] |
+| aliasing.cpp:29:8:29:9 | s1 [read] [m1] | aliasing.cpp:29:11:29:12 | FieldAddress [read] |
+| aliasing.cpp:29:11:29:12 | FieldAddress [read] | aliasing.cpp:29:11:29:12 | m1 |
+| aliasing.cpp:30:8:30:9 | s2 [read] [m1] | aliasing.cpp:30:11:30:12 | FieldAddress [read] |
+| aliasing.cpp:30:11:30:12 | FieldAddress [read] | aliasing.cpp:30:11:30:12 | m1 |
+| aliasing.cpp:60:3:60:4 | s2 [post update] [m1] | aliasing.cpp:62:8:62:12 | copy2 [read] [m1] |
+| aliasing.cpp:60:6:60:7 | m1 [post update] | aliasing.cpp:60:3:60:4 | s2 [post update] [m1] |
+| aliasing.cpp:60:11:60:20 | call to user_input | aliasing.cpp:60:6:60:7 | m1 [post update] |
+| aliasing.cpp:62:8:62:12 | copy2 [read] [m1] | aliasing.cpp:62:14:62:15 | FieldAddress [read] |
+| aliasing.cpp:62:14:62:15 | FieldAddress [read] | aliasing.cpp:62:14:62:15 | m1 |
+| aliasing.cpp:92:3:92:3 | w [post update] [s, m1] | aliasing.cpp:93:8:93:8 | w [read] [s, m1] |
+| aliasing.cpp:92:5:92:5 | s [post update] [m1] | aliasing.cpp:92:3:92:3 | w [post update] [s, m1] |
+| aliasing.cpp:92:7:92:8 | m1 [post update] | aliasing.cpp:92:5:92:5 | s [post update] [m1] |
+| aliasing.cpp:92:12:92:21 | call to user_input | aliasing.cpp:92:7:92:8 | m1 [post update] |
+| aliasing.cpp:93:8:93:8 | w [read] [s, m1] | aliasing.cpp:93:10:93:10 | s [read] [m1] |
+| aliasing.cpp:93:10:93:10 | s [read] [m1] | aliasing.cpp:93:12:93:13 | FieldAddress [read] |
+| aliasing.cpp:93:12:93:13 | FieldAddress [read] | aliasing.cpp:93:12:93:13 | m1 |
+| aliasing.cpp:106:3:106:5 | * ... [post update] | aliasing.cpp:141:3:141:13 | data [post update] |
+| aliasing.cpp:106:3:106:5 | * ... [post update] | aliasing.cpp:158:3:158:13 | data [post update] |
+| aliasing.cpp:106:3:106:5 | * ... [post update] | aliasing.cpp:164:3:164:13 | data [post update] |
+| aliasing.cpp:106:3:106:5 | * ... [post update] | aliasing.cpp:175:3:175:13 | & ... [post update] |
+| aliasing.cpp:106:3:106:5 | * ... [post update] | aliasing.cpp:187:3:187:13 | & ... [post update] |
+| aliasing.cpp:106:3:106:5 | * ... [post update] | aliasing.cpp:200:3:200:13 | & ... [post update] |
+| aliasing.cpp:106:4:106:5 | pa [post update] | aliasing.cpp:141:3:141:13 | data [post update] |
+| aliasing.cpp:106:4:106:5 | pa [post update] | aliasing.cpp:158:3:158:13 | data [post update] |
+| aliasing.cpp:106:4:106:5 | pa [post update] | aliasing.cpp:164:3:164:13 | data [post update] |
+| aliasing.cpp:106:4:106:5 | pa [post update] | aliasing.cpp:175:3:175:13 | & ... [post update] |
+| aliasing.cpp:106:4:106:5 | pa [post update] | aliasing.cpp:187:3:187:13 | & ... [post update] |
+| aliasing.cpp:106:4:106:5 | pa [post update] | aliasing.cpp:200:3:200:13 | & ... [post update] |
+| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:106:3:106:5 | * ... [post update] |
+| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:106:4:106:5 | pa [post update] |
+| aliasing.cpp:141:3:141:13 | data [post update] | aliasing.cpp:141:17:141:20 | FieldAddress [post update] |
+| aliasing.cpp:141:15:141:15 | s [post update] [data] | aliasing.cpp:143:8:143:8 | s [read] [data] |
+| aliasing.cpp:141:17:141:20 | FieldAddress [post update] | aliasing.cpp:141:15:141:15 | s [post update] [data] |
+| aliasing.cpp:143:8:143:8 | s [read] [data] | aliasing.cpp:143:10:143:13 | FieldAddress [read] |
+| aliasing.cpp:143:10:143:13 | FieldAddress [read] | aliasing.cpp:143:8:143:16 | access to array |
+| aliasing.cpp:158:3:158:13 | data [post update] | aliasing.cpp:158:17:158:20 | data [post update] |
+| aliasing.cpp:158:15:158:15 | s [post update] [data] | aliasing.cpp:159:9:159:9 | s [read] [data] |
+| aliasing.cpp:158:17:158:20 | data [post update] | aliasing.cpp:158:15:158:15 | s [post update] [data] |
+| aliasing.cpp:159:9:159:9 | s [read] [data] | aliasing.cpp:159:11:159:14 | data [read] |
+| aliasing.cpp:159:11:159:14 | data [read] | aliasing.cpp:159:8:159:14 | * ... |
+| aliasing.cpp:164:3:164:13 | data [post update] | aliasing.cpp:164:17:164:20 | data [post update] |
+| aliasing.cpp:164:15:164:15 | s [post update] [data] | aliasing.cpp:165:8:165:8 | s [read] [data] |
+| aliasing.cpp:164:17:164:20 | data [post update] | aliasing.cpp:164:15:164:15 | s [post update] [data] |
+| aliasing.cpp:165:8:165:8 | s [read] [data] | aliasing.cpp:165:10:165:13 | data [read] |
+| aliasing.cpp:165:10:165:13 | data [read] | aliasing.cpp:165:8:165:16 | access to array |
+| aliasing.cpp:175:3:175:13 | & ... [post update] | aliasing.cpp:175:21:175:22 | m1 [post update] |
+| aliasing.cpp:175:16:175:17 | s2 [post update] [s, m1] | aliasing.cpp:176:8:176:9 | s2 [read] [s, m1] |
+| aliasing.cpp:175:19:175:19 | s [post update] [m1] | aliasing.cpp:175:16:175:17 | s2 [post update] [s, m1] |
+| aliasing.cpp:175:21:175:22 | m1 [post update] | aliasing.cpp:175:19:175:19 | s [post update] [m1] |
+| aliasing.cpp:176:8:176:9 | s2 [read] [s, m1] | aliasing.cpp:176:11:176:11 | s [read] [m1] |
+| aliasing.cpp:176:11:176:11 | s [read] [m1] | aliasing.cpp:176:13:176:14 | FieldAddress [read] |
+| aliasing.cpp:176:13:176:14 | FieldAddress [read] | aliasing.cpp:176:13:176:14 | m1 |
+| aliasing.cpp:187:3:187:13 | & ... [post update] | aliasing.cpp:187:21:187:22 | m1 [post update] |
+| aliasing.cpp:187:16:187:17 | s2 [post update] [s, m1] | aliasing.cpp:189:8:189:11 | s2_2 [read] [s, m1] |
+| aliasing.cpp:187:19:187:19 | s [post update] [m1] | aliasing.cpp:187:16:187:17 | s2 [post update] [s, m1] |
+| aliasing.cpp:187:21:187:22 | m1 [post update] | aliasing.cpp:187:19:187:19 | s [post update] [m1] |
+| aliasing.cpp:189:8:189:11 | s2_2 [read] [s, m1] | aliasing.cpp:189:13:189:13 | s [read] [m1] |
+| aliasing.cpp:189:13:189:13 | s [read] [m1] | aliasing.cpp:189:15:189:16 | FieldAddress [read] |
+| aliasing.cpp:189:15:189:16 | FieldAddress [read] | aliasing.cpp:189:15:189:16 | m1 |
+| aliasing.cpp:200:3:200:13 | & ... [post update] | aliasing.cpp:200:23:200:24 | m1 [post update] |
+| aliasing.cpp:200:16:200:18 | ps2 [post update] [s, m1] | aliasing.cpp:201:8:201:10 | ps2 [read] [s, m1] |
+| aliasing.cpp:200:21:200:21 | s [post update] [m1] | aliasing.cpp:200:16:200:18 | ps2 [post update] [s, m1] |
+| aliasing.cpp:200:23:200:24 | m1 [post update] | aliasing.cpp:200:21:200:21 | s [post update] [m1] |
+| aliasing.cpp:201:8:201:10 | ps2 [read] [s, m1] | aliasing.cpp:201:13:201:13 | s [read] [m1] |
+| aliasing.cpp:201:13:201:13 | s [read] [m1] | aliasing.cpp:201:15:201:16 | FieldAddress [read] |
+| aliasing.cpp:201:15:201:16 | FieldAddress [read] | aliasing.cpp:201:15:201:16 | m1 |
| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:7:8:7:13 | access to array |
+| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:8:8:8:13 | access to array |
| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:9:8:9:11 | * ... |
| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:10:8:10:15 | * ... |
| arrays.cpp:15:14:15:23 | call to user_input | arrays.cpp:16:8:16:13 | access to array |
-| arrays.cpp:36:26:36:35 | call to user_input | arrays.cpp:37:24:37:27 | data |
-| by_reference.cpp:11:48:11:52 | value | by_reference.cpp:12:5:12:16 | Chi [a] |
-| by_reference.cpp:15:26:15:30 | value | by_reference.cpp:16:5:16:19 | Chi [a] |
+| arrays.cpp:15:14:15:23 | call to user_input | arrays.cpp:17:8:17:13 | access to array |
+| arrays.cpp:36:3:36:3 | o [post update] [nested, arr, data] | arrays.cpp:37:8:37:8 | o [read] [nested, arr, data] |
+| arrays.cpp:36:3:36:3 | o [post update] [nested, arr, data] | arrays.cpp:38:8:38:8 | o [read] [nested, arr, data] |
+| arrays.cpp:36:3:36:17 | access to array [post update] [data] | arrays.cpp:36:12:36:14 | arr [post update] [data] |
+| arrays.cpp:36:5:36:10 | nested [post update] [arr, data] | arrays.cpp:36:3:36:3 | o [post update] [nested, arr, data] |
+| arrays.cpp:36:12:36:14 | arr [post update] [data] | arrays.cpp:36:5:36:10 | nested [post update] [arr, data] |
+| arrays.cpp:36:19:36:22 | data [post update] | arrays.cpp:36:3:36:17 | access to array [post update] [data] |
+| arrays.cpp:36:26:36:35 | call to user_input | arrays.cpp:36:19:36:22 | data [post update] |
+| arrays.cpp:37:8:37:8 | o [read] [nested, arr, data] | arrays.cpp:37:10:37:15 | nested [read] [arr, data] |
+| arrays.cpp:37:8:37:22 | access to array [read] [data] | arrays.cpp:37:24:37:27 | FieldAddress [read] |
+| arrays.cpp:37:10:37:15 | nested [read] [arr, data] | arrays.cpp:37:17:37:19 | arr [read] [data] |
+| arrays.cpp:37:17:37:19 | arr [read] [data] | arrays.cpp:37:8:37:22 | access to array [read] [data] |
+| arrays.cpp:37:24:37:27 | FieldAddress [read] | arrays.cpp:37:24:37:27 | data |
+| arrays.cpp:38:8:38:8 | o [read] [nested, arr, data] | arrays.cpp:38:10:38:15 | nested [read] [arr, data] |
+| arrays.cpp:38:8:38:22 | access to array [read] [data] | arrays.cpp:38:24:38:27 | FieldAddress [read] |
+| arrays.cpp:38:10:38:15 | nested [read] [arr, data] | arrays.cpp:38:17:38:19 | arr [read] [data] |
+| arrays.cpp:38:17:38:19 | arr [read] [data] | arrays.cpp:38:8:38:22 | access to array [read] [data] |
+| arrays.cpp:38:24:38:27 | FieldAddress [read] | arrays.cpp:38:24:38:27 | data |
+| arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, data] | arrays.cpp:43:8:43:8 | o [read] [indirect, arr, data] |
+| arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, data] | arrays.cpp:44:8:44:8 | o [read] [indirect, arr, data] |
+| arrays.cpp:42:3:42:20 | access to array [post update] [data] | arrays.cpp:42:15:42:17 | arr [post update] [data] |
+| arrays.cpp:42:5:42:12 | FieldAddress [post update] [arr, data] | arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, data] |
+| arrays.cpp:42:5:42:12 | indirect [post update] [arr, data] | arrays.cpp:42:5:42:12 | FieldAddress [post update] [arr, data] |
+| arrays.cpp:42:15:42:17 | arr [post update] [data] | arrays.cpp:42:5:42:12 | indirect [post update] [arr, data] |
+| arrays.cpp:42:22:42:25 | data [post update] | arrays.cpp:42:3:42:20 | access to array [post update] [data] |
+| arrays.cpp:42:29:42:38 | call to user_input | arrays.cpp:42:22:42:25 | data [post update] |
+| arrays.cpp:43:8:43:8 | o [read] [indirect, arr, data] | arrays.cpp:43:10:43:17 | FieldAddress [read] [arr, data] |
+| arrays.cpp:43:8:43:25 | access to array [read] [data] | arrays.cpp:43:27:43:30 | FieldAddress [read] |
+| arrays.cpp:43:10:43:17 | FieldAddress [read] [arr, data] | arrays.cpp:43:10:43:17 | indirect [read] [arr, data] |
+| arrays.cpp:43:10:43:17 | indirect [read] [arr, data] | arrays.cpp:43:20:43:22 | arr [read] [data] |
+| arrays.cpp:43:20:43:22 | arr [read] [data] | arrays.cpp:43:8:43:25 | access to array [read] [data] |
+| arrays.cpp:43:27:43:30 | FieldAddress [read] | arrays.cpp:43:27:43:30 | data |
+| arrays.cpp:44:8:44:8 | o [read] [indirect, arr, data] | arrays.cpp:44:10:44:17 | FieldAddress [read] [arr, data] |
+| arrays.cpp:44:8:44:25 | access to array [read] [data] | arrays.cpp:44:27:44:30 | FieldAddress [read] |
+| arrays.cpp:44:10:44:17 | FieldAddress [read] [arr, data] | arrays.cpp:44:10:44:17 | indirect [read] [arr, data] |
+| arrays.cpp:44:10:44:17 | indirect [read] [arr, data] | arrays.cpp:44:20:44:22 | arr [read] [data] |
+| arrays.cpp:44:20:44:22 | arr [read] [data] | arrays.cpp:44:8:44:25 | access to array [read] [data] |
+| arrays.cpp:44:27:44:30 | FieldAddress [read] | arrays.cpp:44:27:44:30 | data |
+| arrays.cpp:48:3:48:3 | o [post update] [indirect, ptr, data] | arrays.cpp:49:8:49:8 | o [read] [indirect, ptr, data] |
+| arrays.cpp:48:3:48:3 | o [post update] [indirect, ptr, data] | arrays.cpp:50:8:50:8 | o [read] [indirect, ptr, data] |
+| arrays.cpp:48:3:48:20 | access to array [post update] [data] | arrays.cpp:48:15:48:17 | FieldAddress [post update] [data] |
+| arrays.cpp:48:5:48:12 | FieldAddress [post update] [ptr, data] | arrays.cpp:48:3:48:3 | o [post update] [indirect, ptr, data] |
+| arrays.cpp:48:5:48:12 | indirect [post update] [ptr, data] | arrays.cpp:48:5:48:12 | FieldAddress [post update] [ptr, data] |
+| arrays.cpp:48:15:48:17 | FieldAddress [post update] [data] | arrays.cpp:48:5:48:12 | indirect [post update] [ptr, data] |
+| arrays.cpp:48:22:48:25 | data [post update] | arrays.cpp:48:3:48:20 | access to array [post update] [data] |
+| arrays.cpp:48:29:48:38 | call to user_input | arrays.cpp:48:22:48:25 | data [post update] |
+| arrays.cpp:49:8:49:8 | o [read] [indirect, ptr, data] | arrays.cpp:49:10:49:17 | FieldAddress [read] [ptr, data] |
+| arrays.cpp:49:8:49:25 | access to array [read] [data] | arrays.cpp:49:27:49:30 | FieldAddress [read] |
+| arrays.cpp:49:10:49:17 | FieldAddress [read] [ptr, data] | arrays.cpp:49:10:49:17 | indirect [read] [ptr, data] |
+| arrays.cpp:49:10:49:17 | indirect [read] [ptr, data] | arrays.cpp:49:20:49:22 | FieldAddress [read] [data] |
+| arrays.cpp:49:20:49:22 | FieldAddress [read] [data] | arrays.cpp:49:8:49:25 | access to array [read] [data] |
+| arrays.cpp:49:27:49:30 | FieldAddress [read] | arrays.cpp:49:27:49:30 | data |
+| arrays.cpp:50:8:50:8 | o [read] [indirect, ptr, data] | arrays.cpp:50:10:50:17 | FieldAddress [read] [ptr, data] |
+| arrays.cpp:50:8:50:25 | access to array [read] [data] | arrays.cpp:50:27:50:30 | FieldAddress [read] |
+| arrays.cpp:50:10:50:17 | FieldAddress [read] [ptr, data] | arrays.cpp:50:10:50:17 | indirect [read] [ptr, data] |
+| arrays.cpp:50:10:50:17 | indirect [read] [ptr, data] | arrays.cpp:50:20:50:22 | FieldAddress [read] [data] |
+| arrays.cpp:50:20:50:22 | FieldAddress [read] [data] | arrays.cpp:50:8:50:25 | access to array [read] [data] |
+| arrays.cpp:50:27:50:30 | FieldAddress [read] | arrays.cpp:50:27:50:30 | data |
+| by_reference.cpp:11:48:11:52 | value | by_reference.cpp:12:8:12:8 | a [post update] |
+| by_reference.cpp:12:8:12:8 | a [post update] | by_reference.cpp:12:5:12:5 | s [post update] [a] |
+| by_reference.cpp:15:26:15:30 | value | by_reference.cpp:16:11:16:11 | a [post update] |
+| by_reference.cpp:16:11:16:11 | a [post update] | by_reference.cpp:16:5:16:8 | this [post update] [a] |
| by_reference.cpp:19:28:19:32 | value | by_reference.cpp:20:11:20:21 | value |
-| by_reference.cpp:20:5:20:8 | setDirectly output argument [a] | by_reference.cpp:20:5:20:8 | Chi [a] |
+| by_reference.cpp:20:11:20:21 | this [post update] [a] | by_reference.cpp:20:5:20:8 | this [post update] [a] |
| by_reference.cpp:20:11:20:21 | value | by_reference.cpp:15:26:15:30 | value |
-| by_reference.cpp:20:11:20:21 | value | by_reference.cpp:20:5:20:8 | setDirectly output argument [a] |
+| by_reference.cpp:20:11:20:21 | value | by_reference.cpp:20:11:20:21 | this [post update] [a] |
| by_reference.cpp:23:34:23:38 | value | by_reference.cpp:24:5:24:17 | value |
+| by_reference.cpp:24:5:24:17 | this [post update] [a] | by_reference.cpp:24:19:24:22 | this [post update] [a] |
| by_reference.cpp:24:5:24:17 | value | by_reference.cpp:11:48:11:52 | value |
-| by_reference.cpp:24:5:24:17 | value | by_reference.cpp:24:19:24:22 | nonMemberSetA output argument [a] |
-| by_reference.cpp:24:19:24:22 | nonMemberSetA output argument [a] | by_reference.cpp:24:19:24:22 | Chi [a] |
-| by_reference.cpp:31:46:31:46 | *s [a] | by_reference.cpp:32:15:32:15 | Store |
-| by_reference.cpp:35:9:35:19 | *#this [a] | by_reference.cpp:36:18:36:18 | Store |
-| by_reference.cpp:39:9:39:21 | *#this [a] | by_reference.cpp:40:12:40:15 | this indirection [a] |
-| by_reference.cpp:40:12:40:15 | this indirection [a] | by_reference.cpp:35:9:35:19 | *#this [a] |
-| by_reference.cpp:40:12:40:15 | this indirection [a] | by_reference.cpp:40:18:40:28 | call to getDirectly |
-| by_reference.cpp:40:18:40:28 | call to getDirectly | by_reference.cpp:40:18:40:28 | Store |
-| by_reference.cpp:43:9:43:27 | *#this [a] | by_reference.cpp:44:26:44:29 | this indirection [a] |
-| by_reference.cpp:44:12:44:24 | call to nonMemberGetA | by_reference.cpp:44:12:44:24 | Store |
-| by_reference.cpp:44:26:44:29 | this indirection [a] | by_reference.cpp:31:46:31:46 | *s [a] |
-| by_reference.cpp:44:26:44:29 | this indirection [a] | by_reference.cpp:44:12:44:24 | call to nonMemberGetA |
-| by_reference.cpp:50:3:50:3 | setDirectly output argument [a] | by_reference.cpp:51:8:51:8 | s indirection [a] |
+| by_reference.cpp:24:5:24:17 | value | by_reference.cpp:24:5:24:17 | this [post update] [a] |
+| by_reference.cpp:31:46:31:46 | *s [a] | by_reference.cpp:32:12:32:12 | s [read] [a] |
+| by_reference.cpp:31:46:31:46 | s [a] | by_reference.cpp:32:12:32:12 | s [read] [a] |
+| by_reference.cpp:32:12:32:12 | s [read] [a] | by_reference.cpp:32:15:32:15 | FieldAddress [read] |
+| by_reference.cpp:32:12:32:12 | s [read] [a] | by_reference.cpp:32:15:32:15 | FieldAddress [read] |
+| by_reference.cpp:32:15:32:15 | FieldAddress [read] | by_reference.cpp:31:16:31:28 | ReturnValue |
+| by_reference.cpp:32:15:32:15 | FieldAddress [read] | by_reference.cpp:31:16:31:28 | ReturnValue |
+| by_reference.cpp:35:9:35:19 | *#this [a] | by_reference.cpp:36:12:36:15 | this [read] [a] |
+| by_reference.cpp:35:9:35:19 | this [a] | by_reference.cpp:36:12:36:15 | this [read] [a] |
+| by_reference.cpp:36:12:36:15 | this [read] [a] | by_reference.cpp:36:18:36:18 | FieldAddress [read] |
+| by_reference.cpp:36:12:36:15 | this [read] [a] | by_reference.cpp:36:18:36:18 | FieldAddress [read] |
+| by_reference.cpp:36:18:36:18 | FieldAddress [read] | by_reference.cpp:35:9:35:19 | ReturnValue |
+| by_reference.cpp:36:18:36:18 | FieldAddress [read] | by_reference.cpp:35:9:35:19 | ReturnValue |
+| by_reference.cpp:39:9:39:21 | *#this [a] | by_reference.cpp:40:18:40:28 | this [a] |
+| by_reference.cpp:40:18:40:28 | call to getDirectly | by_reference.cpp:39:9:39:21 | ReturnValue |
+| by_reference.cpp:40:18:40:28 | this [a] | by_reference.cpp:35:9:35:19 | this [a] |
+| by_reference.cpp:40:18:40:28 | this [a] | by_reference.cpp:40:18:40:28 | call to getDirectly |
+| by_reference.cpp:43:9:43:27 | *#this [a] | by_reference.cpp:44:12:44:24 | this [a] |
+| by_reference.cpp:44:12:44:24 | call to nonMemberGetA | by_reference.cpp:43:9:43:27 | ReturnValue |
+| by_reference.cpp:44:12:44:24 | this [a] | by_reference.cpp:31:46:31:46 | s [a] |
+| by_reference.cpp:44:12:44:24 | this [a] | by_reference.cpp:44:12:44:24 | call to nonMemberGetA |
| by_reference.cpp:50:5:50:15 | call to user_input | by_reference.cpp:15:26:15:30 | value |
-| by_reference.cpp:50:5:50:15 | call to user_input | by_reference.cpp:50:3:50:3 | setDirectly output argument [a] |
+| by_reference.cpp:50:5:50:15 | call to user_input | by_reference.cpp:50:5:50:15 | s [post update] [a] |
+| by_reference.cpp:50:5:50:15 | s [post update] [a] | by_reference.cpp:51:8:51:8 | s indirection [a] |
| by_reference.cpp:50:17:50:26 | call to user_input | by_reference.cpp:50:5:50:15 | call to user_input |
| by_reference.cpp:51:8:51:8 | s indirection [a] | by_reference.cpp:35:9:35:19 | *#this [a] |
| by_reference.cpp:51:8:51:8 | s indirection [a] | by_reference.cpp:51:10:51:20 | call to getDirectly |
-| by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] | by_reference.cpp:57:8:57:8 | s indirection [a] |
| by_reference.cpp:56:5:56:17 | call to user_input | by_reference.cpp:19:28:19:32 | value |
-| by_reference.cpp:56:5:56:17 | call to user_input | by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] |
+| by_reference.cpp:56:5:56:17 | call to user_input | by_reference.cpp:56:5:56:17 | s [post update] [a] |
+| by_reference.cpp:56:5:56:17 | s [post update] [a] | by_reference.cpp:57:8:57:8 | s indirection [a] |
| by_reference.cpp:56:19:56:28 | call to user_input | by_reference.cpp:56:5:56:17 | call to user_input |
| by_reference.cpp:57:8:57:8 | s indirection [a] | by_reference.cpp:39:9:39:21 | *#this [a] |
| by_reference.cpp:57:8:57:8 | s indirection [a] | by_reference.cpp:57:10:57:22 | call to getIndirectly |
-| by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] | by_reference.cpp:63:8:63:8 | s indirection [a] |
| by_reference.cpp:62:5:62:23 | call to user_input | by_reference.cpp:23:34:23:38 | value |
-| by_reference.cpp:62:5:62:23 | call to user_input | by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] |
+| by_reference.cpp:62:5:62:23 | call to user_input | by_reference.cpp:62:5:62:23 | s [post update] [a] |
+| by_reference.cpp:62:5:62:23 | s [post update] [a] | by_reference.cpp:63:8:63:8 | s indirection [a] |
| by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:62:5:62:23 | call to user_input |
| by_reference.cpp:63:8:63:8 | s indirection [a] | by_reference.cpp:43:9:43:27 | *#this [a] |
| by_reference.cpp:63:8:63:8 | s indirection [a] | by_reference.cpp:63:10:63:28 | call to getThroughNonMember |
+| by_reference.cpp:68:3:68:15 | & ... [post update] [a] | by_reference.cpp:69:22:69:23 | & ... indirection [a] |
| by_reference.cpp:68:3:68:15 | call to user_input | by_reference.cpp:11:48:11:52 | value |
-| by_reference.cpp:68:3:68:15 | call to user_input | by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] |
-| by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] | by_reference.cpp:69:22:69:23 | & ... indirection [a] |
+| by_reference.cpp:68:3:68:15 | call to user_input | by_reference.cpp:68:3:68:15 | & ... [post update] [a] |
| by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:68:3:68:15 | call to user_input |
| by_reference.cpp:69:22:69:23 | & ... indirection [a] | by_reference.cpp:31:46:31:46 | *s [a] |
| by_reference.cpp:69:22:69:23 | & ... indirection [a] | by_reference.cpp:69:8:69:20 | call to nonMemberGetA |
-| by_reference.cpp:84:3:84:25 | Chi [a] | by_reference.cpp:102:21:102:39 | taint_inner_a_ptr output argument [a] |
-| by_reference.cpp:84:3:84:25 | Chi [a] | by_reference.cpp:106:21:106:41 | taint_inner_a_ptr output argument [a] |
-| by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:84:3:84:25 | Chi [a] |
-| by_reference.cpp:88:3:88:24 | Chi [a] | by_reference.cpp:122:21:122:38 | taint_inner_a_ref output argument [a] |
-| by_reference.cpp:88:3:88:24 | Chi [a] | by_reference.cpp:126:21:126:40 | taint_inner_a_ref output argument [a] |
-| by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:88:3:88:24 | Chi [a] |
-| by_reference.cpp:92:3:92:20 | Chi [[]] | by_reference.cpp:104:15:104:22 | taint_a_ptr output argument [[]] |
-| by_reference.cpp:92:3:92:20 | Chi [[]] | by_reference.cpp:108:15:108:24 | taint_a_ptr output argument [[]] |
-| by_reference.cpp:92:9:92:18 | call to user_input | by_reference.cpp:92:3:92:20 | Chi [[]] |
-| by_reference.cpp:96:3:96:19 | Chi [[]] | by_reference.cpp:124:15:124:21 | taint_a_ref output argument [[]] |
-| by_reference.cpp:96:3:96:19 | Chi [[]] | by_reference.cpp:128:15:128:23 | taint_a_ref output argument [[]] |
-| by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:96:3:96:19 | Chi [[]] |
-| by_reference.cpp:102:21:102:39 | Chi [a] | by_reference.cpp:110:27:110:27 | a |
-| by_reference.cpp:102:21:102:39 | taint_inner_a_ptr output argument [a] | by_reference.cpp:102:21:102:39 | Chi [a] |
-| by_reference.cpp:104:15:104:22 | Chi | by_reference.cpp:104:15:104:22 | Chi [a] |
-| by_reference.cpp:104:15:104:22 | Chi [a] | by_reference.cpp:112:14:112:14 | a |
-| by_reference.cpp:104:15:104:22 | taint_a_ptr output argument [[]] | by_reference.cpp:104:15:104:22 | Chi |
-| by_reference.cpp:106:21:106:41 | Chi [a] | by_reference.cpp:114:29:114:29 | a |
-| by_reference.cpp:106:21:106:41 | taint_inner_a_ptr output argument [a] | by_reference.cpp:106:21:106:41 | Chi [a] |
-| by_reference.cpp:108:15:108:24 | Chi | by_reference.cpp:108:15:108:24 | Chi [a] |
-| by_reference.cpp:108:15:108:24 | Chi [a] | by_reference.cpp:116:16:116:16 | a |
-| by_reference.cpp:108:15:108:24 | taint_a_ptr output argument [[]] | by_reference.cpp:108:15:108:24 | Chi |
-| by_reference.cpp:122:21:122:38 | Chi [a] | by_reference.cpp:130:27:130:27 | a |
-| by_reference.cpp:122:21:122:38 | taint_inner_a_ref output argument [a] | by_reference.cpp:122:21:122:38 | Chi [a] |
-| by_reference.cpp:124:15:124:21 | Chi | by_reference.cpp:124:15:124:21 | Chi [a] |
-| by_reference.cpp:124:15:124:21 | Chi [a] | by_reference.cpp:132:14:132:14 | a |
-| by_reference.cpp:124:15:124:21 | taint_a_ref output argument [[]] | by_reference.cpp:124:15:124:21 | Chi |
-| by_reference.cpp:126:21:126:40 | Chi [a] | by_reference.cpp:134:29:134:29 | a |
-| by_reference.cpp:126:21:126:40 | taint_inner_a_ref output argument [a] | by_reference.cpp:126:21:126:40 | Chi [a] |
-| by_reference.cpp:128:15:128:23 | Chi | by_reference.cpp:128:15:128:23 | Chi [a] |
-| by_reference.cpp:128:15:128:23 | Chi [a] | by_reference.cpp:136:16:136:16 | a |
-| by_reference.cpp:128:15:128:23 | taint_a_ref output argument [[]] | by_reference.cpp:128:15:128:23 | Chi |
-| complex.cpp:9:7:9:7 | *#this [a_] | complex.cpp:9:20:9:21 | Store |
-| complex.cpp:10:7:10:7 | *#this [b_] | complex.cpp:10:20:10:21 | Store |
-| complex.cpp:11:17:11:17 | a | complex.cpp:11:22:11:27 | Chi [a_] |
-| complex.cpp:12:8:12:11 | *#this [a_] | complex.cpp:12:22:12:27 | Chi [a_] |
-| complex.cpp:12:17:12:17 | b | complex.cpp:12:22:12:27 | Chi [b_] |
-| complex.cpp:40:17:40:17 | *b [a_] | complex.cpp:42:16:42:16 | f indirection [a_] |
-| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:42:16:42:16 | f indirection [b_] |
-| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:43:16:43:16 | f indirection [b_] |
-| complex.cpp:42:16:42:16 | a output argument [b_] | complex.cpp:43:16:43:16 | f indirection [b_] |
-| complex.cpp:42:16:42:16 | f indirection [a_] | complex.cpp:9:7:9:7 | *#this [a_] |
-| complex.cpp:42:16:42:16 | f indirection [a_] | complex.cpp:42:18:42:18 | call to a |
-| complex.cpp:42:16:42:16 | f indirection [b_] | complex.cpp:9:7:9:7 | *#this [b_] |
-| complex.cpp:42:16:42:16 | f indirection [b_] | complex.cpp:42:16:42:16 | a output argument [b_] |
-| complex.cpp:43:16:43:16 | f indirection [b_] | complex.cpp:10:7:10:7 | *#this [b_] |
-| complex.cpp:43:16:43:16 | f indirection [b_] | complex.cpp:43:18:43:18 | call to b |
-| complex.cpp:53:12:53:12 | setA output argument [a_] | complex.cpp:59:7:59:8 | b1 indirection [a_] |
+| by_reference.cpp:84:3:84:7 | inner [post update] [a] | by_reference.cpp:102:3:102:19 | & ... [post update] [a] |
+| by_reference.cpp:84:3:84:7 | inner [post update] [a] | by_reference.cpp:103:3:103:19 | inner_ptr [post update] [a] |
+| by_reference.cpp:84:3:84:7 | inner [post update] [a] | by_reference.cpp:106:3:106:19 | & ... [post update] [a] |
+| by_reference.cpp:84:3:84:7 | inner [post update] [a] | by_reference.cpp:107:3:107:19 | inner_ptr [post update] [a] |
+| by_reference.cpp:84:10:84:10 | a [post update] | by_reference.cpp:84:3:84:7 | inner [post update] [a] |
+| by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:84:10:84:10 | a [post update] |
+| by_reference.cpp:88:3:88:7 | (reference dereference) [post update] [a] | by_reference.cpp:88:3:88:7 | inner [post update] [a] |
+| by_reference.cpp:88:3:88:7 | (reference dereference) [post update] [a] | by_reference.cpp:122:3:122:19 | inner_nested [post update] [a] |
+| by_reference.cpp:88:3:88:7 | (reference dereference) [post update] [a] | by_reference.cpp:123:3:123:19 | * ... [post update] [a] |
+| by_reference.cpp:88:3:88:7 | (reference dereference) [post update] [a] | by_reference.cpp:126:3:126:19 | inner_nested [post update] [a] |
+| by_reference.cpp:88:3:88:7 | (reference dereference) [post update] [a] | by_reference.cpp:127:3:127:19 | * ... [post update] [a] |
+| by_reference.cpp:88:3:88:7 | inner [post update] [a] | by_reference.cpp:122:3:122:19 | inner_nested [post update] [a] |
+| by_reference.cpp:88:3:88:7 | inner [post update] [a] | by_reference.cpp:123:3:123:19 | * ... [post update] [a] |
+| by_reference.cpp:88:3:88:7 | inner [post update] [a] | by_reference.cpp:126:3:126:19 | inner_nested [post update] [a] |
+| by_reference.cpp:88:3:88:7 | inner [post update] [a] | by_reference.cpp:127:3:127:19 | * ... [post update] [a] |
+| by_reference.cpp:88:9:88:9 | a [post update] | by_reference.cpp:88:3:88:7 | (reference dereference) [post update] [a] |
+| by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:88:9:88:9 | a [post update] |
+| by_reference.cpp:92:3:92:5 | * ... [post update] | by_reference.cpp:104:3:104:13 | & ... [post update] |
+| by_reference.cpp:92:3:92:5 | * ... [post update] | by_reference.cpp:108:3:108:13 | & ... [post update] |
+| by_reference.cpp:92:4:92:5 | pa [post update] | by_reference.cpp:104:3:104:13 | & ... [post update] |
+| by_reference.cpp:92:4:92:5 | pa [post update] | by_reference.cpp:108:3:108:13 | & ... [post update] |
+| by_reference.cpp:92:9:92:18 | call to user_input | by_reference.cpp:92:3:92:5 | * ... [post update] |
+| by_reference.cpp:92:9:92:18 | call to user_input | by_reference.cpp:92:4:92:5 | pa [post update] |
+| by_reference.cpp:96:3:96:4 | (reference dereference) [post update] | by_reference.cpp:124:3:124:13 | a [post update] |
+| by_reference.cpp:96:3:96:4 | (reference dereference) [post update] | by_reference.cpp:128:3:128:13 | a [post update] |
+| by_reference.cpp:96:3:96:4 | pa [post update] | by_reference.cpp:124:3:124:13 | a [post update] |
+| by_reference.cpp:96:3:96:4 | pa [post update] | by_reference.cpp:128:3:128:13 | a [post update] |
+| by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:96:3:96:4 | (reference dereference) [post update] |
+| by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:96:3:96:4 | pa [post update] |
+| by_reference.cpp:102:3:102:19 | & ... [post update] [a] | by_reference.cpp:102:28:102:39 | inner_nested [post update] [a] |
+| by_reference.cpp:102:22:102:26 | outer [post update] [inner_nested, a] | by_reference.cpp:110:8:110:12 | outer [read] [inner_nested, a] |
+| by_reference.cpp:102:28:102:39 | inner_nested [post update] [a] | by_reference.cpp:102:22:102:26 | outer [post update] [inner_nested, a] |
+| by_reference.cpp:103:3:103:19 | inner_ptr [post update] [a] | by_reference.cpp:103:27:103:35 | FieldAddress [post update] [a] |
+| by_reference.cpp:103:21:103:25 | outer [post update] [inner_ptr, a] | by_reference.cpp:111:8:111:12 | outer [read] [inner_ptr, a] |
+| by_reference.cpp:103:27:103:35 | FieldAddress [post update] [a] | by_reference.cpp:103:21:103:25 | outer [post update] [inner_ptr, a] |
+| by_reference.cpp:104:3:104:13 | & ... [post update] | by_reference.cpp:104:22:104:22 | a [post update] |
+| by_reference.cpp:104:16:104:20 | outer [post update] [a] | by_reference.cpp:112:8:112:12 | outer [read] [a] |
+| by_reference.cpp:104:22:104:22 | a [post update] | by_reference.cpp:104:16:104:20 | outer [post update] [a] |
+| by_reference.cpp:106:3:106:19 | & ... [post update] [a] | by_reference.cpp:106:30:106:41 | inner_nested [post update] [a] |
+| by_reference.cpp:106:22:106:27 | pouter [post update] [inner_nested, a] | by_reference.cpp:114:8:114:13 | pouter [read] [inner_nested, a] |
+| by_reference.cpp:106:30:106:41 | inner_nested [post update] [a] | by_reference.cpp:106:22:106:27 | pouter [post update] [inner_nested, a] |
+| by_reference.cpp:107:3:107:19 | inner_ptr [post update] [a] | by_reference.cpp:107:29:107:37 | FieldAddress [post update] [a] |
+| by_reference.cpp:107:21:107:26 | pouter [post update] [inner_ptr, a] | by_reference.cpp:115:8:115:13 | pouter [read] [inner_ptr, a] |
+| by_reference.cpp:107:29:107:37 | FieldAddress [post update] [a] | by_reference.cpp:107:21:107:26 | pouter [post update] [inner_ptr, a] |
+| by_reference.cpp:108:3:108:13 | & ... [post update] | by_reference.cpp:108:24:108:24 | a [post update] |
+| by_reference.cpp:108:16:108:21 | pouter [post update] [a] | by_reference.cpp:116:8:116:13 | pouter [read] [a] |
+| by_reference.cpp:108:24:108:24 | a [post update] | by_reference.cpp:108:16:108:21 | pouter [post update] [a] |
+| by_reference.cpp:110:8:110:12 | outer [read] [inner_nested, a] | by_reference.cpp:110:14:110:25 | inner_nested [read] [a] |
+| by_reference.cpp:110:14:110:25 | inner_nested [read] [a] | by_reference.cpp:110:27:110:27 | FieldAddress [read] |
+| by_reference.cpp:110:27:110:27 | FieldAddress [read] | by_reference.cpp:110:27:110:27 | a |
+| by_reference.cpp:111:8:111:12 | outer [read] [inner_ptr, a] | by_reference.cpp:111:14:111:22 | FieldAddress [read] [a] |
+| by_reference.cpp:111:14:111:22 | FieldAddress [read] [a] | by_reference.cpp:111:14:111:22 | inner_ptr [read] [a] |
+| by_reference.cpp:111:14:111:22 | inner_ptr [read] [a] | by_reference.cpp:111:25:111:25 | FieldAddress [read] |
+| by_reference.cpp:111:25:111:25 | FieldAddress [read] | by_reference.cpp:111:25:111:25 | a |
+| by_reference.cpp:112:8:112:12 | outer [read] [a] | by_reference.cpp:112:14:112:14 | FieldAddress [read] |
+| by_reference.cpp:112:14:112:14 | FieldAddress [read] | by_reference.cpp:112:14:112:14 | a |
+| by_reference.cpp:114:8:114:13 | pouter [read] [inner_nested, a] | by_reference.cpp:114:16:114:27 | inner_nested [read] [a] |
+| by_reference.cpp:114:16:114:27 | inner_nested [read] [a] | by_reference.cpp:114:29:114:29 | FieldAddress [read] |
+| by_reference.cpp:114:29:114:29 | FieldAddress [read] | by_reference.cpp:114:29:114:29 | a |
+| by_reference.cpp:115:8:115:13 | pouter [read] [inner_ptr, a] | by_reference.cpp:115:16:115:24 | FieldAddress [read] [a] |
+| by_reference.cpp:115:16:115:24 | FieldAddress [read] [a] | by_reference.cpp:115:16:115:24 | inner_ptr [read] [a] |
+| by_reference.cpp:115:16:115:24 | inner_ptr [read] [a] | by_reference.cpp:115:27:115:27 | FieldAddress [read] |
+| by_reference.cpp:115:27:115:27 | FieldAddress [read] | by_reference.cpp:115:27:115:27 | a |
+| by_reference.cpp:116:8:116:13 | pouter [read] [a] | by_reference.cpp:116:16:116:16 | FieldAddress [read] |
+| by_reference.cpp:116:16:116:16 | FieldAddress [read] | by_reference.cpp:116:16:116:16 | a |
+| by_reference.cpp:122:3:122:19 | inner_nested [post update] [a] | by_reference.cpp:122:27:122:38 | inner_nested [post update] [a] |
+| by_reference.cpp:122:21:122:25 | outer [post update] [inner_nested, a] | by_reference.cpp:130:8:130:12 | outer [read] [inner_nested, a] |
+| by_reference.cpp:122:27:122:38 | inner_nested [post update] [a] | by_reference.cpp:122:21:122:25 | outer [post update] [inner_nested, a] |
+| by_reference.cpp:123:3:123:19 | * ... [post update] [a] | by_reference.cpp:123:28:123:36 | FieldAddress [post update] [a] |
+| by_reference.cpp:123:22:123:26 | outer [post update] [inner_ptr, a] | by_reference.cpp:131:8:131:12 | outer [read] [inner_ptr, a] |
+| by_reference.cpp:123:28:123:36 | FieldAddress [post update] [a] | by_reference.cpp:123:22:123:26 | outer [post update] [inner_ptr, a] |
+| by_reference.cpp:124:3:124:13 | a [post update] | by_reference.cpp:124:21:124:21 | a [post update] |
+| by_reference.cpp:124:15:124:19 | outer [post update] [a] | by_reference.cpp:132:8:132:12 | outer [read] [a] |
+| by_reference.cpp:124:21:124:21 | a [post update] | by_reference.cpp:124:15:124:19 | outer [post update] [a] |
+| by_reference.cpp:126:3:126:19 | inner_nested [post update] [a] | by_reference.cpp:126:29:126:40 | inner_nested [post update] [a] |
+| by_reference.cpp:126:21:126:26 | pouter [post update] [inner_nested, a] | by_reference.cpp:134:8:134:13 | pouter [read] [inner_nested, a] |
+| by_reference.cpp:126:29:126:40 | inner_nested [post update] [a] | by_reference.cpp:126:21:126:26 | pouter [post update] [inner_nested, a] |
+| by_reference.cpp:127:3:127:19 | * ... [post update] [a] | by_reference.cpp:127:30:127:38 | FieldAddress [post update] [a] |
+| by_reference.cpp:127:22:127:27 | pouter [post update] [inner_ptr, a] | by_reference.cpp:135:8:135:13 | pouter [read] [inner_ptr, a] |
+| by_reference.cpp:127:30:127:38 | FieldAddress [post update] [a] | by_reference.cpp:127:22:127:27 | pouter [post update] [inner_ptr, a] |
+| by_reference.cpp:128:3:128:13 | a [post update] | by_reference.cpp:128:23:128:23 | a [post update] |
+| by_reference.cpp:128:15:128:20 | pouter [post update] [a] | by_reference.cpp:136:8:136:13 | pouter [read] [a] |
+| by_reference.cpp:128:23:128:23 | a [post update] | by_reference.cpp:128:15:128:20 | pouter [post update] [a] |
+| by_reference.cpp:130:8:130:12 | outer [read] [inner_nested, a] | by_reference.cpp:130:14:130:25 | inner_nested [read] [a] |
+| by_reference.cpp:130:14:130:25 | inner_nested [read] [a] | by_reference.cpp:130:27:130:27 | FieldAddress [read] |
+| by_reference.cpp:130:27:130:27 | FieldAddress [read] | by_reference.cpp:130:27:130:27 | a |
+| by_reference.cpp:131:8:131:12 | outer [read] [inner_ptr, a] | by_reference.cpp:131:14:131:22 | FieldAddress [read] [a] |
+| by_reference.cpp:131:14:131:22 | FieldAddress [read] [a] | by_reference.cpp:131:14:131:22 | inner_ptr [read] [a] |
+| by_reference.cpp:131:14:131:22 | inner_ptr [read] [a] | by_reference.cpp:131:25:131:25 | FieldAddress [read] |
+| by_reference.cpp:131:25:131:25 | FieldAddress [read] | by_reference.cpp:131:25:131:25 | a |
+| by_reference.cpp:132:8:132:12 | outer [read] [a] | by_reference.cpp:132:14:132:14 | FieldAddress [read] |
+| by_reference.cpp:132:14:132:14 | FieldAddress [read] | by_reference.cpp:132:14:132:14 | a |
+| by_reference.cpp:134:8:134:13 | pouter [read] [inner_nested, a] | by_reference.cpp:134:16:134:27 | inner_nested [read] [a] |
+| by_reference.cpp:134:16:134:27 | inner_nested [read] [a] | by_reference.cpp:134:29:134:29 | FieldAddress [read] |
+| by_reference.cpp:134:29:134:29 | FieldAddress [read] | by_reference.cpp:134:29:134:29 | a |
+| by_reference.cpp:135:8:135:13 | pouter [read] [inner_ptr, a] | by_reference.cpp:135:16:135:24 | FieldAddress [read] [a] |
+| by_reference.cpp:135:16:135:24 | FieldAddress [read] [a] | by_reference.cpp:135:16:135:24 | inner_ptr [read] [a] |
+| by_reference.cpp:135:16:135:24 | inner_ptr [read] [a] | by_reference.cpp:135:27:135:27 | FieldAddress [read] |
+| by_reference.cpp:135:27:135:27 | FieldAddress [read] | by_reference.cpp:135:27:135:27 | a |
+| by_reference.cpp:136:8:136:13 | pouter [read] [a] | by_reference.cpp:136:16:136:16 | FieldAddress [read] |
+| by_reference.cpp:136:16:136:16 | FieldAddress [read] | by_reference.cpp:136:16:136:16 | a |
+| complex.cpp:9:7:9:7 | this [a_] | complex.cpp:9:20:9:21 | this [read] [a_] |
+| complex.cpp:9:20:9:21 | FieldAddress [read] | complex.cpp:9:7:9:7 | ReturnValue |
+| complex.cpp:9:20:9:21 | this [read] [a_] | complex.cpp:9:20:9:21 | FieldAddress [read] |
+| complex.cpp:10:7:10:7 | this [b_] | complex.cpp:10:20:10:21 | this [read] [b_] |
+| complex.cpp:10:20:10:21 | FieldAddress [read] | complex.cpp:10:7:10:7 | ReturnValue |
+| complex.cpp:10:20:10:21 | this [read] [b_] | complex.cpp:10:20:10:21 | FieldAddress [read] |
+| complex.cpp:11:17:11:17 | a | complex.cpp:11:22:11:23 | a_ [post update] |
+| complex.cpp:11:22:11:23 | a_ [post update] | complex.cpp:11:22:11:23 | this [post update] [a_] |
+| complex.cpp:12:17:12:17 | b | complex.cpp:12:22:12:23 | b_ [post update] |
+| complex.cpp:12:22:12:23 | b_ [post update] | complex.cpp:12:22:12:23 | this [post update] [b_] |
+| complex.cpp:40:17:40:17 | b [inner, f, a_] | complex.cpp:42:8:42:8 | (reference dereference) [read] [inner, f, a_] |
+| complex.cpp:40:17:40:17 | b [inner, f, b_] | complex.cpp:43:8:43:8 | (reference dereference) [read] [inner, f, b_] |
+| complex.cpp:42:8:42:8 | (reference dereference) [read] [inner, f, a_] | complex.cpp:42:10:42:14 | inner [read] [f, a_] |
+| complex.cpp:42:10:42:14 | inner [read] [f, a_] | complex.cpp:42:16:42:16 | f [read] [a_] |
+| complex.cpp:42:16:42:16 | f [read] [a_] | complex.cpp:42:18:42:18 | f [a_] |
+| complex.cpp:42:18:42:18 | f [a_] | complex.cpp:9:7:9:7 | this [a_] |
+| complex.cpp:42:18:42:18 | f [a_] | complex.cpp:42:18:42:18 | call to a |
+| complex.cpp:43:8:43:8 | (reference dereference) [read] [inner, f, b_] | complex.cpp:43:10:43:14 | inner [read] [f, b_] |
+| complex.cpp:43:10:43:14 | inner [read] [f, b_] | complex.cpp:43:16:43:16 | f [read] [b_] |
+| complex.cpp:43:16:43:16 | f [read] [b_] | complex.cpp:43:18:43:18 | f [b_] |
+| complex.cpp:43:18:43:18 | f [b_] | complex.cpp:10:7:10:7 | this [b_] |
+| complex.cpp:43:18:43:18 | f [b_] | complex.cpp:43:18:43:18 | call to b |
+| complex.cpp:53:3:53:4 | b1 [post update] [inner, f, a_] | complex.cpp:59:3:59:5 | b1 [inner, f, a_] |
+| complex.cpp:53:6:53:10 | inner [post update] [f, a_] | complex.cpp:53:3:53:4 | b1 [post update] [inner, f, a_] |
+| complex.cpp:53:12:53:12 | f [post update] [a_] | complex.cpp:53:6:53:10 | inner [post update] [f, a_] |
| complex.cpp:53:14:53:17 | call to user_input | complex.cpp:11:17:11:17 | a |
-| complex.cpp:53:14:53:17 | call to user_input | complex.cpp:53:12:53:12 | setA output argument [a_] |
+| complex.cpp:53:14:53:17 | call to user_input | complex.cpp:53:14:53:17 | f [post update] [a_] |
+| complex.cpp:53:14:53:17 | f [post update] [a_] | complex.cpp:53:12:53:12 | f [post update] [a_] |
| complex.cpp:53:19:53:28 | call to user_input | complex.cpp:53:14:53:17 | call to user_input |
-| complex.cpp:54:12:54:12 | setB output argument [b_] | complex.cpp:62:7:62:8 | b2 indirection [b_] |
+| complex.cpp:54:3:54:4 | b2 [post update] [inner, f, b_] | complex.cpp:62:3:62:5 | b2 [inner, f, b_] |
+| complex.cpp:54:6:54:10 | inner [post update] [f, b_] | complex.cpp:54:3:54:4 | b2 [post update] [inner, f, b_] |
+| complex.cpp:54:12:54:12 | f [post update] [b_] | complex.cpp:54:6:54:10 | inner [post update] [f, b_] |
| complex.cpp:54:14:54:17 | call to user_input | complex.cpp:12:17:12:17 | b |
-| complex.cpp:54:14:54:17 | call to user_input | complex.cpp:54:12:54:12 | setB output argument [b_] |
+| complex.cpp:54:14:54:17 | call to user_input | complex.cpp:54:14:54:17 | f [post update] [b_] |
+| complex.cpp:54:14:54:17 | f [post update] [b_] | complex.cpp:54:12:54:12 | f [post update] [b_] |
| complex.cpp:54:19:54:28 | call to user_input | complex.cpp:54:14:54:17 | call to user_input |
-| complex.cpp:55:12:55:12 | setA output argument [a_] | complex.cpp:56:12:56:12 | f indirection [a_] |
-| complex.cpp:55:12:55:12 | setA output argument [a_] | complex.cpp:65:7:65:8 | b3 indirection [a_] |
+| complex.cpp:55:3:55:4 | b3 [post update] [inner, f, a_] | complex.cpp:65:3:65:5 | b3 [inner, f, a_] |
+| complex.cpp:55:6:55:10 | inner [post update] [f, a_] | complex.cpp:55:3:55:4 | b3 [post update] [inner, f, a_] |
+| complex.cpp:55:12:55:12 | f [post update] [a_] | complex.cpp:55:6:55:10 | inner [post update] [f, a_] |
| complex.cpp:55:14:55:17 | call to user_input | complex.cpp:11:17:11:17 | a |
-| complex.cpp:55:14:55:17 | call to user_input | complex.cpp:55:12:55:12 | setA output argument [a_] |
+| complex.cpp:55:14:55:17 | call to user_input | complex.cpp:55:14:55:17 | f [post update] [a_] |
+| complex.cpp:55:14:55:17 | f [post update] [a_] | complex.cpp:55:12:55:12 | f [post update] [a_] |
| complex.cpp:55:19:55:28 | call to user_input | complex.cpp:55:14:55:17 | call to user_input |
-| complex.cpp:56:12:56:12 | f indirection [a_] | complex.cpp:12:8:12:11 | *#this [a_] |
-| complex.cpp:56:12:56:12 | f indirection [a_] | complex.cpp:56:12:56:12 | setB output argument [a_] |
-| complex.cpp:56:12:56:12 | setB output argument [a_] | complex.cpp:65:7:65:8 | b3 indirection [a_] |
-| complex.cpp:56:12:56:12 | setB output argument [b_] | complex.cpp:65:7:65:8 | b3 indirection [b_] |
+| complex.cpp:56:3:56:4 | b3 [post update] [inner, f, b_] | complex.cpp:65:3:65:5 | b3 [inner, f, b_] |
+| complex.cpp:56:6:56:10 | inner [post update] [f, b_] | complex.cpp:56:3:56:4 | b3 [post update] [inner, f, b_] |
+| complex.cpp:56:12:56:12 | f [post update] [b_] | complex.cpp:56:6:56:10 | inner [post update] [f, b_] |
| complex.cpp:56:14:56:17 | call to user_input | complex.cpp:12:17:12:17 | b |
-| complex.cpp:56:14:56:17 | call to user_input | complex.cpp:56:12:56:12 | setB output argument [b_] |
+| complex.cpp:56:14:56:17 | call to user_input | complex.cpp:56:14:56:17 | f [post update] [b_] |
+| complex.cpp:56:14:56:17 | f [post update] [b_] | complex.cpp:56:12:56:12 | f [post update] [b_] |
| complex.cpp:56:19:56:28 | call to user_input | complex.cpp:56:14:56:17 | call to user_input |
-| complex.cpp:59:7:59:8 | b1 indirection [a_] | complex.cpp:40:17:40:17 | *b [a_] |
-| complex.cpp:62:7:62:8 | b2 indirection [b_] | complex.cpp:40:17:40:17 | *b [b_] |
-| complex.cpp:65:7:65:8 | b3 indirection [a_] | complex.cpp:40:17:40:17 | *b [a_] |
-| complex.cpp:65:7:65:8 | b3 indirection [b_] | complex.cpp:40:17:40:17 | *b [b_] |
-| constructors.cpp:18:9:18:9 | *#this [a_] | constructors.cpp:18:22:18:23 | Store |
-| constructors.cpp:19:9:19:9 | *#this [b_] | constructors.cpp:19:22:19:23 | Store |
-| constructors.cpp:23:13:23:13 | a | constructors.cpp:23:28:23:28 | Chi [a_] |
-| constructors.cpp:23:20:23:20 | b | constructors.cpp:23:35:23:35 | Chi [b_] |
-| constructors.cpp:23:28:23:28 | Chi [a_] | constructors.cpp:23:35:23:35 | Chi [a_] |
-| constructors.cpp:26:15:26:15 | *f [a_] | constructors.cpp:28:10:28:10 | f indirection [a_] |
-| constructors.cpp:26:15:26:15 | *f [b_] | constructors.cpp:28:10:28:10 | f indirection [b_] |
-| constructors.cpp:26:15:26:15 | *f [b_] | constructors.cpp:29:10:29:10 | f indirection [b_] |
-| constructors.cpp:28:10:28:10 | a output argument [b_] | constructors.cpp:29:10:29:10 | f indirection [b_] |
-| constructors.cpp:28:10:28:10 | f indirection [a_] | constructors.cpp:18:9:18:9 | *#this [a_] |
-| constructors.cpp:28:10:28:10 | f indirection [a_] | constructors.cpp:28:12:28:12 | call to a |
-| constructors.cpp:28:10:28:10 | f indirection [b_] | constructors.cpp:18:9:18:9 | *#this [b_] |
-| constructors.cpp:28:10:28:10 | f indirection [b_] | constructors.cpp:28:10:28:10 | a output argument [b_] |
-| constructors.cpp:29:10:29:10 | f indirection [b_] | constructors.cpp:19:9:19:9 | *#this [b_] |
-| constructors.cpp:29:10:29:10 | f indirection [b_] | constructors.cpp:29:12:29:12 | call to b |
+| complex.cpp:59:3:59:5 | b1 [inner, f, a_] | complex.cpp:40:17:40:17 | b [inner, f, a_] |
+| complex.cpp:62:3:62:5 | b2 [inner, f, b_] | complex.cpp:40:17:40:17 | b [inner, f, b_] |
+| complex.cpp:65:3:65:5 | b3 [inner, f, a_] | complex.cpp:40:17:40:17 | b [inner, f, a_] |
+| complex.cpp:65:3:65:5 | b3 [inner, f, b_] | complex.cpp:40:17:40:17 | b [inner, f, b_] |
+| conflated.cpp:10:4:10:5 | (reference dereference) [post update] [p] | conflated.cpp:11:9:11:10 | (reference dereference) [read] [p] |
+| conflated.cpp:10:7:10:7 | FieldAddress [post update] | conflated.cpp:10:4:10:5 | (reference dereference) [post update] [p] |
+| conflated.cpp:10:11:10:20 | call to user_input | conflated.cpp:10:7:10:7 | FieldAddress [post update] |
+| conflated.cpp:11:9:11:10 | (reference dereference) [read] [p] | conflated.cpp:11:12:11:12 | FieldAddress [read] |
+| conflated.cpp:11:12:11:12 | FieldAddress [read] | conflated.cpp:11:8:11:12 | * ... |
+| conflated.cpp:19:19:19:21 | argument_source output argument | conflated.cpp:20:8:20:10 | (void *)... |
+| conflated.cpp:19:19:19:21 | argument_source output argument | conflated.cpp:20:8:20:10 | raw |
+| conflated.cpp:29:3:29:4 | pa [post update] [x] | conflated.cpp:30:8:30:9 | pa [read] [x] |
+| conflated.cpp:29:7:29:7 | x [post update] | conflated.cpp:29:3:29:4 | pa [post update] [x] |
+| conflated.cpp:29:11:29:20 | call to user_input | conflated.cpp:29:7:29:7 | x [post update] |
+| conflated.cpp:30:8:30:9 | pa [read] [x] | conflated.cpp:30:12:30:12 | FieldAddress [read] |
+| conflated.cpp:30:12:30:12 | FieldAddress [read] | conflated.cpp:30:12:30:12 | x |
+| conflated.cpp:36:3:36:4 | pa [post update] [x] | conflated.cpp:37:8:37:9 | pa [read] [x] |
+| conflated.cpp:36:7:36:7 | x [post update] | conflated.cpp:36:3:36:4 | pa [post update] [x] |
+| conflated.cpp:36:11:36:20 | call to user_input | conflated.cpp:36:7:36:7 | x [post update] |
+| conflated.cpp:37:8:37:9 | pa [read] [x] | conflated.cpp:37:12:37:12 | FieldAddress [read] |
+| conflated.cpp:37:12:37:12 | FieldAddress [read] | conflated.cpp:37:12:37:12 | x |
+| conflated.cpp:54:3:54:4 | ll [post update] [next, y] | conflated.cpp:55:8:55:9 | ll [read] [next, y] |
+| conflated.cpp:54:7:54:10 | FieldAddress [post update] [y] | conflated.cpp:54:3:54:4 | ll [post update] [next, y] |
+| conflated.cpp:54:7:54:10 | next [post update] [y] | conflated.cpp:54:7:54:10 | FieldAddress [post update] [y] |
+| conflated.cpp:54:13:54:13 | y [post update] | conflated.cpp:54:7:54:10 | next [post update] [y] |
+| conflated.cpp:54:17:54:26 | call to user_input | conflated.cpp:54:13:54:13 | y [post update] |
+| conflated.cpp:55:8:55:9 | ll [read] [next, y] | conflated.cpp:55:12:55:15 | FieldAddress [read] [y] |
+| conflated.cpp:55:12:55:15 | FieldAddress [read] [y] | conflated.cpp:55:12:55:15 | next [read] [y] |
+| conflated.cpp:55:12:55:15 | next [read] [y] | conflated.cpp:55:18:55:18 | FieldAddress [read] |
+| conflated.cpp:55:18:55:18 | FieldAddress [read] | conflated.cpp:55:18:55:18 | y |
+| conflated.cpp:60:3:60:4 | ll [post update] [next, y] | conflated.cpp:61:8:61:9 | ll [read] [next, y] |
+| conflated.cpp:60:7:60:10 | FieldAddress [post update] [y] | conflated.cpp:60:3:60:4 | ll [post update] [next, y] |
+| conflated.cpp:60:7:60:10 | next [post update] [y] | conflated.cpp:60:7:60:10 | FieldAddress [post update] [y] |
+| conflated.cpp:60:13:60:13 | y [post update] | conflated.cpp:60:7:60:10 | next [post update] [y] |
+| conflated.cpp:60:17:60:26 | call to user_input | conflated.cpp:60:13:60:13 | y [post update] |
+| conflated.cpp:61:8:61:9 | ll [read] [next, y] | conflated.cpp:61:12:61:15 | FieldAddress [read] [y] |
+| conflated.cpp:61:12:61:15 | FieldAddress [read] [y] | conflated.cpp:61:12:61:15 | next [read] [y] |
+| conflated.cpp:61:12:61:15 | next [read] [y] | conflated.cpp:61:18:61:18 | FieldAddress [read] |
+| conflated.cpp:61:18:61:18 | FieldAddress [read] | conflated.cpp:61:18:61:18 | y |
+| constructors.cpp:18:9:18:9 | this [a_] | constructors.cpp:18:22:18:23 | this [read] [a_] |
+| constructors.cpp:18:22:18:23 | FieldAddress [read] | constructors.cpp:18:9:18:9 | ReturnValue |
+| constructors.cpp:18:22:18:23 | this [read] [a_] | constructors.cpp:18:22:18:23 | FieldAddress [read] |
+| constructors.cpp:19:9:19:9 | this [b_] | constructors.cpp:19:22:19:23 | this [read] [b_] |
+| constructors.cpp:19:22:19:23 | FieldAddress [read] | constructors.cpp:19:9:19:9 | ReturnValue |
+| constructors.cpp:19:22:19:23 | this [read] [b_] | constructors.cpp:19:22:19:23 | FieldAddress [read] |
+| constructors.cpp:23:13:23:13 | a | constructors.cpp:23:25:23:29 | FieldAddress [post update] |
+| constructors.cpp:23:20:23:20 | b | constructors.cpp:23:32:23:36 | FieldAddress [post update] |
+| constructors.cpp:23:25:23:29 | FieldAddress [post update] | constructors.cpp:23:5:23:7 | this [post update] [a_] |
+| constructors.cpp:23:32:23:36 | FieldAddress [post update] | constructors.cpp:23:5:23:7 | this [post update] [b_] |
+| constructors.cpp:26:15:26:15 | f [a_] | constructors.cpp:28:12:28:12 | f [a_] |
+| constructors.cpp:26:15:26:15 | f [b_] | constructors.cpp:29:12:29:12 | f [b_] |
+| constructors.cpp:28:12:28:12 | f [a_] | constructors.cpp:18:9:18:9 | this [a_] |
+| constructors.cpp:28:12:28:12 | f [a_] | constructors.cpp:28:12:28:12 | call to a |
+| constructors.cpp:29:12:29:12 | f [b_] | constructors.cpp:19:9:19:9 | this [b_] |
+| constructors.cpp:29:12:29:12 | f [b_] | constructors.cpp:29:12:29:12 | call to b |
| constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:34:11:34:26 | call to user_input |
-| constructors.cpp:34:11:34:26 | Foo output argument [a_] | constructors.cpp:40:9:40:9 | f indirection [a_] |
+| constructors.cpp:34:11:34:26 | Argument this [post update] [a_] | constructors.cpp:40:5:40:7 | f [a_] |
| constructors.cpp:34:11:34:26 | call to user_input | constructors.cpp:23:13:23:13 | a |
-| constructors.cpp:34:11:34:26 | call to user_input | constructors.cpp:34:11:34:26 | Foo output argument [a_] |
-| constructors.cpp:35:11:35:26 | Foo output argument [b_] | constructors.cpp:43:9:43:9 | g indirection [b_] |
+| constructors.cpp:34:11:34:26 | call to user_input | constructors.cpp:34:11:34:26 | Argument this [post update] [a_] |
+| constructors.cpp:35:11:35:26 | Argument this [post update] [b_] | constructors.cpp:43:5:43:7 | g [b_] |
| constructors.cpp:35:11:35:26 | call to user_input | constructors.cpp:23:20:23:20 | b |
-| constructors.cpp:35:11:35:26 | call to user_input | constructors.cpp:35:11:35:26 | Foo output argument [b_] |
+| constructors.cpp:35:11:35:26 | call to user_input | constructors.cpp:35:11:35:26 | Argument this [post update] [b_] |
| constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:35:11:35:26 | call to user_input |
| constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:36:11:36:37 | call to user_input |
-| constructors.cpp:36:11:36:37 | Foo output argument [a_] | constructors.cpp:46:9:46:9 | h indirection [a_] |
-| constructors.cpp:36:11:36:37 | Foo output argument [b_] | constructors.cpp:46:9:46:9 | h indirection [b_] |
+| constructors.cpp:36:11:36:37 | Argument this [post update] [a_] | constructors.cpp:46:5:46:7 | h [a_] |
+| constructors.cpp:36:11:36:37 | Argument this [post update] [b_] | constructors.cpp:46:5:46:7 | h [b_] |
| constructors.cpp:36:11:36:37 | call to user_input | constructors.cpp:23:13:23:13 | a |
| constructors.cpp:36:11:36:37 | call to user_input | constructors.cpp:23:20:23:20 | b |
-| constructors.cpp:36:11:36:37 | call to user_input | constructors.cpp:36:11:36:37 | Foo output argument [a_] |
-| constructors.cpp:36:11:36:37 | call to user_input | constructors.cpp:36:11:36:37 | Foo output argument [b_] |
+| constructors.cpp:36:11:36:37 | call to user_input | constructors.cpp:36:11:36:37 | Argument this [post update] [a_] |
+| constructors.cpp:36:11:36:37 | call to user_input | constructors.cpp:36:11:36:37 | Argument this [post update] [b_] |
| constructors.cpp:36:25:36:34 | call to user_input | constructors.cpp:36:11:36:37 | call to user_input |
-| constructors.cpp:40:9:40:9 | f indirection [a_] | constructors.cpp:26:15:26:15 | *f [a_] |
-| constructors.cpp:43:9:43:9 | g indirection [b_] | constructors.cpp:26:15:26:15 | *f [b_] |
-| constructors.cpp:46:9:46:9 | h indirection [a_] | constructors.cpp:26:15:26:15 | *f [a_] |
-| constructors.cpp:46:9:46:9 | h indirection [b_] | constructors.cpp:26:15:26:15 | *f [b_] |
-| simple.cpp:18:9:18:9 | *#this [a_] | simple.cpp:18:22:18:23 | Store |
-| simple.cpp:19:9:19:9 | *#this [b_] | simple.cpp:19:22:19:23 | Store |
-| simple.cpp:20:19:20:19 | a | simple.cpp:20:24:20:29 | Chi [a_] |
-| simple.cpp:21:10:21:13 | *#this [a_] | simple.cpp:21:24:21:29 | Chi [a_] |
-| simple.cpp:21:19:21:19 | b | simple.cpp:21:24:21:29 | Chi [b_] |
-| simple.cpp:26:15:26:15 | *f [a_] | simple.cpp:28:10:28:10 | f indirection [a_] |
-| simple.cpp:26:15:26:15 | *f [b_] | simple.cpp:28:10:28:10 | f indirection [b_] |
-| simple.cpp:26:15:26:15 | *f [b_] | simple.cpp:29:10:29:10 | f indirection [b_] |
-| simple.cpp:28:10:28:10 | a output argument [b_] | simple.cpp:29:10:29:10 | f indirection [b_] |
-| simple.cpp:28:10:28:10 | f indirection [a_] | simple.cpp:18:9:18:9 | *#this [a_] |
-| simple.cpp:28:10:28:10 | f indirection [a_] | simple.cpp:28:12:28:12 | call to a |
-| simple.cpp:28:10:28:10 | f indirection [b_] | simple.cpp:18:9:18:9 | *#this [b_] |
-| simple.cpp:28:10:28:10 | f indirection [b_] | simple.cpp:28:10:28:10 | a output argument [b_] |
-| simple.cpp:29:10:29:10 | f indirection [b_] | simple.cpp:19:9:19:9 | *#this [b_] |
-| simple.cpp:29:10:29:10 | f indirection [b_] | simple.cpp:29:12:29:12 | call to b |
-| simple.cpp:39:5:39:5 | setA output argument [a_] | simple.cpp:45:9:45:9 | f indirection [a_] |
+| constructors.cpp:40:5:40:7 | f [a_] | constructors.cpp:26:15:26:15 | f [a_] |
+| constructors.cpp:43:5:43:7 | g [b_] | constructors.cpp:26:15:26:15 | f [b_] |
+| constructors.cpp:46:5:46:7 | h [a_] | constructors.cpp:26:15:26:15 | f [a_] |
+| constructors.cpp:46:5:46:7 | h [b_] | constructors.cpp:26:15:26:15 | f [b_] |
+| qualifiers.cpp:9:21:9:25 | value | qualifiers.cpp:9:36:9:36 | a [post update] |
+| qualifiers.cpp:9:36:9:36 | a [post update] | qualifiers.cpp:9:30:9:33 | this [post update] [a] |
+| qualifiers.cpp:12:40:12:44 | value | qualifiers.cpp:12:56:12:56 | a [post update] |
+| qualifiers.cpp:12:56:12:56 | a [post update] | qualifiers.cpp:12:49:12:53 | inner [post update] [a] |
+| qualifiers.cpp:13:42:13:46 | value | qualifiers.cpp:13:57:13:57 | a [post update] |
+| qualifiers.cpp:13:51:13:55 | (reference dereference) [post update] [a] | qualifiers.cpp:13:51:13:55 | inner [post update] [a] |
+| qualifiers.cpp:13:57:13:57 | a [post update] | qualifiers.cpp:13:51:13:55 | (reference dereference) [post update] [a] |
+| qualifiers.cpp:22:11:22:18 | call to getInner [post update] [a] | qualifiers.cpp:22:11:22:18 | outer [post update] [inner, a] |
+| qualifiers.cpp:22:11:22:18 | outer [post update] [inner, a] | qualifiers.cpp:23:10:23:14 | outer [read] [inner, a] |
+| qualifiers.cpp:22:23:22:23 | a [post update] | qualifiers.cpp:22:11:22:18 | call to getInner [post update] [a] |
+| qualifiers.cpp:22:27:22:36 | call to user_input | qualifiers.cpp:22:23:22:23 | a [post update] |
+| qualifiers.cpp:23:10:23:14 | outer [read] [inner, a] | qualifiers.cpp:23:16:23:20 | FieldAddress [read] [a] |
+| qualifiers.cpp:23:16:23:20 | FieldAddress [read] [a] | qualifiers.cpp:23:16:23:20 | inner [read] [a] |
+| qualifiers.cpp:23:16:23:20 | inner [read] [a] | qualifiers.cpp:23:23:23:23 | FieldAddress [read] |
+| qualifiers.cpp:23:23:23:23 | FieldAddress [read] | qualifiers.cpp:23:23:23:23 | a |
+| qualifiers.cpp:27:11:27:18 | call to getInner [post update] [a] | qualifiers.cpp:27:11:27:18 | outer [post update] [inner, a] |
+| qualifiers.cpp:27:11:27:18 | outer [post update] [inner, a] | qualifiers.cpp:28:10:28:14 | outer [read] [inner, a] |
+| qualifiers.cpp:27:23:27:26 | call to getInner [post update] [a] | qualifiers.cpp:27:11:27:18 | call to getInner [post update] [a] |
+| qualifiers.cpp:27:23:27:26 | call to user_input | qualifiers.cpp:9:21:9:25 | value |
+| qualifiers.cpp:27:23:27:26 | call to user_input | qualifiers.cpp:27:23:27:26 | call to getInner [post update] [a] |
+| qualifiers.cpp:27:28:27:37 | call to user_input | qualifiers.cpp:27:23:27:26 | call to user_input |
+| qualifiers.cpp:28:10:28:14 | outer [read] [inner, a] | qualifiers.cpp:28:16:28:20 | FieldAddress [read] [a] |
+| qualifiers.cpp:28:16:28:20 | FieldAddress [read] [a] | qualifiers.cpp:28:16:28:20 | inner [read] [a] |
+| qualifiers.cpp:28:16:28:20 | inner [read] [a] | qualifiers.cpp:28:23:28:23 | FieldAddress [read] |
+| qualifiers.cpp:28:23:28:23 | FieldAddress [read] | qualifiers.cpp:28:23:28:23 | a |
+| qualifiers.cpp:32:5:32:15 | call to getInner [post update] [a] | qualifiers.cpp:32:23:32:30 | call to getInner [post update] [a] |
+| qualifiers.cpp:32:5:32:15 | call to user_input | qualifiers.cpp:12:40:12:44 | value |
+| qualifiers.cpp:32:5:32:15 | call to user_input | qualifiers.cpp:32:5:32:15 | call to getInner [post update] [a] |
+| qualifiers.cpp:32:23:32:30 | call to getInner [post update] [a] | qualifiers.cpp:32:23:32:30 | outer [post update] [inner, a] |
+| qualifiers.cpp:32:23:32:30 | outer [post update] [inner, a] | qualifiers.cpp:33:10:33:14 | outer [read] [inner, a] |
+| qualifiers.cpp:32:35:32:44 | call to user_input | qualifiers.cpp:32:5:32:15 | call to user_input |
+| qualifiers.cpp:33:10:33:14 | outer [read] [inner, a] | qualifiers.cpp:33:16:33:20 | FieldAddress [read] [a] |
+| qualifiers.cpp:33:16:33:20 | FieldAddress [read] [a] | qualifiers.cpp:33:16:33:20 | inner [read] [a] |
+| qualifiers.cpp:33:16:33:20 | inner [read] [a] | qualifiers.cpp:33:23:33:23 | FieldAddress [read] |
+| qualifiers.cpp:33:23:33:23 | FieldAddress [read] | qualifiers.cpp:33:23:33:23 | a |
+| qualifiers.cpp:37:5:37:17 | * ... [post update] [a] | qualifiers.cpp:37:26:37:33 | call to getInner [post update] [a] |
+| qualifiers.cpp:37:5:37:17 | call to user_input | qualifiers.cpp:13:42:13:46 | value |
+| qualifiers.cpp:37:5:37:17 | call to user_input | qualifiers.cpp:37:5:37:17 | * ... [post update] [a] |
+| qualifiers.cpp:37:26:37:33 | call to getInner [post update] [a] | qualifiers.cpp:37:26:37:33 | outer [post update] [inner, a] |
+| qualifiers.cpp:37:26:37:33 | outer [post update] [inner, a] | qualifiers.cpp:38:10:38:14 | outer [read] [inner, a] |
+| qualifiers.cpp:37:38:37:47 | call to user_input | qualifiers.cpp:37:5:37:17 | call to user_input |
+| qualifiers.cpp:38:10:38:14 | outer [read] [inner, a] | qualifiers.cpp:38:16:38:20 | FieldAddress [read] [a] |
+| qualifiers.cpp:38:16:38:20 | FieldAddress [read] [a] | qualifiers.cpp:38:16:38:20 | inner [read] [a] |
+| qualifiers.cpp:38:16:38:20 | inner [read] [a] | qualifiers.cpp:38:23:38:23 | FieldAddress [read] |
+| qualifiers.cpp:38:23:38:23 | FieldAddress [read] | qualifiers.cpp:38:23:38:23 | a |
+| qualifiers.cpp:42:6:42:22 | * ... [post update] [a] | qualifiers.cpp:42:13:42:20 | call to getInner [post update] [a] |
+| qualifiers.cpp:42:13:42:20 | call to getInner [post update] [a] | qualifiers.cpp:42:13:42:20 | outer [post update] [inner, a] |
+| qualifiers.cpp:42:13:42:20 | outer [post update] [inner, a] | qualifiers.cpp:43:10:43:14 | outer [read] [inner, a] |
+| qualifiers.cpp:42:25:42:25 | a [post update] | qualifiers.cpp:42:6:42:22 | * ... [post update] [a] |
+| qualifiers.cpp:42:29:42:38 | call to user_input | qualifiers.cpp:42:25:42:25 | a [post update] |
+| qualifiers.cpp:43:10:43:14 | outer [read] [inner, a] | qualifiers.cpp:43:16:43:20 | FieldAddress [read] [a] |
+| qualifiers.cpp:43:16:43:20 | FieldAddress [read] [a] | qualifiers.cpp:43:16:43:20 | inner [read] [a] |
+| qualifiers.cpp:43:16:43:20 | inner [read] [a] | qualifiers.cpp:43:23:43:23 | FieldAddress [read] |
+| qualifiers.cpp:43:23:43:23 | FieldAddress [read] | qualifiers.cpp:43:23:43:23 | a |
+| qualifiers.cpp:47:15:47:22 | & ... [post update] [inner, a] | qualifiers.cpp:48:10:48:14 | outer [read] [inner, a] |
+| qualifiers.cpp:47:15:47:22 | call to getInner [post update] [a] | qualifiers.cpp:47:15:47:22 | & ... [post update] [inner, a] |
+| qualifiers.cpp:47:27:47:27 | a [post update] | qualifiers.cpp:47:15:47:22 | call to getInner [post update] [a] |
+| qualifiers.cpp:47:31:47:40 | call to user_input | qualifiers.cpp:47:27:47:27 | a [post update] |
+| qualifiers.cpp:48:10:48:14 | outer [read] [inner, a] | qualifiers.cpp:48:16:48:20 | FieldAddress [read] [a] |
+| qualifiers.cpp:48:16:48:20 | FieldAddress [read] [a] | qualifiers.cpp:48:16:48:20 | inner [read] [a] |
+| qualifiers.cpp:48:16:48:20 | inner [read] [a] | qualifiers.cpp:48:23:48:23 | FieldAddress [read] |
+| qualifiers.cpp:48:23:48:23 | FieldAddress [read] | qualifiers.cpp:48:23:48:23 | a |
+| realistic.cpp:53:9:53:11 | foo [post update] [bar, baz, userInput, bufferLen] | realistic.cpp:61:21:61:23 | foo [read] [bar, baz, userInput, bufferLen] |
+| realistic.cpp:53:9:53:18 | access to array [post update] [baz, userInput, bufferLen] | realistic.cpp:53:13:53:15 | bar [post update] [baz, userInput, bufferLen] |
+| realistic.cpp:53:13:53:15 | bar [post update] [baz, userInput, bufferLen] | realistic.cpp:53:9:53:11 | foo [post update] [bar, baz, userInput, bufferLen] |
+| realistic.cpp:53:20:53:22 | FieldAddress [post update] [userInput, bufferLen] | realistic.cpp:53:9:53:18 | access to array [post update] [baz, userInput, bufferLen] |
+| realistic.cpp:53:20:53:22 | baz [post update] [userInput, bufferLen] | realistic.cpp:53:20:53:22 | FieldAddress [post update] [userInput, bufferLen] |
+| realistic.cpp:53:25:53:33 | userInput [post update] [bufferLen] | realistic.cpp:53:20:53:22 | baz [post update] [userInput, bufferLen] |
+| realistic.cpp:53:35:53:43 | bufferLen [post update] | realistic.cpp:53:25:53:33 | userInput [post update] [bufferLen] |
+| realistic.cpp:53:47:53:66 | (size_t)... | realistic.cpp:53:35:53:43 | bufferLen [post update] |
+| realistic.cpp:53:55:53:64 | call to user_input | realistic.cpp:53:35:53:43 | bufferLen [post update] |
+| realistic.cpp:61:21:61:23 | foo [read] [bar, baz, userInput, bufferLen] | realistic.cpp:61:25:61:27 | bar [read] [baz, userInput, bufferLen] |
+| realistic.cpp:61:21:61:30 | access to array [read] [baz, userInput, bufferLen] | realistic.cpp:61:32:61:34 | FieldAddress [read] [userInput, bufferLen] |
+| realistic.cpp:61:25:61:27 | bar [read] [baz, userInput, bufferLen] | realistic.cpp:61:21:61:30 | access to array [read] [baz, userInput, bufferLen] |
+| realistic.cpp:61:32:61:34 | FieldAddress [read] [userInput, bufferLen] | realistic.cpp:61:32:61:34 | baz [read] [userInput, bufferLen] |
+| realistic.cpp:61:32:61:34 | baz [read] [userInput, bufferLen] | realistic.cpp:61:37:61:45 | userInput [read] [bufferLen] |
+| realistic.cpp:61:37:61:45 | userInput [read] [bufferLen] | realistic.cpp:61:47:61:55 | FieldAddress [read] |
+| realistic.cpp:61:47:61:55 | FieldAddress [read] | realistic.cpp:61:14:61:55 | (void *)... |
+| simple.cpp:18:9:18:9 | this [a_] | simple.cpp:18:22:18:23 | this [read] [a_] |
+| simple.cpp:18:22:18:23 | FieldAddress [read] | simple.cpp:18:9:18:9 | ReturnValue |
+| simple.cpp:18:22:18:23 | this [read] [a_] | simple.cpp:18:22:18:23 | FieldAddress [read] |
+| simple.cpp:19:9:19:9 | this [b_] | simple.cpp:19:22:19:23 | this [read] [b_] |
+| simple.cpp:19:22:19:23 | FieldAddress [read] | simple.cpp:19:9:19:9 | ReturnValue |
+| simple.cpp:19:22:19:23 | this [read] [b_] | simple.cpp:19:22:19:23 | FieldAddress [read] |
+| simple.cpp:20:19:20:19 | a | simple.cpp:20:24:20:25 | a_ [post update] |
+| simple.cpp:20:24:20:25 | a_ [post update] | simple.cpp:20:24:20:25 | this [post update] [a_] |
+| simple.cpp:21:10:21:13 | *#this [a_] | simple.cpp:21:10:21:13 | ReturnIndirection [a_] |
+| simple.cpp:21:19:21:19 | b | simple.cpp:21:24:21:25 | b_ [post update] |
+| simple.cpp:21:24:21:25 | b_ [post update] | simple.cpp:21:24:21:25 | this [post update] [b_] |
+| simple.cpp:26:15:26:15 | f [a_] | simple.cpp:28:12:28:12 | f [a_] |
+| simple.cpp:26:15:26:15 | f [b_] | simple.cpp:29:12:29:12 | f [b_] |
+| simple.cpp:28:12:28:12 | f [a_] | simple.cpp:18:9:18:9 | this [a_] |
+| simple.cpp:28:12:28:12 | f [a_] | simple.cpp:28:12:28:12 | call to a |
+| simple.cpp:29:12:29:12 | f [b_] | simple.cpp:19:9:19:9 | this [b_] |
+| simple.cpp:29:12:29:12 | f [b_] | simple.cpp:29:12:29:12 | call to b |
| simple.cpp:39:7:39:10 | call to user_input | simple.cpp:20:19:20:19 | a |
-| simple.cpp:39:7:39:10 | call to user_input | simple.cpp:39:5:39:5 | setA output argument [a_] |
+| simple.cpp:39:7:39:10 | call to user_input | simple.cpp:39:7:39:10 | f [post update] [a_] |
+| simple.cpp:39:7:39:10 | f [post update] [a_] | simple.cpp:45:5:45:7 | f [a_] |
| simple.cpp:39:12:39:21 | call to user_input | simple.cpp:39:7:39:10 | call to user_input |
-| simple.cpp:40:5:40:5 | setB output argument [b_] | simple.cpp:48:9:48:9 | g indirection [b_] |
| simple.cpp:40:7:40:10 | call to user_input | simple.cpp:21:19:21:19 | b |
-| simple.cpp:40:7:40:10 | call to user_input | simple.cpp:40:5:40:5 | setB output argument [b_] |
+| simple.cpp:40:7:40:10 | call to user_input | simple.cpp:40:7:40:10 | g [post update] [b_] |
+| simple.cpp:40:7:40:10 | g [post update] [b_] | simple.cpp:48:5:48:7 | g [b_] |
| simple.cpp:40:12:40:21 | call to user_input | simple.cpp:40:7:40:10 | call to user_input |
-| simple.cpp:41:5:41:5 | setA output argument [a_] | simple.cpp:42:5:42:5 | h indirection [a_] |
-| simple.cpp:41:5:41:5 | setA output argument [a_] | simple.cpp:51:9:51:9 | h indirection [a_] |
| simple.cpp:41:7:41:10 | call to user_input | simple.cpp:20:19:20:19 | a |
-| simple.cpp:41:7:41:10 | call to user_input | simple.cpp:41:5:41:5 | setA output argument [a_] |
+| simple.cpp:41:7:41:10 | call to user_input | simple.cpp:41:7:41:10 | h [post update] [a_] |
+| simple.cpp:41:7:41:10 | h [post update] [a_] | simple.cpp:42:5:42:5 | h indirection [a_] |
+| simple.cpp:41:7:41:10 | h [post update] [a_] | simple.cpp:51:5:51:7 | h [a_] |
| simple.cpp:41:12:41:21 | call to user_input | simple.cpp:41:7:41:10 | call to user_input |
| simple.cpp:42:5:42:5 | h indirection [a_] | simple.cpp:21:10:21:13 | *#this [a_] |
| simple.cpp:42:5:42:5 | h indirection [a_] | simple.cpp:42:5:42:5 | setB output argument [a_] |
-| simple.cpp:42:5:42:5 | setB output argument [a_] | simple.cpp:51:9:51:9 | h indirection [a_] |
-| simple.cpp:42:5:42:5 | setB output argument [b_] | simple.cpp:51:9:51:9 | h indirection [b_] |
+| simple.cpp:42:5:42:5 | setB output argument [a_] | simple.cpp:51:5:51:7 | h [a_] |
| simple.cpp:42:7:42:10 | call to user_input | simple.cpp:21:19:21:19 | b |
-| simple.cpp:42:7:42:10 | call to user_input | simple.cpp:42:5:42:5 | setB output argument [b_] |
+| simple.cpp:42:7:42:10 | call to user_input | simple.cpp:42:7:42:10 | h [post update] [b_] |
+| simple.cpp:42:7:42:10 | h [post update] [b_] | simple.cpp:51:5:51:7 | h [b_] |
| simple.cpp:42:12:42:21 | call to user_input | simple.cpp:42:7:42:10 | call to user_input |
-| simple.cpp:45:9:45:9 | f indirection [a_] | simple.cpp:26:15:26:15 | *f [a_] |
-| simple.cpp:48:9:48:9 | g indirection [b_] | simple.cpp:26:15:26:15 | *f [b_] |
-| simple.cpp:51:9:51:9 | h indirection [a_] | simple.cpp:26:15:26:15 | *f [a_] |
-| simple.cpp:51:9:51:9 | h indirection [b_] | simple.cpp:26:15:26:15 | *f [b_] |
-| simple.cpp:65:5:65:22 | Store [i] | simple.cpp:66:12:66:12 | Store [i] |
-| simple.cpp:65:11:65:20 | call to user_input | simple.cpp:65:5:65:22 | Store [i] |
-| simple.cpp:66:12:66:12 | Store [i] | simple.cpp:67:13:67:13 | i |
-| simple.cpp:78:9:78:15 | *#this [f1] | simple.cpp:79:19:79:20 | Store |
-| simple.cpp:83:9:83:28 | Store [f1] | simple.cpp:84:14:84:20 | this indirection [f1] |
-| simple.cpp:83:17:83:26 | call to user_input | simple.cpp:83:9:83:28 | Store [f1] |
-| simple.cpp:84:14:84:20 | this indirection [f1] | simple.cpp:78:9:78:15 | *#this [f1] |
-| simple.cpp:84:14:84:20 | this indirection [f1] | simple.cpp:84:14:84:20 | call to getf2f1 |
-| simple.cpp:92:5:92:22 | Store [i] | simple.cpp:93:20:93:20 | Store [i] |
-| simple.cpp:92:11:92:20 | call to user_input | simple.cpp:92:5:92:22 | Store [i] |
-| simple.cpp:93:20:93:20 | Store [i] | simple.cpp:94:13:94:13 | i |
-| struct_init.c:14:24:14:25 | *ab [a] | struct_init.c:15:12:15:12 | a |
-| struct_init.c:20:20:20:29 | Chi [a] | struct_init.c:24:10:24:12 | & ... indirection [a] |
-| struct_init.c:20:20:20:29 | call to user_input | struct_init.c:20:20:20:29 | Chi [a] |
-| struct_init.c:20:20:20:29 | call to user_input | struct_init.c:22:11:22:11 | a |
+| simple.cpp:45:5:45:7 | f [a_] | simple.cpp:26:15:26:15 | f [a_] |
+| simple.cpp:48:5:48:7 | g [b_] | simple.cpp:26:15:26:15 | f [b_] |
+| simple.cpp:51:5:51:7 | h [a_] | simple.cpp:26:15:26:15 | f [a_] |
+| simple.cpp:51:5:51:7 | h [b_] | simple.cpp:26:15:26:15 | f [b_] |
+| simple.cpp:65:5:65:5 | a [post update] [i] | simple.cpp:67:10:67:11 | a2 [read] [i] |
+| simple.cpp:65:7:65:7 | i [post update] | simple.cpp:65:5:65:5 | a [post update] [i] |
+| simple.cpp:65:11:65:20 | call to user_input | simple.cpp:65:7:65:7 | i [post update] |
+| simple.cpp:67:10:67:11 | a2 [read] [i] | simple.cpp:67:13:67:13 | FieldAddress [read] |
+| simple.cpp:67:13:67:13 | FieldAddress [read] | simple.cpp:67:13:67:13 | i |
+| simple.cpp:78:9:78:15 | this [f2, f1] | simple.cpp:79:16:79:17 | this [read] [f2, f1] |
+| simple.cpp:79:16:79:17 | f2 [read] [f1] | simple.cpp:79:19:79:20 | FieldAddress [read] |
+| simple.cpp:79:16:79:17 | this [read] [f2, f1] | simple.cpp:79:16:79:17 | f2 [read] [f1] |
+| simple.cpp:79:19:79:20 | FieldAddress [read] | simple.cpp:78:9:78:15 | ReturnValue |
+| simple.cpp:83:9:83:10 | f2 [post update] [f1] | simple.cpp:83:9:83:10 | this [post update] [f2, f1] |
+| simple.cpp:83:9:83:10 | this [post update] [f2, f1] | simple.cpp:84:14:84:20 | this [f2, f1] |
+| simple.cpp:83:12:83:13 | f1 [post update] | simple.cpp:83:9:83:10 | f2 [post update] [f1] |
+| simple.cpp:83:17:83:26 | call to user_input | simple.cpp:83:12:83:13 | f1 [post update] |
+| simple.cpp:84:14:84:20 | this [f2, f1] | simple.cpp:78:9:78:15 | this [f2, f1] |
+| simple.cpp:84:14:84:20 | this [f2, f1] | simple.cpp:84:14:84:20 | call to getf2f1 |
+| simple.cpp:92:5:92:5 | a [post update] [i] | simple.cpp:94:10:94:11 | a2 [read] [i] |
+| simple.cpp:92:7:92:7 | i [post update] | simple.cpp:92:5:92:5 | a [post update] [i] |
+| simple.cpp:92:11:92:20 | call to user_input | simple.cpp:92:7:92:7 | i [post update] |
+| simple.cpp:94:10:94:11 | a2 [read] [i] | simple.cpp:94:13:94:13 | FieldAddress [read] |
+| simple.cpp:94:13:94:13 | FieldAddress [read] | simple.cpp:94:13:94:13 | i |
+| struct_init.c:14:24:14:25 | *ab [a] | struct_init.c:15:8:15:9 | ab [read] [a] |
+| struct_init.c:14:24:14:25 | ab [a] | struct_init.c:15:8:15:9 | ab [read] [a] |
+| struct_init.c:15:8:15:9 | ab [read] [a] | struct_init.c:15:12:15:12 | FieldAddress [read] |
+| struct_init.c:15:12:15:12 | FieldAddress [read] | struct_init.c:15:12:15:12 | a |
+| struct_init.c:20:13:20:14 | VariableAddress [post update] [a] | struct_init.c:22:8:22:9 | ab [read] [a] |
+| struct_init.c:20:13:20:14 | VariableAddress [post update] [a] | struct_init.c:24:10:24:12 | & ... indirection [a] |
+| struct_init.c:20:17:20:36 | FieldAddress [post update] | struct_init.c:20:13:20:14 | VariableAddress [post update] [a] |
+| struct_init.c:20:20:20:29 | call to user_input | struct_init.c:20:17:20:36 | FieldAddress [post update] |
+| struct_init.c:22:8:22:9 | ab [read] [a] | struct_init.c:22:11:22:11 | FieldAddress [read] |
+| struct_init.c:22:11:22:11 | FieldAddress [read] | struct_init.c:22:11:22:11 | a |
| struct_init.c:24:10:24:12 | & ... indirection [a] | struct_init.c:14:24:14:25 | *ab [a] |
-| struct_init.c:27:7:27:16 | Chi [a] | struct_init.c:36:10:36:24 | & ... indirection [a] |
-| struct_init.c:27:7:27:16 | call to user_input | struct_init.c:27:7:27:16 | Chi [a] |
-| struct_init.c:27:7:27:16 | call to user_input | struct_init.c:31:23:31:23 | a |
-| struct_init.c:36:10:36:24 | & ... indirection [a] | struct_init.c:14:24:14:25 | *ab [a] |
+| struct_init.c:26:16:26:20 | VariableAddress [post update] [nestedAB, a] | struct_init.c:31:8:31:12 | outer [read] [nestedAB, a] |
+| struct_init.c:26:16:26:20 | VariableAddress [post update] [nestedAB, a] | struct_init.c:36:11:36:15 | outer [read] [nestedAB, a] |
+| struct_init.c:26:23:29:3 | FieldAddress [post update] [a] | struct_init.c:26:16:26:20 | VariableAddress [post update] [nestedAB, a] |
+| struct_init.c:27:5:27:23 | FieldAddress [post update] | struct_init.c:26:23:29:3 | FieldAddress [post update] [a] |
+| struct_init.c:27:7:27:16 | call to user_input | struct_init.c:27:5:27:23 | FieldAddress [post update] |
+| struct_init.c:31:8:31:12 | outer [read] [nestedAB, a] | struct_init.c:31:14:31:21 | nestedAB [read] [a] |
+| struct_init.c:31:14:31:21 | nestedAB [read] [a] | struct_init.c:31:23:31:23 | FieldAddress [read] |
+| struct_init.c:31:23:31:23 | FieldAddress [read] | struct_init.c:31:23:31:23 | a |
+| struct_init.c:36:3:36:8 | & ... [a] | struct_init.c:14:24:14:25 | ab [a] |
+| struct_init.c:36:11:36:15 | outer [read] [nestedAB, a] | struct_init.c:36:17:36:24 | nestedAB [read] [a] |
+| struct_init.c:36:17:36:24 | nestedAB [read] [a] | struct_init.c:36:3:36:8 | & ... [a] |
nodes
| A.cpp:23:10:23:10 | c | semmle.label | c |
-| A.cpp:25:7:25:17 | Chi [c] | semmle.label | Chi [c] |
+| A.cpp:25:7:25:10 | this [post update] [c] | semmle.label | this [post update] [c] |
+| A.cpp:25:13:25:13 | c [post update] | semmle.label | c [post update] |
| A.cpp:27:17:27:17 | c | semmle.label | c |
-| A.cpp:27:22:27:32 | Chi [c] | semmle.label | Chi [c] |
-| A.cpp:28:8:28:10 | *#this [c] | semmle.label | *#this [c] |
-| A.cpp:28:29:28:29 | Store | semmle.label | Store |
-| A.cpp:55:5:55:5 | set output argument [c] | semmle.label | set output argument [c] |
+| A.cpp:27:22:27:25 | this [post update] [c] | semmle.label | this [post update] [c] |
+| A.cpp:27:28:27:28 | c [post update] | semmle.label | c [post update] |
+| A.cpp:28:8:28:10 | ReturnValue | semmle.label | ReturnValue |
+| A.cpp:28:8:28:10 | this [c] | semmle.label | this [c] |
+| A.cpp:28:23:28:26 | this [read] [c] | semmle.label | this [read] [c] |
+| A.cpp:28:29:28:29 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| A.cpp:29:15:29:18 | ReturnValue [c] | semmle.label | ReturnValue [c] |
+| A.cpp:29:23:29:23 | c | semmle.label | c |
+| A.cpp:31:14:31:21 | c | semmle.label | c |
+| A.cpp:31:14:31:21 | new [post update] [c] | semmle.label | new [post update] [c] |
+| A.cpp:47:12:47:18 | new | semmle.label | new |
+| A.cpp:48:12:48:18 | c | semmle.label | c |
+| A.cpp:48:12:48:18 | call to make [c] | semmle.label | call to make [c] |
+| A.cpp:49:10:49:10 | b [read] [c] | semmle.label | b [read] [c] |
+| A.cpp:49:10:49:13 | (void *)... | semmle.label | (void *)... |
+| A.cpp:49:13:49:13 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| A.cpp:55:8:55:10 | b [post update] [c] | semmle.label | b [post update] [c] |
| A.cpp:55:8:55:10 | new | semmle.label | new |
| A.cpp:55:12:55:19 | (C *)... | semmle.label | (C *)... |
| A.cpp:55:12:55:19 | new | semmle.label | new |
-| A.cpp:56:10:56:10 | b indirection [c] | semmle.label | b indirection [c] |
+| A.cpp:56:10:56:17 | (void *)... | semmle.label | (void *)... |
+| A.cpp:56:13:56:15 | b [c] | semmle.label | b [c] |
| A.cpp:56:13:56:15 | call to get | semmle.label | call to get |
-| A.cpp:57:10:57:25 | new indirection [c] | semmle.label | new indirection [c] |
-| A.cpp:57:11:57:24 | B output argument [c] | semmle.label | B output argument [c] |
+| A.cpp:56:13:56:15 | call to get | semmle.label | call to get |
+| A.cpp:57:10:57:32 | (void *)... | semmle.label | (void *)... |
| A.cpp:57:11:57:24 | new | semmle.label | new |
+| A.cpp:57:11:57:24 | new [post update] [c] | semmle.label | new [post update] [c] |
| A.cpp:57:17:57:23 | new | semmle.label | new |
| A.cpp:57:28:57:30 | call to get | semmle.label | call to get |
+| A.cpp:57:28:57:30 | call to get | semmle.label | call to get |
+| A.cpp:57:28:57:30 | new [c] | semmle.label | new [c] |
+| A.cpp:64:10:64:15 | call to setOnB [c] | semmle.label | call to setOnB [c] |
+| A.cpp:64:10:64:15 | new | semmle.label | new |
+| A.cpp:64:21:64:28 | (C *)... | semmle.label | (C *)... |
+| A.cpp:64:21:64:28 | new | semmle.label | new |
+| A.cpp:66:10:66:11 | b2 [read] [c] | semmle.label | b2 [read] [c] |
+| A.cpp:66:10:66:14 | (void *)... | semmle.label | (void *)... |
+| A.cpp:66:14:66:14 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| A.cpp:73:10:73:19 | call to setOnBWrap [c] | semmle.label | call to setOnBWrap [c] |
+| A.cpp:73:10:73:19 | new | semmle.label | new |
+| A.cpp:73:25:73:32 | (C *)... | semmle.label | (C *)... |
+| A.cpp:73:25:73:32 | new | semmle.label | new |
+| A.cpp:75:10:75:11 | b2 [read] [c] | semmle.label | b2 [read] [c] |
+| A.cpp:75:10:75:14 | (void *)... | semmle.label | (void *)... |
+| A.cpp:75:14:75:14 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| A.cpp:78:6:78:15 | ReturnValue [c] | semmle.label | ReturnValue [c] |
+| A.cpp:78:27:78:27 | c | semmle.label | c |
+| A.cpp:81:10:81:15 | c | semmle.label | c |
+| A.cpp:81:10:81:15 | call to setOnB [c] | semmle.label | call to setOnB [c] |
+| A.cpp:85:9:85:14 | ReturnValue [c] | semmle.label | ReturnValue [c] |
+| A.cpp:85:26:85:26 | c | semmle.label | c |
+| A.cpp:90:11:90:13 | b2 [post update] [c] | semmle.label | b2 [post update] [c] |
+| A.cpp:90:11:90:13 | c | semmle.label | c |
| A.cpp:98:12:98:18 | new | semmle.label | new |
-| A.cpp:100:5:100:13 | Chi [a] | semmle.label | Chi [a] |
-| A.cpp:101:8:101:9 | c1 indirection [a] | semmle.label | c1 indirection [a] |
-| A.cpp:103:14:103:14 | *c [a] | semmle.label | *c [a] |
-| A.cpp:107:16:107:16 | a | semmle.label | a |
-| A.cpp:126:5:126:5 | Chi [c] | semmle.label | Chi [c] |
-| A.cpp:126:5:126:5 | set output argument [c] | semmle.label | set output argument [c] |
+| A.cpp:100:5:100:6 | c1 [post update] [a] | semmle.label | c1 [post update] [a] |
+| A.cpp:100:9:100:9 | a [post update] | semmle.label | a [post update] |
+| A.cpp:101:5:101:6 | c1 [a] | semmle.label | c1 [a] |
+| A.cpp:103:14:103:14 | c [a] | semmle.label | c [a] |
+| A.cpp:107:12:107:13 | c1 [read] [a] | semmle.label | c1 [read] [a] |
+| A.cpp:107:12:107:16 | (void *)... | semmle.label | (void *)... |
+| A.cpp:107:16:107:16 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| A.cpp:120:12:120:13 | c1 [read] [a] | semmle.label | c1 [read] [a] |
+| A.cpp:120:12:120:16 | (void *)... | semmle.label | (void *)... |
+| A.cpp:120:16:120:16 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| A.cpp:126:5:126:5 | b [post update] [c] | semmle.label | b [post update] [c] |
+| A.cpp:126:8:126:10 | b [post update] [c] | semmle.label | b [post update] [c] |
| A.cpp:126:8:126:10 | new | semmle.label | new |
| A.cpp:126:12:126:18 | new | semmle.label | new |
-| A.cpp:131:8:131:8 | Chi [c] | semmle.label | Chi [c] |
-| A.cpp:131:8:131:8 | f7 output argument [c] | semmle.label | f7 output argument [c] |
-| A.cpp:132:13:132:13 | c | semmle.label | c |
+| A.cpp:131:5:131:6 | b [post update] [c] | semmle.label | b [post update] [c] |
+| A.cpp:132:10:132:10 | b [read] [c] | semmle.label | b [read] [c] |
+| A.cpp:132:10:132:13 | (void *)... | semmle.label | (void *)... |
+| A.cpp:132:13:132:13 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| A.cpp:140:13:140:13 | b | semmle.label | b |
-| A.cpp:142:7:142:20 | Chi [c] | semmle.label | Chi [c] |
+| A.cpp:142:7:142:7 | b [post update] [c] | semmle.label | b [post update] [c] |
+| A.cpp:142:10:142:10 | c [post update] | semmle.label | c [post update] |
| A.cpp:142:14:142:20 | new | semmle.label | new |
-| A.cpp:143:7:143:31 | Chi [b] | semmle.label | Chi [b] |
-| A.cpp:143:7:143:31 | Chi [b] | semmle.label | Chi [b] |
+| A.cpp:143:7:143:10 | this [post update] [b, c] | semmle.label | this [post update] [b, c] |
+| A.cpp:143:7:143:10 | this [post update] [b] | semmle.label | this [post update] [b] |
+| A.cpp:143:7:143:10 | this [post update] [b] | semmle.label | this [post update] [b] |
+| A.cpp:143:13:143:13 | b [post update] | semmle.label | b [post update] |
+| A.cpp:143:13:143:13 | b [post update] | semmle.label | b [post update] |
+| A.cpp:143:13:143:13 | b [post update] [c] | semmle.label | b [post update] [c] |
| A.cpp:143:25:143:31 | new | semmle.label | new |
| A.cpp:150:12:150:18 | new | semmle.label | new |
-| A.cpp:151:12:151:24 | Chi [b] | semmle.label | Chi [b] |
-| A.cpp:151:12:151:24 | D output argument [b] | semmle.label | D output argument [b] |
| A.cpp:151:12:151:24 | b | semmle.label | b |
-| A.cpp:151:18:151:18 | Chi [c] | semmle.label | Chi [c] |
-| A.cpp:151:18:151:18 | D output argument [c] | semmle.label | D output argument [c] |
-| A.cpp:152:13:152:13 | b | semmle.label | b |
-| A.cpp:154:13:154:13 | c | semmle.label | c |
-| C.cpp:18:12:18:18 | C output argument [s1] | semmle.label | C output argument [s1] |
-| C.cpp:18:12:18:18 | C output argument [s3] | semmle.label | C output argument [s3] |
-| C.cpp:19:5:19:5 | c indirection [s1] | semmle.label | c indirection [s1] |
-| C.cpp:19:5:19:5 | c indirection [s3] | semmle.label | c indirection [s3] |
-| C.cpp:22:12:22:21 | Chi [s1] | semmle.label | Chi [s1] |
+| A.cpp:151:12:151:24 | b [post update] [c] | semmle.label | b [post update] [c] |
+| A.cpp:151:12:151:24 | new [post update] [b, c] | semmle.label | new [post update] [b, c] |
+| A.cpp:151:12:151:24 | new [post update] [b] | semmle.label | new [post update] [b] |
+| A.cpp:152:10:152:10 | d [read] [b] | semmle.label | d [read] [b] |
+| A.cpp:152:10:152:13 | (void *)... | semmle.label | (void *)... |
+| A.cpp:152:13:152:13 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| A.cpp:153:10:153:10 | d [read] [b, c] | semmle.label | d [read] [b, c] |
+| A.cpp:153:10:153:16 | (void *)... | semmle.label | (void *)... |
+| A.cpp:153:13:153:13 | FieldAddress [read] [c] | semmle.label | FieldAddress [read] [c] |
+| A.cpp:153:13:153:13 | b [read] [c] | semmle.label | b [read] [c] |
+| A.cpp:153:16:153:16 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| A.cpp:154:10:154:10 | b [read] [c] | semmle.label | b [read] [c] |
+| A.cpp:154:10:154:13 | (void *)... | semmle.label | (void *)... |
+| A.cpp:154:13:154:13 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| A.cpp:159:12:159:18 | new | semmle.label | new |
+| A.cpp:160:18:160:60 | b | semmle.label | b |
+| A.cpp:160:18:160:60 | new [post update] [head] | semmle.label | new [post update] [head] |
+| A.cpp:161:18:161:40 | l1 [head] | semmle.label | l1 [head] |
+| A.cpp:161:18:161:40 | new [post update] [next, head] | semmle.label | new [post update] [next, head] |
+| A.cpp:162:18:162:40 | l2 [next, head] | semmle.label | l2 [next, head] |
+| A.cpp:162:18:162:40 | new [post update] [next, next, head] | semmle.label | new [post update] [next, next, head] |
+| A.cpp:165:10:165:11 | l3 [read] [next, next, head] | semmle.label | l3 [read] [next, next, head] |
+| A.cpp:165:10:165:29 | (void *)... | semmle.label | (void *)... |
+| A.cpp:165:14:165:17 | FieldAddress [read] [next, head] | semmle.label | FieldAddress [read] [next, head] |
+| A.cpp:165:14:165:17 | next [read] [next, head] | semmle.label | next [read] [next, head] |
+| A.cpp:165:20:165:23 | FieldAddress [read] [head] | semmle.label | FieldAddress [read] [head] |
+| A.cpp:165:20:165:23 | next [read] [head] | semmle.label | next [read] [head] |
+| A.cpp:165:26:165:29 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| A.cpp:167:44:167:44 | l [read] [next, head] | semmle.label | l [read] [next, head] |
+| A.cpp:167:44:167:44 | l [read] [next, next, head] | semmle.label | l [read] [next, next, head] |
+| A.cpp:167:47:167:50 | FieldAddress [read] [head] | semmle.label | FieldAddress [read] [head] |
+| A.cpp:167:47:167:50 | FieldAddress [read] [next, head] | semmle.label | FieldAddress [read] [next, head] |
+| A.cpp:169:12:169:12 | l [read] [head] | semmle.label | l [read] [head] |
+| A.cpp:169:12:169:18 | (void *)... | semmle.label | (void *)... |
+| A.cpp:169:15:169:18 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| A.cpp:181:15:181:21 | newHead | semmle.label | newHead |
+| A.cpp:181:32:181:35 | next [head] | semmle.label | next [head] |
+| A.cpp:181:32:181:35 | next [next, head] | semmle.label | next [next, head] |
+| A.cpp:183:7:183:10 | head [post update] | semmle.label | head [post update] |
+| A.cpp:183:7:183:10 | this [post update] [head] | semmle.label | this [post update] [head] |
+| A.cpp:184:7:184:10 | this [post update] [next, head] | semmle.label | this [post update] [next, head] |
+| A.cpp:184:7:184:10 | this [post update] [next, next, head] | semmle.label | this [post update] [next, next, head] |
+| A.cpp:184:13:184:16 | next [post update] [head] | semmle.label | next [post update] [head] |
+| A.cpp:184:13:184:16 | next [post update] [next, head] | semmle.label | next [post update] [next, head] |
+| B.cpp:6:15:6:24 | new | semmle.label | new |
+| B.cpp:7:16:7:35 | e | semmle.label | e |
+| B.cpp:7:16:7:35 | new [post update] [elem1] | semmle.label | new [post update] [elem1] |
+| B.cpp:8:16:8:27 | b1 [elem1] | semmle.label | b1 [elem1] |
+| B.cpp:8:16:8:27 | new [post update] [box1, elem1] | semmle.label | new [post update] [box1, elem1] |
+| B.cpp:9:10:9:11 | b2 [read] [box1, elem1] | semmle.label | b2 [read] [box1, elem1] |
+| B.cpp:9:10:9:24 | (void *)... | semmle.label | (void *)... |
+| B.cpp:9:14:9:17 | FieldAddress [read] [elem1] | semmle.label | FieldAddress [read] [elem1] |
+| B.cpp:9:14:9:17 | box1 [read] [elem1] | semmle.label | box1 [read] [elem1] |
+| B.cpp:9:20:9:24 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| B.cpp:15:15:15:27 | new | semmle.label | new |
+| B.cpp:16:16:16:38 | e | semmle.label | e |
+| B.cpp:16:16:16:38 | new [post update] [elem2] | semmle.label | new [post update] [elem2] |
+| B.cpp:17:16:17:27 | b1 [elem2] | semmle.label | b1 [elem2] |
+| B.cpp:17:16:17:27 | new [post update] [box1, elem2] | semmle.label | new [post update] [box1, elem2] |
+| B.cpp:19:10:19:11 | b2 [read] [box1, elem2] | semmle.label | b2 [read] [box1, elem2] |
+| B.cpp:19:10:19:24 | (void *)... | semmle.label | (void *)... |
+| B.cpp:19:14:19:17 | FieldAddress [read] [elem2] | semmle.label | FieldAddress [read] [elem2] |
+| B.cpp:19:14:19:17 | box1 [read] [elem2] | semmle.label | box1 [read] [elem2] |
+| B.cpp:19:20:19:24 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| B.cpp:33:16:33:17 | e1 | semmle.label | e1 |
+| B.cpp:33:26:33:27 | e2 | semmle.label | e2 |
+| B.cpp:35:7:35:10 | this [post update] [elem1] | semmle.label | this [post update] [elem1] |
+| B.cpp:35:13:35:17 | elem1 [post update] | semmle.label | elem1 [post update] |
+| B.cpp:36:7:36:10 | this [post update] [elem2] | semmle.label | this [post update] [elem2] |
+| B.cpp:36:13:36:17 | elem2 [post update] | semmle.label | elem2 [post update] |
+| B.cpp:44:16:44:17 | b1 [elem1] | semmle.label | b1 [elem1] |
+| B.cpp:44:16:44:17 | b1 [elem2] | semmle.label | b1 [elem2] |
+| B.cpp:46:7:46:10 | this [post update] [box1, elem1] | semmle.label | this [post update] [box1, elem1] |
+| B.cpp:46:7:46:10 | this [post update] [box1, elem2] | semmle.label | this [post update] [box1, elem2] |
+| B.cpp:46:13:46:16 | box1 [post update] [elem1] | semmle.label | box1 [post update] [elem1] |
+| B.cpp:46:13:46:16 | box1 [post update] [elem2] | semmle.label | box1 [post update] [elem2] |
+| C.cpp:18:12:18:18 | new [post update] [s1] | semmle.label | new [post update] [s1] |
+| C.cpp:19:8:19:11 | c [s1] | semmle.label | c [s1] |
+| C.cpp:22:3:22:3 | this [post update] [s1] | semmle.label | this [post update] [s1] |
+| C.cpp:22:9:22:22 | FieldAddress [post update] | semmle.label | FieldAddress [post update] |
| C.cpp:22:12:22:21 | new | semmle.label | new |
-| C.cpp:24:5:24:25 | Chi [s1] | semmle.label | Chi [s1] |
-| C.cpp:24:5:24:25 | Chi [s3] | semmle.label | Chi [s3] |
-| C.cpp:24:16:24:25 | new | semmle.label | new |
-| C.cpp:27:8:27:11 | *#this [s1] | semmle.label | *#this [s1] |
-| C.cpp:27:8:27:11 | *#this [s3] | semmle.label | *#this [s3] |
+| C.cpp:27:8:27:11 | this [s1] | semmle.label | this [s1] |
+| C.cpp:29:10:29:11 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| C.cpp:29:10:29:11 | s1 | semmle.label | s1 |
-| C.cpp:31:10:31:11 | s3 | semmle.label | s3 |
-| aliasing.cpp:9:3:9:22 | Chi [m1] | semmle.label | Chi [m1] |
+| C.cpp:29:10:29:11 | this [read] [s1] | semmle.label | this [read] [s1] |
+| D.cpp:10:11:10:17 | ReturnValue | semmle.label | ReturnValue |
+| D.cpp:10:11:10:17 | this [elem] | semmle.label | this [elem] |
+| D.cpp:10:30:10:33 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| D.cpp:10:30:10:33 | this [read] [elem] | semmle.label | this [read] [elem] |
+| D.cpp:11:24:11:24 | e | semmle.label | e |
+| D.cpp:11:29:11:32 | elem [post update] | semmle.label | elem [post update] |
+| D.cpp:11:29:11:32 | this [post update] [elem] | semmle.label | this [post update] [elem] |
+| D.cpp:17:11:17:17 | ReturnValue [elem] | semmle.label | ReturnValue [elem] |
+| D.cpp:17:11:17:17 | this [box, elem] | semmle.label | this [box, elem] |
+| D.cpp:17:30:17:32 | FieldAddress [read] [elem] | semmle.label | FieldAddress [read] [elem] |
+| D.cpp:17:30:17:32 | this [read] [box, elem] | semmle.label | this [read] [box, elem] |
+| D.cpp:21:30:21:31 | b2 [box, elem] | semmle.label | b2 [box, elem] |
+| D.cpp:22:10:22:33 | (void *)... | semmle.label | (void *)... |
+| D.cpp:22:14:22:20 | b2 [box, elem] | semmle.label | b2 [box, elem] |
+| D.cpp:22:14:22:20 | call to getBox1 [elem] | semmle.label | call to getBox1 [elem] |
+| D.cpp:22:25:22:31 | call to getBox1 [elem] | semmle.label | call to getBox1 [elem] |
+| D.cpp:22:25:22:31 | call to getElem | semmle.label | call to getElem |
+| D.cpp:22:25:22:31 | call to getElem | semmle.label | call to getElem |
+| D.cpp:28:15:28:24 | new | semmle.label | new |
+| D.cpp:30:5:30:5 | b [post update] [box, elem] | semmle.label | b [post update] [box, elem] |
+| D.cpp:30:8:30:10 | FieldAddress [post update] [elem] | semmle.label | FieldAddress [post update] [elem] |
+| D.cpp:30:8:30:10 | box [post update] [elem] | semmle.label | box [post update] [elem] |
+| D.cpp:30:13:30:16 | elem [post update] | semmle.label | elem [post update] |
+| D.cpp:31:5:31:12 | b [box, elem] | semmle.label | b [box, elem] |
+| D.cpp:35:15:35:24 | new | semmle.label | new |
+| D.cpp:37:5:37:5 | b [post update] [box, elem] | semmle.label | b [post update] [box, elem] |
+| D.cpp:37:8:37:10 | FieldAddress [post update] [elem] | semmle.label | FieldAddress [post update] [elem] |
+| D.cpp:37:13:37:19 | box [post update] [elem] | semmle.label | box [post update] [elem] |
+| D.cpp:37:13:37:19 | e | semmle.label | e |
+| D.cpp:38:5:38:12 | b [box, elem] | semmle.label | b [box, elem] |
+| D.cpp:42:15:42:24 | new | semmle.label | new |
+| D.cpp:44:8:44:14 | b [post update] [box, elem] | semmle.label | b [post update] [box, elem] |
+| D.cpp:44:8:44:14 | call to getBox1 [post update] [elem] | semmle.label | call to getBox1 [post update] [elem] |
+| D.cpp:44:19:44:22 | elem [post update] | semmle.label | elem [post update] |
+| D.cpp:45:5:45:12 | b [box, elem] | semmle.label | b [box, elem] |
+| D.cpp:49:15:49:24 | new | semmle.label | new |
+| D.cpp:51:8:51:14 | b [post update] [box, elem] | semmle.label | b [post update] [box, elem] |
+| D.cpp:51:8:51:14 | call to getBox1 [post update] [elem] | semmle.label | call to getBox1 [post update] [elem] |
+| D.cpp:51:19:51:25 | call to getBox1 [post update] [elem] | semmle.label | call to getBox1 [post update] [elem] |
+| D.cpp:51:19:51:25 | e | semmle.label | e |
+| D.cpp:52:5:52:12 | b [box, elem] | semmle.label | b [box, elem] |
+| D.cpp:56:15:56:24 | new | semmle.label | new |
+| D.cpp:58:5:58:12 | FieldAddress [post update] [box, elem] | semmle.label | FieldAddress [post update] [box, elem] |
+| D.cpp:58:5:58:12 | boxfield [post update] [box, elem] | semmle.label | boxfield [post update] [box, elem] |
+| D.cpp:58:5:58:12 | this [post update] [boxfield, box, elem] | semmle.label | this [post update] [boxfield, box, elem] |
+| D.cpp:58:15:58:17 | FieldAddress [post update] [elem] | semmle.label | FieldAddress [post update] [elem] |
+| D.cpp:58:15:58:17 | box [post update] [elem] | semmle.label | box [post update] [elem] |
+| D.cpp:58:20:58:23 | elem [post update] | semmle.label | elem [post update] |
+| D.cpp:59:5:59:7 | this [boxfield, box, elem] | semmle.label | this [boxfield, box, elem] |
+| D.cpp:63:8:63:10 | this [boxfield, box, elem] | semmle.label | this [boxfield, box, elem] |
+| D.cpp:64:10:64:17 | FieldAddress [read] [box, elem] | semmle.label | FieldAddress [read] [box, elem] |
+| D.cpp:64:10:64:17 | boxfield [read] [box, elem] | semmle.label | boxfield [read] [box, elem] |
+| D.cpp:64:10:64:17 | this [read] [boxfield, box, elem] | semmle.label | this [read] [boxfield, box, elem] |
+| D.cpp:64:10:64:28 | (void *)... | semmle.label | (void *)... |
+| D.cpp:64:20:64:22 | FieldAddress [read] [elem] | semmle.label | FieldAddress [read] [elem] |
+| D.cpp:64:20:64:22 | box [read] [elem] | semmle.label | box [read] [elem] |
+| D.cpp:64:25:64:28 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| E.cpp:19:27:19:27 | *p [data, buffer] | semmle.label | *p [data, buffer] |
+| E.cpp:21:10:21:10 | p [read] [data, buffer] | semmle.label | p [read] [data, buffer] |
+| E.cpp:21:13:21:16 | data [read] [buffer] | semmle.label | data [read] [buffer] |
+| E.cpp:21:18:21:23 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| E.cpp:21:18:21:23 | buffer | semmle.label | buffer |
+| E.cpp:28:21:28:23 | argument_source output argument | semmle.label | argument_source output argument |
+| E.cpp:29:21:29:21 | b [post update] [buffer] | semmle.label | b [post update] [buffer] |
+| E.cpp:29:21:29:29 | argument_source output argument | semmle.label | argument_source output argument |
+| E.cpp:29:24:29:29 | FieldAddress [post update] | semmle.label | FieldAddress [post update] |
+| E.cpp:30:21:30:21 | p [post update] [data, buffer] | semmle.label | p [post update] [data, buffer] |
+| E.cpp:30:21:30:33 | argument_source output argument | semmle.label | argument_source output argument |
+| E.cpp:30:23:30:26 | data [post update] [buffer] | semmle.label | data [post update] [buffer] |
+| E.cpp:30:28:30:33 | FieldAddress [post update] | semmle.label | FieldAddress [post update] |
+| E.cpp:31:10:31:12 | raw | semmle.label | raw |
+| E.cpp:32:10:32:10 | b [read] [buffer] | semmle.label | b [read] [buffer] |
+| E.cpp:32:13:32:18 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| E.cpp:32:13:32:18 | buffer | semmle.label | buffer |
+| E.cpp:33:18:33:19 | & ... indirection [data, buffer] | semmle.label | & ... indirection [data, buffer] |
+| aliasing.cpp:9:3:9:3 | s [post update] [m1] | semmle.label | s [post update] [m1] |
+| aliasing.cpp:9:6:9:7 | m1 [post update] | semmle.label | m1 [post update] |
| aliasing.cpp:9:11:9:20 | call to user_input | semmle.label | call to user_input |
-| aliasing.cpp:13:3:13:21 | Chi [m1] | semmle.label | Chi [m1] |
+| aliasing.cpp:13:3:13:3 | (reference dereference) [post update] [m1] | semmle.label | (reference dereference) [post update] [m1] |
+| aliasing.cpp:13:3:13:3 | s [post update] [m1] | semmle.label | s [post update] [m1] |
+| aliasing.cpp:13:5:13:6 | m1 [post update] | semmle.label | m1 [post update] |
| aliasing.cpp:13:10:13:19 | call to user_input | semmle.label | call to user_input |
-| aliasing.cpp:25:17:25:19 | Chi [m1] | semmle.label | Chi [m1] |
-| aliasing.cpp:25:17:25:19 | pointerSetter output argument [m1] | semmle.label | pointerSetter output argument [m1] |
-| aliasing.cpp:26:19:26:20 | Chi [m1] | semmle.label | Chi [m1] |
-| aliasing.cpp:26:19:26:20 | referenceSetter output argument [m1] | semmle.label | referenceSetter output argument [m1] |
+| aliasing.cpp:25:3:25:15 | & ... [post update] [m1] | semmle.label | & ... [post update] [m1] |
+| aliasing.cpp:26:3:26:17 | s2 [post update] [m1] | semmle.label | s2 [post update] [m1] |
+| aliasing.cpp:29:8:29:9 | s1 [read] [m1] | semmle.label | s1 [read] [m1] |
+| aliasing.cpp:29:11:29:12 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| aliasing.cpp:29:11:29:12 | m1 | semmle.label | m1 |
+| aliasing.cpp:30:8:30:9 | s2 [read] [m1] | semmle.label | s2 [read] [m1] |
+| aliasing.cpp:30:11:30:12 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| aliasing.cpp:30:11:30:12 | m1 | semmle.label | m1 |
-| aliasing.cpp:37:13:37:22 | call to user_input | semmle.label | call to user_input |
-| aliasing.cpp:38:11:38:12 | m1 | semmle.label | m1 |
-| aliasing.cpp:42:11:42:20 | call to user_input | semmle.label | call to user_input |
-| aliasing.cpp:43:13:43:14 | m1 | semmle.label | m1 |
-| aliasing.cpp:60:3:60:22 | Chi [m1] | semmle.label | Chi [m1] |
+| aliasing.cpp:60:3:60:4 | s2 [post update] [m1] | semmle.label | s2 [post update] [m1] |
+| aliasing.cpp:60:6:60:7 | m1 [post update] | semmle.label | m1 [post update] |
| aliasing.cpp:60:11:60:20 | call to user_input | semmle.label | call to user_input |
-| aliasing.cpp:61:13:61:14 | Store [m1] | semmle.label | Store [m1] |
+| aliasing.cpp:62:8:62:12 | copy2 [read] [m1] | semmle.label | copy2 [read] [m1] |
+| aliasing.cpp:62:14:62:15 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| aliasing.cpp:62:14:62:15 | m1 | semmle.label | m1 |
-| aliasing.cpp:79:11:79:20 | call to user_input | semmle.label | call to user_input |
-| aliasing.cpp:80:12:80:13 | m1 | semmle.label | m1 |
-| aliasing.cpp:86:10:86:19 | call to user_input | semmle.label | call to user_input |
-| aliasing.cpp:87:12:87:13 | m1 | semmle.label | m1 |
+| aliasing.cpp:92:3:92:3 | w [post update] [s, m1] | semmle.label | w [post update] [s, m1] |
+| aliasing.cpp:92:5:92:5 | s [post update] [m1] | semmle.label | s [post update] [m1] |
+| aliasing.cpp:92:7:92:8 | m1 [post update] | semmle.label | m1 [post update] |
| aliasing.cpp:92:12:92:21 | call to user_input | semmle.label | call to user_input |
+| aliasing.cpp:93:8:93:8 | w [read] [s, m1] | semmle.label | w [read] [s, m1] |
+| aliasing.cpp:93:10:93:10 | s [read] [m1] | semmle.label | s [read] [m1] |
+| aliasing.cpp:93:12:93:13 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| aliasing.cpp:93:12:93:13 | m1 | semmle.label | m1 |
-| aliasing.cpp:98:3:98:21 | Chi [m1] | semmle.label | Chi [m1] |
-| aliasing.cpp:98:10:98:19 | call to user_input | semmle.label | call to user_input |
-| aliasing.cpp:100:14:100:14 | Store [m1] | semmle.label | Store [m1] |
-| aliasing.cpp:102:8:102:10 | * ... | semmle.label | * ... |
-| aliasing.cpp:106:3:106:20 | Chi [[]] | semmle.label | Chi [[]] |
+| aliasing.cpp:106:3:106:5 | * ... [post update] | semmle.label | * ... [post update] |
+| aliasing.cpp:106:4:106:5 | pa [post update] | semmle.label | pa [post update] |
| aliasing.cpp:106:9:106:18 | call to user_input | semmle.label | call to user_input |
-| aliasing.cpp:121:15:121:16 | Chi [[]] | semmle.label | Chi [[]] |
-| aliasing.cpp:121:15:121:16 | taint_a_ptr output argument [[]] | semmle.label | taint_a_ptr output argument [[]] |
-| aliasing.cpp:122:8:122:12 | access to array | semmle.label | access to array |
-| aliasing.cpp:131:15:131:16 | Chi [[]] | semmle.label | Chi [[]] |
-| aliasing.cpp:131:15:131:16 | taint_a_ptr output argument [[]] | semmle.label | taint_a_ptr output argument [[]] |
-| aliasing.cpp:132:8:132:14 | * ... | semmle.label | * ... |
-| aliasing.cpp:136:15:136:17 | Chi [[]] | semmle.label | Chi [[]] |
-| aliasing.cpp:136:15:136:17 | taint_a_ptr output argument [[]] | semmle.label | taint_a_ptr output argument [[]] |
-| aliasing.cpp:137:8:137:11 | * ... | semmle.label | * ... |
-| aliasing.cpp:175:15:175:22 | Chi | semmle.label | Chi |
-| aliasing.cpp:175:15:175:22 | Chi [m1] | semmle.label | Chi [m1] |
-| aliasing.cpp:175:15:175:22 | taint_a_ptr output argument [[]] | semmle.label | taint_a_ptr output argument [[]] |
+| aliasing.cpp:141:3:141:13 | data [post update] | semmle.label | data [post update] |
+| aliasing.cpp:141:15:141:15 | s [post update] [data] | semmle.label | s [post update] [data] |
+| aliasing.cpp:141:17:141:20 | FieldAddress [post update] | semmle.label | FieldAddress [post update] |
+| aliasing.cpp:143:8:143:8 | s [read] [data] | semmle.label | s [read] [data] |
+| aliasing.cpp:143:8:143:16 | access to array | semmle.label | access to array |
+| aliasing.cpp:143:10:143:13 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| aliasing.cpp:158:3:158:13 | data [post update] | semmle.label | data [post update] |
+| aliasing.cpp:158:15:158:15 | s [post update] [data] | semmle.label | s [post update] [data] |
+| aliasing.cpp:158:17:158:20 | data [post update] | semmle.label | data [post update] |
+| aliasing.cpp:159:8:159:14 | * ... | semmle.label | * ... |
+| aliasing.cpp:159:9:159:9 | s [read] [data] | semmle.label | s [read] [data] |
+| aliasing.cpp:159:11:159:14 | data [read] | semmle.label | data [read] |
+| aliasing.cpp:164:3:164:13 | data [post update] | semmle.label | data [post update] |
+| aliasing.cpp:164:15:164:15 | s [post update] [data] | semmle.label | s [post update] [data] |
+| aliasing.cpp:164:17:164:20 | data [post update] | semmle.label | data [post update] |
+| aliasing.cpp:165:8:165:8 | s [read] [data] | semmle.label | s [read] [data] |
+| aliasing.cpp:165:8:165:16 | access to array | semmle.label | access to array |
+| aliasing.cpp:165:10:165:13 | data [read] | semmle.label | data [read] |
+| aliasing.cpp:175:3:175:13 | & ... [post update] | semmle.label | & ... [post update] |
+| aliasing.cpp:175:16:175:17 | s2 [post update] [s, m1] | semmle.label | s2 [post update] [s, m1] |
+| aliasing.cpp:175:19:175:19 | s [post update] [m1] | semmle.label | s [post update] [m1] |
+| aliasing.cpp:175:21:175:22 | m1 [post update] | semmle.label | m1 [post update] |
+| aliasing.cpp:176:8:176:9 | s2 [read] [s, m1] | semmle.label | s2 [read] [s, m1] |
+| aliasing.cpp:176:11:176:11 | s [read] [m1] | semmle.label | s [read] [m1] |
+| aliasing.cpp:176:13:176:14 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| aliasing.cpp:176:13:176:14 | m1 | semmle.label | m1 |
-| aliasing.cpp:187:15:187:22 | Chi | semmle.label | Chi |
-| aliasing.cpp:187:15:187:22 | Chi [m1] | semmle.label | Chi [m1] |
-| aliasing.cpp:187:15:187:22 | taint_a_ptr output argument [[]] | semmle.label | taint_a_ptr output argument [[]] |
-| aliasing.cpp:188:13:188:14 | Store [m1] | semmle.label | Store [m1] |
+| aliasing.cpp:187:3:187:13 | & ... [post update] | semmle.label | & ... [post update] |
+| aliasing.cpp:187:16:187:17 | s2 [post update] [s, m1] | semmle.label | s2 [post update] [s, m1] |
+| aliasing.cpp:187:19:187:19 | s [post update] [m1] | semmle.label | s [post update] [m1] |
+| aliasing.cpp:187:21:187:22 | m1 [post update] | semmle.label | m1 [post update] |
+| aliasing.cpp:189:8:189:11 | s2_2 [read] [s, m1] | semmle.label | s2_2 [read] [s, m1] |
+| aliasing.cpp:189:13:189:13 | s [read] [m1] | semmle.label | s [read] [m1] |
+| aliasing.cpp:189:15:189:16 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| aliasing.cpp:189:15:189:16 | m1 | semmle.label | m1 |
-| aliasing.cpp:200:15:200:24 | Chi | semmle.label | Chi |
-| aliasing.cpp:200:15:200:24 | Chi [m1] | semmle.label | Chi [m1] |
-| aliasing.cpp:200:15:200:24 | taint_a_ptr output argument [[]] | semmle.label | taint_a_ptr output argument [[]] |
+| aliasing.cpp:200:3:200:13 | & ... [post update] | semmle.label | & ... [post update] |
+| aliasing.cpp:200:16:200:18 | ps2 [post update] [s, m1] | semmle.label | ps2 [post update] [s, m1] |
+| aliasing.cpp:200:21:200:21 | s [post update] [m1] | semmle.label | s [post update] [m1] |
+| aliasing.cpp:200:23:200:24 | m1 [post update] | semmle.label | m1 [post update] |
+| aliasing.cpp:201:8:201:10 | ps2 [read] [s, m1] | semmle.label | ps2 [read] [s, m1] |
+| aliasing.cpp:201:13:201:13 | s [read] [m1] | semmle.label | s [read] [m1] |
+| aliasing.cpp:201:15:201:16 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| aliasing.cpp:201:15:201:16 | m1 | semmle.label | m1 |
| arrays.cpp:6:12:6:21 | call to user_input | semmle.label | call to user_input |
| arrays.cpp:7:8:7:13 | access to array | semmle.label | access to array |
+| arrays.cpp:8:8:8:13 | access to array | semmle.label | access to array |
| arrays.cpp:9:8:9:11 | * ... | semmle.label | * ... |
| arrays.cpp:10:8:10:15 | * ... | semmle.label | * ... |
| arrays.cpp:15:14:15:23 | call to user_input | semmle.label | call to user_input |
| arrays.cpp:16:8:16:13 | access to array | semmle.label | access to array |
+| arrays.cpp:17:8:17:13 | access to array | semmle.label | access to array |
+| arrays.cpp:36:3:36:3 | o [post update] [nested, arr, data] | semmle.label | o [post update] [nested, arr, data] |
+| arrays.cpp:36:3:36:17 | access to array [post update] [data] | semmle.label | access to array [post update] [data] |
+| arrays.cpp:36:5:36:10 | nested [post update] [arr, data] | semmle.label | nested [post update] [arr, data] |
+| arrays.cpp:36:12:36:14 | arr [post update] [data] | semmle.label | arr [post update] [data] |
+| arrays.cpp:36:19:36:22 | data [post update] | semmle.label | data [post update] |
| arrays.cpp:36:26:36:35 | call to user_input | semmle.label | call to user_input |
+| arrays.cpp:37:8:37:8 | o [read] [nested, arr, data] | semmle.label | o [read] [nested, arr, data] |
+| arrays.cpp:37:8:37:22 | access to array [read] [data] | semmle.label | access to array [read] [data] |
+| arrays.cpp:37:10:37:15 | nested [read] [arr, data] | semmle.label | nested [read] [arr, data] |
+| arrays.cpp:37:17:37:19 | arr [read] [data] | semmle.label | arr [read] [data] |
+| arrays.cpp:37:24:37:27 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| arrays.cpp:37:24:37:27 | data | semmle.label | data |
+| arrays.cpp:38:8:38:8 | o [read] [nested, arr, data] | semmle.label | o [read] [nested, arr, data] |
+| arrays.cpp:38:8:38:22 | access to array [read] [data] | semmle.label | access to array [read] [data] |
+| arrays.cpp:38:10:38:15 | nested [read] [arr, data] | semmle.label | nested [read] [arr, data] |
+| arrays.cpp:38:17:38:19 | arr [read] [data] | semmle.label | arr [read] [data] |
+| arrays.cpp:38:24:38:27 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| arrays.cpp:38:24:38:27 | data | semmle.label | data |
+| arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, data] | semmle.label | o [post update] [indirect, arr, data] |
+| arrays.cpp:42:3:42:20 | access to array [post update] [data] | semmle.label | access to array [post update] [data] |
+| arrays.cpp:42:5:42:12 | FieldAddress [post update] [arr, data] | semmle.label | FieldAddress [post update] [arr, data] |
+| arrays.cpp:42:5:42:12 | indirect [post update] [arr, data] | semmle.label | indirect [post update] [arr, data] |
+| arrays.cpp:42:15:42:17 | arr [post update] [data] | semmle.label | arr [post update] [data] |
+| arrays.cpp:42:22:42:25 | data [post update] | semmle.label | data [post update] |
+| arrays.cpp:42:29:42:38 | call to user_input | semmle.label | call to user_input |
+| arrays.cpp:43:8:43:8 | o [read] [indirect, arr, data] | semmle.label | o [read] [indirect, arr, data] |
+| arrays.cpp:43:8:43:25 | access to array [read] [data] | semmle.label | access to array [read] [data] |
+| arrays.cpp:43:10:43:17 | FieldAddress [read] [arr, data] | semmle.label | FieldAddress [read] [arr, data] |
+| arrays.cpp:43:10:43:17 | indirect [read] [arr, data] | semmle.label | indirect [read] [arr, data] |
+| arrays.cpp:43:20:43:22 | arr [read] [data] | semmle.label | arr [read] [data] |
+| arrays.cpp:43:27:43:30 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| arrays.cpp:43:27:43:30 | data | semmle.label | data |
+| arrays.cpp:44:8:44:8 | o [read] [indirect, arr, data] | semmle.label | o [read] [indirect, arr, data] |
+| arrays.cpp:44:8:44:25 | access to array [read] [data] | semmle.label | access to array [read] [data] |
+| arrays.cpp:44:10:44:17 | FieldAddress [read] [arr, data] | semmle.label | FieldAddress [read] [arr, data] |
+| arrays.cpp:44:10:44:17 | indirect [read] [arr, data] | semmle.label | indirect [read] [arr, data] |
+| arrays.cpp:44:20:44:22 | arr [read] [data] | semmle.label | arr [read] [data] |
+| arrays.cpp:44:27:44:30 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| arrays.cpp:44:27:44:30 | data | semmle.label | data |
+| arrays.cpp:48:3:48:3 | o [post update] [indirect, ptr, data] | semmle.label | o [post update] [indirect, ptr, data] |
+| arrays.cpp:48:3:48:20 | access to array [post update] [data] | semmle.label | access to array [post update] [data] |
+| arrays.cpp:48:5:48:12 | FieldAddress [post update] [ptr, data] | semmle.label | FieldAddress [post update] [ptr, data] |
+| arrays.cpp:48:5:48:12 | indirect [post update] [ptr, data] | semmle.label | indirect [post update] [ptr, data] |
+| arrays.cpp:48:15:48:17 | FieldAddress [post update] [data] | semmle.label | FieldAddress [post update] [data] |
+| arrays.cpp:48:22:48:25 | data [post update] | semmle.label | data [post update] |
+| arrays.cpp:48:29:48:38 | call to user_input | semmle.label | call to user_input |
+| arrays.cpp:49:8:49:8 | o [read] [indirect, ptr, data] | semmle.label | o [read] [indirect, ptr, data] |
+| arrays.cpp:49:8:49:25 | access to array [read] [data] | semmle.label | access to array [read] [data] |
+| arrays.cpp:49:10:49:17 | FieldAddress [read] [ptr, data] | semmle.label | FieldAddress [read] [ptr, data] |
+| arrays.cpp:49:10:49:17 | indirect [read] [ptr, data] | semmle.label | indirect [read] [ptr, data] |
+| arrays.cpp:49:20:49:22 | FieldAddress [read] [data] | semmle.label | FieldAddress [read] [data] |
+| arrays.cpp:49:27:49:30 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| arrays.cpp:49:27:49:30 | data | semmle.label | data |
+| arrays.cpp:50:8:50:8 | o [read] [indirect, ptr, data] | semmle.label | o [read] [indirect, ptr, data] |
+| arrays.cpp:50:8:50:25 | access to array [read] [data] | semmle.label | access to array [read] [data] |
+| arrays.cpp:50:10:50:17 | FieldAddress [read] [ptr, data] | semmle.label | FieldAddress [read] [ptr, data] |
+| arrays.cpp:50:10:50:17 | indirect [read] [ptr, data] | semmle.label | indirect [read] [ptr, data] |
+| arrays.cpp:50:20:50:22 | FieldAddress [read] [data] | semmle.label | FieldAddress [read] [data] |
+| arrays.cpp:50:27:50:30 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| arrays.cpp:50:27:50:30 | data | semmle.label | data |
| by_reference.cpp:11:48:11:52 | value | semmle.label | value |
-| by_reference.cpp:12:5:12:16 | Chi [a] | semmle.label | Chi [a] |
+| by_reference.cpp:12:5:12:5 | s [post update] [a] | semmle.label | s [post update] [a] |
+| by_reference.cpp:12:8:12:8 | a [post update] | semmle.label | a [post update] |
| by_reference.cpp:15:26:15:30 | value | semmle.label | value |
-| by_reference.cpp:16:5:16:19 | Chi [a] | semmle.label | Chi [a] |
+| by_reference.cpp:16:5:16:8 | this [post update] [a] | semmle.label | this [post update] [a] |
+| by_reference.cpp:16:11:16:11 | a [post update] | semmle.label | a [post update] |
| by_reference.cpp:19:28:19:32 | value | semmle.label | value |
-| by_reference.cpp:20:5:20:8 | Chi [a] | semmle.label | Chi [a] |
-| by_reference.cpp:20:5:20:8 | setDirectly output argument [a] | semmle.label | setDirectly output argument [a] |
+| by_reference.cpp:20:5:20:8 | this [post update] [a] | semmle.label | this [post update] [a] |
+| by_reference.cpp:20:11:20:21 | this [post update] [a] | semmle.label | this [post update] [a] |
| by_reference.cpp:20:11:20:21 | value | semmle.label | value |
| by_reference.cpp:23:34:23:38 | value | semmle.label | value |
+| by_reference.cpp:24:5:24:17 | this [post update] [a] | semmle.label | this [post update] [a] |
| by_reference.cpp:24:5:24:17 | value | semmle.label | value |
-| by_reference.cpp:24:19:24:22 | Chi [a] | semmle.label | Chi [a] |
-| by_reference.cpp:24:19:24:22 | nonMemberSetA output argument [a] | semmle.label | nonMemberSetA output argument [a] |
+| by_reference.cpp:24:19:24:22 | this [post update] [a] | semmle.label | this [post update] [a] |
+| by_reference.cpp:31:16:31:28 | ReturnValue | semmle.label | ReturnValue |
+| by_reference.cpp:31:16:31:28 | ReturnValue | semmle.label | ReturnValue |
| by_reference.cpp:31:46:31:46 | *s [a] | semmle.label | *s [a] |
-| by_reference.cpp:32:15:32:15 | Store | semmle.label | Store |
+| by_reference.cpp:31:46:31:46 | s [a] | semmle.label | s [a] |
+| by_reference.cpp:32:12:32:12 | s [read] [a] | semmle.label | s [read] [a] |
+| by_reference.cpp:32:12:32:12 | s [read] [a] | semmle.label | s [read] [a] |
+| by_reference.cpp:32:15:32:15 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| by_reference.cpp:32:15:32:15 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| by_reference.cpp:35:9:35:19 | *#this [a] | semmle.label | *#this [a] |
-| by_reference.cpp:36:18:36:18 | Store | semmle.label | Store |
+| by_reference.cpp:35:9:35:19 | ReturnValue | semmle.label | ReturnValue |
+| by_reference.cpp:35:9:35:19 | ReturnValue | semmle.label | ReturnValue |
+| by_reference.cpp:35:9:35:19 | this [a] | semmle.label | this [a] |
+| by_reference.cpp:36:12:36:15 | this [read] [a] | semmle.label | this [read] [a] |
+| by_reference.cpp:36:12:36:15 | this [read] [a] | semmle.label | this [read] [a] |
+| by_reference.cpp:36:18:36:18 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| by_reference.cpp:36:18:36:18 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| by_reference.cpp:39:9:39:21 | *#this [a] | semmle.label | *#this [a] |
-| by_reference.cpp:40:12:40:15 | this indirection [a] | semmle.label | this indirection [a] |
-| by_reference.cpp:40:18:40:28 | Store | semmle.label | Store |
+| by_reference.cpp:39:9:39:21 | ReturnValue | semmle.label | ReturnValue |
| by_reference.cpp:40:18:40:28 | call to getDirectly | semmle.label | call to getDirectly |
+| by_reference.cpp:40:18:40:28 | this [a] | semmle.label | this [a] |
| by_reference.cpp:43:9:43:27 | *#this [a] | semmle.label | *#this [a] |
-| by_reference.cpp:44:12:44:24 | Store | semmle.label | Store |
+| by_reference.cpp:43:9:43:27 | ReturnValue | semmle.label | ReturnValue |
| by_reference.cpp:44:12:44:24 | call to nonMemberGetA | semmle.label | call to nonMemberGetA |
-| by_reference.cpp:44:26:44:29 | this indirection [a] | semmle.label | this indirection [a] |
-| by_reference.cpp:50:3:50:3 | setDirectly output argument [a] | semmle.label | setDirectly output argument [a] |
+| by_reference.cpp:44:12:44:24 | this [a] | semmle.label | this [a] |
| by_reference.cpp:50:5:50:15 | call to user_input | semmle.label | call to user_input |
+| by_reference.cpp:50:5:50:15 | s [post update] [a] | semmle.label | s [post update] [a] |
| by_reference.cpp:50:17:50:26 | call to user_input | semmle.label | call to user_input |
| by_reference.cpp:51:8:51:8 | s indirection [a] | semmle.label | s indirection [a] |
| by_reference.cpp:51:10:51:20 | call to getDirectly | semmle.label | call to getDirectly |
-| by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] | semmle.label | setIndirectly output argument [a] |
| by_reference.cpp:56:5:56:17 | call to user_input | semmle.label | call to user_input |
+| by_reference.cpp:56:5:56:17 | s [post update] [a] | semmle.label | s [post update] [a] |
| by_reference.cpp:56:19:56:28 | call to user_input | semmle.label | call to user_input |
| by_reference.cpp:57:8:57:8 | s indirection [a] | semmle.label | s indirection [a] |
| by_reference.cpp:57:10:57:22 | call to getIndirectly | semmle.label | call to getIndirectly |
-| by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] | semmle.label | setThroughNonMember output argument [a] |
| by_reference.cpp:62:5:62:23 | call to user_input | semmle.label | call to user_input |
+| by_reference.cpp:62:5:62:23 | s [post update] [a] | semmle.label | s [post update] [a] |
| by_reference.cpp:62:25:62:34 | call to user_input | semmle.label | call to user_input |
| by_reference.cpp:63:8:63:8 | s indirection [a] | semmle.label | s indirection [a] |
| by_reference.cpp:63:10:63:28 | call to getThroughNonMember | semmle.label | call to getThroughNonMember |
+| by_reference.cpp:68:3:68:15 | & ... [post update] [a] | semmle.label | & ... [post update] [a] |
| by_reference.cpp:68:3:68:15 | call to user_input | semmle.label | call to user_input |
-| by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] | semmle.label | nonMemberSetA output argument [a] |
| by_reference.cpp:68:21:68:30 | call to user_input | semmle.label | call to user_input |
| by_reference.cpp:69:8:69:20 | call to nonMemberGetA | semmle.label | call to nonMemberGetA |
| by_reference.cpp:69:22:69:23 | & ... indirection [a] | semmle.label | & ... indirection [a] |
-| by_reference.cpp:84:3:84:25 | Chi [a] | semmle.label | Chi [a] |
+| by_reference.cpp:84:3:84:7 | inner [post update] [a] | semmle.label | inner [post update] [a] |
+| by_reference.cpp:84:10:84:10 | a [post update] | semmle.label | a [post update] |
| by_reference.cpp:84:14:84:23 | call to user_input | semmle.label | call to user_input |
-| by_reference.cpp:88:3:88:24 | Chi [a] | semmle.label | Chi [a] |
+| by_reference.cpp:88:3:88:7 | (reference dereference) [post update] [a] | semmle.label | (reference dereference) [post update] [a] |
+| by_reference.cpp:88:3:88:7 | inner [post update] [a] | semmle.label | inner [post update] [a] |
+| by_reference.cpp:88:9:88:9 | a [post update] | semmle.label | a [post update] |
| by_reference.cpp:88:13:88:22 | call to user_input | semmle.label | call to user_input |
-| by_reference.cpp:92:3:92:20 | Chi [[]] | semmle.label | Chi [[]] |
+| by_reference.cpp:92:3:92:5 | * ... [post update] | semmle.label | * ... [post update] |
+| by_reference.cpp:92:4:92:5 | pa [post update] | semmle.label | pa [post update] |
| by_reference.cpp:92:9:92:18 | call to user_input | semmle.label | call to user_input |
-| by_reference.cpp:96:3:96:19 | Chi [[]] | semmle.label | Chi [[]] |
+| by_reference.cpp:96:3:96:4 | (reference dereference) [post update] | semmle.label | (reference dereference) [post update] |
+| by_reference.cpp:96:3:96:4 | pa [post update] | semmle.label | pa [post update] |
| by_reference.cpp:96:8:96:17 | call to user_input | semmle.label | call to user_input |
-| by_reference.cpp:102:21:102:39 | Chi [a] | semmle.label | Chi [a] |
-| by_reference.cpp:102:21:102:39 | taint_inner_a_ptr output argument [a] | semmle.label | taint_inner_a_ptr output argument [a] |
-| by_reference.cpp:104:15:104:22 | Chi | semmle.label | Chi |
-| by_reference.cpp:104:15:104:22 | Chi [a] | semmle.label | Chi [a] |
-| by_reference.cpp:104:15:104:22 | taint_a_ptr output argument [[]] | semmle.label | taint_a_ptr output argument [[]] |
-| by_reference.cpp:106:21:106:41 | Chi [a] | semmle.label | Chi [a] |
-| by_reference.cpp:106:21:106:41 | taint_inner_a_ptr output argument [a] | semmle.label | taint_inner_a_ptr output argument [a] |
-| by_reference.cpp:108:15:108:24 | Chi | semmle.label | Chi |
-| by_reference.cpp:108:15:108:24 | Chi [a] | semmle.label | Chi [a] |
-| by_reference.cpp:108:15:108:24 | taint_a_ptr output argument [[]] | semmle.label | taint_a_ptr output argument [[]] |
+| by_reference.cpp:102:3:102:19 | & ... [post update] [a] | semmle.label | & ... [post update] [a] |
+| by_reference.cpp:102:22:102:26 | outer [post update] [inner_nested, a] | semmle.label | outer [post update] [inner_nested, a] |
+| by_reference.cpp:102:28:102:39 | inner_nested [post update] [a] | semmle.label | inner_nested [post update] [a] |
+| by_reference.cpp:103:3:103:19 | inner_ptr [post update] [a] | semmle.label | inner_ptr [post update] [a] |
+| by_reference.cpp:103:21:103:25 | outer [post update] [inner_ptr, a] | semmle.label | outer [post update] [inner_ptr, a] |
+| by_reference.cpp:103:27:103:35 | FieldAddress [post update] [a] | semmle.label | FieldAddress [post update] [a] |
+| by_reference.cpp:104:3:104:13 | & ... [post update] | semmle.label | & ... [post update] |
+| by_reference.cpp:104:16:104:20 | outer [post update] [a] | semmle.label | outer [post update] [a] |
+| by_reference.cpp:104:22:104:22 | a [post update] | semmle.label | a [post update] |
+| by_reference.cpp:106:3:106:19 | & ... [post update] [a] | semmle.label | & ... [post update] [a] |
+| by_reference.cpp:106:22:106:27 | pouter [post update] [inner_nested, a] | semmle.label | pouter [post update] [inner_nested, a] |
+| by_reference.cpp:106:30:106:41 | inner_nested [post update] [a] | semmle.label | inner_nested [post update] [a] |
+| by_reference.cpp:107:3:107:19 | inner_ptr [post update] [a] | semmle.label | inner_ptr [post update] [a] |
+| by_reference.cpp:107:21:107:26 | pouter [post update] [inner_ptr, a] | semmle.label | pouter [post update] [inner_ptr, a] |
+| by_reference.cpp:107:29:107:37 | FieldAddress [post update] [a] | semmle.label | FieldAddress [post update] [a] |
+| by_reference.cpp:108:3:108:13 | & ... [post update] | semmle.label | & ... [post update] |
+| by_reference.cpp:108:16:108:21 | pouter [post update] [a] | semmle.label | pouter [post update] [a] |
+| by_reference.cpp:108:24:108:24 | a [post update] | semmle.label | a [post update] |
+| by_reference.cpp:110:8:110:12 | outer [read] [inner_nested, a] | semmle.label | outer [read] [inner_nested, a] |
+| by_reference.cpp:110:14:110:25 | inner_nested [read] [a] | semmle.label | inner_nested [read] [a] |
+| by_reference.cpp:110:27:110:27 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| by_reference.cpp:110:27:110:27 | a | semmle.label | a |
+| by_reference.cpp:111:8:111:12 | outer [read] [inner_ptr, a] | semmle.label | outer [read] [inner_ptr, a] |
+| by_reference.cpp:111:14:111:22 | FieldAddress [read] [a] | semmle.label | FieldAddress [read] [a] |
+| by_reference.cpp:111:14:111:22 | inner_ptr [read] [a] | semmle.label | inner_ptr [read] [a] |
+| by_reference.cpp:111:25:111:25 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| by_reference.cpp:111:25:111:25 | a | semmle.label | a |
+| by_reference.cpp:112:8:112:12 | outer [read] [a] | semmle.label | outer [read] [a] |
+| by_reference.cpp:112:14:112:14 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| by_reference.cpp:112:14:112:14 | a | semmle.label | a |
+| by_reference.cpp:114:8:114:13 | pouter [read] [inner_nested, a] | semmle.label | pouter [read] [inner_nested, a] |
+| by_reference.cpp:114:16:114:27 | inner_nested [read] [a] | semmle.label | inner_nested [read] [a] |
+| by_reference.cpp:114:29:114:29 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| by_reference.cpp:114:29:114:29 | a | semmle.label | a |
+| by_reference.cpp:115:8:115:13 | pouter [read] [inner_ptr, a] | semmle.label | pouter [read] [inner_ptr, a] |
+| by_reference.cpp:115:16:115:24 | FieldAddress [read] [a] | semmle.label | FieldAddress [read] [a] |
+| by_reference.cpp:115:16:115:24 | inner_ptr [read] [a] | semmle.label | inner_ptr [read] [a] |
+| by_reference.cpp:115:27:115:27 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| by_reference.cpp:115:27:115:27 | a | semmle.label | a |
+| by_reference.cpp:116:8:116:13 | pouter [read] [a] | semmle.label | pouter [read] [a] |
+| by_reference.cpp:116:16:116:16 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| by_reference.cpp:116:16:116:16 | a | semmle.label | a |
-| by_reference.cpp:122:21:122:38 | Chi [a] | semmle.label | Chi [a] |
-| by_reference.cpp:122:21:122:38 | taint_inner_a_ref output argument [a] | semmle.label | taint_inner_a_ref output argument [a] |
-| by_reference.cpp:124:15:124:21 | Chi | semmle.label | Chi |
-| by_reference.cpp:124:15:124:21 | Chi [a] | semmle.label | Chi [a] |
-| by_reference.cpp:124:15:124:21 | taint_a_ref output argument [[]] | semmle.label | taint_a_ref output argument [[]] |
-| by_reference.cpp:126:21:126:40 | Chi [a] | semmle.label | Chi [a] |
-| by_reference.cpp:126:21:126:40 | taint_inner_a_ref output argument [a] | semmle.label | taint_inner_a_ref output argument [a] |
-| by_reference.cpp:128:15:128:23 | Chi | semmle.label | Chi |
-| by_reference.cpp:128:15:128:23 | Chi [a] | semmle.label | Chi [a] |
-| by_reference.cpp:128:15:128:23 | taint_a_ref output argument [[]] | semmle.label | taint_a_ref output argument [[]] |
+| by_reference.cpp:122:3:122:19 | inner_nested [post update] [a] | semmle.label | inner_nested [post update] [a] |
+| by_reference.cpp:122:21:122:25 | outer [post update] [inner_nested, a] | semmle.label | outer [post update] [inner_nested, a] |
+| by_reference.cpp:122:27:122:38 | inner_nested [post update] [a] | semmle.label | inner_nested [post update] [a] |
+| by_reference.cpp:123:3:123:19 | * ... [post update] [a] | semmle.label | * ... [post update] [a] |
+| by_reference.cpp:123:22:123:26 | outer [post update] [inner_ptr, a] | semmle.label | outer [post update] [inner_ptr, a] |
+| by_reference.cpp:123:28:123:36 | FieldAddress [post update] [a] | semmle.label | FieldAddress [post update] [a] |
+| by_reference.cpp:124:3:124:13 | a [post update] | semmle.label | a [post update] |
+| by_reference.cpp:124:15:124:19 | outer [post update] [a] | semmle.label | outer [post update] [a] |
+| by_reference.cpp:124:21:124:21 | a [post update] | semmle.label | a [post update] |
+| by_reference.cpp:126:3:126:19 | inner_nested [post update] [a] | semmle.label | inner_nested [post update] [a] |
+| by_reference.cpp:126:21:126:26 | pouter [post update] [inner_nested, a] | semmle.label | pouter [post update] [inner_nested, a] |
+| by_reference.cpp:126:29:126:40 | inner_nested [post update] [a] | semmle.label | inner_nested [post update] [a] |
+| by_reference.cpp:127:3:127:19 | * ... [post update] [a] | semmle.label | * ... [post update] [a] |
+| by_reference.cpp:127:22:127:27 | pouter [post update] [inner_ptr, a] | semmle.label | pouter [post update] [inner_ptr, a] |
+| by_reference.cpp:127:30:127:38 | FieldAddress [post update] [a] | semmle.label | FieldAddress [post update] [a] |
+| by_reference.cpp:128:3:128:13 | a [post update] | semmle.label | a [post update] |
+| by_reference.cpp:128:15:128:20 | pouter [post update] [a] | semmle.label | pouter [post update] [a] |
+| by_reference.cpp:128:23:128:23 | a [post update] | semmle.label | a [post update] |
+| by_reference.cpp:130:8:130:12 | outer [read] [inner_nested, a] | semmle.label | outer [read] [inner_nested, a] |
+| by_reference.cpp:130:14:130:25 | inner_nested [read] [a] | semmle.label | inner_nested [read] [a] |
+| by_reference.cpp:130:27:130:27 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| by_reference.cpp:130:27:130:27 | a | semmle.label | a |
+| by_reference.cpp:131:8:131:12 | outer [read] [inner_ptr, a] | semmle.label | outer [read] [inner_ptr, a] |
+| by_reference.cpp:131:14:131:22 | FieldAddress [read] [a] | semmle.label | FieldAddress [read] [a] |
+| by_reference.cpp:131:14:131:22 | inner_ptr [read] [a] | semmle.label | inner_ptr [read] [a] |
+| by_reference.cpp:131:25:131:25 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| by_reference.cpp:131:25:131:25 | a | semmle.label | a |
+| by_reference.cpp:132:8:132:12 | outer [read] [a] | semmle.label | outer [read] [a] |
+| by_reference.cpp:132:14:132:14 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| by_reference.cpp:132:14:132:14 | a | semmle.label | a |
+| by_reference.cpp:134:8:134:13 | pouter [read] [inner_nested, a] | semmle.label | pouter [read] [inner_nested, a] |
+| by_reference.cpp:134:16:134:27 | inner_nested [read] [a] | semmle.label | inner_nested [read] [a] |
+| by_reference.cpp:134:29:134:29 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| by_reference.cpp:134:29:134:29 | a | semmle.label | a |
+| by_reference.cpp:135:8:135:13 | pouter [read] [inner_ptr, a] | semmle.label | pouter [read] [inner_ptr, a] |
+| by_reference.cpp:135:16:135:24 | FieldAddress [read] [a] | semmle.label | FieldAddress [read] [a] |
+| by_reference.cpp:135:16:135:24 | inner_ptr [read] [a] | semmle.label | inner_ptr [read] [a] |
+| by_reference.cpp:135:27:135:27 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| by_reference.cpp:135:27:135:27 | a | semmle.label | a |
+| by_reference.cpp:136:8:136:13 | pouter [read] [a] | semmle.label | pouter [read] [a] |
+| by_reference.cpp:136:16:136:16 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| by_reference.cpp:136:16:136:16 | a | semmle.label | a |
-| complex.cpp:9:7:9:7 | *#this [a_] | semmle.label | *#this [a_] |
-| complex.cpp:9:7:9:7 | *#this [b_] | semmle.label | *#this [b_] |
-| complex.cpp:9:20:9:21 | Store | semmle.label | Store |
-| complex.cpp:10:7:10:7 | *#this [b_] | semmle.label | *#this [b_] |
-| complex.cpp:10:20:10:21 | Store | semmle.label | Store |
+| complex.cpp:9:7:9:7 | ReturnValue | semmle.label | ReturnValue |
+| complex.cpp:9:7:9:7 | this [a_] | semmle.label | this [a_] |
+| complex.cpp:9:20:9:21 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| complex.cpp:9:20:9:21 | this [read] [a_] | semmle.label | this [read] [a_] |
+| complex.cpp:10:7:10:7 | ReturnValue | semmle.label | ReturnValue |
+| complex.cpp:10:7:10:7 | this [b_] | semmle.label | this [b_] |
+| complex.cpp:10:20:10:21 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| complex.cpp:10:20:10:21 | this [read] [b_] | semmle.label | this [read] [b_] |
| complex.cpp:11:17:11:17 | a | semmle.label | a |
-| complex.cpp:11:22:11:27 | Chi [a_] | semmle.label | Chi [a_] |
-| complex.cpp:12:8:12:11 | *#this [a_] | semmle.label | *#this [a_] |
+| complex.cpp:11:22:11:23 | a_ [post update] | semmle.label | a_ [post update] |
+| complex.cpp:11:22:11:23 | this [post update] [a_] | semmle.label | this [post update] [a_] |
| complex.cpp:12:17:12:17 | b | semmle.label | b |
-| complex.cpp:12:22:12:27 | Chi [a_] | semmle.label | Chi [a_] |
-| complex.cpp:12:22:12:27 | Chi [b_] | semmle.label | Chi [b_] |
-| complex.cpp:40:17:40:17 | *b [a_] | semmle.label | *b [a_] |
-| complex.cpp:40:17:40:17 | *b [b_] | semmle.label | *b [b_] |
-| complex.cpp:42:16:42:16 | a output argument [b_] | semmle.label | a output argument [b_] |
-| complex.cpp:42:16:42:16 | f indirection [a_] | semmle.label | f indirection [a_] |
-| complex.cpp:42:16:42:16 | f indirection [b_] | semmle.label | f indirection [b_] |
+| complex.cpp:12:22:12:23 | b_ [post update] | semmle.label | b_ [post update] |
+| complex.cpp:12:22:12:23 | this [post update] [b_] | semmle.label | this [post update] [b_] |
+| complex.cpp:40:17:40:17 | b [inner, f, a_] | semmle.label | b [inner, f, a_] |
+| complex.cpp:40:17:40:17 | b [inner, f, b_] | semmle.label | b [inner, f, b_] |
+| complex.cpp:42:8:42:8 | (reference dereference) [read] [inner, f, a_] | semmle.label | (reference dereference) [read] [inner, f, a_] |
+| complex.cpp:42:10:42:14 | inner [read] [f, a_] | semmle.label | inner [read] [f, a_] |
+| complex.cpp:42:16:42:16 | f [read] [a_] | semmle.label | f [read] [a_] |
| complex.cpp:42:18:42:18 | call to a | semmle.label | call to a |
-| complex.cpp:43:16:43:16 | f indirection [b_] | semmle.label | f indirection [b_] |
+| complex.cpp:42:18:42:18 | f [a_] | semmle.label | f [a_] |
+| complex.cpp:43:8:43:8 | (reference dereference) [read] [inner, f, b_] | semmle.label | (reference dereference) [read] [inner, f, b_] |
+| complex.cpp:43:10:43:14 | inner [read] [f, b_] | semmle.label | inner [read] [f, b_] |
+| complex.cpp:43:16:43:16 | f [read] [b_] | semmle.label | f [read] [b_] |
| complex.cpp:43:18:43:18 | call to b | semmle.label | call to b |
-| complex.cpp:53:12:53:12 | setA output argument [a_] | semmle.label | setA output argument [a_] |
+| complex.cpp:43:18:43:18 | f [b_] | semmle.label | f [b_] |
+| complex.cpp:53:3:53:4 | b1 [post update] [inner, f, a_] | semmle.label | b1 [post update] [inner, f, a_] |
+| complex.cpp:53:6:53:10 | inner [post update] [f, a_] | semmle.label | inner [post update] [f, a_] |
+| complex.cpp:53:12:53:12 | f [post update] [a_] | semmle.label | f [post update] [a_] |
| complex.cpp:53:14:53:17 | call to user_input | semmle.label | call to user_input |
+| complex.cpp:53:14:53:17 | f [post update] [a_] | semmle.label | f [post update] [a_] |
| complex.cpp:53:19:53:28 | call to user_input | semmle.label | call to user_input |
-| complex.cpp:54:12:54:12 | setB output argument [b_] | semmle.label | setB output argument [b_] |
+| complex.cpp:54:3:54:4 | b2 [post update] [inner, f, b_] | semmle.label | b2 [post update] [inner, f, b_] |
+| complex.cpp:54:6:54:10 | inner [post update] [f, b_] | semmle.label | inner [post update] [f, b_] |
+| complex.cpp:54:12:54:12 | f [post update] [b_] | semmle.label | f [post update] [b_] |
| complex.cpp:54:14:54:17 | call to user_input | semmle.label | call to user_input |
+| complex.cpp:54:14:54:17 | f [post update] [b_] | semmle.label | f [post update] [b_] |
| complex.cpp:54:19:54:28 | call to user_input | semmle.label | call to user_input |
-| complex.cpp:55:12:55:12 | setA output argument [a_] | semmle.label | setA output argument [a_] |
+| complex.cpp:55:3:55:4 | b3 [post update] [inner, f, a_] | semmle.label | b3 [post update] [inner, f, a_] |
+| complex.cpp:55:6:55:10 | inner [post update] [f, a_] | semmle.label | inner [post update] [f, a_] |
+| complex.cpp:55:12:55:12 | f [post update] [a_] | semmle.label | f [post update] [a_] |
| complex.cpp:55:14:55:17 | call to user_input | semmle.label | call to user_input |
+| complex.cpp:55:14:55:17 | f [post update] [a_] | semmle.label | f [post update] [a_] |
| complex.cpp:55:19:55:28 | call to user_input | semmle.label | call to user_input |
-| complex.cpp:56:12:56:12 | f indirection [a_] | semmle.label | f indirection [a_] |
-| complex.cpp:56:12:56:12 | setB output argument [a_] | semmle.label | setB output argument [a_] |
-| complex.cpp:56:12:56:12 | setB output argument [b_] | semmle.label | setB output argument [b_] |
+| complex.cpp:56:3:56:4 | b3 [post update] [inner, f, b_] | semmle.label | b3 [post update] [inner, f, b_] |
+| complex.cpp:56:6:56:10 | inner [post update] [f, b_] | semmle.label | inner [post update] [f, b_] |
+| complex.cpp:56:12:56:12 | f [post update] [b_] | semmle.label | f [post update] [b_] |
| complex.cpp:56:14:56:17 | call to user_input | semmle.label | call to user_input |
+| complex.cpp:56:14:56:17 | f [post update] [b_] | semmle.label | f [post update] [b_] |
| complex.cpp:56:19:56:28 | call to user_input | semmle.label | call to user_input |
-| complex.cpp:59:7:59:8 | b1 indirection [a_] | semmle.label | b1 indirection [a_] |
-| complex.cpp:62:7:62:8 | b2 indirection [b_] | semmle.label | b2 indirection [b_] |
-| complex.cpp:65:7:65:8 | b3 indirection [a_] | semmle.label | b3 indirection [a_] |
-| complex.cpp:65:7:65:8 | b3 indirection [b_] | semmle.label | b3 indirection [b_] |
-| constructors.cpp:18:9:18:9 | *#this [a_] | semmle.label | *#this [a_] |
-| constructors.cpp:18:9:18:9 | *#this [b_] | semmle.label | *#this [b_] |
-| constructors.cpp:18:22:18:23 | Store | semmle.label | Store |
-| constructors.cpp:19:9:19:9 | *#this [b_] | semmle.label | *#this [b_] |
-| constructors.cpp:19:22:19:23 | Store | semmle.label | Store |
+| complex.cpp:59:3:59:5 | b1 [inner, f, a_] | semmle.label | b1 [inner, f, a_] |
+| complex.cpp:62:3:62:5 | b2 [inner, f, b_] | semmle.label | b2 [inner, f, b_] |
+| complex.cpp:65:3:65:5 | b3 [inner, f, a_] | semmle.label | b3 [inner, f, a_] |
+| complex.cpp:65:3:65:5 | b3 [inner, f, b_] | semmle.label | b3 [inner, f, b_] |
+| conflated.cpp:10:4:10:5 | (reference dereference) [post update] [p] | semmle.label | (reference dereference) [post update] [p] |
+| conflated.cpp:10:7:10:7 | FieldAddress [post update] | semmle.label | FieldAddress [post update] |
+| conflated.cpp:10:11:10:20 | call to user_input | semmle.label | call to user_input |
+| conflated.cpp:11:8:11:12 | * ... | semmle.label | * ... |
+| conflated.cpp:11:9:11:10 | (reference dereference) [read] [p] | semmle.label | (reference dereference) [read] [p] |
+| conflated.cpp:11:12:11:12 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| conflated.cpp:19:19:19:21 | argument_source output argument | semmle.label | argument_source output argument |
+| conflated.cpp:20:8:20:10 | (void *)... | semmle.label | (void *)... |
+| conflated.cpp:20:8:20:10 | raw | semmle.label | raw |
+| conflated.cpp:29:3:29:4 | pa [post update] [x] | semmle.label | pa [post update] [x] |
+| conflated.cpp:29:7:29:7 | x [post update] | semmle.label | x [post update] |
+| conflated.cpp:29:11:29:20 | call to user_input | semmle.label | call to user_input |
+| conflated.cpp:30:8:30:9 | pa [read] [x] | semmle.label | pa [read] [x] |
+| conflated.cpp:30:12:30:12 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| conflated.cpp:30:12:30:12 | x | semmle.label | x |
+| conflated.cpp:36:3:36:4 | pa [post update] [x] | semmle.label | pa [post update] [x] |
+| conflated.cpp:36:7:36:7 | x [post update] | semmle.label | x [post update] |
+| conflated.cpp:36:11:36:20 | call to user_input | semmle.label | call to user_input |
+| conflated.cpp:37:8:37:9 | pa [read] [x] | semmle.label | pa [read] [x] |
+| conflated.cpp:37:12:37:12 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| conflated.cpp:37:12:37:12 | x | semmle.label | x |
+| conflated.cpp:54:3:54:4 | ll [post update] [next, y] | semmle.label | ll [post update] [next, y] |
+| conflated.cpp:54:7:54:10 | FieldAddress [post update] [y] | semmle.label | FieldAddress [post update] [y] |
+| conflated.cpp:54:7:54:10 | next [post update] [y] | semmle.label | next [post update] [y] |
+| conflated.cpp:54:13:54:13 | y [post update] | semmle.label | y [post update] |
+| conflated.cpp:54:17:54:26 | call to user_input | semmle.label | call to user_input |
+| conflated.cpp:55:8:55:9 | ll [read] [next, y] | semmle.label | ll [read] [next, y] |
+| conflated.cpp:55:12:55:15 | FieldAddress [read] [y] | semmle.label | FieldAddress [read] [y] |
+| conflated.cpp:55:12:55:15 | next [read] [y] | semmle.label | next [read] [y] |
+| conflated.cpp:55:18:55:18 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| conflated.cpp:55:18:55:18 | y | semmle.label | y |
+| conflated.cpp:60:3:60:4 | ll [post update] [next, y] | semmle.label | ll [post update] [next, y] |
+| conflated.cpp:60:7:60:10 | FieldAddress [post update] [y] | semmle.label | FieldAddress [post update] [y] |
+| conflated.cpp:60:7:60:10 | next [post update] [y] | semmle.label | next [post update] [y] |
+| conflated.cpp:60:13:60:13 | y [post update] | semmle.label | y [post update] |
+| conflated.cpp:60:17:60:26 | call to user_input | semmle.label | call to user_input |
+| conflated.cpp:61:8:61:9 | ll [read] [next, y] | semmle.label | ll [read] [next, y] |
+| conflated.cpp:61:12:61:15 | FieldAddress [read] [y] | semmle.label | FieldAddress [read] [y] |
+| conflated.cpp:61:12:61:15 | next [read] [y] | semmle.label | next [read] [y] |
+| conflated.cpp:61:18:61:18 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| conflated.cpp:61:18:61:18 | y | semmle.label | y |
+| constructors.cpp:18:9:18:9 | ReturnValue | semmle.label | ReturnValue |
+| constructors.cpp:18:9:18:9 | this [a_] | semmle.label | this [a_] |
+| constructors.cpp:18:22:18:23 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| constructors.cpp:18:22:18:23 | this [read] [a_] | semmle.label | this [read] [a_] |
+| constructors.cpp:19:9:19:9 | ReturnValue | semmle.label | ReturnValue |
+| constructors.cpp:19:9:19:9 | this [b_] | semmle.label | this [b_] |
+| constructors.cpp:19:22:19:23 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| constructors.cpp:19:22:19:23 | this [read] [b_] | semmle.label | this [read] [b_] |
+| constructors.cpp:23:5:23:7 | this [post update] [a_] | semmle.label | this [post update] [a_] |
+| constructors.cpp:23:5:23:7 | this [post update] [b_] | semmle.label | this [post update] [b_] |
| constructors.cpp:23:13:23:13 | a | semmle.label | a |
| constructors.cpp:23:20:23:20 | b | semmle.label | b |
-| constructors.cpp:23:28:23:28 | Chi [a_] | semmle.label | Chi [a_] |
-| constructors.cpp:23:35:23:35 | Chi [a_] | semmle.label | Chi [a_] |
-| constructors.cpp:23:35:23:35 | Chi [b_] | semmle.label | Chi [b_] |
-| constructors.cpp:26:15:26:15 | *f [a_] | semmle.label | *f [a_] |
-| constructors.cpp:26:15:26:15 | *f [b_] | semmle.label | *f [b_] |
-| constructors.cpp:28:10:28:10 | a output argument [b_] | semmle.label | a output argument [b_] |
-| constructors.cpp:28:10:28:10 | f indirection [a_] | semmle.label | f indirection [a_] |
-| constructors.cpp:28:10:28:10 | f indirection [b_] | semmle.label | f indirection [b_] |
+| constructors.cpp:23:25:23:29 | FieldAddress [post update] | semmle.label | FieldAddress [post update] |
+| constructors.cpp:23:32:23:36 | FieldAddress [post update] | semmle.label | FieldAddress [post update] |
+| constructors.cpp:26:15:26:15 | f [a_] | semmle.label | f [a_] |
+| constructors.cpp:26:15:26:15 | f [b_] | semmle.label | f [b_] |
| constructors.cpp:28:12:28:12 | call to a | semmle.label | call to a |
-| constructors.cpp:29:10:29:10 | f indirection [b_] | semmle.label | f indirection [b_] |
+| constructors.cpp:28:12:28:12 | f [a_] | semmle.label | f [a_] |
| constructors.cpp:29:12:29:12 | call to b | semmle.label | call to b |
+| constructors.cpp:29:12:29:12 | f [b_] | semmle.label | f [b_] |
| constructors.cpp:34:11:34:20 | call to user_input | semmle.label | call to user_input |
-| constructors.cpp:34:11:34:26 | Foo output argument [a_] | semmle.label | Foo output argument [a_] |
+| constructors.cpp:34:11:34:26 | Argument this [post update] [a_] | semmle.label | Argument this [post update] [a_] |
| constructors.cpp:34:11:34:26 | call to user_input | semmle.label | call to user_input |
-| constructors.cpp:35:11:35:26 | Foo output argument [b_] | semmle.label | Foo output argument [b_] |
+| constructors.cpp:35:11:35:26 | Argument this [post update] [b_] | semmle.label | Argument this [post update] [b_] |
| constructors.cpp:35:11:35:26 | call to user_input | semmle.label | call to user_input |
| constructors.cpp:35:14:35:23 | call to user_input | semmle.label | call to user_input |
| constructors.cpp:36:11:36:20 | call to user_input | semmle.label | call to user_input |
-| constructors.cpp:36:11:36:37 | Foo output argument [a_] | semmle.label | Foo output argument [a_] |
-| constructors.cpp:36:11:36:37 | Foo output argument [b_] | semmle.label | Foo output argument [b_] |
+| constructors.cpp:36:11:36:37 | Argument this [post update] [a_] | semmle.label | Argument this [post update] [a_] |
+| constructors.cpp:36:11:36:37 | Argument this [post update] [b_] | semmle.label | Argument this [post update] [b_] |
| constructors.cpp:36:11:36:37 | call to user_input | semmle.label | call to user_input |
| constructors.cpp:36:11:36:37 | call to user_input | semmle.label | call to user_input |
| constructors.cpp:36:25:36:34 | call to user_input | semmle.label | call to user_input |
-| constructors.cpp:40:9:40:9 | f indirection [a_] | semmle.label | f indirection [a_] |
-| constructors.cpp:43:9:43:9 | g indirection [b_] | semmle.label | g indirection [b_] |
-| constructors.cpp:46:9:46:9 | h indirection [a_] | semmle.label | h indirection [a_] |
-| constructors.cpp:46:9:46:9 | h indirection [b_] | semmle.label | h indirection [b_] |
-| simple.cpp:18:9:18:9 | *#this [a_] | semmle.label | *#this [a_] |
-| simple.cpp:18:9:18:9 | *#this [b_] | semmle.label | *#this [b_] |
-| simple.cpp:18:22:18:23 | Store | semmle.label | Store |
-| simple.cpp:19:9:19:9 | *#this [b_] | semmle.label | *#this [b_] |
-| simple.cpp:19:22:19:23 | Store | semmle.label | Store |
+| constructors.cpp:40:5:40:7 | f [a_] | semmle.label | f [a_] |
+| constructors.cpp:43:5:43:7 | g [b_] | semmle.label | g [b_] |
+| constructors.cpp:46:5:46:7 | h [a_] | semmle.label | h [a_] |
+| constructors.cpp:46:5:46:7 | h [b_] | semmle.label | h [b_] |
+| qualifiers.cpp:9:21:9:25 | value | semmle.label | value |
+| qualifiers.cpp:9:30:9:33 | this [post update] [a] | semmle.label | this [post update] [a] |
+| qualifiers.cpp:9:36:9:36 | a [post update] | semmle.label | a [post update] |
+| qualifiers.cpp:12:40:12:44 | value | semmle.label | value |
+| qualifiers.cpp:12:49:12:53 | inner [post update] [a] | semmle.label | inner [post update] [a] |
+| qualifiers.cpp:12:56:12:56 | a [post update] | semmle.label | a [post update] |
+| qualifiers.cpp:13:42:13:46 | value | semmle.label | value |
+| qualifiers.cpp:13:51:13:55 | (reference dereference) [post update] [a] | semmle.label | (reference dereference) [post update] [a] |
+| qualifiers.cpp:13:51:13:55 | inner [post update] [a] | semmle.label | inner [post update] [a] |
+| qualifiers.cpp:13:57:13:57 | a [post update] | semmle.label | a [post update] |
+| qualifiers.cpp:22:11:22:18 | call to getInner [post update] [a] | semmle.label | call to getInner [post update] [a] |
+| qualifiers.cpp:22:11:22:18 | outer [post update] [inner, a] | semmle.label | outer [post update] [inner, a] |
+| qualifiers.cpp:22:23:22:23 | a [post update] | semmle.label | a [post update] |
+| qualifiers.cpp:22:27:22:36 | call to user_input | semmle.label | call to user_input |
+| qualifiers.cpp:23:10:23:14 | outer [read] [inner, a] | semmle.label | outer [read] [inner, a] |
+| qualifiers.cpp:23:16:23:20 | FieldAddress [read] [a] | semmle.label | FieldAddress [read] [a] |
+| qualifiers.cpp:23:16:23:20 | inner [read] [a] | semmle.label | inner [read] [a] |
+| qualifiers.cpp:23:23:23:23 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| qualifiers.cpp:23:23:23:23 | a | semmle.label | a |
+| qualifiers.cpp:27:11:27:18 | call to getInner [post update] [a] | semmle.label | call to getInner [post update] [a] |
+| qualifiers.cpp:27:11:27:18 | outer [post update] [inner, a] | semmle.label | outer [post update] [inner, a] |
+| qualifiers.cpp:27:23:27:26 | call to getInner [post update] [a] | semmle.label | call to getInner [post update] [a] |
+| qualifiers.cpp:27:23:27:26 | call to user_input | semmle.label | call to user_input |
+| qualifiers.cpp:27:28:27:37 | call to user_input | semmle.label | call to user_input |
+| qualifiers.cpp:28:10:28:14 | outer [read] [inner, a] | semmle.label | outer [read] [inner, a] |
+| qualifiers.cpp:28:16:28:20 | FieldAddress [read] [a] | semmle.label | FieldAddress [read] [a] |
+| qualifiers.cpp:28:16:28:20 | inner [read] [a] | semmle.label | inner [read] [a] |
+| qualifiers.cpp:28:23:28:23 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| qualifiers.cpp:28:23:28:23 | a | semmle.label | a |
+| qualifiers.cpp:32:5:32:15 | call to getInner [post update] [a] | semmle.label | call to getInner [post update] [a] |
+| qualifiers.cpp:32:5:32:15 | call to user_input | semmle.label | call to user_input |
+| qualifiers.cpp:32:23:32:30 | call to getInner [post update] [a] | semmle.label | call to getInner [post update] [a] |
+| qualifiers.cpp:32:23:32:30 | outer [post update] [inner, a] | semmle.label | outer [post update] [inner, a] |
+| qualifiers.cpp:32:35:32:44 | call to user_input | semmle.label | call to user_input |
+| qualifiers.cpp:33:10:33:14 | outer [read] [inner, a] | semmle.label | outer [read] [inner, a] |
+| qualifiers.cpp:33:16:33:20 | FieldAddress [read] [a] | semmle.label | FieldAddress [read] [a] |
+| qualifiers.cpp:33:16:33:20 | inner [read] [a] | semmle.label | inner [read] [a] |
+| qualifiers.cpp:33:23:33:23 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| qualifiers.cpp:33:23:33:23 | a | semmle.label | a |
+| qualifiers.cpp:37:5:37:17 | * ... [post update] [a] | semmle.label | * ... [post update] [a] |
+| qualifiers.cpp:37:5:37:17 | call to user_input | semmle.label | call to user_input |
+| qualifiers.cpp:37:26:37:33 | call to getInner [post update] [a] | semmle.label | call to getInner [post update] [a] |
+| qualifiers.cpp:37:26:37:33 | outer [post update] [inner, a] | semmle.label | outer [post update] [inner, a] |
+| qualifiers.cpp:37:38:37:47 | call to user_input | semmle.label | call to user_input |
+| qualifiers.cpp:38:10:38:14 | outer [read] [inner, a] | semmle.label | outer [read] [inner, a] |
+| qualifiers.cpp:38:16:38:20 | FieldAddress [read] [a] | semmle.label | FieldAddress [read] [a] |
+| qualifiers.cpp:38:16:38:20 | inner [read] [a] | semmle.label | inner [read] [a] |
+| qualifiers.cpp:38:23:38:23 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| qualifiers.cpp:38:23:38:23 | a | semmle.label | a |
+| qualifiers.cpp:42:6:42:22 | * ... [post update] [a] | semmle.label | * ... [post update] [a] |
+| qualifiers.cpp:42:13:42:20 | call to getInner [post update] [a] | semmle.label | call to getInner [post update] [a] |
+| qualifiers.cpp:42:13:42:20 | outer [post update] [inner, a] | semmle.label | outer [post update] [inner, a] |
+| qualifiers.cpp:42:25:42:25 | a [post update] | semmle.label | a [post update] |
+| qualifiers.cpp:42:29:42:38 | call to user_input | semmle.label | call to user_input |
+| qualifiers.cpp:43:10:43:14 | outer [read] [inner, a] | semmle.label | outer [read] [inner, a] |
+| qualifiers.cpp:43:16:43:20 | FieldAddress [read] [a] | semmle.label | FieldAddress [read] [a] |
+| qualifiers.cpp:43:16:43:20 | inner [read] [a] | semmle.label | inner [read] [a] |
+| qualifiers.cpp:43:23:43:23 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| qualifiers.cpp:43:23:43:23 | a | semmle.label | a |
+| qualifiers.cpp:47:15:47:22 | & ... [post update] [inner, a] | semmle.label | & ... [post update] [inner, a] |
+| qualifiers.cpp:47:15:47:22 | call to getInner [post update] [a] | semmle.label | call to getInner [post update] [a] |
+| qualifiers.cpp:47:27:47:27 | a [post update] | semmle.label | a [post update] |
+| qualifiers.cpp:47:31:47:40 | call to user_input | semmle.label | call to user_input |
+| qualifiers.cpp:48:10:48:14 | outer [read] [inner, a] | semmle.label | outer [read] [inner, a] |
+| qualifiers.cpp:48:16:48:20 | FieldAddress [read] [a] | semmle.label | FieldAddress [read] [a] |
+| qualifiers.cpp:48:16:48:20 | inner [read] [a] | semmle.label | inner [read] [a] |
+| qualifiers.cpp:48:23:48:23 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| qualifiers.cpp:48:23:48:23 | a | semmle.label | a |
+| realistic.cpp:53:9:53:11 | foo [post update] [bar, baz, userInput, bufferLen] | semmle.label | foo [post update] [bar, baz, userInput, bufferLen] |
+| realistic.cpp:53:9:53:18 | access to array [post update] [baz, userInput, bufferLen] | semmle.label | access to array [post update] [baz, userInput, bufferLen] |
+| realistic.cpp:53:13:53:15 | bar [post update] [baz, userInput, bufferLen] | semmle.label | bar [post update] [baz, userInput, bufferLen] |
+| realistic.cpp:53:20:53:22 | FieldAddress [post update] [userInput, bufferLen] | semmle.label | FieldAddress [post update] [userInput, bufferLen] |
+| realistic.cpp:53:20:53:22 | baz [post update] [userInput, bufferLen] | semmle.label | baz [post update] [userInput, bufferLen] |
+| realistic.cpp:53:25:53:33 | userInput [post update] [bufferLen] | semmle.label | userInput [post update] [bufferLen] |
+| realistic.cpp:53:35:53:43 | bufferLen [post update] | semmle.label | bufferLen [post update] |
+| realistic.cpp:53:47:53:66 | (size_t)... | semmle.label | (size_t)... |
+| realistic.cpp:53:55:53:64 | call to user_input | semmle.label | call to user_input |
+| realistic.cpp:61:14:61:55 | (void *)... | semmle.label | (void *)... |
+| realistic.cpp:61:21:61:23 | foo [read] [bar, baz, userInput, bufferLen] | semmle.label | foo [read] [bar, baz, userInput, bufferLen] |
+| realistic.cpp:61:21:61:30 | access to array [read] [baz, userInput, bufferLen] | semmle.label | access to array [read] [baz, userInput, bufferLen] |
+| realistic.cpp:61:25:61:27 | bar [read] [baz, userInput, bufferLen] | semmle.label | bar [read] [baz, userInput, bufferLen] |
+| realistic.cpp:61:32:61:34 | FieldAddress [read] [userInput, bufferLen] | semmle.label | FieldAddress [read] [userInput, bufferLen] |
+| realistic.cpp:61:32:61:34 | baz [read] [userInput, bufferLen] | semmle.label | baz [read] [userInput, bufferLen] |
+| realistic.cpp:61:37:61:45 | userInput [read] [bufferLen] | semmle.label | userInput [read] [bufferLen] |
+| realistic.cpp:61:47:61:55 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| simple.cpp:18:9:18:9 | ReturnValue | semmle.label | ReturnValue |
+| simple.cpp:18:9:18:9 | this [a_] | semmle.label | this [a_] |
+| simple.cpp:18:22:18:23 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| simple.cpp:18:22:18:23 | this [read] [a_] | semmle.label | this [read] [a_] |
+| simple.cpp:19:9:19:9 | ReturnValue | semmle.label | ReturnValue |
+| simple.cpp:19:9:19:9 | this [b_] | semmle.label | this [b_] |
+| simple.cpp:19:22:19:23 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| simple.cpp:19:22:19:23 | this [read] [b_] | semmle.label | this [read] [b_] |
| simple.cpp:20:19:20:19 | a | semmle.label | a |
-| simple.cpp:20:24:20:29 | Chi [a_] | semmle.label | Chi [a_] |
+| simple.cpp:20:24:20:25 | a_ [post update] | semmle.label | a_ [post update] |
+| simple.cpp:20:24:20:25 | this [post update] [a_] | semmle.label | this [post update] [a_] |
| simple.cpp:21:10:21:13 | *#this [a_] | semmle.label | *#this [a_] |
+| simple.cpp:21:10:21:13 | ReturnIndirection [a_] | semmle.label | ReturnIndirection [a_] |
| simple.cpp:21:19:21:19 | b | semmle.label | b |
-| simple.cpp:21:24:21:29 | Chi [a_] | semmle.label | Chi [a_] |
-| simple.cpp:21:24:21:29 | Chi [b_] | semmle.label | Chi [b_] |
-| simple.cpp:26:15:26:15 | *f [a_] | semmle.label | *f [a_] |
-| simple.cpp:26:15:26:15 | *f [b_] | semmle.label | *f [b_] |
-| simple.cpp:28:10:28:10 | a output argument [b_] | semmle.label | a output argument [b_] |
-| simple.cpp:28:10:28:10 | f indirection [a_] | semmle.label | f indirection [a_] |
-| simple.cpp:28:10:28:10 | f indirection [b_] | semmle.label | f indirection [b_] |
+| simple.cpp:21:24:21:25 | b_ [post update] | semmle.label | b_ [post update] |
+| simple.cpp:21:24:21:25 | this [post update] [b_] | semmle.label | this [post update] [b_] |
+| simple.cpp:26:15:26:15 | f [a_] | semmle.label | f [a_] |
+| simple.cpp:26:15:26:15 | f [b_] | semmle.label | f [b_] |
| simple.cpp:28:12:28:12 | call to a | semmle.label | call to a |
-| simple.cpp:29:10:29:10 | f indirection [b_] | semmle.label | f indirection [b_] |
+| simple.cpp:28:12:28:12 | f [a_] | semmle.label | f [a_] |
| simple.cpp:29:12:29:12 | call to b | semmle.label | call to b |
-| simple.cpp:39:5:39:5 | setA output argument [a_] | semmle.label | setA output argument [a_] |
+| simple.cpp:29:12:29:12 | f [b_] | semmle.label | f [b_] |
| simple.cpp:39:7:39:10 | call to user_input | semmle.label | call to user_input |
+| simple.cpp:39:7:39:10 | f [post update] [a_] | semmle.label | f [post update] [a_] |
| simple.cpp:39:12:39:21 | call to user_input | semmle.label | call to user_input |
-| simple.cpp:40:5:40:5 | setB output argument [b_] | semmle.label | setB output argument [b_] |
| simple.cpp:40:7:40:10 | call to user_input | semmle.label | call to user_input |
+| simple.cpp:40:7:40:10 | g [post update] [b_] | semmle.label | g [post update] [b_] |
| simple.cpp:40:12:40:21 | call to user_input | semmle.label | call to user_input |
-| simple.cpp:41:5:41:5 | setA output argument [a_] | semmle.label | setA output argument [a_] |
| simple.cpp:41:7:41:10 | call to user_input | semmle.label | call to user_input |
+| simple.cpp:41:7:41:10 | h [post update] [a_] | semmle.label | h [post update] [a_] |
| simple.cpp:41:12:41:21 | call to user_input | semmle.label | call to user_input |
| simple.cpp:42:5:42:5 | h indirection [a_] | semmle.label | h indirection [a_] |
| simple.cpp:42:5:42:5 | setB output argument [a_] | semmle.label | setB output argument [a_] |
-| simple.cpp:42:5:42:5 | setB output argument [b_] | semmle.label | setB output argument [b_] |
| simple.cpp:42:7:42:10 | call to user_input | semmle.label | call to user_input |
+| simple.cpp:42:7:42:10 | h [post update] [b_] | semmle.label | h [post update] [b_] |
| simple.cpp:42:12:42:21 | call to user_input | semmle.label | call to user_input |
-| simple.cpp:45:9:45:9 | f indirection [a_] | semmle.label | f indirection [a_] |
-| simple.cpp:48:9:48:9 | g indirection [b_] | semmle.label | g indirection [b_] |
-| simple.cpp:51:9:51:9 | h indirection [a_] | semmle.label | h indirection [a_] |
-| simple.cpp:51:9:51:9 | h indirection [b_] | semmle.label | h indirection [b_] |
-| simple.cpp:65:5:65:22 | Store [i] | semmle.label | Store [i] |
+| simple.cpp:45:5:45:7 | f [a_] | semmle.label | f [a_] |
+| simple.cpp:48:5:48:7 | g [b_] | semmle.label | g [b_] |
+| simple.cpp:51:5:51:7 | h [a_] | semmle.label | h [a_] |
+| simple.cpp:51:5:51:7 | h [b_] | semmle.label | h [b_] |
+| simple.cpp:65:5:65:5 | a [post update] [i] | semmle.label | a [post update] [i] |
+| simple.cpp:65:7:65:7 | i [post update] | semmle.label | i [post update] |
| simple.cpp:65:11:65:20 | call to user_input | semmle.label | call to user_input |
-| simple.cpp:66:12:66:12 | Store [i] | semmle.label | Store [i] |
+| simple.cpp:67:10:67:11 | a2 [read] [i] | semmle.label | a2 [read] [i] |
+| simple.cpp:67:13:67:13 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| simple.cpp:67:13:67:13 | i | semmle.label | i |
-| simple.cpp:78:9:78:15 | *#this [f1] | semmle.label | *#this [f1] |
-| simple.cpp:79:19:79:20 | Store | semmle.label | Store |
-| simple.cpp:83:9:83:28 | Store [f1] | semmle.label | Store [f1] |
+| simple.cpp:78:9:78:15 | ReturnValue | semmle.label | ReturnValue |
+| simple.cpp:78:9:78:15 | this [f2, f1] | semmle.label | this [f2, f1] |
+| simple.cpp:79:16:79:17 | f2 [read] [f1] | semmle.label | f2 [read] [f1] |
+| simple.cpp:79:16:79:17 | this [read] [f2, f1] | semmle.label | this [read] [f2, f1] |
+| simple.cpp:79:19:79:20 | FieldAddress [read] | semmle.label | FieldAddress [read] |
+| simple.cpp:83:9:83:10 | f2 [post update] [f1] | semmle.label | f2 [post update] [f1] |
+| simple.cpp:83:9:83:10 | this [post update] [f2, f1] | semmle.label | this [post update] [f2, f1] |
+| simple.cpp:83:12:83:13 | f1 [post update] | semmle.label | f1 [post update] |
| simple.cpp:83:17:83:26 | call to user_input | semmle.label | call to user_input |
| simple.cpp:84:14:84:20 | call to getf2f1 | semmle.label | call to getf2f1 |
-| simple.cpp:84:14:84:20 | this indirection [f1] | semmle.label | this indirection [f1] |
-| simple.cpp:92:5:92:22 | Store [i] | semmle.label | Store [i] |
+| simple.cpp:84:14:84:20 | this [f2, f1] | semmle.label | this [f2, f1] |
+| simple.cpp:92:5:92:5 | a [post update] [i] | semmle.label | a [post update] [i] |
+| simple.cpp:92:7:92:7 | i [post update] | semmle.label | i [post update] |
| simple.cpp:92:11:92:20 | call to user_input | semmle.label | call to user_input |
-| simple.cpp:93:20:93:20 | Store [i] | semmle.label | Store [i] |
+| simple.cpp:94:10:94:11 | a2 [read] [i] | semmle.label | a2 [read] [i] |
+| simple.cpp:94:13:94:13 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| simple.cpp:94:13:94:13 | i | semmle.label | i |
| struct_init.c:14:24:14:25 | *ab [a] | semmle.label | *ab [a] |
+| struct_init.c:14:24:14:25 | ab [a] | semmle.label | ab [a] |
+| struct_init.c:15:8:15:9 | ab [read] [a] | semmle.label | ab [read] [a] |
+| struct_init.c:15:12:15:12 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| struct_init.c:15:12:15:12 | a | semmle.label | a |
-| struct_init.c:20:20:20:29 | Chi [a] | semmle.label | Chi [a] |
+| struct_init.c:20:13:20:14 | VariableAddress [post update] [a] | semmle.label | VariableAddress [post update] [a] |
+| struct_init.c:20:17:20:36 | FieldAddress [post update] | semmle.label | FieldAddress [post update] |
| struct_init.c:20:20:20:29 | call to user_input | semmle.label | call to user_input |
+| struct_init.c:22:8:22:9 | ab [read] [a] | semmle.label | ab [read] [a] |
+| struct_init.c:22:11:22:11 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| struct_init.c:22:11:22:11 | a | semmle.label | a |
| struct_init.c:24:10:24:12 | & ... indirection [a] | semmle.label | & ... indirection [a] |
-| struct_init.c:27:7:27:16 | Chi [a] | semmle.label | Chi [a] |
+| struct_init.c:26:16:26:20 | VariableAddress [post update] [nestedAB, a] | semmle.label | VariableAddress [post update] [nestedAB, a] |
+| struct_init.c:26:23:29:3 | FieldAddress [post update] [a] | semmle.label | FieldAddress [post update] [a] |
+| struct_init.c:27:5:27:23 | FieldAddress [post update] | semmle.label | FieldAddress [post update] |
| struct_init.c:27:7:27:16 | call to user_input | semmle.label | call to user_input |
+| struct_init.c:31:8:31:12 | outer [read] [nestedAB, a] | semmle.label | outer [read] [nestedAB, a] |
+| struct_init.c:31:14:31:21 | nestedAB [read] [a] | semmle.label | nestedAB [read] [a] |
+| struct_init.c:31:23:31:23 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| struct_init.c:31:23:31:23 | a | semmle.label | a |
-| struct_init.c:36:10:36:24 | & ... indirection [a] | semmle.label | & ... indirection [a] |
+| struct_init.c:36:3:36:8 | & ... [a] | semmle.label | & ... [a] |
+| struct_init.c:36:11:36:15 | outer [read] [nestedAB, a] | semmle.label | outer [read] [nestedAB, a] |
+| struct_init.c:36:17:36:24 | nestedAB [read] [a] | semmle.label | nestedAB [read] [a] |
subpaths
-| A.cpp:55:8:55:10 | new | A.cpp:27:17:27:17 | c | A.cpp:27:22:27:32 | Chi [c] | A.cpp:55:5:55:5 | set output argument [c] |
-| A.cpp:56:10:56:10 | b indirection [c] | A.cpp:28:8:28:10 | *#this [c] | A.cpp:28:29:28:29 | Store | A.cpp:56:13:56:15 | call to get |
-| A.cpp:57:10:57:25 | new indirection [c] | A.cpp:28:8:28:10 | *#this [c] | A.cpp:28:29:28:29 | Store | A.cpp:57:28:57:30 | call to get |
-| A.cpp:57:11:57:24 | new | A.cpp:23:10:23:10 | c | A.cpp:25:7:25:17 | Chi [c] | A.cpp:57:11:57:24 | B output argument [c] |
-| A.cpp:126:8:126:10 | new | A.cpp:27:17:27:17 | c | A.cpp:27:22:27:32 | Chi [c] | A.cpp:126:5:126:5 | set output argument [c] |
-| A.cpp:151:12:151:24 | b | A.cpp:140:13:140:13 | b | A.cpp:143:7:143:31 | Chi [b] | A.cpp:151:12:151:24 | D output argument [b] |
-| by_reference.cpp:20:11:20:21 | value | by_reference.cpp:15:26:15:30 | value | by_reference.cpp:16:5:16:19 | Chi [a] | by_reference.cpp:20:5:20:8 | setDirectly output argument [a] |
-| by_reference.cpp:24:5:24:17 | value | by_reference.cpp:11:48:11:52 | value | by_reference.cpp:12:5:12:16 | Chi [a] | by_reference.cpp:24:19:24:22 | nonMemberSetA output argument [a] |
-| by_reference.cpp:40:12:40:15 | this indirection [a] | by_reference.cpp:35:9:35:19 | *#this [a] | by_reference.cpp:36:18:36:18 | Store | by_reference.cpp:40:18:40:28 | call to getDirectly |
-| by_reference.cpp:44:26:44:29 | this indirection [a] | by_reference.cpp:31:46:31:46 | *s [a] | by_reference.cpp:32:15:32:15 | Store | by_reference.cpp:44:12:44:24 | call to nonMemberGetA |
-| by_reference.cpp:50:5:50:15 | call to user_input | by_reference.cpp:15:26:15:30 | value | by_reference.cpp:16:5:16:19 | Chi [a] | by_reference.cpp:50:3:50:3 | setDirectly output argument [a] |
-| by_reference.cpp:51:8:51:8 | s indirection [a] | by_reference.cpp:35:9:35:19 | *#this [a] | by_reference.cpp:36:18:36:18 | Store | by_reference.cpp:51:10:51:20 | call to getDirectly |
-| by_reference.cpp:56:5:56:17 | call to user_input | by_reference.cpp:19:28:19:32 | value | by_reference.cpp:20:5:20:8 | Chi [a] | by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] |
-| by_reference.cpp:57:8:57:8 | s indirection [a] | by_reference.cpp:39:9:39:21 | *#this [a] | by_reference.cpp:40:18:40:28 | Store | by_reference.cpp:57:10:57:22 | call to getIndirectly |
-| by_reference.cpp:62:5:62:23 | call to user_input | by_reference.cpp:23:34:23:38 | value | by_reference.cpp:24:19:24:22 | Chi [a] | by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] |
-| by_reference.cpp:63:8:63:8 | s indirection [a] | by_reference.cpp:43:9:43:27 | *#this [a] | by_reference.cpp:44:12:44:24 | Store | by_reference.cpp:63:10:63:28 | call to getThroughNonMember |
-| by_reference.cpp:68:3:68:15 | call to user_input | by_reference.cpp:11:48:11:52 | value | by_reference.cpp:12:5:12:16 | Chi [a] | by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] |
-| by_reference.cpp:69:22:69:23 | & ... indirection [a] | by_reference.cpp:31:46:31:46 | *s [a] | by_reference.cpp:32:15:32:15 | Store | by_reference.cpp:69:8:69:20 | call to nonMemberGetA |
-| complex.cpp:42:16:42:16 | f indirection [a_] | complex.cpp:9:7:9:7 | *#this [a_] | complex.cpp:9:20:9:21 | Store | complex.cpp:42:18:42:18 | call to a |
-| complex.cpp:42:16:42:16 | f indirection [b_] | complex.cpp:9:7:9:7 | *#this [b_] | complex.cpp:9:7:9:7 | *#this [b_] | complex.cpp:42:16:42:16 | a output argument [b_] |
-| complex.cpp:43:16:43:16 | f indirection [b_] | complex.cpp:10:7:10:7 | *#this [b_] | complex.cpp:10:20:10:21 | Store | complex.cpp:43:18:43:18 | call to b |
-| complex.cpp:53:14:53:17 | call to user_input | complex.cpp:11:17:11:17 | a | complex.cpp:11:22:11:27 | Chi [a_] | complex.cpp:53:12:53:12 | setA output argument [a_] |
-| complex.cpp:54:14:54:17 | call to user_input | complex.cpp:12:17:12:17 | b | complex.cpp:12:22:12:27 | Chi [b_] | complex.cpp:54:12:54:12 | setB output argument [b_] |
-| complex.cpp:55:14:55:17 | call to user_input | complex.cpp:11:17:11:17 | a | complex.cpp:11:22:11:27 | Chi [a_] | complex.cpp:55:12:55:12 | setA output argument [a_] |
-| complex.cpp:56:12:56:12 | f indirection [a_] | complex.cpp:12:8:12:11 | *#this [a_] | complex.cpp:12:22:12:27 | Chi [a_] | complex.cpp:56:12:56:12 | setB output argument [a_] |
-| complex.cpp:56:14:56:17 | call to user_input | complex.cpp:12:17:12:17 | b | complex.cpp:12:22:12:27 | Chi [b_] | complex.cpp:56:12:56:12 | setB output argument [b_] |
-| constructors.cpp:28:10:28:10 | f indirection [a_] | constructors.cpp:18:9:18:9 | *#this [a_] | constructors.cpp:18:22:18:23 | Store | constructors.cpp:28:12:28:12 | call to a |
-| constructors.cpp:28:10:28:10 | f indirection [b_] | constructors.cpp:18:9:18:9 | *#this [b_] | constructors.cpp:18:9:18:9 | *#this [b_] | constructors.cpp:28:10:28:10 | a output argument [b_] |
-| constructors.cpp:29:10:29:10 | f indirection [b_] | constructors.cpp:19:9:19:9 | *#this [b_] | constructors.cpp:19:22:19:23 | Store | constructors.cpp:29:12:29:12 | call to b |
-| constructors.cpp:34:11:34:26 | call to user_input | constructors.cpp:23:13:23:13 | a | constructors.cpp:23:35:23:35 | Chi [a_] | constructors.cpp:34:11:34:26 | Foo output argument [a_] |
-| constructors.cpp:35:11:35:26 | call to user_input | constructors.cpp:23:20:23:20 | b | constructors.cpp:23:35:23:35 | Chi [b_] | constructors.cpp:35:11:35:26 | Foo output argument [b_] |
-| constructors.cpp:36:11:36:37 | call to user_input | constructors.cpp:23:13:23:13 | a | constructors.cpp:23:35:23:35 | Chi [a_] | constructors.cpp:36:11:36:37 | Foo output argument [a_] |
-| constructors.cpp:36:11:36:37 | call to user_input | constructors.cpp:23:20:23:20 | b | constructors.cpp:23:35:23:35 | Chi [b_] | constructors.cpp:36:11:36:37 | Foo output argument [b_] |
-| simple.cpp:28:10:28:10 | f indirection [a_] | simple.cpp:18:9:18:9 | *#this [a_] | simple.cpp:18:22:18:23 | Store | simple.cpp:28:12:28:12 | call to a |
-| simple.cpp:28:10:28:10 | f indirection [b_] | simple.cpp:18:9:18:9 | *#this [b_] | simple.cpp:18:9:18:9 | *#this [b_] | simple.cpp:28:10:28:10 | a output argument [b_] |
-| simple.cpp:29:10:29:10 | f indirection [b_] | simple.cpp:19:9:19:9 | *#this [b_] | simple.cpp:19:22:19:23 | Store | simple.cpp:29:12:29:12 | call to b |
-| simple.cpp:39:7:39:10 | call to user_input | simple.cpp:20:19:20:19 | a | simple.cpp:20:24:20:29 | Chi [a_] | simple.cpp:39:5:39:5 | setA output argument [a_] |
-| simple.cpp:40:7:40:10 | call to user_input | simple.cpp:21:19:21:19 | b | simple.cpp:21:24:21:29 | Chi [b_] | simple.cpp:40:5:40:5 | setB output argument [b_] |
-| simple.cpp:41:7:41:10 | call to user_input | simple.cpp:20:19:20:19 | a | simple.cpp:20:24:20:29 | Chi [a_] | simple.cpp:41:5:41:5 | setA output argument [a_] |
-| simple.cpp:42:5:42:5 | h indirection [a_] | simple.cpp:21:10:21:13 | *#this [a_] | simple.cpp:21:24:21:29 | Chi [a_] | simple.cpp:42:5:42:5 | setB output argument [a_] |
-| simple.cpp:42:7:42:10 | call to user_input | simple.cpp:21:19:21:19 | b | simple.cpp:21:24:21:29 | Chi [b_] | simple.cpp:42:5:42:5 | setB output argument [b_] |
-| simple.cpp:84:14:84:20 | this indirection [f1] | simple.cpp:78:9:78:15 | *#this [f1] | simple.cpp:79:19:79:20 | Store | simple.cpp:84:14:84:20 | call to getf2f1 |
+| A.cpp:31:14:31:21 | c | A.cpp:23:10:23:10 | c | A.cpp:25:7:25:10 | this [post update] [c] | A.cpp:31:14:31:21 | new [post update] [c] |
+| A.cpp:48:12:48:18 | c | A.cpp:29:23:29:23 | c | A.cpp:29:15:29:18 | ReturnValue [c] | A.cpp:48:12:48:18 | call to make [c] |
+| A.cpp:55:8:55:10 | new | A.cpp:27:17:27:17 | c | A.cpp:27:22:27:25 | this [post update] [c] | A.cpp:55:8:55:10 | b [post update] [c] |
+| A.cpp:56:13:56:15 | b [c] | A.cpp:28:8:28:10 | this [c] | A.cpp:28:8:28:10 | ReturnValue | A.cpp:56:13:56:15 | call to get |
+| A.cpp:57:11:57:24 | new | A.cpp:23:10:23:10 | c | A.cpp:25:7:25:10 | this [post update] [c] | A.cpp:57:11:57:24 | new [post update] [c] |
+| A.cpp:57:28:57:30 | new [c] | A.cpp:28:8:28:10 | this [c] | A.cpp:28:8:28:10 | ReturnValue | A.cpp:57:28:57:30 | call to get |
+| A.cpp:64:10:64:15 | new | A.cpp:85:26:85:26 | c | A.cpp:85:9:85:14 | ReturnValue [c] | A.cpp:64:10:64:15 | call to setOnB [c] |
+| A.cpp:73:10:73:19 | new | A.cpp:78:27:78:27 | c | A.cpp:78:6:78:15 | ReturnValue [c] | A.cpp:73:10:73:19 | call to setOnBWrap [c] |
+| A.cpp:81:10:81:15 | c | A.cpp:85:26:85:26 | c | A.cpp:85:9:85:14 | ReturnValue [c] | A.cpp:81:10:81:15 | call to setOnB [c] |
+| A.cpp:90:11:90:13 | c | A.cpp:27:17:27:17 | c | A.cpp:27:22:27:25 | this [post update] [c] | A.cpp:90:11:90:13 | b2 [post update] [c] |
+| A.cpp:126:8:126:10 | new | A.cpp:27:17:27:17 | c | A.cpp:27:22:27:25 | this [post update] [c] | A.cpp:126:8:126:10 | b [post update] [c] |
+| A.cpp:151:12:151:24 | b | A.cpp:140:13:140:13 | b | A.cpp:143:7:143:10 | this [post update] [b] | A.cpp:151:12:151:24 | new [post update] [b] |
+| A.cpp:160:18:160:60 | b | A.cpp:181:15:181:21 | newHead | A.cpp:183:7:183:10 | this [post update] [head] | A.cpp:160:18:160:60 | new [post update] [head] |
+| A.cpp:161:18:161:40 | l1 [head] | A.cpp:181:32:181:35 | next [head] | A.cpp:184:7:184:10 | this [post update] [next, head] | A.cpp:161:18:161:40 | new [post update] [next, head] |
+| A.cpp:162:18:162:40 | l2 [next, head] | A.cpp:181:32:181:35 | next [next, head] | A.cpp:184:7:184:10 | this [post update] [next, next, head] | A.cpp:162:18:162:40 | new [post update] [next, next, head] |
+| B.cpp:7:16:7:35 | e | B.cpp:33:16:33:17 | e1 | B.cpp:35:7:35:10 | this [post update] [elem1] | B.cpp:7:16:7:35 | new [post update] [elem1] |
+| B.cpp:8:16:8:27 | b1 [elem1] | B.cpp:44:16:44:17 | b1 [elem1] | B.cpp:46:7:46:10 | this [post update] [box1, elem1] | B.cpp:8:16:8:27 | new [post update] [box1, elem1] |
+| B.cpp:16:16:16:38 | e | B.cpp:33:26:33:27 | e2 | B.cpp:36:7:36:10 | this [post update] [elem2] | B.cpp:16:16:16:38 | new [post update] [elem2] |
+| B.cpp:17:16:17:27 | b1 [elem2] | B.cpp:44:16:44:17 | b1 [elem2] | B.cpp:46:7:46:10 | this [post update] [box1, elem2] | B.cpp:17:16:17:27 | new [post update] [box1, elem2] |
+| D.cpp:22:14:22:20 | b2 [box, elem] | D.cpp:17:11:17:17 | this [box, elem] | D.cpp:17:11:17:17 | ReturnValue [elem] | D.cpp:22:14:22:20 | call to getBox1 [elem] |
+| D.cpp:22:25:22:31 | call to getBox1 [elem] | D.cpp:10:11:10:17 | this [elem] | D.cpp:10:11:10:17 | ReturnValue | D.cpp:22:25:22:31 | call to getElem |
+| D.cpp:37:13:37:19 | e | D.cpp:11:24:11:24 | e | D.cpp:11:29:11:32 | this [post update] [elem] | D.cpp:37:13:37:19 | box [post update] [elem] |
+| D.cpp:51:19:51:25 | e | D.cpp:11:24:11:24 | e | D.cpp:11:29:11:32 | this [post update] [elem] | D.cpp:51:19:51:25 | call to getBox1 [post update] [elem] |
+| by_reference.cpp:20:11:20:21 | value | by_reference.cpp:15:26:15:30 | value | by_reference.cpp:16:5:16:8 | this [post update] [a] | by_reference.cpp:20:11:20:21 | this [post update] [a] |
+| by_reference.cpp:24:5:24:17 | value | by_reference.cpp:11:48:11:52 | value | by_reference.cpp:12:5:12:5 | s [post update] [a] | by_reference.cpp:24:5:24:17 | this [post update] [a] |
+| by_reference.cpp:40:18:40:28 | this [a] | by_reference.cpp:35:9:35:19 | this [a] | by_reference.cpp:35:9:35:19 | ReturnValue | by_reference.cpp:40:18:40:28 | call to getDirectly |
+| by_reference.cpp:44:12:44:24 | this [a] | by_reference.cpp:31:46:31:46 | s [a] | by_reference.cpp:31:16:31:28 | ReturnValue | by_reference.cpp:44:12:44:24 | call to nonMemberGetA |
+| by_reference.cpp:50:5:50:15 | call to user_input | by_reference.cpp:15:26:15:30 | value | by_reference.cpp:16:5:16:8 | this [post update] [a] | by_reference.cpp:50:5:50:15 | s [post update] [a] |
+| by_reference.cpp:51:8:51:8 | s indirection [a] | by_reference.cpp:35:9:35:19 | *#this [a] | by_reference.cpp:35:9:35:19 | ReturnValue | by_reference.cpp:51:10:51:20 | call to getDirectly |
+| by_reference.cpp:56:5:56:17 | call to user_input | by_reference.cpp:19:28:19:32 | value | by_reference.cpp:20:5:20:8 | this [post update] [a] | by_reference.cpp:56:5:56:17 | s [post update] [a] |
+| by_reference.cpp:56:5:56:17 | call to user_input | by_reference.cpp:19:28:19:32 | value | by_reference.cpp:20:11:20:21 | this [post update] [a] | by_reference.cpp:56:5:56:17 | s [post update] [a] |
+| by_reference.cpp:57:8:57:8 | s indirection [a] | by_reference.cpp:39:9:39:21 | *#this [a] | by_reference.cpp:39:9:39:21 | ReturnValue | by_reference.cpp:57:10:57:22 | call to getIndirectly |
+| by_reference.cpp:62:5:62:23 | call to user_input | by_reference.cpp:23:34:23:38 | value | by_reference.cpp:24:5:24:17 | this [post update] [a] | by_reference.cpp:62:5:62:23 | s [post update] [a] |
+| by_reference.cpp:62:5:62:23 | call to user_input | by_reference.cpp:23:34:23:38 | value | by_reference.cpp:24:19:24:22 | this [post update] [a] | by_reference.cpp:62:5:62:23 | s [post update] [a] |
+| by_reference.cpp:63:8:63:8 | s indirection [a] | by_reference.cpp:43:9:43:27 | *#this [a] | by_reference.cpp:43:9:43:27 | ReturnValue | by_reference.cpp:63:10:63:28 | call to getThroughNonMember |
+| by_reference.cpp:68:3:68:15 | call to user_input | by_reference.cpp:11:48:11:52 | value | by_reference.cpp:12:5:12:5 | s [post update] [a] | by_reference.cpp:68:3:68:15 | & ... [post update] [a] |
+| by_reference.cpp:69:22:69:23 | & ... indirection [a] | by_reference.cpp:31:46:31:46 | *s [a] | by_reference.cpp:31:16:31:28 | ReturnValue | by_reference.cpp:69:8:69:20 | call to nonMemberGetA |
+| complex.cpp:42:18:42:18 | f [a_] | complex.cpp:9:7:9:7 | this [a_] | complex.cpp:9:7:9:7 | ReturnValue | complex.cpp:42:18:42:18 | call to a |
+| complex.cpp:43:18:43:18 | f [b_] | complex.cpp:10:7:10:7 | this [b_] | complex.cpp:10:7:10:7 | ReturnValue | complex.cpp:43:18:43:18 | call to b |
+| complex.cpp:53:14:53:17 | call to user_input | complex.cpp:11:17:11:17 | a | complex.cpp:11:22:11:23 | this [post update] [a_] | complex.cpp:53:14:53:17 | f [post update] [a_] |
+| complex.cpp:54:14:54:17 | call to user_input | complex.cpp:12:17:12:17 | b | complex.cpp:12:22:12:23 | this [post update] [b_] | complex.cpp:54:14:54:17 | f [post update] [b_] |
+| complex.cpp:55:14:55:17 | call to user_input | complex.cpp:11:17:11:17 | a | complex.cpp:11:22:11:23 | this [post update] [a_] | complex.cpp:55:14:55:17 | f [post update] [a_] |
+| complex.cpp:56:14:56:17 | call to user_input | complex.cpp:12:17:12:17 | b | complex.cpp:12:22:12:23 | this [post update] [b_] | complex.cpp:56:14:56:17 | f [post update] [b_] |
+| constructors.cpp:28:12:28:12 | f [a_] | constructors.cpp:18:9:18:9 | this [a_] | constructors.cpp:18:9:18:9 | ReturnValue | constructors.cpp:28:12:28:12 | call to a |
+| constructors.cpp:29:12:29:12 | f [b_] | constructors.cpp:19:9:19:9 | this [b_] | constructors.cpp:19:9:19:9 | ReturnValue | constructors.cpp:29:12:29:12 | call to b |
+| constructors.cpp:34:11:34:26 | call to user_input | constructors.cpp:23:13:23:13 | a | constructors.cpp:23:5:23:7 | this [post update] [a_] | constructors.cpp:34:11:34:26 | Argument this [post update] [a_] |
+| constructors.cpp:35:11:35:26 | call to user_input | constructors.cpp:23:20:23:20 | b | constructors.cpp:23:5:23:7 | this [post update] [b_] | constructors.cpp:35:11:35:26 | Argument this [post update] [b_] |
+| constructors.cpp:36:11:36:37 | call to user_input | constructors.cpp:23:13:23:13 | a | constructors.cpp:23:5:23:7 | this [post update] [a_] | constructors.cpp:36:11:36:37 | Argument this [post update] [a_] |
+| constructors.cpp:36:11:36:37 | call to user_input | constructors.cpp:23:20:23:20 | b | constructors.cpp:23:5:23:7 | this [post update] [b_] | constructors.cpp:36:11:36:37 | Argument this [post update] [b_] |
+| qualifiers.cpp:27:23:27:26 | call to user_input | qualifiers.cpp:9:21:9:25 | value | qualifiers.cpp:9:30:9:33 | this [post update] [a] | qualifiers.cpp:27:23:27:26 | call to getInner [post update] [a] |
+| qualifiers.cpp:32:5:32:15 | call to user_input | qualifiers.cpp:12:40:12:44 | value | qualifiers.cpp:12:49:12:53 | inner [post update] [a] | qualifiers.cpp:32:5:32:15 | call to getInner [post update] [a] |
+| qualifiers.cpp:37:5:37:17 | call to user_input | qualifiers.cpp:13:42:13:46 | value | qualifiers.cpp:13:51:13:55 | (reference dereference) [post update] [a] | qualifiers.cpp:37:5:37:17 | * ... [post update] [a] |
+| qualifiers.cpp:37:5:37:17 | call to user_input | qualifiers.cpp:13:42:13:46 | value | qualifiers.cpp:13:51:13:55 | inner [post update] [a] | qualifiers.cpp:37:5:37:17 | * ... [post update] [a] |
+| simple.cpp:28:12:28:12 | f [a_] | simple.cpp:18:9:18:9 | this [a_] | simple.cpp:18:9:18:9 | ReturnValue | simple.cpp:28:12:28:12 | call to a |
+| simple.cpp:29:12:29:12 | f [b_] | simple.cpp:19:9:19:9 | this [b_] | simple.cpp:19:9:19:9 | ReturnValue | simple.cpp:29:12:29:12 | call to b |
+| simple.cpp:39:7:39:10 | call to user_input | simple.cpp:20:19:20:19 | a | simple.cpp:20:24:20:25 | this [post update] [a_] | simple.cpp:39:7:39:10 | f [post update] [a_] |
+| simple.cpp:40:7:40:10 | call to user_input | simple.cpp:21:19:21:19 | b | simple.cpp:21:24:21:25 | this [post update] [b_] | simple.cpp:40:7:40:10 | g [post update] [b_] |
+| simple.cpp:41:7:41:10 | call to user_input | simple.cpp:20:19:20:19 | a | simple.cpp:20:24:20:25 | this [post update] [a_] | simple.cpp:41:7:41:10 | h [post update] [a_] |
+| simple.cpp:42:5:42:5 | h indirection [a_] | simple.cpp:21:10:21:13 | *#this [a_] | simple.cpp:21:10:21:13 | ReturnIndirection [a_] | simple.cpp:42:5:42:5 | setB output argument [a_] |
+| simple.cpp:42:7:42:10 | call to user_input | simple.cpp:21:19:21:19 | b | simple.cpp:21:24:21:25 | this [post update] [b_] | simple.cpp:42:7:42:10 | h [post update] [b_] |
+| simple.cpp:84:14:84:20 | this [f2, f1] | simple.cpp:78:9:78:15 | this [f2, f1] | simple.cpp:78:9:78:15 | ReturnValue | simple.cpp:84:14:84:20 | call to getf2f1 |
#select
+| A.cpp:49:10:49:13 | (void *)... | A.cpp:47:12:47:18 | new | A.cpp:49:10:49:13 | (void *)... | (void *)... flows from $@ | A.cpp:47:12:47:18 | new | new |
+| A.cpp:56:10:56:17 | (void *)... | A.cpp:55:12:55:19 | (C *)... | A.cpp:56:10:56:17 | (void *)... | (void *)... flows from $@ | A.cpp:55:12:55:19 | (C *)... | (C *)... |
+| A.cpp:56:10:56:17 | (void *)... | A.cpp:55:12:55:19 | new | A.cpp:56:10:56:17 | (void *)... | (void *)... flows from $@ | A.cpp:55:12:55:19 | new | new |
| A.cpp:56:13:56:15 | call to get | A.cpp:55:12:55:19 | (C *)... | A.cpp:56:13:56:15 | call to get | call to get flows from $@ | A.cpp:55:12:55:19 | (C *)... | (C *)... |
| A.cpp:56:13:56:15 | call to get | A.cpp:55:12:55:19 | new | A.cpp:56:13:56:15 | call to get | call to get flows from $@ | A.cpp:55:12:55:19 | new | new |
+| A.cpp:57:10:57:32 | (void *)... | A.cpp:57:17:57:23 | new | A.cpp:57:10:57:32 | (void *)... | (void *)... flows from $@ | A.cpp:57:17:57:23 | new | new |
| A.cpp:57:28:57:30 | call to get | A.cpp:57:17:57:23 | new | A.cpp:57:28:57:30 | call to get | call to get flows from $@ | A.cpp:57:17:57:23 | new | new |
-| A.cpp:107:16:107:16 | a | A.cpp:98:12:98:18 | new | A.cpp:107:16:107:16 | a | a flows from $@ | A.cpp:98:12:98:18 | new | new |
-| A.cpp:132:13:132:13 | c | A.cpp:126:12:126:18 | new | A.cpp:132:13:132:13 | c | c flows from $@ | A.cpp:126:12:126:18 | new | new |
-| A.cpp:152:13:152:13 | b | A.cpp:143:25:143:31 | new | A.cpp:152:13:152:13 | b | b flows from $@ | A.cpp:143:25:143:31 | new | new |
-| A.cpp:152:13:152:13 | b | A.cpp:150:12:150:18 | new | A.cpp:152:13:152:13 | b | b flows from $@ | A.cpp:150:12:150:18 | new | new |
-| A.cpp:154:13:154:13 | c | A.cpp:142:14:142:20 | new | A.cpp:154:13:154:13 | c | c flows from $@ | A.cpp:142:14:142:20 | new | new |
+| A.cpp:66:10:66:14 | (void *)... | A.cpp:64:21:64:28 | (C *)... | A.cpp:66:10:66:14 | (void *)... | (void *)... flows from $@ | A.cpp:64:21:64:28 | (C *)... | (C *)... |
+| A.cpp:66:10:66:14 | (void *)... | A.cpp:64:21:64:28 | new | A.cpp:66:10:66:14 | (void *)... | (void *)... flows from $@ | A.cpp:64:21:64:28 | new | new |
+| A.cpp:75:10:75:14 | (void *)... | A.cpp:73:25:73:32 | (C *)... | A.cpp:75:10:75:14 | (void *)... | (void *)... flows from $@ | A.cpp:73:25:73:32 | (C *)... | (C *)... |
+| A.cpp:75:10:75:14 | (void *)... | A.cpp:73:25:73:32 | new | A.cpp:75:10:75:14 | (void *)... | (void *)... flows from $@ | A.cpp:73:25:73:32 | new | new |
+| A.cpp:107:12:107:16 | (void *)... | A.cpp:98:12:98:18 | new | A.cpp:107:12:107:16 | (void *)... | (void *)... flows from $@ | A.cpp:98:12:98:18 | new | new |
+| A.cpp:120:12:120:16 | (void *)... | A.cpp:98:12:98:18 | new | A.cpp:120:12:120:16 | (void *)... | (void *)... flows from $@ | A.cpp:98:12:98:18 | new | new |
+| A.cpp:132:10:132:13 | (void *)... | A.cpp:126:12:126:18 | new | A.cpp:132:10:132:13 | (void *)... | (void *)... flows from $@ | A.cpp:126:12:126:18 | new | new |
+| A.cpp:152:10:152:13 | (void *)... | A.cpp:143:25:143:31 | new | A.cpp:152:10:152:13 | (void *)... | (void *)... flows from $@ | A.cpp:143:25:143:31 | new | new |
+| A.cpp:152:10:152:13 | (void *)... | A.cpp:150:12:150:18 | new | A.cpp:152:10:152:13 | (void *)... | (void *)... flows from $@ | A.cpp:150:12:150:18 | new | new |
+| A.cpp:153:10:153:16 | (void *)... | A.cpp:142:14:142:20 | new | A.cpp:153:10:153:16 | (void *)... | (void *)... flows from $@ | A.cpp:142:14:142:20 | new | new |
+| A.cpp:154:10:154:13 | (void *)... | A.cpp:142:14:142:20 | new | A.cpp:154:10:154:13 | (void *)... | (void *)... flows from $@ | A.cpp:142:14:142:20 | new | new |
+| A.cpp:165:10:165:29 | (void *)... | A.cpp:159:12:159:18 | new | A.cpp:165:10:165:29 | (void *)... | (void *)... flows from $@ | A.cpp:159:12:159:18 | new | new |
+| A.cpp:169:12:169:18 | (void *)... | A.cpp:159:12:159:18 | new | A.cpp:169:12:169:18 | (void *)... | (void *)... flows from $@ | A.cpp:159:12:159:18 | new | new |
+| B.cpp:9:10:9:24 | (void *)... | B.cpp:6:15:6:24 | new | B.cpp:9:10:9:24 | (void *)... | (void *)... flows from $@ | B.cpp:6:15:6:24 | new | new |
+| B.cpp:19:10:19:24 | (void *)... | B.cpp:15:15:15:27 | new | B.cpp:19:10:19:24 | (void *)... | (void *)... flows from $@ | B.cpp:15:15:15:27 | new | new |
| C.cpp:29:10:29:11 | s1 | C.cpp:22:12:22:21 | new | C.cpp:29:10:29:11 | s1 | s1 flows from $@ | C.cpp:22:12:22:21 | new | new |
-| C.cpp:31:10:31:11 | s3 | C.cpp:24:16:24:25 | new | C.cpp:31:10:31:11 | s3 | s3 flows from $@ | C.cpp:24:16:24:25 | new | new |
+| D.cpp:22:10:22:33 | (void *)... | D.cpp:28:15:28:24 | new | D.cpp:22:10:22:33 | (void *)... | (void *)... flows from $@ | D.cpp:28:15:28:24 | new | new |
+| D.cpp:22:10:22:33 | (void *)... | D.cpp:35:15:35:24 | new | D.cpp:22:10:22:33 | (void *)... | (void *)... flows from $@ | D.cpp:35:15:35:24 | new | new |
+| D.cpp:22:10:22:33 | (void *)... | D.cpp:42:15:42:24 | new | D.cpp:22:10:22:33 | (void *)... | (void *)... flows from $@ | D.cpp:42:15:42:24 | new | new |
+| D.cpp:22:10:22:33 | (void *)... | D.cpp:49:15:49:24 | new | D.cpp:22:10:22:33 | (void *)... | (void *)... flows from $@ | D.cpp:49:15:49:24 | new | new |
+| D.cpp:22:25:22:31 | call to getElem | D.cpp:28:15:28:24 | new | D.cpp:22:25:22:31 | call to getElem | call to getElem flows from $@ | D.cpp:28:15:28:24 | new | new |
+| D.cpp:22:25:22:31 | call to getElem | D.cpp:35:15:35:24 | new | D.cpp:22:25:22:31 | call to getElem | call to getElem flows from $@ | D.cpp:35:15:35:24 | new | new |
+| D.cpp:22:25:22:31 | call to getElem | D.cpp:42:15:42:24 | new | D.cpp:22:25:22:31 | call to getElem | call to getElem flows from $@ | D.cpp:42:15:42:24 | new | new |
+| D.cpp:22:25:22:31 | call to getElem | D.cpp:49:15:49:24 | new | D.cpp:22:25:22:31 | call to getElem | call to getElem flows from $@ | D.cpp:49:15:49:24 | new | new |
+| D.cpp:64:10:64:28 | (void *)... | D.cpp:56:15:56:24 | new | D.cpp:64:10:64:28 | (void *)... | (void *)... flows from $@ | D.cpp:56:15:56:24 | new | new |
+| E.cpp:21:18:21:23 | buffer | E.cpp:30:21:30:33 | argument_source output argument | E.cpp:21:18:21:23 | buffer | buffer flows from $@ | E.cpp:30:21:30:33 | argument_source output argument | argument_source output argument |
+| E.cpp:31:10:31:12 | raw | E.cpp:28:21:28:23 | argument_source output argument | E.cpp:31:10:31:12 | raw | raw flows from $@ | E.cpp:28:21:28:23 | argument_source output argument | argument_source output argument |
+| E.cpp:32:13:32:18 | buffer | E.cpp:29:21:29:29 | argument_source output argument | E.cpp:32:13:32:18 | buffer | buffer flows from $@ | E.cpp:29:21:29:29 | argument_source output argument | argument_source output argument |
| aliasing.cpp:29:11:29:12 | m1 | aliasing.cpp:9:11:9:20 | call to user_input | aliasing.cpp:29:11:29:12 | m1 | m1 flows from $@ | aliasing.cpp:9:11:9:20 | call to user_input | call to user_input |
| aliasing.cpp:30:11:30:12 | m1 | aliasing.cpp:13:10:13:19 | call to user_input | aliasing.cpp:30:11:30:12 | m1 | m1 flows from $@ | aliasing.cpp:13:10:13:19 | call to user_input | call to user_input |
-| aliasing.cpp:38:11:38:12 | m1 | aliasing.cpp:37:13:37:22 | call to user_input | aliasing.cpp:38:11:38:12 | m1 | m1 flows from $@ | aliasing.cpp:37:13:37:22 | call to user_input | call to user_input |
-| aliasing.cpp:43:13:43:14 | m1 | aliasing.cpp:42:11:42:20 | call to user_input | aliasing.cpp:43:13:43:14 | m1 | m1 flows from $@ | aliasing.cpp:42:11:42:20 | call to user_input | call to user_input |
| aliasing.cpp:62:14:62:15 | m1 | aliasing.cpp:60:11:60:20 | call to user_input | aliasing.cpp:62:14:62:15 | m1 | m1 flows from $@ | aliasing.cpp:60:11:60:20 | call to user_input | call to user_input |
-| aliasing.cpp:80:12:80:13 | m1 | aliasing.cpp:79:11:79:20 | call to user_input | aliasing.cpp:80:12:80:13 | m1 | m1 flows from $@ | aliasing.cpp:79:11:79:20 | call to user_input | call to user_input |
-| aliasing.cpp:87:12:87:13 | m1 | aliasing.cpp:86:10:86:19 | call to user_input | aliasing.cpp:87:12:87:13 | m1 | m1 flows from $@ | aliasing.cpp:86:10:86:19 | call to user_input | call to user_input |
| aliasing.cpp:93:12:93:13 | m1 | aliasing.cpp:92:12:92:21 | call to user_input | aliasing.cpp:93:12:93:13 | m1 | m1 flows from $@ | aliasing.cpp:92:12:92:21 | call to user_input | call to user_input |
-| aliasing.cpp:102:8:102:10 | * ... | aliasing.cpp:98:10:98:19 | call to user_input | aliasing.cpp:102:8:102:10 | * ... | * ... flows from $@ | aliasing.cpp:98:10:98:19 | call to user_input | call to user_input |
-| aliasing.cpp:122:8:122:12 | access to array | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:122:8:122:12 | access to array | access to array flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
-| aliasing.cpp:132:8:132:14 | * ... | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:132:8:132:14 | * ... | * ... flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
-| aliasing.cpp:137:8:137:11 | * ... | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:137:8:137:11 | * ... | * ... flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
+| aliasing.cpp:143:8:143:16 | access to array | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:143:8:143:16 | access to array | access to array flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
+| aliasing.cpp:159:8:159:14 | * ... | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:159:8:159:14 | * ... | * ... flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
+| aliasing.cpp:165:8:165:16 | access to array | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:165:8:165:16 | access to array | access to array flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
| aliasing.cpp:176:13:176:14 | m1 | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:176:13:176:14 | m1 | m1 flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
| aliasing.cpp:189:15:189:16 | m1 | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:189:15:189:16 | m1 | m1 flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
| aliasing.cpp:201:15:201:16 | m1 | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:201:15:201:16 | m1 | m1 flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
| arrays.cpp:7:8:7:13 | access to array | arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:7:8:7:13 | access to array | access to array flows from $@ | arrays.cpp:6:12:6:21 | call to user_input | call to user_input |
+| arrays.cpp:8:8:8:13 | access to array | arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:8:8:8:13 | access to array | access to array flows from $@ | arrays.cpp:6:12:6:21 | call to user_input | call to user_input |
| arrays.cpp:9:8:9:11 | * ... | arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:9:8:9:11 | * ... | * ... flows from $@ | arrays.cpp:6:12:6:21 | call to user_input | call to user_input |
| arrays.cpp:10:8:10:15 | * ... | arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:10:8:10:15 | * ... | * ... flows from $@ | arrays.cpp:6:12:6:21 | call to user_input | call to user_input |
| arrays.cpp:16:8:16:13 | access to array | arrays.cpp:15:14:15:23 | call to user_input | arrays.cpp:16:8:16:13 | access to array | access to array flows from $@ | arrays.cpp:15:14:15:23 | call to user_input | call to user_input |
+| arrays.cpp:17:8:17:13 | access to array | arrays.cpp:15:14:15:23 | call to user_input | arrays.cpp:17:8:17:13 | access to array | access to array flows from $@ | arrays.cpp:15:14:15:23 | call to user_input | call to user_input |
| arrays.cpp:37:24:37:27 | data | arrays.cpp:36:26:36:35 | call to user_input | arrays.cpp:37:24:37:27 | data | data flows from $@ | arrays.cpp:36:26:36:35 | call to user_input | call to user_input |
+| arrays.cpp:38:24:38:27 | data | arrays.cpp:36:26:36:35 | call to user_input | arrays.cpp:38:24:38:27 | data | data flows from $@ | arrays.cpp:36:26:36:35 | call to user_input | call to user_input |
+| arrays.cpp:43:27:43:30 | data | arrays.cpp:42:29:42:38 | call to user_input | arrays.cpp:43:27:43:30 | data | data flows from $@ | arrays.cpp:42:29:42:38 | call to user_input | call to user_input |
+| arrays.cpp:44:27:44:30 | data | arrays.cpp:42:29:42:38 | call to user_input | arrays.cpp:44:27:44:30 | data | data flows from $@ | arrays.cpp:42:29:42:38 | call to user_input | call to user_input |
+| arrays.cpp:49:27:49:30 | data | arrays.cpp:48:29:48:38 | call to user_input | arrays.cpp:49:27:49:30 | data | data flows from $@ | arrays.cpp:48:29:48:38 | call to user_input | call to user_input |
+| arrays.cpp:50:27:50:30 | data | arrays.cpp:48:29:48:38 | call to user_input | arrays.cpp:50:27:50:30 | data | data flows from $@ | arrays.cpp:48:29:48:38 | call to user_input | call to user_input |
| by_reference.cpp:51:10:51:20 | call to getDirectly | by_reference.cpp:50:17:50:26 | call to user_input | by_reference.cpp:51:10:51:20 | call to getDirectly | call to getDirectly flows from $@ | by_reference.cpp:50:17:50:26 | call to user_input | call to user_input |
| by_reference.cpp:57:10:57:22 | call to getIndirectly | by_reference.cpp:56:19:56:28 | call to user_input | by_reference.cpp:57:10:57:22 | call to getIndirectly | call to getIndirectly flows from $@ | by_reference.cpp:56:19:56:28 | call to user_input | call to user_input |
| by_reference.cpp:63:10:63:28 | call to getThroughNonMember | by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:63:10:63:28 | call to getThroughNonMember | call to getThroughNonMember flows from $@ | by_reference.cpp:62:25:62:34 | call to user_input | call to user_input |
| by_reference.cpp:69:8:69:20 | call to nonMemberGetA | by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:69:8:69:20 | call to nonMemberGetA | call to nonMemberGetA flows from $@ | by_reference.cpp:68:21:68:30 | call to user_input | call to user_input |
| by_reference.cpp:110:27:110:27 | a | by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:110:27:110:27 | a | a flows from $@ | by_reference.cpp:84:14:84:23 | call to user_input | call to user_input |
+| by_reference.cpp:111:25:111:25 | a | by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:111:25:111:25 | a | a flows from $@ | by_reference.cpp:84:14:84:23 | call to user_input | call to user_input |
| by_reference.cpp:112:14:112:14 | a | by_reference.cpp:92:9:92:18 | call to user_input | by_reference.cpp:112:14:112:14 | a | a flows from $@ | by_reference.cpp:92:9:92:18 | call to user_input | call to user_input |
| by_reference.cpp:114:29:114:29 | a | by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:114:29:114:29 | a | a flows from $@ | by_reference.cpp:84:14:84:23 | call to user_input | call to user_input |
+| by_reference.cpp:115:27:115:27 | a | by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:115:27:115:27 | a | a flows from $@ | by_reference.cpp:84:14:84:23 | call to user_input | call to user_input |
| by_reference.cpp:116:16:116:16 | a | by_reference.cpp:92:9:92:18 | call to user_input | by_reference.cpp:116:16:116:16 | a | a flows from $@ | by_reference.cpp:92:9:92:18 | call to user_input | call to user_input |
| by_reference.cpp:130:27:130:27 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:130:27:130:27 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input |
+| by_reference.cpp:131:25:131:25 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:131:25:131:25 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input |
| by_reference.cpp:132:14:132:14 | a | by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:132:14:132:14 | a | a flows from $@ | by_reference.cpp:96:8:96:17 | call to user_input | call to user_input |
| by_reference.cpp:134:29:134:29 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:134:29:134:29 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input |
+| by_reference.cpp:135:27:135:27 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:135:27:135:27 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input |
| by_reference.cpp:136:16:136:16 | a | by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:136:16:136:16 | a | a flows from $@ | by_reference.cpp:96:8:96:17 | call to user_input | call to user_input |
| complex.cpp:42:18:42:18 | call to a | complex.cpp:53:19:53:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:53:19:53:28 | call to user_input | call to user_input |
| complex.cpp:42:18:42:18 | call to a | complex.cpp:55:19:55:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:55:19:55:28 | call to user_input | call to user_input |
| complex.cpp:43:18:43:18 | call to b | complex.cpp:54:19:54:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:54:19:54:28 | call to user_input | call to user_input |
| complex.cpp:43:18:43:18 | call to b | complex.cpp:56:19:56:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:56:19:56:28 | call to user_input | call to user_input |
+| conflated.cpp:11:8:11:12 | * ... | conflated.cpp:10:11:10:20 | call to user_input | conflated.cpp:11:8:11:12 | * ... | * ... flows from $@ | conflated.cpp:10:11:10:20 | call to user_input | call to user_input |
+| conflated.cpp:20:8:20:10 | (void *)... | conflated.cpp:19:19:19:21 | argument_source output argument | conflated.cpp:20:8:20:10 | (void *)... | (void *)... flows from $@ | conflated.cpp:19:19:19:21 | argument_source output argument | argument_source output argument |
+| conflated.cpp:20:8:20:10 | raw | conflated.cpp:19:19:19:21 | argument_source output argument | conflated.cpp:20:8:20:10 | raw | raw flows from $@ | conflated.cpp:19:19:19:21 | argument_source output argument | argument_source output argument |
+| conflated.cpp:30:12:30:12 | x | conflated.cpp:29:11:29:20 | call to user_input | conflated.cpp:30:12:30:12 | x | x flows from $@ | conflated.cpp:29:11:29:20 | call to user_input | call to user_input |
+| conflated.cpp:37:12:37:12 | x | conflated.cpp:36:11:36:20 | call to user_input | conflated.cpp:37:12:37:12 | x | x flows from $@ | conflated.cpp:36:11:36:20 | call to user_input | call to user_input |
+| conflated.cpp:55:18:55:18 | y | conflated.cpp:54:17:54:26 | call to user_input | conflated.cpp:55:18:55:18 | y | y flows from $@ | conflated.cpp:54:17:54:26 | call to user_input | call to user_input |
+| conflated.cpp:61:18:61:18 | y | conflated.cpp:60:17:60:26 | call to user_input | conflated.cpp:61:18:61:18 | y | y flows from $@ | conflated.cpp:60:17:60:26 | call to user_input | call to user_input |
| constructors.cpp:28:12:28:12 | call to a | constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:34:11:34:20 | call to user_input | call to user_input |
| constructors.cpp:28:12:28:12 | call to a | constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:36:11:36:20 | call to user_input | call to user_input |
| constructors.cpp:29:12:29:12 | call to b | constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:29:12:29:12 | call to b | call to b flows from $@ | constructors.cpp:35:14:35:23 | call to user_input | call to user_input |
| constructors.cpp:29:12:29:12 | call to b | constructors.cpp:36:25:36:34 | call to user_input | constructors.cpp:29:12:29:12 | call to b | call to b flows from $@ | constructors.cpp:36:25:36:34 | call to user_input | call to user_input |
+| qualifiers.cpp:23:23:23:23 | a | qualifiers.cpp:22:27:22:36 | call to user_input | qualifiers.cpp:23:23:23:23 | a | a flows from $@ | qualifiers.cpp:22:27:22:36 | call to user_input | call to user_input |
+| qualifiers.cpp:28:23:28:23 | a | qualifiers.cpp:27:28:27:37 | call to user_input | qualifiers.cpp:28:23:28:23 | a | a flows from $@ | qualifiers.cpp:27:28:27:37 | call to user_input | call to user_input |
+| qualifiers.cpp:33:23:33:23 | a | qualifiers.cpp:32:35:32:44 | call to user_input | qualifiers.cpp:33:23:33:23 | a | a flows from $@ | qualifiers.cpp:32:35:32:44 | call to user_input | call to user_input |
+| qualifiers.cpp:38:23:38:23 | a | qualifiers.cpp:37:38:37:47 | call to user_input | qualifiers.cpp:38:23:38:23 | a | a flows from $@ | qualifiers.cpp:37:38:37:47 | call to user_input | call to user_input |
+| qualifiers.cpp:43:23:43:23 | a | qualifiers.cpp:42:29:42:38 | call to user_input | qualifiers.cpp:43:23:43:23 | a | a flows from $@ | qualifiers.cpp:42:29:42:38 | call to user_input | call to user_input |
+| qualifiers.cpp:48:23:48:23 | a | qualifiers.cpp:47:31:47:40 | call to user_input | qualifiers.cpp:48:23:48:23 | a | a flows from $@ | qualifiers.cpp:47:31:47:40 | call to user_input | call to user_input |
+| realistic.cpp:61:14:61:55 | (void *)... | realistic.cpp:53:47:53:66 | (size_t)... | realistic.cpp:61:14:61:55 | (void *)... | (void *)... flows from $@ | realistic.cpp:53:47:53:66 | (size_t)... | (size_t)... |
+| realistic.cpp:61:14:61:55 | (void *)... | realistic.cpp:53:55:53:64 | call to user_input | realistic.cpp:61:14:61:55 | (void *)... | (void *)... flows from $@ | realistic.cpp:53:55:53:64 | call to user_input | call to user_input |
| simple.cpp:28:12:28:12 | call to a | simple.cpp:39:12:39:21 | call to user_input | simple.cpp:28:12:28:12 | call to a | call to a flows from $@ | simple.cpp:39:12:39:21 | call to user_input | call to user_input |
| simple.cpp:28:12:28:12 | call to a | simple.cpp:41:12:41:21 | call to user_input | simple.cpp:28:12:28:12 | call to a | call to a flows from $@ | simple.cpp:41:12:41:21 | call to user_input | call to user_input |
| simple.cpp:29:12:29:12 | call to b | simple.cpp:40:12:40:21 | call to user_input | simple.cpp:29:12:29:12 | call to b | call to b flows from $@ | simple.cpp:40:12:40:21 | call to user_input | call to user_input |
diff --git a/cpp/ql/test/library-tests/dataflow/fields/qualifiers.cpp b/cpp/ql/test/library-tests/dataflow/fields/qualifiers.cpp
index 18798c1f5fe..3b5b8c32b61 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/qualifiers.cpp
+++ b/cpp/ql/test/library-tests/dataflow/fields/qualifiers.cpp
@@ -20,31 +20,31 @@ namespace qualifiers {
void assignToGetter(Outer outer) {
outer.getInner()->a = user_input();
- sink(outer.inner->a); // $ ast MISSING: ir
+ sink(outer.inner->a); // $ ast,ir
}
void getterArgument1(Outer outer) {
outer.getInner()->setA(user_input());
- sink(outer.inner->a); // $ ast MISSING: ir
+ sink(outer.inner->a); // $ ast,ir
}
void getterArgument2(Outer outer) {
pointerSetA(outer.getInner(), user_input());
- sink(outer.inner->a); // $ ast MISSING: ir
+ sink(outer.inner->a); // $ ast,ir
}
void getterArgument2Ref(Outer outer) {
referenceSetA(*outer.getInner(), user_input());
- sink(outer.inner->a); // $ ast MISSING: ir
+ sink(outer.inner->a); // $ ast,ir
}
void assignToGetterStar(Outer outer) {
(*outer.getInner()).a = user_input();
- sink(outer.inner->a); // $ ast MISSING: ir
+ sink(outer.inner->a); // $ ast,ir
}
void assignToGetterAmp(Outer outer) {
(&outer)->getInner()->a = user_input();
- sink(outer.inner->a); // $ ast MISSING: ir
+ sink(outer.inner->a); // $ ast,ir
}
}
\ No newline at end of file
diff --git a/cpp/ql/test/library-tests/dataflow/fields/realistic.cpp b/cpp/ql/test/library-tests/dataflow/fields/realistic.cpp
index 89f1b4f6248..a185382914f 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/realistic.cpp
+++ b/cpp/ql/test/library-tests/dataflow/fields/realistic.cpp
@@ -58,7 +58,7 @@ int main(int argc, char** argv) {
return -1;
}
memcpy(dst, foo.bar[i].baz->userInput.buffer, foo.bar[i].baz->userInput.bufferLen);
- sink((void*)foo.bar[i].baz->userInput.bufferLen); // $ ast MISSING: ir
+ sink((void*)foo.bar[i].baz->userInput.bufferLen); // $ ast ir=53:47 ir=53:55
// There is no flow to the following two `sink` calls because the
// source is the _pointer_ returned by `user_input` rather than the
// _data_ to which it points.
diff --git a/cpp/ql/test/library-tests/dataflow/security-taint/tainted_diff.expected b/cpp/ql/test/library-tests/dataflow/security-taint/tainted_diff.expected
index 2b1b021716b..ac95d5659a3 100644
--- a/cpp/ql/test/library-tests/dataflow/security-taint/tainted_diff.expected
+++ b/cpp/ql/test/library-tests/dataflow/security-taint/tainted_diff.expected
@@ -10,6 +10,10 @@
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:29:50:40 | envStrGlobal | AST only |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:2:52:12 | * ... | AST only |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:3:52:12 | envStr_ptr | AST only |
+| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:6:54:35 | ! ... | AST only |
+| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:7:54:12 | call to strcmp | AST only |
+| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:7:54:35 | (bool)... | AST only |
+| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:14:54:25 | envStrGlobal | AST only |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:10:27:10:27 | s | AST only |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:18:60:25 | userName | AST only |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:11:20:11:21 | s1 | AST only |
diff --git a/cpp/ql/test/library-tests/dataflow/security-taint/tainted_ir.expected b/cpp/ql/test/library-tests/dataflow/security-taint/tainted_ir.expected
index fb19ea7fc9f..01d8f5092ed 100644
--- a/cpp/ql/test/library-tests/dataflow/security-taint/tainted_ir.expected
+++ b/cpp/ql/test/library-tests/dataflow/security-taint/tainted_ir.expected
@@ -14,10 +14,6 @@
| test.cpp:49:23:49:28 | call to getenv | test.cpp:49:23:49:28 | call to getenv |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:49:23:49:40 | (const char *)... |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:16:52:21 | envStr |
-| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:6:54:35 | ! ... |
-| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:7:54:12 | call to strcmp |
-| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:7:54:35 | (bool)... |
-| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:14:54:25 | envStrGlobal |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:29:60:34 | call to getenv |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:29:60:47 | (const char *)... |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:25:64:32 | userName |
diff --git a/cpp/ql/test/library-tests/dataflow/smart-pointers-taint/test.cpp b/cpp/ql/test/library-tests/dataflow/smart-pointers-taint/test.cpp
index bd24f0e72f2..7836bc01c8e 100644
--- a/cpp/ql/test/library-tests/dataflow/smart-pointers-taint/test.cpp
+++ b/cpp/ql/test/library-tests/dataflow/smart-pointers-taint/test.cpp
@@ -7,7 +7,7 @@ void test_unique_ptr_int() {
std::unique_ptr p1(new int(source()));
std::unique_ptr p2 = std::make_unique(source());
- sink(*p1); // $ ir MISSING: ast
+ sink(*p1); // $ MISSING: ast,ir
sink(*p2); // $ ast ir=8:50
}
@@ -21,7 +21,7 @@ void test_unique_ptr_struct() {
std::unique_ptr p1(new A{source(), 0});
std::unique_ptr p2 = std::make_unique(source(), 0);
- sink(p1->x); // $ ir MISSING: ast
+ sink(p1->x); // $ MISSING: ast,ir
sink(p1->y);
sink(p2->x); // $ MISSING: ast,ir
sink(p2->y);
@@ -31,7 +31,7 @@ void test_shared_ptr_int() {
std::shared_ptr p1(new int(source()));
std::shared_ptr p2 = std::make_shared(source());
- sink(*p1); // $ ast ir
+ sink(*p1); // $ ast MISSING: ir
sink(*p2); // $ ast ir=32:50
}
@@ -39,7 +39,7 @@ void test_shared_ptr_struct() {
std::shared_ptr p1(new A{source(), 0});
std::shared_ptr p2 = std::make_shared(source(), 0);
- sink(p1->x); // $ ir MISSING: ast
+ sink(p1->x); // $ MISSING: ast,ir
sink(p1->y);
sink(p2->x); // $ MISSING: ast,ir
sink(p2->y);
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/arrayassignment.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/arrayassignment.cpp
index dbec5f52d56..011c6bc1fe7 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/arrayassignment.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/arrayassignment.cpp
@@ -13,10 +13,10 @@ void test_pointer_deref_assignment()
*p_x = source();
- sink(x); // $ ir MISSING: ast
+ sink(x); // $ MISSING: ast,ir
sink(*p_x); // $ ast,ir
- sink(*p2_x); // $ ir MISSING: ast
- sink(r_x); // $ ir MISSING: ast
+ sink(*p2_x); // $ MISSING: ast,ir
+ sink(r_x); // $ MISSING: ast,ir
}
void test_reference_deref_assignment()
@@ -28,10 +28,10 @@ void test_reference_deref_assignment()
r_x = source();
- sink(x); // $ ir MISSING: ast
- sink(*p_x); // $ ir MISSING: ast
+ sink(x); // $ MISSING: ast,ir
+ sink(*p_x); // $ MISSING: ast,ir
sink(r_x); // $ ast,ir
- sink(r2_x); // $ ir MISSING: ast
+ sink(r2_x); // $ MISSING: ast,ir
}
class MyInt
@@ -53,7 +53,7 @@ void test_myint_member_assignment()
mi.i = source();
- sink(mi); // $ ir MISSING: ast
+ sink(mi); // $ MISSING: ast,ir
sink(mi.get()); // $ ast,ir
}
@@ -107,7 +107,7 @@ void test_myarray_method_assignment()
ma.get(0) = source();
- sink(ma.get(0)); // $ MISSING: ast,ir
+ sink(ma.get(0)); // $ ir MISSING: ast
}
void test_myarray_overloaded_assignment()
@@ -133,15 +133,15 @@ void test_array_reference_assignment()
ref1 = source();
sink(ref1); // $ ast,ir
- sink(arr1[5]); // $ ir MISSING: ast
+ sink(arr1[5]); // $ MISSING: ast,ir
ptr2 = &(arr2[5]);
*ptr2 = source();
sink(*ptr2); // $ ast,ir
- sink(arr2[5]); // $ ir MISSING: ast
+ sink(arr2[5]); // $ MISSING: ast,ir
ptr3 = arr3;
ptr3[5] = source();
sink(ptr3[5]); // $ ast,ir
- sink(arr3[5]); // $ ir MISSING: ast
+ sink(arr3[5]); // $ MISSING: ast,ir
}
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/map.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/map.cpp
index 4e85a6b9aa3..e044dbb62a5 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/map.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/map.cpp
@@ -28,12 +28,12 @@ void test_pair()
b.first = source();
sink(b.first); // $ ast,ir
sink(b.second);
- sink(b); // $ MISSING: ast,ir
+ sink(b); // $ ir MISSING: ast
c.second = source();
sink(c.first);
sink(c.second); // $ ast,ir
- sink(c); // $ MISSING: ast,ir
+ sink(c); // $ ir MISSING: ast
std::pair d("123", "456");
sink(d.first);
@@ -43,21 +43,21 @@ void test_pair()
std::pair e(source(), "456");
sink(e.first); // $ ast,ir
sink(e.second);
- sink(e); // $ MISSING: ast,ir
+ sink(e); // $ ir MISSING: ast
std::pair f("123", source());
- sink(f.first); // $ SPURIOUS: ir
+ sink(f.first);
sink(f.second); // $ ast,ir
sink(f); // $ ast,ir
std::pair g(f);
- sink(g.first); // $ SPURIOUS: ir
+ sink(g.first);
sink(g.second); // $ ast,ir
sink(g); // $ ast,ir
std::pair h;
h = f;
- sink(h.first); // $ SPURIOUS: ir
+ sink(h.first);
sink(h.second); // $ ast,ir
sink(h); // $ ast,ir
@@ -67,17 +67,17 @@ void test_pair()
std::pair l("123", "456");
i.swap(j);
k.swap(l);
- sink(i.first); // $ SPURIOUS: ir
- sink(i.second); // $ ir MISSING: ast
+ sink(i.first);
+ sink(i.second); // $ MISSING: ast,ir
sink(i); // $ ast,ir
- sink(j.first); // $ SPURIOUS: ir
+ sink(j.first);
sink(j.second); // $ SPURIOUS: ast,ir
sink(j); // $ SPURIOUS: ast,ir
- sink(k.first); // $ SPURIOUS: ir
+ sink(k.first);
sink(k.second); // $ SPURIOUS: ast,ir
sink(k); // $ SPURIOUS: ast,ir
- sink(l.first); // $ SPURIOUS: ir
- sink(l.second); // $ ir MISSING: ast
+ sink(l.first);
+ sink(l.second); // $ MISSING: ast,ir
sink(l); // $ ast,ir
sink(make_pair("123", "456"));
@@ -87,7 +87,7 @@ void test_pair()
sink(make_pair(source(), "456").first); // $ ast,ir
sink(make_pair(source(), "456").second);
sink(make_pair("123", source())); // $ ast,ir
- sink(make_pair("123", source()).first); // $ SPURIOUS: ir
+ sink(make_pair("123", source()).first);
sink(make_pair("123", source()).second); // $ ast,ir
std::pair, char *> m;
@@ -105,10 +105,10 @@ void test_map()
std::map m1, m2, m3, m4, m5, m6;
sink(m1.insert(std::make_pair("abc", "def")).first);
- sink(m2.insert(std::make_pair("abc", source())).first); // $ SPURIOUS: ir
+ sink(m2.insert(std::make_pair("abc", source())).first);
sink(m3.insert(std::make_pair(source(), "def")).first); // $ MISSING: ast,ir
sink(m4.insert(m4.begin(), std::pair("abc", source()))); // $ ast,ir
- sink(m5.insert_or_assign("abc", source()).first); // $ SPURIOUS: ir
+ sink(m5.insert_or_assign("abc", source()).first);
sink(m6.insert_or_assign(m6.begin(), "abc", source())); // $ ast,ir
sink(m1);
sink(m2); // $ ast,ir
@@ -169,9 +169,9 @@ void test_map()
sink(m12.at("abc") = "def");
sink(m13.at("abc") = source()); // $ ast,ir
sink(m10["abc"]);
- sink(m11["abc"]); // $ ast MISSING: ir
+ sink(m11["abc"]); // $ ast,ir
sink(m12["abc"]);
- sink(m13["abc"]); // $ ast MISSING: ir
+ sink(m13["abc"]); // $ ast,ir
// ranges
std::map m14;
@@ -179,12 +179,12 @@ void test_map()
m14.insert(std::make_pair("b", source()));
m14.insert(std::make_pair("c", source()));
m14.insert(std::make_pair("d", "d"));
- sink(m2.lower_bound("b")); // $ ast,ir
- sink(m2.upper_bound("b")); // $ ast,ir
- sink(m2.equal_range("b").first); // $ ir
- sink(m2.equal_range("b").second); // $ ir MISSING: ast
- sink(m2.upper_bound("c")); // $ SPURIOUS: ast,ir
- sink(m2.equal_range("c").second); // $ SPURIOUS: ir
+ sink(m2.lower_bound("b")); // $ ast MISSING: ir
+ sink(m2.upper_bound("b")); // $ ast MISSING: ir
+ sink(m2.equal_range("b").first); // $ MISSING: ast,ir
+ sink(m2.equal_range("b").second); // $ MISSING: ast,ir
+ sink(m2.upper_bound("c")); // $ SPURIOUS: ast
+ sink(m2.equal_range("c").second);
// swap
std::map m15, m16, m17, m18;
@@ -232,7 +232,7 @@ void test_map()
std::map m24, m25;
sink(m24.emplace("abc", "def").first);
sink(m24);
- sink(m24.emplace("abc", source()).first); // $ SPURIOUS: ir
+ sink(m24.emplace("abc", source()).first);
sink(m24); // $ ast,ir
sink(m25.emplace_hint(m25.begin(), "abc", "def"));
sink(m25);
@@ -243,7 +243,7 @@ void test_map()
std::map m26, m27;
sink(m26.try_emplace("abc", "def").first);
sink(m26);
- sink(m26.try_emplace("abc", source()).first); // $ SPURIOUS: ir
+ sink(m26.try_emplace("abc", source()).first);
sink(m26); // $ ast,ir
sink(m27.try_emplace(m27.begin(), "abc", "def"));
sink(m27);
@@ -257,10 +257,10 @@ void test_unordered_map()
std::unordered_map m1, m2, m3, m4, m5, m6;
sink(m1.insert(std::make_pair("abc", "def")).first);
- sink(m2.insert(std::make_pair("abc", source())).first); // $ SPURIOUS: ir
+ sink(m2.insert(std::make_pair("abc", source())).first);
sink(m3.insert(std::make_pair(source(), "def")).first); // $ MISSING: ast,ir
sink(m4.insert(m4.begin(), std::pair("abc", source()))); // $ ast,ir
- sink(m5.insert_or_assign("abc", source()).first); // $ SPURIOUS: ir
+ sink(m5.insert_or_assign("abc", source()).first);
sink(m6.insert_or_assign(m6.begin(), "abc", source())); // $ ast,ir
sink(m1);
sink(m2); // $ ast,ir
@@ -321,9 +321,9 @@ void test_unordered_map()
sink(m12.at("abc") = "def");
sink(m13.at("abc") = source()); // $ ast,ir
sink(m10["abc"]);
- sink(m11["abc"]); // $ ast MISSING: ir
+ sink(m11["abc"]); // $ ast,ir
sink(m12["abc"]);
- sink(m13["abc"]); // $ ast MISSING: ir
+ sink(m13["abc"]); // $ ast,ir
// ranges
std::unordered_map m14;
@@ -331,9 +331,9 @@ void test_unordered_map()
m14.insert(std::make_pair("b", source()));
m14.insert(std::make_pair("c", source()));
m14.insert(std::make_pair("d", "d"));
- sink(m2.equal_range("b").first); // $ ir
- sink(m2.equal_range("b").second); // $ ir MISSING: ast
- sink(m2.equal_range("c").second); // $ SPURIOUS: ir
+ sink(m2.equal_range("b").first);
+ sink(m2.equal_range("b").second); // $ MISSING: ast,ir
+ sink(m2.equal_range("c").second);
// swap
std::unordered_map m15, m16, m17, m18;
@@ -381,7 +381,7 @@ void test_unordered_map()
std::unordered_map m24, m25;
sink(m24.emplace("abc", "def").first);
sink(m24);
- sink(m24.emplace("abc", source()).first); // $ SPURIOUS: ir
+ sink(m24.emplace("abc", source()).first);
sink(m24); // $ ast,ir
sink(m25.emplace_hint(m25.begin(), "abc", "def"));
sink(m25);
@@ -393,8 +393,8 @@ void test_unordered_map()
sink(m26.try_emplace("abc", "def").first);
sink(m26.try_emplace("abc", "def").second);
sink(m26);
- sink(m26.try_emplace("abc", source()).first); // $ SPURIOUS: ir
- sink(m26.try_emplace("abc", source()).second); // $ ir=396:30 SPURIOUS: ir=397:30 MISSING: ast=396:30
+ sink(m26.try_emplace("abc", source()).first);
+ sink(m26.try_emplace("abc", source()).second); // $ MISSING: ast,ir=396:30
sink(m26); // $ ast,ir=396:30 SPURIOUS: ast,ir=397:30
sink(m27.try_emplace(m27.begin(), "abc", "def"));
sink(m27);
@@ -428,7 +428,7 @@ void test_unordered_map()
std::unordered_map m34, m35;
sink(m34.emplace(std::pair("abc", "def")).first);
sink(m34);
- sink(m34.emplace(std::pair("abc", source())).first); // $ SPURIOUS: ir
+ sink(m34.emplace(std::pair("abc", source())).first);
sink(m34); // $ ast,ir
sink(m34.emplace_hint(m34.begin(), "abc", "def")); // $ ast,ir
sink(m35.emplace().first);
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/set.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/set.cpp
index 86060ed87d8..c6c19d90089 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/set.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/set.cpp
@@ -17,7 +17,7 @@ void test_set()
std::set s1, s2, s3, s4, s5, s6;
sink(s1.insert("abc").first);
- sink(s2.insert(source()).first); // $ ir MISSING: ast
+ sink(s2.insert(source()).first); // $ MISSING: ast,ir
sink(s3.insert(s3.begin(), "abc"));
sink(s4.insert(s4.begin(), source())); // $ ast,ir
s5.insert(s1.begin(), s1.end());
@@ -68,8 +68,8 @@ void test_set()
s11.insert("c");
sink(s11.lower_bound("b")); // $ ast,ir
sink(s11.upper_bound("b")); // $ ast,ir
- sink(s11.equal_range("b").first); // $ ir MISSING: ast
- sink(s11.equal_range("b").second); // $ ir MISSING: ast
+ sink(s11.equal_range("b").first); // $ MISSING: ast,ir
+ sink(s11.equal_range("b").second); // $ MISSING: ast,ir
// swap
std::set s12, s13, s14, s15;
@@ -117,7 +117,7 @@ void test_set()
std::set s21, s22;
sink(s21.emplace("abc").first);
sink(s21);
- sink(s21.emplace(source()).first); // $ ir MISSING: ast
+ sink(s21.emplace(source()).first); // $ MISSING: ast,ir
sink(s21); // $ ast,ir
sink(s22.emplace_hint(s22.begin(), "abc"));
sink(s22);
@@ -131,7 +131,7 @@ void test_unordered_set()
std::unordered_set s1, s2, s3, s4, s5, s6;
sink(s1.insert("abc").first);
- sink(s2.insert(source()).first); // $ ir MISSING: ast
+ sink(s2.insert(source()).first); // $ MISSING: ast,ir
sink(s3.insert(s3.begin(), "abc"));
sink(s4.insert(s4.begin(), source())); // $ ast,ir
s5.insert(s1.begin(), s1.end());
@@ -180,8 +180,8 @@ void test_unordered_set()
s11.insert("a");
s11.insert(source());
s11.insert("c");
- sink(s11.equal_range("b").first); // $ ir MISSING: ast
- sink(s11.equal_range("b").second); // $ ir MISSING: ast
+ sink(s11.equal_range("b").first); // $ MISSING: ast,ir
+ sink(s11.equal_range("b").second); // $ MISSING: ast,ir
// swap
std::unordered_set s12, s13, s14, s15;
@@ -229,7 +229,7 @@ void test_unordered_set()
std::unordered_set s21, s22;
sink(s21.emplace("abc").first);
sink(s21);
- sink(s21.emplace(source()).first); // $ ir MISSING: ast
+ sink(s21.emplace(source()).first); // $ MISSING: ast,ir
sink(s21); // $ ast,ir
sink(s22.emplace_hint(s22.begin(), "abc"));
sink(s22);
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/smart_pointer.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/smart_pointer.cpp
index 28e44779009..0771c1b7432 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/smart_pointer.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/smart_pointer.cpp
@@ -101,7 +101,7 @@ void taint_x(A* pa) {
void reverse_taint_smart_pointer() {
std::unique_ptr p = std::unique_ptr(new A);
taint_x(p.get());
- sink(p->x); // $ ast,ir
+ sink(p->x); // $ ast MISSING: ir
}
struct C {
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp
index 9ce653c654a..78428c60224 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp
@@ -127,7 +127,7 @@ void test_range_based_for_loop_string() {
}
for(char& c : s) {
- sink(c); // $ ast,ir
+ sink(c); // $ ast MISSING: ir
}
const std::string const_s(source());
@@ -337,9 +337,9 @@ void test_string_at()
b.at(0) = ns_char::source();
c[0] = a[0];
- sink(a); // $ ast MISSING: ir
- sink(b); // $ ast MISSING: ir
- sink(c); // $ ast MISSING: ir
+ sink(a); // $ ast,ir
+ sink(b); // $ ast,ir
+ sink(c); // $ ast,ir
}
void test_string_data_more()
@@ -347,8 +347,8 @@ void test_string_data_more()
std::string str("123");
str.data()[1] = ns_char::source();
- sink(str); // $ ast MISSING: ir
- sink(str.data()); // $ ast MISSING: ir
+ sink(str); // $ ast,ir
+ sink(str.data()); // $ ast,ir
}
void test_string_iterators() {
@@ -540,7 +540,7 @@ void test_string_return_assign() {
sink(b);
sink(c); // $ ast,ir
sink(d); // $ ast,ir
- sink(e); // $ ast MISSING: ir
+ sink(e); // $ ast,ir
sink(f); // $ ast,ir
}
@@ -560,7 +560,7 @@ void test_string_return_assign() {
sink(b);
sink(c); // $ ast,ir
sink(d); // $ ast,ir
- sink(e); // $ ast MISSING: ir
+ sink(e); // $ ast,ir
sink(f); // $ SPURIOUS: ast,ir
}
}
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp
index 249c8ac21bf..a84b3606f92 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp
@@ -36,12 +36,12 @@ void test_stringstream_string(int amount)
sink(ss1);
sink(ss2); // $ ast,ir
- sink(ss3); // $ ast MISSING: ir
+ sink(ss3); // $ ast,ir
sink(ss4); // $ ast,ir
sink(ss5); // $ ast,ir
sink(ss1.str());
sink(ss2.str()); // $ ast,ir
- sink(ss3.str()); // $ ast MISSING: ir
+ sink(ss3.str()); // $ ast,ir
sink(ss4.str()); // $ ast,ir
sink(ss5.str()); // $ ast,ir
@@ -57,14 +57,14 @@ void test_stringstream_string(int amount)
sink(ss10.put('a').put(ns_char::source()).put('z')); // $ ast,ir
sink(ss8);
sink(ss9); // $ ast,ir
- sink(ss10); // $ ast MISSING: ir
+ sink(ss10); // $ ast,ir
sink(ss11.write("begin", 5));
sink(ss12.write(source(), 5)); // $ ast,ir
sink(ss13.write("begin", 5).write(source(), amount).write("end", 3)); // $ ast,ir
sink(ss11);
sink(ss12); // $ ast,ir
- sink(ss13); // $ ast MISSING: ir
+ sink(ss13); // $ ast,ir
}
void test_stringstream_int(int source)
@@ -264,5 +264,5 @@ void test_chaining()
sink(b2); // $ ast,ir
sink(ss2.write("abc", 3).flush().write(source(), 3).flush().write("xyz", 3)); // $ ast,ir
- sink(ss2); // $ ast MISSING: ir
+ sink(ss2); // $ ast,ir
}
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp
index 92a44f2950c..83a4a29046b 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp
@@ -38,9 +38,9 @@ void do_source()
global10 = zero(source());
sink(global6);
- sink(global7); // $ ast MISSING: ir
- sink(global8); // $ ast MISSING: ir
- sink(global9); // $ ast MISSING: ir
+ sink(global7); // $ ast,ir
+ sink(global8); // $ ast,ir
+ sink(global9); // $ ast,ir
sink(global10);
}
@@ -87,11 +87,11 @@ void class_field_test() {
sink(mc1.a);
sink(mc1.b); // $ ast,ir
- sink(mc1.c); // $ ast,ir
+ sink(mc1.c); // $ ast MISSING: ir
sink(mc1.d); // $ ast,ir
sink(mc2.a);
sink(mc2.b); // $ ast,ir
- sink(mc2.c); // $ ast,ir
+ sink(mc2.c); // $ ast MISSING: ir
sink(mc2.d);
}
@@ -126,12 +126,12 @@ void pointer_test() {
*p2 = source();
- sink(*p1); // $ ast,ir
+ sink(*p1); // $ ast MISSING: ir
sink(*p2); // $ ast,ir
sink(*p3);
p3 = &t1;
- sink(*p3); // $ ast,ir
+ sink(*p3); // $ ast MISSING: ir
*p3 = 0;
sink(*p3); // $ SPURIOUS: ast
@@ -233,7 +233,7 @@ void test_lambdas()
sink(a()); // $ ast,ir
auto b = [&] {
- sink(t); // $ ast MISSING: ir
+ sink(t); // $ ast,ir
sink(u); // clean
v = source(); // (v is reference captured)
};
@@ -448,9 +448,9 @@ void test_qualifiers()
sink(b);
sink(b.getMember());
b.member = source();
- sink(b); // $ ir MISSING: ast
+ sink(b); // $ MISSING: ast,ir
sink(b.member); // $ ast,ir
- sink(b.getMember()); // $ ir MISSING: ast
+ sink(b.getMember()); // $ MISSING: ast,ir
c = new MyClass2(0);
@@ -648,7 +648,7 @@ void test__strnextc(const char* source) {
unsigned c = 0;
do {
c = _strnextc(source++);
- sink(c); // $ ast,ir
+ sink(c); // $ ast MISSING: ir
} while(c != '\0');
c = _strnextc("");
sink(c);
@@ -665,7 +665,7 @@ public:
void test_no_const_member(char* source) {
C_no_const_member_function c;
memcpy(c.data(), source, 16);
- sink(c.data()); // $ ast MISSING: ir
+ sink(c.data()); // $ ast,ir
}
class C_const_member_function {
@@ -691,6 +691,6 @@ void test_argument_source_field_to_obj() {
argument_source(s.x);
sink(s); // $ SPURIOUS: ast
- sink(s.x); // $ ast MISSING: ir
+ sink(s.x); // $ ast,ir
sink(s.y); // clean
}
\ No newline at end of file
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.ql b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.ql
index 6bb8e2a5a9b..4bf7420a9fd 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.ql
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.ql
@@ -82,7 +82,7 @@ module IRTest {
TestAllocationConfig() { this = "TestAllocationConfig" }
override predicate isSource(DataFlow::Node source) {
- source.(DataFlow::ExprNode).getConvertedExpr().(FunctionCall).getTarget().getName() = "source"
+ source.asConvertedExpr().(FunctionCall).getTarget().getName() = "source"
or
source.asParameter().getName().matches("source%")
or
@@ -95,11 +95,11 @@ module IRTest {
override predicate isSink(DataFlow::Node sink) {
exists(FunctionCall call |
call.getTarget().getName() = "sink" and
- sink.(DataFlow::ExprNode).getConvertedExpr() = call.getAnArgument()
+ sink.asConvertedExpr() = call.getAnArgument()
or
call.getTarget().getName() = "sink" and
sink.asExpr() = call.getAnArgument() and
- sink.(DataFlow::ExprNode).getConvertedExpr() instanceof ReferenceDereferenceExpr
+ sink.asConvertedExpr() instanceof ReferenceDereferenceExpr
)
or
exists(ReadSideEffectInstruction read |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
index aacef1f4a5b..704246fd5a9 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
@@ -25,7 +25,7 @@ void test_range_based_for_loop_vector(int source1) {
}
for(int& x : v) {
- sink(x); // $ ast,ir
+ sink(x); // $ ast MISSING: ir
}
const std::vector const_v(100, source1);
@@ -49,22 +49,22 @@ void test_element_taint(int x) {
sink(v1.back());
v2[0] = source();
- sink(v2); // $ ast MISSING: ir
- sink(v2[0]); // $ ast MISSING: ir
- sink(v2[1]); // $ SPURIOUS: ast
- sink(v2[x]); // $ ast MISSING: ir
+ sink(v2); // $ ast,ir
+ sink(v2[0]); // $ ast,ir
+ sink(v2[1]); // $ SPURIOUS: ast,ir
+ sink(v2[x]); // $ ast,ir
v3 = v2;
- sink(v3); // $ ast MISSING: ir
- sink(v3[0]); // $ ast MISSING: ir
- sink(v3[1]); // $ SPURIOUS: ast
- sink(v3[x]); // $ ast MISSING: ir
+ sink(v3); // $ ast,ir
+ sink(v3[0]); // $ ast,ir
+ sink(v3[1]); // $ SPURIOUS: ast,ir
+ sink(v3[x]); // $ ast,ir
v4[x] = source();
- sink(v4); // $ ast MISSING: ir
- sink(v4[0]); // $ ast MISSING: ir
- sink(v4[1]); // $ ast MISSING: ir
- sink(v4[x]); // $ ast MISSING: ir
+ sink(v4); // $ ast,ir
+ sink(v4[0]); // $ ast,ir
+ sink(v4[1]); // $ ast,ir
+ sink(v4[x]); // $ ast,ir
v5.push_back(source());
sink(v5); // $ ast,ir
@@ -72,8 +72,8 @@ void test_element_taint(int x) {
sink(v5.back()); // $ ast,ir
v6.data()[2] = source();
- sink(v6); // $ ast MISSING: ir
- sink(v6.data()[2]); // $ ast MISSING: ir
+ sink(v6); // $ ast,ir
+ sink(v6.data()[2]); // $ ast,ir
{
@@ -94,10 +94,10 @@ void test_element_taint(int x) {
sink(v8.back()); // $ MISSING: ast,ir
v9.at(x) = source();
- sink(v9); // $ ast MISSING: ir
- sink(v9.at(0)); // $ ast MISSING: ir
- sink(v9.at(1)); // $ ast MISSING: ir
- sink(v9.at(x)); // $ ast MISSING: ir
+ sink(v9); // $ ast,ir
+ sink(v9.at(0)); // $ ast,ir
+ sink(v9.at(1)); // $ ast,ir
+ sink(v9.at(x)); // $ ast,ir
}
void test_vector_swap() {
@@ -168,7 +168,7 @@ void test_nested_vectors()
bb[0].push_back(0);
sink(bb[0][0]);
bb[0][0] = source();
- sink(bb[0][0]); // $ ast MISSING: ir
+ sink(bb[0][0]); // $ ast,ir
}
{
@@ -177,7 +177,7 @@ void test_nested_vectors()
cc[0].push_back(0);
sink(cc[0][0]);
cc[0][0] = source();
- sink(cc[0][0]); // $ ast MISSING: ir
+ sink(cc[0][0]); // $ ast,ir
}
{
@@ -188,7 +188,7 @@ void test_nested_vectors()
sink(dd[0].a);
sink(dd[0].b);
dd[0].a = source();
- sink(dd[0].a); // $ MISSING: ast,ir
+ sink(dd[0].a); // $ ir MISSING: ast
sink(dd[0].b);
}
@@ -198,7 +198,7 @@ void test_nested_vectors()
ee.vs.push_back(0);
sink(ee.vs[0]);
ee.vs[0] = source();
- sink(ee.vs[0]); // $ ast MISSING: ir
+ sink(ee.vs[0]); // $ ast,ir
}
{
@@ -209,7 +209,7 @@ void test_nested_vectors()
ff.push_back(mvc);
sink(ff[0].vs[0]);
ff[0].vs[0] = source();
- sink(ff[0].vs[0]); // $ MISSING: ast,ir
+ sink(ff[0].vs[0]); // $ ir MISSING: ast
}
}
@@ -287,9 +287,9 @@ void test_data_more() {
sink(v1.data()[2]); // $ ast,ir
*(v2.data()) = ns_int::source();
- sink(v2); // $ ast MISSING: ir
- sink(v2.data()); // $ ast MISSING: ir
- sink(v2.data()[2]); // $ ast MISSING: ir
+ sink(v2); // $ ast,ir
+ sink(v2.data()); // $ ast,ir
+ sink(v2.data()[2]); // $ ast,ir
}
void sink(std::vector::iterator);
@@ -470,7 +470,7 @@ void test_vector_memcpy()
sink(v);
memcpy(&v[i], &s, sizeof(int));
- sink(v); // $ ast MISSING: ir
+ sink(v); // $ ast,ir
}
{
@@ -483,7 +483,7 @@ void test_vector_memcpy()
sink(cs);
memcpy(&cs[offs + 1], src.c_str(), len);
sink(src); // $ ast,ir
- sink(cs); // $ ast MISSING: ir
+ sink(cs); // $ ast,ir
}
}
From 21a1ee775880a3ecde4b383f7b3a85681ad7391f Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Wed, 20 Oct 2021 15:55:03 +0100
Subject: [PATCH 201/471] C++: Add annoying case in SSA.qll related to
'NewExpr' and accept test changes.
---
.../code/cpp/ir/dataflow/internal/Ssa.qll | 34 +++++++++++++++++++
.../test/library-tests/dataflow/fields/A.cpp | 12 +++----
.../test/library-tests/dataflow/fields/B.cpp | 4 +--
.../test/library-tests/dataflow/fields/C.cpp | 2 +-
.../dataflow/smart-pointers-taint/test.cpp | 4 +--
5 files changed, 45 insertions(+), 11 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
index aba581452ea..dfd75a74bcf 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
@@ -324,6 +324,30 @@ private module Cached {
use.hasRankInBlock(bb2, i2) and
flowOutOfAddressStep(use.getOperand(), nodeTo)
)
+ or
+ // This final case is a bit annoying. The write side effect on an expression like `a = new A;` writes
+ // to a fresh address returned by `operator new`, and there's no easy way to use the shared SSA
+ // library to hook that up to the assignment to `a`. So instead we flow to the _first_ use of the
+ // value computed by `operator new` that occurs after `nodeFrom` (to avoid a loop in the
+ // dataflow graph).
+ exists(WriteSideEffectInstruction write, IRBlock bb, int i1, int i2, Operand op |
+ nodeFrom.getInstruction().(CallInstruction).getStaticCallTarget() instanceof
+ Alloc::OperatorNewAllocationFunction and
+ write = nodeFrom.getStoreInstruction() and
+ bb.getInstruction(i1) = write and
+ bb.getInstruction(i2) = op.getUse() and
+ // Flow to an instruction that occurs later in the block.
+ valueFlow*(nodeFrom.getInstruction(), op.getDef()) and
+ nodeTo.asOperand() = op and
+ i2 > i1 and
+ // There is no previous instruction that also occurs after `nodeFrom`.
+ not exists(Instruction instr, int i |
+ bb.getInstruction(i) = instr and
+ valueFlow(instr, op.getDef()) and
+ i1 < i and
+ i < i2
+ )
+ )
}
private predicate fromReadNode(ReadNode nodeFrom, Node nodeTo) {
@@ -402,6 +426,16 @@ private module Cached {
)
}
+ private predicate valueFlow(Instruction iFrom, Instruction iTo) {
+ iTo.(CopyValueInstruction).getSourceValue() = iFrom
+ or
+ iTo.(ConvertInstruction).getUnary() = iFrom
+ or
+ iTo.(CheckedConvertOrNullInstruction).getUnary() = iFrom
+ or
+ iTo.(InheritanceConversionInstruction).getUnary() = iFrom
+ }
+
private predicate flowOutOfAddressStep(Operand operand, Node nTo) {
// Flow into a read node
exists(ReadNode readNode | readNode = nTo |
diff --git a/cpp/ql/test/library-tests/dataflow/fields/A.cpp b/cpp/ql/test/library-tests/dataflow/fields/A.cpp
index 395d32f1cd0..3c8cfb3551b 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/A.cpp
+++ b/cpp/ql/test/library-tests/dataflow/fields/A.cpp
@@ -46,7 +46,7 @@ public:
{
C *c = new C();
B *b = B::make(c);
- sink(b->c); // $ast MISSING: ir
+ sink(b->c); // $ast,ir
}
void f2()
@@ -54,7 +54,7 @@ public:
B *b = new B();
b->set(new C1());
sink(b->get()); // $ ast ir=55:12
- sink((new B(new C()))->get()); // $ ast MISSING: ir
+ sink((new B(new C()))->get()); // $ ast,ir
}
void f3()
@@ -149,8 +149,8 @@ public:
{
B *b = new B();
D *d = new D(b, r());
- sink(d->b); // $ ast MISSING: ir
- sink(d->b->c); // $ ast MISSING: ir
+ sink(d->b); // $ ast,ir=143:25 ast,ir=150:12
+ sink(d->b->c); // $ ast,ir
sink(b->c); // $ ast,ir
}
@@ -162,11 +162,11 @@ public:
MyList *l3 = new MyList(nullptr, l2);
sink(l3->head); // no flow, b is nested beneath at least one ->next
sink(l3->next->head); // no flow
- sink(l3->next->next->head); // $ ast MISSING: ir
+ sink(l3->next->next->head); // $ ast,ir
sink(l3->next->next->next->head); // no flow
for (MyList *l = l3; l != nullptr; l = l->next)
{
- sink(l->head); // $ ast MISSING: ir
+ sink(l->head); // $ ast,ir
}
}
diff --git a/cpp/ql/test/library-tests/dataflow/fields/B.cpp b/cpp/ql/test/library-tests/dataflow/fields/B.cpp
index 3df917afa0d..57c142b5771 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/B.cpp
+++ b/cpp/ql/test/library-tests/dataflow/fields/B.cpp
@@ -6,7 +6,7 @@ class B
Elem *e = new Elem();
Box1 *b1 = new Box1(e, nullptr);
Box2 *b2 = new Box2(b1);
- sink(b2->box1->elem1); // $ ast MISSING: ir
+ sink(b2->box1->elem1); // $ ast,ir
sink(b2->box1->elem2); // no flow
}
@@ -16,7 +16,7 @@ class B
Box1 *b1 = new B::Box1(nullptr, e);
Box2 *b2 = new Box2(b1);
sink(b2->box1->elem1); // no flow
- sink(b2->box1->elem2); // $ ast MISSING: ir
+ sink(b2->box1->elem2); // $ ast,ir
}
static void sink(void *o) {}
diff --git a/cpp/ql/test/library-tests/dataflow/fields/C.cpp b/cpp/ql/test/library-tests/dataflow/fields/C.cpp
index bc1c662c3db..98121af7055 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/C.cpp
+++ b/cpp/ql/test/library-tests/dataflow/fields/C.cpp
@@ -26,7 +26,7 @@ public:
void func()
{
- sink(s1); // $ast MISSING: ir
+ sink(s1); // $ast,ir
sink(s2); // $ MISSING: ast,ir
sink(s3); // $ast MISSING: ir
sink(s4); // $ MISSING: ast,ir
diff --git a/cpp/ql/test/library-tests/dataflow/smart-pointers-taint/test.cpp b/cpp/ql/test/library-tests/dataflow/smart-pointers-taint/test.cpp
index 7836bc01c8e..0e890f8aec4 100644
--- a/cpp/ql/test/library-tests/dataflow/smart-pointers-taint/test.cpp
+++ b/cpp/ql/test/library-tests/dataflow/smart-pointers-taint/test.cpp
@@ -21,9 +21,9 @@ void test_unique_ptr_struct() {
std::unique_ptr p1(new A{source(), 0});
std::unique_ptr p2 = std::make_unique(source(), 0);
- sink(p1->x); // $ MISSING: ast,ir
+ sink(p1->x); // $ ir MISSING: ast
sink(p1->y);
- sink(p2->x); // $ MISSING: ast,ir
+ sink(p2->x); // $ ir=22:46 MISSING: ast
sink(p2->y);
}
From 2547a8d746e1c1c6d04b447a391f65ec50315ce2 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Wed, 20 Oct 2021 16:12:55 +0100
Subject: [PATCH 202/471] C++: Fix join orders in 'DataFlowDispatch.qll' and
`Ssa.qll`.
---
.../ir/dataflow/internal/DataFlowDispatch.qll | 7 +++++--
.../code/cpp/ir/dataflow/internal/Ssa.qll | 17 +++++++++++++----
2 files changed, 18 insertions(+), 6 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll
index 4ebd8cbf758..9b421df2df3 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll
@@ -63,8 +63,10 @@ private module VirtualDispatch {
|
// Call argument
exists(DataFlowCall call, int i |
- other.(DataFlow::ParameterNode).isParameterOf(call.getStaticCallTarget(), i) and
- src.(ArgumentNode).argumentOf(call, i)
+ other
+ .(DataFlow::ParameterNode)
+ .isParameterOf(pragma[only_bind_into](call).getStaticCallTarget(), i) and
+ src.(ArgumentNode).argumentOf(call, pragma[only_bind_into](pragma[only_bind_out](i)))
) and
allowOtherFromArg = true and
allowFromArg = true
@@ -128,6 +130,7 @@ private module VirtualDispatch {
*
* Used to fix a join ordering issue in flowsFrom.
*/
+ pragma[noinline]
private predicate returnNodeWithKindAndEnclosingCallable(
ReturnNode node, ReturnKind kind, DataFlowCallable callable
) {
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
index dfd75a74bcf..11705a43774 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
@@ -436,6 +436,16 @@ private module Cached {
iTo.(InheritanceConversionInstruction).getUnary() = iFrom
}
+ pragma[noinline]
+ private predicate callTargetHasInputOutput(
+ CallInstruction call, DataFlow::FunctionInput input, DataFlow::FunctionOutput output
+ ) {
+ exists(DataFlow::DataFlowFunction func |
+ call.getStaticCallTarget() = func and
+ func.hasDataFlow(input, output)
+ )
+ }
+
private predicate flowOutOfAddressStep(Operand operand, Node nTo) {
// Flow into a read node
exists(ReadNode readNode | readNode = nTo |
@@ -500,13 +510,12 @@ private module Cached {
or
// Flow through a modelled function that has parameter -> return value flow.
exists(
- CallInstruction call, DataFlow::DataFlowFunction func, int index,
- DataFlow::FunctionInput input, DataFlow::FunctionOutput output
+ CallInstruction call, int index, DataFlow::FunctionInput input,
+ DataFlow::FunctionOutput output
|
- call.getStaticCallTarget() = func and
+ callTargetHasInputOutput(call, input, output) and
call.getArgumentOperand(index) = operand and
not getSideEffectFor(call, index) instanceof ReadSideEffectInstruction and
- func.hasDataFlow(input, output) and
input.isParameter(index) and
output.isReturnValue() and
flowOutOfAddressStep(call.getAUse(), nTo)
From 521d863429aaef64d72a62f5b1dde194648427bb Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Wed, 20 Oct 2021 21:17:23 +0100
Subject: [PATCH 203/471] C++: Autoformat.
---
.../code/cpp/ir/dataflow/internal/DataFlowPrivate.qll | 2 +-
.../semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
index 8852d54d7b8..9c3a092346d 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
@@ -241,7 +241,7 @@ private predicate suppressUnusedNode(Node n) { any() }
// Java QL library compatibility wrappers
//////////////////////////////////////////////////////////////////////////////
/** A node that performs a type cast. */
-class CastNode extends InstructionNode {
+class CastNode extends Node {
CastNode() { none() } // stub implementation
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
index 076ee494bc5..3f4f8bb7281 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
@@ -202,7 +202,7 @@ class OperandNode extends Node, TOperandNode {
/**
* INTERNAL: do not use.
- *
+ *
* A `StoreNode` is a node that has been (or is about to be) the
* source or target of a `storeStep`.
*/
@@ -295,7 +295,7 @@ private class StoreNodeOperand extends StoreNode, TStoreNodeOperand {
/**
* INTERNAL: do not use.
- *
+ *
* A `ReadNode` is a node that has been (or is about to be) the
* source or target of a `readStep`.
*/
@@ -350,7 +350,7 @@ class ReadNode extends Node, TReadNode {
/**
* INTERNAL: do not use.
- *
+ *
* A phi node produced by the shared SSA library, viewed as a node in a data flow graph.
*/
class SsaPhiNode extends Node, TSsaPhiNode {
From 228e9e973ab7bbf1b52923129133d313f8ef1870 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 28 Oct 2021 13:35:20 +0200
Subject: [PATCH 204/471] Python: Minor flask refactor
---
.../ql/lib/semmle/python/frameworks/Flask.qll | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/python/ql/lib/semmle/python/frameworks/Flask.qll b/python/ql/lib/semmle/python/frameworks/Flask.qll
index fa31bd77220..4a46acd9017 100644
--- a/python/ql/lib/semmle/python/frameworks/Flask.qll
+++ b/python/ql/lib/semmle/python/frameworks/Flask.qll
@@ -525,13 +525,20 @@ module Flask {
*
* See https://flask.palletsprojects.com/en/1.1.x/api/#flask.send_from_directory
*/
- class FlaskSendFromDirectory extends FileSystemAccess::Range, DataFlow::CallCfgNode {
- FlaskSendFromDirectory() {
+ private class FlaskSendFromDirectoryCall extends FileSystemAccess::Range, DataFlow::CallCfgNode {
+ FlaskSendFromDirectoryCall() {
this = API::moduleImport("flask").getMember("send_from_directory").getACall()
}
override DataFlow::Node getAPathArgument() {
- result in [this.getArg(_), this.getArgByName(["directory", "filename"])]
+ result in [
+ this.getArg(0), this.getArgByName("directory"),
+ // as described in the docs, the `filename` argument is restrained to be within
+ // the provided directory, so is not exposed to path-injection. (but is still a
+ // path-argument).
+ this.getArg(1), this.getArgByName("filename")
+ // TODO: Exclude filename as path-injection sink
+ ]
}
}
@@ -540,8 +547,8 @@ module Flask {
*
* See https://flask.palletsprojects.com/en/1.1.x/api/#flask.send_file
*/
- class FlaskSendFile extends FileSystemAccess::Range, DataFlow::CallCfgNode {
- FlaskSendFile() { this = API::moduleImport("flask").getMember("send_file").getACall() }
+ private class FlaskSendFileCall extends FileSystemAccess::Range, DataFlow::CallCfgNode {
+ FlaskSendFileCall() { this = API::moduleImport("flask").getMember("send_file").getACall() }
override DataFlow::Node getAPathArgument() {
result in [this.getArg(0), this.getArgByName("filename_or_fp")]
From 2cd23e5ee0ab289c81da6ad53088bae11bd8fdbf Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Thu, 28 Oct 2021 10:30:53 +0100
Subject: [PATCH 205/471] Accept test changes.
---
.../dataflow/fields/ir-path-flow.expected | 34 +-
.../dataflow/taint-tests/arrayassignment.cpp | 6 +-
.../dataflow/taint-tests/map.cpp | 6 +-
.../dataflow/taint-tests/string.cpp | 2 +-
.../dataflow/taint-tests/taint.cpp | 2 +-
.../dataflow/taint-tests/vector.cpp | 2 +-
.../dataflow-ir-consistency.expected | 1335 +++++++++++++++--
.../SAMATE/TaintedPath/TaintedPath.expected | 4 +
.../CWE-022/semmle/tests/TaintedPath.expected | 4 +
.../SAMATE/ExecTainted/ExecTainted.expected | 10 +-
.../semmle/tests/UnboundedWrite.expected | 2 +
.../SAMATE/UncontrolledFormatString.expected | 8 +
.../CWE-134/semmle/argv/argvLocal.expected | 92 +-
.../CWE-134/semmle/funcs/funcsLocal.expected | 48 +
...olledFormatStringThroughGlobalVar.expected | 36 +-
.../Security/CWE/CWE-134/semmle/ifs/ifs.c | 4 +-
.../CWE/CWE-134/semmle/ifs/ifs.expected | 36 +
.../ArithmeticUncontrolled.expected | 47 +-
.../semmle/ArithmeticUncontrolled/test.c | 2 +-
.../semmle/ArithmeticUncontrolled/test.cpp | 2 +-
.../TaintedAllocationSize.expected | 42 +-
.../semmle/tainted/ArithmeticTainted.expected | 12 +-
.../TaintedCondition.expected | 8 -
.../CWE-807/semmle/TaintedCondition/test.cpp | 2 +-
24 files changed, 1483 insertions(+), 263 deletions(-)
diff --git a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected
index 703bf4d4eb1..8f070732ab3 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected
+++ b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected
@@ -389,25 +389,19 @@ edges
| by_reference.cpp:24:5:24:17 | value | by_reference.cpp:11:48:11:52 | value |
| by_reference.cpp:24:5:24:17 | value | by_reference.cpp:24:5:24:17 | this [post update] [a] |
| by_reference.cpp:31:46:31:46 | *s [a] | by_reference.cpp:32:12:32:12 | s [read] [a] |
-| by_reference.cpp:31:46:31:46 | s [a] | by_reference.cpp:32:12:32:12 | s [read] [a] |
| by_reference.cpp:32:12:32:12 | s [read] [a] | by_reference.cpp:32:15:32:15 | FieldAddress [read] |
-| by_reference.cpp:32:12:32:12 | s [read] [a] | by_reference.cpp:32:15:32:15 | FieldAddress [read] |
-| by_reference.cpp:32:15:32:15 | FieldAddress [read] | by_reference.cpp:31:16:31:28 | ReturnValue |
| by_reference.cpp:32:15:32:15 | FieldAddress [read] | by_reference.cpp:31:16:31:28 | ReturnValue |
| by_reference.cpp:35:9:35:19 | *#this [a] | by_reference.cpp:36:12:36:15 | this [read] [a] |
-| by_reference.cpp:35:9:35:19 | this [a] | by_reference.cpp:36:12:36:15 | this [read] [a] |
-| by_reference.cpp:36:12:36:15 | this [read] [a] | by_reference.cpp:36:18:36:18 | FieldAddress [read] |
| by_reference.cpp:36:12:36:15 | this [read] [a] | by_reference.cpp:36:18:36:18 | FieldAddress [read] |
| by_reference.cpp:36:18:36:18 | FieldAddress [read] | by_reference.cpp:35:9:35:19 | ReturnValue |
-| by_reference.cpp:36:18:36:18 | FieldAddress [read] | by_reference.cpp:35:9:35:19 | ReturnValue |
-| by_reference.cpp:39:9:39:21 | *#this [a] | by_reference.cpp:40:18:40:28 | this [a] |
+| by_reference.cpp:39:9:39:21 | *#this [a] | by_reference.cpp:40:12:40:15 | this indirection [a] |
+| by_reference.cpp:40:12:40:15 | this indirection [a] | by_reference.cpp:35:9:35:19 | *#this [a] |
+| by_reference.cpp:40:12:40:15 | this indirection [a] | by_reference.cpp:40:18:40:28 | call to getDirectly |
| by_reference.cpp:40:18:40:28 | call to getDirectly | by_reference.cpp:39:9:39:21 | ReturnValue |
-| by_reference.cpp:40:18:40:28 | this [a] | by_reference.cpp:35:9:35:19 | this [a] |
-| by_reference.cpp:40:18:40:28 | this [a] | by_reference.cpp:40:18:40:28 | call to getDirectly |
-| by_reference.cpp:43:9:43:27 | *#this [a] | by_reference.cpp:44:12:44:24 | this [a] |
+| by_reference.cpp:43:9:43:27 | *#this [a] | by_reference.cpp:44:26:44:29 | this indirection [a] |
| by_reference.cpp:44:12:44:24 | call to nonMemberGetA | by_reference.cpp:43:9:43:27 | ReturnValue |
-| by_reference.cpp:44:12:44:24 | this [a] | by_reference.cpp:31:46:31:46 | s [a] |
-| by_reference.cpp:44:12:44:24 | this [a] | by_reference.cpp:44:12:44:24 | call to nonMemberGetA |
+| by_reference.cpp:44:26:44:29 | this indirection [a] | by_reference.cpp:31:46:31:46 | *s [a] |
+| by_reference.cpp:44:26:44:29 | this indirection [a] | by_reference.cpp:44:12:44:24 | call to nonMemberGetA |
| by_reference.cpp:50:5:50:15 | call to user_input | by_reference.cpp:15:26:15:30 | value |
| by_reference.cpp:50:5:50:15 | call to user_input | by_reference.cpp:50:5:50:15 | s [post update] [a] |
| by_reference.cpp:50:5:50:15 | s [post update] [a] | by_reference.cpp:51:8:51:8 | s indirection [a] |
@@ -1226,29 +1220,21 @@ nodes
| by_reference.cpp:24:5:24:17 | value | semmle.label | value |
| by_reference.cpp:24:19:24:22 | this [post update] [a] | semmle.label | this [post update] [a] |
| by_reference.cpp:31:16:31:28 | ReturnValue | semmle.label | ReturnValue |
-| by_reference.cpp:31:16:31:28 | ReturnValue | semmle.label | ReturnValue |
| by_reference.cpp:31:46:31:46 | *s [a] | semmle.label | *s [a] |
-| by_reference.cpp:31:46:31:46 | s [a] | semmle.label | s [a] |
| by_reference.cpp:32:12:32:12 | s [read] [a] | semmle.label | s [read] [a] |
-| by_reference.cpp:32:12:32:12 | s [read] [a] | semmle.label | s [read] [a] |
-| by_reference.cpp:32:15:32:15 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| by_reference.cpp:32:15:32:15 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| by_reference.cpp:35:9:35:19 | *#this [a] | semmle.label | *#this [a] |
| by_reference.cpp:35:9:35:19 | ReturnValue | semmle.label | ReturnValue |
-| by_reference.cpp:35:9:35:19 | ReturnValue | semmle.label | ReturnValue |
-| by_reference.cpp:35:9:35:19 | this [a] | semmle.label | this [a] |
| by_reference.cpp:36:12:36:15 | this [read] [a] | semmle.label | this [read] [a] |
-| by_reference.cpp:36:12:36:15 | this [read] [a] | semmle.label | this [read] [a] |
-| by_reference.cpp:36:18:36:18 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| by_reference.cpp:36:18:36:18 | FieldAddress [read] | semmle.label | FieldAddress [read] |
| by_reference.cpp:39:9:39:21 | *#this [a] | semmle.label | *#this [a] |
| by_reference.cpp:39:9:39:21 | ReturnValue | semmle.label | ReturnValue |
+| by_reference.cpp:40:12:40:15 | this indirection [a] | semmle.label | this indirection [a] |
| by_reference.cpp:40:18:40:28 | call to getDirectly | semmle.label | call to getDirectly |
-| by_reference.cpp:40:18:40:28 | this [a] | semmle.label | this [a] |
| by_reference.cpp:43:9:43:27 | *#this [a] | semmle.label | *#this [a] |
| by_reference.cpp:43:9:43:27 | ReturnValue | semmle.label | ReturnValue |
| by_reference.cpp:44:12:44:24 | call to nonMemberGetA | semmle.label | call to nonMemberGetA |
-| by_reference.cpp:44:12:44:24 | this [a] | semmle.label | this [a] |
+| by_reference.cpp:44:26:44:29 | this indirection [a] | semmle.label | this indirection [a] |
| by_reference.cpp:50:5:50:15 | call to user_input | semmle.label | call to user_input |
| by_reference.cpp:50:5:50:15 | s [post update] [a] | semmle.label | s [post update] [a] |
| by_reference.cpp:50:17:50:26 | call to user_input | semmle.label | call to user_input |
@@ -1694,8 +1680,8 @@ subpaths
| D.cpp:51:19:51:25 | e | D.cpp:11:24:11:24 | e | D.cpp:11:29:11:32 | this [post update] [elem] | D.cpp:51:19:51:25 | call to getBox1 [post update] [elem] |
| by_reference.cpp:20:11:20:21 | value | by_reference.cpp:15:26:15:30 | value | by_reference.cpp:16:5:16:8 | this [post update] [a] | by_reference.cpp:20:11:20:21 | this [post update] [a] |
| by_reference.cpp:24:5:24:17 | value | by_reference.cpp:11:48:11:52 | value | by_reference.cpp:12:5:12:5 | s [post update] [a] | by_reference.cpp:24:5:24:17 | this [post update] [a] |
-| by_reference.cpp:40:18:40:28 | this [a] | by_reference.cpp:35:9:35:19 | this [a] | by_reference.cpp:35:9:35:19 | ReturnValue | by_reference.cpp:40:18:40:28 | call to getDirectly |
-| by_reference.cpp:44:12:44:24 | this [a] | by_reference.cpp:31:46:31:46 | s [a] | by_reference.cpp:31:16:31:28 | ReturnValue | by_reference.cpp:44:12:44:24 | call to nonMemberGetA |
+| by_reference.cpp:40:12:40:15 | this indirection [a] | by_reference.cpp:35:9:35:19 | *#this [a] | by_reference.cpp:35:9:35:19 | ReturnValue | by_reference.cpp:40:18:40:28 | call to getDirectly |
+| by_reference.cpp:44:26:44:29 | this indirection [a] | by_reference.cpp:31:46:31:46 | *s [a] | by_reference.cpp:31:16:31:28 | ReturnValue | by_reference.cpp:44:12:44:24 | call to nonMemberGetA |
| by_reference.cpp:50:5:50:15 | call to user_input | by_reference.cpp:15:26:15:30 | value | by_reference.cpp:16:5:16:8 | this [post update] [a] | by_reference.cpp:50:5:50:15 | s [post update] [a] |
| by_reference.cpp:51:8:51:8 | s indirection [a] | by_reference.cpp:35:9:35:19 | *#this [a] | by_reference.cpp:35:9:35:19 | ReturnValue | by_reference.cpp:51:10:51:20 | call to getDirectly |
| by_reference.cpp:56:5:56:17 | call to user_input | by_reference.cpp:19:28:19:32 | value | by_reference.cpp:20:5:20:8 | this [post update] [a] | by_reference.cpp:56:5:56:17 | s [post update] [a] |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/arrayassignment.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/arrayassignment.cpp
index 011c6bc1fe7..b541e50c72b 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/arrayassignment.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/arrayassignment.cpp
@@ -54,7 +54,7 @@ void test_myint_member_assignment()
mi.i = source();
sink(mi); // $ MISSING: ast,ir
- sink(mi.get()); // $ ast,ir
+ sink(mi.get()); // $ ast MISSING: ir
}
void test_myint_method_assignment()
@@ -64,7 +64,7 @@ void test_myint_method_assignment()
mi.get() = source();
sink(mi); // $ ir MISSING: ast
- sink(mi.get()); // $ ast,ir
+ sink(mi.get()); // $ ast MISSING: ir
}
void test_myint_overloaded_assignment()
@@ -107,7 +107,7 @@ void test_myarray_method_assignment()
ma.get(0) = source();
- sink(ma.get(0)); // $ ir MISSING: ast
+ sink(ma.get(0)); // $ MISSING: ast,ir
}
void test_myarray_overloaded_assignment()
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/map.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/map.cpp
index e044dbb62a5..d98378a63b5 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/map.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/map.cpp
@@ -179,11 +179,11 @@ void test_map()
m14.insert(std::make_pair("b", source()));
m14.insert(std::make_pair("c", source()));
m14.insert(std::make_pair("d", "d"));
- sink(m2.lower_bound("b")); // $ ast MISSING: ir
- sink(m2.upper_bound("b")); // $ ast MISSING: ir
+ sink(m2.lower_bound("b")); // $ ast,ir
+ sink(m2.upper_bound("b")); // $ ast,ir
sink(m2.equal_range("b").first); // $ MISSING: ast,ir
sink(m2.equal_range("b").second); // $ MISSING: ast,ir
- sink(m2.upper_bound("c")); // $ SPURIOUS: ast
+ sink(m2.upper_bound("c")); // $ SPURIOUS: ast,ir
sink(m2.equal_range("c").second);
// swap
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp
index 78428c60224..e2b99945724 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp
@@ -127,7 +127,7 @@ void test_range_based_for_loop_string() {
}
for(char& c : s) {
- sink(c); // $ ast MISSING: ir
+ sink(c); // $ ast,ir
}
const std::string const_s(source());
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp
index 83a4a29046b..c2a70eae4fe 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp
@@ -648,7 +648,7 @@ void test__strnextc(const char* source) {
unsigned c = 0;
do {
c = _strnextc(source++);
- sink(c); // $ ast MISSING: ir
+ sink(c); // $ ast,ir
} while(c != '\0');
c = _strnextc("");
sink(c);
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
index 704246fd5a9..0fa7c5c2154 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
@@ -25,7 +25,7 @@ void test_range_based_for_loop_vector(int source1) {
}
for(int& x : v) {
- sink(x); // $ ast MISSING: ir
+ sink(x); // $ ast,ir
}
const std::vector const_v(100, source1);
diff --git a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected
index 511edc7e45a..d25d12c3372 100644
--- a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected
+++ b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected
@@ -51,6 +51,7 @@ uniqueNodeLocation
| allocators.cpp:14:5:14:8 | Phi | Node should have one location but has 4. |
| allocators.cpp:14:5:14:8 | Phi | Node should have one location but has 4. |
| allocators.cpp:14:5:14:8 | Phi | Node should have one location but has 4. |
+| allocators.cpp:14:5:14:8 | Phi | Node should have one location but has 4. |
| allocators.cpp:14:5:14:8 | ReturnValue | Node should have one location but has 4. |
| allocators.cpp:14:5:14:8 | SideEffect | Node should have one location but has 4. |
| allocators.cpp:14:5:14:8 | VariableAddress | Node should have one location but has 4. |
@@ -925,6 +926,7 @@ uniqueNodeLocation
| no_dynamic_init.cpp:9:5:9:8 | Phi | Node should have one location but has 4. |
| no_dynamic_init.cpp:9:5:9:8 | Phi | Node should have one location but has 4. |
| no_dynamic_init.cpp:9:5:9:8 | Phi | Node should have one location but has 4. |
+| no_dynamic_init.cpp:9:5:9:8 | Phi | Node should have one location but has 4. |
| no_dynamic_init.cpp:9:5:9:8 | ReturnValue | Node should have one location but has 4. |
| no_dynamic_init.cpp:9:5:9:8 | SideEffect | Node should have one location but has 4. |
| no_dynamic_init.cpp:9:5:9:8 | VariableAddress | Node should have one location but has 4. |
@@ -1084,6 +1086,7 @@ uniqueNodeLocation
| parameterinitializer.cpp:18:5:18:8 | Phi | Node should have one location but has 4. |
| parameterinitializer.cpp:18:5:18:8 | Phi | Node should have one location but has 4. |
| parameterinitializer.cpp:18:5:18:8 | Phi | Node should have one location but has 4. |
+| parameterinitializer.cpp:18:5:18:8 | Phi | Node should have one location but has 4. |
| parameterinitializer.cpp:18:5:18:8 | ReturnValue | Node should have one location but has 4. |
| parameterinitializer.cpp:18:5:18:8 | SideEffect | Node should have one location but has 4. |
| parameterinitializer.cpp:18:5:18:8 | VariableAddress | Node should have one location but has 4. |
@@ -1225,6 +1228,7 @@ uniqueNodeLocation
| stream_it.cpp:16:5:16:8 | Phi | Node should have one location but has 4. |
| stream_it.cpp:16:5:16:8 | Phi | Node should have one location but has 4. |
| stream_it.cpp:16:5:16:8 | Phi | Node should have one location but has 4. |
+| stream_it.cpp:16:5:16:8 | Phi | Node should have one location but has 4. |
| stream_it.cpp:16:5:16:8 | ReturnValue | Node should have one location but has 4. |
| stream_it.cpp:16:5:16:8 | SideEffect | Node should have one location but has 4. |
| stream_it.cpp:16:5:16:8 | VariableAddress | Node should have one location but has 4. |
@@ -1464,113 +1468,1234 @@ unreachableNodeCCtx
localCallNodes
postIsNotPre
postHasUniquePre
-| assignexpr.cpp:9:2:9:12 | Store | PostUpdateNode should have one pre-update node but has 0. |
-| bad_asts.cpp:15:10:15:12 | Store | PostUpdateNode should have one pre-update node but has 0. |
-| cpp11.cpp:65:19:65:45 | Store | PostUpdateNode should have one pre-update node but has 0. |
-| ir.cpp:531:14:531:14 | Store | PostUpdateNode should have one pre-update node but has 0. |
uniquePostUpdate
postIsInSameCallable
reverseRead
argHasPostUpdate
postWithInFlow
-| aggregateinitializer.c:3:14:3:18 | Chi | PostUpdateNode should not be the target of local flow. |
-| aggregateinitializer.c:3:21:3:25 | Chi | PostUpdateNode should not be the target of local flow. |
-| allocators.cpp:3:27:3:27 | Chi | PostUpdateNode should not be the target of local flow. |
-| allocators.cpp:3:35:3:35 | Chi | PostUpdateNode should not be the target of local flow. |
-| allocators.cpp:4:11:4:23 | Chi | PostUpdateNode should not be the target of local flow. |
-| allocators.cpp:4:17:4:23 | Chi | PostUpdateNode should not be the target of local flow. |
-| assignexpr.cpp:9:2:9:12 | Store | PostUpdateNode should not be the target of local flow. |
-| bad_asts.cpp:15:10:15:12 | Store | PostUpdateNode should not be the target of local flow. |
-| builtin.c:14:26:14:26 | Chi | PostUpdateNode should not be the target of local flow. |
-| builtin.c:14:29:14:29 | Chi | PostUpdateNode should not be the target of local flow. |
-| builtin.c:14:32:14:32 | Chi | PostUpdateNode should not be the target of local flow. |
-| builtin.c:14:35:14:35 | Chi | PostUpdateNode should not be the target of local flow. |
-| condition_decls.cpp:3:5:3:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| condition_decls.cpp:3:21:3:21 | Chi | PostUpdateNode should not be the target of local flow. |
-| conditional_destructors.cpp:6:13:6:19 | Chi | PostUpdateNode should not be the target of local flow. |
-| conditional_destructors.cpp:18:13:18:19 | Chi | PostUpdateNode should not be the target of local flow. |
-| cpp11.cpp:65:19:65:45 | Store | PostUpdateNode should not be the target of local flow. |
-| cpp11.cpp:82:17:82:55 | Chi | PostUpdateNode should not be the target of local flow. |
-| cpp11.cpp:82:17:82:55 | Chi | PostUpdateNode should not be the target of local flow. |
-| defdestructordeleteexpr.cpp:4:9:4:15 | Chi | PostUpdateNode should not be the target of local flow. |
-| deleteexpr.cpp:7:9:7:15 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:177:5:177:12 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:178:5:178:12 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:183:5:183:12 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:184:5:184:12 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:342:5:342:10 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:428:5:428:12 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:429:5:429:15 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:504:19:504:19 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:504:22:504:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:505:16:505:21 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:505:19:505:19 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:506:16:506:18 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:506:16:506:18 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:513:14:513:16 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:513:14:513:16 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:514:14:514:26 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:514:19:514:19 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:514:22:514:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:515:19:515:19 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:515:22:515:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:515:29:515:29 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:515:32:515:32 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:516:17:516:21 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:516:19:516:19 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:516:24:516:28 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:516:26:516:26 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:521:19:521:19 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:521:22:521:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:521:25:521:25 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:522:16:522:21 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:522:19:522:19 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:531:14:531:14 | Store | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:577:16:577:21 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:577:19:577:19 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:578:19:578:19 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:578:22:578:22 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:579:16:579:21 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:579:19:579:19 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:643:9:643:21 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:644:9:644:23 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:645:9:645:15 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:659:9:659:14 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:660:13:660:13 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:661:9:661:13 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:662:9:662:19 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:663:5:663:5 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:745:8:745:8 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:745:8:745:8 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:748:10:748:10 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:754:8:754:8 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:757:12:757:12 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:763:8:763:8 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:766:13:766:13 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:775:15:775:15 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:784:15:784:15 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:793:15:793:15 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:943:3:943:11 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:947:3:947:25 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:962:17:962:47 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:962:17:962:47 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:962:17:962:47 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:962:26:962:30 | Chi | PostUpdateNode should not be the target of local flow. |
-| ir.cpp:962:41:962:45 | Chi | PostUpdateNode should not be the target of local flow. |
-| misc.c:130:5:130:11 | Chi | PostUpdateNode should not be the target of local flow. |
-| misc.c:131:5:131:13 | Chi | PostUpdateNode should not be the target of local flow. |
-| misc.c:154:32:154:32 | Chi | PostUpdateNode should not be the target of local flow. |
-| misc.c:154:35:154:35 | Chi | PostUpdateNode should not be the target of local flow. |
-| misc.c:154:40:154:40 | Chi | PostUpdateNode should not be the target of local flow. |
-| misc.c:154:43:154:43 | Chi | PostUpdateNode should not be the target of local flow. |
-| misc.c:157:14:157:18 | Chi | PostUpdateNode should not be the target of local flow. |
-| misc.c:158:14:158:18 | Chi | PostUpdateNode should not be the target of local flow. |
-| misc.c:160:31:160:33 | Chi | PostUpdateNode should not be the target of local flow. |
-| misc.c:160:31:160:33 | Chi | PostUpdateNode should not be the target of local flow. |
-| misc.c:220:3:223:3 | Chi | PostUpdateNode should not be the target of local flow. |
-| misc.c:221:10:221:10 | Chi | PostUpdateNode should not be the target of local flow. |
-| misc.c:222:10:222:10 | Chi | PostUpdateNode should not be the target of local flow. |
-| range_analysis.c:102:5:102:15 | Chi | PostUpdateNode should not be the target of local flow. |
-| static_init_templates.cpp:3:2:3:8 | Chi | PostUpdateNode should not be the target of local flow. |
-| static_init_templates.cpp:21:2:21:12 | Chi | PostUpdateNode should not be the target of local flow. |
-| static_init_templates.cpp:240:7:240:7 | Chi | PostUpdateNode should not be the target of local flow. |
+| FunctionTryStmt.cpp:2:3:2:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| VacuousDestructorCall.cpp:10:3:10:16 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| VacuousDestructorCall.cpp:10:21:10:22 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| VacuousDestructorCall.cpp:10:22:10:22 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| abortingfunctions.cpp:49:5:49:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aggregateinitializer.c:3:6:3:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| aggregateinitializer.c:3:11:3:27 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| aggregateinitializer.c:3:11:3:27 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| allocators.cpp:3:23:3:28 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| allocators.cpp:3:31:3:36 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| allocators.cpp:4:11:4:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| allocators.cpp:4:11:4:13 | m_x [post update] | PostUpdateNode should not be the target of local flow. |
+| allocators.cpp:4:17:4:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| allocators.cpp:4:17:4:19 | m_y [post update] | PostUpdateNode should not be the target of local flow. |
+| allocators.cpp:7:56:7:70 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| allocators.cpp:16:8:16:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| allocators.cpp:16:14:16:36 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| allocators.cpp:16:14:16:36 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| allocators.cpp:16:14:16:36 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| allocators.cpp:18:1:18:1 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| assignexpr.cpp:9:4:9:4 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| bad_asts.cpp:10:7:10:23 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| bad_asts.cpp:15:10:15:12 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| bad_asts.cpp:16:5:16:5 | s [post update] | PostUpdateNode should not be the target of local flow. |
+| bad_asts.cpp:16:7:16:23 | s [post update] | PostUpdateNode should not be the target of local flow. |
+| bad_asts.cpp:27:11:27:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| break_labels.c:3:9:3:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| break_labels.c:5:9:5:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| break_labels.c:7:17:7:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| break_labels.c:8:17:8:22 | result [post update] | PostUpdateNode should not be the target of local flow. |
+| break_labels.c:18:9:18:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| break_labels.c:20:9:20:9 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| break_labels.c:20:24:20:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| break_labels.c:27:9:27:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:8:3:8:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:12:3:12:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:14:18:14:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:14:23:14:37 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:14:23:14:37 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:14:23:14:37 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:14:23:14:37 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:15:18:15:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:20:3:20:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:21:3:21:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:22:3:22:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:29:5:29:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:34:3:34:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:34:10:34:20 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:34:22:34:31 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:34:22:34:31 | (volatile void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:34:23:34:31 | staticint [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:39:3:39:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:39:10:39:23 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:39:36:39:45 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:39:37:39:45 | carry_out [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:43:3:43:24 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:43:40:43:49 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:43:41:43:49 | staticint [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:45:3:45:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:48:2:48:4 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:51:3:51:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:51:10:51:27 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:51:29:51:38 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:51:30:51:38 | staticint [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:54:3:54:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:54:10:54:26 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:54:28:54:38 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:54:29:54:38 | atomic_int [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.c:56:3:56:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.cpp:5:5:5:34 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.cpp:10:10:10:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.cpp:10:14:10:22 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.cpp:10:24:10:24 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.cpp:10:24:10:24 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.cpp:14:11:14:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.cpp:15:5:15:29 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.cpp:15:31:15:35 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.cpp:15:32:15:35 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| builtin.cpp:15:33:15:35 | ptr [post update] | PostUpdateNode should not be the target of local flow. |
+| condition_decl_int.cpp:2:7:2:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| condition_decl_int.cpp:3:9:3:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| condition_decls.cpp:3:5:3:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| condition_decls.cpp:3:5:3:9 | m_ptr [post update] | PostUpdateNode should not be the target of local flow. |
+| condition_decls.cpp:3:13:3:22 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| condition_decls.cpp:3:13:3:22 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| condition_decls.cpp:9:5:9:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| condition_decls.cpp:16:6:16:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| condition_decls.cpp:16:19:16:20 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| condition_decls.cpp:26:10:26:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| condition_decls.cpp:26:23:26:24 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| condition_decls.cpp:41:9:41:23 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| condition_decls.cpp:41:22:41:23 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| condition_decls.cpp:48:16:48:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| condition_decls.cpp:48:22:48:24 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| condition_decls.cpp:48:27:48:31 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| condition_decls.cpp:48:34:48:36 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| condition_decls.cpp:48:39:48:53 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| condition_decls.cpp:48:52:48:53 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| conditional_destructors.cpp:6:13:6:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| conditional_destructors.cpp:6:13:6:15 | val [post update] | PostUpdateNode should not be the target of local flow. |
+| conditional_destructors.cpp:10:9:10:32 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| conditional_destructors.cpp:18:13:18:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| conditional_destructors.cpp:18:13:18:15 | val [post update] | PostUpdateNode should not be the target of local flow. |
+| conditional_destructors.cpp:25:9:25:32 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| conditional_destructors.cpp:30:9:30:13 | call to C1 [post update] | PostUpdateNode should not be the target of local flow. |
+| conditional_destructors.cpp:30:9:30:13 | temporary object [post update] | PostUpdateNode should not be the target of local flow. |
+| conditional_destructors.cpp:30:18:30:22 | call to C1 [post update] | PostUpdateNode should not be the target of local flow. |
+| conditional_destructors.cpp:30:18:30:22 | temporary object [post update] | PostUpdateNode should not be the target of local flow. |
+| conditional_destructors.cpp:33:9:33:13 | call to C1 [post update] | PostUpdateNode should not be the target of local flow. |
+| conditional_destructors.cpp:33:9:33:13 | temporary object [post update] | PostUpdateNode should not be the target of local flow. |
+| conditional_destructors.cpp:33:18:33:22 | call to C1 [post update] | PostUpdateNode should not be the target of local flow. |
+| conditional_destructors.cpp:33:18:33:22 | temporary object [post update] | PostUpdateNode should not be the target of local flow. |
+| conditional_destructors.cpp:39:9:39:13 | call to C2 [post update] | PostUpdateNode should not be the target of local flow. |
+| conditional_destructors.cpp:39:9:39:13 | temporary object [post update] | PostUpdateNode should not be the target of local flow. |
+| conditional_destructors.cpp:39:18:39:22 | call to C2 [post update] | PostUpdateNode should not be the target of local flow. |
+| conditional_destructors.cpp:39:18:39:22 | temporary object [post update] | PostUpdateNode should not be the target of local flow. |
+| conditional_destructors.cpp:42:9:42:13 | call to C2 [post update] | PostUpdateNode should not be the target of local flow. |
+| conditional_destructors.cpp:42:9:42:13 | temporary object [post update] | PostUpdateNode should not be the target of local flow. |
+| conditional_destructors.cpp:42:18:42:22 | call to C2 [post update] | PostUpdateNode should not be the target of local flow. |
+| conditional_destructors.cpp:42:18:42:22 | temporary object [post update] | PostUpdateNode should not be the target of local flow. |
+| constmemberaccess.cpp:9:2:9:2 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| constructorinitializer.cpp:8:4:8:4 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| constructorinitializer.cpp:8:6:8:18 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:6:5:6:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:6:5:6:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:6:5:6:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:6:16:6:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:7:7:7:8 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:7:7:7:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:7:7:7:8 | el [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:28:5:28:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:28:5:28:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:28:5:28:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:28:16:28:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:28:21:28:21 | (__begin) [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:28:21:28:21 | (__begin) [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:28:21:28:21 | (__begin) [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:28:21:28:21 | (__begin) [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:28:21:28:21 | (__range) [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:28:21:28:21 | (__range) [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:28:21:28:21 | (__range) [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:28:21:28:21 | (__range) [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:28:21:28:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:28:21:28:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:28:21:28:34 | temporary object [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:29:7:29:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:31:5:31:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:56:14:56:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:56:14:56:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:60:15:60:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:60:15:60:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:65:10:65:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:65:19:65:45 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:65:35:65:43 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:77:19:77:21 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:77:19:77:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:82:11:82:14 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:82:11:82:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:82:17:82:55 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:82:17:82:55 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:82:17:82:55 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:82:45:82:48 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:82:45:82:48 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:82:51:82:51 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:82:51:82:51 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:88:25:88:30 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:88:25:88:30 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:88:33:88:38 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:88:33:88:38 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:99:5:99:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:108:5:108:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:148:13:148:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:150:13:150:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:169:7:169:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp17.cpp:15:5:15:45 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp17.cpp:15:5:15:45 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp17.cpp:15:5:15:45 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp17.cpp:19:5:19:8 | 1 [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp17.cpp:19:5:19:8 | 2 [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp17.cpp:19:5:19:8 | p [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp17.cpp:19:10:19:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp17.cpp:19:10:19:10 | p [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp17.cpp:19:13:19:13 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp17.cpp:19:13:19:13 | temporary object [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp17.cpp:19:16:19:16 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp17.cpp:19:16:19:16 | temporary object [post update] | PostUpdateNode should not be the target of local flow. |
+| defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| defdestructordeleteexpr.cpp:4:9:4:15 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| defdestructordeleteexpr.cpp:4:9:4:15 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| deleteexpr.cpp:7:5:7:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| deleteexpr.cpp:7:9:7:15 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| deleteexpr.cpp:7:9:7:15 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| destructors.cpp:11:7:11:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| destructors.cpp:16:5:16:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| destructors.cpp:19:3:19:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| destructors.cpp:50:9:50:13 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| destructors.cpp:50:9:50:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| destructors.cpp:51:16:51:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| destructors.cpp:51:22:51:56 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| destructors.cpp:51:22:51:56 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| destructors.cpp:51:36:51:38 | call to C [post update] | PostUpdateNode should not be the target of local flow. |
+| destructors.cpp:51:36:51:38 | temporary object [post update] | PostUpdateNode should not be the target of local flow. |
+| destructors.cpp:52:7:52:26 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dostmt.c:33:7:33:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| dostmt.c:35:7:35:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| duff2.c:3:9:3:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| duff2.c:13:16:13:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| duff2.c:17:9:17:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| duff2.c:21:16:21:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| duff.c:3:9:3:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| duff.c:13:24:13:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ellipsisexceptionhandler.cpp:6:4:6:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ellipsisexceptionhandler.cpp:16:18:16:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| exceptionhandler.cpp:7:2:7:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| exceptionhandler.cpp:14:4:14:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| fieldaccess.cpp:9:2:9:2 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| file://:0:0:0:0 | (Base *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| file://:0:0:0:0 | (Middle *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| file://:0:0:0:0 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| file://:0:0:0:0 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| file://:0:0:0:0 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| file://:0:0:0:0 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| file://:0:0:0:0 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| file://:0:0:0:0 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| file://:0:0:0:0 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| file://:0:0:0:0 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| file://:0:0:0:0 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| file://:0:0:0:0 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| file://:0:0:0:0 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| forstmt.cpp:2:14:2:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| forstmt.cpp:2:29:2:29 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| forstmt.cpp:9:14:9:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| forstmt.cpp:14:14:14:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| forstmt.cpp:14:27:14:27 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| forstmt.cpp:19:14:19:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| forstmt.cpp:19:28:19:28 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| initializer.c:3:6:3:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:2:10:2:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:3:10:3:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:5:17:5:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:6:17:6:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:8:19:8:22 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:9:19:9:22 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:11:11:11:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:12:20:12:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:14:9:14:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:15:18:15:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:17:10:17:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:18:19:18:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:20:15:20:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:21:15:21:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:22:24:22:28 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:23:24:23:30 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:25:10:25:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:26:10:26:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:28:13:28:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:29:13:29:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:31:14:31:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:32:14:32:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:34:11:34:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:35:11:35:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:36:11:36:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:38:12:38:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:39:12:39:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:40:12:40:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:44:9:44:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:45:11:45:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:46:5:46:5 | y [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:47:5:47:5 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:53:5:53:5 | z [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:54:5:54:5 | z [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:55:5:55:5 | z [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:56:5:56:5 | z [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:57:5:57:5 | z [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:59:5:59:5 | z [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:60:5:60:5 | z [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:61:5:61:5 | z [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:63:5:63:5 | z [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:64:5:64:5 | z [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:66:5:66:5 | z [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:68:5:68:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:69:5:69:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:70:5:70:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:71:5:71:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:72:5:72:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:74:5:74:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:75:5:75:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:76:5:76:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:78:5:78:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:79:5:79:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:81:5:81:5 | z [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:82:5:82:5 | z [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:83:5:83:5 | z [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:84:5:84:5 | z [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:90:5:90:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:91:5:91:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:92:5:92:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:93:5:93:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:94:5:94:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:95:5:95:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:101:5:101:5 | y [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:101:11:101:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:102:5:102:5 | y [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:102:11:102:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:103:5:103:5 | y [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:103:9:103:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:104:5:104:5 | y [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:104:9:104:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:110:5:110:5 | p [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:110:13:110:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:111:5:111:5 | p [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:111:13:111:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:117:5:117:5 | z [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:118:5:118:5 | z [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:119:5:119:5 | z [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:120:5:120:5 | z [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:122:5:122:5 | z [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:124:5:124:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:125:5:125:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:126:5:126:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:127:5:127:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:129:5:129:5 | z [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:130:5:130:5 | z [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:136:5:136:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:137:5:137:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:138:5:138:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:139:5:139:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:140:5:140:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:141:5:141:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:147:5:147:5 | y [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:147:11:147:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:148:5:148:5 | y [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:148:11:148:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:149:5:149:5 | y [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:149:9:149:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:150:5:150:5 | y [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:150:9:150:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:157:5:157:5 | q [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:158:5:158:5 | q [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:159:5:159:5 | q [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:160:5:160:5 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:162:5:162:5 | q [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:164:5:164:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:165:5:165:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:167:5:167:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:168:5:168:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:174:5:174:5 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:175:5:175:5 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:177:5:177:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:177:5:177:5 | p [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:177:5:177:8 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:178:5:178:8 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:178:7:178:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:178:7:178:7 | p [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:181:5:181:5 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:182:5:182:5 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:183:5:183:5 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:183:5:183:5 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:183:5:183:8 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:184:5:184:8 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:184:7:184:7 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:184:7:184:7 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:188:10:188:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:189:14:189:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:190:13:190:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:196:5:196:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:197:5:197:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:198:5:198:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:199:5:199:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:200:5:200:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:201:5:201:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:207:5:207:5 | q [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:207:11:207:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:208:5:208:5 | q [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:208:11:208:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:209:5:209:5 | q [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:209:9:209:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:210:5:210:5 | q [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:210:9:210:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:215:9:215:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:216:5:216:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:219:11:219:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:220:5:220:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:223:5:223:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:226:10:226:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:227:5:227:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:232:9:232:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:236:5:236:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:244:9:244:9 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:248:9:248:9 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:250:9:250:9 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:255:9:255:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:261:9:261:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:273:14:273:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:279:9:279:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:286:9:286:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:287:13:287:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:293:14:293:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:299:14:299:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:299:22:299:22 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:305:9:305:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:306:20:306:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:312:14:312:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:312:29:312:29 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:318:14:318:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:318:29:318:29 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:326:14:326:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:326:29:326:29 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:334:14:334:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:342:5:342:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:342:6:342:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:342:6:342:6 | p [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:343:5:343:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:349:5:349:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:356:9:356:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:365:9:365:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:377:5:377:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:381:5:381:37 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:390:13:390:13 | y [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:395:13:395:13 | y [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:399:13:399:13 | y [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:401:13:401:13 | y [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:405:13:405:13 | y [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:423:5:423:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:428:8:428:8 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:429:8:429:8 | y [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:430:10:430:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:436:9:436:9 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:440:9:440:9 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:443:9:443:9 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:450:9:450:9 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:454:9:454:9 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:457:9:457:9 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:464:9:464:9 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:468:9:468:9 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:471:9:471:9 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:477:5:477:5 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:477:9:477:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:477:9:477:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:478:5:478:5 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:478:9:478:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:478:9:478:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:479:5:479:5 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:479:11:479:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:479:11:479:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:483:9:483:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:483:13:483:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:483:13:483:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:489:6:489:14 | ... ? ... : ... [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:489:6:489:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:489:6:489:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:489:6:489:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:497:10:497:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:498:10:498:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:499:5:499:5 | p [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:500:5:500:5 | q [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:504:16:504:24 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:504:16:504:24 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:505:16:505:21 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:505:16:505:21 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:506:16:506:18 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:506:16:506:18 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:508:9:508:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:509:9:509:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:513:14:513:16 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:513:14:513:16 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:514:14:514:26 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:514:17:514:24 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:514:17:514:24 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:515:17:515:24 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:515:17:515:24 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:515:27:515:34 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:515:27:515:34 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:516:17:516:21 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:516:17:516:21 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:516:24:516:28 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:516:24:516:28 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:520:9:520:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:520:16:520:18 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:521:9:521:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:521:16:521:27 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:521:16:521:27 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:521:16:521:27 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:522:9:522:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:522:16:522:21 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:522:16:522:21 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:531:11:531:16 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:540:5:540:5 | y [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:545:9:545:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:548:5:548:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:552:5:552:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:563:13:563:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:565:13:565:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:567:13:567:22 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:572:10:572:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:573:10:573:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:574:10:574:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:576:10:576:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:576:16:576:18 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:577:10:577:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:577:16:577:21 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:577:16:577:21 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:578:10:578:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:578:16:578:24 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:578:16:578:24 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:579:10:579:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:579:16:579:21 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:579:16:579:21 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:585:5:585:18 | string [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:585:32:585:39 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:585:32:585:39 | string [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:591:11:591:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:592:5:592:7 | pfn [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:593:5:593:7 | pfn [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:594:5:594:7 | pfn [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:616:12:616:13 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:616:12:616:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:617:12:617:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:617:15:617:22 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:618:12:618:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:619:12:619:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:619:16:619:30 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:631:9:631:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:635:9:635:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:639:9:639:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:643:9:643:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:643:15:643:17 | m_a [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:644:11:644:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:644:11:644:14 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:644:17:644:19 | m_a [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:645:9:645:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:645:9:645:11 | m_a [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:647:9:647:9 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:648:9:648:9 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:649:9:649:9 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:653:9:653:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:653:9:653:12 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:653:15:653:36 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:654:10:654:14 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:654:11:654:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:654:11:654:14 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:654:17:654:38 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:655:9:655:30 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:655:9:655:30 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:655:9:655:30 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:659:9:659:14 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:660:9:660:14 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:661:9:661:13 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:662:9:662:19 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:662:9:662:19 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:663:5:663:5 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:663:5:663:5 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:676:5:676:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:680:5:680:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:686:10:686:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:687:10:687:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:688:19:688:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:693:9:693:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:694:7:694:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:698:8:698:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:699:8:699:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:705:3:705:25 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:705:10:705:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:705:10:705:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:709:3:709:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:716:5:716:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:716:12:716:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:721:3:721:54 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:721:10:721:39 | 0 [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:721:41:721:47 | (void *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:726:9:726:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:728:7:728:28 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:733:5:733:5 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:736:5:736:19 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:736:5:736:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:745:8:745:8 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:745:8:745:8 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:745:8:745:8 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:745:8:745:8 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:745:8:745:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:745:8:745:8 | base_s [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:748:10:748:10 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:748:10:748:10 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:754:8:754:8 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:754:8:754:8 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:754:8:754:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:754:8:754:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:754:8:754:8 | middle_s [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:754:8:754:8 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:754:8:754:8 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:757:3:757:8 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:757:12:757:12 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:757:12:757:12 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:757:12:757:12 | ConvertToNonVirtualBase [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:757:12:757:12 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:763:8:763:8 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:763:8:763:8 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:763:8:763:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:763:8:763:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:763:8:763:8 | derived_s [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:763:8:763:8 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:763:8:763:8 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:766:3:766:9 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:766:13:766:13 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:766:13:766:13 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:766:13:766:13 | ConvertToNonVirtualBase [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:766:13:766:13 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:775:3:775:11 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:775:15:775:15 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:775:15:775:15 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:775:15:775:15 | ConvertToNonVirtualBase [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:775:15:775:15 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:784:3:784:11 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:784:15:784:15 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:784:15:784:15 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:784:15:784:15 | ConvertToNonVirtualBase [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:784:15:784:15 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:793:3:793:11 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:793:15:793:15 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:793:15:793:15 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:793:15:793:15 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:793:15:793:15 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:793:15:793:15 | ConvertToNonVirtualBase [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:793:15:793:15 | ConvertToNonVirtualBase [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:793:15:793:15 | ConvertToNonVirtualBase [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:793:15:793:15 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:800:8:800:8 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:800:8:800:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:801:10:801:10 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:801:10:801:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:802:11:802:11 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:802:11:802:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:804:9:804:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:805:11:805:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:806:12:806:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:808:3:808:3 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:808:5:808:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:809:3:809:3 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:809:5:809:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:809:7:809:13 | call to Base [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:809:7:809:13 | temporary object [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:810:3:810:3 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:810:5:810:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:810:7:810:26 | call to Base [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:810:7:810:26 | temporary object [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:811:3:811:4 | pb [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:812:3:812:4 | pb [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:813:3:813:4 | pb [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:814:3:814:4 | pb [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:816:3:816:3 | m [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:816:5:816:5 | m [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:817:3:817:3 | m [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:817:5:817:5 | m [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:818:3:818:4 | pm [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:819:3:819:4 | pm [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:820:3:820:4 | pm [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:822:3:822:3 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:822:5:822:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:823:3:823:3 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:823:5:823:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:823:7:823:13 | call to Base [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:823:7:823:13 | temporary object [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:824:3:824:3 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:824:5:824:5 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:824:7:824:26 | call to Base [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:824:7:824:26 | temporary object [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:825:3:825:4 | pb [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:826:3:826:4 | pb [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:827:3:827:4 | pb [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:828:3:828:4 | pb [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:830:3:830:3 | d [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:830:5:830:5 | d [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:831:3:831:3 | d [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:831:5:831:5 | d [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:832:3:832:4 | pd [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:833:3:833:4 | pd [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:834:3:834:4 | pd [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:836:14:836:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:837:14:837:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:838:3:838:4 | pb [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:839:3:839:4 | pb [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:846:8:846:8 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:846:8:846:8 | ConvertToNonVirtualBase [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:846:8:846:8 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:850:19:850:19 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:850:19:850:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:851:22:851:22 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:851:22:851:22 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:853:20:853:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:854:23:854:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:857:3:857:4 | pb [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:858:20:858:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:860:3:860:4 | pd [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:861:23:861:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:863:9:863:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:864:15:864:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:867:1:867:14 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:868:3:868:12 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:873:15:873:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:874:3:874:3 | p [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:875:3:875:3 | p [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:876:3:876:3 | p [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:877:10:877:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:878:16:878:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:879:16:879:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:880:3:880:4 | pa [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:884:3:884:3 | p [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:885:3:885:5 | pfn [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:891:22:891:25 | args [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:891:22:891:25 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:893:22:893:26 | args2 [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:893:22:893:26 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:894:10:894:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:894:31:894:34 | args [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:894:31:894:34 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:895:9:895:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:895:30:895:33 | args [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:895:30:895:33 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:905:8:905:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:906:7:906:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:906:11:906:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:943:3:943:11 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:943:3:943:11 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:944:3:944:14 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:944:3:944:14 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:944:3:944:14 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:945:3:945:27 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:945:3:945:27 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:945:3:945:27 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:947:3:947:25 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:947:3:947:25 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:962:7:962:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:962:17:962:47 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:962:17:962:47 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:962:17:962:47 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:962:17:962:47 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:962:17:962:47 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
+| ir.cpp:963:3:963:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ltrbinopexpr.c:28:5:28:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ltrbinopexpr.c:29:5:29:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ltrbinopexpr.c:30:5:30:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ltrbinopexpr.c:31:5:31:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ltrbinopexpr.c:32:5:32:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ltrbinopexpr.c:33:5:33:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ltrbinopexpr.c:34:5:34:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ltrbinopexpr.c:35:5:35:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ltrbinopexpr.c:36:5:36:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ltrbinopexpr.c:37:5:37:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ltrbinopexpr.c:39:5:39:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ltrbinopexpr.c:40:5:40:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:18:5:18:5 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:19:5:19:5 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:20:7:20:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:21:5:21:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:23:9:23:9 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:25:9:25:9 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:28:9:28:9 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:30:9:30:9 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:33:9:33:9 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:35:9:35:9 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:38:9:38:9 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:40:9:40:9 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:45:9:45:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:48:9:48:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:51:9:51:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:54:9:54:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:57:9:57:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:60:9:60:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:62:9:62:9 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:62:24:62:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:64:19:64:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:66:9:66:9 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:66:18:66:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:93:5:93:5 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:93:9:93:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:93:9:93:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:94:5:94:5 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:94:9:94:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:94:9:94:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:123:9:123:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:124:9:124:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:124:25:124:26 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:126:5:126:5 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:129:5:129:6 | sp [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:130:7:130:7 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:131:5:131:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:131:9:131:9 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:132:5:132:5 | j [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:133:5:133:5 | j [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:135:5:135:5 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:136:5:136:5 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:137:5:137:5 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:147:5:147:5 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:154:23:154:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:154:31:154:36 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:154:31:154:36 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:154:39:154:44 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:154:39:154:44 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:156:31:159:5 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:156:31:159:5 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:160:31:160:33 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:160:31:160:33 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:165:5:165:5 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:178:17:178:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:179:17:179:23 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:188:5:188:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:200:24:200:27 | args [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:200:24:200:27 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:208:1:208:3 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:216:3:216:26 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:220:3:220:5 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:220:4:220:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:220:4:220:5 | sp [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:220:9:223:3 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:220:9:223:3 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:228:7:228:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:229:7:229:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:230:2:230:3 | p1 [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:231:2:231:40 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| modeled-functions.cpp:6:3:6:8 | 0 [post update] | PostUpdateNode should not be the target of local flow. |
+| modeled-functions.cpp:6:3:6:8 | 0 [post update] | PostUpdateNode should not be the target of local flow. |
+| modeled-functions.cpp:6:19:6:19 | (char *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| modeled-functions.cpp:6:22:6:22 | (unsigned long *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| ms_assume.cpp:13:8:13:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ms_assume.cpp:14:8:14:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ms_assume.cpp:28:3:28:8 | result [post update] | PostUpdateNode should not be the target of local flow. |
+| ms_assume.cpp:28:12:28:16 | buffer [post update] | PostUpdateNode should not be the target of local flow. |
+| ms_assume.cpp:28:18:28:23 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| ms_assume.cpp:28:18:28:23 | buffer [post update] | PostUpdateNode should not be the target of local flow. |
+| ms_assume.cpp:34:1:34:1 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ms_try_mix.cpp:11:7:11:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ms_try_mix.cpp:11:12:11:15 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ms_try_mix.cpp:28:7:28:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ms_try_mix.cpp:28:12:28:15 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| ms_try_mix.cpp:48:5:48:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ms_try_mix.cpp:48:10:48:13 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| newexpr.cpp:8:2:8:20 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| newexpr.cpp:8:2:8:20 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| newexpr.cpp:8:2:8:20 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| no_dynamic_init.cpp:11:3:11:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ops.cpp:19:11:19:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ops.cpp:20:11:20:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ops.cpp:21:11:21:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ops.cpp:26:26:26:27 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| ops.cpp:26:31:26:53 | Call [post update] | PostUpdateNode should not be the target of local flow. |
+| ops.cpp:26:31:26:53 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| ops.cpp:26:31:26:53 | new [post update] | PostUpdateNode should not be the target of local flow. |
+| parameterinitializer.cpp:8:5:8:10 | Got %d\n [post update] | PostUpdateNode should not be the target of local flow. |
+| parameterinitializer.cpp:8:12:8:21 | (char *)... [post update] | PostUpdateNode should not be the target of local flow. |
+| parameterinitializer.cpp:8:12:8:21 | Got %d\n [post update] | PostUpdateNode should not be the target of local flow. |
+| parameterinitializer.cpp:8:12:8:21 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
+| parameterinitializer.cpp:25:5:25:8 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| parameterinitializer.cpp:25:5:25:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| parameterinitializer.cpp:27:3:27:6 | my_c [post update] | PostUpdateNode should not be the target of local flow. |
+| parameterinitializer.cpp:27:8:27:13 | my_c [post update] | PostUpdateNode should not be the target of local flow. |
+| parameterinitializer.cpp:30:5:30:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| pointer_to_member.cpp:23:6:23:8 | obj [post update] | PostUpdateNode should not be the target of local flow. |
+| pointer_to_member.cpp:23:10:23:14 | obj [post update] | PostUpdateNode should not be the target of local flow. |
+| pointer_to_member.cpp:26:5:26:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| pointer_to_member.cpp:27:5:27:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| pointer_to_member.cpp:27:12:27:14 | obj [post update] | PostUpdateNode should not be the target of local flow. |
+| pointer_to_member.cpp:27:16:27:20 | obj [post update] | PostUpdateNode should not be the target of local flow. |
+| pointer_to_member.cpp:29:5:29:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| pruning.c:69:9:69:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| pruning.c:78:9:78:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| pruning.c:87:9:87:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| pruning.c:96:13:96:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| pruning.c:105:13:105:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| pruning.c:114:13:114:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| pruning.c:123:13:123:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| pruning.c:132:13:132:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| pruning.c:165:9:165:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| pruning.c:172:9:172:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| pruning.c:179:9:179:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| pruning.c:186:9:186:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| pruning.c:193:9:193:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| questionexpr.c:3:2:3:2 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| questionexpr.c:3:6:3:27 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| questionexpr.c:3:6:3:27 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:6:7:6:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:7:13:7:13 | p [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:8:5:8:9 | count [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:10:3:10:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:14:7:14:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:15:13:15:13 | p [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:16:5:16:9 | count [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:18:3:18:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:22:7:22:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:23:13:23:13 | p [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:24:5:24:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:25:5:25:9 | count [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:27:3:27:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:31:7:31:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:32:7:32:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:33:8:33:8 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:33:22:33:22 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:34:5:34:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:36:3:36:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:40:7:40:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:41:7:41:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:42:8:42:8 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:42:22:42:22 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:43:5:43:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:45:3:45:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:49:7:49:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:50:7:50:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:51:8:51:8 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:51:24:51:24 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:52:5:52:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:54:3:54:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:60:7:60:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:63:3:63:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:69:7:69:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:72:3:72:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:78:7:78:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:82:7:82:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:85:3:85:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:91:7:91:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:93:5:93:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:95:3:95:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:100:3:100:3 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:102:5:102:8 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:102:6:102:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:102:6:102:6 | p [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:102:6:102:8 | ... ++ [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:105:5:105:5 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:107:7:107:10 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:107:8:107:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:107:8:107:8 | p [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:107:8:107:10 | ... ++ [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:110:7:110:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:112:3:112:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:119:3:119:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:119:10:119:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:123:14:123:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:126:22:126:27 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:127:6:127:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:130:4:130:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:135:17:135:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:136:16:136:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:137:16:137:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:138:7:138:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:139:3:139:42 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:144:7:144:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:145:7:145:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:146:7:146:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:147:7:147:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:148:8:148:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:149:18:149:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:150:3:150:37 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:154:3:154:41 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:154:10:154:40 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:154:10:154:40 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:159:7:159:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:162:9:162:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:163:9:163:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:164:5:164:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:167:9:167:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:168:9:168:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:169:5:169:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:172:9:172:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:173:9:173:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:174:5:174:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:177:9:177:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:178:9:178:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:179:5:179:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:182:9:182:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:183:9:183:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:184:5:184:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:187:9:187:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:188:9:188:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:189:5:189:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:192:3:192:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:198:7:198:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:201:9:201:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:202:5:202:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:205:9:205:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:206:5:206:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:209:9:209:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:210:5:210:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:213:9:213:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:214:5:214:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:217:9:217:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:218:5:218:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:221:3:221:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:226:7:226:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:229:9:229:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:230:5:230:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:233:9:233:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:234:5:234:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:237:9:237:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:238:5:238:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:241:9:241:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:242:5:242:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:245:9:245:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:246:5:246:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:249:3:249:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:254:7:254:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:257:9:257:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:258:5:258:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:261:9:261:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:262:5:262:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:265:9:265:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:266:5:266:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:269:9:269:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:270:5:270:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:273:9:273:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:274:5:274:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:277:3:277:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:282:7:282:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:285:9:285:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:286:5:286:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:289:9:289:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:290:5:290:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:293:9:293:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:294:5:294:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:297:9:297:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:298:5:298:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:301:9:301:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:302:5:302:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:305:3:305:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:310:7:310:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:313:9:313:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:314:5:314:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:317:9:317:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:318:5:318:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:321:9:321:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:322:5:322:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:325:9:325:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:326:5:326:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:329:9:329:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:330:5:330:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:333:3:333:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:337:10:337:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:339:5:339:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:343:5:343:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:345:3:345:3 | d [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:348:7:348:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:351:3:351:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:357:3:357:4 | y1 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:357:8:357:23 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:357:8:357:23 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:358:3:358:4 | y2 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:358:8:358:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:358:8:358:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:359:3:359:4 | y3 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:360:3:360:4 | y4 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:361:3:361:4 | y5 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:362:3:362:4 | y6 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:363:3:363:4 | y7 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:364:3:364:4 | y8 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:366:5:366:6 | y3 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:366:10:366:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:366:10:366:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:367:5:367:6 | y4 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:367:10:367:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:367:10:367:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:368:5:368:6 | y5 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:368:10:368:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:368:10:368:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:369:5:369:6 | y6 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:369:10:369:36 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:369:10:369:36 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:370:5:370:6 | y7 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:370:10:370:38 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:370:10:370:38 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:371:5:371:6 | y8 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:371:10:371:39 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:371:10:371:39 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:373:3:373:47 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:379:3:379:4 | y1 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:379:8:379:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:379:8:379:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:380:3:380:4 | y2 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:380:8:380:25 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:380:8:380:25 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:381:3:381:4 | y3 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:382:3:382:4 | y4 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:383:3:383:4 | y5 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:385:5:385:6 | y3 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:385:10:385:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:385:10:385:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:386:5:386:6 | y4 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:386:10:386:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:386:10:386:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:387:5:387:6 | y5 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:387:10:387:38 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:387:10:387:38 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:389:3:389:32 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:394:16:394:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:394:20:394:36 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:394:20:394:36 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:397:3:397:4 | y1 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:397:11:397:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:398:3:398:4 | y2 [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:398:9:398:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:398:14:398:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| range_analysis.c:399:3:399:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| rethrow_error.cpp:17:3:17:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| returnstmt.c:8:5:8:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| revsubscriptexpr.c:4:2:4:2 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| shortforstmt.cpp:35:5:35:5 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| statements.cpp:32:39:32:39 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| statements.cpp:48:16:48:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| statements.cpp:48:16:48:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| statements.cpp:48:22:48:22 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| statements.cpp:56:5:56:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:3:2:3:4 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:3:2:3:4 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:3:2:3:4 | ref [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:18:7:18:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:20:2:20:10 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:20:12:20:12 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:20:12:20:12 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:21:2:21:4 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:21:2:21:4 | val [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:22:2:22:8 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:31:10:31:11 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:31:10:31:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:33:11:33:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:81:5:81:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:81:5:81:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:90:5:90:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:90:5:90:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:98:5:98:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:98:5:98:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:106:5:106:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:106:5:106:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:126:5:126:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:134:5:134:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:227:19:227:26 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:233:24:233:31 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:236:7:236:7 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:236:7:236:7 | ConvertToNonVirtualBase [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:236:7:236:7 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:240:7:240:7 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:240:7:240:7 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:249:21:249:23 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:249:21:249:23 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:250:17:250:19 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:250:17:250:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:251:20:251:23 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| static_init_templates.cpp:251:20:251:23 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| staticlocals.cpp:4:5:4:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| staticlocals.cpp:8:5:8:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| staticlocals.cpp:12:16:12:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| staticlocals.cpp:12:16:12:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| staticlocals.cpp:12:25:12:25 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| staticlocals.cpp:12:25:12:25 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| staticlocals.cpp:13:16:13:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| staticlocals.cpp:13:16:13:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| staticlocals.cpp:18:3:18:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| staticlocals.cpp:26:19:26:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| staticlocals.cpp:29:14:29:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| staticlocals.cpp:29:14:29:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| stmt_expr.cpp:13:16:13:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| stmt_expr.cpp:13:18:13:19 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| stmt_expr.cpp:27:5:27:7 | ptr [post update] | PostUpdateNode should not be the target of local flow. |
+| stream_it.cpp:4:16:4:30 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| stream_it.cpp:5:14:5:28 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| stream_it.cpp:11:3:11:3 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| stream_it.cpp:11:3:11:3 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| stream_it.cpp:11:3:11:3 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| stream_it.cpp:11:10:11:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| stream_it.cpp:11:16:11:16 | (__range) [post update] | PostUpdateNode should not be the target of local flow. |
+| stream_it.cpp:11:16:11:16 | (__range) [post update] | PostUpdateNode should not be the target of local flow. |
+| stream_it.cpp:11:16:11:16 | (__range) [post update] | PostUpdateNode should not be the target of local flow. |
+| stream_it.cpp:11:16:11:16 | (__range) [post update] | PostUpdateNode should not be the target of local flow. |
+| stream_it.cpp:11:16:11:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| stream_it.cpp:11:16:11:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| stream_it.cpp:19:3:19:11 | xs [post update] | PostUpdateNode should not be the target of local flow. |
+| stream_it.cpp:19:13:19:14 | (reference to) [post update] | PostUpdateNode should not be the target of local flow. |
+| stream_it.cpp:19:13:19:14 | xs [post update] | PostUpdateNode should not be the target of local flow. |
+| stream_it.cpp:20:3:20:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| subscriptexpr.c:4:2:4:2 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| switchbody.c:5:11:5:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| switchbody.c:5:11:5:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| switchbody.c:7:5:7:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| switchbody.c:9:5:9:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| switchbody.c:16:11:16:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| switchbody.c:16:11:16:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| switchbody.c:19:5:19:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| switchbody.c:20:3:20:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| switchbody.c:28:11:28:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| switchbody.c:28:11:28:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| switchbody.c:31:9:31:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| switchbody.c:33:9:33:17 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.c:28:9:28:9 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| test.c:28:24:28:24 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.c:36:9:36:9 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| test.c:36:19:36:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.c:44:9:44:9 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| test.c:52:9:52:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.c:73:9:73:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.c:93:9:93:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.c:93:13:93:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.c:93:13:93:21 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.c:98:9:98:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.c:103:9:103:9 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.c:204:12:204:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.c:204:12:204:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.c:220:5:220:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.c:222:3:222:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.c:227:5:227:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.c:229:3:229:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.c:233:7:233:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| test.c:233:7:233:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| try_catch.cpp:7:8:7:8 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| try_catch.cpp:7:8:7:8 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| try_catch.cpp:7:8:7:8 | ConvertToNonVirtualBase [post update] | PostUpdateNode should not be the target of local flow. |
+| try_catch.cpp:7:8:7:8 | ConvertToNonVirtualBase [post update] | PostUpdateNode should not be the target of local flow. |
+| try_catch.cpp:7:8:7:8 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| try_catch.cpp:7:8:7:8 | this [post update] | PostUpdateNode should not be the target of local flow. |
+| try_catch.cpp:13:5:13:16 | Argument this [post update] | PostUpdateNode should not be the target of local flow. |
+| try_catch.cpp:13:5:13:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| try_catch.cpp:21:14:21:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| try_catch.cpp:23:8:23:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| unaryopexpr.c:10:5:10:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| unaryopexpr.c:11:5:11:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| unaryopexpr.c:12:7:12:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| unaryopexpr.c:13:7:13:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| void_bug.cpp:5:3:5:11 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| whilestmt.c:9:7:9:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| whilestmt.c:11:5:11:8 | done [post update] | PostUpdateNode should not be the target of local flow. |
+| whilestmt.c:40:7:40:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| whilestmt.c:42:7:42:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-022/SAMATE/TaintedPath/TaintedPath.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-022/SAMATE/TaintedPath/TaintedPath.expected
index 6271123d9d1..4f929e2b6c9 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-022/SAMATE/TaintedPath/TaintedPath.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-022/SAMATE/TaintedPath/TaintedPath.expected
@@ -1,9 +1,11 @@
edges
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | (const char *)... |
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data |
+| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data |
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data indirection |
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | (const char *)... |
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data |
+| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data |
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data indirection |
subpaths
nodes
@@ -12,6 +14,8 @@ nodes
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | (const char *)... | semmle.label | (const char *)... |
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | (const char *)... | semmle.label | (const char *)... |
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data | semmle.label | data |
+| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data | semmle.label | data |
+| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data | semmle.label | data |
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data indirection | semmle.label | data indirection |
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data indirection | semmle.label | data indirection |
#select
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected
index cb8dfd321b3..6069d6153bc 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected
@@ -3,6 +3,8 @@ edges
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | (const char *)... |
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
+| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
+| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName indirection |
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName indirection |
subpaths
@@ -12,6 +14,8 @@ nodes
| test.c:17:11:17:18 | (const char *)... | semmle.label | (const char *)... |
| test.c:17:11:17:18 | (const char *)... | semmle.label | (const char *)... |
| test.c:17:11:17:18 | fileName | semmle.label | fileName |
+| test.c:17:11:17:18 | fileName | semmle.label | fileName |
+| test.c:17:11:17:18 | fileName | semmle.label | fileName |
| test.c:17:11:17:18 | fileName indirection | semmle.label | fileName indirection |
| test.c:17:11:17:18 | fileName indirection | semmle.label | fileName indirection |
#select
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-078/SAMATE/ExecTainted/ExecTainted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-078/SAMATE/ExecTainted/ExecTainted.expected
index 51c8906abb5..8875333fbeb 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-078/SAMATE/ExecTainted/ExecTainted.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-078/SAMATE/ExecTainted/ExecTainted.expected
@@ -1,16 +1,16 @@
edges
+| tests.cpp:26:15:26:23 | ReturnValue | tests.cpp:51:12:51:20 | call to badSource |
| tests.cpp:33:34:33:39 | call to getenv | tests.cpp:38:39:38:49 | environment indirection |
-| tests.cpp:38:25:38:36 | strncat output argument | tests.cpp:42:5:42:16 | Phi |
+| tests.cpp:38:25:38:36 | strncat output argument | tests.cpp:26:15:26:23 | ReturnValue |
| tests.cpp:38:39:38:49 | environment indirection | tests.cpp:38:25:38:36 | strncat output argument |
| tests.cpp:38:39:38:49 | environment indirection | tests.cpp:38:25:38:36 | strncat output argument |
-| tests.cpp:42:5:42:16 | Phi | tests.cpp:51:22:51:25 | badSource output argument |
-| tests.cpp:51:22:51:25 | badSource output argument | tests.cpp:53:16:53:19 | data indirection |
+| tests.cpp:51:12:51:20 | call to badSource | tests.cpp:53:16:53:19 | data indirection |
nodes
+| tests.cpp:26:15:26:23 | ReturnValue | semmle.label | ReturnValue |
| tests.cpp:33:34:33:39 | call to getenv | semmle.label | call to getenv |
| tests.cpp:38:25:38:36 | strncat output argument | semmle.label | strncat output argument |
| tests.cpp:38:39:38:49 | environment indirection | semmle.label | environment indirection |
-| tests.cpp:42:5:42:16 | Phi | semmle.label | Phi |
-| tests.cpp:51:22:51:25 | badSource output argument | semmle.label | badSource output argument |
+| tests.cpp:51:12:51:20 | call to badSource | semmle.label | call to badSource |
| tests.cpp:53:16:53:19 | data indirection | semmle.label | data indirection |
subpaths
#select
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected
index e98353732b9..53344b776af 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected
@@ -31,6 +31,8 @@ edges
| tests.c:29:28:29:31 | argv | tests.c:33:21:33:29 | buffer100 indirection |
| tests.c:31:15:31:23 | array to pointer conversion | tests.c:31:15:31:23 | buffer100 |
| tests.c:31:15:31:23 | array to pointer conversion | tests.c:31:15:31:23 | buffer100 indirection |
+| tests.c:31:15:31:23 | array to pointer conversion | tests.c:33:21:33:29 | buffer100 |
+| tests.c:31:15:31:23 | array to pointer conversion | tests.c:33:21:33:29 | buffer100 indirection |
| tests.c:31:15:31:23 | buffer100 | tests.c:31:15:31:23 | buffer100 |
| tests.c:31:15:31:23 | buffer100 | tests.c:31:15:31:23 | buffer100 indirection |
| tests.c:31:15:31:23 | buffer100 | tests.c:33:21:33:29 | buffer100 |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/SAMATE/UncontrolledFormatString.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/SAMATE/UncontrolledFormatString.expected
index 5bce7b8aa38..ee4d25029be 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/SAMATE/UncontrolledFormatString.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/SAMATE/UncontrolledFormatString.expected
@@ -7,14 +7,18 @@ edges
| char_connect_socket_w32_vsnprintf_01_bad.c:94:55:94:68 | ... + ... | char_connect_socket_w32_vsnprintf_01_bad.c:125:15:125:18 | data indirection |
| char_console_fprintf_01_bad.c:30:23:30:35 | ... + ... | char_console_fprintf_01_bad.c:49:21:49:24 | (const char *)... |
| char_console_fprintf_01_bad.c:30:23:30:35 | ... + ... | char_console_fprintf_01_bad.c:49:21:49:24 | data |
+| char_console_fprintf_01_bad.c:30:23:30:35 | ... + ... | char_console_fprintf_01_bad.c:49:21:49:24 | data |
| char_console_fprintf_01_bad.c:30:23:30:35 | ... + ... | char_console_fprintf_01_bad.c:49:21:49:24 | data indirection |
| char_console_fprintf_01_bad.c:30:23:30:35 | fgets output argument | char_console_fprintf_01_bad.c:49:21:49:24 | (const char *)... |
| char_console_fprintf_01_bad.c:30:23:30:35 | fgets output argument | char_console_fprintf_01_bad.c:49:21:49:24 | data |
+| char_console_fprintf_01_bad.c:30:23:30:35 | fgets output argument | char_console_fprintf_01_bad.c:49:21:49:24 | data |
| char_console_fprintf_01_bad.c:30:23:30:35 | fgets output argument | char_console_fprintf_01_bad.c:49:21:49:24 | data indirection |
| char_environment_fprintf_01_bad.c:27:30:27:35 | call to getenv | char_environment_fprintf_01_bad.c:36:21:36:24 | (const char *)... |
| char_environment_fprintf_01_bad.c:27:30:27:35 | call to getenv | char_environment_fprintf_01_bad.c:36:21:36:24 | (const char *)... |
| char_environment_fprintf_01_bad.c:27:30:27:35 | call to getenv | char_environment_fprintf_01_bad.c:36:21:36:24 | data |
| char_environment_fprintf_01_bad.c:27:30:27:35 | call to getenv | char_environment_fprintf_01_bad.c:36:21:36:24 | data |
+| char_environment_fprintf_01_bad.c:27:30:27:35 | call to getenv | char_environment_fprintf_01_bad.c:36:21:36:24 | data |
+| char_environment_fprintf_01_bad.c:27:30:27:35 | call to getenv | char_environment_fprintf_01_bad.c:36:21:36:24 | data |
| char_environment_fprintf_01_bad.c:27:30:27:35 | call to getenv | char_environment_fprintf_01_bad.c:36:21:36:24 | data indirection |
| char_environment_fprintf_01_bad.c:27:30:27:35 | call to getenv | char_environment_fprintf_01_bad.c:36:21:36:24 | data indirection |
subpaths
@@ -31,6 +35,8 @@ nodes
| char_console_fprintf_01_bad.c:49:21:49:24 | (const char *)... | semmle.label | (const char *)... |
| char_console_fprintf_01_bad.c:49:21:49:24 | (const char *)... | semmle.label | (const char *)... |
| char_console_fprintf_01_bad.c:49:21:49:24 | data | semmle.label | data |
+| char_console_fprintf_01_bad.c:49:21:49:24 | data | semmle.label | data |
+| char_console_fprintf_01_bad.c:49:21:49:24 | data | semmle.label | data |
| char_console_fprintf_01_bad.c:49:21:49:24 | data indirection | semmle.label | data indirection |
| char_console_fprintf_01_bad.c:49:21:49:24 | data indirection | semmle.label | data indirection |
| char_environment_fprintf_01_bad.c:27:30:27:35 | call to getenv | semmle.label | call to getenv |
@@ -38,6 +44,8 @@ nodes
| char_environment_fprintf_01_bad.c:36:21:36:24 | (const char *)... | semmle.label | (const char *)... |
| char_environment_fprintf_01_bad.c:36:21:36:24 | (const char *)... | semmle.label | (const char *)... |
| char_environment_fprintf_01_bad.c:36:21:36:24 | data | semmle.label | data |
+| char_environment_fprintf_01_bad.c:36:21:36:24 | data | semmle.label | data |
+| char_environment_fprintf_01_bad.c:36:21:36:24 | data | semmle.label | data |
| char_environment_fprintf_01_bad.c:36:21:36:24 | data indirection | semmle.label | data indirection |
| char_environment_fprintf_01_bad.c:36:21:36:24 | data indirection | semmle.label | data indirection |
#select
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected
index a5a17967f69..5a4a8eae9e2 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected
@@ -1,5 +1,5 @@
edges
-| argvLocal.c:9:25:9:31 | correct | argvLocal.c:9:25:9:31 | *correct |
+| argvLocal.c:9:25:9:31 | *correct | argvLocal.c:9:25:9:31 | ReturnIndirection |
| argvLocal.c:95:9:95:12 | argv | argvLocal.c:95:9:95:15 | (const char *)... |
| argvLocal.c:95:9:95:12 | argv | argvLocal.c:95:9:95:15 | (const char *)... |
| argvLocal.c:95:9:95:12 | argv | argvLocal.c:95:9:95:15 | access to array |
@@ -29,6 +29,8 @@ edges
| argvLocal.c:100:7:100:10 | argv | argvLocal.c:102:15:102:16 | i1 |
| argvLocal.c:100:7:100:10 | argv | argvLocal.c:102:15:102:16 | i1 indirection |
| argvLocal.c:100:7:100:10 | argv | argvLocal.c:102:15:102:16 | i1 indirection |
+| argvLocal.c:100:7:100:10 | argv | argvLocal.c:102:15:102:16 | i1 indirection |
+| argvLocal.c:100:7:100:10 | argv | argvLocal.c:102:15:102:16 | i1 indirection |
| argvLocal.c:100:7:100:10 | argv | argvLocal.c:144:9:144:10 | (const char *)... |
| argvLocal.c:100:7:100:10 | argv | argvLocal.c:144:9:144:10 | (const char *)... |
| argvLocal.c:100:7:100:10 | argv | argvLocal.c:144:9:144:10 | i7 |
@@ -44,6 +46,14 @@ edges
| argvLocal.c:100:7:100:10 | argv | argvLocal.c:145:15:145:16 | i7 indirection |
| argvLocal.c:100:7:100:10 | argv | argvLocal.c:145:15:145:16 | i7 indirection |
| argvLocal.c:102:15:102:16 | i1 indirection | argvLocal.c:9:25:9:31 | *correct |
+| argvLocal.c:102:15:102:16 | i1 indirection | argvLocal.c:102:15:102:16 | printWrapper output argument |
+| argvLocal.c:102:15:102:16 | printWrapper output argument | argvLocal.c:144:9:144:10 | (const char *)... |
+| argvLocal.c:102:15:102:16 | printWrapper output argument | argvLocal.c:144:9:144:10 | i7 |
+| argvLocal.c:102:15:102:16 | printWrapper output argument | argvLocal.c:144:9:144:10 | i7 |
+| argvLocal.c:102:15:102:16 | printWrapper output argument | argvLocal.c:144:9:144:10 | i7 indirection |
+| argvLocal.c:102:15:102:16 | printWrapper output argument | argvLocal.c:145:15:145:16 | i7 |
+| argvLocal.c:102:15:102:16 | printWrapper output argument | argvLocal.c:145:15:145:16 | i7 |
+| argvLocal.c:102:15:102:16 | printWrapper output argument | argvLocal.c:145:15:145:16 | i7 indirection |
| argvLocal.c:105:14:105:17 | argv | argvLocal.c:106:9:106:13 | (const char *)... |
| argvLocal.c:105:14:105:17 | argv | argvLocal.c:106:9:106:13 | (const char *)... |
| argvLocal.c:105:14:105:17 | argv | argvLocal.c:106:9:106:13 | access to array |
@@ -58,6 +68,8 @@ edges
| argvLocal.c:105:14:105:17 | argv | argvLocal.c:107:15:107:19 | access to array |
| argvLocal.c:105:14:105:17 | argv | argvLocal.c:107:15:107:19 | access to array indirection |
| argvLocal.c:105:14:105:17 | argv | argvLocal.c:107:15:107:19 | access to array indirection |
+| argvLocal.c:105:14:105:17 | argv | argvLocal.c:107:15:107:19 | access to array indirection |
+| argvLocal.c:105:14:105:17 | argv | argvLocal.c:107:15:107:19 | access to array indirection |
| argvLocal.c:105:14:105:17 | argv | argvLocal.c:110:9:110:11 | (const char *)... |
| argvLocal.c:105:14:105:17 | argv | argvLocal.c:110:9:110:11 | (const char *)... |
| argvLocal.c:105:14:105:17 | argv | argvLocal.c:110:9:110:11 | * ... |
@@ -73,6 +85,14 @@ edges
| argvLocal.c:105:14:105:17 | argv | argvLocal.c:111:15:111:17 | * ... indirection |
| argvLocal.c:105:14:105:17 | argv | argvLocal.c:111:15:111:17 | * ... indirection |
| argvLocal.c:107:15:107:19 | access to array indirection | argvLocal.c:9:25:9:31 | *correct |
+| argvLocal.c:107:15:107:19 | access to array indirection | argvLocal.c:107:15:107:19 | printWrapper output argument |
+| argvLocal.c:107:15:107:19 | printWrapper output argument | argvLocal.c:110:9:110:11 | (const char *)... |
+| argvLocal.c:107:15:107:19 | printWrapper output argument | argvLocal.c:110:9:110:11 | * ... |
+| argvLocal.c:107:15:107:19 | printWrapper output argument | argvLocal.c:110:9:110:11 | * ... |
+| argvLocal.c:107:15:107:19 | printWrapper output argument | argvLocal.c:110:9:110:11 | * ... indirection |
+| argvLocal.c:107:15:107:19 | printWrapper output argument | argvLocal.c:111:15:111:17 | * ... |
+| argvLocal.c:107:15:107:19 | printWrapper output argument | argvLocal.c:111:15:111:17 | * ... |
+| argvLocal.c:107:15:107:19 | printWrapper output argument | argvLocal.c:111:15:111:17 | * ... indirection |
| argvLocal.c:111:15:111:17 | * ... indirection | argvLocal.c:9:25:9:31 | *correct |
| argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | (const char *)... |
| argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | (const char *)... |
@@ -80,8 +100,6 @@ edges
| argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | i3 |
| argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | i3 indirection |
| argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | i3 indirection |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:2:117:13 | i3 |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:2:117:13 | i3 |
| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | array to pointer conversion |
| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | array to pointer conversion |
| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | i3 |
@@ -90,70 +108,41 @@ edges
| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | i3 indirection |
| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | i3 indirection |
| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | i3 indirection |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | (const char *)... |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | (const char *)... |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | i4 |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | i4 |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | i4 indirection |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | i4 indirection |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:2:122:13 | i4 |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:2:122:13 | i4 |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 indirection |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 indirection |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 indirection |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 indirection |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:135:9:135:12 | (const char *)... |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:135:9:135:12 | (const char *)... |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:135:9:135:12 | ... ++ |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:135:9:135:12 | ... ++ |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:135:9:135:12 | ... ++ indirection |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:135:9:135:12 | ... ++ indirection |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | -- ... |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | -- ... |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | -- ... |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | -- ... |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | -- ... indirection |
-| argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | -- ... indirection |
-| argvLocal.c:117:2:117:13 | i3 | argvLocal.c:9:25:9:31 | correct |
-| argvLocal.c:117:2:117:13 | i3 | argvLocal.c:117:15:117:16 | printWrapper output argument |
| argvLocal.c:117:15:117:16 | i3 indirection | argvLocal.c:9:25:9:31 | *correct |
| argvLocal.c:117:15:117:16 | i3 indirection | argvLocal.c:117:15:117:16 | printWrapper output argument |
| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:121:9:121:10 | (const char *)... |
| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:121:9:121:10 | i4 |
| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:121:9:121:10 | i4 indirection |
-| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:122:2:122:13 | i4 |
| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:122:15:122:16 | i4 |
| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:122:15:122:16 | i4 |
| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:122:15:122:16 | i4 indirection |
| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:122:15:122:16 | i4 indirection |
+| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:135:9:135:10 | i4 |
| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | (const char *)... |
| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | ... ++ |
+| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | ... ++ |
| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | ... ++ indirection |
| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:136:15:136:18 | -- ... |
| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:136:15:136:18 | -- ... |
| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:136:15:136:18 | -- ... indirection |
-| argvLocal.c:122:2:122:13 | i4 | argvLocal.c:9:25:9:31 | correct |
-| argvLocal.c:122:2:122:13 | i4 | argvLocal.c:122:15:122:16 | printWrapper output argument |
+| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:136:17:136:18 | i4 |
| argvLocal.c:122:15:122:16 | i4 indirection | argvLocal.c:9:25:9:31 | *correct |
| argvLocal.c:122:15:122:16 | i4 indirection | argvLocal.c:122:15:122:16 | printWrapper output argument |
+| argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:135:9:135:10 | i4 |
| argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | (const char *)... |
| argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | ... ++ |
+| argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | ... ++ |
| argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | ... ++ indirection |
| argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:136:15:136:18 | -- ... |
| argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:136:15:136:18 | -- ... |
| argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:136:15:136:18 | -- ... indirection |
+| argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:136:17:136:18 | i4 |
| argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | (const char *)... |
| argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | (const char *)... |
| argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | i5 |
| argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | i5 |
| argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | i5 indirection |
| argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | i5 indirection |
-| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:2:128:13 | i5 |
-| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:2:128:13 | i5 |
| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | array to pointer conversion |
| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | array to pointer conversion |
| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | i5 |
@@ -174,8 +163,6 @@ edges
| argvLocal.c:126:10:126:13 | argv | argvLocal.c:132:15:132:20 | ... + ... |
| argvLocal.c:126:10:126:13 | argv | argvLocal.c:132:15:132:20 | ... + ... indirection |
| argvLocal.c:126:10:126:13 | argv | argvLocal.c:132:15:132:20 | ... + ... indirection |
-| argvLocal.c:128:2:128:13 | i5 | argvLocal.c:9:25:9:31 | correct |
-| argvLocal.c:128:2:128:13 | i5 | argvLocal.c:128:15:128:16 | printWrapper output argument |
| argvLocal.c:128:15:128:16 | i5 indirection | argvLocal.c:9:25:9:31 | *correct |
| argvLocal.c:128:15:128:16 | i5 indirection | argvLocal.c:128:15:128:16 | printWrapper output argument |
| argvLocal.c:128:15:128:16 | printWrapper output argument | argvLocal.c:131:9:131:14 | (const char *)... |
@@ -248,16 +235,14 @@ edges
| argvLocal.c:168:18:168:21 | argv | argvLocal.c:170:24:170:26 | i10 |
| argvLocal.c:170:15:170:26 | i10 indirection | argvLocal.c:9:25:9:31 | *correct |
subpaths
-| argvLocal.c:117:2:117:13 | i3 | argvLocal.c:9:25:9:31 | correct | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:117:15:117:16 | printWrapper output argument |
-| argvLocal.c:117:15:117:16 | i3 indirection | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:117:15:117:16 | printWrapper output argument |
-| argvLocal.c:122:2:122:13 | i4 | argvLocal.c:9:25:9:31 | correct | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:122:15:122:16 | printWrapper output argument |
-| argvLocal.c:122:15:122:16 | i4 indirection | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:122:15:122:16 | printWrapper output argument |
-| argvLocal.c:128:2:128:13 | i5 | argvLocal.c:9:25:9:31 | correct | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:128:15:128:16 | printWrapper output argument |
-| argvLocal.c:128:15:128:16 | i5 indirection | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:128:15:128:16 | printWrapper output argument |
+| argvLocal.c:102:15:102:16 | i1 indirection | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:9:25:9:31 | ReturnIndirection | argvLocal.c:102:15:102:16 | printWrapper output argument |
+| argvLocal.c:107:15:107:19 | access to array indirection | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:9:25:9:31 | ReturnIndirection | argvLocal.c:107:15:107:19 | printWrapper output argument |
+| argvLocal.c:117:15:117:16 | i3 indirection | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:9:25:9:31 | ReturnIndirection | argvLocal.c:117:15:117:16 | printWrapper output argument |
+| argvLocal.c:122:15:122:16 | i4 indirection | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:9:25:9:31 | ReturnIndirection | argvLocal.c:122:15:122:16 | printWrapper output argument |
+| argvLocal.c:128:15:128:16 | i5 indirection | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:9:25:9:31 | ReturnIndirection | argvLocal.c:128:15:128:16 | printWrapper output argument |
nodes
| argvLocal.c:9:25:9:31 | *correct | semmle.label | *correct |
-| argvLocal.c:9:25:9:31 | *correct | semmle.label | *correct |
-| argvLocal.c:9:25:9:31 | correct | semmle.label | correct |
+| argvLocal.c:9:25:9:31 | ReturnIndirection | semmle.label | ReturnIndirection |
| argvLocal.c:95:9:95:12 | argv | semmle.label | argv |
| argvLocal.c:95:9:95:12 | argv | semmle.label | argv |
| argvLocal.c:95:9:95:15 | (const char *)... | semmle.label | (const char *)... |
@@ -288,6 +273,7 @@ nodes
| argvLocal.c:102:15:102:16 | i1 | semmle.label | i1 |
| argvLocal.c:102:15:102:16 | i1 indirection | semmle.label | i1 indirection |
| argvLocal.c:102:15:102:16 | i1 indirection | semmle.label | i1 indirection |
+| argvLocal.c:102:15:102:16 | printWrapper output argument | semmle.label | printWrapper output argument |
| argvLocal.c:105:14:105:17 | argv | semmle.label | argv |
| argvLocal.c:105:14:105:17 | argv | semmle.label | argv |
| argvLocal.c:106:9:106:13 | (const char *)... | semmle.label | (const char *)... |
@@ -302,6 +288,7 @@ nodes
| argvLocal.c:107:15:107:19 | access to array | semmle.label | access to array |
| argvLocal.c:107:15:107:19 | access to array indirection | semmle.label | access to array indirection |
| argvLocal.c:107:15:107:19 | access to array indirection | semmle.label | access to array indirection |
+| argvLocal.c:107:15:107:19 | printWrapper output argument | semmle.label | printWrapper output argument |
| argvLocal.c:110:9:110:11 | (const char *)... | semmle.label | (const char *)... |
| argvLocal.c:110:9:110:11 | (const char *)... | semmle.label | (const char *)... |
| argvLocal.c:110:9:110:11 | * ... | semmle.label | * ... |
@@ -321,7 +308,6 @@ nodes
| argvLocal.c:116:9:116:10 | i3 | semmle.label | i3 |
| argvLocal.c:116:9:116:10 | i3 indirection | semmle.label | i3 indirection |
| argvLocal.c:116:9:116:10 | i3 indirection | semmle.label | i3 indirection |
-| argvLocal.c:117:2:117:13 | i3 | semmle.label | i3 |
| argvLocal.c:117:15:117:16 | array to pointer conversion | semmle.label | array to pointer conversion |
| argvLocal.c:117:15:117:16 | array to pointer conversion | semmle.label | array to pointer conversion |
| argvLocal.c:117:15:117:16 | i3 | semmle.label | i3 |
@@ -333,7 +319,6 @@ nodes
| argvLocal.c:121:9:121:10 | i4 | semmle.label | i4 |
| argvLocal.c:121:9:121:10 | i4 indirection | semmle.label | i4 indirection |
| argvLocal.c:121:9:121:10 | i4 indirection | semmle.label | i4 indirection |
-| argvLocal.c:122:2:122:13 | i4 | semmle.label | i4 |
| argvLocal.c:122:15:122:16 | i4 | semmle.label | i4 |
| argvLocal.c:122:15:122:16 | i4 | semmle.label | i4 |
| argvLocal.c:122:15:122:16 | i4 | semmle.label | i4 |
@@ -347,7 +332,6 @@ nodes
| argvLocal.c:127:9:127:10 | i5 | semmle.label | i5 |
| argvLocal.c:127:9:127:10 | i5 indirection | semmle.label | i5 indirection |
| argvLocal.c:127:9:127:10 | i5 indirection | semmle.label | i5 indirection |
-| argvLocal.c:128:2:128:13 | i5 | semmle.label | i5 |
| argvLocal.c:128:15:128:16 | array to pointer conversion | semmle.label | array to pointer conversion |
| argvLocal.c:128:15:128:16 | array to pointer conversion | semmle.label | array to pointer conversion |
| argvLocal.c:128:15:128:16 | i5 | semmle.label | i5 |
@@ -364,9 +348,13 @@ nodes
| argvLocal.c:132:15:132:20 | ... + ... | semmle.label | ... + ... |
| argvLocal.c:132:15:132:20 | ... + ... indirection | semmle.label | ... + ... indirection |
| argvLocal.c:132:15:132:20 | ... + ... indirection | semmle.label | ... + ... indirection |
+| argvLocal.c:135:9:135:10 | i4 | semmle.label | i4 |
+| argvLocal.c:135:9:135:10 | i4 | semmle.label | i4 |
| argvLocal.c:135:9:135:12 | (const char *)... | semmle.label | (const char *)... |
| argvLocal.c:135:9:135:12 | (const char *)... | semmle.label | (const char *)... |
| argvLocal.c:135:9:135:12 | ... ++ | semmle.label | ... ++ |
+| argvLocal.c:135:9:135:12 | ... ++ | semmle.label | ... ++ |
+| argvLocal.c:135:9:135:12 | ... ++ | semmle.label | ... ++ |
| argvLocal.c:135:9:135:12 | ... ++ indirection | semmle.label | ... ++ indirection |
| argvLocal.c:135:9:135:12 | ... ++ indirection | semmle.label | ... ++ indirection |
| argvLocal.c:136:15:136:18 | -- ... | semmle.label | -- ... |
@@ -374,6 +362,8 @@ nodes
| argvLocal.c:136:15:136:18 | -- ... | semmle.label | -- ... |
| argvLocal.c:136:15:136:18 | -- ... indirection | semmle.label | -- ... indirection |
| argvLocal.c:136:15:136:18 | -- ... indirection | semmle.label | -- ... indirection |
+| argvLocal.c:136:17:136:18 | i4 | semmle.label | i4 |
+| argvLocal.c:136:17:136:18 | i4 | semmle.label | i4 |
| argvLocal.c:144:9:144:10 | (const char *)... | semmle.label | (const char *)... |
| argvLocal.c:144:9:144:10 | (const char *)... | semmle.label | (const char *)... |
| argvLocal.c:144:9:144:10 | i7 | semmle.label | i7 |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected
index 0f78d29fd36..2586fc17f87 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected
@@ -1,4 +1,10 @@
edges
+| funcsLocal.c:16:8:16:9 | (void *)... | funcsLocal.c:17:9:17:10 | (const char *)... |
+| funcsLocal.c:16:8:16:9 | (void *)... | funcsLocal.c:17:9:17:10 | i1 |
+| funcsLocal.c:16:8:16:9 | (void *)... | funcsLocal.c:17:9:17:10 | i1 indirection |
+| funcsLocal.c:16:8:16:9 | (void *)... | funcsLocal.c:58:9:58:10 | (const char *)... |
+| funcsLocal.c:16:8:16:9 | (void *)... | funcsLocal.c:58:9:58:10 | e1 |
+| funcsLocal.c:16:8:16:9 | (void *)... | funcsLocal.c:58:9:58:10 | e1 indirection |
| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:17:9:17:10 | (const char *)... |
| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:17:9:17:10 | i1 |
| funcsLocal.c:16:8:16:9 | fread output argument | funcsLocal.c:17:9:17:10 | i1 indirection |
@@ -51,8 +57,29 @@ edges
| funcsLocal.c:41:18:41:20 | i61 | funcsLocal.c:42:9:42:10 | (const char *)... |
| funcsLocal.c:41:18:41:20 | i61 | funcsLocal.c:42:9:42:10 | i6 |
| funcsLocal.c:41:18:41:20 | i61 | funcsLocal.c:42:9:42:10 | i6 indirection |
+| funcsLocal.c:46:7:46:9 | * ... | funcsLocal.c:47:9:47:11 | (const char *)... |
+| funcsLocal.c:46:7:46:9 | * ... | funcsLocal.c:47:9:47:11 | (const char *)... |
+| funcsLocal.c:46:7:46:9 | * ... | funcsLocal.c:47:9:47:11 | * ... |
+| funcsLocal.c:46:7:46:9 | * ... | funcsLocal.c:47:9:47:11 | * ... |
+| funcsLocal.c:46:7:46:9 | * ... | funcsLocal.c:47:9:47:11 | * ... |
+| funcsLocal.c:46:7:46:9 | * ... | funcsLocal.c:47:9:47:11 | * ... |
+| funcsLocal.c:46:7:46:9 | * ... | funcsLocal.c:47:9:47:11 | * ... indirection |
+| funcsLocal.c:46:7:46:9 | * ... | funcsLocal.c:47:9:47:11 | * ... indirection |
+| funcsLocal.c:46:7:46:9 | gets output argument | funcsLocal.c:47:9:47:11 | (const char *)... |
+| funcsLocal.c:46:7:46:9 | gets output argument | funcsLocal.c:47:9:47:11 | * ... |
+| funcsLocal.c:46:7:46:9 | gets output argument | funcsLocal.c:47:9:47:11 | * ... |
+| funcsLocal.c:46:7:46:9 | gets output argument | funcsLocal.c:47:9:47:11 | * ... indirection |
+| funcsLocal.c:52:8:52:11 | call to gets | funcsLocal.c:53:9:53:11 | (const char *)... |
+| funcsLocal.c:52:8:52:11 | call to gets | funcsLocal.c:53:9:53:11 | (const char *)... |
+| funcsLocal.c:52:8:52:11 | call to gets | funcsLocal.c:53:9:53:11 | * ... |
+| funcsLocal.c:52:8:52:11 | call to gets | funcsLocal.c:53:9:53:11 | * ... |
+| funcsLocal.c:52:8:52:11 | call to gets | funcsLocal.c:53:9:53:11 | * ... |
+| funcsLocal.c:52:8:52:11 | call to gets | funcsLocal.c:53:9:53:11 | * ... |
+| funcsLocal.c:52:8:52:11 | call to gets | funcsLocal.c:53:9:53:11 | * ... indirection |
+| funcsLocal.c:52:8:52:11 | call to gets | funcsLocal.c:53:9:53:11 | * ... indirection |
subpaths
nodes
+| funcsLocal.c:16:8:16:9 | (void *)... | semmle.label | (void *)... |
| funcsLocal.c:16:8:16:9 | fread output argument | semmle.label | fread output argument |
| funcsLocal.c:16:8:16:9 | i1 | semmle.label | i1 |
| funcsLocal.c:17:9:17:10 | (const char *)... | semmle.label | (const char *)... |
@@ -96,6 +123,25 @@ nodes
| funcsLocal.c:42:9:42:10 | i6 | semmle.label | i6 |
| funcsLocal.c:42:9:42:10 | i6 indirection | semmle.label | i6 indirection |
| funcsLocal.c:42:9:42:10 | i6 indirection | semmle.label | i6 indirection |
+| funcsLocal.c:46:7:46:9 | * ... | semmle.label | * ... |
+| funcsLocal.c:46:7:46:9 | * ... | semmle.label | * ... |
+| funcsLocal.c:46:7:46:9 | gets output argument | semmle.label | gets output argument |
+| funcsLocal.c:47:9:47:11 | (const char *)... | semmle.label | (const char *)... |
+| funcsLocal.c:47:9:47:11 | (const char *)... | semmle.label | (const char *)... |
+| funcsLocal.c:47:9:47:11 | * ... | semmle.label | * ... |
+| funcsLocal.c:47:9:47:11 | * ... | semmle.label | * ... |
+| funcsLocal.c:47:9:47:11 | * ... | semmle.label | * ... |
+| funcsLocal.c:47:9:47:11 | * ... indirection | semmle.label | * ... indirection |
+| funcsLocal.c:47:9:47:11 | * ... indirection | semmle.label | * ... indirection |
+| funcsLocal.c:52:8:52:11 | call to gets | semmle.label | call to gets |
+| funcsLocal.c:52:8:52:11 | call to gets | semmle.label | call to gets |
+| funcsLocal.c:53:9:53:11 | (const char *)... | semmle.label | (const char *)... |
+| funcsLocal.c:53:9:53:11 | (const char *)... | semmle.label | (const char *)... |
+| funcsLocal.c:53:9:53:11 | * ... | semmle.label | * ... |
+| funcsLocal.c:53:9:53:11 | * ... | semmle.label | * ... |
+| funcsLocal.c:53:9:53:11 | * ... | semmle.label | * ... |
+| funcsLocal.c:53:9:53:11 | * ... indirection | semmle.label | * ... indirection |
+| funcsLocal.c:53:9:53:11 | * ... indirection | semmle.label | * ... indirection |
| funcsLocal.c:58:9:58:10 | (const char *)... | semmle.label | (const char *)... |
| funcsLocal.c:58:9:58:10 | (const char *)... | semmle.label | (const char *)... |
| funcsLocal.c:58:9:58:10 | e1 | semmle.label | e1 |
@@ -109,4 +155,6 @@ nodes
| funcsLocal.c:37:9:37:10 | i5 | funcsLocal.c:36:7:36:8 | i5 | funcsLocal.c:37:9:37:10 | i5 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:36:7:36:8 | i5 | gets |
| funcsLocal.c:42:9:42:10 | i6 | funcsLocal.c:41:13:41:16 | call to gets | funcsLocal.c:42:9:42:10 | i6 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:41:13:41:16 | call to gets | gets |
| funcsLocal.c:42:9:42:10 | i6 | funcsLocal.c:41:18:41:20 | i61 | funcsLocal.c:42:9:42:10 | i6 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:41:18:41:20 | i61 | gets |
+| funcsLocal.c:47:9:47:11 | * ... | funcsLocal.c:46:7:46:9 | * ... | funcsLocal.c:47:9:47:11 | * ... | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:46:7:46:9 | * ... | gets |
+| funcsLocal.c:53:9:53:11 | * ... | funcsLocal.c:52:8:52:11 | call to gets | funcsLocal.c:53:9:53:11 | * ... | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:52:8:52:11 | call to gets | gets |
| funcsLocal.c:58:9:58:10 | e1 | funcsLocal.c:16:8:16:9 | i1 | funcsLocal.c:58:9:58:10 | e1 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:16:8:16:9 | i1 | fread |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected
index 8d957ee499c..db35fb14f0f 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected
@@ -5,6 +5,7 @@ edges
| globalVars.c:8:7:8:10 | copy | globalVars.c:30:15:30:18 | copy |
| globalVars.c:8:7:8:10 | copy | globalVars.c:30:15:30:18 | copy |
| globalVars.c:8:7:8:10 | copy | globalVars.c:30:15:30:18 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:33:15:33:18 | copy |
| globalVars.c:8:7:8:10 | copy | globalVars.c:35:11:35:14 | copy |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:38:9:38:13 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:38:9:38:13 | copy2 |
@@ -12,15 +13,16 @@ edges
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:41:15:41:19 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:41:15:41:19 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:41:15:41:19 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:44:15:44:19 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 |
| globalVars.c:11:22:11:25 | *argv | globalVars.c:12:2:12:15 | Store |
-| globalVars.c:11:22:11:25 | argv | globalVars.c:11:22:11:25 | *argv |
| globalVars.c:11:22:11:25 | argv | globalVars.c:12:2:12:15 | Store |
| globalVars.c:12:2:12:15 | Store | globalVars.c:8:7:8:10 | copy |
| globalVars.c:15:21:15:23 | val | globalVars.c:16:2:16:12 | Store |
| globalVars.c:16:2:16:12 | Store | globalVars.c:9:7:9:11 | copy2 |
+| globalVars.c:19:25:19:27 | *str | globalVars.c:19:25:19:27 | ReturnIndirection |
| globalVars.c:24:2:24:9 | argv | globalVars.c:11:22:11:25 | argv |
| globalVars.c:24:11:24:14 | argv | globalVars.c:24:2:24:9 | argv |
| globalVars.c:24:11:24:14 | argv | globalVars.c:24:2:24:9 | argv |
@@ -34,6 +36,12 @@ edges
| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy |
| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy |
| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy indirection |
+| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy indirection |
+| globalVars.c:30:15:30:18 | copy | globalVars.c:35:2:35:9 | copy |
+| globalVars.c:30:15:30:18 | copy indirection | globalVars.c:19:25:19:27 | *str |
+| globalVars.c:30:15:30:18 | copy indirection | globalVars.c:30:15:30:18 | printWrapper output argument |
+| globalVars.c:30:15:30:18 | printWrapper output argument | globalVars.c:35:2:35:9 | copy |
+| globalVars.c:33:15:33:18 | copy | globalVars.c:35:2:35:9 | copy |
| globalVars.c:35:2:35:9 | copy | globalVars.c:15:21:15:23 | val |
| globalVars.c:35:11:35:14 | copy | globalVars.c:35:2:35:9 | copy |
| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | (const char *)... |
@@ -43,10 +51,30 @@ edges
| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 |
| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 |
| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 indirection |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 indirection |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:50:9:50:13 | (const char *)... |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:50:9:50:13 | copy2 indirection |
+| globalVars.c:41:15:41:19 | copy2 indirection | globalVars.c:19:25:19:27 | *str |
+| globalVars.c:41:15:41:19 | copy2 indirection | globalVars.c:41:15:41:19 | printWrapper output argument |
+| globalVars.c:41:15:41:19 | printWrapper output argument | globalVars.c:50:9:50:13 | (const char *)... |
+| globalVars.c:41:15:41:19 | printWrapper output argument | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:41:15:41:19 | printWrapper output argument | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:41:15:41:19 | printWrapper output argument | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:41:15:41:19 | printWrapper output argument | globalVars.c:50:9:50:13 | copy2 indirection |
+| globalVars.c:44:15:44:19 | copy2 | globalVars.c:50:9:50:13 | (const char *)... |
+| globalVars.c:44:15:44:19 | copy2 | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:44:15:44:19 | copy2 | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:44:15:44:19 | copy2 | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:44:15:44:19 | copy2 | globalVars.c:50:9:50:13 | copy2 indirection |
| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | (const char *)... |
| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | copy2 |
| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | copy2 indirection |
subpaths
+| globalVars.c:30:15:30:18 | copy indirection | globalVars.c:19:25:19:27 | *str | globalVars.c:19:25:19:27 | ReturnIndirection | globalVars.c:30:15:30:18 | printWrapper output argument |
+| globalVars.c:41:15:41:19 | copy2 indirection | globalVars.c:19:25:19:27 | *str | globalVars.c:19:25:19:27 | ReturnIndirection | globalVars.c:41:15:41:19 | printWrapper output argument |
nodes
| globalVars.c:8:7:8:10 | copy | semmle.label | copy |
| globalVars.c:9:7:9:11 | copy2 | semmle.label | copy2 |
@@ -55,6 +83,8 @@ nodes
| globalVars.c:12:2:12:15 | Store | semmle.label | Store |
| globalVars.c:15:21:15:23 | val | semmle.label | val |
| globalVars.c:16:2:16:12 | Store | semmle.label | Store |
+| globalVars.c:19:25:19:27 | *str | semmle.label | *str |
+| globalVars.c:19:25:19:27 | ReturnIndirection | semmle.label | ReturnIndirection |
| globalVars.c:24:2:24:9 | argv | semmle.label | argv |
| globalVars.c:24:11:24:14 | argv | semmle.label | argv |
| globalVars.c:24:11:24:14 | argv | semmle.label | argv |
@@ -71,6 +101,8 @@ nodes
| globalVars.c:30:15:30:18 | copy | semmle.label | copy |
| globalVars.c:30:15:30:18 | copy indirection | semmle.label | copy indirection |
| globalVars.c:30:15:30:18 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:30:15:30:18 | printWrapper output argument | semmle.label | printWrapper output argument |
+| globalVars.c:33:15:33:18 | copy | semmle.label | copy |
| globalVars.c:35:2:35:9 | copy | semmle.label | copy |
| globalVars.c:35:11:35:14 | copy | semmle.label | copy |
| globalVars.c:38:9:38:13 | (const char *)... | semmle.label | (const char *)... |
@@ -85,6 +117,8 @@ nodes
| globalVars.c:41:15:41:19 | copy2 | semmle.label | copy2 |
| globalVars.c:41:15:41:19 | copy2 indirection | semmle.label | copy2 indirection |
| globalVars.c:41:15:41:19 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:41:15:41:19 | printWrapper output argument | semmle.label | printWrapper output argument |
+| globalVars.c:44:15:44:19 | copy2 | semmle.label | copy2 |
| globalVars.c:50:9:50:13 | (const char *)... | semmle.label | (const char *)... |
| globalVars.c:50:9:50:13 | (const char *)... | semmle.label | (const char *)... |
| globalVars.c:50:9:50:13 | copy2 | semmle.label | copy2 |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.c b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.c
index 69a46dd3879..3d15905d82d 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.c
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.c
@@ -86,13 +86,13 @@ int main(int argc, char **argv) {
i3 = argv[1];
printf(i3);
- // BAD [FALSE NEGATIVE]: varOne is 1 so condition is true and it always goes inside the if
+ // BAD: varOne is 1 so condition is true and it always goes inside the if
char *i4;
if (varOne)
i4 = argv[1];
printf(i4);
- // BAD [FALSE NEGATIVE]: varZero is 0 so condition is true and it always goes inside the if
+ // BAD: varZero is 0 so condition is true and it always goes inside the if
char *i5;
if (!varZero)
i5 = argv[1];
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected
index 2805eed6ad0..08c78246a8a 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected
@@ -39,6 +39,22 @@ edges
| ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | i3 |
| ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | i3 indirection |
| ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | i3 indirection |
+| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | (const char *)... |
+| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | (const char *)... |
+| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 |
+| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 |
+| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 |
+| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 |
+| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 indirection |
+| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 indirection |
+| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | (const char *)... |
+| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | (const char *)... |
+| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 |
+| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 |
+| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 |
+| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 |
+| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 indirection |
+| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 indirection |
| ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | (const char *)... |
| ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | (const char *)... |
| ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | i6 |
@@ -118,6 +134,24 @@ nodes
| ifs.c:87:9:87:10 | i3 | semmle.label | i3 |
| ifs.c:87:9:87:10 | i3 indirection | semmle.label | i3 indirection |
| ifs.c:87:9:87:10 | i3 indirection | semmle.label | i3 indirection |
+| ifs.c:92:8:92:11 | argv | semmle.label | argv |
+| ifs.c:92:8:92:11 | argv | semmle.label | argv |
+| ifs.c:93:9:93:10 | (const char *)... | semmle.label | (const char *)... |
+| ifs.c:93:9:93:10 | (const char *)... | semmle.label | (const char *)... |
+| ifs.c:93:9:93:10 | i4 | semmle.label | i4 |
+| ifs.c:93:9:93:10 | i4 | semmle.label | i4 |
+| ifs.c:93:9:93:10 | i4 | semmle.label | i4 |
+| ifs.c:93:9:93:10 | i4 indirection | semmle.label | i4 indirection |
+| ifs.c:93:9:93:10 | i4 indirection | semmle.label | i4 indirection |
+| ifs.c:98:8:98:11 | argv | semmle.label | argv |
+| ifs.c:98:8:98:11 | argv | semmle.label | argv |
+| ifs.c:99:9:99:10 | (const char *)... | semmle.label | (const char *)... |
+| ifs.c:99:9:99:10 | (const char *)... | semmle.label | (const char *)... |
+| ifs.c:99:9:99:10 | i5 | semmle.label | i5 |
+| ifs.c:99:9:99:10 | i5 | semmle.label | i5 |
+| ifs.c:99:9:99:10 | i5 | semmle.label | i5 |
+| ifs.c:99:9:99:10 | i5 indirection | semmle.label | i5 indirection |
+| ifs.c:99:9:99:10 | i5 indirection | semmle.label | i5 indirection |
| ifs.c:105:8:105:11 | argv | semmle.label | argv |
| ifs.c:105:8:105:11 | argv | semmle.label | argv |
| ifs.c:106:9:106:10 | (const char *)... | semmle.label | (const char *)... |
@@ -160,6 +194,8 @@ nodes
| ifs.c:75:9:75:10 | i1 | ifs.c:74:8:74:11 | argv | ifs.c:75:9:75:10 | i1 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:74:8:74:11 | argv | argv |
| ifs.c:81:9:81:10 | i2 | ifs.c:80:8:80:11 | argv | ifs.c:81:9:81:10 | i2 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:80:8:80:11 | argv | argv |
| ifs.c:87:9:87:10 | i3 | ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | i3 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:86:8:86:11 | argv | argv |
+| ifs.c:93:9:93:10 | i4 | ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:92:8:92:11 | argv | argv |
+| ifs.c:99:9:99:10 | i5 | ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:98:8:98:11 | argv | argv |
| ifs.c:106:9:106:10 | i6 | ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | i6 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:105:8:105:11 | argv | argv |
| ifs.c:112:9:112:10 | i7 | ifs.c:111:8:111:11 | argv | ifs.c:112:9:112:10 | i7 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:111:8:111:11 | argv | argv |
| ifs.c:118:9:118:10 | i8 | ifs.c:117:8:117:11 | argv | ifs.c:118:9:118:10 | i8 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:117:8:117:11 | argv | argv |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ArithmeticUncontrolled/ArithmeticUncontrolled.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ArithmeticUncontrolled/ArithmeticUncontrolled.expected
index 8f7f3aa2efa..9499152ed39 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ArithmeticUncontrolled/ArithmeticUncontrolled.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ArithmeticUncontrolled/ArithmeticUncontrolled.expected
@@ -6,25 +6,24 @@ edges
| test.c:75:13:75:19 | call to rand | test.c:77:9:77:9 | r |
| test.c:81:14:81:17 | call to rand | test.c:83:9:83:9 | r |
| test.c:81:23:81:26 | call to rand | test.c:83:9:83:9 | r |
-| test.c:99:14:99:19 | call to rand | test.c:100:5:100:5 | r |
| test.c:125:13:125:16 | call to rand | test.c:127:9:127:9 | r |
| test.c:131:13:131:16 | call to rand | test.c:133:5:133:5 | r |
| test.c:137:13:137:16 | call to rand | test.c:139:10:139:10 | r |
| test.c:155:22:155:25 | call to rand | test.c:157:9:157:9 | r |
| test.c:155:22:155:27 | (unsigned int)... | test.c:157:9:157:9 | r |
-| test.cpp:8:9:8:12 | Store | test.cpp:24:11:24:18 | call to get_rand |
-| test.cpp:8:9:8:12 | call to rand | test.cpp:8:9:8:12 | Store |
-| test.cpp:13:2:13:15 | Chi [[]] | test.cpp:30:13:30:14 | get_rand2 output argument [[]] |
-| test.cpp:13:10:13:13 | call to rand | test.cpp:13:2:13:15 | Chi [[]] |
-| test.cpp:18:2:18:14 | Chi [[]] | test.cpp:36:13:36:13 | get_rand3 output argument [[]] |
-| test.cpp:18:9:18:12 | call to rand | test.cpp:18:2:18:14 | Chi [[]] |
+| test.cpp:6:5:6:12 | ReturnValue | test.cpp:24:11:24:18 | call to get_rand |
+| test.cpp:8:9:8:12 | call to rand | test.cpp:6:5:6:12 | ReturnValue |
+| test.cpp:13:2:13:6 | * ... [post update] | test.cpp:30:3:30:11 | & ... [post update] |
+| test.cpp:13:3:13:6 | dest [post update] | test.cpp:30:3:30:11 | & ... [post update] |
+| test.cpp:13:10:13:13 | call to rand | test.cpp:13:2:13:6 | * ... [post update] |
+| test.cpp:13:10:13:13 | call to rand | test.cpp:13:3:13:6 | dest [post update] |
+| test.cpp:18:2:18:5 | (reference dereference) [post update] | test.cpp:36:3:36:11 | r [post update] |
+| test.cpp:18:2:18:5 | dest [post update] | test.cpp:36:3:36:11 | r [post update] |
+| test.cpp:18:9:18:12 | call to rand | test.cpp:18:2:18:5 | (reference dereference) [post update] |
+| test.cpp:18:9:18:12 | call to rand | test.cpp:18:2:18:5 | dest [post update] |
| test.cpp:24:11:24:18 | call to get_rand | test.cpp:25:7:25:7 | r |
-| test.cpp:30:13:30:14 | Chi | test.cpp:31:7:31:7 | r |
-| test.cpp:30:13:30:14 | get_rand2 output argument [[]] | test.cpp:30:13:30:14 | Chi |
-| test.cpp:36:13:36:13 | Chi | test.cpp:37:7:37:7 | r |
-| test.cpp:36:13:36:13 | get_rand3 output argument [[]] | test.cpp:36:13:36:13 | Chi |
-| test.cpp:62:19:62:22 | call to rand | test.cpp:65:9:65:9 | x |
-| test.cpp:62:19:62:24 | (unsigned int)... | test.cpp:65:9:65:9 | x |
+| test.cpp:30:3:30:11 | & ... [post update] | test.cpp:31:7:31:7 | r |
+| test.cpp:36:3:36:11 | r [post update] | test.cpp:37:7:37:7 | r |
| test.cpp:86:10:86:13 | call to rand | test.cpp:90:10:90:10 | x |
| test.cpp:98:10:98:13 | call to rand | test.cpp:102:10:102:10 | x |
| test.cpp:137:10:137:13 | call to rand | test.cpp:146:9:146:9 | y |
@@ -53,8 +52,6 @@ nodes
| test.c:81:14:81:17 | call to rand | semmle.label | call to rand |
| test.c:81:23:81:26 | call to rand | semmle.label | call to rand |
| test.c:83:9:83:9 | r | semmle.label | r |
-| test.c:99:14:99:19 | call to rand | semmle.label | call to rand |
-| test.c:100:5:100:5 | r | semmle.label | r |
| test.c:125:13:125:16 | call to rand | semmle.label | call to rand |
| test.c:127:9:127:9 | r | semmle.label | r |
| test.c:131:13:131:16 | call to rand | semmle.label | call to rand |
@@ -64,23 +61,20 @@ nodes
| test.c:155:22:155:25 | call to rand | semmle.label | call to rand |
| test.c:155:22:155:27 | (unsigned int)... | semmle.label | (unsigned int)... |
| test.c:157:9:157:9 | r | semmle.label | r |
-| test.cpp:8:9:8:12 | Store | semmle.label | Store |
+| test.cpp:6:5:6:12 | ReturnValue | semmle.label | ReturnValue |
| test.cpp:8:9:8:12 | call to rand | semmle.label | call to rand |
-| test.cpp:13:2:13:15 | Chi [[]] | semmle.label | Chi [[]] |
+| test.cpp:13:2:13:6 | * ... [post update] | semmle.label | * ... [post update] |
+| test.cpp:13:3:13:6 | dest [post update] | semmle.label | dest [post update] |
| test.cpp:13:10:13:13 | call to rand | semmle.label | call to rand |
-| test.cpp:18:2:18:14 | Chi [[]] | semmle.label | Chi [[]] |
+| test.cpp:18:2:18:5 | (reference dereference) [post update] | semmle.label | (reference dereference) [post update] |
+| test.cpp:18:2:18:5 | dest [post update] | semmle.label | dest [post update] |
| test.cpp:18:9:18:12 | call to rand | semmle.label | call to rand |
| test.cpp:24:11:24:18 | call to get_rand | semmle.label | call to get_rand |
| test.cpp:25:7:25:7 | r | semmle.label | r |
-| test.cpp:30:13:30:14 | Chi | semmle.label | Chi |
-| test.cpp:30:13:30:14 | get_rand2 output argument [[]] | semmle.label | get_rand2 output argument [[]] |
+| test.cpp:30:3:30:11 | & ... [post update] | semmle.label | & ... [post update] |
| test.cpp:31:7:31:7 | r | semmle.label | r |
-| test.cpp:36:13:36:13 | Chi | semmle.label | Chi |
-| test.cpp:36:13:36:13 | get_rand3 output argument [[]] | semmle.label | get_rand3 output argument [[]] |
+| test.cpp:36:3:36:11 | r [post update] | semmle.label | r [post update] |
| test.cpp:37:7:37:7 | r | semmle.label | r |
-| test.cpp:62:19:62:22 | call to rand | semmle.label | call to rand |
-| test.cpp:62:19:62:24 | (unsigned int)... | semmle.label | (unsigned int)... |
-| test.cpp:65:9:65:9 | x | semmle.label | x |
| test.cpp:86:10:86:13 | call to rand | semmle.label | call to rand |
| test.cpp:90:10:90:10 | x | semmle.label | x |
| test.cpp:98:10:98:13 | call to rand | semmle.label | call to rand |
@@ -114,7 +108,6 @@ subpaths
| test.c:77:9:77:9 | r | test.c:75:13:75:19 | call to rand | test.c:77:9:77:9 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:75:13:75:19 | call to rand | Uncontrolled value |
| test.c:83:9:83:9 | r | test.c:81:14:81:17 | call to rand | test.c:83:9:83:9 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:81:14:81:17 | call to rand | Uncontrolled value |
| test.c:83:9:83:9 | r | test.c:81:23:81:26 | call to rand | test.c:83:9:83:9 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:81:23:81:26 | call to rand | Uncontrolled value |
-| test.c:100:5:100:5 | r | test.c:99:14:99:19 | call to rand | test.c:100:5:100:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:99:14:99:19 | call to rand | Uncontrolled value |
| test.c:127:9:127:9 | r | test.c:125:13:125:16 | call to rand | test.c:127:9:127:9 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:125:13:125:16 | call to rand | Uncontrolled value |
| test.c:133:5:133:5 | r | test.c:131:13:131:16 | call to rand | test.c:133:5:133:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:131:13:131:16 | call to rand | Uncontrolled value |
| test.c:139:10:139:10 | r | test.c:137:13:137:16 | call to rand | test.c:139:10:139:10 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:137:13:137:16 | call to rand | Uncontrolled value |
@@ -123,8 +116,6 @@ subpaths
| test.cpp:25:7:25:7 | r | test.cpp:8:9:8:12 | call to rand | test.cpp:25:7:25:7 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:8:9:8:12 | call to rand | Uncontrolled value |
| test.cpp:31:7:31:7 | r | test.cpp:13:10:13:13 | call to rand | test.cpp:31:7:31:7 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:13:10:13:13 | call to rand | Uncontrolled value |
| test.cpp:37:7:37:7 | r | test.cpp:18:9:18:12 | call to rand | test.cpp:37:7:37:7 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:18:9:18:12 | call to rand | Uncontrolled value |
-| test.cpp:65:9:65:9 | x | test.cpp:62:19:62:22 | call to rand | test.cpp:65:9:65:9 | x | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.cpp:62:19:62:22 | call to rand | Uncontrolled value |
-| test.cpp:65:9:65:9 | x | test.cpp:62:19:62:24 | (unsigned int)... | test.cpp:65:9:65:9 | x | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.cpp:62:19:62:22 | call to rand | Uncontrolled value |
| test.cpp:90:10:90:10 | x | test.cpp:86:10:86:13 | call to rand | test.cpp:90:10:90:10 | x | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:86:10:86:13 | call to rand | Uncontrolled value |
| test.cpp:102:10:102:10 | x | test.cpp:98:10:98:13 | call to rand | test.cpp:102:10:102:10 | x | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:98:10:98:13 | call to rand | Uncontrolled value |
| test.cpp:146:9:146:9 | y | test.cpp:137:10:137:13 | call to rand | test.cpp:146:9:146:9 | y | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:137:10:137:13 | call to rand | Uncontrolled value |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ArithmeticUncontrolled/test.c b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ArithmeticUncontrolled/test.c
index 9b308ebb937..eaec2a38e2e 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ArithmeticUncontrolled/test.c
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ArithmeticUncontrolled/test.c
@@ -97,7 +97,7 @@ void randomTester() {
int r = 0;
int *ptr_r = &r;
*ptr_r = RAND();
- r += 100; // BAD
+ r += 100; // BAD [NOT DETECTED]
}
{
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ArithmeticUncontrolled/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ArithmeticUncontrolled/test.cpp
index c64e4665eca..7904037b797 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ArithmeticUncontrolled/test.cpp
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ArithmeticUncontrolled/test.cpp
@@ -62,7 +62,7 @@ unsigned int test_remainder_subtract_unsigned()
unsigned int x = rand();
unsigned int y = x % 100; // y <= x
- return x - y; // GOOD (as y <= x) [FALSE POSITIVE]
+ return x - y; // GOOD (as y <= x)
}
typedef unsigned long size_t;
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected
index 7fe8651f89a..8c6fca273a7 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected
@@ -39,10 +39,10 @@ edges
| test.cpp:148:20:148:25 | call to getenv | test.cpp:152:11:152:28 | ... * ... |
| test.cpp:148:20:148:33 | (const char *)... | test.cpp:152:11:152:28 | ... * ... |
| test.cpp:148:20:148:33 | (const char *)... | test.cpp:152:11:152:28 | ... * ... |
-| test.cpp:211:9:211:42 | Store | test.cpp:241:9:241:24 | call to get_tainted_size |
-| test.cpp:211:9:211:42 | Store | test.cpp:241:9:241:24 | call to get_tainted_size |
-| test.cpp:211:14:211:19 | call to getenv | test.cpp:211:9:211:42 | Store |
-| test.cpp:211:14:211:27 | (const char *)... | test.cpp:211:9:211:42 | Store |
+| test.cpp:209:8:209:23 | ReturnValue | test.cpp:241:9:241:24 | call to get_tainted_size |
+| test.cpp:209:8:209:23 | ReturnValue | test.cpp:241:9:241:24 | call to get_tainted_size |
+| test.cpp:211:14:211:19 | call to getenv | test.cpp:209:8:209:23 | ReturnValue |
+| test.cpp:211:14:211:27 | (const char *)... | test.cpp:209:8:209:23 | ReturnValue |
| test.cpp:224:23:224:23 | s | test.cpp:225:21:225:21 | s |
| test.cpp:224:23:224:23 | s | test.cpp:225:21:225:21 | s |
| test.cpp:230:21:230:21 | s | test.cpp:231:21:231:21 | s |
@@ -59,20 +59,22 @@ edges
| test.cpp:237:24:237:37 | (const char *)... | test.cpp:247:2:247:8 | local_size |
| test.cpp:245:2:245:9 | local_size | test.cpp:224:23:224:23 | s |
| test.cpp:247:2:247:8 | local_size | test.cpp:230:21:230:21 | s |
-| test.cpp:251:2:251:32 | Chi [[]] | test.cpp:289:17:289:20 | get_size output argument [[]] |
-| test.cpp:251:2:251:32 | Chi [[]] | test.cpp:305:18:305:21 | get_size output argument [[]] |
-| test.cpp:251:18:251:23 | call to getenv | test.cpp:251:2:251:32 | Chi [[]] |
-| test.cpp:251:18:251:31 | (const char *)... | test.cpp:251:2:251:32 | Chi [[]] |
+| test.cpp:251:2:251:9 | (reference dereference) [post update] | test.cpp:289:8:289:15 | size [post update] |
+| test.cpp:251:2:251:9 | (reference dereference) [post update] | test.cpp:305:9:305:16 | size [post update] |
+| test.cpp:251:2:251:9 | out_size [post update] | test.cpp:289:8:289:15 | size [post update] |
+| test.cpp:251:2:251:9 | out_size [post update] | test.cpp:305:9:305:16 | size [post update] |
+| test.cpp:251:18:251:23 | call to getenv | test.cpp:251:2:251:9 | (reference dereference) [post update] |
+| test.cpp:251:18:251:23 | call to getenv | test.cpp:251:2:251:9 | out_size [post update] |
+| test.cpp:251:18:251:31 | (const char *)... | test.cpp:251:2:251:9 | (reference dereference) [post update] |
+| test.cpp:251:18:251:31 | (const char *)... | test.cpp:251:2:251:9 | out_size [post update] |
| test.cpp:259:20:259:25 | call to getenv | test.cpp:263:11:263:29 | ... * ... |
| test.cpp:259:20:259:25 | call to getenv | test.cpp:263:11:263:29 | ... * ... |
| test.cpp:259:20:259:33 | (const char *)... | test.cpp:263:11:263:29 | ... * ... |
| test.cpp:259:20:259:33 | (const char *)... | test.cpp:263:11:263:29 | ... * ... |
-| test.cpp:289:17:289:20 | Chi | test.cpp:291:11:291:28 | ... * ... |
-| test.cpp:289:17:289:20 | Chi | test.cpp:291:11:291:28 | ... * ... |
-| test.cpp:289:17:289:20 | get_size output argument [[]] | test.cpp:289:17:289:20 | Chi |
-| test.cpp:305:18:305:21 | Chi | test.cpp:308:10:308:27 | ... * ... |
-| test.cpp:305:18:305:21 | Chi | test.cpp:308:10:308:27 | ... * ... |
-| test.cpp:305:18:305:21 | get_size output argument [[]] | test.cpp:305:18:305:21 | Chi |
+| test.cpp:289:8:289:15 | size [post update] | test.cpp:291:11:291:28 | ... * ... |
+| test.cpp:289:8:289:15 | size [post update] | test.cpp:291:11:291:28 | ... * ... |
+| test.cpp:305:9:305:16 | size [post update] | test.cpp:308:10:308:27 | ... * ... |
+| test.cpp:305:9:305:16 | size [post update] | test.cpp:308:10:308:27 | ... * ... |
subpaths
nodes
| test.cpp:40:21:40:24 | argv | semmle.label | argv |
@@ -114,7 +116,7 @@ nodes
| test.cpp:152:11:152:28 | ... * ... | semmle.label | ... * ... |
| test.cpp:152:11:152:28 | ... * ... | semmle.label | ... * ... |
| test.cpp:152:11:152:28 | ... * ... | semmle.label | ... * ... |
-| test.cpp:211:9:211:42 | Store | semmle.label | Store |
+| test.cpp:209:8:209:23 | ReturnValue | semmle.label | ReturnValue |
| test.cpp:211:14:211:19 | call to getenv | semmle.label | call to getenv |
| test.cpp:211:14:211:27 | (const char *)... | semmle.label | (const char *)... |
| test.cpp:224:23:224:23 | s | semmle.label | s |
@@ -137,8 +139,8 @@ nodes
| test.cpp:241:9:241:24 | call to get_tainted_size | semmle.label | call to get_tainted_size |
| test.cpp:245:2:245:9 | local_size | semmle.label | local_size |
| test.cpp:247:2:247:8 | local_size | semmle.label | local_size |
-| test.cpp:251:2:251:32 | Chi [[]] | semmle.label | Chi [[]] |
-| test.cpp:251:2:251:32 | ChiPartial | semmle.label | ChiPartial |
+| test.cpp:251:2:251:9 | (reference dereference) [post update] | semmle.label | (reference dereference) [post update] |
+| test.cpp:251:2:251:9 | out_size [post update] | semmle.label | out_size [post update] |
| test.cpp:251:18:251:23 | call to getenv | semmle.label | call to getenv |
| test.cpp:251:18:251:31 | (const char *)... | semmle.label | (const char *)... |
| test.cpp:259:20:259:25 | call to getenv | semmle.label | call to getenv |
@@ -146,13 +148,11 @@ nodes
| test.cpp:263:11:263:29 | ... * ... | semmle.label | ... * ... |
| test.cpp:263:11:263:29 | ... * ... | semmle.label | ... * ... |
| test.cpp:263:11:263:29 | ... * ... | semmle.label | ... * ... |
-| test.cpp:289:17:289:20 | Chi | semmle.label | Chi |
-| test.cpp:289:17:289:20 | get_size output argument [[]] | semmle.label | get_size output argument [[]] |
+| test.cpp:289:8:289:15 | size [post update] | semmle.label | size [post update] |
| test.cpp:291:11:291:28 | ... * ... | semmle.label | ... * ... |
| test.cpp:291:11:291:28 | ... * ... | semmle.label | ... * ... |
| test.cpp:291:11:291:28 | ... * ... | semmle.label | ... * ... |
-| test.cpp:305:18:305:21 | Chi | semmle.label | Chi |
-| test.cpp:305:18:305:21 | get_size output argument [[]] | semmle.label | get_size output argument [[]] |
+| test.cpp:305:9:305:16 | size [post update] | semmle.label | size [post update] |
| test.cpp:308:10:308:27 | ... * ... | semmle.label | ... * ... |
| test.cpp:308:10:308:27 | ... * ... | semmle.label | ... * ... |
| test.cpp:308:10:308:27 | ... * ... | semmle.label | ... * ... |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected
index e0f72bffd44..383b45a0812 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected
@@ -4,11 +4,11 @@ edges
| test2.cpp:25:22:25:23 | & ... | test2.cpp:27:2:27:11 | v |
| test2.cpp:25:22:25:23 | fscanf output argument | test2.cpp:27:2:27:11 | v |
| test2.cpp:27:2:27:11 | v | test2.cpp:12:21:12:21 | v |
-| test5.cpp:9:7:9:9 | buf | test5.cpp:10:9:10:27 | Store |
-| test5.cpp:9:7:9:9 | gets output argument | test5.cpp:10:9:10:27 | Store |
-| test5.cpp:10:9:10:27 | Store | test5.cpp:17:6:17:18 | call to getTaintedInt |
-| test5.cpp:10:9:10:27 | Store | test5.cpp:17:6:17:18 | call to getTaintedInt |
-| test5.cpp:10:9:10:27 | Store | test5.cpp:18:6:18:18 | call to getTaintedInt |
+| test5.cpp:5:5:5:17 | ReturnValue | test5.cpp:17:6:17:18 | call to getTaintedInt |
+| test5.cpp:5:5:5:17 | ReturnValue | test5.cpp:17:6:17:18 | call to getTaintedInt |
+| test5.cpp:5:5:5:17 | ReturnValue | test5.cpp:18:6:18:18 | call to getTaintedInt |
+| test5.cpp:9:7:9:9 | buf | test5.cpp:5:5:5:17 | ReturnValue |
+| test5.cpp:9:7:9:9 | gets output argument | test5.cpp:5:5:5:17 | ReturnValue |
| test5.cpp:18:6:18:18 | call to getTaintedInt | test5.cpp:19:6:19:6 | y |
| test5.cpp:18:6:18:18 | call to getTaintedInt | test5.cpp:19:6:19:6 | y |
| test.c:11:29:11:32 | argv | test.c:14:15:14:28 | maxConnections |
@@ -32,9 +32,9 @@ nodes
| test2.cpp:25:22:25:23 | & ... | semmle.label | & ... |
| test2.cpp:25:22:25:23 | fscanf output argument | semmle.label | fscanf output argument |
| test2.cpp:27:2:27:11 | v | semmle.label | v |
+| test5.cpp:5:5:5:17 | ReturnValue | semmle.label | ReturnValue |
| test5.cpp:9:7:9:9 | buf | semmle.label | buf |
| test5.cpp:9:7:9:9 | gets output argument | semmle.label | gets output argument |
-| test5.cpp:10:9:10:27 | Store | semmle.label | Store |
| test5.cpp:17:6:17:18 | call to getTaintedInt | semmle.label | call to getTaintedInt |
| test5.cpp:17:6:17:18 | call to getTaintedInt | semmle.label | call to getTaintedInt |
| test5.cpp:17:6:17:18 | call to getTaintedInt | semmle.label | call to getTaintedInt |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-807/semmle/TaintedCondition/TaintedCondition.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-807/semmle/TaintedCondition/TaintedCondition.expected
index 1fdb1497922..bf06935046b 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-807/semmle/TaintedCondition/TaintedCondition.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-807/semmle/TaintedCondition/TaintedCondition.expected
@@ -1,12 +1,8 @@
edges
| test.cpp:20:29:20:34 | call to getenv | test.cpp:24:10:24:35 | ! ... |
| test.cpp:20:29:20:34 | call to getenv | test.cpp:24:11:24:16 | call to strcmp |
-| test.cpp:20:29:20:34 | call to getenv | test.cpp:41:10:41:38 | ! ... |
-| test.cpp:20:29:20:34 | call to getenv | test.cpp:41:11:41:16 | call to strcmp |
| test.cpp:20:29:20:47 | (const char *)... | test.cpp:24:10:24:35 | ! ... |
| test.cpp:20:29:20:47 | (const char *)... | test.cpp:24:11:24:16 | call to strcmp |
-| test.cpp:20:29:20:47 | (const char *)... | test.cpp:41:10:41:38 | ! ... |
-| test.cpp:20:29:20:47 | (const char *)... | test.cpp:41:11:41:16 | call to strcmp |
subpaths
nodes
| test.cpp:20:29:20:34 | call to getenv | semmle.label | call to getenv |
@@ -14,9 +10,5 @@ nodes
| test.cpp:24:10:24:35 | ! ... | semmle.label | ! ... |
| test.cpp:24:11:24:16 | call to strcmp | semmle.label | call to strcmp |
| test.cpp:24:11:24:16 | call to strcmp | semmle.label | call to strcmp |
-| test.cpp:41:10:41:38 | ! ... | semmle.label | ! ... |
-| test.cpp:41:11:41:16 | call to strcmp | semmle.label | call to strcmp |
-| test.cpp:41:11:41:16 | call to strcmp | semmle.label | call to strcmp |
#select
| test.cpp:24:10:24:35 | ! ... | test.cpp:20:29:20:34 | call to getenv | test.cpp:24:10:24:35 | ! ... | Reliance on untrusted input $@ to raise privilege at $@ | test.cpp:20:29:20:34 | call to getenv | call to getenv | test.cpp:25:9:25:27 | ... = ... | ... = ... |
-| test.cpp:41:10:41:38 | ! ... | test.cpp:20:29:20:34 | call to getenv | test.cpp:41:10:41:38 | ! ... | Reliance on untrusted input $@ to raise privilege at $@ | test.cpp:20:29:20:34 | call to getenv | call to getenv | test.cpp:42:8:42:26 | ... = ... | ... = ... |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-807/semmle/TaintedCondition/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-807/semmle/TaintedCondition/test.cpp
index 0430d861095..641cbaa7be7 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-807/semmle/TaintedCondition/test.cpp
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-807/semmle/TaintedCondition/test.cpp
@@ -35,7 +35,7 @@ void processRequest()
adminPrivileges = 0; // OK, since it's a 0 and not a 1
}
- // BAD (requires pointer analysis to catch)
+ // BAD (requires pointer analysis to catch) [NOT DETECTED]
const char** userp = ¤tUser;
*userp = userName;
if (!strcmp(currentUser, "admin")) {
From 71972161852340fc25bc9b7182de2ce824f64f73 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Tue, 26 Oct 2021 13:12:07 +0100
Subject: [PATCH 206/471] Add a copy of SsaImplCommon to the identical-files
script.
---
config/identical-files.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/config/identical-files.json b/config/identical-files.json
index a60bd919028..a04fc77841d 100644
--- a/config/identical-files.json
+++ b/config/identical-files.json
@@ -449,7 +449,8 @@
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll",
"csharp/ql/lib/semmle/code/cil/internal/SsaImplCommon.qll",
- "ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll"
+ "ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll",
+ "cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll"
],
"CryptoAlgorithms Python/JS": [
"javascript/ql/lib/semmle/javascript/security/CryptoAlgorithms.qll",
From 12e0185b0dd8d7b1f0a7523a0b1374bdb577b3e2 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Tue, 26 Oct 2021 13:12:25 +0100
Subject: [PATCH 207/471] C++: Sync identical files.
---
.../lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll | 1 +
1 file changed, 1 insertion(+)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll
index 884f4406d01..395cb5cb171 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll
@@ -156,6 +156,7 @@ private predicate dominatesPredecessor(BasicBlock bb1, BasicBlock bb2) {
}
/** Holds if `df` is in the dominance frontier of `bb`. */
+pragma[noinline]
private predicate inDominanceFrontier(BasicBlock bb, BasicBlock df) {
dominatesPredecessor(bb, df) and
not strictlyDominates(bb, df)
From 8c3349f40fb4e1c4e2bd5a031bbed86b60e74af5 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 28 Oct 2021 13:41:39 +0200
Subject: [PATCH 208/471] Python: Properly model `flask.send_from_directory`
To not include `filename` as path-injection sink.
---
python/ql/lib/semmle/python/frameworks/Flask.qll | 13 ++++++++++++-
.../python/security/dataflow/PathInjection.qll | 10 +++++++++-
.../dataflow/PathInjectionCustomizations.qll | 10 ++++++++++
.../frameworks/flask/file_sending.py | 4 ++--
.../CWE-022-PathInjection/PathInjection.expected | 15 ---------------
5 files changed, 33 insertions(+), 19 deletions(-)
diff --git a/python/ql/lib/semmle/python/frameworks/Flask.qll b/python/ql/lib/semmle/python/frameworks/Flask.qll
index 4a46acd9017..5bb838447cb 100644
--- a/python/ql/lib/semmle/python/frameworks/Flask.qll
+++ b/python/ql/lib/semmle/python/frameworks/Flask.qll
@@ -11,6 +11,7 @@ private import semmle.python.Concepts
private import semmle.python.frameworks.Werkzeug
private import semmle.python.ApiGraphs
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
+private import semmle.python.security.dataflow.PathInjectionCustomizations
/**
* Provides models for the `flask` PyPI package.
@@ -537,11 +538,21 @@ module Flask {
// the provided directory, so is not exposed to path-injection. (but is still a
// path-argument).
this.getArg(1), this.getArgByName("filename")
- // TODO: Exclude filename as path-injection sink
]
}
}
+ /**
+ * To exclude `filename` argument to `flask.send_from_directory` as a path-injection sink.
+ */
+ private class FlaskSendFromDirectoryCallFilenameSanitizer extends PathInjection::Sanitizer {
+ FlaskSendFromDirectoryCallFilenameSanitizer() {
+ this = any(FlaskSendFromDirectoryCall c).getArg(1)
+ or
+ this = any(FlaskSendFromDirectoryCall c).getArgByName("filename")
+ }
+ }
+
/**
* A call to `flask.send_file`.
*
diff --git a/python/ql/lib/semmle/python/security/dataflow/PathInjection.qll b/python/ql/lib/semmle/python/security/dataflow/PathInjection.qll
index 30570c32f58..827fe806d71 100644
--- a/python/ql/lib/semmle/python/security/dataflow/PathInjection.qll
+++ b/python/ql/lib/semmle/python/security/dataflow/PathInjection.qll
@@ -26,7 +26,11 @@ class PathNotNormalizedConfiguration extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
- override predicate isSanitizer(DataFlow::Node node) { node instanceof Path::PathNormalization }
+ override predicate isSanitizer(DataFlow::Node node) {
+ node instanceof Sanitizer
+ or
+ node instanceof Path::PathNormalization
+ }
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
guard instanceof SanitizerGuard
@@ -52,6 +56,8 @@ class FirstNormalizationConfiguration extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) { sink instanceof Path::PathNormalization }
+ override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
+
override predicate isSanitizerOut(DataFlow::Node node) { node instanceof Path::PathNormalization }
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
@@ -67,6 +73,8 @@ class NormalizedPathNotCheckedConfiguration extends TaintTracking2::Configuratio
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
+ override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
+
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
guard instanceof Path::SafeAccessCheck
or
diff --git a/python/ql/lib/semmle/python/security/dataflow/PathInjectionCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/PathInjectionCustomizations.qll
index 21a6a2093e5..410eee50b29 100644
--- a/python/ql/lib/semmle/python/security/dataflow/PathInjectionCustomizations.qll
+++ b/python/ql/lib/semmle/python/security/dataflow/PathInjectionCustomizations.qll
@@ -32,6 +32,16 @@ module PathInjection {
*/
abstract class Sink extends DataFlow::Node { }
+ /**
+ * A sanitizer for "path injection" vulnerabilities.
+ *
+ * This should only be used for things like calls to library functions that perform their own
+ * (correct) normalization/escaping of untrusted paths.
+ *
+ * Please also see `Path::SafeAccessCheck` and `Path::PathNormalization` Concepts.
+ */
+ abstract class Sanitizer extends DataFlow::Node { }
+
/**
* A sanitizer guard for "path injection" vulnerabilities.
*/
diff --git a/python/ql/test/library-tests/frameworks/flask/file_sending.py b/python/ql/test/library-tests/frameworks/flask/file_sending.py
index 54cece7071b..9abebda146d 100644
--- a/python/ql/test/library-tests/frameworks/flask/file_sending.py
+++ b/python/ql/test/library-tests/frameworks/flask/file_sending.py
@@ -1,7 +1,7 @@
from flask import send_from_directory, send_file
-send_from_directory("filepath", "file") # $ getAPathArgument="filepath" getAPathArgument="file"
-send_from_directory(directory="filepath", filename="file") # $ getAPathArgument="filepath" getAPathArgument="file"
+send_from_directory("dir", "file") # $ getAPathArgument="dir" getAPathArgument="file"
+send_from_directory(directory="dir", filename="file") # $ getAPathArgument="dir" getAPathArgument="file"
send_file("file") # $ getAPathArgument="file"
send_file(filename_or_fp="file") # $ getAPathArgument="file"
diff --git a/python/ql/test/query-tests/Security/CWE-022-PathInjection/PathInjection.expected b/python/ql/test/query-tests/Security/CWE-022-PathInjection/PathInjection.expected
index 9f5f0200400..1229e3ba716 100644
--- a/python/ql/test/query-tests/Security/CWE-022-PathInjection/PathInjection.expected
+++ b/python/ql/test/query-tests/Security/CWE-022-PathInjection/PathInjection.expected
@@ -1,12 +1,6 @@
edges
-| flask_path_injection.py:11:16:11:22 | ControlFlowNode for request | flask_path_injection.py:11:16:11:27 | ControlFlowNode for Attribute |
-| flask_path_injection.py:11:16:11:27 | ControlFlowNode for Attribute | flask_path_injection.py:13:44:13:51 | ControlFlowNode for filename |
| flask_path_injection.py:19:15:19:21 | ControlFlowNode for request | flask_path_injection.py:19:15:19:26 | ControlFlowNode for Attribute |
-| flask_path_injection.py:19:15:19:21 | ControlFlowNode for request | flask_path_injection.py:20:16:20:22 | ControlFlowNode for request |
-| flask_path_injection.py:19:15:19:21 | ControlFlowNode for request | flask_path_injection.py:20:16:20:27 | ControlFlowNode for Attribute |
| flask_path_injection.py:19:15:19:26 | ControlFlowNode for Attribute | flask_path_injection.py:21:32:21:38 | ControlFlowNode for dirname |
-| flask_path_injection.py:20:16:20:22 | ControlFlowNode for request | flask_path_injection.py:20:16:20:27 | ControlFlowNode for Attribute |
-| flask_path_injection.py:20:16:20:27 | ControlFlowNode for Attribute | flask_path_injection.py:21:41:21:48 | ControlFlowNode for filename |
| path_injection.py:12:16:12:22 | ControlFlowNode for request | path_injection.py:12:16:12:27 | ControlFlowNode for Attribute |
| path_injection.py:12:16:12:27 | ControlFlowNode for Attribute | path_injection.py:13:14:13:47 | ControlFlowNode for Attribute() |
| path_injection.py:19:16:19:22 | ControlFlowNode for request | path_injection.py:19:16:19:27 | ControlFlowNode for Attribute |
@@ -76,15 +70,9 @@ edges
| test_chaining.py:41:9:41:16 | ControlFlowNode for source() | test_chaining.py:42:9:42:19 | ControlFlowNode for normpath() |
| test_chaining.py:44:13:44:23 | ControlFlowNode for normpath() | test_chaining.py:45:14:45:14 | ControlFlowNode for z |
nodes
-| flask_path_injection.py:11:16:11:22 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
-| flask_path_injection.py:11:16:11:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
-| flask_path_injection.py:13:44:13:51 | ControlFlowNode for filename | semmle.label | ControlFlowNode for filename |
| flask_path_injection.py:19:15:19:21 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| flask_path_injection.py:19:15:19:26 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
-| flask_path_injection.py:20:16:20:22 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
-| flask_path_injection.py:20:16:20:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| flask_path_injection.py:21:32:21:38 | ControlFlowNode for dirname | semmle.label | ControlFlowNode for dirname |
-| flask_path_injection.py:21:41:21:48 | ControlFlowNode for filename | semmle.label | ControlFlowNode for filename |
| path_injection.py:12:16:12:22 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| path_injection.py:12:16:12:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| path_injection.py:13:14:13:47 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
@@ -170,10 +158,7 @@ nodes
| test_chaining.py:44:13:44:23 | ControlFlowNode for normpath() | semmle.label | ControlFlowNode for normpath() |
| test_chaining.py:45:14:45:14 | ControlFlowNode for z | semmle.label | ControlFlowNode for z |
#select
-| flask_path_injection.py:13:44:13:51 | ControlFlowNode for filename | flask_path_injection.py:11:16:11:22 | ControlFlowNode for request | flask_path_injection.py:13:44:13:51 | ControlFlowNode for filename | This path depends on $@. | flask_path_injection.py:11:16:11:22 | ControlFlowNode for request | a user-provided value |
| flask_path_injection.py:21:32:21:38 | ControlFlowNode for dirname | flask_path_injection.py:19:15:19:21 | ControlFlowNode for request | flask_path_injection.py:21:32:21:38 | ControlFlowNode for dirname | This path depends on $@. | flask_path_injection.py:19:15:19:21 | ControlFlowNode for request | a user-provided value |
-| flask_path_injection.py:21:41:21:48 | ControlFlowNode for filename | flask_path_injection.py:19:15:19:21 | ControlFlowNode for request | flask_path_injection.py:21:41:21:48 | ControlFlowNode for filename | This path depends on $@. | flask_path_injection.py:19:15:19:21 | ControlFlowNode for request | a user-provided value |
-| flask_path_injection.py:21:41:21:48 | ControlFlowNode for filename | flask_path_injection.py:20:16:20:22 | ControlFlowNode for request | flask_path_injection.py:21:41:21:48 | ControlFlowNode for filename | This path depends on $@. | flask_path_injection.py:20:16:20:22 | ControlFlowNode for request | a user-provided value |
| path_injection.py:13:14:13:47 | ControlFlowNode for Attribute() | path_injection.py:12:16:12:22 | ControlFlowNode for request | path_injection.py:13:14:13:47 | ControlFlowNode for Attribute() | This path depends on $@. | path_injection.py:12:16:12:22 | ControlFlowNode for request | a user-provided value |
| path_injection.py:21:14:21:18 | ControlFlowNode for npath | path_injection.py:19:16:19:22 | ControlFlowNode for request | path_injection.py:21:14:21:18 | ControlFlowNode for npath | This path depends on $@. | path_injection.py:19:16:19:22 | ControlFlowNode for request | a user-provided value |
| path_injection.py:31:14:31:18 | ControlFlowNode for npath | path_injection.py:27:16:27:22 | ControlFlowNode for request | path_injection.py:31:14:31:18 | ControlFlowNode for npath | This path depends on $@. | path_injection.py:27:16:27:22 | ControlFlowNode for request | a user-provided value |
From 0acf6aaec8f62abdb2cbcb751b5399f042cb26d0 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 28 Oct 2021 13:45:34 +0200
Subject: [PATCH 209/471] Python: Add change-note
---
python/change-notes/2021-10-28-flask-send_file.md | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 python/change-notes/2021-10-28-flask-send_file.md
diff --git a/python/change-notes/2021-10-28-flask-send_file.md b/python/change-notes/2021-10-28-flask-send_file.md
new file mode 100644
index 00000000000..1e875bd5412
--- /dev/null
+++ b/python/change-notes/2021-10-28-flask-send_file.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* Added modeling of the `send_from_directory` and `send_file` functions from the `flask` PyPI package, resulting in additional sinks for the _Uncontrolled data used in path expression_ (`py/path-injection`) query. This addition was originally [submitted as an external contribution by @porcupineyhairs](https://github.com/github/codeql/pull/6330).
From 3abe3e43d09a00ec3be76f6ac81ac494a7dd3cd9 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Thu, 28 Oct 2021 13:58:01 +0200
Subject: [PATCH 210/471] Python: autoformat
---
python/ql/lib/semmle/python/frameworks/SqlAlchemy.qll | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/python/ql/lib/semmle/python/frameworks/SqlAlchemy.qll b/python/ql/lib/semmle/python/frameworks/SqlAlchemy.qll
index 7e168524b89..a343e3f4283 100644
--- a/python/ql/lib/semmle/python/frameworks/SqlAlchemy.qll
+++ b/python/ql/lib/semmle/python/frameworks/SqlAlchemy.qll
@@ -315,9 +315,7 @@ module SqlAlchemy {
*/
abstract class TextClauseConstruction extends SqlConstruction::Range, DataFlow::CallCfgNode {
/** Gets the argument that specifies the SQL text. */
- override DataFlow::Node getSql() {
- result in [this.getArg(0), this.getArgByName("text")]
- }
+ override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("text")] }
}
/** `TextClause` constructions from the `sqlalchemy` package. */
From a33a8fd518aa63ee8d166eeedeab62125e9912d6 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 28 Oct 2021 14:02:03 +0200
Subject: [PATCH 211/471] Python: Support `flask.blueprints.Blueprint`
Thanks to @haby0 who originally proposed this as part of
https://github.com/github/codeql/pull/6977
---
python/ql/lib/semmle/python/frameworks/Flask.qll | 6 +++++-
.../ql/test/library-tests/frameworks/flask/routing_test.py | 4 ++--
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/python/ql/lib/semmle/python/frameworks/Flask.qll b/python/ql/lib/semmle/python/frameworks/Flask.qll
index fa31bd77220..55311d84d23 100644
--- a/python/ql/lib/semmle/python/frameworks/Flask.qll
+++ b/python/ql/lib/semmle/python/frameworks/Flask.qll
@@ -73,7 +73,11 @@ module Flask {
*/
module Blueprint {
/** Gets a reference to the `flask.Blueprint` class. */
- API::Node classRef() { result = API::moduleImport("flask").getMember("Blueprint") }
+ API::Node classRef() {
+ result = API::moduleImport("flask").getMember("Blueprint")
+ or
+ result = API::moduleImport("flask").getMember("blueprints").getMember("Blueprint")
+ }
/** Gets a reference to an instance of `flask.Blueprint`. */
API::Node instance() { result = classRef().getReturn() }
diff --git a/python/ql/test/library-tests/frameworks/flask/routing_test.py b/python/ql/test/library-tests/frameworks/flask/routing_test.py
index 11e44f52e71..1e9eafe2611 100644
--- a/python/ql/test/library-tests/frameworks/flask/routing_test.py
+++ b/python/ql/test/library-tests/frameworks/flask/routing_test.py
@@ -105,8 +105,8 @@ def bp1_example(foo): # $ requestHandler routedParameter=foo
app.register_blueprint(bp1) # by default, URLs of blueprints are not prefixed
-
-bp2 = flask.Blueprint("bp2", __name__)
+import flask.blueprints
+bp2 = flask.blueprints.Blueprint("bp2", __name__)
@bp2.route("/example") # $ routeSetup="/example"
def bp2_example(): # $ requestHandler
From c92249525b3c587a41d9fdc501a53e091d8e9c21 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Thu, 28 Oct 2021 14:03:09 +0200
Subject: [PATCH 212/471] Python: update test expectations
---
python/ql/test/experimental/dataflow/ApiGraphs/async_test.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/test/experimental/dataflow/ApiGraphs/async_test.py b/python/ql/test/experimental/dataflow/ApiGraphs/async_test.py
index faee160cc68..c621291a46b 100644
--- a/python/ql/test/experimental/dataflow/ApiGraphs/async_test.py
+++ b/python/ql/test/experimental/dataflow/ApiGraphs/async_test.py
@@ -13,7 +13,7 @@ async def bar():
async def test_async_with():
async with pkg.async_func() as result: # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited() awaited=moduleImport("pkg").getMember("async_func").getReturn()
- return result # $ awaited=moduleImport("pkg").getMember("async_func").getReturn()
+ return result # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited() awaited=moduleImport("pkg").getMember("async_func").getReturn()
async def test_async_for():
async for _ in pkg.async_func(): # $ use=moduleImport("pkg").getMember("async_func").getReturn() awaited=moduleImport("pkg").getMember("async_func").getReturn()
From 4f6e5c903b53aa928db78b76fc4202ebde07f65b Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 28 Oct 2021 14:27:07 +0200
Subject: [PATCH 213/471] filter out writes to number indexes
---
.../dataflow/PrototypePollutingAssignmentQuery.qll | 10 +++++++++-
.../PrototypePollutingAssignment.expected | 4 ++++
.../CWE-915/PrototypePollutingAssignment/lib.js | 3 +++
3 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
index 826fbe0828d..70c48bd3eef 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll
@@ -79,7 +79,15 @@ class Configuration extends TaintTracking::Configuration {
source.getNode() = src and sink.getNode() = snk
|
snk = write.getBase() and
- exists(write.getPropertyName())
+ (
+ // fixed property name
+ exists(write.getPropertyName())
+ or
+ // non-string property name (likely number)
+ exists(Expr prop | prop = write.getPropertyNameExpr() |
+ not prop.analyze().getAType() = TTString()
+ )
+ )
)
}
diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
index f8832cbdf4e..32a8259bcff 100644
--- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected
@@ -86,6 +86,8 @@ nodes
| lib.js:91:24:91:27 | path |
| lib.js:92:3:92:12 | maybeProto |
| lib.js:92:3:92:12 | maybeProto |
+| lib.js:95:3:95:12 | maybeProto |
+| lib.js:95:3:95:12 | maybeProto |
| tst.js:5:9:5:38 | taint |
| tst.js:5:17:5:38 | String( ... y.data) |
| tst.js:5:24:5:37 | req.query.data |
@@ -203,6 +205,8 @@ edges
| lib.js:90:43:90:46 | path | lib.js:91:24:91:27 | path |
| lib.js:91:7:91:28 | maybeProto | lib.js:92:3:92:12 | maybeProto |
| lib.js:91:7:91:28 | maybeProto | lib.js:92:3:92:12 | maybeProto |
+| lib.js:91:7:91:28 | maybeProto | lib.js:95:3:95:12 | maybeProto |
+| lib.js:91:7:91:28 | maybeProto | lib.js:95:3:95:12 | maybeProto |
| lib.js:91:20:91:28 | obj[path] | lib.js:91:7:91:28 | maybeProto |
| lib.js:91:24:91:27 | path | lib.js:91:20:91:28 | obj[path] |
| tst.js:5:9:5:38 | taint | tst.js:8:12:8:16 | taint |
diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
index 2e9ecabe089..516ba00530b 100644
--- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
+++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js
@@ -90,4 +90,7 @@ module.exports.delete = function() {
module.exports.fixedProp = function (obj, path, value) {
var maybeProto = obj[path];
maybeProto.foo = value; // OK - fixed properties from library inputs are OK.
+
+ var i = 0;
+ maybeProto[i + 2] = value; // OK - number properties are OK.
}
\ No newline at end of file
From 8c72cc0cddd1162987df79f662e725ef399ee66b Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Thu, 28 Oct 2021 14:53:46 +0200
Subject: [PATCH 214/471] Python: update change note
---
python/change-notes/2021-09-29-model-asyncpg.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/python/change-notes/2021-09-29-model-asyncpg.md b/python/change-notes/2021-09-29-model-asyncpg.md
index 0c44e6cb908..beddfa22d00 100644
--- a/python/change-notes/2021-09-29-model-asyncpg.md
+++ b/python/change-notes/2021-09-29-model-asyncpg.md
@@ -1,2 +1,3 @@
lgtm,codescanning
* Added modeling of `asyncpg` for sinks executing SQL and/or accessing the file system.
+* Corrected the API graph, such that all awaited values now are referred to via `getAwaited`.
From 387c96d1e2d48e4dbcc2cdf049994940802fb68c Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Thu, 28 Oct 2021 14:00:50 +0100
Subject: [PATCH 215/471] Rename 'SourceVariable.getVariable' to
'SourceVariable.getIRVariable' and replace 'Def.getVariable' to
'Def.getSourceVariable'.
---
.../code/cpp/ir/dataflow/internal/Ssa.qll | 22 +++++++++----------
.../ir/dataflow/internal/SsaImplSpecific.qll | 10 ++++-----
2 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
index 11705a43774..00c28b35899 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
@@ -73,7 +73,7 @@ abstract class Def extends DefOrUse {
Instruction getInstruction() { result = store }
/** Gets the variable that is defined by this definition. */
- abstract SourceVariable getVariable();
+ abstract SourceVariable getSourceVariable();
/** Holds if this definition is guaranteed to happen. */
abstract predicate isCertain();
@@ -94,10 +94,10 @@ abstract class Def extends DefOrUse {
private class ExplicitDef extends Def, TExplicitDef {
ExplicitDef() { this = TExplicitDef(store) }
- override SourceVariable getVariable() {
+ override SourceVariable getSourceVariable() {
exists(VariableInstruction var |
explicitWrite(_, this.getInstruction(), var) and
- result.getVariable() = var.getIRVariable() and
+ result.getIRVariable() = var.getIRVariable() and
not result.isIndirection()
)
}
@@ -108,11 +108,11 @@ private class ExplicitDef extends Def, TExplicitDef {
private class ParameterDef extends Def, TInitializeParam {
ParameterDef() { this = TInitializeParam(store) }
- override SourceVariable getVariable() {
- result.getVariable() = store.(InitializeParameterInstruction).getIRVariable() and
+ override SourceVariable getSourceVariable() {
+ result.getIRVariable() = store.(InitializeParameterInstruction).getIRVariable() and
not result.isIndirection()
or
- result.getVariable() = store.(InitializeIndirectionInstruction).getIRVariable() and
+ result.getIRVariable() = store.(InitializeIndirectionInstruction).getIRVariable() and
result.isIndirection()
}
@@ -130,7 +130,7 @@ abstract class Use extends DefOrUse {
override string toString() { result = "Use" }
/** Gets the variable that is used by this use. */
- abstract SourceVariable getVariable();
+ abstract SourceVariable getSourceVariable();
override IRBlock getBlock() { result = use.getUse().getBlock() }
@@ -144,10 +144,10 @@ abstract class Use extends DefOrUse {
private class ExplicitUse extends Use, TExplicitUse {
ExplicitUse() { this = TExplicitUse(use) }
- override SourceVariable getVariable() {
+ override SourceVariable getSourceVariable() {
exists(VariableInstruction var |
use.getDef() = var and
- result.getVariable() = var.getIRVariable() and
+ result.getIRVariable() = var.getIRVariable() and
(
if use.getUse() instanceof ReadSideEffectInstruction
then result.isIndirection()
@@ -160,10 +160,10 @@ private class ExplicitUse extends Use, TExplicitUse {
private class ReturnParameterIndirection extends Use, TReturnParamIndirection {
ReturnParameterIndirection() { this = TReturnParamIndirection(use) }
- override SourceVariable getVariable() {
+ override SourceVariable getSourceVariable() {
exists(ReturnIndirectionInstruction ret |
returnParameterIndirection(use, ret) and
- result.getVariable() = ret.getIRVariable() and
+ result.getIRVariable() = ret.getIRVariable() and
result.isIndirection()
)
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplSpecific.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplSpecific.qll
index 196d32205ad..211fc2514b3 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplSpecific.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplSpecific.qll
@@ -21,7 +21,7 @@ private newtype TSourceVariable =
abstract class SourceVariable extends TSourceVariable {
IRVariable var;
- IRVariable getVariable() { result = var }
+ IRVariable getIRVariable() { result = var }
abstract string toString();
@@ -31,7 +31,7 @@ abstract class SourceVariable extends TSourceVariable {
class SourceIRVariable extends SourceVariable, TSourceIRVariable {
SourceIRVariable() { this = TSourceIRVariable(var) }
- override string toString() { result = this.getVariable().toString() }
+ override string toString() { result = this.getIRVariable().toString() }
}
class SourceIRVariableIndirection extends SourceVariable, TSourceIRVariableIndirection {
@@ -41,7 +41,7 @@ class SourceIRVariableIndirection extends SourceVariable, TSourceIRVariableIndir
this = TSourceIRVariableIndirection(init) and var = init.getIRVariable()
}
- override string toString() { result = "*" + this.getVariable().toString() }
+ override string toString() { result = "*" + this.getIRVariable().toString() }
override predicate isIndirection() { any() }
}
@@ -50,7 +50,7 @@ predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain)
DataFlowImplCommon::forceCachingInSameStage() and
exists(Ssa::Def def |
def.hasRankInBlock(bb, i) and
- v = def.getVariable() and
+ v = def.getSourceVariable() and
(if def.isCertain() then certain = true else certain = false)
)
}
@@ -58,7 +58,7 @@ predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain)
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
exists(Ssa::Use use |
use.hasRankInBlock(bb, i) and
- v = use.getVariable() and
+ v = use.getSourceVariable() and
certain = true
)
}
From cde80ccf837d5e5a238f2f4d70f634df101e8c4a Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Thu, 28 Oct 2021 14:09:26 +0100
Subject: [PATCH 216/471] Replace 'hasLocationInfo' with 'getLocation'.
---
.../code/cpp/ir/dataflow/internal/Ssa.qll | 24 ++++---------------
1 file changed, 4 insertions(+), 20 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
index 00c28b35899..a970398bc0d 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
@@ -48,16 +48,8 @@ private class DefOrUse extends TDefOrUse {
rnk = getRank(this, block)
}
- /**
- * 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://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
- */
- abstract predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- );
+ /** Gets the location of this element. */
+ abstract Cpp::Location getLocation();
}
private Instruction toInstruction(DefOrUse defOrUse) {
@@ -84,11 +76,7 @@ abstract class Def extends DefOrUse {
override IRBlock getBlock() { result = this.getInstruction().getBlock() }
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- store.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
+ override Cpp::Location getLocation() { result = store.getLocation() }
}
private class ExplicitDef extends Def, TExplicitDef {
@@ -134,11 +122,7 @@ abstract class Use extends DefOrUse {
override IRBlock getBlock() { result = use.getUse().getBlock() }
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- use.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
+ override Cpp::Location getLocation() { result = use.getLocation() }
}
private class ExplicitUse extends Use, TExplicitUse {
From ee2541c3bcefb03f1081e4d95d7027baff07371a Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Thu, 28 Oct 2021 14:12:22 +0100
Subject: [PATCH 217/471] C++: Fix QLDoc on 'getDestinationAddress'.
---
cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
index a970398bc0d..230f6b535fb 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
@@ -201,8 +201,7 @@ predicate addressFlowTC(Instruction iFrom, Instruction iTo) {
/**
* Gets the destination address of `instr` if it is a `StoreInstruction` or
- * a `WriteSideEffectInstruction`. The destination address of a `WriteSideEffectInstruction` is adjusted
- * in the case of calls to operator `new` to give the destination address of a subsequent store (if any).
+ * a `WriteSideEffectInstruction`.
*/
Instruction getDestinationAddress(Instruction instr) {
result =
From 246a5151756d29e6b8c634b1c073c2e708c0f50c Mon Sep 17 00:00:00 2001
From: Ian Lynagh
Date: Thu, 28 Oct 2021 14:19:30 +0100
Subject: [PATCH 218/471] Java: instanceof pattern matching is no longer a
preview feature
---
java/ql/lib/semmle/code/java/Expr.qll | 4 ----
1 file changed, 4 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/Expr.qll b/java/ql/lib/semmle/code/java/Expr.qll
index 6a329d62a04..f2a309904ad 100755
--- a/java/ql/lib/semmle/code/java/Expr.qll
+++ b/java/ql/lib/semmle/code/java/Expr.qll
@@ -1366,15 +1366,11 @@ class InstanceOfExpr extends Expr, @instanceofexpr {
}
/**
- * PREVIEW FEATURE in Java 14. Subject to removal in a future release.
- *
* Holds if this `instanceof` expression uses pattern matching.
*/
predicate isPattern() { exists(this.getLocalVariableDeclExpr()) }
/**
- * PREVIEW FEATURE in Java 14. Subject to removal in a future release.
- *
* Gets the local variable declaration of this `instanceof` expression if pattern matching is used.
*/
LocalVariableDeclExpr getLocalVariableDeclExpr() { result.isNthChildOf(this, 0) }
From 675e284c0e26fcc4f0d16901265ed92e19563d6f Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Thu, 28 Oct 2021 14:52:57 +0100
Subject: [PATCH 219/471] C++: A 'LoadInstruction' in a store chain always sets
'certain = false'.
---
cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll | 4 +++-
.../dataflow/dataflow-tests/dataflow-ir-consistency.expected | 3 +++
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
index 230f6b535fb..62b0a6afeb5 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
@@ -275,7 +275,9 @@ predicate explicitWrite(boolean certain, Instruction instr, Instruction address)
|
if
addressFlowTC(any(Instruction i |
- i instanceof FieldAddressInstruction or i instanceof PointerArithmeticInstruction
+ i instanceof FieldAddressInstruction or
+ i instanceof PointerArithmeticInstruction or
+ i instanceof LoadInstruction
), store.getDestinationAddress())
then certain = false
else certain = true
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected
index a441de0d72a..f2498e224d7 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected
@@ -181,7 +181,10 @@ postWithInFlow
| dispatch.cpp:130:10:130:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| dispatch.cpp:130:10:130:15 | topRef [post update] | PostUpdateNode should not be the target of local flow. |
| dispatch.cpp:130:17:130:24 | topRef [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:148:3:148:3 | u [post update] | PostUpdateNode should not be the target of local flow. |
| dispatch.cpp:148:5:148:5 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:168:3:168:4 | u2 [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:168:6:168:6 | u [post update] | PostUpdateNode should not be the target of local flow. |
| dispatch.cpp:168:8:168:8 | f [post update] | PostUpdateNode should not be the target of local flow. |
| example.c:17:19:17:22 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
| example.c:17:19:17:22 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
From c34b089bc5fe56c4795c44cc145a84def553a87e Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 28 Oct 2021 16:02:36 +0200
Subject: [PATCH 220/471] autoformat
---
.../ql/lib/semmle/javascript/frameworks/CookieLibraries.qll | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
index 83ca03335c9..46db18a0e2f 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll
@@ -79,9 +79,7 @@ private predicate canHaveSensitiveCookie(DataFlow::Node node) {
* `=; Domain=; Secure; HttpOnly`
*/
bindingset[s]
-private string getCookieName(string s) {
- result = s.regexpCapture("([^=]*)=.*", 1).trim()
-}
+private string getCookieName(string s) { result = s.regexpCapture("([^=]*)=.*", 1).trim() }
/**
* Holds if the `Set-Cookie` header value contains the specified attribute
From e75448ebb0830ac663bcdfb22c73e937dae165fa Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 28 Oct 2021 16:35:53 +0200
Subject: [PATCH 221/471] remove redundant inline casts
---
.../semmle/code/cpp/commons/Dependency.qll | 6 ++---
cpp/ql/lib/semmle/code/cpp/commons/Printf.qll | 26 ++++++-------------
.../code/cpp/controlflow/internal/CFG.qll | 4 +--
.../internal/AddressConstantExpression.qll | 2 +-
.../ir/dataflow/internal/DataFlowPrivate.qll | 2 +-
.../raw/internal/TranslatedExpr.qll | 2 +-
.../raw/internal/TranslatedStmt.qll | 4 +--
.../semmle/code/cpp/security/FileWrite.qll | 2 +-
.../code/cpp/valuenumbering/HashCons.qll | 4 +--
.../Likely Errors/EmptyBlock.ql | 5 ++--
.../Likely Typos/ExprHasNoEffect.ql | 2 +-
.../Security/CWE/CWE-497/ExposedSystemData.ql | 2 +-
cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql | 2 +-
.../code/csharp/dataflow/ExternalFlow.qll | 2 +-
.../csharp/security/xml/InsecureXMLQuery.qll | 4 +--
csharp/ql/src/Stubs/Stubs.qll | 2 +-
.../raw/internal/TranslatedExpr.qll | 2 +-
.../raw/internal/TranslatedStmt.qll | 4 +--
java/ql/lib/semmle/code/java/Reflection.qll | 2 +-
.../semmle/code/java/deadcode/DeadCode.qll | 4 +--
.../semmle/code/java/deadcode/EntryPoints.qll | 2 +-
.../code/java/frameworks/javaee/ejb/EJB.qll | 6 ++---
.../code/java/security/ExternalAPIs.qll | 3 +--
java/ql/src/Language Abuse/UselessUpcast.ql | 2 +-
.../Likely Bugs/Resource Leaks/CloseType.qll | 2 +-
.../NosqlInjectionATM.qll | 5 +---
.../ql/lib/semmle/javascript/PrintAst.qll | 2 +-
.../dataflow/internal/AnalyzedParameters.qll | 2 +-
.../internal/BasicExprTypeInference.qll | 4 +--
.../semmle/javascript/frameworks/jQuery.qll | 2 +-
.../javascript/security/UselessUseOfCat.qll | 2 +-
...unctionWithAnalyzedParameters_inference.ql | 2 +-
python/ql/lib/semmle/python/Stmts.qll | 4 +--
.../ql/lib/semmle/python/essa/Definitions.qll | 6 ++---
.../lib/semmle/python/frameworks/Aiohttp.qll | 4 +--
.../lib/semmle/python/objects/Constants.qll | 4 +--
.../lib/codeql/ruby/controlflow/CfgNodes.qll | 2 +-
.../ruby/typetracking/TypeTrackerSpecific.qll | 2 +-
38 files changed, 60 insertions(+), 79 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/commons/Dependency.qll b/cpp/ql/lib/semmle/code/cpp/commons/Dependency.qll
index 59776d5b87a..6f89564e5f0 100644
--- a/cpp/ql/lib/semmle/code/cpp/commons/Dependency.qll
+++ b/cpp/ql/lib/semmle/code/cpp/commons/Dependency.qll
@@ -275,7 +275,7 @@ private predicate dependsOnDeclarationEntry(Element src, DeclarationEntry dest)
dependsOnTransitive(src, mid) and
not mid instanceof Type and
not mid instanceof EnumConstant and
- getDeclarationEntries(mid, dest.(DeclarationEntry)) and
+ getDeclarationEntries(mid, dest) and
not dest instanceof TypeDeclarationEntry
)
or
@@ -283,9 +283,9 @@ private predicate dependsOnDeclarationEntry(Element src, DeclarationEntry dest)
// dependency from a Type / Variable / Function use -> any (visible) definition
dependsOnTransitive(src, mid) and
not mid instanceof EnumConstant and
- getDeclarationEntries(mid, dest.(DeclarationEntry)) and
+ getDeclarationEntries(mid, dest) and
// must be definition
- dest.(DeclarationEntry).isDefinition()
+ dest.isDefinition()
)
}
diff --git a/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll b/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll
index a7c72a5db8d..40a2082eaf4 100644
--- a/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll
+++ b/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll
@@ -175,9 +175,7 @@ class FormattingFunctionCall extends Expr {
/**
* Gets the index at which the format string occurs in the argument list.
*/
- int getFormatParameterIndex() {
- result = this.getTarget().(FormattingFunction).getFormatParameterIndex()
- }
+ int getFormatParameterIndex() { result = this.getTarget().getFormatParameterIndex() }
/**
* Gets the format expression used in this call.
@@ -191,7 +189,7 @@ class FormattingFunctionCall extends Expr {
exists(int i |
result = this.getArgument(i) and
n >= 0 and
- n = i - this.getTarget().(FormattingFunction).getFirstFormatArgumentIndex()
+ n = i - this.getTarget().getFirstFormatArgumentIndex()
)
}
@@ -251,7 +249,7 @@ class FormattingFunctionCall extends Expr {
int getNumFormatArgument() {
result = count(this.getFormatArgument(_)) and
// format arguments must be known
- exists(this.getTarget().(FormattingFunction).getFirstFormatArgumentIndex())
+ exists(this.getTarget().getFirstFormatArgumentIndex())
}
/**
@@ -289,35 +287,27 @@ class FormatLiteral extends Literal {
* a `char *` (either way, `%S` will have the opposite meaning).
* DEPRECATED: Use getDefaultCharType() instead.
*/
- deprecated predicate isWideCharDefault() {
- this.getUse().getTarget().(FormattingFunction).isWideCharDefault()
- }
+ deprecated predicate isWideCharDefault() { this.getUse().getTarget().isWideCharDefault() }
/**
* Gets the default character type expected for `%s` by this format literal. Typically
* `char` or `wchar_t`.
*/
- Type getDefaultCharType() {
- result = this.getUse().getTarget().(FormattingFunction).getDefaultCharType()
- }
+ Type getDefaultCharType() { result = this.getUse().getTarget().getDefaultCharType() }
/**
* Gets the non-default character type expected for `%S` by this format literal. Typically
* `wchar_t` or `char`. On some snapshots there may be multiple results where we can't tell
* which is correct for a particular function.
*/
- Type getNonDefaultCharType() {
- result = this.getUse().getTarget().(FormattingFunction).getNonDefaultCharType()
- }
+ Type getNonDefaultCharType() { result = this.getUse().getTarget().getNonDefaultCharType() }
/**
* Gets the wide character type for this format literal. This is usually `wchar_t`. On some
* snapshots there may be multiple results where we can't tell which is correct for a
* particular function.
*/
- Type getWideCharType() {
- result = this.getUse().getTarget().(FormattingFunction).getWideCharType()
- }
+ Type getWideCharType() { result = this.getUse().getTarget().getWideCharType() }
/**
* Holds if this `FormatLiteral` is in a context that supports
@@ -896,7 +886,7 @@ class FormatLiteral extends Literal {
exists(string len, string conv |
this.parseConvSpec(n, _, _, _, _, _, len, conv) and
(len != "l" and len != "w" and len != "h") and
- this.getUse().getTarget().(FormattingFunction).getFormatCharType().getSize() > 1 and // wide function
+ this.getUse().getTarget().getFormatCharType().getSize() > 1 and // wide function
(
conv = "c" and
result = this.getNonDefaultCharType()
diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/internal/CFG.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/internal/CFG.qll
index 31ef5570451..ca1964a43c3 100644
--- a/cpp/ql/lib/semmle/code/cpp/controlflow/internal/CFG.qll
+++ b/cpp/ql/lib/semmle/code/cpp/controlflow/internal/CFG.qll
@@ -231,7 +231,7 @@ private class PostOrderInitializer extends Initializer {
or
this.getDeclaration() = for.getRangeVariable()
or
- this.getDeclaration() = for.getBeginEndDeclaration().(DeclStmt).getADeclaration()
+ this.getDeclaration() = for.getBeginEndDeclaration().getADeclaration()
)
}
}
@@ -1143,7 +1143,7 @@ private class ExceptionSource extends Node {
this.reachesParent(mid) and
not mid = any(TryStmt try).getStmt() and
not mid = any(MicrosoftTryStmt try).getStmt() and
- parent = mid.(Node).getParentNode()
+ parent = mid.getParentNode()
)
}
diff --git a/cpp/ql/lib/semmle/code/cpp/internal/AddressConstantExpression.qll b/cpp/ql/lib/semmle/code/cpp/internal/AddressConstantExpression.qll
index 436be8384e8..b75703b11fc 100644
--- a/cpp/ql/lib/semmle/code/cpp/internal/AddressConstantExpression.qll
+++ b/cpp/ql/lib/semmle/code/cpp/internal/AddressConstantExpression.qll
@@ -31,7 +31,7 @@ private predicate addressConstantVariable(Variable v) {
private predicate constantAddressLValue(Expr lvalue) {
lvalue.(VariableAccess).getTarget() =
any(Variable v |
- v.(Variable).isStatic()
+ v.isStatic()
or
v instanceof GlobalOrNamespaceVariable
)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
index 3f215883df3..1913852d65e 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
@@ -188,7 +188,7 @@ private predicate fieldStoreStepNoChi(Node node1, FieldContent f, PostUpdateNode
exists(StoreInstruction store, Class c |
store = node2.asInstruction() and
store.getSourceValueOperand() = node1.asOperand() and
- getWrittenField(store, f.(FieldContent).getAField(), c) and
+ getWrittenField(store, f.getAField(), c) and
f.hasOffset(c, _, _)
)
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll
index 3f0f4f10ee3..e15b8c36972 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll
@@ -1032,7 +1032,7 @@ abstract class TranslatedConversion extends TranslatedNonConstantExpr {
final override TranslatedElement getChild(int id) { id = 0 and result = this.getOperand() }
- final TranslatedExpr getOperand() { result = getTranslatedExpr(expr.(Conversion).getExpr()) }
+ final TranslatedExpr getOperand() { result = getTranslatedExpr(expr.getExpr()) }
}
/**
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll
index ce08fc9367f..2bc3b5bc3ef 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll
@@ -103,9 +103,7 @@ class TranslatedDeclStmt extends TranslatedStmt {
class TranslatedExprStmt extends TranslatedStmt {
override ExprStmt stmt;
- TranslatedExpr getExpr() {
- result = getTranslatedExpr(stmt.(ExprStmt).getExpr().getFullyConverted())
- }
+ TranslatedExpr getExpr() { result = getTranslatedExpr(stmt.getExpr().getFullyConverted()) }
override TranslatedElement getChild(int id) { id = 0 and result = getExpr() }
diff --git a/cpp/ql/lib/semmle/code/cpp/security/FileWrite.qll b/cpp/ql/lib/semmle/code/cpp/security/FileWrite.qll
index 36f8b58d812..dc421e8f3ae 100644
--- a/cpp/ql/lib/semmle/code/cpp/security/FileWrite.qll
+++ b/cpp/ql/lib/semmle/code/cpp/security/FileWrite.qll
@@ -173,6 +173,6 @@ private predicate fileWriteWithConvChar(FormattingFunctionCall ffc, Expr source,
source = ffc.getFormatArgument(n)
|
exists(f.getOutputParameterIndex(true)) and
- conv = ffc.(FormattingFunctionCall).getFormat().(FormatLiteral).getConversionChar(n)
+ conv = ffc.getFormat().(FormatLiteral).getConversionChar(n)
)
}
diff --git a/cpp/ql/lib/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/lib/semmle/code/cpp/valuenumbering/HashCons.qll
index c16389ce9bf..0073154dd3c 100644
--- a/cpp/ql/lib/semmle/code/cpp/valuenumbering/HashCons.qll
+++ b/cpp/ql/lib/semmle/code/cpp/valuenumbering/HashCons.qll
@@ -589,7 +589,7 @@ private predicate mk_HasAlloc(HashCons hc, NewOrNewArrayExpr new) {
}
private predicate mk_HasExtent(HashCons hc, NewArrayExpr new) {
- hc = hashCons(new.(NewArrayExpr).getExtent().getFullyConverted())
+ hc = hashCons(new.getExtent().getFullyConverted())
}
private predicate analyzableNewExpr(NewExpr new) {
@@ -619,7 +619,7 @@ private predicate analyzableNewArrayExpr(NewArrayExpr new) {
strictcount(new.getAllocatedType().getUnspecifiedType()) = 1 and
count(new.getAllocatorCall().getFullyConverted()) <= 1 and
count(new.getInitializer().getFullyConverted()) <= 1 and
- count(new.(NewArrayExpr).getExtent().getFullyConverted()) <= 1
+ count(new.getExtent().getFullyConverted()) <= 1
}
private predicate mk_NewArrayExpr(
diff --git a/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.ql b/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.ql
index 9fa8c4e5e3f..6e434c85c95 100644
--- a/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.ql
+++ b/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.ql
@@ -81,9 +81,8 @@ class BlockOrNonChild extends Element {
predicate emptyBlockContainsNonchild(BlockStmt b) {
emptyBlock(_, b) and
exists(BlockOrNonChild c, AffectedFile file |
- c.(BlockOrNonChild).getStartRankIn(file) = 1 + b.(BlockOrNonChild).getStartRankIn(file) and
- c.(BlockOrNonChild).getNonContiguousEndRankIn(file) <
- b.(BlockOrNonChild).getNonContiguousEndRankIn(file)
+ c.getStartRankIn(file) = 1 + b.(BlockOrNonChild).getStartRankIn(file) and
+ c.getNonContiguousEndRankIn(file) < b.(BlockOrNonChild).getNonContiguousEndRankIn(file)
)
}
diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/ExprHasNoEffect.ql b/cpp/ql/src/Likely Bugs/Likely Typos/ExprHasNoEffect.ql
index d3a7dcb8939..6f240fa2d2a 100644
--- a/cpp/ql/src/Likely Bugs/Likely Typos/ExprHasNoEffect.ql
+++ b/cpp/ql/src/Likely Bugs/Likely Typos/ExprHasNoEffect.ql
@@ -66,7 +66,7 @@ predicate functionDefinedInIfDefRecursive(Function f) {
*/
predicate baseCall(FunctionCall call) {
call.getNameQualifier().getQualifyingElement() =
- call.getEnclosingFunction().getDeclaringType().(Class).getABaseClass+()
+ call.getEnclosingFunction().getDeclaringType().getABaseClass+()
}
from PureExprInVoidContext peivc, Locatable parent, Locatable info, string info_text, string tail
diff --git a/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql b/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql
index 8d016751f39..bd2c0fcd6d6 100644
--- a/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql
+++ b/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql
@@ -378,5 +378,5 @@ class SocketOutput extends DataOutput {
from SystemData sd, DataOutput ow
where
sd.getAnExprIndirect() = ow.getASource() or
- sd.getAnExprIndirect() = ow.getASource().(Expr).getAChild*()
+ sd.getAnExprIndirect() = ow.getASource().getAChild*()
select ow, "This operation exposes system data from $@.", sd, sd.toString()
diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql
index adeb54746df..b1eab42af37 100644
--- a/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql
+++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql
@@ -142,7 +142,7 @@ class Resource extends MemberVariable {
predicate acquisitionWithRequiredKind(Assignment acquireAssign, string kind) {
// acquireAssign is an assignment to this resource
- acquireAssign.(Assignment).getLValue() = this.getAnAccess() and
+ acquireAssign.getLValue() = this.getAnAccess() and
// Should be in this class, but *any* member method will do
this.inSameClass(acquireAssign) and
// Check that it is an acquisition function and return the corresponding kind
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll
index b689602d7bf..8b0bc25f261 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll
@@ -427,7 +427,7 @@ private Element interpretElement0(
result = t
or
subtypes = true and
- result = t.(UnboundValueOrRefType).getASubTypeUnbound+()
+ result = t.getASubTypeUnbound+()
) and
result = t and
name = "" and
diff --git a/csharp/ql/lib/semmle/code/csharp/security/xml/InsecureXMLQuery.qll b/csharp/ql/lib/semmle/code/csharp/security/xml/InsecureXMLQuery.qll
index 17856d3e7e8..e885fdb2778 100644
--- a/csharp/ql/lib/semmle/code/csharp/security/xml/InsecureXMLQuery.qll
+++ b/csharp/ql/lib/semmle/code/csharp/security/xml/InsecureXMLQuery.qll
@@ -209,9 +209,7 @@ module XmlReader {
/** Provides predicates related to `System.Xml.XmlTextReader`. */
module XmlTextReader {
private class InsecureXmlTextReader extends InsecureXmlProcessing, ObjectCreation {
- InsecureXmlTextReader() {
- this.getObjectType().(ValueOrRefType).hasQualifiedName("System.Xml.XmlTextReader")
- }
+ InsecureXmlTextReader() { this.getObjectType().hasQualifiedName("System.Xml.XmlTextReader") }
override predicate isUnsafe(string reason) {
not exists(Expr xmlResolverVal |
diff --git a/csharp/ql/src/Stubs/Stubs.qll b/csharp/ql/src/Stubs/Stubs.qll
index e0c009b8783..ea38367c876 100644
--- a/csharp/ql/src/Stubs/Stubs.qll
+++ b/csharp/ql/src/Stubs/Stubs.qll
@@ -697,7 +697,7 @@ private string stubMethod(Method m, Assembly assembly) {
if not m.getDeclaringType() instanceof Enum
then
result =
- " " + stubModifiers(m) + stubClassName(m.(Method).getReturnType()) + " " +
+ " " + stubModifiers(m) + stubClassName(m.getReturnType()) + " " +
stubExplicitImplementation(m) + escapeIfKeyword(m.getUndecoratedName()) +
stubGenericMethodParams(m) + "(" + stubParameters(m) + ")" +
stubTypeParametersConstraints(m) + stubImplementation(m) + ";\n"
diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedExpr.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedExpr.qll
index 362ed3e0d2b..79321fe6f11 100644
--- a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedExpr.qll
+++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedExpr.qll
@@ -1083,7 +1083,7 @@ class TranslatedCast extends TranslatedNonConstantExpr {
)
}
- private TranslatedExpr getOperand() { result = getTranslatedExpr(expr.(Cast).getExpr()) }
+ private TranslatedExpr getOperand() { result = getTranslatedExpr(expr.getExpr()) }
private Opcode getOpcode() {
expr instanceof CastExpr and result instanceof Opcode::CheckedConvertOrThrow
diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedStmt.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedStmt.qll
index 2f91484094a..ac69a9c0f28 100644
--- a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedStmt.qll
+++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedStmt.qll
@@ -89,7 +89,7 @@ class TranslatedDeclStmt extends TranslatedStmt {
class TranslatedExprStmt extends TranslatedStmt {
override ExprStmt stmt;
- TranslatedExpr getExpr() { result = getTranslatedExpr(stmt.(ExprStmt).getExpr()) }
+ TranslatedExpr getExpr() { result = getTranslatedExpr(stmt.getExpr()) }
override TranslatedElement getChild(int id) { id = 0 and result = this.getExpr() }
@@ -123,7 +123,7 @@ class TranslatedExprStmtAccessorSet extends TranslatedExprStmt {
}
override TranslatedExpr getExpr() {
- result = getTranslatedExpr(stmt.(ExprStmt).getExpr().(AssignExpr).getLValue())
+ result = getTranslatedExpr(stmt.getExpr().(AssignExpr).getLValue())
}
override TranslatedElement getChild(int id) { id = 0 and result = this.getExpr() }
diff --git a/java/ql/lib/semmle/code/java/Reflection.qll b/java/ql/lib/semmle/code/java/Reflection.qll
index 71864c5cfe9..ffec13a30f3 100644
--- a/java/ql/lib/semmle/code/java/Reflection.qll
+++ b/java/ql/lib/semmle/code/java/Reflection.qll
@@ -150,7 +150,7 @@ private Type parameterForSubTypes(ParameterizedType type) {
lowerBound = arg.(Wildcard).getLowerBoundType()
|
// `T super Foo` implies that `Foo`, or any super-type of `Foo`, may be represented.
- lowerBound.(RefType).getAnAncestor() = result
+ lowerBound.getAnAncestor() = result
)
)
}
diff --git a/java/ql/lib/semmle/code/java/deadcode/DeadCode.qll b/java/ql/lib/semmle/code/java/deadcode/DeadCode.qll
index 53e0ccc1f35..587b53daf4a 100644
--- a/java/ql/lib/semmle/code/java/deadcode/DeadCode.qll
+++ b/java/ql/lib/semmle/code/java/deadcode/DeadCode.qll
@@ -93,8 +93,8 @@ class SuppressedConstructor extends Constructor {
not this.isDefaultConstructor() and
// Verify that there is only one statement, which is the `super()` call. This exists
// even for empty constructors.
- this.getBody().(BlockStmt).getNumStmt() = 1 and
- this.getBody().(BlockStmt).getAStmt().(SuperConstructorInvocationStmt).getNumArgument() = 0 and
+ this.getBody().getNumStmt() = 1 and
+ this.getBody().getAStmt().(SuperConstructorInvocationStmt).getNumArgument() = 0 and
// A constructor that is called is not acting to suppress the default constructor. We permit
// calls from suppressed and default constructors - in both cases, they can only come from
// sub-class constructors.
diff --git a/java/ql/lib/semmle/code/java/deadcode/EntryPoints.qll b/java/ql/lib/semmle/code/java/deadcode/EntryPoints.qll
index 0a0dd2d0808..0f4d493b06e 100644
--- a/java/ql/lib/semmle/code/java/deadcode/EntryPoints.qll
+++ b/java/ql/lib/semmle/code/java/deadcode/EntryPoints.qll
@@ -262,7 +262,7 @@ class ManagedBeanImplEntryPoint extends EntryPoint, RegisteredManagedBeanImpl {
// Find the method that will be called for each method on each managed bean that this class
// implements.
this.inherits(result) and
- result.(Method).overrides(this.getAnImplementedManagedBean().getAMethod())
+ result.overrides(this.getAnImplementedManagedBean().getAMethod())
}
}
diff --git a/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJB.qll b/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJB.qll
index 8509afc5622..8d200a98c54 100644
--- a/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJB.qll
+++ b/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJB.qll
@@ -668,7 +668,7 @@ Type inheritsMatchingMethodExceptThrows(SessionEJB ejb, Method m) {
sig = n.getSignature() and
sig = m.getSignature() and
exists(Exception ex | ex = n.getAnException() and not throwsExplicitUncheckedException(n, ex) |
- not ex.getType().(RefType).hasSupertype*(m.getAnException().getType()) and
+ not ex.getType().hasSupertype*(m.getAnException().getType()) and
result = ex.getType()
)
)
@@ -717,7 +717,7 @@ Type inheritsMatchingCreateMethodExceptThrows(StatefulSessionEJB ejb, EjbInterfa
exists(Exception ex |
ex = cm.getAnException() and not throwsExplicitUncheckedException(cm, ex)
|
- not ex.getType().(RefType).hasSupertype*(icm.getAnException().getType()) and
+ not ex.getType().hasSupertype*(icm.getAnException().getType()) and
result = ex.getType()
)
)
@@ -732,7 +732,7 @@ Type inheritsMatchingCreateMethodExceptThrows(StatefulSessionEJB ejb, EjbInterfa
exists(Exception ex |
ex = im.getAnException() and not throwsExplicitUncheckedException(im, ex)
|
- not ex.getType().(RefType).hasSupertype*(icm.getAnException().getType()) and
+ not ex.getType().hasSupertype*(icm.getAnException().getType()) and
result = ex.getType()
)
)
diff --git a/java/ql/lib/semmle/code/java/security/ExternalAPIs.qll b/java/ql/lib/semmle/code/java/security/ExternalAPIs.qll
index 3b264bd0283..0aee4aaf802 100644
--- a/java/ql/lib/semmle/code/java/security/ExternalAPIs.qll
+++ b/java/ql/lib/semmle/code/java/security/ExternalAPIs.qll
@@ -141,8 +141,7 @@ class ExternalAPIUsedWithUntrustedData extends TExternalAPI {
|
this = TExternalAPIParameter(m, index) and
result =
- m.getDeclaringType().(RefType).getQualifiedName() + "." + m.getSignature() + " [" +
- indexString + "]"
+ m.getDeclaringType().getQualifiedName() + "." + m.getSignature() + " [" + indexString + "]"
)
}
}
diff --git a/java/ql/src/Language Abuse/UselessUpcast.ql b/java/ql/src/Language Abuse/UselessUpcast.ql
index 062d6fb961e..466cc606b4d 100644
--- a/java/ql/src/Language Abuse/UselessUpcast.ql
+++ b/java/ql/src/Language Abuse/UselessUpcast.ql
@@ -25,7 +25,7 @@ predicate usefulUpcast(CastExpr e) {
other.getName() = target.getName() and
other.getSourceDeclaration() != target.getSourceDeclaration()
|
- c.(MethodAccess).getReceiverType().(RefType).inherits(other.(Method)) or
+ c.(MethodAccess).getReceiverType().inherits(other.(Method)) or
other = target.(Constructor).getDeclaringType().getAConstructor()
)
)
diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseType.qll b/java/ql/src/Likely Bugs/Resource Leaks/CloseType.qll
index d88f1bb82c7..9fdaf75b0be 100644
--- a/java/ql/src/Likely Bugs/Resource Leaks/CloseType.qll
+++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseType.qll
@@ -45,7 +45,7 @@ private predicate closeableType(RefType t) {
class SqlResourceOpeningMethodAccess extends MethodAccess {
SqlResourceOpeningMethodAccess() {
exists(Method m | this.getMethod() = m |
- m.getDeclaringType().(RefType).hasQualifiedName("java.sql", _) and
+ m.getDeclaringType().hasQualifiedName("java.sql", _) and
m.getReturnType().(RefType).hasQualifiedName("java.sql", _) and
m.getName().regexpMatch("(create|prepare|execute).*") and
closeableType(m.getReturnType()) and
diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/NosqlInjectionATM.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/NosqlInjectionATM.qll
index 7fc5c44f2f6..411ba028d67 100644
--- a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/NosqlInjectionATM.qll
+++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/NosqlInjectionATM.qll
@@ -126,10 +126,7 @@ DataFlow::Node getASubexpressionWithinQuery(DataFlow::Node query) {
exists(DataFlow::SourceNode receiver |
receiver.flowsTo(getASubexpressionWithinQuery*(query.getALocalSource())) and
result =
- [
- receiver.(DataFlow::SourceNode).getAPropertyWrite().getRhs(),
- receiver.(DataFlow::ArrayCreationNode).getAnElement()
- ]
+ [receiver.getAPropertyWrite().getRhs(), receiver.(DataFlow::ArrayCreationNode).getAnElement()]
)
}
diff --git a/javascript/ql/lib/semmle/javascript/PrintAst.qll b/javascript/ql/lib/semmle/javascript/PrintAst.qll
index 5fbcd4f4ec6..143565e14ea 100644
--- a/javascript/ql/lib/semmle/javascript/PrintAst.qll
+++ b/javascript/ql/lib/semmle/javascript/PrintAst.qll
@@ -699,7 +699,7 @@ module PrintHTML {
childIndex = -1 and result.(HTMLAttributesNodes).getElement() = element
or
exists(HTML::Element child | result.(HTMLElementNode).getElement() = child |
- child = element.(HTML::Element).getChild(childIndex)
+ child = element.getChild(childIndex)
)
}
}
diff --git a/javascript/ql/lib/semmle/javascript/dataflow/internal/AnalyzedParameters.qll b/javascript/ql/lib/semmle/javascript/dataflow/internal/AnalyzedParameters.qll
index 9825db73ad2..17f153ea7e9 100644
--- a/javascript/ql/lib/semmle/javascript/dataflow/internal/AnalyzedParameters.qll
+++ b/javascript/ql/lib/semmle/javascript/dataflow/internal/AnalyzedParameters.qll
@@ -8,7 +8,7 @@ pragma[nomagic]
predicate isAnalyzedParameter(Parameter p) {
exists(FunctionWithAnalyzedParameters f, int parmIdx | p = f.getParameter(parmIdx) |
// we cannot track flow into rest parameters
- not p.(Parameter).isRestParameter()
+ not p.isRestParameter()
)
}
diff --git a/javascript/ql/lib/semmle/javascript/dataflow/internal/BasicExprTypeInference.qll b/javascript/ql/lib/semmle/javascript/dataflow/internal/BasicExprTypeInference.qll
index 4054a97466f..786dcd08496 100644
--- a/javascript/ql/lib/semmle/javascript/dataflow/internal/BasicExprTypeInference.qll
+++ b/javascript/ql/lib/semmle/javascript/dataflow/internal/BasicExprTypeInference.qll
@@ -199,7 +199,7 @@ private class AnalyzedNewExpr extends DataFlow::AnalyzedValueNode {
*/
private predicate isIndefinite() {
exists(DataFlow::AnalyzedNode callee, AbstractValue calleeVal |
- callee = astNode.(NewExpr).getCallee().analyze() and
+ callee = astNode.getCallee().analyze() and
calleeVal = callee.getALocalValue()
|
calleeVal.isIndefinite(_) or
@@ -217,7 +217,7 @@ private class NewInstance extends DataFlow::AnalyzedValueNode {
override AbstractValue getALocalValue() {
exists(DataFlow::AnalyzedNode callee |
- callee = astNode.(NewExpr).getCallee().analyze() and
+ callee = astNode.getCallee().analyze() and
result = TAbstractInstance(callee.getALocalValue())
)
}
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/jQuery.qll b/javascript/ql/lib/semmle/javascript/frameworks/jQuery.qll
index b91770b8bf6..e907f3bac80 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/jQuery.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/jQuery.qll
@@ -139,7 +139,7 @@ private class JQueryDomElementDefinition extends DOM::ElementDefinition, @call_e
JQueryDomElementDefinition() {
this = call and
call = jquery().getACall().asExpr() and
- exists(string s | s = call.getArgument(0).(Expr).getStringValue() |
+ exists(string s | s = call.getArgument(0).getStringValue() |
// match an opening angle bracket followed by a tag name, followed by arbitrary
// text and a closing angle bracket, potentially with whitespace in between
tagName = s.regexpCapture("\\s*<\\s*(\\w+)\\b[^>]*>\\s*", 1).toLowerCase()
diff --git a/javascript/ql/lib/semmle/javascript/security/UselessUseOfCat.qll b/javascript/ql/lib/semmle/javascript/security/UselessUseOfCat.qll
index b2b65038508..a9a1c3ace73 100644
--- a/javascript/ql/lib/semmle/javascript/security/UselessUseOfCat.qll
+++ b/javascript/ql/lib/semmle/javascript/security/UselessUseOfCat.qll
@@ -317,7 +317,7 @@ module PrettyPrintCatCall {
*/
string createFileThatIsReadFromCommandList(CommandCall call) {
exists(DataFlow::ArrayCreationNode array, DataFlow::Node element |
- array = call.getArgumentList().(DataFlow::ArrayCreationNode) and
+ array = call.getArgumentList() and
array.getSize() = 1 and
element = array.getElement(0)
|
diff --git a/javascript/ql/test/library-tests/TypeInference/FunctionWithAnalyzedParameters/FunctionWithAnalyzedParameters_inference.ql b/javascript/ql/test/library-tests/TypeInference/FunctionWithAnalyzedParameters/FunctionWithAnalyzedParameters_inference.ql
index 7f494e5db59..66dca096304 100644
--- a/javascript/ql/test/library-tests/TypeInference/FunctionWithAnalyzedParameters/FunctionWithAnalyzedParameters_inference.ql
+++ b/javascript/ql/test/library-tests/TypeInference/FunctionWithAnalyzedParameters/FunctionWithAnalyzedParameters_inference.ql
@@ -4,4 +4,4 @@ from FunctionWithAnalyzedParameters f, SimpleParameter p, AnalyzedVarDef var
where
f.argumentPassing(p, _) and
var.getAVariable() = p.getVariable()
-select p, var.(AnalyzedVarDef).getAnAssignedValue()
+select p, var.getAnAssignedValue()
diff --git a/python/ql/lib/semmle/python/Stmts.qll b/python/ql/lib/semmle/python/Stmts.qll
index 56612ef7283..3dc71e9d990 100644
--- a/python/ql/lib/semmle/python/Stmts.qll
+++ b/python/ql/lib/semmle/python/Stmts.qll
@@ -94,13 +94,13 @@ class AugAssign extends AugAssign_ {
* Gets the target of this augmented assignment statement.
* That is, the `a` in `a += b`.
*/
- Expr getTarget() { result = this.getOperation().(BinaryExpr).getLeft() }
+ Expr getTarget() { result = this.getOperation().getLeft() }
/**
* Gets the value of this augmented assignment statement.
* That is, the `b` in `a += b`.
*/
- Expr getValue() { result = this.getOperation().(BinaryExpr).getRight() }
+ Expr getValue() { result = this.getOperation().getRight() }
override Stmt getASubStatement() { none() }
}
diff --git a/python/ql/lib/semmle/python/essa/Definitions.qll b/python/ql/lib/semmle/python/essa/Definitions.qll
index e4e4bba747f..5af5309f441 100644
--- a/python/ql/lib/semmle/python/essa/Definitions.qll
+++ b/python/ql/lib/semmle/python/essa/Definitions.qll
@@ -221,7 +221,7 @@ class ModuleVariable extends SsaSourceVariable {
pragma[noinline]
ImportMemberNode global_variable_import() {
result.getScope() = this.(GlobalVariable).getScope() and
- import_from_dot_in_init(result.(ImportMemberNode).getModule(this.getName()))
+ import_from_dot_in_init(result.getModule(this.getName()))
}
override ControlFlowNode getAnImplicitUse() {
@@ -306,7 +306,7 @@ class EscapingGlobalVariable extends ModuleVariable {
Scope scope_as_global_variable() { result = this.(GlobalVariable).getScope() }
override CallNode redefinedAtCallSite() {
- result.(CallNode).getScope().getScope*() = this.scope_as_global_variable()
+ result.getScope().getScope*() = this.scope_as_global_variable()
}
}
@@ -332,7 +332,7 @@ class SpecialSsaSourceVariable extends SsaSourceVariable {
Scope scope_as_global_variable() { result = this.(GlobalVariable).getScope() }
override CallNode redefinedAtCallSite() {
- result.(CallNode).getScope().getScope*() = this.scope_as_global_variable()
+ result.getScope().getScope*() = this.scope_as_global_variable()
}
}
diff --git a/python/ql/lib/semmle/python/frameworks/Aiohttp.qll b/python/ql/lib/semmle/python/frameworks/Aiohttp.qll
index 748f6c92d39..3ec70643986 100644
--- a/python/ql/lib/semmle/python/frameworks/Aiohttp.qll
+++ b/python/ql/lib/semmle/python/frameworks/Aiohttp.qll
@@ -330,8 +330,8 @@ module AiohttpWebModel {
exists(Await await, DataFlow::CallCfgNode call, DataFlow::AttrRead read |
this.asExpr() = await
|
- read.(DataFlow::AttrRead).getObject() = Request::instance() and
- read.(DataFlow::AttrRead).getAttributeName() = "post" and
+ read.getObject() = Request::instance() and
+ read.getAttributeName() = "post" and
call.getFunction() = read and
await.getValue() = call.asExpr()
)
diff --git a/python/ql/lib/semmle/python/objects/Constants.qll b/python/ql/lib/semmle/python/objects/Constants.qll
index 941f631b9b3..2f5c062e63a 100644
--- a/python/ql/lib/semmle/python/objects/Constants.qll
+++ b/python/ql/lib/semmle/python/objects/Constants.qll
@@ -248,7 +248,7 @@ class UnicodeObjectInternal extends ConstantObjectInternal, TUnicode {
override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("unicode")) }
override Builtin getBuiltin() {
- result.(Builtin).strValue() = this.strValue() and
+ result.strValue() = this.strValue() and
result.getClass() = Builtin::special("unicode")
}
@@ -281,7 +281,7 @@ class BytesObjectInternal extends ConstantObjectInternal, TBytes {
override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("bytes")) }
override Builtin getBuiltin() {
- result.(Builtin).strValue() = this.strValue() and
+ result.strValue() = this.strValue() and
result.getClass() = Builtin::special("bytes")
}
diff --git a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll
index f41cdf35924..9b4296b7498 100644
--- a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll
+++ b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll
@@ -74,7 +74,7 @@ class AstCfgNode extends CfgNode, TElementNode {
override Location getLocation() { result = n.getLocation() }
final override string toString() {
- exists(string s | s = n.(AstNode).toString() |
+ exists(string s | s = n.toString() |
result = "[" + this.getSplitsString() + "] " + s
or
not exists(this.getSplitsString()) and result = s
diff --git a/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll b/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll
index 40beb734d37..88cc5920772 100644
--- a/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll
+++ b/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll
@@ -132,7 +132,7 @@ private string getSetterCallAttributeName(AST::SetterMethodCall call) {
predicate basicLoadStep(Node nodeFrom, Node nodeTo, string content) {
exists(ExprNodes::MethodCallCfgNode call |
call.getExpr().getNumberOfArguments() = 0 and
- content = call.getExpr().(AST::MethodCall).getMethodName() and
+ content = call.getExpr().getMethodName() and
nodeFrom.asExpr() = call.getReceiver() and
nodeTo.asExpr() = call
)
From 15c90adec5b02ee003fcc2ad70596e89f12a63f4 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 28 Oct 2021 18:08:20 +0200
Subject: [PATCH 222/471] remove redundant cast where the type is enforced by
an equality comparison
---
cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll | 2 +-
cpp/ql/src/Likely Bugs/Format/NonConstantFormat.ql | 4 ++--
cpp/ql/src/Likely Bugs/Leap Year/LeapYear.qll | 2 +-
cpp/ql/src/Metrics/Files/FCyclomaticComplexity.ql | 2 +-
cpp/ql/src/jsf/4.22 Pointers and References/AV Rule 173.ql | 2 +-
cpp/ql/src/jsf/4.28 Portable Code/AV Rule 210.ql | 6 +++---
java/ql/lib/semmle/code/java/ControlFlowGraph.qll | 2 +-
java/ql/lib/semmle/code/java/PrintAst.qll | 4 ++--
java/ql/lib/semmle/code/java/frameworks/JAXB.qll | 2 +-
java/ql/lib/semmle/code/xml/MavenPom.qll | 4 +---
java/ql/src/Security/CWE/CWE-833/LockOrderInconsistency.ql | 2 +-
.../ql/lib/semmle/javascript/security/dataflow/Xss.qll | 4 +---
javascript/ql/test/library-tests/CFG/StaticInit.ql | 2 +-
python/ql/lib/semmle/python/SpecialMethods.qll | 2 +-
python/ql/lib/semmle/python/frameworks/Django.qll | 2 +-
python/ql/lib/semmle/python/frameworks/Flask.qll | 6 +++---
python/ql/lib/semmle/python/frameworks/Tornado.qll | 2 +-
python/ql/src/Expressions/CallArgs.qll | 4 ++--
ruby/ql/lib/codeql/ruby/frameworks/XmlParsing.qll | 2 +-
19 files changed, 26 insertions(+), 30 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll b/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll
index 2811d1703aa..a10a94f357d 100644
--- a/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll
+++ b/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll
@@ -31,7 +31,7 @@ class Expr extends StmtParent, @expr {
override Stmt getEnclosingStmt() {
result = this.getParent().(Expr).getEnclosingStmt()
or
- result = this.getParent().(Stmt)
+ result = this.getParent()
or
exists(Expr other | result = other.getEnclosingStmt() and other.getConversion() = this)
or
diff --git a/cpp/ql/src/Likely Bugs/Format/NonConstantFormat.ql b/cpp/ql/src/Likely Bugs/Format/NonConstantFormat.ql
index f00dfa2213b..2a04669e85b 100644
--- a/cpp/ql/src/Likely Bugs/Format/NonConstantFormat.ql
+++ b/cpp/ql/src/Likely Bugs/Format/NonConstantFormat.ql
@@ -63,14 +63,14 @@ predicate cannotContainString(Type t) {
predicate isNonConst(DataFlow::Node node) {
exists(Expr e | e = node.asExpr() |
- exists(FunctionCall fc | fc = e.(FunctionCall) |
+ exists(FunctionCall fc | fc = e |
not (
whitelistFunction(fc.getTarget(), _) or
fc.getTarget().hasDefinition()
)
)
or
- exists(Parameter p | p = e.(VariableAccess).getTarget().(Parameter) |
+ exists(Parameter p | p = e.(VariableAccess).getTarget() |
p.getFunction().getName() = "main" and p.getType() instanceof PointerType
)
or
diff --git a/cpp/ql/src/Likely Bugs/Leap Year/LeapYear.qll b/cpp/ql/src/Likely Bugs/Leap Year/LeapYear.qll
index 23b66bd94a6..e164523700b 100644
--- a/cpp/ql/src/Likely Bugs/Leap Year/LeapYear.qll
+++ b/cpp/ql/src/Likely Bugs/Leap Year/LeapYear.qll
@@ -10,7 +10,7 @@ import semmle.code.cpp.commons.DateTime
* Get the top-level `BinaryOperation` enclosing the expression e.
*/
private BinaryOperation getATopLevelBinaryOperationExpression(Expr e) {
- result = e.getEnclosingElement().(BinaryOperation)
+ result = e.getEnclosingElement()
or
result = getATopLevelBinaryOperationExpression(e.getEnclosingElement())
}
diff --git a/cpp/ql/src/Metrics/Files/FCyclomaticComplexity.ql b/cpp/ql/src/Metrics/Files/FCyclomaticComplexity.ql
index 8aae5042ca4..3c6953caa5a 100644
--- a/cpp/ql/src/Metrics/Files/FCyclomaticComplexity.ql
+++ b/cpp/ql/src/Metrics/Files/FCyclomaticComplexity.ql
@@ -15,7 +15,7 @@ import cpp
from File f, float complexity, float loc
where
f.fromSource() and
- loc = sum(FunctionDeclarationEntry fde | fde.getFile() = f | fde.getNumberOfLines()).(float) and
+ loc = sum(FunctionDeclarationEntry fde | fde.getFile() = f | fde.getNumberOfLines()) and
if loc > 0
then
// Weighted average of complexity by function length
diff --git a/cpp/ql/src/jsf/4.22 Pointers and References/AV Rule 173.ql b/cpp/ql/src/jsf/4.22 Pointers and References/AV Rule 173.ql
index 49ed13f1b48..1b1e92bd4a7 100644
--- a/cpp/ql/src/jsf/4.22 Pointers and References/AV Rule 173.ql
+++ b/cpp/ql/src/jsf/4.22 Pointers and References/AV Rule 173.ql
@@ -26,7 +26,7 @@ import cpp
from Assignment a, Variable global, Variable local
where
a.fromSource() and
- global.getAnAccess() = a.getLValue().(VariableAccess) and
+ global.getAnAccess() = a.getLValue() and
local.getAnAccess() = a.getRValue().(AddressOfExpr).getOperand() and
local.hasSpecifier("auto") and
(
diff --git a/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 210.ql b/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 210.ql
index c12edf0c02a..a30509d9beb 100644
--- a/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 210.ql
+++ b/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 210.ql
@@ -49,11 +49,11 @@ class ExposingIntegralUnion extends Union {
exists(MemberVariable mv1, MemberVariable mv2, IntegralType mv1tp, IntegralType mv2tp |
mv1 = this.getAMemberVariable() and
mv2 = this.getAMemberVariable() and
- mv1tp = mv1.getUnderlyingType().(IntegralType) and
+ mv1tp = mv1.getUnderlyingType() and
(
- mv2tp = mv2.getUnderlyingType().(IntegralType)
+ mv2tp = mv2.getUnderlyingType()
or
- mv2tp = mv2.getUnderlyingType().(ArrayType).getBaseType().getUnderlyingType().(IntegralType)
+ mv2tp = mv2.getUnderlyingType().(ArrayType).getBaseType().getUnderlyingType()
) and
mv1tp.getSize() > mv2tp.getSize()
)
diff --git a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll
index 8e5325902dd..14738dda96f 100644
--- a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll
+++ b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll
@@ -150,7 +150,7 @@ private module ControlFlowGraphImpl {
* `TypeThrowable` which results in both `TypeError` and `TypeRuntimeException`.
*/
UncheckedThrowableType getAnUncheckedSubtype() {
- result = this.(UncheckedThrowableType)
+ result = this
or
result instanceof TypeError and this instanceof TypeThrowable
or
diff --git a/java/ql/lib/semmle/code/java/PrintAst.qll b/java/ql/lib/semmle/code/java/PrintAst.qll
index 0d785e9ee28..9527787e3a4 100644
--- a/java/ql/lib/semmle/code/java/PrintAst.qll
+++ b/java/ql/lib/semmle/code/java/PrintAst.qll
@@ -665,7 +665,7 @@ final class GenericTypeNode extends PrintAstNode, TGenericTypeNode {
override Location getLocation() { none() }
override ElementNode getChild(int childIndex) {
- result.getElement().(TypeVariable) = ty.getTypeParameter(childIndex)
+ result.getElement() = ty.getTypeParameter(childIndex)
}
/**
@@ -686,7 +686,7 @@ final class GenericCallableNode extends PrintAstNode, TGenericCallableNode {
override string toString() { result = "(Generic Parameters)" }
override ElementNode getChild(int childIndex) {
- result.getElement().(TypeVariable) = c.getTypeParameter(childIndex)
+ result.getElement() = c.getTypeParameter(childIndex)
}
/**
diff --git a/java/ql/lib/semmle/code/java/frameworks/JAXB.qll b/java/ql/lib/semmle/code/java/frameworks/JAXB.qll
index bf5e33cb004..edbc5091d49 100644
--- a/java/ql/lib/semmle/code/java/frameworks/JAXB.qll
+++ b/java/ql/lib/semmle/code/java/frameworks/JAXB.qll
@@ -54,7 +54,7 @@ class JaxbType extends Class {
this.getAnAnnotation() = a and
a.getType().(JaxbAnnotationType).hasName("XmlAccessorType")
|
- result.getAnAccess() = a.getValue("value").(VarAccess)
+ result.getAnAccess() = a.getValue("value")
)
}
diff --git a/java/ql/lib/semmle/code/xml/MavenPom.qll b/java/ql/lib/semmle/code/xml/MavenPom.qll
index 6303de34348..3855ccc74e5 100644
--- a/java/ql/lib/semmle/code/xml/MavenPom.qll
+++ b/java/ql/lib/semmle/code/xml/MavenPom.qll
@@ -395,9 +395,7 @@ class MavenRepo extends Folder {
/**
* Gets a Jar file contained within this repository.
*/
- File getAJarFile() {
- result = this.getAChildContainer*().(File) and result.getExtension() = "jar"
- }
+ File getAJarFile() { result = this.getAChildContainer*() and result.getExtension() = "jar" }
/**
* Gets any jar artifacts in this repository that match the POM project definition. This is an
diff --git a/java/ql/src/Security/CWE/CWE-833/LockOrderInconsistency.ql b/java/ql/src/Security/CWE/CWE-833/LockOrderInconsistency.ql
index 5ad653bd6dd..18736de4b3e 100644
--- a/java/ql/src/Security/CWE/CWE-833/LockOrderInconsistency.ql
+++ b/java/ql/src/Security/CWE/CWE-833/LockOrderInconsistency.ql
@@ -159,7 +159,7 @@ predicate badMethodAccessLockOrder(
MethodAccess outerAccess, MethodAccess innerAccess, MethodAccess other
) {
exists(Synched outer, Synched inner |
- inner.(MethodAccess) = innerAccess and
+ inner = innerAccess and
inner = outer.getInnerSynch() and
inner.getLockType() = outer.getLockType() and
exists(Parameter p, int i | outer.(Method).getAParameter() = p and p.getPosition() = i |
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/Xss.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/Xss.qll
index 03628f299a2..68026a02de4 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/Xss.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/Xss.qll
@@ -526,9 +526,7 @@ module ReflectedXss {
* ```
*/
predicate isLocalHeaderDefinition(HTTP::HeaderDefinition header) {
- exists(ReachableBasicBlock headerBlock |
- headerBlock = header.getBasicBlock().(ReachableBasicBlock)
- |
+ exists(ReachableBasicBlock headerBlock | headerBlock = header.getBasicBlock() |
1 =
strictcount(HTTP::ResponseSendArgument sender |
sender.getRouteHandler() = header.getRouteHandler() and
diff --git a/javascript/ql/test/library-tests/CFG/StaticInit.ql b/javascript/ql/test/library-tests/CFG/StaticInit.ql
index 942209dc73d..1b66ce26800 100644
--- a/javascript/ql/test/library-tests/CFG/StaticInit.ql
+++ b/javascript/ql/test/library-tests/CFG/StaticInit.ql
@@ -4,5 +4,5 @@ from ClassDefinition class_, FieldDefinition field
where
class_.getAField() = field and
field.isStatic() and
- field.getInit().getFirstControlFlowNode().getAPredecessor*() = class_.(ControlFlowNode)
+ field.getInit().getFirstControlFlowNode().getAPredecessor*() = class_
select field, "Field initializer occurs after its class is created"
diff --git a/python/ql/lib/semmle/python/SpecialMethods.qll b/python/ql/lib/semmle/python/SpecialMethods.qll
index 2218791794e..b548e7a52a3 100644
--- a/python/ql/lib/semmle/python/SpecialMethods.qll
+++ b/python/ql/lib/semmle/python/SpecialMethods.qll
@@ -107,7 +107,7 @@ class SpecialMethodCallNode extends PotentialSpecialMethodCallNode {
SpecialMethodCallNode() {
exists(SpecialMethod::Potential pot |
- this.(SpecialMethod::Potential) = pot and
+ this = pot and
pot.getSelf().pointsTo().getClass().lookup(pot.getSpecialMethodName()) = resolvedSpecialMethod
)
}
diff --git a/python/ql/lib/semmle/python/frameworks/Django.qll b/python/ql/lib/semmle/python/frameworks/Django.qll
index 89ff0537c97..2670d4f418e 100644
--- a/python/ql/lib/semmle/python/frameworks/Django.qll
+++ b/python/ql/lib/semmle/python/frameworks/Django.qll
@@ -1820,7 +1820,7 @@ private module PrivateDjango {
/** Gets a reference to this class. */
private DataFlow::TypeTrackingNode getARef(DataFlow::TypeTracker t) {
t.start() and
- result.asExpr().(ClassExpr) = this.getParent()
+ result.asExpr() = this.getParent()
or
exists(DataFlow::TypeTracker t2 | result = this.getARef(t2).track(t2, t))
}
diff --git a/python/ql/lib/semmle/python/frameworks/Flask.qll b/python/ql/lib/semmle/python/frameworks/Flask.qll
index 5bb838447cb..aaf962ae4f2 100644
--- a/python/ql/lib/semmle/python/frameworks/Flask.qll
+++ b/python/ql/lib/semmle/python/frameworks/Flask.qll
@@ -188,7 +188,7 @@ module Flask {
FlaskViewClass() {
this.getABase() = Views::View::subclassRef().getAUse().asExpr() and
- api_node.getAnImmediateUse().asExpr().(ClassExpr) = this.getParent()
+ api_node.getAnImmediateUse().asExpr() = this.getParent()
}
/** Gets a function that could handle incoming requests, if any. */
@@ -213,7 +213,7 @@ module Flask {
class FlaskMethodViewClass extends FlaskViewClass {
FlaskMethodViewClass() {
this.getABase() = Views::MethodView::subclassRef().getAUse().asExpr() and
- api_node.getAnImmediateUse().asExpr().(ClassExpr) = this.getParent()
+ api_node.getAnImmediateUse().asExpr() = this.getParent()
}
override Function getARequestHandler() {
@@ -294,7 +294,7 @@ module Flask {
override Function getARequestHandler() {
exists(DataFlow::LocalSourceNode func_src |
func_src.flowsTo(this.getViewArg()) and
- func_src.asExpr().(CallableExpr) = result.getDefinition()
+ func_src.asExpr() = result.getDefinition()
)
or
exists(FlaskViewClass vc |
diff --git a/python/ql/lib/semmle/python/frameworks/Tornado.qll b/python/ql/lib/semmle/python/frameworks/Tornado.qll
index 91ae3ac2575..67bcce97e4b 100644
--- a/python/ql/lib/semmle/python/frameworks/Tornado.qll
+++ b/python/ql/lib/semmle/python/frameworks/Tornado.qll
@@ -102,7 +102,7 @@ private module Tornado {
/** Gets a reference to this class. */
private DataFlow::TypeTrackingNode getARef(DataFlow::TypeTracker t) {
t.start() and
- result.asExpr().(ClassExpr) = this.getParent()
+ result.asExpr() = this.getParent()
or
exists(DataFlow::TypeTracker t2 | result = this.getARef(t2).track(t2, t))
}
diff --git a/python/ql/src/Expressions/CallArgs.qll b/python/ql/src/Expressions/CallArgs.qll
index 5e7e56d99d8..d2f94ca7cf0 100644
--- a/python/ql/src/Expressions/CallArgs.qll
+++ b/python/ql/src/Expressions/CallArgs.qll
@@ -99,14 +99,14 @@ private ControlFlowNode get_a_call(Value callable) {
/** 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)
+ result = func_or_cls
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)
+ result = func_or_cls
or
result = func_or_cls.(ClassValue).declaredAttribute("__init__")
}
diff --git a/ruby/ql/lib/codeql/ruby/frameworks/XmlParsing.qll b/ruby/ql/lib/codeql/ruby/frameworks/XmlParsing.qll
index 00f0e1d7b5d..2545c97f044 100644
--- a/ruby/ql/lib/codeql/ruby/frameworks/XmlParsing.qll
+++ b/ruby/ql/lib/codeql/ruby/frameworks/XmlParsing.qll
@@ -131,7 +131,7 @@ private DataFlow::LocalSourceNode trackFeature(Feature f, boolean enable, TypeTr
// same code.
exists(CfgNodes::ExprNodes::OperationCfgNode operation |
bitWiseAndOr(operation) and
- operation = result.asExpr().(CfgNodes::ExprNodes::OperationCfgNode) and
+ operation = result.asExpr() and
operation.getAnOperand() = trackFeature(f, enable).asExpr()
)
or
From 05900cda874984bd225499d3da686bb65ca01311 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Thu, 28 Oct 2021 17:10:48 +0100
Subject: [PATCH 223/471] C++: Rename 'Ssa' to 'SsaInternals' and move
definitions from 'SSaImplSpecific' to 'SsaInternals'. Now we can avoid cyclic
imports.
---
.../cpp/ir/dataflow/internal/DataFlowUtil.qll | 2 +-
.../ir/dataflow/internal/SsaImplSpecific.qll | 56 ++--------------
.../internal/{Ssa.qll => SsaInternals.qll} | 64 ++++++++++++++++++-
3 files changed, 68 insertions(+), 54 deletions(-)
rename cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/{Ssa.qll => SsaInternals.qll} (90%)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
index 3ebe81d4b4c..20c6792e787 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
@@ -11,7 +11,7 @@ private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.controlflow.IRGuards
private import semmle.code.cpp.models.interfaces.DataFlow
private import DataFlowPrivate
-private import Ssa as Ssa
+private import SsaInternals as Ssa
cached
private module Cached {
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplSpecific.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplSpecific.qll
index 211fc2514b3..20f9d1894b1 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplSpecific.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplSpecific.qll
@@ -1,11 +1,10 @@
private import semmle.code.cpp.ir.IR
-private import DataFlowUtil
-private import DataFlowPrivate
-private import DataFlowImplCommon as DataFlowImplCommon
-private import Ssa as Ssa
+private import SsaInternals as Ssa
class BasicBlock = IRBlock;
+class SourceVariable = Ssa::SourceVariable;
+
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.immediatelyDominates(bb) }
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
@@ -14,51 +13,6 @@ class ExitBasicBlock extends IRBlock {
ExitBasicBlock() { this.getLastInstruction() instanceof ExitFunctionInstruction }
}
-private newtype TSourceVariable =
- TSourceIRVariable(IRVariable var) or
- TSourceIRVariableIndirection(InitializeIndirectionInstruction init)
+predicate variableWrite = Ssa::variableWrite/4;
-abstract class SourceVariable extends TSourceVariable {
- IRVariable var;
-
- IRVariable getIRVariable() { result = var }
-
- abstract string toString();
-
- predicate isIndirection() { none() }
-}
-
-class SourceIRVariable extends SourceVariable, TSourceIRVariable {
- SourceIRVariable() { this = TSourceIRVariable(var) }
-
- override string toString() { result = this.getIRVariable().toString() }
-}
-
-class SourceIRVariableIndirection extends SourceVariable, TSourceIRVariableIndirection {
- InitializeIndirectionInstruction init;
-
- SourceIRVariableIndirection() {
- this = TSourceIRVariableIndirection(init) and var = init.getIRVariable()
- }
-
- override string toString() { result = "*" + this.getIRVariable().toString() }
-
- override predicate isIndirection() { any() }
-}
-
-predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
- DataFlowImplCommon::forceCachingInSameStage() and
- exists(Ssa::Def def |
- def.hasRankInBlock(bb, i) and
- v = def.getSourceVariable() and
- (if def.isCertain() then certain = true else certain = false)
- )
-}
-
-predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
- exists(Ssa::Use use |
- use.hasRankInBlock(bb, i) and
- v = use.getSourceVariable() and
- certain = true
- )
-}
+predicate variableRead = Ssa::variableRead/4;
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll
similarity index 90%
rename from cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
rename to cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll
index 62b0a6afeb5..5844e192767 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll
@@ -1,12 +1,47 @@
import SsaImplCommon
-import SsaImplSpecific
private import cpp as Cpp
private import semmle.code.cpp.ir.IR
private import DataFlowUtil
-private import DataFlowPrivate
+private import DataFlowImplCommon as DataFlowImplCommon
private import semmle.code.cpp.models.interfaces.Allocation as Alloc
private import semmle.code.cpp.models.interfaces.DataFlow as DataFlow
+private module SourceVariables {
+ private newtype TSourceVariable =
+ TSourceIRVariable(IRVariable var) or
+ TSourceIRVariableIndirection(InitializeIndirectionInstruction init)
+
+ abstract class SourceVariable extends TSourceVariable {
+ IRVariable var;
+
+ IRVariable getIRVariable() { result = var }
+
+ abstract string toString();
+
+ predicate isIndirection() { none() }
+ }
+
+ private class SourceIRVariable extends SourceVariable, TSourceIRVariable {
+ SourceIRVariable() { this = TSourceIRVariable(var) }
+
+ override string toString() { result = this.getIRVariable().toString() }
+ }
+
+ private class SourceIRVariableIndirection extends SourceVariable, TSourceIRVariableIndirection {
+ InitializeIndirectionInstruction init;
+
+ SourceIRVariableIndirection() {
+ this = TSourceIRVariableIndirection(init) and var = init.getIRVariable()
+ }
+
+ override string toString() { result = "*" + this.getIRVariable().toString() }
+
+ override predicate isIndirection() { any() }
+ }
+}
+
+import SourceVariables
+
cached
private newtype TDefOrUse =
TExplicitDef(Instruction store) { explicitWrite(_, store, _) } or
@@ -509,3 +544,28 @@ private module Cached {
}
import Cached
+
+/**
+ * Holds if the `i`'th write in block `bb` writes to the variable `v`.
+ * `certain` is `true` if the write is guaranteed to overwrite the entire variable.
+ */
+predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
+ DataFlowImplCommon::forceCachingInSameStage() and
+ exists(Def def |
+ def.hasRankInBlock(bb, i) and
+ v = def.getSourceVariable() and
+ (if def.isCertain() then certain = true else certain = false)
+ )
+}
+
+/**
+ * Holds if the `i`'th read in block `bb` reads to the variable `v`.
+ * `certain` is `true` if the read is guaranteed. For C++, this is always the case.
+ */
+predicate variableRead(IRBlock bb, int i, SourceVariable v, boolean certain) {
+ exists(Use use |
+ use.hasRankInBlock(bb, i) and
+ v = use.getSourceVariable() and
+ certain = true
+ )
+}
From cfc5629435604b50bb9844da8a4e1f86436ed333 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 28 Oct 2021 18:19:37 +0200
Subject: [PATCH 224/471] apply all doc fixes
Co-authored-by: hubwriter
---
javascript/ql/src/Security/CWE-1004/ClientExposedCookie.qhelp | 4 ++--
javascript/ql/src/Security/CWE-614/ClearTextCookie.qhelp | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/javascript/ql/src/Security/CWE-1004/ClientExposedCookie.qhelp b/javascript/ql/src/Security/CWE-1004/ClientExposedCookie.qhelp
index 54c689f8731..c8ed75683e7 100644
--- a/javascript/ql/src/Security/CWE-1004/ClientExposedCookie.qhelp
+++ b/javascript/ql/src/Security/CWE-1004/ClientExposedCookie.qhelp
@@ -21,7 +21,7 @@ Set the httpOnly flag on all cookies that are not needed by the cli
The following example stores an authentication token in a cookie that can
-viewed by the client.
+be viewed by the client.
@@ -34,7 +34,7 @@ attribute on the cookie.
ExpressJS: Use cookies securely.
OWASP: Set cookie flags appropriately.
-Set-Cookie.
+Mozilla: Set-Cookie.
diff --git a/javascript/ql/src/Security/CWE-614/ClearTextCookie.qhelp b/javascript/ql/src/Security/CWE-614/ClearTextCookie.qhelp
index 8b0716d0a4f..6286f68ed0c 100644
--- a/javascript/ql/src/Security/CWE-614/ClearTextCookie.qhelp
+++ b/javascript/ql/src/Security/CWE-614/ClearTextCookie.qhelp
@@ -33,6 +33,6 @@ attribute on the cookie.
ExpressJS: Use cookies securely.
OWASP: Set cookie flags appropriately.
-Set-Cookie.
+Mozilla: Set-Cookie.
From 490156d7db7de16bde2f76b1b379d1428bc9c335 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Thu, 28 Oct 2021 17:26:28 +0100
Subject: [PATCH 225/471] C++: Remove the 'isIndirection' predicate on
'SourceVariable' and move the rootdef of 'getIRVariable' into the two
subclasses.
---
.../cpp/ir/dataflow/internal/SsaInternals.qll | 37 ++++++++-----------
1 file changed, 15 insertions(+), 22 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll
index 5844e192767..1dd3d61951a 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll
@@ -14,29 +14,27 @@ private module SourceVariables {
abstract class SourceVariable extends TSourceVariable {
IRVariable var;
- IRVariable getIRVariable() { result = var }
-
abstract string toString();
-
- predicate isIndirection() { none() }
}
- private class SourceIRVariable extends SourceVariable, TSourceIRVariable {
+ class SourceIRVariable extends SourceVariable, TSourceIRVariable {
SourceIRVariable() { this = TSourceIRVariable(var) }
+ IRVariable getIRVariable() { result = var }
+
override string toString() { result = this.getIRVariable().toString() }
}
- private class SourceIRVariableIndirection extends SourceVariable, TSourceIRVariableIndirection {
+ class SourceIRVariableIndirection extends SourceVariable, TSourceIRVariableIndirection {
InitializeIndirectionInstruction init;
SourceIRVariableIndirection() {
this = TSourceIRVariableIndirection(init) and var = init.getIRVariable()
}
- override string toString() { result = "*" + this.getIRVariable().toString() }
+ IRVariable getUnderlyingIRVariable() { result = var }
- override predicate isIndirection() { any() }
+ override string toString() { result = "*" + this.getUnderlyingIRVariable().toString() }
}
}
@@ -120,8 +118,7 @@ private class ExplicitDef extends Def, TExplicitDef {
override SourceVariable getSourceVariable() {
exists(VariableInstruction var |
explicitWrite(_, this.getInstruction(), var) and
- result.getIRVariable() = var.getIRVariable() and
- not result.isIndirection()
+ result.(SourceIRVariable).getIRVariable() = var.getIRVariable()
)
}
@@ -132,11 +129,11 @@ private class ParameterDef extends Def, TInitializeParam {
ParameterDef() { this = TInitializeParam(store) }
override SourceVariable getSourceVariable() {
- result.getIRVariable() = store.(InitializeParameterInstruction).getIRVariable() and
- not result.isIndirection()
+ result.(SourceIRVariable).getIRVariable() =
+ store.(InitializeParameterInstruction).getIRVariable()
or
- result.getIRVariable() = store.(InitializeIndirectionInstruction).getIRVariable() and
- result.isIndirection()
+ result.(SourceIRVariableIndirection).getUnderlyingIRVariable() =
+ store.(InitializeIndirectionInstruction).getIRVariable()
}
override predicate isCertain() { any() }
@@ -166,12 +163,9 @@ private class ExplicitUse extends Use, TExplicitUse {
override SourceVariable getSourceVariable() {
exists(VariableInstruction var |
use.getDef() = var and
- result.getIRVariable() = var.getIRVariable() and
- (
- if use.getUse() instanceof ReadSideEffectInstruction
- then result.isIndirection()
- else not result.isIndirection()
- )
+ if use.getUse() instanceof ReadSideEffectInstruction
+ then result.(SourceIRVariableIndirection).getUnderlyingIRVariable() = var.getIRVariable()
+ else result.(SourceIRVariable).getIRVariable() = var.getIRVariable()
)
}
}
@@ -182,8 +176,7 @@ private class ReturnParameterIndirection extends Use, TReturnParamIndirection {
override SourceVariable getSourceVariable() {
exists(ReturnIndirectionInstruction ret |
returnParameterIndirection(use, ret) and
- result.getIRVariable() = ret.getIRVariable() and
- result.isIndirection()
+ result.(SourceIRVariableIndirection).getUnderlyingIRVariable() = ret.getIRVariable()
)
}
}
From 8f9741ae724311b256f33d458f860f6bc5bff077 Mon Sep 17 00:00:00 2001
From: yoff
Date: Thu, 28 Oct 2021 19:13:08 +0200
Subject: [PATCH 226/471] Update
python/ql/lib/semmle/python/internal/Awaited.qll
Co-authored-by: Rasmus Wriedt Larsen
---
python/ql/lib/semmle/python/internal/Awaited.qll | 2 ++
1 file changed, 2 insertions(+)
diff --git a/python/ql/lib/semmle/python/internal/Awaited.qll b/python/ql/lib/semmle/python/internal/Awaited.qll
index bca318913d5..356464ef580 100644
--- a/python/ql/lib/semmle/python/internal/Awaited.qll
+++ b/python/ql/lib/semmle/python/internal/Awaited.qll
@@ -8,6 +8,8 @@ private import python
private import semmle.python.dataflow.new.DataFlow
/**
+ * INTERNAL: Do not use.
+ *
* Holds if `result` is the result of awaiting `awaitedValue`.
*/
cached
From 0f2f68bcbba36a2526c3441a18fda938224ad89e Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Thu, 28 Oct 2021 19:14:02 +0200
Subject: [PATCH 227/471] Python: rename file
---
.../test/library-tests/frameworks/asyncpg/{asyncpg.py => test.py} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename python/ql/test/library-tests/frameworks/asyncpg/{asyncpg.py => test.py} (100%)
diff --git a/python/ql/test/library-tests/frameworks/asyncpg/asyncpg.py b/python/ql/test/library-tests/frameworks/asyncpg/test.py
similarity index 100%
rename from python/ql/test/library-tests/frameworks/asyncpg/asyncpg.py
rename to python/ql/test/library-tests/frameworks/asyncpg/test.py
From 5f73fb21b86c4e4b207ed2b73b6ff35f87109a39 Mon Sep 17 00:00:00 2001
From: Ethan P <56270045+ethanpalm@users.noreply.github.com>
Date: Thu, 28 Oct 2021 10:55:44 -0700
Subject: [PATCH 228/471] Add new article to ref page
---
docs/codeql/codeql-cli/codeql-cli-reference.rst | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/docs/codeql/codeql-cli/codeql-cli-reference.rst b/docs/codeql/codeql-cli/codeql-cli-reference.rst
index 2cc5e755350..ec48a2061dd 100644
--- a/docs/codeql/codeql-cli/codeql-cli-reference.rst
+++ b/docs/codeql/codeql-cli/codeql-cli-reference.rst
@@ -14,7 +14,7 @@ Learn more about the files you can use when running CodeQL processes and the res
query-reference-files
sarif-output
exit-codes
-
+ extractor-options
- :doc:`About CodeQL packs `: CodeQL packs are created with the CodeQL CLI and are used to create, depend on, publish, and run CodeQL queries and libraries.
- :doc:`About QL packs `: QL packs are used to organize the files used in CodeQL analysis. They
@@ -23,3 +23,4 @@ Learn more about the files you can use when running CodeQL processes and the res
- :doc:`SARIF output `: CodeQL supports SARIF as an output format for sharing static analysis results.
- :doc:`Exit codes `: The CodeQL CLI reports the status of each command it runs as an exit code.
This exit code provides information for subsequent commands or for other tools that rely on the CodeQL CLI.
+- :doc: `Extractor options
Date: Fri, 29 Oct 2021 09:44:43 +0300
Subject: [PATCH 229/471] Update InsecureTemporaryFile.ql
---
.../experimental/Security/CWE/CWE-377/InsecureTemporaryFile.ql | 2 ++
1 file changed, 2 insertions(+)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.ql b/cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.ql
index e039f7a7e62..0b7458d407d 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-377/InsecureTemporaryFile.ql
@@ -39,6 +39,7 @@ predicate numberArgumentWrite(Function f, int apos) {
from FunctionCall fc, string msg
where
+ // search for functions for generating a name, without a guarantee of the absence of a file during the period of work with it.
(
fc.getTarget().hasGlobalOrStdName("tmpnam") or
fc.getTarget().hasGlobalOrStdName("tmpnam_s") or
@@ -53,6 +54,7 @@ where
msg =
"Finding the name of a file that does not exist does not mean that it will not be exist at the next operation."
or
+ // finding places to work with a file without setting permissions, but with predictable names.
(
fc.getTarget().hasGlobalOrStdName("fopen") or
fc.getTarget().hasGlobalOrStdName("open")
From 635a668670c88268f2c1a0da370aac4d7767ec02 Mon Sep 17 00:00:00 2001
From: ihsinme
Date: Fri, 29 Oct 2021 10:08:41 +0300
Subject: [PATCH 230/471] Update IncorrectChangingWorkingDirectory.ql
---
.../Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.ql | 1 +
1 file changed, 1 insertion(+)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.ql b/cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.ql
index d731c70a8e2..f0c8479dc59 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-243/IncorrectChangingWorkingDirectory.ql
@@ -50,6 +50,7 @@ where
fc.getTarget().hasGlobalOrStdName("chroot") and
not inExistsChdir(fc) and
not outExistsChdir(fc) and
+ // in this section I want to exclude calls to functions containing chroot that have a direct path to chdir, or to a function containing chdir
exists(FunctionCall fctmp |
fc.getEnclosingStmt().getParentStmt*() = fctmp.getTarget().getEntryPoint().getChildStmt*() and
not inExistsChdir(fctmp) and
From 2b4e3a7d9b9d452cd0c12ebd6825a13fd88e9acc Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Fri, 29 Oct 2021 10:59:36 +0200
Subject: [PATCH 231/471] Dataflow: Refactor the getEnclosingCallable and
ParameterNode interface.
---
.../java/dataflow/internal/DataFlowImplCommon.qll | 6 ++----
.../dataflow/internal/DataFlowImplConsistency.qll | 12 ++++++------
.../code/java/dataflow/internal/DataFlowNodes.qll | 8 ++++++++
3 files changed, 16 insertions(+), 10 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll
index e11244c42b0..9d2ff1d35eb 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll
@@ -251,7 +251,7 @@ private module Cached {
predicate forceCachingInSameStage() { any() }
cached
- predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() }
+ predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = nodeGetEnclosingCallable(n) }
cached
predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) {
@@ -316,9 +316,7 @@ private module Cached {
}
cached
- predicate parameterNode(Node n, DataFlowCallable c, int i) {
- n.(ParameterNode).isParameterOf(c, i)
- }
+ predicate parameterNode(Node p, DataFlowCallable c, int pos) { isParameterNode(p, c, pos) }
cached
predicate argumentNode(Node n, DataFlowCall call, int pos) {
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll
index dd64fc70039..acf31338f9a 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll
@@ -31,7 +31,7 @@ module Consistency {
query predicate uniqueEnclosingCallable(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
- c = count(n.getEnclosingCallable()) and
+ c = count(nodeGetEnclosingCallable(n)) and
c != 1 and
msg = "Node should have one enclosing callable but has " + c + "."
)
@@ -85,13 +85,13 @@ module Consistency {
}
query predicate parameterCallable(ParameterNode p, string msg) {
- exists(DataFlowCallable c | p.isParameterOf(c, _) and c != p.getEnclosingCallable()) and
+ exists(DataFlowCallable c | isParameterNode(p, c, _) and c != nodeGetEnclosingCallable(p)) and
msg = "Callable mismatch for parameter."
}
query predicate localFlowIsLocal(Node n1, Node n2, string msg) {
simpleLocalFlowStep(n1, n2) and
- n1.getEnclosingCallable() != n2.getEnclosingCallable() and
+ nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
msg = "Local flow step does not preserve enclosing callable."
}
@@ -106,7 +106,7 @@ module Consistency {
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
isUnreachableInCall(n, call) and
exists(DataFlowCallable c |
- c = n.getEnclosingCallable() and
+ c = nodeGetEnclosingCallable(n) and
not viableCallable(call) = c
) and
msg = "Call context for isUnreachableInCall is inconsistent with call graph."
@@ -120,7 +120,7 @@ module Consistency {
n.(ArgumentNode).argumentOf(call, _) and
msg = "ArgumentNode and call does not share enclosing callable."
) and
- n.getEnclosingCallable() != call.getEnclosingCallable()
+ nodeGetEnclosingCallable(n) != call.getEnclosingCallable()
}
// This predicate helps the compiler forget that in some languages
@@ -151,7 +151,7 @@ module Consistency {
}
query predicate postIsInSameCallable(PostUpdateNode n, string msg) {
- n.getEnclosingCallable() != n.getPreUpdateNode().getEnclosingCallable() and
+ nodeGetEnclosingCallable(n) != nodeGetEnclosingCallable(n.getPreUpdateNode()) and
msg = "PostUpdateNode does not share callable with its pre-update node."
}
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll
index 5ea421db0c7..70f4f38f134 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll
@@ -304,6 +304,14 @@ private class ImplicitExprPostUpdate extends ImplicitPostUpdateNode, TImplicitEx
}
module Private {
+ /** Gets the callable in which this node occurs. */
+ DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() }
+
+ /** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */
+ predicate isParameterNode(ParameterNode p, DataFlowCallable c, int pos) {
+ p.isParameterOf(c, pos)
+ }
+
/**
* A data flow node that occurs as the argument of a call and is passed as-is
* to the callable. Arguments that are wrapped in an implicit varargs array
From 00df6798b1ced1a020dffb48177ab07d436bc05b Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Fri, 29 Oct 2021 11:00:23 +0200
Subject: [PATCH 232/471] Dataflow: Sync
---
.../cpp/dataflow/internal/DataFlowImplCommon.qll | 6 ++----
.../dataflow/internal/DataFlowImplConsistency.qll | 12 ++++++------
.../cpp/ir/dataflow/internal/DataFlowImplCommon.qll | 6 ++----
.../ir/dataflow/internal/DataFlowImplConsistency.qll | 12 ++++++------
.../csharp/dataflow/internal/DataFlowImplCommon.qll | 6 ++----
.../dataflow/internal/DataFlowImplConsistency.qll | 12 ++++++------
.../dataflow/new/internal/DataFlowImplCommon.qll | 6 ++----
.../new/internal/DataFlowImplConsistency.qll | 12 ++++++------
.../ruby/dataflow/internal/DataFlowImplCommon.qll | 6 ++----
.../dataflow/internal/DataFlowImplConsistency.qll | 12 ++++++------
10 files changed, 40 insertions(+), 50 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
index e11244c42b0..9d2ff1d35eb 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
@@ -251,7 +251,7 @@ private module Cached {
predicate forceCachingInSameStage() { any() }
cached
- predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() }
+ predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = nodeGetEnclosingCallable(n) }
cached
predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) {
@@ -316,9 +316,7 @@ private module Cached {
}
cached
- predicate parameterNode(Node n, DataFlowCallable c, int i) {
- n.(ParameterNode).isParameterOf(c, i)
- }
+ predicate parameterNode(Node p, DataFlowCallable c, int pos) { isParameterNode(p, c, pos) }
cached
predicate argumentNode(Node n, DataFlowCall call, int pos) {
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll
index dd64fc70039..acf31338f9a 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll
@@ -31,7 +31,7 @@ module Consistency {
query predicate uniqueEnclosingCallable(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
- c = count(n.getEnclosingCallable()) and
+ c = count(nodeGetEnclosingCallable(n)) and
c != 1 and
msg = "Node should have one enclosing callable but has " + c + "."
)
@@ -85,13 +85,13 @@ module Consistency {
}
query predicate parameterCallable(ParameterNode p, string msg) {
- exists(DataFlowCallable c | p.isParameterOf(c, _) and c != p.getEnclosingCallable()) and
+ exists(DataFlowCallable c | isParameterNode(p, c, _) and c != nodeGetEnclosingCallable(p)) and
msg = "Callable mismatch for parameter."
}
query predicate localFlowIsLocal(Node n1, Node n2, string msg) {
simpleLocalFlowStep(n1, n2) and
- n1.getEnclosingCallable() != n2.getEnclosingCallable() and
+ nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
msg = "Local flow step does not preserve enclosing callable."
}
@@ -106,7 +106,7 @@ module Consistency {
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
isUnreachableInCall(n, call) and
exists(DataFlowCallable c |
- c = n.getEnclosingCallable() and
+ c = nodeGetEnclosingCallable(n) and
not viableCallable(call) = c
) and
msg = "Call context for isUnreachableInCall is inconsistent with call graph."
@@ -120,7 +120,7 @@ module Consistency {
n.(ArgumentNode).argumentOf(call, _) and
msg = "ArgumentNode and call does not share enclosing callable."
) and
- n.getEnclosingCallable() != call.getEnclosingCallable()
+ nodeGetEnclosingCallable(n) != call.getEnclosingCallable()
}
// This predicate helps the compiler forget that in some languages
@@ -151,7 +151,7 @@ module Consistency {
}
query predicate postIsInSameCallable(PostUpdateNode n, string msg) {
- n.getEnclosingCallable() != n.getPreUpdateNode().getEnclosingCallable() and
+ nodeGetEnclosingCallable(n) != nodeGetEnclosingCallable(n.getPreUpdateNode()) and
msg = "PostUpdateNode does not share callable with its pre-update node."
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
index e11244c42b0..9d2ff1d35eb 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
@@ -251,7 +251,7 @@ private module Cached {
predicate forceCachingInSameStage() { any() }
cached
- predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() }
+ predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = nodeGetEnclosingCallable(n) }
cached
predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) {
@@ -316,9 +316,7 @@ private module Cached {
}
cached
- predicate parameterNode(Node n, DataFlowCallable c, int i) {
- n.(ParameterNode).isParameterOf(c, i)
- }
+ predicate parameterNode(Node p, DataFlowCallable c, int pos) { isParameterNode(p, c, pos) }
cached
predicate argumentNode(Node n, DataFlowCall call, int pos) {
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll
index dd64fc70039..acf31338f9a 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll
@@ -31,7 +31,7 @@ module Consistency {
query predicate uniqueEnclosingCallable(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
- c = count(n.getEnclosingCallable()) and
+ c = count(nodeGetEnclosingCallable(n)) and
c != 1 and
msg = "Node should have one enclosing callable but has " + c + "."
)
@@ -85,13 +85,13 @@ module Consistency {
}
query predicate parameterCallable(ParameterNode p, string msg) {
- exists(DataFlowCallable c | p.isParameterOf(c, _) and c != p.getEnclosingCallable()) and
+ exists(DataFlowCallable c | isParameterNode(p, c, _) and c != nodeGetEnclosingCallable(p)) and
msg = "Callable mismatch for parameter."
}
query predicate localFlowIsLocal(Node n1, Node n2, string msg) {
simpleLocalFlowStep(n1, n2) and
- n1.getEnclosingCallable() != n2.getEnclosingCallable() and
+ nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
msg = "Local flow step does not preserve enclosing callable."
}
@@ -106,7 +106,7 @@ module Consistency {
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
isUnreachableInCall(n, call) and
exists(DataFlowCallable c |
- c = n.getEnclosingCallable() and
+ c = nodeGetEnclosingCallable(n) and
not viableCallable(call) = c
) and
msg = "Call context for isUnreachableInCall is inconsistent with call graph."
@@ -120,7 +120,7 @@ module Consistency {
n.(ArgumentNode).argumentOf(call, _) and
msg = "ArgumentNode and call does not share enclosing callable."
) and
- n.getEnclosingCallable() != call.getEnclosingCallable()
+ nodeGetEnclosingCallable(n) != call.getEnclosingCallable()
}
// This predicate helps the compiler forget that in some languages
@@ -151,7 +151,7 @@ module Consistency {
}
query predicate postIsInSameCallable(PostUpdateNode n, string msg) {
- n.getEnclosingCallable() != n.getPreUpdateNode().getEnclosingCallable() and
+ nodeGetEnclosingCallable(n) != nodeGetEnclosingCallable(n.getPreUpdateNode()) and
msg = "PostUpdateNode does not share callable with its pre-update node."
}
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll
index e11244c42b0..9d2ff1d35eb 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll
@@ -251,7 +251,7 @@ private module Cached {
predicate forceCachingInSameStage() { any() }
cached
- predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() }
+ predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = nodeGetEnclosingCallable(n) }
cached
predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) {
@@ -316,9 +316,7 @@ private module Cached {
}
cached
- predicate parameterNode(Node n, DataFlowCallable c, int i) {
- n.(ParameterNode).isParameterOf(c, i)
- }
+ predicate parameterNode(Node p, DataFlowCallable c, int pos) { isParameterNode(p, c, pos) }
cached
predicate argumentNode(Node n, DataFlowCall call, int pos) {
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll
index dd64fc70039..acf31338f9a 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll
@@ -31,7 +31,7 @@ module Consistency {
query predicate uniqueEnclosingCallable(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
- c = count(n.getEnclosingCallable()) and
+ c = count(nodeGetEnclosingCallable(n)) and
c != 1 and
msg = "Node should have one enclosing callable but has " + c + "."
)
@@ -85,13 +85,13 @@ module Consistency {
}
query predicate parameterCallable(ParameterNode p, string msg) {
- exists(DataFlowCallable c | p.isParameterOf(c, _) and c != p.getEnclosingCallable()) and
+ exists(DataFlowCallable c | isParameterNode(p, c, _) and c != nodeGetEnclosingCallable(p)) and
msg = "Callable mismatch for parameter."
}
query predicate localFlowIsLocal(Node n1, Node n2, string msg) {
simpleLocalFlowStep(n1, n2) and
- n1.getEnclosingCallable() != n2.getEnclosingCallable() and
+ nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
msg = "Local flow step does not preserve enclosing callable."
}
@@ -106,7 +106,7 @@ module Consistency {
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
isUnreachableInCall(n, call) and
exists(DataFlowCallable c |
- c = n.getEnclosingCallable() and
+ c = nodeGetEnclosingCallable(n) and
not viableCallable(call) = c
) and
msg = "Call context for isUnreachableInCall is inconsistent with call graph."
@@ -120,7 +120,7 @@ module Consistency {
n.(ArgumentNode).argumentOf(call, _) and
msg = "ArgumentNode and call does not share enclosing callable."
) and
- n.getEnclosingCallable() != call.getEnclosingCallable()
+ nodeGetEnclosingCallable(n) != call.getEnclosingCallable()
}
// This predicate helps the compiler forget that in some languages
@@ -151,7 +151,7 @@ module Consistency {
}
query predicate postIsInSameCallable(PostUpdateNode n, string msg) {
- n.getEnclosingCallable() != n.getPreUpdateNode().getEnclosingCallable() and
+ nodeGetEnclosingCallable(n) != nodeGetEnclosingCallable(n.getPreUpdateNode()) and
msg = "PostUpdateNode does not share callable with its pre-update node."
}
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll
index e11244c42b0..9d2ff1d35eb 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll
@@ -251,7 +251,7 @@ private module Cached {
predicate forceCachingInSameStage() { any() }
cached
- predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() }
+ predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = nodeGetEnclosingCallable(n) }
cached
predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) {
@@ -316,9 +316,7 @@ private module Cached {
}
cached
- predicate parameterNode(Node n, DataFlowCallable c, int i) {
- n.(ParameterNode).isParameterOf(c, i)
- }
+ predicate parameterNode(Node p, DataFlowCallable c, int pos) { isParameterNode(p, c, pos) }
cached
predicate argumentNode(Node n, DataFlowCall call, int pos) {
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll
index dd64fc70039..acf31338f9a 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll
@@ -31,7 +31,7 @@ module Consistency {
query predicate uniqueEnclosingCallable(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
- c = count(n.getEnclosingCallable()) and
+ c = count(nodeGetEnclosingCallable(n)) and
c != 1 and
msg = "Node should have one enclosing callable but has " + c + "."
)
@@ -85,13 +85,13 @@ module Consistency {
}
query predicate parameterCallable(ParameterNode p, string msg) {
- exists(DataFlowCallable c | p.isParameterOf(c, _) and c != p.getEnclosingCallable()) and
+ exists(DataFlowCallable c | isParameterNode(p, c, _) and c != nodeGetEnclosingCallable(p)) and
msg = "Callable mismatch for parameter."
}
query predicate localFlowIsLocal(Node n1, Node n2, string msg) {
simpleLocalFlowStep(n1, n2) and
- n1.getEnclosingCallable() != n2.getEnclosingCallable() and
+ nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
msg = "Local flow step does not preserve enclosing callable."
}
@@ -106,7 +106,7 @@ module Consistency {
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
isUnreachableInCall(n, call) and
exists(DataFlowCallable c |
- c = n.getEnclosingCallable() and
+ c = nodeGetEnclosingCallable(n) and
not viableCallable(call) = c
) and
msg = "Call context for isUnreachableInCall is inconsistent with call graph."
@@ -120,7 +120,7 @@ module Consistency {
n.(ArgumentNode).argumentOf(call, _) and
msg = "ArgumentNode and call does not share enclosing callable."
) and
- n.getEnclosingCallable() != call.getEnclosingCallable()
+ nodeGetEnclosingCallable(n) != call.getEnclosingCallable()
}
// This predicate helps the compiler forget that in some languages
@@ -151,7 +151,7 @@ module Consistency {
}
query predicate postIsInSameCallable(PostUpdateNode n, string msg) {
- n.getEnclosingCallable() != n.getPreUpdateNode().getEnclosingCallable() and
+ nodeGetEnclosingCallable(n) != nodeGetEnclosingCallable(n.getPreUpdateNode()) and
msg = "PostUpdateNode does not share callable with its pre-update node."
}
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll
index e11244c42b0..9d2ff1d35eb 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll
@@ -251,7 +251,7 @@ private module Cached {
predicate forceCachingInSameStage() { any() }
cached
- predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() }
+ predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = nodeGetEnclosingCallable(n) }
cached
predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) {
@@ -316,9 +316,7 @@ private module Cached {
}
cached
- predicate parameterNode(Node n, DataFlowCallable c, int i) {
- n.(ParameterNode).isParameterOf(c, i)
- }
+ predicate parameterNode(Node p, DataFlowCallable c, int pos) { isParameterNode(p, c, pos) }
cached
predicate argumentNode(Node n, DataFlowCall call, int pos) {
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll
index dd64fc70039..acf31338f9a 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll
@@ -31,7 +31,7 @@ module Consistency {
query predicate uniqueEnclosingCallable(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
- c = count(n.getEnclosingCallable()) and
+ c = count(nodeGetEnclosingCallable(n)) and
c != 1 and
msg = "Node should have one enclosing callable but has " + c + "."
)
@@ -85,13 +85,13 @@ module Consistency {
}
query predicate parameterCallable(ParameterNode p, string msg) {
- exists(DataFlowCallable c | p.isParameterOf(c, _) and c != p.getEnclosingCallable()) and
+ exists(DataFlowCallable c | isParameterNode(p, c, _) and c != nodeGetEnclosingCallable(p)) and
msg = "Callable mismatch for parameter."
}
query predicate localFlowIsLocal(Node n1, Node n2, string msg) {
simpleLocalFlowStep(n1, n2) and
- n1.getEnclosingCallable() != n2.getEnclosingCallable() and
+ nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
msg = "Local flow step does not preserve enclosing callable."
}
@@ -106,7 +106,7 @@ module Consistency {
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
isUnreachableInCall(n, call) and
exists(DataFlowCallable c |
- c = n.getEnclosingCallable() and
+ c = nodeGetEnclosingCallable(n) and
not viableCallable(call) = c
) and
msg = "Call context for isUnreachableInCall is inconsistent with call graph."
@@ -120,7 +120,7 @@ module Consistency {
n.(ArgumentNode).argumentOf(call, _) and
msg = "ArgumentNode and call does not share enclosing callable."
) and
- n.getEnclosingCallable() != call.getEnclosingCallable()
+ nodeGetEnclosingCallable(n) != call.getEnclosingCallable()
}
// This predicate helps the compiler forget that in some languages
@@ -151,7 +151,7 @@ module Consistency {
}
query predicate postIsInSameCallable(PostUpdateNode n, string msg) {
- n.getEnclosingCallable() != n.getPreUpdateNode().getEnclosingCallable() and
+ nodeGetEnclosingCallable(n) != nodeGetEnclosingCallable(n.getPreUpdateNode()) and
msg = "PostUpdateNode does not share callable with its pre-update node."
}
From 5951ae79b9ac5a1c897be08e3ab110b2c17c7bc8 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Fri, 29 Oct 2021 11:11:35 +0200
Subject: [PATCH 233/471] Dataflow: Add language specific predicates.
---
.../semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll | 6 ++++++
.../code/cpp/ir/dataflow/internal/DataFlowPrivate.qll | 6 ++++++
.../code/csharp/dataflow/internal/DataFlowPrivate.qll | 6 ++++++
.../semmle/python/dataflow/new/internal/DataFlowPrivate.qll | 6 ++++++
.../lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll | 6 ++++++
5 files changed, 30 insertions(+)
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
index 0eecf7a6040..462a6a98328 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
@@ -3,6 +3,12 @@ private import DataFlowUtil
private import DataFlowDispatch
private import FlowVar
+/** Gets the callable in which this node occurs. */
+DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() }
+
+/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */
+predicate isParameterNode(ParameterNode p, DataFlowCallable c, int pos) { p.isParameterOf(c, pos) }
+
/** Gets the instance argument of a non-static call. */
private Node getInstanceArgument(Call call) {
result.asExpr() = call.getQualifier()
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
index 3f215883df3..89203b0acf8 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
@@ -3,6 +3,12 @@ private import DataFlowUtil
private import semmle.code.cpp.ir.IR
private import DataFlowDispatch
+/** Gets the callable in which this node occurs. */
+DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() }
+
+/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */
+predicate isParameterNode(ParameterNode p, DataFlowCallable c, int pos) { p.isParameterOf(c, pos) }
+
/**
* A data flow node that occurs as the argument of a call and is passed as-is
* to the callable. Instance arguments (`this` pointer) and read side effects
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
index d47cfd95d8f..f397141cd72 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
@@ -18,6 +18,12 @@ private import semmle.code.csharp.frameworks.NHibernate
private import semmle.code.csharp.frameworks.system.Collections
private import semmle.code.csharp.frameworks.system.threading.Tasks
+/** Gets the callable in which this node occurs. */
+DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() }
+
+/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */
+predicate isParameterNode(ParameterNode p, DataFlowCallable c, int pos) { p.isParameterOf(c, pos) }
+
abstract class NodeImpl extends Node {
/** Do not call: use `getEnclosingCallable()` instead. */
abstract DataFlowCallable getEnclosingCallableImpl();
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll
index b1a9ea7aa3a..9863b7ed85e 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll
@@ -3,6 +3,12 @@ private import DataFlowPublic
import semmle.python.SpecialMethods
private import semmle.python.essa.SsaCompute
+/** Gets the callable in which this node occurs. */
+DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() }
+
+/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */
+predicate isParameterNode(ParameterNode p, DataFlowCallable c, int pos) { p.isParameterOf(c, pos) }
+
//--------
// Data flow graph
//--------
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll
index d3d9c857f96..dfbb5c5e546 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll
@@ -6,6 +6,12 @@ private import DataFlowDispatch
private import SsaImpl as SsaImpl
private import FlowSummaryImpl as FlowSummaryImpl
+/** Gets the callable in which this node occurs. */
+DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() }
+
+/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */
+predicate isParameterNode(ParameterNode p, DataFlowCallable c, int pos) { p.isParameterOf(c, pos) }
+
abstract class NodeImpl extends Node {
/** Do not call: use `getEnclosingCallable()` instead. */
abstract CfgScope getCfgScope();
From bfacd23573f82438ee253c49ec93766441259251 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Fri, 29 Oct 2021 11:20:19 +0200
Subject: [PATCH 234/471] Dataflow: Adjust documentation.
---
docs/ql-libraries/dataflow/dataflow.md | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/docs/ql-libraries/dataflow/dataflow.md b/docs/ql-libraries/dataflow/dataflow.md
index ea3379f3967..270907dc394 100644
--- a/docs/ql-libraries/dataflow/dataflow.md
+++ b/docs/ql-libraries/dataflow/dataflow.md
@@ -150,14 +150,19 @@ call-graph should be defined as a predicate:
```ql
DataFlowCallable viableCallable(DataFlowCall c)
```
+Furthermore, each `Node` must be associated with exactly one callable and this
+relation should be defined as:
+```ql
+DataFlowCallable nodeGetEnclosingCallable(Node n)
+```
In order to connect data-flow across calls, the 4 `Node` subclasses
`ArgumentNode`, `ParameterNode`, `ReturnNode`, and `OutNode` are used.
Flow into callables from arguments to parameters are matched up using an
-integer position, so these two classes must define:
+integer position, so these two predicates must be defined:
```ql
ArgumentNode::argumentOf(DataFlowCall call, int pos)
-ParameterNode::isParameterOf(DataFlowCallable c, int pos)
+predicate isParameterNode(ParameterNode p, DataFlowCallable c, int pos)
```
It is typical to use `pos = -1` for an implicit `this`-parameter.
From bc91f664aceb1fd2d253e3a6bbc4a5dc7d5949e7 Mon Sep 17 00:00:00 2001
From: Max Schaefer
Date: Fri, 29 Oct 2021 11:19:54 +0100
Subject: [PATCH 235/471] JavaScript: Teach API graphs to handle some forms of
property copying.
In particular, copied promises are now handled better.
---
.../ql/lib/semmle/javascript/ApiGraphs.qll | 79 +++++++++++++------
.../ql/lib/semmle/javascript/Promises.qll | 8 ++
.../ql/test/ApiGraphs/async-await/tst.ts | 9 +++
3 files changed, 73 insertions(+), 23 deletions(-)
create mode 100644 javascript/ql/test/ApiGraphs/async-await/tst.ts
diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll
index 30fb9980214..0e5aca466bc 100644
--- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll
+++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll
@@ -411,7 +411,7 @@ module API {
any(Type t).hasUnderlyingType(moduleName, exportName)
} or
MkSyntheticCallbackArg(DataFlow::Node src, int bound, DataFlow::InvokeNode nd) {
- trackUseNode(src, true, bound).flowsTo(nd.getCalleeNode())
+ trackUseNode(src, true, bound, "").flowsTo(nd.getCalleeNode())
}
class TDef = MkModuleDef or TNonModuleDef;
@@ -528,7 +528,7 @@ module API {
*/
private predicate argumentPassing(TApiNode base, int i, DataFlow::Node arg) {
exists(DataFlow::Node use, DataFlow::SourceNode pred, int bound |
- use(base, use) and pred = trackUseNode(use, _, bound)
+ use(base, use) and pred = trackUseNode(use, _, bound, "")
|
arg = pred.getAnInvocation().getArgument(i - bound)
or
@@ -567,26 +567,37 @@ module API {
base = MkRoot() and
ref = lbl.(EntryPoint).getAUse()
or
- exists(DataFlow::SourceNode src, DataFlow::SourceNode pred |
- use(base, src) and pred = trackUseNode(src)
+ exists(DataFlow::SourceNode src, DataFlow::SourceNode pred, string prop |
+ use(base, src) and pred = trackUseNode(src, false, 0, prop)
|
// `module.exports` is special: it is a use of a def-node, not a use-node,
// so we want to exclude it here
(base instanceof TNonModuleDef or base instanceof TUse) and
lbl = Label::memberFromRef(ref) and
+ (
+ lbl = Label::member(prop)
+ or
+ prop = ""
+ ) and
ref = pred.getAPropertyRead()
or
lbl = Label::instance() and
+ prop = "" and
ref = pred.getAnInstantiation()
or
lbl = Label::return() and
+ prop = "" and
ref = pred.getAnInvocation()
or
- lbl = Label::promised() and
- PromiseFlow::loadStep(pred.getALocalUse(), ref, Promises::valueProp())
- or
- lbl = Label::promisedError() and
- PromiseFlow::loadStep(pred.getALocalUse(), ref, Promises::errorProp())
+ (
+ lbl = Label::promised() and
+ (prop = Promises::valueProp() or prop = "") and
+ PromiseFlow::loadStep(pred.getALocalUse(), ref, Promises::valueProp())
+ or
+ lbl = Label::promisedError() and
+ (prop = Promises::errorProp() or prop = "") and
+ PromiseFlow::loadStep(pred.getALocalUse(), ref, Promises::errorProp())
+ )
)
or
exists(DataFlow::Node def, DataFlow::FunctionNode fn |
@@ -680,36 +691,58 @@ module API {
)
}
+ private import semmle.javascript.dataflow.TypeTracking
+
/**
* Gets a data-flow node to which `nd`, which is a use of an API-graph node, flows.
*
- * The flow from `nd` to that node may be inter-procedural. If `promisified` is `true`, the
- * flow goes through a promisification, and `boundArgs` indicates how many arguments have been
- * bound throughout the flow. (To ensure termination, we somewhat arbitrarily constrain the
- * number of bound arguments to be at most ten.)
+ * The flow from `nd` to that node may be inter-procedural, and is further described by three
+ * flags:
+ *
+ * - `promisified`: if true `true`, the flow goes through a promisification;
+ * - `boundArgs`: for function values, tracks how many arguments have been bound throughout
+ * the flow. To ensure termination, we somewhat arbitrarily constrain the number of bound
+ * arguments to be at most ten.
+ * - `prop`: if non-empty, the flow is only guaranteed to preserve the value of this property,
+ * and not necessarily the entire object.
*/
private DataFlow::SourceNode trackUseNode(
- DataFlow::SourceNode nd, boolean promisified, int boundArgs, DataFlow::TypeTracker t
+ DataFlow::SourceNode nd, boolean promisified, int boundArgs, string prop,
+ DataFlow::TypeTracker t
) {
t.start() and
use(_, nd) and
result = nd and
promisified = false and
- boundArgs = 0
+ boundArgs = 0 and
+ prop = ""
or
exists(Promisify::PromisifyCall promisify |
- trackUseNode(nd, false, boundArgs, t.continue()).flowsTo(promisify.getArgument(0)) and
+ trackUseNode(nd, false, boundArgs, prop, t.continue()).flowsTo(promisify.getArgument(0)) and
promisified = true and
+ prop = "" and
result = promisify
)
or
exists(DataFlow::PartialInvokeNode pin, DataFlow::Node pred, int predBoundArgs |
- trackUseNode(nd, promisified, predBoundArgs, t.continue()).flowsTo(pred) and
+ trackUseNode(nd, promisified, predBoundArgs, prop, t.continue()).flowsTo(pred) and
+ prop = "" and
result = pin.getBoundFunction(pred, boundArgs - predBoundArgs) and
boundArgs in [0 .. 10]
)
or
- t = useStep(nd, promisified, boundArgs, result)
+ exists(DataFlow::Node pred, string preprop |
+ trackUseNode(nd, promisified, boundArgs, preprop, t.continue()).flowsTo(pred) and
+ promisified = false and
+ boundArgs = 0 and
+ SharedTypeTrackingStep::loadStoreStep(pred, result, prop)
+ |
+ prop = preprop
+ or
+ preprop = ""
+ )
+ or
+ t = useStep(nd, promisified, boundArgs, prop, result)
}
private import semmle.javascript.dataflow.internal.StepSummary
@@ -723,19 +756,19 @@ module API {
*/
pragma[noopt]
private DataFlow::TypeTracker useStep(
- DataFlow::Node nd, boolean promisified, int boundArgs, DataFlow::Node res
+ DataFlow::Node nd, boolean promisified, int boundArgs, string prop, DataFlow::Node res
) {
exists(DataFlow::TypeTracker t, StepSummary summary, DataFlow::SourceNode prev |
- prev = trackUseNode(nd, promisified, boundArgs, t) and
+ prev = trackUseNode(nd, promisified, boundArgs, prop, t) and
StepSummary::step(prev, res, summary) and
result = t.append(summary)
)
}
private DataFlow::SourceNode trackUseNode(
- DataFlow::SourceNode nd, boolean promisified, int boundArgs
+ DataFlow::SourceNode nd, boolean promisified, int boundArgs, string prop
) {
- result = trackUseNode(nd, promisified, boundArgs, DataFlow::TypeTracker::end())
+ result = trackUseNode(nd, promisified, boundArgs, prop, DataFlow::TypeTracker::end())
}
/**
@@ -743,7 +776,7 @@ module API {
*/
cached
DataFlow::SourceNode trackUseNode(DataFlow::SourceNode nd) {
- result = trackUseNode(nd, false, 0)
+ result = trackUseNode(nd, false, 0, "")
}
private DataFlow::SourceNode trackDefNode(DataFlow::Node nd, DataFlow::TypeBackTracker t) {
diff --git a/javascript/ql/lib/semmle/javascript/Promises.qll b/javascript/ql/lib/semmle/javascript/Promises.qll
index 887e13c4396..d427763a050 100644
--- a/javascript/ql/lib/semmle/javascript/Promises.qll
+++ b/javascript/ql/lib/semmle/javascript/Promises.qll
@@ -425,6 +425,14 @@ module PromiseFlow {
prop = errorProp() and
pred = call.getCallback(0).getAReturn()
)
+ or
+ // return from `async` function
+ exists(DataFlow::FunctionNode f | f.getFunction().isAsync() |
+ // ordinary return
+ prop = valueProp() and
+ pred = f.getAReturn() and
+ succ = f.getReturnNode()
+ )
}
}
diff --git a/javascript/ql/test/ApiGraphs/async-await/tst.ts b/javascript/ql/test/ApiGraphs/async-await/tst.ts
new file mode 100644
index 00000000000..72c9ba80667
--- /dev/null
+++ b/javascript/ql/test/ApiGraphs/async-await/tst.ts
@@ -0,0 +1,9 @@
+import { readFile } from 'fs/promises';
+
+async function readFileUtf8(path: string): Promise {
+ return readFile(path, { encoding: 'utf8' });
+}
+
+async function test(path: string) {
+ await readFileUtf8(path); /* use (promised (return (member readFile (member exports (module fs/promises))))) */
+}
From abf508eeeb71dbfa8aa8a0c7605c55ef4d9724b7 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Fri, 8 Oct 2021 15:57:09 +0200
Subject: [PATCH 236/471] Java: Add FieldValueNode to break up cartesian step
relation.
---
.../dataflow/internal/DataFlowDispatch.qll | 17 +++----
.../java/dataflow/internal/DataFlowNodes.qll | 46 +++++++++++++------
.../dataflow/internal/DataFlowPrivate.qll | 45 +++++++++++++++---
.../internal/FlowSummaryImplSpecific.qll | 6 +--
.../code/java/dispatch/DispatchFlow.qll | 7 ++-
.../lib/semmle/code/java/dispatch/ObjFlow.qll | 9 ++--
6 files changed, 88 insertions(+), 42 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowDispatch.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowDispatch.qll
index 0e61dc4582f..91b8b391df5 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowDispatch.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowDispatch.qll
@@ -8,9 +8,9 @@ private import semmle.code.java.dispatch.VirtualDispatch as VirtualDispatch
private module DispatchImpl {
/** Gets a viable implementation of the target of the given `Call`. */
DataFlowCallable viableCallable(DataFlowCall c) {
- result = VirtualDispatch::viableCallable(c.asCall())
+ result.asCallable() = VirtualDispatch::viableCallable(c.asCall())
or
- result.(SummarizedCallable) = c.asCall().getCallee().getSourceDeclaration()
+ result.(SummarizedCallable).asCallable() = c.asCall().getCallee().getSourceDeclaration()
}
/**
@@ -93,31 +93,32 @@ private module DispatchImpl {
* qualifier is a parameter of the enclosing callable `c`.
*/
predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable c) {
- mayBenefitFromCallContext(call.asCall(), c, _)
+ mayBenefitFromCallContext(call.asCall(), c.asCallable(), _)
}
/**
* Gets a viable dispatch target of `call` in the context `ctx`. This is
* restricted to those `call`s for which a context might make a difference.
*/
- Method viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
+ DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
result = viableCallable(call) and
exists(int i, Callable c, Method def, RefType t, boolean exact, MethodAccess ma |
ma = call.asCall() and
mayBenefitFromCallContext(ma, c, i) and
- c = viableCallable(ctx) and
+ c = viableCallable(ctx).asCallable() and
contextArgHasType(ctx.asCall(), i, t, exact) and
ma.getMethod().getSourceDeclaration() = def
|
- exact = true and result = VirtualDispatch::exactMethodImpl(def, t.getSourceDeclaration())
+ exact = true and
+ result.asCallable() = VirtualDispatch::exactMethodImpl(def, t.getSourceDeclaration())
or
exact = false and
exists(RefType t2 |
- result = VirtualDispatch::viableMethodImpl(def, t.getSourceDeclaration(), t2) and
+ result.asCallable() = VirtualDispatch::viableMethodImpl(def, t.getSourceDeclaration(), t2) and
not failsUnification(t, t2)
)
or
- result = def and def instanceof SummarizedCallable
+ result.asCallable() = def and result instanceof SummarizedCallable
)
}
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll
index 70f4f38f134..3509b38108e 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll
@@ -14,14 +14,14 @@ newtype TNode =
not e.getParent*() instanceof Annotation
} or
TExplicitParameterNode(Parameter p) {
- exists(p.getCallable().getBody()) or p.getCallable() instanceof SummarizedCallable
+ exists(p.getCallable().getBody()) or p.getCallable() = any(SummarizedCallable sc).asCallable()
} or
TImplicitVarargsArray(Call c) {
c.getCallee().isVarargs() and
not exists(Argument arg | arg.getCall() = c and arg.isExplicitVarargsArray())
} or
TInstanceParameterNode(Callable c) {
- (exists(c.getBody()) or c instanceof SummarizedCallable) and
+ (exists(c.getBody()) or c = any(SummarizedCallable sc).asCallable()) and
not c.isStatic()
} or
TImplicitInstanceAccess(InstanceAccessExt ia) { not ia.isExplicit(_) } or
@@ -44,7 +44,8 @@ newtype TNode =
} or
TSummaryInternalNode(SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state) {
FlowSummaryImpl::Private::summaryNodeRange(c, state)
- }
+ } or
+ TFieldValueNode(Field f)
private predicate explicitInstanceArgument(Call call, Expr instarg) {
call instanceof MethodAccess and
@@ -94,19 +95,12 @@ module Public {
result = this.(MallocNode).getClassInstanceExpr().getType()
or
result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getType()
+ or
+ result = this.(FieldValueNode).getField().getType()
}
/** Gets the callable in which this node occurs. */
- Callable getEnclosingCallable() {
- result = this.asExpr().getEnclosingCallable() or
- result = this.asParameter().getCallable() or
- result = this.(ImplicitVarargsArray).getCall().getEnclosingCallable() or
- result = this.(InstanceParameterNode).getCallable() or
- result = this.(ImplicitInstanceAccess).getInstanceAccess().getEnclosingCallable() or
- result = this.(MallocNode).getClassInstanceExpr().getEnclosingCallable() or
- result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getEnclosingCallable() or
- this = TSummaryInternalNode(result, _)
- }
+ Callable getEnclosingCallable() { result = nodeGetEnclosingCallable(this).asCallable() }
private Type getImprovedTypeBound() {
exprTypeFlow(this.asExpr(), result, _) or
@@ -257,6 +251,18 @@ module Public {
abstract Node getPreUpdateNode();
}
+ /**
+ * A node representing the value of a field.
+ */
+ class FieldValueNode extends Node, TFieldValueNode {
+ /** Gets the field corresponding to this node. */
+ Field getField() { this = TFieldValueNode(result) }
+
+ override string toString() { result = getField().toString() }
+
+ override Location getLocation() { result = getField().getLocation() }
+ }
+
/**
* Gets the node that occurs as the qualifier of `fa`.
*/
@@ -305,11 +311,21 @@ private class ImplicitExprPostUpdate extends ImplicitPostUpdateNode, TImplicitEx
module Private {
/** Gets the callable in which this node occurs. */
- DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() }
+ DataFlowCallable nodeGetEnclosingCallable(Node n) {
+ result.asCallable() = n.asExpr().getEnclosingCallable() or
+ result.asCallable() = n.asParameter().getCallable() or
+ result.asCallable() = n.(ImplicitVarargsArray).getCall().getEnclosingCallable() or
+ result.asCallable() = n.(InstanceParameterNode).getCallable() or
+ result.asCallable() = n.(ImplicitInstanceAccess).getInstanceAccess().getEnclosingCallable() or
+ result.asCallable() = n.(MallocNode).getClassInstanceExpr().getEnclosingCallable() or
+ result = nodeGetEnclosingCallable(n.(ImplicitPostUpdateNode).getPreUpdateNode()) or
+ n = TSummaryInternalNode(result, _) or
+ result.asFieldScope() = n.(FieldValueNode).getField()
+ }
/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */
predicate isParameterNode(ParameterNode p, DataFlowCallable c, int pos) {
- p.isParameterOf(c, pos)
+ p.isParameterOf(c.asCallable(), pos)
}
/**
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll
index d0df4c33cf5..89c53c8854e 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll
@@ -32,12 +32,18 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
/**
* Holds if data can flow from `node1` to `node2` through a static field.
*/
-private predicate staticFieldStep(ExprNode node1, ExprNode node2) {
+private predicate staticFieldStep(Node node1, Node node2) {
+ exists(Field f |
+ f.isStatic() and
+ f.getAnAssignedValue() = node1.asExpr() and
+ node2.(FieldValueNode).getField() = f
+ )
+ or
exists(Field f, FieldRead fr |
f.isStatic() and
- f.getAnAssignedValue() = node1.getExpr() and
+ node1.(FieldValueNode).getField() = f and
fr.getField() = f and
- fr = node2.getExpr() and
+ fr = node2.asExpr() and
hasNonlocalValue(fr)
)
}
@@ -205,7 +211,30 @@ class CastNode extends ExprNode {
CastNode() { this.getExpr() instanceof CastExpr }
}
-class DataFlowCallable = Callable;
+private newtype TDataFlowCallable =
+ TCallable(Callable c) or
+ TFieldScope(Field f)
+
+class DataFlowCallable extends TDataFlowCallable {
+ Callable asCallable() { this = TCallable(result) }
+
+ Field asFieldScope() { this = TFieldScope(result) }
+
+ RefType getDeclaringType() {
+ result = asCallable().getDeclaringType() or
+ result = asFieldScope().getDeclaringType()
+ }
+
+ string toString() {
+ result = asCallable().toString() or
+ result = "Field scope: " + asFieldScope().toString()
+ }
+
+ Location getLocation() {
+ result = asCallable().getLocation() or
+ result = asFieldScope().getLocation()
+ }
+}
class DataFlowExpr = Expr;
@@ -251,7 +280,9 @@ class SrcCall extends DataFlowCall, TCall {
SrcCall() { this = TCall(call) }
- override DataFlowCallable getEnclosingCallable() { result = call.getEnclosingCallable() }
+ override DataFlowCallable getEnclosingCallable() {
+ result.asCallable() = call.getEnclosingCallable()
+ }
override string toString() { result = call.toString() }
@@ -345,10 +376,10 @@ class LambdaCallKind = Method; // the "apply" method in the functional interface
predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) {
exists(ClassInstanceExpr func, Interface t, FunctionalInterface interface |
creation.asExpr() = func and
- func.getAnonymousClass().getAMethod() = c and
+ func.getAnonymousClass().getAMethod() = c.asCallable() and
func.getConstructedType().extendsOrImplements+(t) and
t.getSourceDeclaration() = interface and
- c.(Method).overridesOrInstantiates+(pragma[only_bind_into](kind)) and
+ c.asCallable().(Method).overridesOrInstantiates+(pragma[only_bind_into](kind)) and
pragma[only_bind_into](kind) = interface.getRunMethod().getSourceDeclaration()
)
}
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll b/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll
index e4c3091f05e..a3d5d64a766 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll
@@ -30,7 +30,7 @@ DataFlowType getContentType(Content c) { result = c.getType() }
/** Gets the return type of kind `rk` for callable `c`. */
DataFlowType getReturnType(SummarizedCallable c, ReturnKind rk) {
- result = getErasedRepr(c.getReturnType()) and
+ result = getErasedRepr(c.asCallable().getReturnType()) and
exists(rk)
}
@@ -62,7 +62,7 @@ predicate summaryElement(DataFlowCallable c, string input, string output, string
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind) and
- c = interpretElement(namespace, type, subtypes, name, signature, ext)
+ c.asCallable() = interpretElement(namespace, type, subtypes, name, signature, ext)
)
}
@@ -119,7 +119,7 @@ class InterpretNode extends TInterpretNode {
DataFlowCall asCall() { result.asCall() = this.asElement() }
/** Gets the callable that this node corresponds to, if any. */
- DataFlowCallable asCallable() { result = this.asElement() }
+ DataFlowCallable asCallable() { result.asCallable() = this.asElement() }
/** Gets the target of this call, if any. */
Callable getCallTarget() { result = this.asCall().asCall().getCallee().getSourceDeclaration() }
diff --git a/java/ql/lib/semmle/code/java/dispatch/DispatchFlow.qll b/java/ql/lib/semmle/code/java/dispatch/DispatchFlow.qll
index bbb516d3bf0..1b13e6ef4c0 100644
--- a/java/ql/lib/semmle/code/java/dispatch/DispatchFlow.qll
+++ b/java/ql/lib/semmle/code/java/dispatch/DispatchFlow.qll
@@ -189,10 +189,9 @@ private predicate flowStep(RelevantNode n1, RelevantNode n2) {
n2.(ImplicitInstanceAccess).getInstanceAccess().(OwnInstanceAccess).getEnclosingCallable() = c
)
or
- exists(Field f |
- f.getAnAssignedValue() = n1.asExpr() and
- n2.asExpr().(FieldRead).getField() = f
- )
+ n2.(FieldValueNode).getField().getAnAssignedValue() = n1.asExpr()
+ or
+ n2.asExpr().(FieldRead).getField() = n1.(FieldValueNode).getField()
or
exists(EnumType enum, Method getValue |
enum.getAnEnumConstant().getAnAssignedValue() = n1.asExpr() and
diff --git a/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll b/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll
index 045ce76e9af..005b54a9259 100644
--- a/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll
+++ b/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll
@@ -94,10 +94,9 @@ private predicate step(Node n1, Node n2) {
n2.(ImplicitInstanceAccess).getInstanceAccess().(OwnInstanceAccess).getEnclosingCallable() = c
)
or
- exists(Field f |
- f.getAnAssignedValue() = n1.asExpr() and
- n2.asExpr().(FieldRead).getField() = f
- )
+ n2.(FieldValueNode).getField().getAnAssignedValue() = n1.asExpr()
+ or
+ n2.asExpr().(FieldRead).getField() = n1.(FieldValueNode).getField()
or
n2.asExpr().(CastExpr).getExpr() = n1.asExpr()
or
@@ -132,7 +131,7 @@ private predicate step(Node n1, Node n2) {
or
exists(Field v |
containerStep(n1.asExpr(), v.getAnAccess()) and
- n2.asExpr() = v.getAnAccess()
+ n2.(FieldValueNode).getField() = v
)
}
From 7e7c363e433842cb48241bd0501224187b6922de Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Fri, 29 Oct 2021 13:59:36 +0200
Subject: [PATCH 237/471] Python: Apply suggestions from code review
Co-authored-by: yoff
---
python/ql/lib/semmle/python/frameworks/FastApi.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/lib/semmle/python/frameworks/FastApi.qll b/python/ql/lib/semmle/python/frameworks/FastApi.qll
index a6d9e52efad..7e396bf9014 100644
--- a/python/ql/lib/semmle/python/frameworks/FastApi.qll
+++ b/python/ql/lib/semmle/python/frameworks/FastApi.qll
@@ -117,7 +117,7 @@ private module FastApi {
*/
private API::Node getModeledResponseClass(string name) {
name = "Response" and
- result = API::moduleImport("fastapi").getMember("Response")
+ result = API::moduleImport("fastapi").getMember(name)
or
// see https://github.com/tiangolo/fastapi/blob/master/fastapi/responses.py
name in [
From afa6424d676d0953a87680c91a368c61483f7aeb Mon Sep 17 00:00:00 2001
From: Asger Feldthaus
Date: Fri, 29 Oct 2021 14:15:24 +0200
Subject: [PATCH 238/471] JS: Add test with FP
---
.../MixedStaticInstanceThisAccess.expected | 1 +
.../MixedStaticInstanceThisAccess/abstract.ts | 8 ++++++++
2 files changed, 9 insertions(+)
create mode 100644 javascript/ql/test/query-tests/Declarations/MixedStaticInstanceThisAccess/abstract.ts
diff --git a/javascript/ql/test/query-tests/Declarations/MixedStaticInstanceThisAccess/MixedStaticInstanceThisAccess.expected b/javascript/ql/test/query-tests/Declarations/MixedStaticInstanceThisAccess/MixedStaticInstanceThisAccess.expected
index 1e5f4d64324..d981de022ea 100644
--- a/javascript/ql/test/query-tests/Declarations/MixedStaticInstanceThisAccess/MixedStaticInstanceThisAccess.expected
+++ b/javascript/ql/test/query-tests/Declarations/MixedStaticInstanceThisAccess/MixedStaticInstanceThisAccess.expected
@@ -1,3 +1,4 @@
+| abstract.ts:6:9:6:17 | this.test | Access to static method $@ from instance method $@ is not possible through `this`. | abstract.ts:3:5:3:20 | static test() {} | test | abstract.ts:5:5:7:5 | method( ... K\\n } | method |
| instanceStatic.js:3:9:3:16 | this.baz | Access to instance method $@ from static method $@ is not possible through `this`. | instanceStatic.js:5:5:7:5 | baz(){\\n\\n } | baz | instanceStatic.js:2:5:4:5 | static ... K\\n } | bar |
| staticInstance.js:3:9:3:16 | this.baz | Access to static method $@ from instance method $@ is not possible through `this`. | staticInstance.js:5:5:6:5 | static baz(){\\n } | baz | staticInstance.js:2:5:4:5 | bar(){\\n ... K\\n } | bar |
| tst.js:66:9:66:14 | this.f | Access to instance method $@ from static method $@ is not possible through `this`. | tst.js:60:5:62:5 | f() {\\n\\n } | f | tst.js:65:5:67:5 | static ... K\\n } | test |
diff --git a/javascript/ql/test/query-tests/Declarations/MixedStaticInstanceThisAccess/abstract.ts b/javascript/ql/test/query-tests/Declarations/MixedStaticInstanceThisAccess/abstract.ts
new file mode 100644
index 00000000000..8a22bec0454
--- /dev/null
+++ b/javascript/ql/test/query-tests/Declarations/MixedStaticInstanceThisAccess/abstract.ts
@@ -0,0 +1,8 @@
+abstract class Q {
+ abstract test();
+ static test() {}
+
+ method() {
+ this.test(); // OK
+ }
+}
From d52b2bd863ad6ddfe802575d077b5ec19ba161ad Mon Sep 17 00:00:00 2001
From: Asger Feldthaus
Date: Fri, 29 Oct 2021 14:08:05 +0200
Subject: [PATCH 239/471] =?UTF-8?q?JS:=20Fix=20FP=20in=20=CB=9AMixedStatic?=
=?UTF-8?q?InstanceThisAccess?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../ql/src/Declarations/MixedStaticInstanceThisAccess.ql | 6 +++---
.../MixedStaticInstanceThisAccess.expected | 1 -
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/javascript/ql/src/Declarations/MixedStaticInstanceThisAccess.ql b/javascript/ql/src/Declarations/MixedStaticInstanceThisAccess.ql
index c41b514737a..a8f771706f1 100644
--- a/javascript/ql/src/Declarations/MixedStaticInstanceThisAccess.ql
+++ b/javascript/ql/src/Declarations/MixedStaticInstanceThisAccess.ql
@@ -12,7 +12,7 @@
import javascript
/** Holds if `base` declares or inherits method `m` with the given `name`. */
-predicate hasMethod(ClassDefinition base, string name, MethodDefinition m) {
+predicate hasMethod(ClassDefinition base, string name, MethodDeclaration m) {
m = base.getMethod(name) or
hasMethod(base.getSuperClassDefinition(), name, m)
}
@@ -22,7 +22,7 @@ predicate hasMethod(ClassDefinition base, string name, MethodDefinition m) {
* where `fromMethod` and `toMethod` are of kind `fromKind` and `toKind`, respectively.
*/
predicate isLocalMethodAccess(
- PropAccess access, MethodDefinition fromMethod, string fromKind, MethodDefinition toMethod,
+ PropAccess access, MethodDefinition fromMethod, string fromKind, MethodDeclaration toMethod,
string toKind
) {
hasMethod(fromMethod.getDeclaringClass(), access.getPropertyName(), toMethod) and
@@ -32,7 +32,7 @@ predicate isLocalMethodAccess(
toKind = getKind(toMethod)
}
-string getKind(MethodDefinition m) {
+string getKind(MethodDeclaration m) {
if m.isStatic() then result = "static" else result = "instance"
}
diff --git a/javascript/ql/test/query-tests/Declarations/MixedStaticInstanceThisAccess/MixedStaticInstanceThisAccess.expected b/javascript/ql/test/query-tests/Declarations/MixedStaticInstanceThisAccess/MixedStaticInstanceThisAccess.expected
index d981de022ea..1e5f4d64324 100644
--- a/javascript/ql/test/query-tests/Declarations/MixedStaticInstanceThisAccess/MixedStaticInstanceThisAccess.expected
+++ b/javascript/ql/test/query-tests/Declarations/MixedStaticInstanceThisAccess/MixedStaticInstanceThisAccess.expected
@@ -1,4 +1,3 @@
-| abstract.ts:6:9:6:17 | this.test | Access to static method $@ from instance method $@ is not possible through `this`. | abstract.ts:3:5:3:20 | static test() {} | test | abstract.ts:5:5:7:5 | method( ... K\\n } | method |
| instanceStatic.js:3:9:3:16 | this.baz | Access to instance method $@ from static method $@ is not possible through `this`. | instanceStatic.js:5:5:7:5 | baz(){\\n\\n } | baz | instanceStatic.js:2:5:4:5 | static ... K\\n } | bar |
| staticInstance.js:3:9:3:16 | this.baz | Access to static method $@ from instance method $@ is not possible through `this`. | staticInstance.js:5:5:6:5 | static baz(){\\n } | baz | staticInstance.js:2:5:4:5 | bar(){\\n ... K\\n } | bar |
| tst.js:66:9:66:14 | this.f | Access to instance method $@ from static method $@ is not possible through `this`. | tst.js:60:5:62:5 | f() {\\n\\n } | f | tst.js:65:5:67:5 | static ... K\\n } | test |
From e51a10a8164591eb0babf22ea02a2ab3eb6a0695 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Fri, 29 Oct 2021 14:25:43 +0200
Subject: [PATCH 240/471] Java: Fix tests.
---
.../security/CWE-297/UnsafeHostnameVerification.expected | 4 +++-
.../semmle/tests/HardcodedCredentialsApiCall.expected | 6 ++++--
.../semmle/tests/HardcodedCredentialsSourceCall.expected | 4 +++-
3 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/java/ql/test/query-tests/security/CWE-297/UnsafeHostnameVerification.expected b/java/ql/test/query-tests/security/CWE-297/UnsafeHostnameVerification.expected
index 405b02b4877..13a14d69756 100644
--- a/java/ql/test/query-tests/security/CWE-297/UnsafeHostnameVerification.expected
+++ b/java/ql/test/query-tests/security/CWE-297/UnsafeHostnameVerification.expected
@@ -1,7 +1,8 @@
edges
| UnsafeHostnameVerification.java:66:37:80:9 | new (...) : new HostnameVerifier(...) { ... } | UnsafeHostnameVerification.java:81:55:81:62 | verifier |
| UnsafeHostnameVerification.java:88:37:93:9 | new (...) : new HostnameVerifier(...) { ... } | UnsafeHostnameVerification.java:94:55:94:62 | verifier |
-| UnsafeHostnameVerification.java:97:72:102:5 | new (...) : new HostnameVerifier(...) { ... } | UnsafeHostnameVerification.java:34:59:34:85 | ALLOW_ALL_HOSTNAME_VERIFIER |
+| UnsafeHostnameVerification.java:97:42:97:68 | ALLOW_ALL_HOSTNAME_VERIFIER : new HostnameVerifier(...) { ... } | UnsafeHostnameVerification.java:34:59:34:85 | ALLOW_ALL_HOSTNAME_VERIFIER |
+| UnsafeHostnameVerification.java:97:72:102:5 | new (...) : new HostnameVerifier(...) { ... } | UnsafeHostnameVerification.java:97:42:97:68 | ALLOW_ALL_HOSTNAME_VERIFIER : new HostnameVerifier(...) { ... } |
nodes
| UnsafeHostnameVerification.java:14:55:19:9 | new (...) | semmle.label | new (...) |
| UnsafeHostnameVerification.java:26:55:26:71 | ...->... | semmle.label | ...->... |
@@ -12,6 +13,7 @@ nodes
| UnsafeHostnameVerification.java:81:55:81:62 | verifier | semmle.label | verifier |
| UnsafeHostnameVerification.java:88:37:93:9 | new (...) : new HostnameVerifier(...) { ... } | semmle.label | new (...) : new HostnameVerifier(...) { ... } |
| UnsafeHostnameVerification.java:94:55:94:62 | verifier | semmle.label | verifier |
+| UnsafeHostnameVerification.java:97:42:97:68 | ALLOW_ALL_HOSTNAME_VERIFIER : new HostnameVerifier(...) { ... } | semmle.label | ALLOW_ALL_HOSTNAME_VERIFIER : new HostnameVerifier(...) { ... } |
| UnsafeHostnameVerification.java:97:72:102:5 | new (...) : new HostnameVerifier(...) { ... } | semmle.label | new (...) : new HostnameVerifier(...) { ... } |
subpaths
#select
diff --git a/java/ql/test/query-tests/security/CWE-798/semmle/tests/HardcodedCredentialsApiCall.expected b/java/ql/test/query-tests/security/CWE-798/semmle/tests/HardcodedCredentialsApiCall.expected
index 33b8879d9e5..96cdab78eb8 100644
--- a/java/ql/test/query-tests/security/CWE-798/semmle/tests/HardcodedCredentialsApiCall.expected
+++ b/java/ql/test/query-tests/security/CWE-798/semmle/tests/HardcodedCredentialsApiCall.expected
@@ -1,6 +1,7 @@
edges
-| CredentialsTest.java:7:34:7:41 | "123456" : String | CredentialsTest.java:13:39:13:39 | p |
-| CredentialsTest.java:7:34:7:41 | "123456" : String | CredentialsTest.java:14:16:14:16 | p : String |
+| CredentialsTest.java:7:30:7:30 | p : String | CredentialsTest.java:13:39:13:39 | p |
+| CredentialsTest.java:7:30:7:30 | p : String | CredentialsTest.java:14:16:14:16 | p : String |
+| CredentialsTest.java:7:34:7:41 | "123456" : String | CredentialsTest.java:7:30:7:30 | p : String |
| CredentialsTest.java:11:14:11:20 | "admin" : String | CredentialsTest.java:13:36:13:36 | u |
| CredentialsTest.java:11:14:11:20 | "admin" : String | CredentialsTest.java:14:13:14:13 | u : String |
| CredentialsTest.java:14:13:14:13 | u : String | CredentialsTest.java:17:38:17:45 | v : String |
@@ -44,6 +45,7 @@ edges
| Test.java:29:38:29:48 | user : String | Test.java:30:36:30:39 | user |
| Test.java:29:51:29:65 | password : String | Test.java:30:42:30:49 | password |
nodes
+| CredentialsTest.java:7:30:7:30 | p : String | semmle.label | p : String |
| CredentialsTest.java:7:34:7:41 | "123456" : String | semmle.label | "123456" : String |
| CredentialsTest.java:11:14:11:20 | "admin" : String | semmle.label | "admin" : String |
| CredentialsTest.java:13:36:13:36 | u | semmle.label | u |
diff --git a/java/ql/test/query-tests/security/CWE-798/semmle/tests/HardcodedCredentialsSourceCall.expected b/java/ql/test/query-tests/security/CWE-798/semmle/tests/HardcodedCredentialsSourceCall.expected
index a960464f0d9..bf73fdaa93d 100644
--- a/java/ql/test/query-tests/security/CWE-798/semmle/tests/HardcodedCredentialsSourceCall.expected
+++ b/java/ql/test/query-tests/security/CWE-798/semmle/tests/HardcodedCredentialsSourceCall.expected
@@ -12,7 +12,8 @@ edges
| HardcodedAzureCredentials.java:61:3:61:33 | new HardcodedAzureCredentials(...) [clientSecret] : String | HardcodedAzureCredentials.java:15:14:15:42 | parameter this [clientSecret] : String |
| HardcodedAzureCredentials.java:61:3:61:33 | new HardcodedAzureCredentials(...) [username] : String | HardcodedAzureCredentials.java:15:14:15:42 | parameter this [username] : String |
| Test.java:10:17:10:24 | "123456" : String | Test.java:26:17:26:20 | pass |
-| User.java:2:43:2:50 | "123456" : String | User.java:5:15:5:24 | DEFAULT_PW |
+| User.java:2:30:2:39 | DEFAULT_PW : String | User.java:5:15:5:24 | DEFAULT_PW |
+| User.java:2:43:2:50 | "123456" : String | User.java:2:30:2:39 | DEFAULT_PW : String |
nodes
| HardcodedAzureCredentials.java:8:14:8:38 | this <.method> [post update] [clientSecret] : String | semmle.label | this <.method> [post update] [clientSecret] : String |
| HardcodedAzureCredentials.java:8:14:8:38 | this <.method> [post update] [username] : String | semmle.label | this <.method> [post update] [username] : String |
@@ -30,6 +31,7 @@ nodes
| HardcodedAzureCredentials.java:61:3:61:33 | new HardcodedAzureCredentials(...) [username] : String | semmle.label | new HardcodedAzureCredentials(...) [username] : String |
| Test.java:10:17:10:24 | "123456" : String | semmle.label | "123456" : String |
| Test.java:26:17:26:20 | pass | semmle.label | pass |
+| User.java:2:30:2:39 | DEFAULT_PW : String | semmle.label | DEFAULT_PW : String |
| User.java:2:43:2:50 | "123456" : String | semmle.label | "123456" : String |
| User.java:5:15:5:24 | DEFAULT_PW | semmle.label | DEFAULT_PW |
subpaths
From 35b6cbe54942effa43c53e5d6ec00bd204a58ed6 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Fri, 29 Oct 2021 14:26:36 +0200
Subject: [PATCH 241/471] Java: Fix compilation error.
---
java/ql/src/Telemetry/ExternalAPI.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/Telemetry/ExternalAPI.qll b/java/ql/src/Telemetry/ExternalAPI.qll
index 76ed5734f3f..5f41ff14d3a 100644
--- a/java/ql/src/Telemetry/ExternalAPI.qll
+++ b/java/ql/src/Telemetry/ExternalAPI.qll
@@ -62,7 +62,7 @@ class ExternalAPI extends Callable {
/** Holds if this API has a supported summary. */
predicate hasSummary() {
- this instanceof SummarizedCallable or
+ this = any(SummarizedCallable sc).asCallable() or
TaintTracking::localAdditionalTaintStep(this.getAnInput(), _)
}
From 97300216410daf52b08a6a0b9270dd5b742f8c98 Mon Sep 17 00:00:00 2001
From: Marcono1234
Date: Fri, 29 Oct 2021 14:23:07 +0200
Subject: [PATCH 242/471] Java: Add `CharacterLiteral.getCodePointValue()`
---
java/ql/lib/semmle/code/java/Expr.qll | 20 ++++++-
java/ql/lib/semmle/code/java/Type.qll | 5 +-
.../constants/constants/Values.java | 4 +-
.../constants/getIntValue.expected | 2 +
.../literals/charLiterals/CharLiterals.java | 1 +
.../charLiterals/charLiterals.expected | 40 ++++++-------
.../literals/charLiterals/charLiterals.ql | 2 +-
.../stringLiterals/StringLiterals.java | 1 +
.../stringLiterals/stringLiterals.expected | 56 +++++++++----------
9 files changed, 76 insertions(+), 55 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/Expr.qll b/java/ql/lib/semmle/code/java/Expr.qll
index f2a309904ad..19f41772ff6 100755
--- a/java/ql/lib/semmle/code/java/Expr.qll
+++ b/java/ql/lib/semmle/code/java/Expr.qll
@@ -298,19 +298,20 @@ class CompileTimeConstantExpr extends Expr {
*
* Note that this does not handle the following cases:
*
- * - values of type `long`,
- * - `char` literals.
+ * - values of type `long`.
*/
cached
int getIntValue() {
exists(IntegralType t | this.getType() = t | t.getName().toLowerCase() != "long") and
(
exists(string lit | lit = this.(Literal).getValue() |
- // `char` literals may get parsed incorrectly, so disallow.
+ // Don't parse `char` literal as int, instead get its code point value (see below)
not this instanceof CharacterLiteral and
result = lit.toInt()
)
or
+ result = this.(CharacterLiteral).getCodePointValue()
+ or
exists(CastExpr cast, int val |
cast = this and val = cast.getExpr().(CompileTimeConstantExpr).getIntValue()
|
@@ -719,6 +720,19 @@ class DoubleLiteral extends Literal, @doubleliteral {
/** A character literal. For example, `'\n'`. */
class CharacterLiteral extends Literal, @characterliteral {
override string getAPrimaryQlClass() { result = "CharacterLiteral" }
+
+ /**
+ * Gets a string which consists of the single character represented by
+ * this literal.
+ */
+ override string getValue() { result = super.getValue() }
+
+ /**
+ * Gets the Unicode code point value of the character represented by
+ * this literal. The result is the same as if the Java code had cast
+ * the character to an `int`.
+ */
+ int getCodePointValue() { result = any(int i | i.toUnicode() = getValue()) }
}
/**
diff --git a/java/ql/lib/semmle/code/java/Type.qll b/java/ql/lib/semmle/code/java/Type.qll
index 1d0658595b2..7ceec7483d1 100755
--- a/java/ql/lib/semmle/code/java/Type.qll
+++ b/java/ql/lib/semmle/code/java/Type.qll
@@ -1123,7 +1123,10 @@ predicate erasedHaveIntersection(RefType t1, RefType t2) {
t2 = erase(_)
}
-/** An integral type, which may be either a primitive or a boxed type. */
+/**
+ * An integral type, which may be either a primitive or a boxed type.
+ * This includes the types `char` and `Character`.
+ */
class IntegralType extends Type {
IntegralType() {
exists(string name |
diff --git a/java/ql/test/library-tests/constants/constants/Values.java b/java/ql/test/library-tests/constants/constants/Values.java
index ad061e96c77..7cf88fb9ad8 100644
--- a/java/ql/test/library-tests/constants/constants/Values.java
+++ b/java/ql/test/library-tests/constants/constants/Values.java
@@ -16,7 +16,7 @@ class Values {
int binary_literal = 0b101010; //42
int negative_binary_literal = -0b101010; //-42
int binary_literal_underscores = 0b1_0101_0; //42
- char char_literal = '*'; //Not handled
+ char char_literal = '*'; //42
long long_literal = 42L; //Not handled
boolean boolean_literal = true; //true
Integer boxed_int = new Integer(42); //Not handled
@@ -30,7 +30,7 @@ class Values {
byte downcast_byte_4 = (byte) 214; // -42
byte downcast_byte_5 = (byte) (-214); // 42
short downcast_short = (short) 32768; // -32768
- int cast_of_non_constant = (int) '*'; //Not handled
+ int cast_of_non_constant = (int) '*'; //42
long cast_to_long = (long) 42; //Not handled
int unary_plus = +42; //42
diff --git a/java/ql/test/library-tests/constants/getIntValue.expected b/java/ql/test/library-tests/constants/getIntValue.expected
index bee36f5122b..8dfd0fc7841 100644
--- a/java/ql/test/library-tests/constants/getIntValue.expected
+++ b/java/ql/test/library-tests/constants/getIntValue.expected
@@ -9,6 +9,7 @@
| constants/Values.java:16:30:16:37 | 0b101010 | 42 |
| constants/Values.java:17:39:17:47 | -... | -42 |
| constants/Values.java:18:42:18:51 | 0b1_0101_0 | 42 |
+| constants/Values.java:19:29:19:31 | '*' | 42 |
| constants/Values.java:25:20:25:27 | (...)... | 42 |
| constants/Values.java:26:25:26:33 | (...)... | 42 |
| constants/Values.java:27:32:27:43 | (...)... | -42 |
@@ -17,6 +18,7 @@
| constants/Values.java:30:32:30:41 | (...)... | -42 |
| constants/Values.java:31:32:31:44 | (...)... | 42 |
| constants/Values.java:32:32:32:44 | (...)... | -32768 |
+| constants/Values.java:33:36:33:44 | (...)... | 42 |
| constants/Values.java:36:26:36:28 | +... | 42 |
| constants/Values.java:39:27:39:29 | -... | -42 |
| constants/Values.java:43:27:43:28 | ~... | -1 |
diff --git a/java/ql/test/library-tests/literals/charLiterals/CharLiterals.java b/java/ql/test/library-tests/literals/charLiterals/CharLiterals.java
index 51274f9899e..8640a50b06c 100644
--- a/java/ql/test/library-tests/literals/charLiterals/CharLiterals.java
+++ b/java/ql/test/library-tests/literals/charLiterals/CharLiterals.java
@@ -13,6 +13,7 @@ public class CharLiterals {
'\\',
'\'',
'\123', // octal escape sequence for 'S'
+ // CodeQL uses U+FFFD for unpaired surrogates, see https://github.com/github/codeql/issues/6611
'\uD800', // high surrogate
'\uDC00', // low surrogate
// Using Unicode escapes (which are handled during pre-processing)
diff --git a/java/ql/test/library-tests/literals/charLiterals/charLiterals.expected b/java/ql/test/library-tests/literals/charLiterals/charLiterals.expected
index 987f7c17fe4..28fae87a636 100644
--- a/java/ql/test/library-tests/literals/charLiterals/charLiterals.expected
+++ b/java/ql/test/library-tests/literals/charLiterals/charLiterals.expected
@@ -1,20 +1,20 @@
-| CharLiterals.java:5:3:5:5 | 'a' | a |
-| CharLiterals.java:6:3:6:10 | '\\u0061' | a |
-| CharLiterals.java:7:3:7:10 | '\\u0000' | \u0000 |
-| CharLiterals.java:8:3:8:10 | '\\uFFFF' | \uffff |
-| CharLiterals.java:9:3:9:10 | '\\ufFfF' | \uffff |
-| CharLiterals.java:10:3:10:6 | '\\0' | \u0000 |
-| CharLiterals.java:11:3:11:6 | '\\n' | \n |
-| CharLiterals.java:12:3:12:5 | '"' | " |
-| CharLiterals.java:13:3:13:6 | '\\\\' | \\ |
-| CharLiterals.java:14:3:14:6 | '\\'' | ' |
-| CharLiterals.java:15:3:15:8 | '\\123' | S |
-| CharLiterals.java:16:3:16:10 | '\\uD800' | \ufffd |
-| CharLiterals.java:17:3:17:10 | '\\uDC00' | \ufffd |
-| CharLiterals.java:19:3:19:16 | '\\u005C\\u005C' | \\ |
-| CharLiterals.java:20:3:20:16 | '\\u005C\\u0027' | ' |
-| CharLiterals.java:21:8:21:15 | 7a\\u0027 | a |
-| CharLiterals.java:26:4:26:6 | 'a' | a |
-| CharLiterals.java:27:4:27:6 | 'a' | a |
-| CharLiterals.java:32:3:32:5 | 'a' | a |
-| CharLiterals.java:32:9:32:11 | 'b' | b |
+| CharLiterals.java:5:3:5:5 | 'a' | a | 97 |
+| CharLiterals.java:6:3:6:10 | '\\u0061' | a | 97 |
+| CharLiterals.java:7:3:7:10 | '\\u0000' | \u0000 | 0 |
+| CharLiterals.java:8:3:8:10 | '\\uFFFF' | \uffff | 65535 |
+| CharLiterals.java:9:3:9:10 | '\\ufFfF' | \uffff | 65535 |
+| CharLiterals.java:10:3:10:6 | '\\0' | \u0000 | 0 |
+| CharLiterals.java:11:3:11:6 | '\\n' | \n | 10 |
+| CharLiterals.java:12:3:12:5 | '"' | " | 34 |
+| CharLiterals.java:13:3:13:6 | '\\\\' | \\ | 92 |
+| CharLiterals.java:14:3:14:6 | '\\'' | ' | 39 |
+| CharLiterals.java:15:3:15:8 | '\\123' | S | 83 |
+| CharLiterals.java:17:3:17:10 | '\\uD800' | \ufffd | 65533 |
+| CharLiterals.java:18:3:18:10 | '\\uDC00' | \ufffd | 65533 |
+| CharLiterals.java:20:3:20:16 | '\\u005C\\u005C' | \\ | 92 |
+| CharLiterals.java:21:3:21:16 | '\\u005C\\u0027' | ' | 39 |
+| CharLiterals.java:22:8:22:15 | 7a\\u0027 | a | 97 |
+| CharLiterals.java:27:4:27:6 | 'a' | a | 97 |
+| CharLiterals.java:28:4:28:6 | 'a' | a | 97 |
+| CharLiterals.java:33:3:33:5 | 'a' | a | 97 |
+| CharLiterals.java:33:9:33:11 | 'b' | b | 98 |
diff --git a/java/ql/test/library-tests/literals/charLiterals/charLiterals.ql b/java/ql/test/library-tests/literals/charLiterals/charLiterals.ql
index 104e2c03dc4..9a374e64457 100644
--- a/java/ql/test/library-tests/literals/charLiterals/charLiterals.ql
+++ b/java/ql/test/library-tests/literals/charLiterals/charLiterals.ql
@@ -1,4 +1,4 @@
import semmle.code.java.Expr
from CharacterLiteral lit
-select lit, lit.getValue()
+select lit, lit.getValue(), lit.getCodePointValue()
diff --git a/java/ql/test/library-tests/literals/stringLiterals/StringLiterals.java b/java/ql/test/library-tests/literals/stringLiterals/StringLiterals.java
index 9c0c55b12e5..f40f57361f3 100644
--- a/java/ql/test/library-tests/literals/stringLiterals/StringLiterals.java
+++ b/java/ql/test/library-tests/literals/stringLiterals/StringLiterals.java
@@ -24,6 +24,7 @@ public class StringLiterals {
"\uD800\uDC00", // surrogate pair
"\uDBFF\uDFFF", // U+10FFFF
// Unpaired surrogates
+ // CodeQL uses U+FFFD for them, see https://github.com/github/codeql/issues/6611
"\uD800",
"\uDC00",
"hello\uD800hello\uDC00world", // malformed surrogates
diff --git a/java/ql/test/library-tests/literals/stringLiterals/stringLiterals.expected b/java/ql/test/library-tests/literals/stringLiterals/stringLiterals.expected
index bf91a56e723..e08d7778e69 100644
--- a/java/ql/test/library-tests/literals/stringLiterals/stringLiterals.expected
+++ b/java/ql/test/library-tests/literals/stringLiterals/stringLiterals.expected
@@ -17,32 +17,32 @@
| StringLiterals.java:23:3:23:18 | "\\uaBcDeF\\u0aB1" | \uabcdeF\u0ab1 | \uabcdeF\u0ab1 | |
| StringLiterals.java:24:3:24:16 | "\\uD800\\uDC00" | \ud800\udc00 | \ud800\udc00 | |
| StringLiterals.java:25:3:25:16 | "\\uDBFF\\uDFFF" | \udbff\udfff | \udbff\udfff | |
-| StringLiterals.java:27:3:27:10 | "\\uD800" | \ufffd | \ufffd | |
-| StringLiterals.java:28:3:28:10 | "\\uDC00" | \ufffd | \ufffd | |
-| StringLiterals.java:29:3:29:31 | "hello\\uD800hello\\uDC00world" | hello\ufffdhello\ufffdworld | hello\ufffdhello\ufffdworld | |
-| StringLiterals.java:31:3:31:16 | "\\u005C\\u0022" | " | " | |
-| StringLiterals.java:32:8:32:20 | 2\\u0061\\u0022 | a | a | |
-| StringLiterals.java:37:3:39:5 | """ \t \n\t\ttest "text" and escaped \\u0022\n\t\t""" | test "text" and escaped "\n | test "text" and escaped "\n | text-block |
-| StringLiterals.java:41:3:43:5 | """\n\t\t\tindented\n\t\t""" | \tindented\n | \tindented\n | text-block |
-| StringLiterals.java:44:3:46:5 | """\n\tno indentation last line\n\t\t""" | no indentation last line\n | no indentation last line\n | text-block |
-| StringLiterals.java:47:3:49:7 | """\n\tindentation last line\n\t\t\\s""" | indentation last line\n\t | indentation last line\n\t | text-block |
-| StringLiterals.java:50:3:52:6 | """\n\t\t\tnot-indented\n\t\t\t""" | not-indented\n | not-indented\n | text-block |
-| StringLiterals.java:53:3:55:4 | """\n\t\tindented\n\t""" | \tindented\n | \tindented\n | text-block |
-| StringLiterals.java:56:4:58:5 | """\n\t\tnot-indented\n\t\t""" | not-indented\n | not-indented\n | text-block |
-| StringLiterals.java:59:3:62:6 | """\n\t\t spaces (only single space is trimmed)\n\t\t\ttab\n\t\t\t""" | spaces (only single space is trimmed)\ntab\n | spaces (only single space is trimmed)\ntab\n | text-block |
-| StringLiterals.java:63:3:64:22 | """\n\t\t\tend on same line""" | end on same line | end on same line | text-block |
-| StringLiterals.java:65:3:68:5 | """\n\t\ttrailing spaces ignored: \t \n\t\tnot ignored: \t \\s\n\t\t""" | trailing spaces ignored:\nnot ignored: \t \n | trailing spaces ignored:\nnot ignored: \t \n | text-block |
-| StringLiterals.java:69:3:70:18 | """\n\t\t3 quotes:""\\"""" | 3 quotes:""" | 3 quotes:""" | text-block |
-| StringLiterals.java:71:3:74:5 | """\n\t\tline \\\n\t\tcontinuation \\\n\t\t""" | line continuation | line continuation | text-block |
-| StringLiterals.java:75:3:79:5 | """\n\t\tExplicit line breaks:\\n\n\t\t\\r\\n\n\t\t\\r\n\t\t""" | Explicit line breaks:\n\n\r\n\n\r\n | Explicit line breaks:\n\n\r\n\n\r\n | text-block |
-| StringLiterals.java:82:10:84:16 | 2"\\u0022\n\t\ttest\n\t\t\\u0022\\uu0022" | test\n | test\n | |
-| StringLiterals.java:90:3:90:19 | "hello" + "world" | helloworld | helloworld | |
-| StringLiterals.java:91:3:92:20 | """\n\t\thello""" + "world" | helloworld | helloworld | text-block |
-| StringLiterals.java:93:10:93:12 | "a" | a | a | |
-| StringLiterals.java:94:3:94:5 | "a" | a | a | |
+| StringLiterals.java:28:3:28:10 | "\\uD800" | \ufffd | \ufffd | |
+| StringLiterals.java:29:3:29:10 | "\\uDC00" | \ufffd | \ufffd | |
+| StringLiterals.java:30:3:30:31 | "hello\\uD800hello\\uDC00world" | hello\ufffdhello\ufffdworld | hello\ufffdhello\ufffdworld | |
+| StringLiterals.java:32:3:32:16 | "\\u005C\\u0022" | " | " | |
+| StringLiterals.java:33:8:33:20 | 2\\u0061\\u0022 | a | a | |
+| StringLiterals.java:38:3:40:5 | """ \t \n\t\ttest "text" and escaped \\u0022\n\t\t""" | test "text" and escaped "\n | test "text" and escaped "\n | text-block |
+| StringLiterals.java:42:3:44:5 | """\n\t\t\tindented\n\t\t""" | \tindented\n | \tindented\n | text-block |
+| StringLiterals.java:45:3:47:5 | """\n\tno indentation last line\n\t\t""" | no indentation last line\n | no indentation last line\n | text-block |
+| StringLiterals.java:48:3:50:7 | """\n\tindentation last line\n\t\t\\s""" | indentation last line\n\t | indentation last line\n\t | text-block |
+| StringLiterals.java:51:3:53:6 | """\n\t\t\tnot-indented\n\t\t\t""" | not-indented\n | not-indented\n | text-block |
+| StringLiterals.java:54:3:56:4 | """\n\t\tindented\n\t""" | \tindented\n | \tindented\n | text-block |
+| StringLiterals.java:57:4:59:5 | """\n\t\tnot-indented\n\t\t""" | not-indented\n | not-indented\n | text-block |
+| StringLiterals.java:60:3:63:6 | """\n\t\t spaces (only single space is trimmed)\n\t\t\ttab\n\t\t\t""" | spaces (only single space is trimmed)\ntab\n | spaces (only single space is trimmed)\ntab\n | text-block |
+| StringLiterals.java:64:3:65:22 | """\n\t\t\tend on same line""" | end on same line | end on same line | text-block |
+| StringLiterals.java:66:3:69:5 | """\n\t\ttrailing spaces ignored: \t \n\t\tnot ignored: \t \\s\n\t\t""" | trailing spaces ignored:\nnot ignored: \t \n | trailing spaces ignored:\nnot ignored: \t \n | text-block |
+| StringLiterals.java:70:3:71:18 | """\n\t\t3 quotes:""\\"""" | 3 quotes:""" | 3 quotes:""" | text-block |
+| StringLiterals.java:72:3:75:5 | """\n\t\tline \\\n\t\tcontinuation \\\n\t\t""" | line continuation | line continuation | text-block |
+| StringLiterals.java:76:3:80:5 | """\n\t\tExplicit line breaks:\\n\n\t\t\\r\\n\n\t\t\\r\n\t\t""" | Explicit line breaks:\n\n\r\n\n\r\n | Explicit line breaks:\n\n\r\n\n\r\n | text-block |
+| StringLiterals.java:83:10:85:16 | 2"\\u0022\n\t\ttest\n\t\t\\u0022\\uu0022" | test\n | test\n | |
+| StringLiterals.java:91:3:91:19 | "hello" + "world" | helloworld | helloworld | |
+| StringLiterals.java:92:3:93:20 | """\n\t\thello""" + "world" | helloworld | helloworld | text-block |
+| StringLiterals.java:94:10:94:12 | "a" | a | a | |
| StringLiterals.java:95:3:95:5 | "a" | a | a | |
-| StringLiterals.java:96:7:96:9 | "a" | a | a | |
-| StringLiterals.java:97:3:97:5 | "a" | a | a | |
-| StringLiterals.java:98:10:98:12 | "a" | a | a | |
-| StringLiterals.java:99:3:99:5 | "a" | a | a | |
-| StringLiterals.java:100:9:100:11 | "a" | a | a | |
+| StringLiterals.java:96:3:96:5 | "a" | a | a | |
+| StringLiterals.java:97:7:97:9 | "a" | a | a | |
+| StringLiterals.java:98:3:98:5 | "a" | a | a | |
+| StringLiterals.java:99:10:99:12 | "a" | a | a | |
+| StringLiterals.java:100:3:100:5 | "a" | a | a | |
+| StringLiterals.java:101:9:101:11 | "a" | a | a | |
From 4f59886a658b6dc80142285c90e0c98cf404ba79 Mon Sep 17 00:00:00 2001
From: Marcono1234
Date: Fri, 29 Oct 2021 14:24:30 +0200
Subject: [PATCH 243/471] Java: Simplify CompileTimeConstantExpr.getIntValue()
The changed code previously also only covered IntegerLiteral:
- Restricted to Literal
- Integral type
- != "long"
- != "char"
So the only class left which matches all of these is IntegerLiteral.
---
java/ql/lib/semmle/code/java/Expr.qll | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/Expr.qll b/java/ql/lib/semmle/code/java/Expr.qll
index 19f41772ff6..719ad8da4ab 100755
--- a/java/ql/lib/semmle/code/java/Expr.qll
+++ b/java/ql/lib/semmle/code/java/Expr.qll
@@ -304,11 +304,7 @@ class CompileTimeConstantExpr extends Expr {
int getIntValue() {
exists(IntegralType t | this.getType() = t | t.getName().toLowerCase() != "long") and
(
- exists(string lit | lit = this.(Literal).getValue() |
- // Don't parse `char` literal as int, instead get its code point value (see below)
- not this instanceof CharacterLiteral and
- result = lit.toInt()
- )
+ result = this.(IntegerLiteral).getIntValue()
or
result = this.(CharacterLiteral).getCodePointValue()
or
From d36c66cfcaa4e2ff09255f4bb839cec99c7f5f1f Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Fri, 29 Oct 2021 14:37:56 +0200
Subject: [PATCH 244/471] remove redundant inline casts in arguments where the
type is inferred by the call target
---
cpp/ql/lib/semmle/code/cpp/Class.qll | 9 ++++-----
cpp/ql/lib/semmle/code/cpp/Declaration.qll | 3 +--
cpp/ql/lib/semmle/code/cpp/commons/Dependency.qll | 2 +-
.../semmle/code/cpp/controlflow/DefinitionsAndUses.qll | 2 +-
cpp/ql/lib/semmle/code/cpp/controlflow/SSA.qll | 2 +-
cpp/ql/lib/semmle/code/cpp/controlflow/SSAUtils.qll | 2 +-
.../code/cpp/controlflow/internal/ConstantExprs.qll | 8 ++++----
.../ir/implementation/raw/internal/TranslatedExpr.qll | 6 +++---
cpp/ql/lib/semmle/code/cpp/padding/Padding.qll | 8 ++++----
cpp/ql/lib/semmle/code/cpp/rangeanalysis/RangeSSA.qll | 2 +-
.../Unused Entities/UnusedStaticFunctions.ql | 2 +-
...cedenceLogicErrorWhenUseBitwiseOrLogicalOperations.ql | 5 ++---
.../jsf/4.15 Declarations and Definitions/AV Rule 135.ql | 2 +-
.../controlflow/controlflow/SsaDominance.ql | 4 +---
.../rangeanalysis/RangeSSA/RangeSsaDominance.ql | 4 +---
.../csharp/controlflow/internal/ControlFlowGraphImpl.qll | 2 +-
.../ir/implementation/raw/internal/TranslatedExpr.qll | 6 +++---
csharp/ql/src/experimental/ir/internal/IRGuards.qll | 8 ++++----
java/ql/lib/semmle/code/java/Statement.qll | 4 ++--
java/ql/lib/semmle/code/java/Type.qll | 2 +-
.../internal/rangeanalysis/SignAnalysisCommon.qll | 2 +-
java/ql/lib/semmle/code/java/deadcode/DeadCode.qll | 6 +++---
java/ql/src/Likely Bugs/Arithmetic/InformationLoss.ql | 2 +-
java/ql/src/Security/CWE/CWE-190/ArithmeticCommon.qll | 4 +---
java/ql/src/Security/CWE/CWE-681/NumericCastCommon.qll | 2 +-
javascript/ql/lib/semmle/javascript/ES2015Modules.qll | 2 +-
python/ql/lib/semmle/python/types/Properties.qll | 4 ++--
python/ql/lib/semmle/python/values/StringAttributes.qll | 2 +-
python/ql/src/Lexical/CommentedOutCode.qll | 4 ++--
29 files changed, 51 insertions(+), 60 deletions(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/Class.qll b/cpp/ql/lib/semmle/code/cpp/Class.qll
index c4fdbfb40f6..4fe1b07e32a 100644
--- a/cpp/ql/lib/semmle/code/cpp/Class.qll
+++ b/cpp/ql/lib/semmle/code/cpp/Class.qll
@@ -237,7 +237,7 @@ class Class extends UserType {
exists(ClassDerivation cd | cd.getBaseClass() = base |
result =
this.accessOfBaseMemberMulti(cd.getDerivedClass(),
- fieldInBase.accessInDirectDerived(cd.getASpecifier().(AccessSpecifier)))
+ fieldInBase.accessInDirectDerived(cd.getASpecifier()))
)
}
@@ -261,8 +261,7 @@ class Class extends UserType {
* includes the case of `base` = `this`.
*/
AccessSpecifier accessOfBaseMember(Declaration member) {
- result =
- this.accessOfBaseMember(member.getDeclaringType(), member.getASpecifier().(AccessSpecifier))
+ result = this.accessOfBaseMember(member.getDeclaringType(), member.getASpecifier())
}
/**
@@ -319,7 +318,7 @@ class Class extends UserType {
exists(Type t | t = this.getAFieldSubobjectType().getUnspecifiedType() |
// Note: Overload resolution is not implemented -- all copy
// constructors are considered equal.
- this.cannotAccessCopyConstructorOnAny(t.(Class))
+ this.cannotAccessCopyConstructorOnAny(t)
)
or
// - T has direct or virtual base class that cannot be copied (has deleted,
@@ -392,7 +391,7 @@ class Class extends UserType {
exists(Type t | t = this.getAFieldSubobjectType().getUnspecifiedType() |
// Note: Overload resolution is not implemented -- all copy assignment
// operators are considered equal.
- this.cannotAccessCopyAssignmentOperatorOnAny(t.(Class))
+ this.cannotAccessCopyAssignmentOperatorOnAny(t)
)
or
exists(Class c | c = this.getADirectOrVirtualBase() |
diff --git a/cpp/ql/lib/semmle/code/cpp/Declaration.qll b/cpp/ql/lib/semmle/code/cpp/Declaration.qll
index 0433be1f120..8def15e8c13 100644
--- a/cpp/ql/lib/semmle/code/cpp/Declaration.qll
+++ b/cpp/ql/lib/semmle/code/cpp/Declaration.qll
@@ -490,8 +490,7 @@ class AccessHolder extends Declaration, TAccessHolder {
*/
pragma[inline]
predicate canAccessMember(Declaration member, Class derived) {
- this.couldAccessMember(member.getDeclaringType(), member.getASpecifier().(AccessSpecifier),
- derived)
+ this.couldAccessMember(member.getDeclaringType(), member.getASpecifier(), derived)
}
/**
diff --git a/cpp/ql/lib/semmle/code/cpp/commons/Dependency.qll b/cpp/ql/lib/semmle/code/cpp/commons/Dependency.qll
index 6f89564e5f0..ec95b29177b 100644
--- a/cpp/ql/lib/semmle/code/cpp/commons/Dependency.qll
+++ b/cpp/ql/lib/semmle/code/cpp/commons/Dependency.qll
@@ -307,7 +307,7 @@ private predicate dependsOnFull(DependsSource src, Symbol dest, int category) {
// dependency from a Variable / Function use -> non-visible definition (link time)
dependsOnTransitive(src, mid) and
not mid instanceof EnumConstant and
- getDeclarationEntries(mid, dest.(DeclarationEntry)) and
+ getDeclarationEntries(mid, dest) and
not dest instanceof TypeDeclarationEntry and
// must be definition
dest.(DeclarationEntry).isDefinition() and
diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/DefinitionsAndUses.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/DefinitionsAndUses.qll
index f6eb0a8a645..dcabba51ce2 100644
--- a/cpp/ql/lib/semmle/code/cpp/controlflow/DefinitionsAndUses.qll
+++ b/cpp/ql/lib/semmle/code/cpp/controlflow/DefinitionsAndUses.qll
@@ -25,7 +25,7 @@ predicate definitionUsePair(SemanticStackVariable var, Expr def, Expr use) {
* Holds if the definition `def` of some stack variable can reach `node`, which
* is a definition or use, without crossing definitions of the same variable.
*/
-predicate definitionReaches(Expr def, Expr node) { def.(Def).reaches(true, _, node.(DefOrUse)) }
+predicate definitionReaches(Expr def, Expr node) { def.(Def).reaches(true, _, node) }
private predicate hasAddressOfAccess(SemanticStackVariable var) {
var.getAnAccess().isAddressOfAccessNonConst()
diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/SSA.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/SSA.qll
index 49805b9fb3c..c7af3fe4326 100644
--- a/cpp/ql/lib/semmle/code/cpp/controlflow/SSA.qll
+++ b/cpp/ql/lib/semmle/code/cpp/controlflow/SSA.qll
@@ -62,7 +62,7 @@ class SsaDefinition extends ControlFlowNodeBase {
BasicBlock getBasicBlock() { result.contains(this.getDefinition()) }
/** Holds if this definition is a phi node for variable `v`. */
- predicate isPhiNode(StackVariable v) { exists(StandardSSA x | x.phi_node(v, this.(BasicBlock))) }
+ predicate isPhiNode(StackVariable v) { exists(StandardSSA x | x.phi_node(v, this)) }
/** Gets the location of this definition. */
Location getLocation() { result = this.(ControlFlowNode).getLocation() }
diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/SSAUtils.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/SSAUtils.qll
index caae23c3bbd..3b02a0c828f 100644
--- a/cpp/ql/lib/semmle/code/cpp/controlflow/SSAUtils.qll
+++ b/cpp/ql/lib/semmle/code/cpp/controlflow/SSAUtils.qll
@@ -292,7 +292,7 @@ library class SSAHelper extends int {
*/
cached
string toString(ControlFlowNode node, StackVariable v) {
- if phi_node(v, node.(BasicBlock))
+ if phi_node(v, node)
then result = "SSA phi(" + v.getName() + ")"
else (
ssa_defn(v, node, _, _) and result = "SSA def(" + v.getName() + ")"
diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/internal/ConstantExprs.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/internal/ConstantExprs.qll
index da5204bb063..d2b24db0938 100644
--- a/cpp/ql/lib/semmle/code/cpp/controlflow/internal/ConstantExprs.qll
+++ b/cpp/ql/lib/semmle/code/cpp/controlflow/internal/ConstantExprs.qll
@@ -484,7 +484,7 @@ library class ExprEvaluator extends int {
this.interestingInternal(e, req, true) and
(
result = req.(CompileTimeConstantInt).getIntValue() or
- result = this.getCompoundValue(e, req.(CompileTimeVariableExpr))
+ result = this.getCompoundValue(e, req)
) and
(
req.getUnderlyingType().(IntegralType).isSigned() or
@@ -611,7 +611,7 @@ library class ExprEvaluator extends int {
or
exists(AssignExpr req | req = val | result = this.getValueInternal(e, req.getRValue()))
or
- result = this.getVariableValue(e, val.(VariableAccess))
+ result = this.getVariableValue(e, val)
or
exists(FunctionCall call | call = val and not callWithMultipleTargets(call) |
result = this.getFunctionValue(call.getTarget())
@@ -663,7 +663,7 @@ library class ExprEvaluator extends int {
this.interestingInternal(_, req, false) and
(
result = req.(CompileTimeConstantInt).getIntValue() or
- result = this.getCompoundValueNonSubExpr(req.(CompileTimeVariableExpr))
+ result = this.getCompoundValueNonSubExpr(req)
) and
(
req.getUnderlyingType().(IntegralType).isSigned() or
@@ -787,7 +787,7 @@ library class ExprEvaluator extends int {
or
exists(AssignExpr req | req = val | result = this.getValueInternalNonSubExpr(req.getRValue()))
or
- result = this.getVariableValueNonSubExpr(val.(VariableAccess))
+ result = this.getVariableValueNonSubExpr(val)
or
exists(FunctionCall call | call = val and not callWithMultipleTargets(call) |
result = this.getFunctionValue(call.getTarget())
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll
index e15b8c36972..d3f70b94db7 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll
@@ -1305,9 +1305,9 @@ class TranslatedBinaryOperation extends TranslatedSingleInstructionExpr {
}
override Opcode getOpcode() {
- result = binaryArithmeticOpcode(expr.(BinaryArithmeticOperation)) or
- result = binaryBitwiseOpcode(expr.(BinaryBitwiseOperation)) or
- result = comparisonOpcode(expr.(ComparisonOperation))
+ result = binaryArithmeticOpcode(expr) or
+ result = binaryBitwiseOpcode(expr) or
+ result = comparisonOpcode(expr)
}
override int getInstructionElementSize(InstructionTag tag) {
diff --git a/cpp/ql/lib/semmle/code/cpp/padding/Padding.qll b/cpp/ql/lib/semmle/code/cpp/padding/Padding.qll
index e9bde69cfda..c5c1903b4d1 100644
--- a/cpp/ql/lib/semmle/code/cpp/padding/Padding.qll
+++ b/cpp/ql/lib/semmle/code/cpp/padding/Padding.qll
@@ -88,7 +88,7 @@ abstract class Architecture extends string {
or
t instanceof LongLongType and result = this.longLongSize()
or
- result = this.enumBitSize(t.(Enum))
+ result = this.enumBitSize(t)
or
result = this.integralBitSize(t.(SpecifiedType).getBaseType())
or
@@ -183,7 +183,7 @@ abstract class Architecture extends string {
or
t instanceof ReferenceType and result = this.pointerSize()
or
- result = this.enumAlignment(t.(Enum))
+ result = this.enumAlignment(t)
or
result = this.alignment(t.(SpecifiedType).getBaseType())
or
@@ -232,14 +232,14 @@ private Field getAnInitialField(PaddedType t) {
result = t.getAField()
or
// Initial field of the type of a field of the union
- result = getAnInitialField(t.getAField().getUnspecifiedType().(PaddedType))
+ result = getAnInitialField(t.getAField().getUnspecifiedType())
else
exists(Field firstField | t.fieldIndex(firstField) = 1 |
// The first field of `t`
result = firstField
or
// Initial field of the first field of `t`
- result = getAnInitialField(firstField.getUnspecifiedType().(PaddedType))
+ result = getAnInitialField(firstField.getUnspecifiedType())
)
}
diff --git a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/RangeSSA.qll b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/RangeSSA.qll
index 5aebf07f2f1..d2d2fbd5b3c 100644
--- a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/RangeSSA.qll
+++ b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/RangeSSA.qll
@@ -91,7 +91,7 @@ class RangeSsaDefinition extends ControlFlowNodeBase {
BasicBlock getBasicBlock() { result.contains(this.getDefinition()) }
/** Whether this definition is a phi node for variable `v`. */
- predicate isPhiNode(StackVariable v) { exists(RangeSSA x | x.phi_node(v, this.(BasicBlock))) }
+ predicate isPhiNode(StackVariable v) { exists(RangeSSA x | x.phi_node(v, this)) }
/**
* DEPRECATED: Use isGuardPhi/4 instead
diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.ql b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.ql
index 2d7649d534e..4a08ce4fb4b 100644
--- a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.ql
+++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.ql
@@ -62,7 +62,7 @@ class Thing extends Locatable {
}
Thing callsOrAccesses() {
- this.(Function).calls(result.(Function))
+ this.(Function).calls(result)
or
this.(Function).accesses(result.(Function))
or
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBitwiseOrLogicalOperations.ql b/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBitwiseOrLogicalOperations.ql
index eae74f76749..78f539aae8b 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBitwiseOrLogicalOperations.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBitwiseOrLogicalOperations.ql
@@ -188,8 +188,7 @@ where
isBitwiseandBitwise(exp) and
isDifferentResults(exp.(BinaryBitwiseOperation).getLeftOperand(),
exp.(BinaryBitwiseOperation).getRightOperand().(BinaryBitwiseOperation).getLeftOperand(),
- exp.(BinaryBitwiseOperation).getRightOperand().(BinaryBitwiseOperation).getRightOperand(),
- exp.(BinaryBitwiseOperation),
- exp.(BinaryBitwiseOperation).getRightOperand().(BinaryBitwiseOperation)) and
+ exp.(BinaryBitwiseOperation).getRightOperand().(BinaryBitwiseOperation).getRightOperand(), exp,
+ exp.(BinaryBitwiseOperation).getRightOperand()) and
msg = "specify the priority with parentheses."
select exp, msg
diff --git a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.ql b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.ql
index d3e23c9a7f6..aa4cc984170 100644
--- a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.ql
+++ b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.ql
@@ -31,7 +31,7 @@ from Variable v, Variable shadowed
where
not v.getParentScope().(BlockStmt).isInMacroExpansion() and
(
- v.(LocalVariableOrParameter).shadowsGlobal(shadowed.(GlobalVariable)) or
+ v.(LocalVariableOrParameter).shadowsGlobal(shadowed) or
localShadowsParameter(v, shadowed) or
shadowing(v, shadowed)
)
diff --git a/cpp/ql/test/library-tests/controlflow/controlflow/SsaDominance.ql b/cpp/ql/test/library-tests/controlflow/controlflow/SsaDominance.ql
index 087b8a809fb..5086ee5d964 100644
--- a/cpp/ql/test/library-tests/controlflow/controlflow/SsaDominance.ql
+++ b/cpp/ql/test/library-tests/controlflow/controlflow/SsaDominance.ql
@@ -14,9 +14,7 @@ import semmle.code.cpp.controlflow.SSA
select count(SsaDefinition d, StackVariable v, Expr u |
d.getAUse(v) = u and
- not exists(BasicBlock bd, BasicBlock bu |
- bd.contains(mkElement(d).(ControlFlowNode)) and bu.contains(u)
- |
+ not exists(BasicBlock bd, BasicBlock bu | bd.contains(mkElement(d)) and bu.contains(u) |
bbStrictlyDominates(bd, bu)
or
exists(int i, int j |
diff --git a/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaDominance.ql b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaDominance.ql
index 6dab291f125..bf6f7007919 100644
--- a/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaDominance.ql
+++ b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaDominance.ql
@@ -14,9 +14,7 @@ import semmle.code.cpp.rangeanalysis.RangeSSA
select count(RangeSsaDefinition d, StackVariable v, Expr u |
d.getAUse(v) = u and
- not exists(BasicBlock bd, BasicBlock bu |
- bd.contains(mkElement(d).(ControlFlowNode)) and bu.contains(u)
- |
+ not exists(BasicBlock bd, BasicBlock bu | bd.contains(mkElement(d)) and bu.contains(u) |
bbStrictlyDominates(bd, bu)
or
exists(int i, int j |
diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll
index 82eb5d302ad..473fa8f83c4 100644
--- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll
+++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll
@@ -1540,7 +1540,7 @@ module Statements {
c =
any(NestedCompletion nc |
nc.getNestLevel() = 0 and
- this.throwMayBeUncaught(nc.getOuterCompletion().(ThrowCompletion)) and
+ this.throwMayBeUncaught(nc.getOuterCompletion()) and
(
// Incompatible exception type: clause itself
last = this and
diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedExpr.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedExpr.qll
index 79321fe6f11..4f4c1a5f073 100644
--- a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedExpr.qll
+++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedExpr.qll
@@ -1183,9 +1183,9 @@ class TranslatedBinaryOperation extends TranslatedSingleInstructionExpr {
}
override Opcode getOpcode() {
- result = binaryArithmeticOpcode(expr.(BinaryArithmeticOperation)) or
- result = binaryBitwiseOpcode(expr.(BinaryBitwiseOperation)) or
- result = comparisonOpcode(expr.(ComparisonOperation))
+ result = binaryArithmeticOpcode(expr) or
+ result = binaryBitwiseOpcode(expr) or
+ result = comparisonOpcode(expr)
}
override int getInstructionElementSize(InstructionTag tag) {
diff --git a/csharp/ql/src/experimental/ir/internal/IRGuards.qll b/csharp/ql/src/experimental/ir/internal/IRGuards.qll
index 40780a3920e..2a530b71357 100644
--- a/csharp/ql/src/experimental/ir/internal/IRGuards.qll
+++ b/csharp/ql/src/experimental/ir/internal/IRGuards.qll
@@ -107,7 +107,7 @@ private predicate impliesValue(
wholeIsTrue = true and partIsTrue = true and part = blo.getAnOperand()
or
wholeIsTrue = true and
- impliesValue(blo.getAnOperand().(BinaryLogicalOperation), part, partIsTrue, true)
+ impliesValue(blo.getAnOperand(), part, partIsTrue, true)
)
or
blo instanceof LogicalOrExpr and
@@ -115,7 +115,7 @@ private predicate impliesValue(
wholeIsTrue = false and partIsTrue = false and part = blo.getAnOperand()
or
wholeIsTrue = false and
- impliesValue(blo.getAnOperand().(BinaryLogicalOperation), part, partIsTrue, false)
+ impliesValue(blo.getAnOperand(), part, partIsTrue, false)
)
}
@@ -139,7 +139,7 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition {
override predicate comparesLt(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) {
exists(boolean partIsTrue, GuardCondition part |
- impliesValue(this.(BinaryLogicalOperation), part, partIsTrue, testIsTrue)
+ impliesValue(this, part, partIsTrue, testIsTrue)
|
part.comparesLt(left, right, k, isLessThan, partIsTrue)
)
@@ -153,7 +153,7 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition {
override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
exists(boolean partIsTrue, GuardCondition part |
- impliesValue(this.(BinaryLogicalOperation), part, partIsTrue, testIsTrue)
+ impliesValue(this, part, partIsTrue, testIsTrue)
|
part.comparesEq(left, right, k, areEqual, partIsTrue)
)
diff --git a/java/ql/lib/semmle/code/java/Statement.qll b/java/ql/lib/semmle/code/java/Statement.qll
index c3b4deef6a3..082fb2ab295 100755
--- a/java/ql/lib/semmle/code/java/Statement.qll
+++ b/java/ql/lib/semmle/code/java/Statement.qll
@@ -567,7 +567,7 @@ class ThrowStmt extends Stmt, @throwstmt {
or
exists(Stmt mid |
mid = this.findEnclosing() and
- not exists(this.catchClauseForThis(mid.(TryStmt))) and
+ not exists(this.catchClauseForThis(mid)) and
result = mid.getEnclosingStmt()
)
}
@@ -575,7 +575,7 @@ class ThrowStmt extends Stmt, @throwstmt {
private CatchClause catchClauseForThis(TryStmt try) {
result = try.getACatchClause() and
result.getEnclosingCallable() = this.getEnclosingCallable() and
- this.getExpr().getType().(RefType).hasSupertype*(result.getVariable().getType().(RefType)) and
+ this.getExpr().getType().(RefType).hasSupertype*(result.getVariable().getType()) and
not this.getEnclosingStmt+() = result
}
diff --git a/java/ql/lib/semmle/code/java/Type.qll b/java/ql/lib/semmle/code/java/Type.qll
index 1d0658595b2..1ef6d172939 100755
--- a/java/ql/lib/semmle/code/java/Type.qll
+++ b/java/ql/lib/semmle/code/java/Type.qll
@@ -511,7 +511,7 @@ class RefType extends Type, Annotatable, Modifiable, @reftype {
this.getSourceDeclaration().inherits(f)
)
or
- this.hasMethod(m.(Method), _)
+ this.hasMethod(m, _)
}
/** Holds if this is a top-level type, which is not nested inside any other types. */
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll b/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll
index 06af17094a5..59a69614c72 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll
@@ -299,7 +299,7 @@ Sign exprSign(Expr e) {
exists(VarAccess access | access = e |
not exists(SsaVariable v | getARead(v) = access) and
(
- s = fieldSign(getField(access.(FieldAccess)))
+ s = fieldSign(getField(access))
or
anySign(s) and not access instanceof FieldAccess
)
diff --git a/java/ql/lib/semmle/code/java/deadcode/DeadCode.qll b/java/ql/lib/semmle/code/java/deadcode/DeadCode.qll
index 587b53daf4a..27122112ea5 100644
--- a/java/ql/lib/semmle/code/java/deadcode/DeadCode.qll
+++ b/java/ql/lib/semmle/code/java/deadcode/DeadCode.qll
@@ -18,12 +18,12 @@ predicate isLive(Callable c) {
* would imply the liveness of `c`.
*/
Callable possibleLivenessCause(Callable c, string reason) {
- c.(Method).overridesOrInstantiates(result.(Method)) and
+ c.(Method).overridesOrInstantiates(result) and
reason = "is overridden or instantiated by"
or
result.calls(c) and reason = "calls"
or
- result.callsConstructor(c.(Constructor)) and reason = "calls constructor"
+ result.callsConstructor(c) and reason = "calls constructor"
or
exists(ClassInstanceExpr e | e.getEnclosingCallable() = result |
e.getConstructor() = c and reason = "constructs"
@@ -243,7 +243,7 @@ class DeadMethod extends Callable {
) and
not (
this.(Method).isAbstract() and
- exists(Method m | m.overridesOrInstantiates+(this.(Method)) | isLive(m))
+ exists(Method m | m.overridesOrInstantiates+(this) | isLive(m))
) and
// A getter or setter associated with a live JPA field.
//
diff --git a/java/ql/src/Likely Bugs/Arithmetic/InformationLoss.ql b/java/ql/src/Likely Bugs/Arithmetic/InformationLoss.ql
index 29b60cae012..d2ff4c24060 100644
--- a/java/ql/src/Likely Bugs/Arithmetic/InformationLoss.ql
+++ b/java/ql/src/Likely Bugs/Arithmetic/InformationLoss.ql
@@ -25,7 +25,7 @@ class DangerousAssignOpExpr extends AssignOp {
}
}
-predicate problematicCasting(Type t, Expr e) { e.getType().(NumType).widerThan(t.(NumType)) }
+predicate problematicCasting(Type t, Expr e) { e.getType().(NumType).widerThan(t) }
from DangerousAssignOpExpr a, Expr e
where
diff --git a/java/ql/src/Security/CWE/CWE-190/ArithmeticCommon.qll b/java/ql/src/Security/CWE/CWE-190/ArithmeticCommon.qll
index 21f6708f472..8edf8ec2bbb 100644
--- a/java/ql/src/Security/CWE/CWE-190/ArithmeticCommon.qll
+++ b/java/ql/src/Security/CWE/CWE-190/ArithmeticCommon.qll
@@ -14,9 +14,7 @@ private import semmle.code.java.controlflow.internal.GuardsLogic
predicate narrowerThanOrEqualTo(ArithExpr exp, NumType numType) {
exp.getType().(NumType).widerThan(numType)
implies
- exists(CastExpr cast | cast.getAChildExpr() = exp |
- numType.widerThanOrEqualTo(cast.getType().(NumType))
- )
+ exists(CastExpr cast | cast.getAChildExpr() = exp | numType.widerThanOrEqualTo(cast.getType()))
}
private Guard sizeGuard(SsaVariable v, boolean branch, boolean upper) {
diff --git a/java/ql/src/Security/CWE/CWE-681/NumericCastCommon.qll b/java/ql/src/Security/CWE/CWE-681/NumericCastCommon.qll
index 4887c3fefad..4dec14a204f 100644
--- a/java/ql/src/Security/CWE/CWE-681/NumericCastCommon.qll
+++ b/java/ql/src/Security/CWE/CWE-681/NumericCastCommon.qll
@@ -9,7 +9,7 @@ class NumericNarrowingCastExpr extends CastExpr {
exists(NumericType sourceType, NumericType targetType |
sourceType = getExpr().getType() and targetType = getType()
|
- not targetType.(NumType).widerThanOrEqualTo(sourceType.(NumType))
+ not targetType.(NumType).widerThanOrEqualTo(sourceType)
)
}
}
diff --git a/javascript/ql/lib/semmle/javascript/ES2015Modules.qll b/javascript/ql/lib/semmle/javascript/ES2015Modules.qll
index 03494c23a6a..b11903c2e48 100644
--- a/javascript/ql/lib/semmle/javascript/ES2015Modules.qll
+++ b/javascript/ql/lib/semmle/javascript/ES2015Modules.qll
@@ -658,7 +658,7 @@ abstract class ReExportDeclaration extends ExportDeclaration {
cached
Module getReExportedModule() {
Stages::Imports::ref() and
- result.getFile() = getEnclosingModule().resolve(getImportedPath().(PathExpr))
+ result.getFile() = getEnclosingModule().resolve(getImportedPath())
or
result = resolveFromTypeRoot()
}
diff --git a/python/ql/lib/semmle/python/types/Properties.qll b/python/ql/lib/semmle/python/types/Properties.qll
index 09bd08b6c15..a0efe6ca5eb 100644
--- a/python/ql/lib/semmle/python/types/Properties.qll
+++ b/python/ql/lib/semmle/python/types/Properties.qll
@@ -84,7 +84,7 @@ private predicate property_getter(CallNode decorated, FunctionObject 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))
+ prop_setter.getObject("setter").refersTo(decorated)
|
setter_call.getArg(0).refersTo(setter) and
setter_call.getFunction() = prop_setter
@@ -97,7 +97,7 @@ private predicate property_setter(CallNode decorated, FunctionObject 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))
+ prop_deleter.getObject("deleter").refersTo(decorated)
|
deleter_call.getArg(0).refersTo(deleter) and
deleter_call.getFunction() = prop_deleter
diff --git a/python/ql/lib/semmle/python/values/StringAttributes.qll b/python/ql/lib/semmle/python/values/StringAttributes.qll
index a7a8ef00f00..792ee923227 100644
--- a/python/ql/lib/semmle/python/values/StringAttributes.qll
+++ b/python/ql/lib/semmle/python/values/StringAttributes.qll
@@ -78,5 +78,5 @@ private predicate tracking_step(ControlFlowNode src, ControlFlowNode dest) {
or
tracked_call_step(src, dest)
or
- dest.refersTo(src.(Object))
+ dest.refersTo(src)
}
diff --git a/python/ql/src/Lexical/CommentedOutCode.qll b/python/ql/src/Lexical/CommentedOutCode.qll
index 94d8ca44e35..afe72e927a6 100644
--- a/python/ql/src/Lexical/CommentedOutCode.qll
+++ b/python/ql/src/Lexical/CommentedOutCode.qll
@@ -210,9 +210,9 @@ class CommentedOutCodeBlock extends @py_comment {
/** 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(CommentBlock block | block.contains(this) |
exists(int all_code |
- all_code = sum(CommentedOutCodeBlock code | block.contains(code.(Comment)) | code.length()) and
+ all_code = sum(CommentedOutCodeBlock code | block.contains(code) | code.length()) and
/* This ratio may need fine tuning */
block.length() > all_code * 2
)
From 0897b004eb776a17bf463ac144fc5387ca23b20a Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Fri, 29 Oct 2021 14:40:27 +0200
Subject: [PATCH 245/471] revert removal of redundant inline casts in some
python files
---
python/ql/lib/semmle/python/frameworks/Django.qll | 2 +-
python/ql/lib/semmle/python/frameworks/Flask.qll | 6 +++---
python/ql/lib/semmle/python/frameworks/Tornado.qll | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/python/ql/lib/semmle/python/frameworks/Django.qll b/python/ql/lib/semmle/python/frameworks/Django.qll
index 2670d4f418e..89ff0537c97 100644
--- a/python/ql/lib/semmle/python/frameworks/Django.qll
+++ b/python/ql/lib/semmle/python/frameworks/Django.qll
@@ -1820,7 +1820,7 @@ private module PrivateDjango {
/** Gets a reference to this class. */
private DataFlow::TypeTrackingNode getARef(DataFlow::TypeTracker t) {
t.start() and
- result.asExpr() = this.getParent()
+ result.asExpr().(ClassExpr) = this.getParent()
or
exists(DataFlow::TypeTracker t2 | result = this.getARef(t2).track(t2, t))
}
diff --git a/python/ql/lib/semmle/python/frameworks/Flask.qll b/python/ql/lib/semmle/python/frameworks/Flask.qll
index aaf962ae4f2..5bb838447cb 100644
--- a/python/ql/lib/semmle/python/frameworks/Flask.qll
+++ b/python/ql/lib/semmle/python/frameworks/Flask.qll
@@ -188,7 +188,7 @@ module Flask {
FlaskViewClass() {
this.getABase() = Views::View::subclassRef().getAUse().asExpr() and
- api_node.getAnImmediateUse().asExpr() = this.getParent()
+ api_node.getAnImmediateUse().asExpr().(ClassExpr) = this.getParent()
}
/** Gets a function that could handle incoming requests, if any. */
@@ -213,7 +213,7 @@ module Flask {
class FlaskMethodViewClass extends FlaskViewClass {
FlaskMethodViewClass() {
this.getABase() = Views::MethodView::subclassRef().getAUse().asExpr() and
- api_node.getAnImmediateUse().asExpr() = this.getParent()
+ api_node.getAnImmediateUse().asExpr().(ClassExpr) = this.getParent()
}
override Function getARequestHandler() {
@@ -294,7 +294,7 @@ module Flask {
override Function getARequestHandler() {
exists(DataFlow::LocalSourceNode func_src |
func_src.flowsTo(this.getViewArg()) and
- func_src.asExpr() = result.getDefinition()
+ func_src.asExpr().(CallableExpr) = result.getDefinition()
)
or
exists(FlaskViewClass vc |
diff --git a/python/ql/lib/semmle/python/frameworks/Tornado.qll b/python/ql/lib/semmle/python/frameworks/Tornado.qll
index 67bcce97e4b..91ae3ac2575 100644
--- a/python/ql/lib/semmle/python/frameworks/Tornado.qll
+++ b/python/ql/lib/semmle/python/frameworks/Tornado.qll
@@ -102,7 +102,7 @@ private module Tornado {
/** Gets a reference to this class. */
private DataFlow::TypeTrackingNode getARef(DataFlow::TypeTracker t) {
t.start() and
- result.asExpr() = this.getParent()
+ result.asExpr().(ClassExpr) = this.getParent()
or
exists(DataFlow::TypeTracker t2 | result = this.getARef(t2).track(t2, t))
}
From f676fc00d31e9cf2b8c9351eff693436945836f9 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Fri, 29 Oct 2021 14:42:38 +0200
Subject: [PATCH 246/471] revert a change in an identical file
---
.../java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll b/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll
index 59a69614c72..06af17094a5 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll
@@ -299,7 +299,7 @@ Sign exprSign(Expr e) {
exists(VarAccess access | access = e |
not exists(SsaVariable v | getARead(v) = access) and
(
- s = fieldSign(getField(access))
+ s = fieldSign(getField(access.(FieldAccess)))
or
anySign(s) and not access instanceof FieldAccess
)
From bfb9577d1550a00df2f8307417a67c45a6cd80d4 Mon Sep 17 00:00:00 2001
From: Marcono1234
Date: Fri, 29 Oct 2021 14:50:15 +0200
Subject: [PATCH 247/471] Java: Deprecate
`StringLiteral.getRepresentedString()`
---
java/ql/lib/semmle/code/java/Expr.qll | 13 ++-
.../lib/semmle/code/java/JDKAnnotations.qll | 4 +-
java/ql/lib/semmle/code/java/Reflection.qll | 6 +-
java/ql/lib/semmle/code/java/StringFormat.qll | 4 +-
java/ql/lib/semmle/code/java/UnitTests.qll | 4 +-
.../dataflow/internal/TaintTrackingUtil.qll | 4 +-
.../frameworks/spring/SpringComponentScan.qll | 6 +-
.../code/java/security/ControlledString.qll | 2 +-
.../semmle/code/java/security/HttpsUrls.qll | 4 +-
.../code/java/security/InsecureBasicAuth.qll | 2 +-
.../code/java/security/RelativePaths.qll | 2 +-
.../code/java/security/SecurityFlag.qll | 2 +-
.../code/java/security/SensitiveActions.qll | 4 +-
.../Performance/InefficientEmptyStringTest.ql | 4 +-
.../CWE/CWE-327/BrokenCryptoAlgorithm.ql | 8 +-
.../CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql | 8 +-
.../CWE/CWE-798/HardcodedCredentials.qll | 2 +-
.../Dead Code/NonAssignedFields.ql | 4 +-
.../CWE/CWE-094/SpringViewManipulationLib.qll | 4 +-
.../CWE/CWE-326/InsufficientKeySize.ql | 2 +-
...nsafeSpringExporterInConfigurationClass.ql | 2 +-
.../Security/CWE/CWE-522/InsecureLdapAuth.ql | 2 +-
.../CWE/CWE-939/IncorrectURLVerification.ql | 13 +--
.../test/library-tests/Encryption/insecure.ql | 2 +-
.../test/library-tests/Encryption/secure.ql | 2 +-
.../stringLiterals/stringLiterals.expected | 96 +++++++++----------
.../literals/stringLiterals/stringLiterals.ql | 2 +-
27 files changed, 104 insertions(+), 104 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/Expr.qll b/java/ql/lib/semmle/code/java/Expr.qll
index f2a309904ad..8616f974c1c 100755
--- a/java/ql/lib/semmle/code/java/Expr.qll
+++ b/java/ql/lib/semmle/code/java/Expr.qll
@@ -166,7 +166,7 @@ class CompileTimeConstantExpr extends Expr {
*/
pragma[nomagic]
string getStringValue() {
- result = this.(StringLiteral).getRepresentedString()
+ result = this.(StringLiteral).getValue()
or
result =
this.(AddExpr).getLeftOperand().(CompileTimeConstantExpr).getStringValue() +
@@ -732,9 +732,18 @@ class CharacterLiteral extends Literal, @characterliteral {
*/
class StringLiteral extends Literal, @stringliteral {
/**
+ * Gets the string represented by this string literal, that is, the content
+ * of the literal without enclosing quotes and with escape sequences translated.
+ */
+ override string getValue() { result = super.getValue() }
+
+ /**
+ * DEPRECATED: This predicate will be removed in a future version because
+ * it is just an alias for `getValue()`; that predicate should be used instead.
+ *
* Gets the literal string without the quotes.
*/
- string getRepresentedString() { result = this.getValue() }
+ deprecated string getRepresentedString() { result = this.getValue() }
/** Holds if this string literal is a text block (`""" ... """`). */
predicate isTextBlock() { this.getLiteral().matches("\"\"\"%") }
diff --git a/java/ql/lib/semmle/code/java/JDKAnnotations.qll b/java/ql/lib/semmle/code/java/JDKAnnotations.qll
index 0b56599caa2..2dff70c4d8e 100644
--- a/java/ql/lib/semmle/code/java/JDKAnnotations.qll
+++ b/java/ql/lib/semmle/code/java/JDKAnnotations.qll
@@ -25,9 +25,7 @@ class SuppressWarningsAnnotation extends Annotation {
}
/** Gets the name of a warning suppressed by this annotation. */
- string getASuppressedWarning() {
- result = this.getASuppressedWarningLiteral().getRepresentedString()
- }
+ string getASuppressedWarning() { result = this.getASuppressedWarningLiteral().getValue() }
}
/** A `@Target` annotation. */
diff --git a/java/ql/lib/semmle/code/java/Reflection.qll b/java/ql/lib/semmle/code/java/Reflection.qll
index 71864c5cfe9..cd1c9f59f0c 100644
--- a/java/ql/lib/semmle/code/java/Reflection.qll
+++ b/java/ql/lib/semmle/code/java/Reflection.qll
@@ -75,7 +75,7 @@ class ReflectiveClassIdentifierMethodAccess extends ReflectiveClassIdentifier, M
/**
* If the argument to this call is a `StringLiteral`, then return that string.
*/
- string getTypeName() { result = this.getArgument(0).(StringLiteral).getRepresentedString() }
+ string getTypeName() { result = this.getArgument(0).(StringLiteral).getValue() }
override RefType getReflectivelyIdentifiedClass() {
// We only handle cases where the class is specified as a string literal to this call.
@@ -360,7 +360,7 @@ class ReflectiveMethodAccess extends ClassMethodAccess {
this.getInferredClassType().inherits(result)
) and
// Only consider instances where the method name is provided as a `StringLiteral`.
- result.hasName(this.getArgument(0).(StringLiteral).getRepresentedString())
+ result.hasName(this.getArgument(0).(StringLiteral).getValue())
}
}
@@ -400,6 +400,6 @@ class ReflectiveFieldAccess extends ClassMethodAccess {
this.getInferredClassType().inherits(result)
)
) and
- result.hasName(this.getArgument(0).(StringLiteral).getRepresentedString())
+ result.hasName(this.getArgument(0).(StringLiteral).getValue())
}
}
diff --git a/java/ql/lib/semmle/code/java/StringFormat.qll b/java/ql/lib/semmle/code/java/StringFormat.qll
index c6f9a7814db..2938f5255fa 100644
--- a/java/ql/lib/semmle/code/java/StringFormat.qll
+++ b/java/ql/lib/semmle/code/java/StringFormat.qll
@@ -279,7 +279,7 @@ private predicate formatStringFragment(Expr fmt) {
private predicate formatStringValue(Expr e, string fmtvalue) {
formatStringFragment(e) and
(
- e.(StringLiteral).getRepresentedString() = fmtvalue
+ e.(StringLiteral).getValue() = fmtvalue
or
e.getType() instanceof IntegralType and fmtvalue = "1" // dummy value
or
@@ -318,7 +318,7 @@ private predicate formatStringValue(Expr e, string fmtvalue) {
getprop.hasName("getProperty") and
getprop.getDeclaringType().hasQualifiedName("java.lang", "System") and
getprop.getNumberOfParameters() = 1 and
- ma.getAnArgument().(StringLiteral).getRepresentedString() = prop and
+ ma.getAnArgument().(StringLiteral).getValue() = prop and
(prop = "line.separator" or prop = "file.separator" or prop = "path.separator") and
fmtvalue = "x" // dummy value
)
diff --git a/java/ql/lib/semmle/code/java/UnitTests.qll b/java/ql/lib/semmle/code/java/UnitTests.qll
index 6115094e5d7..e56b9a6dc23 100644
--- a/java/ql/lib/semmle/code/java/UnitTests.qll
+++ b/java/ql/lib/semmle/code/java/UnitTests.qll
@@ -162,7 +162,7 @@ class TestNGTestMethod extends Method {
testAnnotation = this.getAnAnnotation() and
// The data provider must have the same name as the referenced data provider
result.getDataProviderName() =
- testAnnotation.getValue("dataProvider").(StringLiteral).getRepresentedString()
+ testAnnotation.getValue("dataProvider").(StringLiteral).getValue()
|
// Either the data provider should be on the current class, or a supertype
this.getDeclaringType().getAnAncestor() = result.getDeclaringType()
@@ -258,7 +258,7 @@ class TestNGDataProviderMethod extends Method {
.(TestNGDataProviderAnnotation)
.getValue("name")
.(StringLiteral)
- .getRepresentedString()
+ .getValue()
}
}
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll b/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
index e4bfaaae1cc..4e742238209 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
@@ -300,8 +300,8 @@ private predicate unsafeEscape(MethodAccess ma) {
// Removing `