mirror of
https://github.com/github/codeql.git
synced 2026-07-06 12:05:31 +02:00
Compare commits
151 Commits
codeql-cli
...
tiferet/co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5726591780 | ||
|
|
cc3ad50c39 | ||
|
|
11cd44ca69 | ||
|
|
78c4690eee | ||
|
|
fddc2cc6de | ||
|
|
700c1f5a35 | ||
|
|
426e635437 | ||
|
|
a769c96aa2 | ||
|
|
5b1bfe2799 | ||
|
|
6e2897b0f8 | ||
|
|
f28b85c059 | ||
|
|
b493702a5a | ||
|
|
e2ffba2e47 | ||
|
|
a507d6dc1d | ||
|
|
da5f664265 | ||
|
|
d52af5599a | ||
|
|
05460f6444 | ||
|
|
c7f96928dc | ||
|
|
4351d9e861 | ||
|
|
c3766477dc | ||
|
|
b6922813a8 | ||
|
|
5de0edc773 | ||
|
|
a8c1febf88 | ||
|
|
2b334055db | ||
|
|
46958e5bff | ||
|
|
6815a13a00 | ||
|
|
2af7817691 | ||
|
|
6c2713dd8b | ||
|
|
9d22ec88fd | ||
|
|
1cca377e7d | ||
|
|
ecdaeb0c10 | ||
|
|
c998370c84 | ||
|
|
36585a7469 | ||
|
|
6dc6a78293 | ||
|
|
08b6a17097 | ||
|
|
22ebe68b1b | ||
|
|
1645fcf79c | ||
|
|
5ddfb37f29 | ||
|
|
6f24947ec6 | ||
|
|
3c3a65243f | ||
|
|
fbb3e8d780 | ||
|
|
d232283647 | ||
|
|
4eacbd1cbe | ||
|
|
6d58dd2823 | ||
|
|
3da73b9001 | ||
|
|
58dd75881c | ||
|
|
ac41451798 | ||
|
|
0c1285f5d9 | ||
|
|
7c2841f058 | ||
|
|
474c808373 | ||
|
|
69671ce90d | ||
|
|
7cfc696d62 | ||
|
|
f846915b58 | ||
|
|
c8cdbfa352 | ||
|
|
b4eadefb92 | ||
|
|
286c894f34 | ||
|
|
188915e597 | ||
|
|
76606b5995 | ||
|
|
ea9640a39d | ||
|
|
d408105fad | ||
|
|
5ef71e6ef3 | ||
|
|
9e8e2e2b48 | ||
|
|
e7b091086d | ||
|
|
aafa5762ad | ||
|
|
0e6bb28016 | ||
|
|
b9ea4a8709 | ||
|
|
fd9199c0c0 | ||
|
|
d7ed325b3f | ||
|
|
f4310898b3 | ||
|
|
1cd42ea668 | ||
|
|
8040d9cfcf | ||
|
|
e6e52a3b32 | ||
|
|
c616f5784d | ||
|
|
dc022430ee | ||
|
|
412bd32f45 | ||
|
|
b84c03672d | ||
|
|
bca6cecd1c | ||
|
|
78e3906ea7 | ||
|
|
cce3780481 | ||
|
|
a0b7f267ff | ||
|
|
0234e77d2f | ||
|
|
b8809a20d8 | ||
|
|
b639a8d183 | ||
|
|
2d4176bec0 | ||
|
|
dbd393b77a | ||
|
|
974c7b0898 | ||
|
|
74ac234f1c | ||
|
|
8740e879b4 | ||
|
|
a546b38ee0 | ||
|
|
6960a7b97e | ||
|
|
ef972159a6 | ||
|
|
4cfd978bfe | ||
|
|
b92758883b | ||
|
|
e2bd792fc2 | ||
|
|
739fe75194 | ||
|
|
58de6d143f | ||
|
|
747ab122c3 | ||
|
|
8564c9001a | ||
|
|
9500c9c8bc | ||
|
|
35baa1c3df | ||
|
|
83b4070f31 | ||
|
|
281f25403d | ||
|
|
bc10fd94cb | ||
|
|
0e9fcc6c39 | ||
|
|
157f56f48a | ||
|
|
f36bb8baaf | ||
|
|
842f617bc1 | ||
|
|
be150f269b | ||
|
|
2654e27123 | ||
|
|
6b2460d4a1 | ||
|
|
060862ab3b | ||
|
|
e607953b9c | ||
|
|
9a859334d4 | ||
|
|
6c59333716 | ||
|
|
7dae6122d9 | ||
|
|
ca9d5439f0 | ||
|
|
7a7ec06819 | ||
|
|
1a4fd7bc7d | ||
|
|
2b2ac82fb7 | ||
|
|
c616eb1473 | ||
|
|
38579ef25b | ||
|
|
a80d50cbc0 | ||
|
|
a1d8dfb524 | ||
|
|
c844f5382f | ||
|
|
f9fea15a52 | ||
|
|
364de55b8d | ||
|
|
cd11ef3bf6 | ||
|
|
c3462be2c9 | ||
|
|
4ca006ba3d | ||
|
|
88032afdc3 | ||
|
|
ec772fb6b2 | ||
|
|
32ef40c77b | ||
|
|
1efe1e0d10 | ||
|
|
4af3775b72 | ||
|
|
02f500b9c2 | ||
|
|
8a4b043cb1 | ||
|
|
62e729501c | ||
|
|
97264b5dda | ||
|
|
c15ddf6e92 | ||
|
|
2ddf445caf | ||
|
|
44afa34e37 | ||
|
|
8d6cac76cc | ||
|
|
aafae24ef2 | ||
|
|
8d556ed1e1 | ||
|
|
99ed4a1a89 | ||
|
|
fd64ff9ef1 | ||
|
|
c40ffab093 | ||
|
|
672e4a3d72 | ||
|
|
60993214d5 | ||
|
|
01e345c2cc | ||
|
|
8535e6f281 |
51
benjamin-button.md
Normal file
51
benjamin-button.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# benjamin-buttons.md
|
||||
|
||||
This file describes the changes that have been applied to
|
||||
the library to make it behave as if it was younger.
|
||||
|
||||
## TaintedPath.ql
|
||||
|
||||
Sinks added between 2020-01-01 and 2020-10-06 have been removed. Found by looking at:
|
||||
|
||||
- the commit titles of https://github.com/github/codeql/commits/main/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected
|
||||
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sink
|
||||
|
||||
Sinks added between 2018-08-02 and 2020-01-01 have been removed. Found by looking at:
|
||||
|
||||
- the commit titles of https://github.com/github/codeql/commits/main/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected
|
||||
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sink
|
||||
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+pathinjection
|
||||
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+tainted-path
|
||||
|
||||
Sinks from the "graceful-fs" and "fs-extra" (added before the open-sourcing squash).
|
||||
|
||||
## Xss.ql
|
||||
|
||||
Sinks added between 2020-01-01 and 2020-10-06 have been removed. Found by looking at:
|
||||
|
||||
- the commit titles of https://github.com/github/codeql/commits/main/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected
|
||||
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sink
|
||||
|
||||
- recursive type tracking for `jQuery::dollar`, `DOM::domValueRef`.
|
||||
|
||||
## SqlInjection.ql
|
||||
|
||||
Sinks added between 2020-01-01 and 2020-10-06 have been removed. Found by looking at:
|
||||
|
||||
- the commit titles of https://github.com/github/codeql/commits/main/javascript/ql/test/query-tests/Security/CWE-089
|
||||
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sink
|
||||
|
||||
Sinks added between 2018-08-02 and 2020-01-01 have been removed. Found by looking at:
|
||||
|
||||
- the commit titles of https://github.com/github/codeql/commits/main/javascript/ql/test/query-tests/Security/CWE-089
|
||||
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sink
|
||||
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sql
|
||||
|
||||
TypeTracking in SQL.qll (added before the open-sourcing squash)
|
||||
|
||||
The model of `mssql` and `sequelize` (added before the open-sourcing squash)
|
||||
|
||||
## PseudoProperties
|
||||
|
||||
Pseudo-properties (`$name$`) used in type-tracking and global dataflow configurations have been disabled.
|
||||
Found by searching for `"\$.*\$"`.
|
||||
@@ -471,7 +471,12 @@
|
||||
"ReDoS Polynomial Python/JS": [
|
||||
"javascript/ql/lib/semmle/javascript/security/performance/SuperlinearBackTracking.qll",
|
||||
"python/ql/lib/semmle/python/security/performance/SuperlinearBackTracking.qll",
|
||||
"ruby/ql/lib/codeql/ruby/regexp/SuperlinearBackTracking.qll"
|
||||
"ruby/ql/lib/codeql/ruby/security/performance/SuperlinearBackTracking.qll"
|
||||
],
|
||||
"BadTagFilterQuery Python/JS/Ruby": [
|
||||
"javascript/ql/lib/semmle/javascript/security/BadTagFilterQuery.qll",
|
||||
"python/ql/lib/semmle/python/security/BadTagFilterQuery.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/BadTagFilterQuery.qll"
|
||||
],
|
||||
"CFG": [
|
||||
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll",
|
||||
|
||||
@@ -34,6 +34,16 @@ private class IteratorByTraits extends Iterator {
|
||||
IteratorByTraits() { exists(IteratorTraits it | it.getIteratorType() = this) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The C++ standard includes an `std::iterator_traits` specialization for pointer types. When
|
||||
* this specialization is included in the database, a pointer type `T*` will be an instance
|
||||
* of the `IteratorByTraits` class. However, if the `T*` specialization is not in the database,
|
||||
* we need to explicitly include them with this class.
|
||||
*/
|
||||
private class IteratorByPointer extends Iterator instanceof PointerType {
|
||||
IteratorByPointer() { not this instanceof IteratorByTraits }
|
||||
}
|
||||
|
||||
/**
|
||||
* A type which has the typedefs expected for an iterator.
|
||||
*/
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
* @id cpp/non-https-url
|
||||
* @tags security
|
||||
* external/cwe/cwe-319
|
||||
* external/cwe/cwe-345
|
||||
*/
|
||||
|
||||
import cpp
|
||||
@@ -61,7 +62,11 @@ class HttpStringToUrlOpenConfig extends TaintTracking::Configuration {
|
||||
// accessed as a URL, for example using it in a network access. Some
|
||||
// URLs are only ever displayed or used for data processing.
|
||||
exists(FunctionCall fc |
|
||||
fc.getTarget().hasGlobalOrStdName(["system", "gethostbyname", "getaddrinfo"]) and
|
||||
fc.getTarget()
|
||||
.hasGlobalOrStdName([
|
||||
"system", "gethostbyname", "gethostbyname2", "gethostbyname_r", "getaddrinfo",
|
||||
"X509_load_http", "X509_CRL_load_http"
|
||||
]) and
|
||||
sink.asExpr() = fc.getArgument(0)
|
||||
or
|
||||
fc.getTarget().hasGlobalOrStdName(["send", "URLDownloadToFile", "URLDownloadToCacheFile"]) and
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
| example.c:15:37:15:37 | b | example.c:15:37:15:37 | b |
|
||||
| example.c:15:37:15:37 | b | example.c:19:6:19:6 | b |
|
||||
| example.c:15:44:15:46 | pos | example.c:24:24:24:26 | pos |
|
||||
| example.c:15:44:15:46 | pos | example.c:28:23:28:25 | pos |
|
||||
@@ -70,8 +71,10 @@
|
||||
| test.cpp:391:11:391:13 | tmp | test.cpp:391:10:391:13 | & ... |
|
||||
| test.cpp:391:17:391:23 | source1 | test.cpp:391:10:391:13 | ref arg & ... |
|
||||
| test.cpp:391:17:391:23 | source1 | test.cpp:391:16:391:23 | & ... |
|
||||
| test.cpp:480:67:480:67 | s | test.cpp:480:67:480:67 | s |
|
||||
| test.cpp:480:67:480:67 | s | test.cpp:481:21:481:21 | s |
|
||||
| test.cpp:480:67:480:67 | s | test.cpp:482:20:482:20 | s |
|
||||
| test.cpp:481:21:481:21 | s [post update] | test.cpp:480:67:480:67 | s |
|
||||
| test.cpp:481:21:481:21 | s [post update] | test.cpp:482:20:482:20 | s |
|
||||
| test.cpp:481:24:481:30 | ref arg content | test.cpp:482:23:482:29 | content |
|
||||
| test.cpp:482:23:482:29 | content | test.cpp:483:9:483:17 | p_content |
|
||||
|
||||
@@ -10,29 +10,35 @@ edges
|
||||
| A.cpp:31:20:31:20 | c | A.cpp:23:10:23:10 | c |
|
||||
| A.cpp:31:20:31:20 | c | A.cpp:31:14:31:21 | call to B [c] |
|
||||
| A.cpp:41:15:41:21 | new | A.cpp:43:10:43:12 | & ... |
|
||||
| A.cpp:43:10:43:12 | & ... | A.cpp:173:26:173:26 | o |
|
||||
| A.cpp:47:12:47:18 | new | A.cpp:48:20:48:20 | c |
|
||||
| A.cpp:48:12:48:18 | call to make [c] | A.cpp:49:10:49:10 | b [c] |
|
||||
| A.cpp:48:20:48:20 | c | A.cpp:29:23:29:23 | c |
|
||||
| A.cpp:48:20:48:20 | c | A.cpp:48:12:48:18 | call to make [c] |
|
||||
| A.cpp:49:10:49:10 | b [c] | A.cpp:49:13:49:13 | c |
|
||||
| A.cpp:49:13:49:13 | c | A.cpp:173:26:173:26 | o |
|
||||
| A.cpp:55:5:55:5 | ref arg b [c] | A.cpp:56:10:56:10 | b [c] |
|
||||
| A.cpp:55:12:55:19 | new | A.cpp:27:17:27:17 | c |
|
||||
| A.cpp:55:12:55:19 | new | A.cpp:55:5:55:5 | ref arg b [c] |
|
||||
| A.cpp:56:10:56:10 | b [c] | A.cpp:28:8:28:10 | this [c] |
|
||||
| A.cpp:56:10:56:10 | b [c] | A.cpp:56:13:56:15 | call to get |
|
||||
| A.cpp:56:13:56:15 | call to get | A.cpp:173:26:173:26 | o |
|
||||
| A.cpp:57:11:57:24 | call to B [c] | A.cpp:57:11:57:24 | new [c] |
|
||||
| A.cpp:57:11:57:24 | new [c] | A.cpp:28:8:28:10 | this [c] |
|
||||
| A.cpp:57:11:57:24 | new [c] | A.cpp:57:28:57:30 | call to get |
|
||||
| A.cpp:57:17:57:23 | new | A.cpp:23:10:23:10 | c |
|
||||
| A.cpp:57:17:57:23 | new | A.cpp:57:11:57:24 | call to B [c] |
|
||||
| A.cpp:57:28:57:30 | call to get | A.cpp:173:26:173:26 | o |
|
||||
| A.cpp:64:10:64:15 | call to setOnB [c] | A.cpp:66:10:66:11 | b2 [c] |
|
||||
| A.cpp:64:21:64:28 | new | A.cpp:64:10:64:15 | call to setOnB [c] |
|
||||
| A.cpp:64:21:64:28 | new | A.cpp:85:26:85:26 | c |
|
||||
| A.cpp:66:10:66:11 | b2 [c] | A.cpp:66:14:66:14 | c |
|
||||
| A.cpp:66:14:66:14 | c | A.cpp:173:26:173:26 | o |
|
||||
| A.cpp:73:10:73:19 | call to setOnBWrap [c] | A.cpp:75:10:75:11 | b2 [c] |
|
||||
| A.cpp:73:25:73:32 | new | A.cpp:73:10:73:19 | call to setOnBWrap [c] |
|
||||
| A.cpp:73:25:73:32 | new | A.cpp:78:27:78:27 | c |
|
||||
| A.cpp:75:10:75:11 | b2 [c] | A.cpp:75:14:75:14 | c |
|
||||
| A.cpp:75:14:75:14 | c | A.cpp:173:26:173:26 | o |
|
||||
| A.cpp:78:27:78:27 | c | A.cpp:81:21:81:21 | c |
|
||||
| A.cpp:81:10:81:15 | call to setOnB [c] | A.cpp:82:12:82:24 | ... ? ... : ... [c] |
|
||||
| A.cpp:81:21:81:21 | c | A.cpp:81:10:81:15 | call to setOnB [c] |
|
||||
@@ -48,13 +54,20 @@ edges
|
||||
| A.cpp:103:14:103:14 | c [a] | A.cpp:107:12:107:13 | c1 [a] |
|
||||
| A.cpp:103:14:103:14 | c [a] | A.cpp:120:12:120:13 | c1 [a] |
|
||||
| A.cpp:107:12:107:13 | c1 [a] | A.cpp:107:16:107:16 | a |
|
||||
| A.cpp:107:16:107:16 | a | A.cpp:173:26:173:26 | o |
|
||||
| A.cpp:120:12:120:13 | c1 [a] | A.cpp:120:16:120:16 | a |
|
||||
| A.cpp:120:16:120:16 | a | A.cpp:173:26:173:26 | o |
|
||||
| A.cpp:124:14:124:14 | b [c] | A.cpp:131:8:131:8 | ref arg b [c] |
|
||||
| A.cpp:126:5:126:5 | ref arg b [c] | A.cpp:124:14:124:14 | b [c] |
|
||||
| A.cpp:126:5:126:5 | ref arg b [c] | A.cpp:131:8:131:8 | ref arg b [c] |
|
||||
| A.cpp:126:12:126:18 | new | A.cpp:27:17:27:17 | c |
|
||||
| A.cpp:126:12:126:18 | new | A.cpp:126:5:126:5 | ref arg b [c] |
|
||||
| A.cpp:131:8:131:8 | ref arg b [c] | A.cpp:132:10:132:10 | b [c] |
|
||||
| A.cpp:132:10:132:10 | b [c] | A.cpp:132:13:132:13 | c |
|
||||
| A.cpp:132:13:132:13 | c | A.cpp:173:26:173:26 | o |
|
||||
| A.cpp:140:13:140:13 | b | A.cpp:143:7:143:31 | ... = ... |
|
||||
| A.cpp:140:13:140:13 | b [c] | A.cpp:151:18:151:18 | ref arg b [c] |
|
||||
| A.cpp:142:7:142:7 | b [post update] [c] | A.cpp:140:13:140:13 | b [c] |
|
||||
| A.cpp:142:7:142:7 | b [post update] [c] | A.cpp:143:7:143:31 | ... = ... [c] |
|
||||
| A.cpp:142:7:142:7 | b [post update] [c] | A.cpp:151:18:151:18 | ref arg b [c] |
|
||||
| A.cpp:142:7:142:20 | ... = ... | A.cpp:142:7:142:7 | b [post update] [c] |
|
||||
@@ -66,15 +79,24 @@ edges
|
||||
| A.cpp:143:7:143:31 | ... = ... [c] | A.cpp:143:7:143:10 | this [post update] [b, c] |
|
||||
| A.cpp:143:25:143:31 | new | A.cpp:143:7:143:31 | ... = ... |
|
||||
| A.cpp:150:12:150:18 | new | A.cpp:151:18:151:18 | b |
|
||||
| A.cpp:151:12:151:24 | call to D [b, c] | A.cpp:152:10:152:10 | d [b, c] |
|
||||
| A.cpp:151:12:151:24 | call to D [b, c] | A.cpp:153:10:153:10 | d [b, c] |
|
||||
| A.cpp:151:12:151:24 | call to D [b] | A.cpp:152:10:152:10 | d [b] |
|
||||
| A.cpp:151:18:151:18 | b | A.cpp:140:13:140:13 | b |
|
||||
| A.cpp:151:18:151:18 | b | A.cpp:151:12:151:24 | call to D [b] |
|
||||
| A.cpp:151:18:151:18 | ref arg b [c] | A.cpp:154:10:154:10 | b [c] |
|
||||
| A.cpp:152:10:152:10 | d [b, c] | A.cpp:152:13:152:13 | b [c] |
|
||||
| A.cpp:152:10:152:10 | d [b] | A.cpp:152:13:152:13 | b |
|
||||
| A.cpp:152:10:152:10 | d [post update] [b, c] | A.cpp:153:10:153:10 | d [b, c] |
|
||||
| A.cpp:152:13:152:13 | b | A.cpp:173:26:173:26 | o |
|
||||
| A.cpp:152:13:152:13 | b [c] | A.cpp:152:13:152:13 | ref arg b [c] |
|
||||
| A.cpp:152:13:152:13 | b [c] | A.cpp:173:26:173:26 | o [c] |
|
||||
| A.cpp:152:13:152:13 | ref arg b [c] | A.cpp:152:10:152:10 | d [post update] [b, c] |
|
||||
| A.cpp:153:10:153:10 | d [b, c] | A.cpp:153:13:153:13 | b [c] |
|
||||
| A.cpp:153:13:153:13 | b [c] | A.cpp:153:16:153:16 | c |
|
||||
| A.cpp:153:16:153:16 | c | A.cpp:173:26:173:26 | o |
|
||||
| A.cpp:154:10:154:10 | b [c] | A.cpp:154:13:154:13 | c |
|
||||
| A.cpp:154:13:154:13 | c | A.cpp:173:26:173:26 | o |
|
||||
| A.cpp:159:12:159:18 | new | A.cpp:160:29:160:29 | b |
|
||||
| A.cpp:160:18:160:60 | call to MyList [head] | A.cpp:161:38:161:39 | l1 [head] |
|
||||
| A.cpp:160:29:160:29 | b | A.cpp:160:18:160:60 | call to MyList [head] |
|
||||
@@ -87,13 +109,23 @@ edges
|
||||
| A.cpp:162:38:162:39 | l2 [next, head] | A.cpp:162:18:162:40 | call to MyList [next, next, head] |
|
||||
| A.cpp:162:38:162:39 | l2 [next, head] | A.cpp:181:32:181:35 | next [next, head] |
|
||||
| A.cpp:165:10:165:11 | l3 [next, next, head] | A.cpp:165:14:165:17 | next [next, head] |
|
||||
| A.cpp:165:10:165:11 | l3 [post update] [next, next, head] | A.cpp:167:44:167:44 | l [next, next, head] |
|
||||
| A.cpp:165:14:165:17 | next [next, head] | A.cpp:165:20:165:23 | next [head] |
|
||||
| A.cpp:165:14:165:17 | next [post update] [next, head] | A.cpp:165:10:165:11 | l3 [post update] [next, next, head] |
|
||||
| A.cpp:165:20:165:23 | next [head] | A.cpp:165:26:165:29 | head |
|
||||
| A.cpp:165:20:165:23 | next [head] | A.cpp:165:26:165:29 | head |
|
||||
| A.cpp:165:20:165:23 | next [post update] [head] | A.cpp:165:14:165:17 | next [post update] [next, head] |
|
||||
| A.cpp:165:26:165:29 | head | A.cpp:165:26:165:29 | ref arg head |
|
||||
| A.cpp:165:26:165:29 | head | A.cpp:173:26:173:26 | o |
|
||||
| A.cpp:165:26:165:29 | ref arg head | A.cpp:165:20:165:23 | next [post update] [head] |
|
||||
| A.cpp:167:44:167:44 | l [next, head] | A.cpp:167:47:167:50 | next [head] |
|
||||
| A.cpp:167:44:167:44 | l [next, next, head] | A.cpp:167:47:167:50 | next [next, head] |
|
||||
| A.cpp:167:47:167:50 | next [head] | A.cpp:169:12:169:12 | l [head] |
|
||||
| A.cpp:167:47:167:50 | next [next, head] | A.cpp:167:44:167:44 | l [next, head] |
|
||||
| A.cpp:169:12:169:12 | l [head] | A.cpp:169:15:169:18 | head |
|
||||
| A.cpp:169:15:169:18 | head | A.cpp:173:26:173:26 | o |
|
||||
| A.cpp:173:26:173:26 | o | A.cpp:173:26:173:26 | o |
|
||||
| A.cpp:173:26:173:26 | o [c] | A.cpp:173:26:173:26 | o [c] |
|
||||
| A.cpp:181:15:181:21 | newHead | A.cpp:183:7:183:20 | ... = ... |
|
||||
| A.cpp:181:32:181:35 | next [head] | A.cpp:184:7:184:23 | ... = ... [head] |
|
||||
| A.cpp:181:32:181:35 | next [next, head] | A.cpp:184:7:184:23 | ... = ... [next, head] |
|
||||
@@ -150,6 +182,7 @@ edges
|
||||
| D.cpp:22:10:22:11 | 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:10:11:10:17 | this [elem] |
|
||||
| D.cpp:22:14:22:20 | call to getBox1 [elem] | D.cpp:22:25:22:31 | call to getElem |
|
||||
| D.cpp:22:25:22:31 | call to getElem | realistic.cpp:41:17:41:17 | o |
|
||||
| D.cpp:28:15:28:24 | new | D.cpp:30:5:30:20 | ... = ... |
|
||||
| D.cpp:30:5:30:5 | b [post update] [box, elem] | D.cpp:31:14:31:14 | b [box, elem] |
|
||||
| D.cpp:30:5:30:20 | ... = ... | D.cpp:30:8:30:10 | box [post update] [elem] |
|
||||
@@ -182,17 +215,23 @@ edges
|
||||
| D.cpp:64:10:64:17 | boxfield [box, elem] | D.cpp:64:20:64:22 | box [elem] |
|
||||
| D.cpp:64:10:64:17 | this [boxfield, box, elem] | D.cpp:64:10:64:17 | boxfield [box, elem] |
|
||||
| D.cpp:64:20:64:22 | box [elem] | D.cpp:64:25:64:28 | elem |
|
||||
| D.cpp:64:25:64:28 | elem | realistic.cpp:41:17:41:17 | o |
|
||||
| E.cpp:19:27:19:27 | p [data, buffer] | E.cpp:21:10:21:10 | p [data, buffer] |
|
||||
| E.cpp:21:10:21:10 | p [data, buffer] | E.cpp:21:13:21:16 | data [buffer] |
|
||||
| E.cpp:21:13:21:16 | data [buffer] | E.cpp:21:18:21:23 | buffer |
|
||||
| E.cpp:21:18:21:23 | buffer | realistic.cpp:41:17:41:17 | o |
|
||||
| E.cpp:28:21:28:23 | ref arg raw | E.cpp:31:10:31:12 | raw |
|
||||
| E.cpp:29:21:29:21 | b [post update] [buffer] | E.cpp:32:10:32:10 | b [buffer] |
|
||||
| E.cpp:29:24:29:29 | ref arg buffer | 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 | & ... [data, buffer] |
|
||||
| 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 | ref arg buffer | E.cpp:30:23:30:26 | data [post update] [buffer] |
|
||||
| E.cpp:31:10:31:12 | raw | realistic.cpp:41:17:41:17 | o |
|
||||
| E.cpp:32:10:32:10 | b [buffer] | E.cpp:32:13:32:18 | buffer |
|
||||
| E.cpp:32:13:32:18 | buffer | realistic.cpp:41:17:41:17 | o |
|
||||
| E.cpp:33:18:33:19 | & ... [data, buffer] | E.cpp:19:27:19:27 | p [data, buffer] |
|
||||
| aliasing.cpp:8:23:8:23 | s [m1] | aliasing.cpp:25:17:25:19 | ref arg & ... [m1] |
|
||||
| aliasing.cpp:9:3:9:3 | s [post update] [m1] | aliasing.cpp:8:23:8:23 | s [m1] |
|
||||
| aliasing.cpp:9:3:9:3 | s [post update] [m1] | aliasing.cpp:25:17:25:19 | ref arg & ... [m1] |
|
||||
| aliasing.cpp:9:3:9:22 | ... = ... | aliasing.cpp:9:3:9:3 | s [post update] [m1] |
|
||||
| aliasing.cpp:9:11:9:20 | call to user_input | aliasing.cpp:9:3:9:22 | ... = ... |
|
||||
@@ -204,30 +243,42 @@ edges
|
||||
| aliasing.cpp:25:17:25:19 | ref arg & ... [m1] | aliasing.cpp:29:8:29:9 | s1 [m1] |
|
||||
| aliasing.cpp:26:19:26:20 | ref arg s2 [m1] | aliasing.cpp:30:8:30:9 | s2 [m1] |
|
||||
| aliasing.cpp:29:8:29:9 | s1 [m1] | aliasing.cpp:29:11:29:12 | m1 |
|
||||
| aliasing.cpp:29:11:29:12 | m1 | realistic.cpp:41:17:41:17 | o |
|
||||
| aliasing.cpp:30:8:30:9 | s2 [m1] | aliasing.cpp:30:11:30:12 | m1 |
|
||||
| aliasing.cpp:30:11:30:12 | m1 | realistic.cpp:41:17:41:17 | o |
|
||||
| aliasing.cpp:60:3:60:4 | s2 [post update] [m1] | aliasing.cpp:62:8:62:12 | copy2 [m1] |
|
||||
| aliasing.cpp:60:3:60:22 | ... = ... | aliasing.cpp:60:3:60:4 | s2 [post update] [m1] |
|
||||
| aliasing.cpp:60:11:60:20 | call to user_input | aliasing.cpp:60:3:60:22 | ... = ... |
|
||||
| aliasing.cpp:62:8:62:12 | copy2 [m1] | aliasing.cpp:62:14:62:15 | m1 |
|
||||
| aliasing.cpp:62:14:62:15 | m1 | realistic.cpp:41:17:41:17 | o |
|
||||
| aliasing.cpp:92:3:92:3 | w [post update] [s, m1] | aliasing.cpp:93:8:93:8 | w [s, m1] |
|
||||
| aliasing.cpp:92:3:92:23 | ... = ... | aliasing.cpp:92:5:92:5 | s [post update] [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:12:92:21 | call to user_input | aliasing.cpp:92:3:92:23 | ... = ... |
|
||||
| aliasing.cpp:93:8:93:8 | w [s, m1] | aliasing.cpp:93:10:93:10 | s [m1] |
|
||||
| aliasing.cpp:93:10:93:10 | s [m1] | aliasing.cpp:93:12:93:13 | m1 |
|
||||
| aliasing.cpp:93:12:93:13 | m1 | realistic.cpp:41:17:41:17 | o |
|
||||
| aliasing.cpp:105:23:105:24 | pa | aliasing.cpp:158:17:158:20 | ref arg data |
|
||||
| aliasing.cpp:105:23:105:24 | pa | aliasing.cpp:164:17:164:20 | ref arg data |
|
||||
| aliasing.cpp:105:23:105:24 | pa | aliasing.cpp:175:15:175:22 | ref arg & ... |
|
||||
| aliasing.cpp:105:23:105:24 | pa | aliasing.cpp:187:15:187:22 | ref arg & ... |
|
||||
| aliasing.cpp:105:23:105:24 | pa | aliasing.cpp:200:15:200:24 | ref arg & ... |
|
||||
| aliasing.cpp:106:4:106:5 | pa [inner post update] | aliasing.cpp:158:17:158:20 | ref arg data |
|
||||
| aliasing.cpp:106:4:106:5 | pa [inner post update] | aliasing.cpp:164:17:164:20 | ref arg data |
|
||||
| aliasing.cpp:106:4:106:5 | pa [inner post update] | aliasing.cpp:175:15:175:22 | ref arg & ... |
|
||||
| aliasing.cpp:106:4:106:5 | pa [inner post update] | aliasing.cpp:187:15:187:22 | ref arg & ... |
|
||||
| aliasing.cpp:106:4:106:5 | pa [inner post update] | aliasing.cpp:200:15:200:24 | ref arg & ... |
|
||||
| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:105:23:105:24 | pa |
|
||||
| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:106:4:106:5 | pa [inner post update] |
|
||||
| aliasing.cpp:158:15:158:15 | s [post update] [data] | aliasing.cpp:159:9:159:9 | s [data] |
|
||||
| aliasing.cpp:158:17:158:20 | ref arg data | aliasing.cpp:158:15:158:15 | s [post update] [data] |
|
||||
| aliasing.cpp:159:8:159:14 | * ... | realistic.cpp:41:17:41:17 | o |
|
||||
| aliasing.cpp:159:9:159:9 | s [data] | aliasing.cpp:159:11:159:14 | data |
|
||||
| aliasing.cpp:159:11:159:14 | data | aliasing.cpp:159:8:159:14 | * ... |
|
||||
| aliasing.cpp:164:15:164:15 | s [post update] [data] | aliasing.cpp:165:8:165:8 | s [data] |
|
||||
| aliasing.cpp:164:17:164:20 | ref arg data | aliasing.cpp:164:15:164:15 | s [post update] [data] |
|
||||
| aliasing.cpp:165:8:165:8 | s [data] | aliasing.cpp:165:10:165:13 | data |
|
||||
| aliasing.cpp:165:8:165:16 | access to array | realistic.cpp:41:17:41:17 | o |
|
||||
| aliasing.cpp:165:10:165:13 | data | aliasing.cpp:165:8:165:16 | access to array |
|
||||
| aliasing.cpp:175:15:175:22 | ref arg & ... | aliasing.cpp:175:21:175:22 | m1 [inner post update] |
|
||||
| aliasing.cpp:175:16:175:17 | s2 [post update] [s, m1] | aliasing.cpp:176:8:176:9 | s2 [s, m1] |
|
||||
@@ -235,24 +286,33 @@ edges
|
||||
| aliasing.cpp:175:21:175:22 | m1 [inner post update] | aliasing.cpp:175:19:175:19 | s [post update] [m1] |
|
||||
| aliasing.cpp:176:8:176:9 | s2 [s, m1] | aliasing.cpp:176:11:176:11 | s [m1] |
|
||||
| aliasing.cpp:176:11:176:11 | s [m1] | aliasing.cpp:176:13:176:14 | m1 |
|
||||
| aliasing.cpp:176:13:176:14 | m1 | realistic.cpp:41:17:41:17 | o |
|
||||
| aliasing.cpp:187:15:187:22 | ref arg & ... | aliasing.cpp:187:21:187:22 | m1 [inner post update] |
|
||||
| aliasing.cpp:187:16:187:17 | s2 [post update] [s, m1] | aliasing.cpp:189:8:189:11 | s2_2 [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 [inner post update] | aliasing.cpp:187:19:187:19 | s [post update] [m1] |
|
||||
| aliasing.cpp:189:8:189:11 | s2_2 [s, m1] | aliasing.cpp:189:13:189:13 | s [m1] |
|
||||
| aliasing.cpp:189:13:189:13 | s [m1] | aliasing.cpp:189:15:189:16 | m1 |
|
||||
| aliasing.cpp:189:15:189:16 | m1 | realistic.cpp:41:17:41:17 | o |
|
||||
| aliasing.cpp:200:15:200:24 | ref arg & ... | aliasing.cpp:200:23:200:24 | m1 [inner post update] |
|
||||
| aliasing.cpp:200:16:200:18 | ps2 [post update] [s, m1] | aliasing.cpp:201:8:201:10 | ps2 [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 [inner post update] | aliasing.cpp:200:21:200:21 | s [post update] [m1] |
|
||||
| aliasing.cpp:201:8:201:10 | ps2 [s, m1] | aliasing.cpp:201:13:201:13 | s [m1] |
|
||||
| aliasing.cpp:201:13:201:13 | s [m1] | aliasing.cpp:201:15:201:16 | m1 |
|
||||
| aliasing.cpp:201:15:201:16 | m1 | realistic.cpp:41:17:41:17 | o |
|
||||
| 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:7:8:7:13 | access to array | realistic.cpp:41:17:41:17 | o |
|
||||
| arrays.cpp:8:8:8:13 | access to array | realistic.cpp:41:17:41:17 | o |
|
||||
| arrays.cpp:9:8:9:11 | * ... | realistic.cpp:41:17:41:17 | o |
|
||||
| arrays.cpp:10:8:10:15 | * ... | realistic.cpp:41:17:41:17 | o |
|
||||
| arrays.cpp:15:14:15:23 | 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:17:8:17:13 | access to array |
|
||||
| arrays.cpp:16:8:16:13 | access to array | realistic.cpp:41:17:41:17 | o |
|
||||
| arrays.cpp:17:8:17:13 | access to array | realistic.cpp:41:17:41:17 | o |
|
||||
| arrays.cpp:36:3:36:3 | o [post update] [nested, arr, data] | arrays.cpp:37:8:37:8 | o [nested, arr, data] |
|
||||
| arrays.cpp:36:3:36:3 | o [post update] [nested, arr, data] | arrays.cpp:38:8:38:8 | o [nested, arr, data] |
|
||||
| arrays.cpp:36:3:36:17 | access to array [post update] [data] | arrays.cpp:36:12:36:14 | arr [inner post update] [data] |
|
||||
@@ -261,13 +321,22 @@ edges
|
||||
| arrays.cpp:36:12:36:14 | arr [inner post update] [data] | arrays.cpp:36:5:36:10 | nested [post update] [arr, data] |
|
||||
| arrays.cpp:36:26:36:35 | call to user_input | arrays.cpp:36:3:36:37 | ... = ... |
|
||||
| arrays.cpp:37:8:37:8 | o [nested, arr, data] | arrays.cpp:37:10:37:15 | nested [arr, data] |
|
||||
| arrays.cpp:37:8:37:8 | o [post update] [nested, arr, data] | arrays.cpp:38:8:38:8 | o [nested, arr, data] |
|
||||
| arrays.cpp:37:8:37:22 | access to array [data] | arrays.cpp:37:24:37:27 | data |
|
||||
| arrays.cpp:37:8:37:22 | access to array [data] | arrays.cpp:37:24:37:27 | data |
|
||||
| arrays.cpp:37:8:37:22 | access to array [post update] [data] | arrays.cpp:37:17:37:19 | arr [inner post update] [data] |
|
||||
| arrays.cpp:37:10:37:15 | nested [arr, data] | arrays.cpp:37:17:37:19 | arr [data] |
|
||||
| arrays.cpp:37:10:37:15 | nested [post update] [arr, data] | arrays.cpp:37:8:37:8 | o [post update] [nested, arr, data] |
|
||||
| arrays.cpp:37:17:37:19 | arr [data] | arrays.cpp:37:8:37:22 | access to array [data] |
|
||||
| arrays.cpp:37:17:37:19 | arr [inner post update] [data] | arrays.cpp:37:10:37:15 | nested [post update] [arr, data] |
|
||||
| arrays.cpp:37:24:37:27 | data | arrays.cpp:37:24:37:27 | ref arg data |
|
||||
| arrays.cpp:37:24:37:27 | data | realistic.cpp:41:17:41:17 | o |
|
||||
| arrays.cpp:37:24:37:27 | ref arg data | arrays.cpp:37:8:37:22 | access to array [post update] [data] |
|
||||
| arrays.cpp:38:8:38:8 | o [nested, arr, data] | arrays.cpp:38:10:38:15 | nested [arr, data] |
|
||||
| arrays.cpp:38:8:38:22 | access to array [data] | arrays.cpp:38:24:38:27 | data |
|
||||
| arrays.cpp:38:10:38:15 | nested [arr, data] | arrays.cpp:38:17:38:19 | arr [data] |
|
||||
| arrays.cpp:38:17:38:19 | arr [data] | arrays.cpp:38:8:38:22 | access to array [data] |
|
||||
| arrays.cpp:38:24:38:27 | data | realistic.cpp:41:17:41:17 | o |
|
||||
| arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, data] | arrays.cpp:43:8:43:8 | o [indirect, arr, data] |
|
||||
| arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, data] | arrays.cpp:44:8:44:8 | o [indirect, arr, data] |
|
||||
| arrays.cpp:42:3:42:20 | access to array [post update] [data] | arrays.cpp:42:15:42:17 | arr [inner post update] [data] |
|
||||
@@ -276,14 +345,24 @@ edges
|
||||
| arrays.cpp:42:15:42:17 | arr [inner post update] [data] | arrays.cpp:42:5:42:12 | indirect [post update] [arr, data] |
|
||||
| arrays.cpp:42:29:42:38 | call to user_input | arrays.cpp:42:3:42:40 | ... = ... |
|
||||
| arrays.cpp:43:8:43:8 | o [indirect, arr, data] | arrays.cpp:43:10:43:17 | indirect [arr, data] |
|
||||
| arrays.cpp:43:8:43:8 | o [post update] [indirect, arr, data] | arrays.cpp:44:8:44:8 | o [indirect, arr, data] |
|
||||
| arrays.cpp:43:8:43:25 | access to array [data] | arrays.cpp:43:27:43:30 | data |
|
||||
| arrays.cpp:43:8:43:25 | access to array [data] | arrays.cpp:43:27:43:30 | data |
|
||||
| arrays.cpp:43:8:43:25 | access to array [post update] [data] | arrays.cpp:43:20:43:22 | arr [inner post update] [data] |
|
||||
| arrays.cpp:43:10:43:17 | indirect [arr, data] | arrays.cpp:43:20:43:22 | arr [data] |
|
||||
| arrays.cpp:43:10:43:17 | indirect [post update] [arr, data] | arrays.cpp:43:8:43:8 | o [post update] [indirect, arr, data] |
|
||||
| arrays.cpp:43:20:43:22 | arr [data] | arrays.cpp:43:8:43:25 | access to array [data] |
|
||||
| arrays.cpp:43:20:43:22 | arr [inner post update] [data] | arrays.cpp:43:10:43:17 | indirect [post update] [arr, data] |
|
||||
| arrays.cpp:43:27:43:30 | data | arrays.cpp:43:27:43:30 | ref arg data |
|
||||
| arrays.cpp:43:27:43:30 | data | realistic.cpp:41:17:41:17 | o |
|
||||
| arrays.cpp:43:27:43:30 | ref arg data | arrays.cpp:43:8:43:25 | access to array [post update] [data] |
|
||||
| arrays.cpp:44:8:44:8 | o [indirect, arr, data] | arrays.cpp:44:10:44:17 | indirect [arr, data] |
|
||||
| arrays.cpp:44:8:44:25 | access to array [data] | arrays.cpp:44:27:44:30 | data |
|
||||
| arrays.cpp:44:10:44:17 | indirect [arr, data] | arrays.cpp:44:20:44:22 | arr [data] |
|
||||
| arrays.cpp:44:20:44:22 | arr [data] | arrays.cpp:44:8:44:25 | access to array [data] |
|
||||
| arrays.cpp:44:27:44:30 | data | realistic.cpp:41:17:41:17 | o |
|
||||
| by_reference.cpp:11:48:11:52 | value | by_reference.cpp:12:5:12:16 | ... = ... |
|
||||
| by_reference.cpp:12:5:12:5 | s [post update] [a] | by_reference.cpp:11:39:11:39 | s [a] |
|
||||
| by_reference.cpp:12:5:12:16 | ... = ... | by_reference.cpp:12:5:12:5 | s [post update] [a] |
|
||||
| by_reference.cpp:15:26:15:30 | value | by_reference.cpp:16:5:16:19 | ... = ... |
|
||||
| by_reference.cpp:16:5:16:19 | ... = ... | by_reference.cpp:16:5:16:8 | this [post update] [a] |
|
||||
@@ -308,21 +387,30 @@ edges
|
||||
| by_reference.cpp:50:17:50:26 | call to user_input | by_reference.cpp:50:3:50:3 | ref arg s [a] |
|
||||
| by_reference.cpp:51:8:51:8 | s [a] | by_reference.cpp:35:9:35:19 | this [a] |
|
||||
| by_reference.cpp:51:8:51:8 | s [a] | by_reference.cpp:51:10:51:20 | call to getDirectly |
|
||||
| by_reference.cpp:51:10:51:20 | call to getDirectly | realistic.cpp:41:17:41:17 | o |
|
||||
| by_reference.cpp:56:3:56:3 | ref arg s [a] | by_reference.cpp:57:8:57:8 | s [a] |
|
||||
| by_reference.cpp:56:19:56:28 | call to user_input | by_reference.cpp:19:28:19:32 | value |
|
||||
| by_reference.cpp:56:19:56:28 | call to user_input | by_reference.cpp:56:3:56:3 | ref arg s [a] |
|
||||
| by_reference.cpp:57:8:57:8 | s [a] | by_reference.cpp:39:9:39:21 | this [a] |
|
||||
| by_reference.cpp:57:8:57:8 | s [a] | by_reference.cpp:57:10:57:22 | call to getIndirectly |
|
||||
| by_reference.cpp:57:10:57:22 | call to getIndirectly | realistic.cpp:41:17:41:17 | o |
|
||||
| by_reference.cpp:62:3:62:3 | ref arg s [a] | by_reference.cpp:63:8:63:8 | s [a] |
|
||||
| by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:23:34:23:38 | value |
|
||||
| by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:62:3:62:3 | ref arg s [a] |
|
||||
| by_reference.cpp:63:8:63:8 | s [a] | by_reference.cpp:43:9:43:27 | this [a] |
|
||||
| by_reference.cpp:63:8:63:8 | s [a] | by_reference.cpp:63:10:63:28 | call to getThroughNonMember |
|
||||
| by_reference.cpp:63:10:63:28 | call to getThroughNonMember | realistic.cpp:41:17:41:17 | o |
|
||||
| by_reference.cpp:68:17:68:18 | ref arg & ... [a] | by_reference.cpp:69:22:69:23 | & ... [a] |
|
||||
| by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:11:48:11:52 | value |
|
||||
| by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:68:17:68:18 | ref arg & ... [a] |
|
||||
| by_reference.cpp:69:8:69:20 | call to nonMemberGetA | realistic.cpp:41:17:41:17 | o |
|
||||
| by_reference.cpp:69:22:69:23 | & ... [a] | by_reference.cpp:31:46:31:46 | s [a] |
|
||||
| by_reference.cpp:69:22:69:23 | & ... [a] | by_reference.cpp:69:8:69:20 | call to nonMemberGetA |
|
||||
| by_reference.cpp:83:31:83:35 | inner [a] | by_reference.cpp:102:21:102:39 | ref arg & ... [a] |
|
||||
| by_reference.cpp:83:31:83:35 | inner [a] | by_reference.cpp:103:27:103:35 | ref arg inner_ptr [a] |
|
||||
| by_reference.cpp:83:31:83:35 | inner [a] | by_reference.cpp:106:21:106:41 | ref arg & ... [a] |
|
||||
| by_reference.cpp:83:31:83:35 | inner [a] | by_reference.cpp:107:29:107:37 | ref arg inner_ptr [a] |
|
||||
| by_reference.cpp:84:3:84:7 | inner [post update] [a] | by_reference.cpp:83:31:83:35 | inner [a] |
|
||||
| by_reference.cpp:84:3:84:7 | inner [post update] [a] | by_reference.cpp:102:21:102:39 | ref arg & ... [a] |
|
||||
| by_reference.cpp:84:3:84:7 | inner [post update] [a] | by_reference.cpp:103:27:103:35 | ref arg inner_ptr [a] |
|
||||
| by_reference.cpp:84:3:84:7 | inner [post update] [a] | by_reference.cpp:106:21:106:41 | ref arg & ... [a] |
|
||||
@@ -340,8 +428,11 @@ edges
|
||||
| by_reference.cpp:88:3:88:7 | inner [post update] [a] | by_reference.cpp:127:21:127:38 | ref arg * ... [a] |
|
||||
| by_reference.cpp:88:3:88:24 | ... = ... | by_reference.cpp:88:3:88:7 | inner [post update] [a] |
|
||||
| by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:88:3:88:24 | ... = ... |
|
||||
| by_reference.cpp:91:25:91:26 | pa | by_reference.cpp:104:15:104:22 | ref arg & ... |
|
||||
| by_reference.cpp:91:25:91:26 | pa | by_reference.cpp:108:15:108:24 | ref arg & ... |
|
||||
| by_reference.cpp:92:4:92:5 | pa [inner post update] | by_reference.cpp:104:15:104:22 | ref arg & ... |
|
||||
| by_reference.cpp:92:4:92:5 | pa [inner post update] | by_reference.cpp:108:15:108:24 | ref arg & ... |
|
||||
| by_reference.cpp:92:9:92:18 | call to user_input | by_reference.cpp:91:25:91:26 | pa |
|
||||
| by_reference.cpp:92:9:92:18 | call to user_input | by_reference.cpp:92:4:92:5 | pa [inner post update] |
|
||||
| by_reference.cpp:95:25:95:26 | pa | by_reference.cpp:124:21:124:21 | ref arg a |
|
||||
| by_reference.cpp:95:25:95:26 | pa | by_reference.cpp:128:23:128:23 | ref arg a |
|
||||
@@ -364,14 +455,20 @@ edges
|
||||
| by_reference.cpp:108:24:108:24 | a [inner post update] | by_reference.cpp:108:16:108:21 | pouter [post update] [a] |
|
||||
| by_reference.cpp:110:8:110:12 | outer [inner_nested, a] | by_reference.cpp:110:14:110:25 | inner_nested [a] |
|
||||
| by_reference.cpp:110:14:110:25 | inner_nested [a] | by_reference.cpp:110:27:110:27 | a |
|
||||
| by_reference.cpp:110:27:110:27 | a | realistic.cpp:41:17:41:17 | o |
|
||||
| by_reference.cpp:111:8:111:12 | outer [inner_ptr, a] | by_reference.cpp:111:14:111:22 | inner_ptr [a] |
|
||||
| by_reference.cpp:111:14:111:22 | inner_ptr [a] | by_reference.cpp:111:25:111:25 | a |
|
||||
| by_reference.cpp:111:25:111:25 | a | realistic.cpp:41:17:41:17 | o |
|
||||
| by_reference.cpp:112:8:112:12 | outer [a] | by_reference.cpp:112:14:112:14 | a |
|
||||
| by_reference.cpp:112:14:112:14 | a | realistic.cpp:41:17:41:17 | o |
|
||||
| by_reference.cpp:114:8:114:13 | pouter [inner_nested, a] | by_reference.cpp:114:16:114:27 | inner_nested [a] |
|
||||
| by_reference.cpp:114:16:114:27 | inner_nested [a] | by_reference.cpp:114:29:114:29 | a |
|
||||
| by_reference.cpp:114:29:114:29 | a | realistic.cpp:41:17:41:17 | o |
|
||||
| by_reference.cpp:115:8:115:13 | pouter [inner_ptr, a] | by_reference.cpp:115:16:115:24 | inner_ptr [a] |
|
||||
| by_reference.cpp:115:16:115:24 | inner_ptr [a] | by_reference.cpp:115:27:115:27 | a |
|
||||
| by_reference.cpp:115:27:115:27 | a | realistic.cpp:41:17:41:17 | o |
|
||||
| by_reference.cpp:116:8:116:13 | pouter [a] | by_reference.cpp:116:16:116:16 | a |
|
||||
| by_reference.cpp:116:16:116:16 | a | realistic.cpp:41:17:41:17 | o |
|
||||
| by_reference.cpp:122:21:122:25 | outer [post update] [inner_nested, a] | by_reference.cpp:130:8:130:12 | outer [inner_nested, a] |
|
||||
| by_reference.cpp:122:27:122:38 | ref arg inner_nested [a] | by_reference.cpp:122:21:122:25 | outer [post update] [inner_nested, a] |
|
||||
| by_reference.cpp:123:21:123:36 | ref arg * ... [a] | by_reference.cpp:123:28:123:36 | inner_ptr [inner post update] [a] |
|
||||
@@ -388,14 +485,20 @@ edges
|
||||
| by_reference.cpp:128:23:128:23 | ref arg a | by_reference.cpp:128:15:128:20 | pouter [post update] [a] |
|
||||
| by_reference.cpp:130:8:130:12 | outer [inner_nested, a] | by_reference.cpp:130:14:130:25 | inner_nested [a] |
|
||||
| by_reference.cpp:130:14:130:25 | inner_nested [a] | by_reference.cpp:130:27:130:27 | a |
|
||||
| by_reference.cpp:130:27:130:27 | a | realistic.cpp:41:17:41:17 | o |
|
||||
| by_reference.cpp:131:8:131:12 | outer [inner_ptr, a] | by_reference.cpp:131:14:131:22 | inner_ptr [a] |
|
||||
| by_reference.cpp:131:14:131:22 | inner_ptr [a] | by_reference.cpp:131:25:131:25 | a |
|
||||
| by_reference.cpp:131:25:131:25 | a | realistic.cpp:41:17:41:17 | o |
|
||||
| by_reference.cpp:132:8:132:12 | outer [a] | by_reference.cpp:132:14:132:14 | a |
|
||||
| by_reference.cpp:132:14:132:14 | a | realistic.cpp:41:17:41:17 | o |
|
||||
| by_reference.cpp:134:8:134:13 | pouter [inner_nested, a] | by_reference.cpp:134:16:134:27 | inner_nested [a] |
|
||||
| by_reference.cpp:134:16:134:27 | inner_nested [a] | by_reference.cpp:134:29:134:29 | a |
|
||||
| by_reference.cpp:134:29:134:29 | a | realistic.cpp:41:17:41:17 | o |
|
||||
| by_reference.cpp:135:8:135:13 | pouter [inner_ptr, a] | by_reference.cpp:135:16:135:24 | inner_ptr [a] |
|
||||
| by_reference.cpp:135:16:135:24 | inner_ptr [a] | by_reference.cpp:135:27:135:27 | a |
|
||||
| by_reference.cpp:135:27:135:27 | a | realistic.cpp:41:17:41:17 | o |
|
||||
| by_reference.cpp:136:8:136:13 | pouter [a] | by_reference.cpp:136:16:136:16 | a |
|
||||
| by_reference.cpp:136:16:136:16 | a | realistic.cpp:41:17:41:17 | o |
|
||||
| complex.cpp:9:7:9:7 | this [a_] | complex.cpp:9:20:9:21 | this [a_] |
|
||||
| complex.cpp:9:20:9:21 | this [a_] | complex.cpp:9:20:9:21 | a_ |
|
||||
| complex.cpp:10:7:10:7 | this [b_] | complex.cpp:10:20:10:21 | this [b_] |
|
||||
@@ -439,26 +542,31 @@ edges
|
||||
| complex.cpp:65:7:65:8 | b3 [inner, f, a_] | complex.cpp:40:17:40:17 | b [inner, f, a_] |
|
||||
| complex.cpp:65:7:65:8 | b3 [inner, f, b_] | complex.cpp:40:17:40:17 | b [inner, f, b_] |
|
||||
| conflated.cpp:19:19:19:21 | ref arg raw | conflated.cpp:20:8:20:10 | raw |
|
||||
| conflated.cpp:20:8:20:10 | raw | realistic.cpp:41:17:41:17 | o |
|
||||
| conflated.cpp:29:3:29:4 | pa [post update] [x] | conflated.cpp:30:8:30:9 | pa [x] |
|
||||
| conflated.cpp:29:3:29:22 | ... = ... | conflated.cpp:29:3:29:4 | pa [post update] [x] |
|
||||
| conflated.cpp:29:11:29:20 | call to user_input | conflated.cpp:29:3:29:22 | ... = ... |
|
||||
| conflated.cpp:30:8:30:9 | pa [x] | conflated.cpp:30:12:30:12 | x |
|
||||
| conflated.cpp:30:12:30:12 | x | realistic.cpp:41:17:41:17 | o |
|
||||
| conflated.cpp:36:3:36:4 | pa [post update] [x] | conflated.cpp:37:8:37:9 | pa [x] |
|
||||
| conflated.cpp:36:3:36:22 | ... = ... | conflated.cpp:36:3:36:4 | pa [post update] [x] |
|
||||
| conflated.cpp:36:11:36:20 | call to user_input | conflated.cpp:36:3:36:22 | ... = ... |
|
||||
| conflated.cpp:37:8:37:9 | pa [x] | conflated.cpp:37:12:37:12 | x |
|
||||
| conflated.cpp:37:12:37:12 | x | realistic.cpp:41:17:41:17 | o |
|
||||
| conflated.cpp:54:3:54:4 | ll [post update] [next, y] | conflated.cpp:55:8:55:9 | ll [next, y] |
|
||||
| conflated.cpp:54:3:54:28 | ... = ... | conflated.cpp:54:7:54:10 | next [post update] [y] |
|
||||
| conflated.cpp:54:7:54:10 | next [post update] [y] | conflated.cpp:54:3:54:4 | ll [post update] [next, y] |
|
||||
| conflated.cpp:54:17:54:26 | call to user_input | conflated.cpp:54:3:54:28 | ... = ... |
|
||||
| conflated.cpp:55:8:55:9 | ll [next, y] | conflated.cpp:55:12:55:15 | next [y] |
|
||||
| conflated.cpp:55:12:55:15 | next [y] | conflated.cpp:55:18:55:18 | y |
|
||||
| conflated.cpp:55:18:55:18 | y | realistic.cpp:41:17:41:17 | o |
|
||||
| conflated.cpp:60:3:60:4 | ll [post update] [next, y] | conflated.cpp:61:8:61:9 | ll [next, y] |
|
||||
| conflated.cpp:60:3:60:28 | ... = ... | conflated.cpp:60:7:60:10 | next [post update] [y] |
|
||||
| conflated.cpp:60:7:60:10 | next [post update] [y] | conflated.cpp:60:3:60:4 | ll [post update] [next, y] |
|
||||
| conflated.cpp:60:17:60:26 | call to user_input | conflated.cpp:60:3:60:28 | ... = ... |
|
||||
| conflated.cpp:61:8:61:9 | ll [next, y] | conflated.cpp:61:12:61:15 | next [y] |
|
||||
| conflated.cpp:61:12:61:15 | next [y] | conflated.cpp:61:18:61:18 | y |
|
||||
| conflated.cpp:61:18:61:18 | y | realistic.cpp:41:17:41:17 | o |
|
||||
| constructors.cpp:18:9:18:9 | this [a_] | constructors.cpp:18:22:18:23 | this [a_] |
|
||||
| constructors.cpp:18:22:18:23 | this [a_] | constructors.cpp:18:22:18:23 | a_ |
|
||||
| constructors.cpp:19:9:19:9 | this [b_] | constructors.cpp:19:22:19:23 | this [b_] |
|
||||
@@ -492,6 +600,7 @@ edges
|
||||
| qualifiers.cpp:9:21:9:25 | value | qualifiers.cpp:9:30:9:44 | ... = ... |
|
||||
| qualifiers.cpp:9:30:9:44 | ... = ... | qualifiers.cpp:9:30:9:33 | this [post update] [a] |
|
||||
| qualifiers.cpp:12:40:12:44 | value | qualifiers.cpp:12:49:12:64 | ... = ... |
|
||||
| qualifiers.cpp:12:49:12:53 | inner [post update] [a] | qualifiers.cpp:12:27:12:31 | inner [a] |
|
||||
| qualifiers.cpp:12:49:12:64 | ... = ... | qualifiers.cpp:12:49:12:53 | inner [post update] [a] |
|
||||
| qualifiers.cpp:13:42:13:46 | value | qualifiers.cpp:13:51:13:65 | ... = ... |
|
||||
| qualifiers.cpp:13:51:13:55 | inner [post update] [a] | qualifiers.cpp:13:29:13:33 | inner [a] |
|
||||
@@ -502,18 +611,21 @@ edges
|
||||
| qualifiers.cpp:22:27:22:36 | call to user_input | qualifiers.cpp:22:5:22:38 | ... = ... |
|
||||
| qualifiers.cpp:23:10:23:14 | outer [inner, a] | qualifiers.cpp:23:16:23:20 | inner [a] |
|
||||
| qualifiers.cpp:23:16:23:20 | inner [a] | qualifiers.cpp:23:23:23:23 | a |
|
||||
| qualifiers.cpp:23:23:23:23 | a | realistic.cpp:41:17:41:17 | o |
|
||||
| qualifiers.cpp:27:5:27:9 | ref arg outer [inner, a] | qualifiers.cpp:28:10:28:14 | outer [inner, a] |
|
||||
| qualifiers.cpp:27:11:27:18 | ref arg call to getInner [a] | qualifiers.cpp:27:5:27:9 | ref arg outer [inner, a] |
|
||||
| qualifiers.cpp:27:28:27:37 | call to user_input | qualifiers.cpp:9:21:9:25 | value |
|
||||
| qualifiers.cpp:27:28:27:37 | call to user_input | qualifiers.cpp:27:11:27:18 | ref arg call to getInner [a] |
|
||||
| qualifiers.cpp:28:10:28:14 | outer [inner, a] | qualifiers.cpp:28:16:28:20 | inner [a] |
|
||||
| qualifiers.cpp:28:16:28:20 | inner [a] | qualifiers.cpp:28:23:28:23 | a |
|
||||
| qualifiers.cpp:28:23:28:23 | a | realistic.cpp:41:17:41:17 | o |
|
||||
| qualifiers.cpp:32:17:32:21 | ref arg outer [inner, a] | qualifiers.cpp:33:10:33:14 | outer [inner, a] |
|
||||
| qualifiers.cpp:32:23:32:30 | ref arg call to getInner [a] | qualifiers.cpp:32:17:32:21 | ref arg outer [inner, a] |
|
||||
| qualifiers.cpp:32:35:32:44 | call to user_input | qualifiers.cpp:12:40:12:44 | value |
|
||||
| qualifiers.cpp:32:35:32:44 | call to user_input | qualifiers.cpp:32:23:32:30 | ref arg call to getInner [a] |
|
||||
| qualifiers.cpp:33:10:33:14 | outer [inner, a] | qualifiers.cpp:33:16:33:20 | inner [a] |
|
||||
| qualifiers.cpp:33:16:33:20 | inner [a] | qualifiers.cpp:33:23:33:23 | a |
|
||||
| qualifiers.cpp:33:23:33:23 | a | realistic.cpp:41:17:41:17 | o |
|
||||
| qualifiers.cpp:37:19:37:35 | ref arg * ... [a] | qualifiers.cpp:37:26:37:33 | call to getInner [inner post update] [a] |
|
||||
| qualifiers.cpp:37:20:37:24 | ref arg outer [inner, a] | qualifiers.cpp:38:10:38:14 | outer [inner, a] |
|
||||
| qualifiers.cpp:37:26:37:33 | call to getInner [inner post update] [a] | qualifiers.cpp:37:20:37:24 | ref arg outer [inner, a] |
|
||||
@@ -521,6 +633,7 @@ edges
|
||||
| qualifiers.cpp:37:38:37:47 | call to user_input | qualifiers.cpp:37:19:37:35 | ref arg * ... [a] |
|
||||
| qualifiers.cpp:38:10:38:14 | outer [inner, a] | qualifiers.cpp:38:16:38:20 | inner [a] |
|
||||
| qualifiers.cpp:38:16:38:20 | inner [a] | qualifiers.cpp:38:23:38:23 | a |
|
||||
| qualifiers.cpp:38:23:38:23 | a | realistic.cpp:41:17:41:17 | o |
|
||||
| qualifiers.cpp:42:5:42:40 | ... = ... | qualifiers.cpp:42:6:42:22 | * ... [post update] [a] |
|
||||
| qualifiers.cpp:42:6:42:22 | * ... [post update] [a] | qualifiers.cpp:42:13:42:20 | call to getInner [inner post update] [a] |
|
||||
| qualifiers.cpp:42:7:42:11 | ref arg outer [inner, a] | qualifiers.cpp:43:10:43:14 | outer [inner, a] |
|
||||
@@ -528,12 +641,15 @@ edges
|
||||
| qualifiers.cpp:42:29:42:38 | call to user_input | qualifiers.cpp:42:5:42:40 | ... = ... |
|
||||
| qualifiers.cpp:43:10:43:14 | outer [inner, a] | qualifiers.cpp:43:16:43:20 | inner [a] |
|
||||
| qualifiers.cpp:43:16:43:20 | inner [a] | qualifiers.cpp:43:23:43:23 | a |
|
||||
| qualifiers.cpp:43:23:43:23 | a | realistic.cpp:41:17:41:17 | o |
|
||||
| qualifiers.cpp:47:5:47:42 | ... = ... | qualifiers.cpp:47:15:47:22 | call to getInner [post update] [a] |
|
||||
| qualifiers.cpp:47:6:47:11 | ref arg & ... [inner, a] | qualifiers.cpp:48:10:48:14 | outer [inner, a] |
|
||||
| qualifiers.cpp:47:15:47:22 | call to getInner [post update] [a] | qualifiers.cpp:47:6:47:11 | ref arg & ... [inner, a] |
|
||||
| qualifiers.cpp:47:31:47:40 | call to user_input | qualifiers.cpp:47:5:47:42 | ... = ... |
|
||||
| qualifiers.cpp:48:10:48:14 | outer [inner, a] | qualifiers.cpp:48:16:48:20 | inner [a] |
|
||||
| qualifiers.cpp:48:16:48:20 | inner [a] | qualifiers.cpp:48:23:48:23 | a |
|
||||
| qualifiers.cpp:48:23:48:23 | a | realistic.cpp:41:17:41:17 | o |
|
||||
| realistic.cpp:41:17:41:17 | o | realistic.cpp:41:17:41:17 | o |
|
||||
| realistic.cpp:53:9:53:11 | foo [post update] [bar, baz, userInput, bufferLen] | realistic.cpp:61:21:61:23 | foo [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 [inner post update] [baz, userInput, bufferLen] |
|
||||
| realistic.cpp:53:9:53:66 | ... = ... | realistic.cpp:53:25:53:33 | userInput [post update] [bufferLen] |
|
||||
@@ -542,10 +658,19 @@ edges
|
||||
| realistic.cpp:53:25:53:33 | userInput [post update] [bufferLen] | realistic.cpp:53:20:53:22 | baz [post update] [userInput, bufferLen] |
|
||||
| realistic.cpp:53:55:53:64 | call to user_input | realistic.cpp:53:9:53:66 | ... = ... |
|
||||
| realistic.cpp:61:21:61:23 | foo [bar, baz, userInput, bufferLen] | realistic.cpp:61:25:61:27 | bar [baz, userInput, bufferLen] |
|
||||
| realistic.cpp:61:21:61:23 | foo [post update] [bar, baz, userInput, bufferLen] | realistic.cpp:61:21:61:23 | foo [bar, baz, userInput, bufferLen] |
|
||||
| realistic.cpp:61:21:61:30 | access to array [baz, userInput, bufferLen] | realistic.cpp:61:32:61:34 | baz [userInput, bufferLen] |
|
||||
| realistic.cpp:61:21:61:30 | access to array [post update] [baz, userInput, bufferLen] | realistic.cpp:61:25:61:27 | bar [inner post update] [baz, userInput, bufferLen] |
|
||||
| realistic.cpp:61:25:61:27 | bar [baz, userInput, bufferLen] | realistic.cpp:61:21:61:30 | access to array [baz, userInput, bufferLen] |
|
||||
| realistic.cpp:61:25:61:27 | bar [inner post update] [baz, userInput, bufferLen] | realistic.cpp:61:21:61:23 | foo [post update] [bar, baz, userInput, bufferLen] |
|
||||
| realistic.cpp:61:32:61:34 | baz [post update] [userInput, bufferLen] | realistic.cpp:61:21:61:30 | access to array [post update] [baz, userInput, bufferLen] |
|
||||
| realistic.cpp:61:32:61:34 | baz [userInput, bufferLen] | realistic.cpp:61:37:61:45 | userInput [bufferLen] |
|
||||
| realistic.cpp:61:37:61:45 | userInput [bufferLen] | realistic.cpp:61:47:61:55 | bufferLen |
|
||||
| realistic.cpp:61:37:61:45 | userInput [bufferLen] | realistic.cpp:61:47:61:55 | bufferLen |
|
||||
| realistic.cpp:61:37:61:45 | userInput [post update] [bufferLen] | realistic.cpp:61:32:61:34 | baz [post update] [userInput, bufferLen] |
|
||||
| realistic.cpp:61:47:61:55 | bufferLen | realistic.cpp:41:17:41:17 | o |
|
||||
| realistic.cpp:61:47:61:55 | bufferLen | realistic.cpp:61:47:61:55 | ref arg bufferLen |
|
||||
| realistic.cpp:61:47:61:55 | ref arg bufferLen | realistic.cpp:61:37:61:45 | userInput [post update] [bufferLen] |
|
||||
| simple.cpp:18:9:18:9 | this [a_] | simple.cpp:18:22:18:23 | this [a_] |
|
||||
| simple.cpp:18:22:18:23 | this [a_] | simple.cpp:18:22:18:23 | a_ |
|
||||
| simple.cpp:19:9:19:9 | this [b_] | simple.cpp:19:22:19:23 | this [b_] |
|
||||
@@ -593,14 +718,28 @@ edges
|
||||
| simple.cpp:92:5:92:22 | ... = ... | simple.cpp:92:5:92:5 | a [post update] [i] |
|
||||
| simple.cpp:92:11:92:20 | call to user_input | simple.cpp:92:5:92:22 | ... = ... |
|
||||
| simple.cpp:94:10:94:11 | a2 [i] | simple.cpp:94:13:94:13 | i |
|
||||
| struct_init.c:14:24:14:25 | ab [a] | struct_init.c:14:24:14:25 | ab [a] |
|
||||
| struct_init.c:14:24:14:25 | ab [a] | struct_init.c:15:8:15:9 | ab [a] |
|
||||
| struct_init.c:15:8:15:9 | ab [a] | struct_init.c:15:12:15:12 | a |
|
||||
| struct_init.c:15:8:15:9 | ab [a] | struct_init.c:15:12:15:12 | a |
|
||||
| struct_init.c:15:8:15:9 | ab [post update] [a] | struct_init.c:14:24:14:25 | ab [a] |
|
||||
| struct_init.c:15:12:15:12 | a | realistic.cpp:41:17:41:17 | o |
|
||||
| struct_init.c:15:12:15:12 | a | struct_init.c:15:12:15:12 | ref arg a |
|
||||
| struct_init.c:15:12:15:12 | ref arg a | struct_init.c:15:8:15:9 | ab [post update] [a] |
|
||||
| struct_init.c:20:17:20:36 | {...} [a] | struct_init.c:22:8:22:9 | ab [a] |
|
||||
| struct_init.c:20:17:20:36 | {...} [a] | struct_init.c:24:10:24:12 | & ... [a] |
|
||||
| struct_init.c:20:17:20:36 | {...} [a] | struct_init.c:28:5:28:7 | & ... [a] |
|
||||
| struct_init.c:20:20:20:29 | call to user_input | struct_init.c:20:17:20:36 | {...} [a] |
|
||||
| struct_init.c:22:8:22:9 | ab [a] | struct_init.c:22:11:22:11 | a |
|
||||
| struct_init.c:22:8:22:9 | ab [a] | struct_init.c:22:11:22:11 | a |
|
||||
| struct_init.c:22:8:22:9 | ab [post update] [a] | struct_init.c:24:10:24:12 | & ... [a] |
|
||||
| struct_init.c:22:8:22:9 | ab [post update] [a] | struct_init.c:28:5:28:7 | & ... [a] |
|
||||
| struct_init.c:22:11:22:11 | a | realistic.cpp:41:17:41:17 | o |
|
||||
| struct_init.c:22:11:22:11 | a | struct_init.c:22:11:22:11 | ref arg a |
|
||||
| struct_init.c:22:11:22:11 | ref arg a | struct_init.c:22:8:22:9 | ab [post update] [a] |
|
||||
| struct_init.c:24:10:24:12 | & ... [a] | struct_init.c:14:24:14:25 | ab [a] |
|
||||
| struct_init.c:24:10:24:12 | & ... [a] | struct_init.c:24:10:24:12 | ref arg & ... [a] |
|
||||
| struct_init.c:24:10:24:12 | ref arg & ... [a] | struct_init.c:28:5:28:7 | & ... [a] |
|
||||
| struct_init.c:26:23:29:3 | {...} [nestedAB, a] | struct_init.c:31:8:31:12 | outer [nestedAB, a] |
|
||||
| struct_init.c:26:23:29:3 | {...} [nestedAB, a] | struct_init.c:36:11:36:15 | outer [nestedAB, a] |
|
||||
| struct_init.c:26:23:29:3 | {...} [pointerAB, a] | struct_init.c:33:8:33:12 | outer [pointerAB, a] |
|
||||
@@ -608,9 +747,16 @@ edges
|
||||
| struct_init.c:27:7:27:16 | call to user_input | struct_init.c:27:5:27:23 | {...} [a] |
|
||||
| struct_init.c:28:5:28:7 | & ... [a] | struct_init.c:26:23:29:3 | {...} [pointerAB, a] |
|
||||
| struct_init.c:31:8:31:12 | outer [nestedAB, a] | struct_init.c:31:14:31:21 | nestedAB [a] |
|
||||
| struct_init.c:31:8:31:12 | outer [post update] [nestedAB, a] | struct_init.c:36:11:36:15 | outer [nestedAB, a] |
|
||||
| struct_init.c:31:14:31:21 | nestedAB [a] | struct_init.c:31:23:31:23 | a |
|
||||
| struct_init.c:31:14:31:21 | nestedAB [a] | struct_init.c:31:23:31:23 | a |
|
||||
| struct_init.c:31:14:31:21 | nestedAB [post update] [a] | struct_init.c:31:8:31:12 | outer [post update] [nestedAB, a] |
|
||||
| struct_init.c:31:23:31:23 | a | realistic.cpp:41:17:41:17 | o |
|
||||
| struct_init.c:31:23:31:23 | a | struct_init.c:31:23:31:23 | ref arg a |
|
||||
| struct_init.c:31:23:31:23 | ref arg a | struct_init.c:31:14:31:21 | nestedAB [post update] [a] |
|
||||
| struct_init.c:33:8:33:12 | outer [pointerAB, a] | struct_init.c:33:14:33:22 | pointerAB [a] |
|
||||
| struct_init.c:33:14:33:22 | pointerAB [a] | struct_init.c:33:25:33:25 | a |
|
||||
| struct_init.c:33:25:33:25 | a | realistic.cpp:41:17:41:17 | o |
|
||||
| struct_init.c:36:10:36:24 | & ... [a] | struct_init.c:14:24:14:25 | ab [a] |
|
||||
| struct_init.c:36:11:36:15 | outer [nestedAB, a] | struct_init.c:36:17:36:24 | nestedAB [a] |
|
||||
| struct_init.c:36:17:36:24 | nestedAB [a] | struct_init.c:36:10:36:24 | & ... [a] |
|
||||
@@ -674,12 +820,14 @@ nodes
|
||||
| A.cpp:107:16:107:16 | a | semmle.label | a |
|
||||
| A.cpp:120:12:120:13 | c1 [a] | semmle.label | c1 [a] |
|
||||
| A.cpp:120:16:120:16 | a | semmle.label | a |
|
||||
| A.cpp:124:14:124:14 | b [c] | semmle.label | b [c] |
|
||||
| A.cpp:126:5:126:5 | ref arg b [c] | semmle.label | ref arg b [c] |
|
||||
| A.cpp:126:12:126:18 | new | semmle.label | new |
|
||||
| A.cpp:131:8:131:8 | ref arg b [c] | semmle.label | ref arg b [c] |
|
||||
| A.cpp:132:10:132:10 | b [c] | semmle.label | b [c] |
|
||||
| A.cpp:132:13:132:13 | c | semmle.label | c |
|
||||
| A.cpp:140:13:140:13 | b | semmle.label | b |
|
||||
| A.cpp:140:13:140:13 | b [c] | semmle.label | b [c] |
|
||||
| A.cpp:142:7:142:7 | b [post update] [c] | semmle.label | b [post update] [c] |
|
||||
| A.cpp:142:7:142:20 | ... = ... | semmle.label | ... = ... |
|
||||
| A.cpp:142:14:142:20 | new | semmle.label | new |
|
||||
@@ -695,8 +843,12 @@ nodes
|
||||
| A.cpp:151:12:151:24 | call to D [b] | semmle.label | call to D [b] |
|
||||
| A.cpp:151:18:151:18 | b | semmle.label | b |
|
||||
| A.cpp:151:18:151:18 | ref arg b [c] | semmle.label | ref arg b [c] |
|
||||
| A.cpp:152:10:152:10 | d [b, c] | semmle.label | d [b, c] |
|
||||
| A.cpp:152:10:152:10 | d [b] | semmle.label | d [b] |
|
||||
| A.cpp:152:10:152:10 | d [post update] [b, c] | semmle.label | d [post update] [b, c] |
|
||||
| A.cpp:152:13:152:13 | b | semmle.label | b |
|
||||
| A.cpp:152:13:152:13 | b [c] | semmle.label | b [c] |
|
||||
| A.cpp:152:13:152:13 | ref arg b [c] | semmle.label | ref arg b [c] |
|
||||
| A.cpp:153:10:153:10 | d [b, c] | semmle.label | d [b, c] |
|
||||
| A.cpp:153:13:153:13 | b [c] | semmle.label | b [c] |
|
||||
| A.cpp:153:16:153:16 | c | semmle.label | c |
|
||||
@@ -710,15 +862,24 @@ nodes
|
||||
| A.cpp:162:18:162:40 | call to MyList [next, next, head] | semmle.label | call to MyList [next, next, head] |
|
||||
| A.cpp:162:38:162:39 | l2 [next, head] | semmle.label | l2 [next, head] |
|
||||
| A.cpp:165:10:165:11 | l3 [next, next, head] | semmle.label | l3 [next, next, head] |
|
||||
| A.cpp:165:10:165:11 | l3 [post update] [next, next, head] | semmle.label | l3 [post update] [next, next, head] |
|
||||
| A.cpp:165:14:165:17 | next [next, head] | semmle.label | next [next, head] |
|
||||
| A.cpp:165:14:165:17 | next [post update] [next, head] | semmle.label | next [post update] [next, head] |
|
||||
| A.cpp:165:20:165:23 | next [head] | semmle.label | next [head] |
|
||||
| A.cpp:165:20:165:23 | next [post update] [head] | semmle.label | next [post update] [head] |
|
||||
| A.cpp:165:26:165:29 | head | semmle.label | head |
|
||||
| A.cpp:165:26:165:29 | head | semmle.label | head |
|
||||
| A.cpp:165:26:165:29 | ref arg head | semmle.label | ref arg head |
|
||||
| A.cpp:167:44:167:44 | l [next, head] | semmle.label | l [next, head] |
|
||||
| A.cpp:167:44:167:44 | l [next, next, head] | semmle.label | l [next, next, head] |
|
||||
| A.cpp:167:47:167:50 | next [head] | semmle.label | next [head] |
|
||||
| A.cpp:167:47:167:50 | next [next, head] | semmle.label | next [next, head] |
|
||||
| A.cpp:169:12:169:12 | l [head] | semmle.label | l [head] |
|
||||
| A.cpp:169:15:169:18 | head | semmle.label | head |
|
||||
| A.cpp:173:26:173:26 | o | semmle.label | o |
|
||||
| A.cpp:173:26:173:26 | o | semmle.label | o |
|
||||
| A.cpp:173:26:173:26 | o [c] | semmle.label | o [c] |
|
||||
| A.cpp:173:26:173:26 | o [c] | semmle.label | o [c] |
|
||||
| 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] |
|
||||
@@ -829,6 +990,7 @@ nodes
|
||||
| E.cpp:32:10:32:10 | b [buffer] | semmle.label | b [buffer] |
|
||||
| E.cpp:32:13:32:18 | buffer | semmle.label | buffer |
|
||||
| E.cpp:33:18:33:19 | & ... [data, buffer] | semmle.label | & ... [data, buffer] |
|
||||
| aliasing.cpp:8:23:8:23 | s [m1] | semmle.label | s [m1] |
|
||||
| aliasing.cpp:9:3:9:3 | s [post update] [m1] | semmle.label | s [post update] [m1] |
|
||||
| aliasing.cpp:9:3:9:22 | ... = ... | semmle.label | ... = ... |
|
||||
| aliasing.cpp:9:11:9:20 | call to user_input | semmle.label | call to user_input |
|
||||
@@ -854,6 +1016,7 @@ nodes
|
||||
| aliasing.cpp:93:8:93:8 | w [s, m1] | semmle.label | w [s, m1] |
|
||||
| aliasing.cpp:93:10:93:10 | s [m1] | semmle.label | s [m1] |
|
||||
| aliasing.cpp:93:12:93:13 | m1 | semmle.label | m1 |
|
||||
| aliasing.cpp:105:23:105:24 | pa | semmle.label | pa |
|
||||
| aliasing.cpp:106:4:106:5 | pa [inner post update] | semmle.label | pa [inner post update] |
|
||||
| aliasing.cpp:106:9:106:18 | call to user_input | semmle.label | call to user_input |
|
||||
| aliasing.cpp:158:15:158:15 | s [post update] [data] | semmle.label | s [post update] [data] |
|
||||
@@ -902,10 +1065,16 @@ nodes
|
||||
| arrays.cpp:36:12:36:14 | arr [inner post update] [data] | semmle.label | arr [inner post update] [data] |
|
||||
| arrays.cpp:36:26:36:35 | call to user_input | semmle.label | call to user_input |
|
||||
| arrays.cpp:37:8:37:8 | o [nested, arr, data] | semmle.label | o [nested, arr, data] |
|
||||
| arrays.cpp:37:8:37:8 | o [post update] [nested, arr, data] | semmle.label | o [post update] [nested, arr, data] |
|
||||
| arrays.cpp:37:8:37:22 | access to array [data] | semmle.label | access to array [data] |
|
||||
| arrays.cpp:37:8:37:22 | access to array [post update] [data] | semmle.label | access to array [post update] [data] |
|
||||
| arrays.cpp:37:10:37:15 | nested [arr, data] | semmle.label | nested [arr, data] |
|
||||
| arrays.cpp:37:10:37:15 | nested [post update] [arr, data] | semmle.label | nested [post update] [arr, data] |
|
||||
| arrays.cpp:37:17:37:19 | arr [data] | semmle.label | arr [data] |
|
||||
| arrays.cpp:37:17:37:19 | arr [inner post update] [data] | semmle.label | arr [inner post update] [data] |
|
||||
| arrays.cpp:37:24:37:27 | data | semmle.label | data |
|
||||
| arrays.cpp:37:24:37:27 | data | semmle.label | data |
|
||||
| arrays.cpp:37:24:37:27 | ref arg data | semmle.label | ref arg data |
|
||||
| arrays.cpp:38:8:38:8 | o [nested, arr, data] | semmle.label | o [nested, arr, data] |
|
||||
| arrays.cpp:38:8:38:22 | access to array [data] | semmle.label | access to array [data] |
|
||||
| arrays.cpp:38:10:38:15 | nested [arr, data] | semmle.label | nested [arr, data] |
|
||||
@@ -918,15 +1087,22 @@ nodes
|
||||
| arrays.cpp:42:15:42:17 | arr [inner post update] [data] | semmle.label | arr [inner post update] [data] |
|
||||
| arrays.cpp:42:29:42:38 | call to user_input | semmle.label | call to user_input |
|
||||
| arrays.cpp:43:8:43:8 | o [indirect, arr, data] | semmle.label | o [indirect, arr, data] |
|
||||
| arrays.cpp:43:8:43:8 | o [post update] [indirect, arr, data] | semmle.label | o [post update] [indirect, arr, data] |
|
||||
| arrays.cpp:43:8:43:25 | access to array [data] | semmle.label | access to array [data] |
|
||||
| arrays.cpp:43:8:43:25 | access to array [post update] [data] | semmle.label | access to array [post update] [data] |
|
||||
| arrays.cpp:43:10:43:17 | indirect [arr, data] | semmle.label | indirect [arr, data] |
|
||||
| arrays.cpp:43:10:43:17 | indirect [post update] [arr, data] | semmle.label | indirect [post update] [arr, data] |
|
||||
| arrays.cpp:43:20:43:22 | arr [data] | semmle.label | arr [data] |
|
||||
| arrays.cpp:43:20:43:22 | arr [inner post update] [data] | semmle.label | arr [inner post update] [data] |
|
||||
| arrays.cpp:43:27:43:30 | data | semmle.label | data |
|
||||
| arrays.cpp:43:27:43:30 | data | semmle.label | data |
|
||||
| arrays.cpp:43:27:43:30 | ref arg data | semmle.label | ref arg data |
|
||||
| arrays.cpp:44:8:44:8 | o [indirect, arr, data] | semmle.label | o [indirect, arr, data] |
|
||||
| arrays.cpp:44:8:44:25 | access to array [data] | semmle.label | access to array [data] |
|
||||
| arrays.cpp:44:10:44:17 | indirect [arr, data] | semmle.label | indirect [arr, data] |
|
||||
| arrays.cpp:44:20:44:22 | arr [data] | semmle.label | arr [data] |
|
||||
| arrays.cpp:44:27:44:30 | data | semmle.label | data |
|
||||
| by_reference.cpp:11:39:11:39 | s [a] | semmle.label | s [a] |
|
||||
| by_reference.cpp:11:48:11:52 | value | semmle.label | value |
|
||||
| by_reference.cpp:12:5:12:5 | s [post update] [a] | semmle.label | s [post update] [a] |
|
||||
| by_reference.cpp:12:5:12:16 | ... = ... | semmle.label | ... = ... |
|
||||
@@ -967,6 +1143,7 @@ nodes
|
||||
| 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 | & ... [a] | semmle.label | & ... [a] |
|
||||
| by_reference.cpp:83:31:83:35 | inner [a] | semmle.label | inner [a] |
|
||||
| by_reference.cpp:84:3:84:7 | inner [post update] [a] | semmle.label | inner [post update] [a] |
|
||||
| by_reference.cpp:84:3:84:25 | ... = ... | semmle.label | ... = ... |
|
||||
| by_reference.cpp:84:14:84:23 | call to user_input | semmle.label | call to user_input |
|
||||
@@ -974,6 +1151,7 @@ nodes
|
||||
| by_reference.cpp:88:3:88:7 | inner [post update] [a] | semmle.label | inner [post update] [a] |
|
||||
| by_reference.cpp:88:3:88:24 | ... = ... | semmle.label | ... = ... |
|
||||
| by_reference.cpp:88:13:88:22 | call to user_input | semmle.label | call to user_input |
|
||||
| by_reference.cpp:91:25:91:26 | pa | semmle.label | pa |
|
||||
| by_reference.cpp:92:4:92:5 | pa [inner post update] | semmle.label | pa [inner post update] |
|
||||
| by_reference.cpp:92:9:92:18 | call to user_input | semmle.label | call to user_input |
|
||||
| by_reference.cpp:95:25:95:26 | pa | semmle.label | pa |
|
||||
@@ -1141,6 +1319,7 @@ nodes
|
||||
| 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:30:9:44 | ... = ... | semmle.label | ... = ... |
|
||||
| qualifiers.cpp:12:27:12:31 | inner [a] | semmle.label | inner [a] |
|
||||
| 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:49:12:64 | ... = ... | semmle.label | ... = ... |
|
||||
@@ -1189,6 +1368,8 @@ nodes
|
||||
| qualifiers.cpp:48:10:48:14 | outer [inner, a] | semmle.label | outer [inner, a] |
|
||||
| qualifiers.cpp:48:16:48:20 | inner [a] | semmle.label | inner [a] |
|
||||
| qualifiers.cpp:48:23:48:23 | a | semmle.label | a |
|
||||
| realistic.cpp:41:17:41:17 | o | semmle.label | o |
|
||||
| realistic.cpp:41:17:41:17 | o | semmle.label | o |
|
||||
| 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:9:53:66 | ... = ... | semmle.label | ... = ... |
|
||||
@@ -1197,11 +1378,18 @@ nodes
|
||||
| realistic.cpp:53:25:53:33 | userInput [post update] [bufferLen] | semmle.label | userInput [post update] [bufferLen] |
|
||||
| realistic.cpp:53:55:53:64 | call to user_input | semmle.label | call to user_input |
|
||||
| realistic.cpp:61:21:61:23 | foo [bar, baz, userInput, bufferLen] | semmle.label | foo [bar, baz, userInput, bufferLen] |
|
||||
| realistic.cpp:61:21:61:23 | foo [post update] [bar, baz, userInput, bufferLen] | semmle.label | foo [post update] [bar, baz, userInput, bufferLen] |
|
||||
| realistic.cpp:61:21:61:30 | access to array [baz, userInput, bufferLen] | semmle.label | access to array [baz, userInput, bufferLen] |
|
||||
| realistic.cpp:61:21:61:30 | access to array [post update] [baz, userInput, bufferLen] | semmle.label | access to array [post update] [baz, userInput, bufferLen] |
|
||||
| realistic.cpp:61:25:61:27 | bar [baz, userInput, bufferLen] | semmle.label | bar [baz, userInput, bufferLen] |
|
||||
| realistic.cpp:61:25:61:27 | bar [inner post update] [baz, userInput, bufferLen] | semmle.label | bar [inner post update] [baz, userInput, bufferLen] |
|
||||
| realistic.cpp:61:32:61:34 | baz [post update] [userInput, bufferLen] | semmle.label | baz [post update] [userInput, bufferLen] |
|
||||
| realistic.cpp:61:32:61:34 | baz [userInput, bufferLen] | semmle.label | baz [userInput, bufferLen] |
|
||||
| realistic.cpp:61:37:61:45 | userInput [bufferLen] | semmle.label | userInput [bufferLen] |
|
||||
| realistic.cpp:61:37:61:45 | userInput [post update] [bufferLen] | semmle.label | userInput [post update] [bufferLen] |
|
||||
| realistic.cpp:61:47:61:55 | bufferLen | semmle.label | bufferLen |
|
||||
| realistic.cpp:61:47:61:55 | bufferLen | semmle.label | bufferLen |
|
||||
| realistic.cpp:61:47:61:55 | ref arg bufferLen | semmle.label | ref arg bufferLen |
|
||||
| simple.cpp:18:9:18:9 | this [a_] | semmle.label | this [a_] |
|
||||
| simple.cpp:18:22:18:23 | a_ | semmle.label | a_ |
|
||||
| simple.cpp:18:22:18:23 | this [a_] | semmle.label | this [a_] |
|
||||
@@ -1253,21 +1441,33 @@ nodes
|
||||
| simple.cpp:94:10:94:11 | a2 [i] | semmle.label | a2 [i] |
|
||||
| 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 [a] | semmle.label | ab [a] |
|
||||
| struct_init.c:15:8:15:9 | ab [post update] [a] | semmle.label | ab [post update] [a] |
|
||||
| struct_init.c:15:12:15:12 | a | semmle.label | a |
|
||||
| struct_init.c:15:12:15:12 | a | semmle.label | a |
|
||||
| struct_init.c:15:12:15:12 | ref arg a | semmle.label | ref arg a |
|
||||
| struct_init.c:20:17:20:36 | {...} [a] | semmle.label | {...} [a] |
|
||||
| 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 [a] | semmle.label | ab [a] |
|
||||
| struct_init.c:22:8:22:9 | ab [post update] [a] | semmle.label | ab [post update] [a] |
|
||||
| struct_init.c:22:11:22:11 | a | semmle.label | a |
|
||||
| struct_init.c:22:11:22:11 | a | semmle.label | a |
|
||||
| struct_init.c:22:11:22:11 | ref arg a | semmle.label | ref arg a |
|
||||
| struct_init.c:24:10:24:12 | & ... [a] | semmle.label | & ... [a] |
|
||||
| struct_init.c:24:10:24:12 | ref arg & ... [a] | semmle.label | ref arg & ... [a] |
|
||||
| struct_init.c:26:23:29:3 | {...} [nestedAB, a] | semmle.label | {...} [nestedAB, a] |
|
||||
| struct_init.c:26:23:29:3 | {...} [pointerAB, a] | semmle.label | {...} [pointerAB, a] |
|
||||
| struct_init.c:27:5:27:23 | {...} [a] | semmle.label | {...} [a] |
|
||||
| struct_init.c:27:7:27:16 | call to user_input | semmle.label | call to user_input |
|
||||
| struct_init.c:28:5:28:7 | & ... [a] | semmle.label | & ... [a] |
|
||||
| struct_init.c:31:8:31:12 | outer [nestedAB, a] | semmle.label | outer [nestedAB, a] |
|
||||
| struct_init.c:31:8:31:12 | outer [post update] [nestedAB, a] | semmle.label | outer [post update] [nestedAB, a] |
|
||||
| struct_init.c:31:14:31:21 | nestedAB [a] | semmle.label | nestedAB [a] |
|
||||
| struct_init.c:31:14:31:21 | nestedAB [post update] [a] | semmle.label | nestedAB [post update] [a] |
|
||||
| struct_init.c:31:23:31:23 | a | semmle.label | a |
|
||||
| struct_init.c:31:23:31:23 | a | semmle.label | a |
|
||||
| struct_init.c:31:23:31:23 | ref arg a | semmle.label | ref arg a |
|
||||
| struct_init.c:33:8:33:12 | outer [pointerAB, a] | semmle.label | outer [pointerAB, a] |
|
||||
| struct_init.c:33:14:33:22 | pointerAB [a] | semmle.label | pointerAB [a] |
|
||||
| struct_init.c:33:25:33:25 | a | semmle.label | a |
|
||||
@@ -1293,9 +1493,11 @@ subpaths
|
||||
| A.cpp:90:15:90:15 | c | A.cpp:27:17:27:17 | c | A.cpp:27:22:27:25 | this [post update] [c] | A.cpp:90:7:90:8 | ref arg b2 [c] |
|
||||
| A.cpp:126:12:126:18 | new | A.cpp:27:17:27:17 | c | A.cpp:27:22:27:25 | this [post update] [c] | A.cpp:126:5:126:5 | ref arg b [c] |
|
||||
| A.cpp:151:18:151:18 | b | A.cpp:140:13:140:13 | b | A.cpp:143:7:143:10 | this [post update] [b] | A.cpp:151:12:151:24 | call to D [b] |
|
||||
| A.cpp:152:13:152:13 | b [c] | A.cpp:173:26:173:26 | o [c] | A.cpp:173:26:173:26 | o [c] | A.cpp:152:13:152:13 | ref arg b [c] |
|
||||
| A.cpp:160:29:160:29 | b | A.cpp:181:15:181:21 | newHead | A.cpp:183:7:183:10 | this [post update] [head] | A.cpp:160:18:160:60 | call to MyList [head] |
|
||||
| A.cpp:161:38:161:39 | 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 | call to MyList [next, head] |
|
||||
| A.cpp:162:38:162:39 | 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 | call to MyList [next, next, head] |
|
||||
| A.cpp:165:26:165:29 | head | A.cpp:173:26:173:26 | o | A.cpp:173:26:173:26 | o | A.cpp:165:26:165:29 | ref arg head |
|
||||
| B.cpp:7:25:7:25 | e | B.cpp:33:16:33:17 | e1 | B.cpp:35:7:35:10 | this [post update] [elem1] | B.cpp:7:16:7:35 | call to Box1 [elem1] |
|
||||
| B.cpp:8:25:8:26 | 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 | call to Box2 [box1, elem1] |
|
||||
| B.cpp:16:37:16:37 | e | B.cpp:33:26:33:27 | e2 | B.cpp:36:7:36:10 | this [post update] [elem2] | B.cpp:16:16:16:38 | call to Box1 [elem2] |
|
||||
@@ -1304,7 +1506,10 @@ subpaths
|
||||
| D.cpp:22:14:22:20 | call to getBox1 [elem] | D.cpp:10:11:10:17 | this [elem] | D.cpp:10:30:10:33 | elem | D.cpp:22:25:22:31 | call to getElem |
|
||||
| D.cpp:37:21:37:21 | e | D.cpp:11:24:11:24 | e | D.cpp:11:29:11:32 | this [post update] [elem] | D.cpp:37:8:37:10 | ref arg box [elem] |
|
||||
| D.cpp:51:27:51:27 | e | D.cpp:11:24:11:24 | e | D.cpp:11:29:11:32 | this [post update] [elem] | D.cpp:51:8:51:14 | ref arg call to getBox1 [elem] |
|
||||
| arrays.cpp:37:24:37:27 | data | realistic.cpp:41:17:41:17 | o | realistic.cpp:41:17:41:17 | o | arrays.cpp:37:24:37:27 | ref arg data |
|
||||
| arrays.cpp:43:27:43:30 | data | realistic.cpp:41:17:41:17 | o | realistic.cpp:41:17:41:17 | o | arrays.cpp:43:27:43:30 | ref arg data |
|
||||
| by_reference.cpp:20:23:20:27 | value | by_reference.cpp:15:26:15:30 | value | by_reference.cpp:16:5:16:8 | this [post update] [a] | by_reference.cpp:20:5:20:8 | ref arg this [a] |
|
||||
| by_reference.cpp:24:25:24:29 | value | by_reference.cpp:11:48:11:52 | value | by_reference.cpp:11:39:11:39 | s [a] | by_reference.cpp:24:19:24:22 | ref arg this [a] |
|
||||
| by_reference.cpp:24:25:24:29 | value | by_reference.cpp:11:48:11:52 | value | by_reference.cpp:12:5:12:5 | s [post update] [a] | by_reference.cpp:24:19:24:22 | ref arg this [a] |
|
||||
| by_reference.cpp:40:12:40:15 | this [a] | by_reference.cpp:35:9:35:19 | this [a] | by_reference.cpp:36:18:36:18 | a | by_reference.cpp:40:18:40:28 | call to getDirectly |
|
||||
| by_reference.cpp:44:26:44:29 | this [a] | by_reference.cpp:31:46:31:46 | s [a] | by_reference.cpp:32:15:32:15 | a | by_reference.cpp:44:12:44:24 | call to nonMemberGetA |
|
||||
@@ -1314,6 +1519,7 @@ subpaths
|
||||
| by_reference.cpp:57:8:57:8 | s [a] | by_reference.cpp:39:9:39:21 | this [a] | by_reference.cpp:40:18:40:28 | call to getDirectly | by_reference.cpp:57:10:57:22 | call to getIndirectly |
|
||||
| by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:23:34:23:38 | value | by_reference.cpp:24:19:24:22 | ref arg this [a] | by_reference.cpp:62:3:62:3 | ref arg s [a] |
|
||||
| by_reference.cpp:63:8:63:8 | s [a] | by_reference.cpp:43:9:43:27 | this [a] | by_reference.cpp:44:12:44:24 | call to nonMemberGetA | by_reference.cpp:63:10:63:28 | call to getThroughNonMember |
|
||||
| by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:11:48:11:52 | value | by_reference.cpp:11:39:11:39 | s [a] | by_reference.cpp:68:17:68:18 | ref arg & ... [a] |
|
||||
| by_reference.cpp:68:21:68:30 | 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:17:68:18 | ref arg & ... [a] |
|
||||
| by_reference.cpp:69:22:69:23 | & ... [a] | by_reference.cpp:31:46:31:46 | s [a] | by_reference.cpp:32:15:32:15 | a | by_reference.cpp:69:8:69:20 | call to nonMemberGetA |
|
||||
| complex.cpp:42:16:42:16 | f [a_] | complex.cpp:9:7:9:7 | this [a_] | complex.cpp:9:20:9:21 | a_ | complex.cpp:42:18:42:18 | call to a |
|
||||
@@ -1329,9 +1535,11 @@ subpaths
|
||||
| constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:23:13:23:13 | a | constructors.cpp:23:25:23:29 | constructor init of field a_ [post-this] [a_] | constructors.cpp:36:11:36:37 | call to Foo [a_] |
|
||||
| constructors.cpp:36:25:36:34 | call to user_input | constructors.cpp:23:20:23:20 | b | constructors.cpp:23:32:23:36 | constructor init of field b_ [post-this] [b_] | constructors.cpp:36:11:36:37 | call to Foo [b_] |
|
||||
| qualifiers.cpp:27:28:27:37 | call to user_input | qualifiers.cpp:9:21:9:25 | value | qualifiers.cpp:9:30:9:33 | this [post update] [a] | qualifiers.cpp:27:11:27:18 | ref arg call to getInner [a] |
|
||||
| qualifiers.cpp:32:35:32:44 | call to user_input | qualifiers.cpp:12:40:12:44 | value | qualifiers.cpp:12:27:12:31 | inner [a] | qualifiers.cpp:32:23:32:30 | ref arg call to getInner [a] |
|
||||
| qualifiers.cpp:32:35:32:44 | call to user_input | qualifiers.cpp:12:40:12:44 | value | qualifiers.cpp:12:49:12:53 | inner [post update] [a] | qualifiers.cpp:32:23:32:30 | ref arg call to getInner [a] |
|
||||
| qualifiers.cpp:37:38:37:47 | call to user_input | qualifiers.cpp:13:42:13:46 | value | qualifiers.cpp:13:29:13:33 | inner [a] | qualifiers.cpp:37:19:37:35 | ref arg * ... [a] |
|
||||
| qualifiers.cpp:37:38:37:47 | call to user_input | qualifiers.cpp:13:42:13:46 | value | qualifiers.cpp:13:51:13:55 | inner [post update] [a] | qualifiers.cpp:37:19:37:35 | ref arg * ... [a] |
|
||||
| realistic.cpp:61:47:61:55 | bufferLen | realistic.cpp:41:17:41:17 | o | realistic.cpp:41:17:41:17 | o | realistic.cpp:61:47:61:55 | ref arg bufferLen |
|
||||
| simple.cpp:28:10:28:10 | f [a_] | simple.cpp:18:9:18:9 | this [a_] | simple.cpp:18:22:18:23 | a_ | simple.cpp:28:12:28:12 | call to a |
|
||||
| simple.cpp:29:10:29:10 | f [b_] | simple.cpp:19:9:19:9 | this [b_] | simple.cpp:19:22:19:23 | b_ | simple.cpp:29:12:29:12 | call to b |
|
||||
| simple.cpp:39:12:39:21 | call to user_input | simple.cpp:20:19:20:19 | a | simple.cpp:20:24:20:25 | this [post update] [a_] | simple.cpp:39:5:39:5 | ref arg f [a_] |
|
||||
@@ -1339,6 +1547,10 @@ subpaths
|
||||
| simple.cpp:41:12:41:21 | call to user_input | simple.cpp:20:19:20:19 | a | simple.cpp:20:24:20:25 | this [post update] [a_] | simple.cpp:41:5:41:5 | ref arg h [a_] |
|
||||
| simple.cpp:42:12:42:21 | call to user_input | simple.cpp:21:19:21:19 | b | simple.cpp:21:24:21:25 | this [post update] [b_] | simple.cpp:42:5:42:5 | ref arg h [b_] |
|
||||
| simple.cpp:84:14:84:20 | this [f2, f1] | simple.cpp:78:9:78:15 | this [f2, f1] | simple.cpp:79:19:79:20 | f1 | simple.cpp:84:14:84:20 | call to getf2f1 |
|
||||
| struct_init.c:15:12:15:12 | a | realistic.cpp:41:17:41:17 | o | realistic.cpp:41:17:41:17 | o | struct_init.c:15:12:15:12 | ref arg a |
|
||||
| struct_init.c:22:11:22:11 | a | realistic.cpp:41:17:41:17 | o | realistic.cpp:41:17:41:17 | o | struct_init.c:22:11:22:11 | ref arg a |
|
||||
| struct_init.c:24:10:24:12 | & ... [a] | struct_init.c:14:24:14:25 | ab [a] | struct_init.c:14:24:14:25 | ab [a] | struct_init.c:24:10:24:12 | ref arg & ... [a] |
|
||||
| struct_init.c:31:23:31:23 | a | realistic.cpp:41:17:41:17 | o | realistic.cpp:41:17:41:17 | o | struct_init.c:31:23:31:23 | ref arg a |
|
||||
#select
|
||||
| A.cpp:43:10:43:12 | & ... | A.cpp:41:15:41:21 | new | A.cpp:43:10:43:12 | & ... | & ... flows from $@ | A.cpp:41:15:41:21 | new | new |
|
||||
| A.cpp:49:13:49:13 | c | A.cpp:47:12:47:18 | new | A.cpp:49:13:49:13 | c | c flows from $@ | A.cpp:47:12:47:18 | new | new |
|
||||
|
||||
@@ -299,12 +299,15 @@
|
||||
| file://:0:0:0:0 | (unnamed parameter 0) | file://:0:0:0:0 | (unnamed parameter 0) | |
|
||||
| file://:0:0:0:0 | (unnamed parameter 0) | file://:0:0:0:0 | (unnamed parameter 0) | |
|
||||
| file://:0:0:0:0 | (unnamed parameter 0) | file://:0:0:0:0 | (unnamed parameter 0) | |
|
||||
| format.cpp:16:21:16:21 | s | format.cpp:16:21:16:21 | s | |
|
||||
| format.cpp:16:21:16:21 | s | format.cpp:22:22:22:22 | s | |
|
||||
| format.cpp:16:31:16:31 | n | format.cpp:22:25:22:25 | n | |
|
||||
| format.cpp:16:46:16:51 | format | format.cpp:16:46:16:51 | format | |
|
||||
| format.cpp:16:46:16:51 | format | format.cpp:22:28:22:33 | format | |
|
||||
| format.cpp:20:10:20:13 | args | format.cpp:22:36:22:39 | args | |
|
||||
| format.cpp:22:12:22:20 | call to vsnprintf | format.cpp:22:3:22:40 | ... = ... | |
|
||||
| format.cpp:22:12:22:20 | call to vsnprintf | format.cpp:25:9:25:14 | result | |
|
||||
| format.cpp:22:22:22:22 | ref arg s | format.cpp:16:21:16:21 | s | |
|
||||
| format.cpp:50:21:50:24 | {...} | format.cpp:51:17:51:22 | buffer | |
|
||||
| format.cpp:50:21:50:24 | {...} | format.cpp:52:8:52:13 | buffer | |
|
||||
| format.cpp:50:23:50:23 | 0 | format.cpp:50:21:50:24 | {...} | TAINT |
|
||||
@@ -3344,7 +3347,9 @@
|
||||
| smart_pointer.cpp:93:8:93:8 | ref arg q | smart_pointer.cpp:94:8:94:8 | q | |
|
||||
| smart_pointer.cpp:94:8:94:8 | q | smart_pointer.cpp:94:9:94:9 | call to operator-> | |
|
||||
| smart_pointer.cpp:94:8:94:8 | ref arg q | smart_pointer.cpp:86:67:86:67 | q | |
|
||||
| smart_pointer.cpp:97:17:97:18 | pa | smart_pointer.cpp:97:17:97:18 | pa | |
|
||||
| smart_pointer.cpp:97:17:97:18 | pa | smart_pointer.cpp:98:5:98:6 | pa | |
|
||||
| smart_pointer.cpp:98:5:98:6 | pa [post update] | smart_pointer.cpp:97:17:97:18 | pa | |
|
||||
| smart_pointer.cpp:98:5:98:20 | ... = ... | smart_pointer.cpp:98:9:98:9 | x [post update] | |
|
||||
| smart_pointer.cpp:98:13:98:18 | call to source | smart_pointer.cpp:98:5:98:20 | ... = ... | |
|
||||
| smart_pointer.cpp:102:25:102:50 | call to unique_ptr | smart_pointer.cpp:103:11:103:11 | p | |
|
||||
@@ -5923,11 +5928,13 @@
|
||||
| taint.cpp:172:10:172:15 | buffer | taint.cpp:172:10:172:15 | ref arg buffer | TAINT |
|
||||
| taint.cpp:172:10:172:15 | ref arg buffer | taint.cpp:173:8:173:13 | buffer | |
|
||||
| taint.cpp:172:18:172:24 | tainted | taint.cpp:172:10:172:15 | ref arg buffer | TAINT |
|
||||
| taint.cpp:180:19:180:19 | p | taint.cpp:180:19:180:19 | p | |
|
||||
| taint.cpp:180:19:180:19 | p | taint.cpp:181:9:181:9 | p | |
|
||||
| taint.cpp:181:9:181:9 | p | taint.cpp:181:8:181:9 | * ... | TAINT |
|
||||
| taint.cpp:185:11:185:16 | call to source | taint.cpp:186:11:186:11 | x | |
|
||||
| taint.cpp:186:10:186:11 | ref arg & ... | taint.cpp:186:11:186:11 | x [inner post update] | |
|
||||
| taint.cpp:186:11:186:11 | x | taint.cpp:186:10:186:11 | & ... | |
|
||||
| taint.cpp:192:23:192:28 | source | taint.cpp:192:23:192:28 | source | |
|
||||
| taint.cpp:192:23:192:28 | source | taint.cpp:194:13:194:18 | source | |
|
||||
| taint.cpp:193:6:193:6 | x | taint.cpp:194:10:194:10 | x | |
|
||||
| taint.cpp:193:6:193:6 | x | taint.cpp:195:7:195:7 | x | |
|
||||
@@ -5935,6 +5942,7 @@
|
||||
| taint.cpp:194:9:194:10 | ref arg & ... | taint.cpp:194:10:194:10 | x [inner post update] | |
|
||||
| taint.cpp:194:9:194:10 | ref arg & ... | taint.cpp:195:7:195:7 | x | |
|
||||
| taint.cpp:194:10:194:10 | x | taint.cpp:194:9:194:10 | & ... | |
|
||||
| taint.cpp:194:13:194:18 | ref arg source | taint.cpp:192:23:192:28 | source | |
|
||||
| taint.cpp:194:13:194:18 | source | taint.cpp:194:2:194:7 | call to memcpy | TAINT |
|
||||
| taint.cpp:194:13:194:18 | source | taint.cpp:194:9:194:10 | ref arg & ... | TAINT |
|
||||
| taint.cpp:207:6:207:11 | call to source | taint.cpp:207:2:207:13 | ... = ... | |
|
||||
@@ -6040,18 +6048,22 @@
|
||||
| taint.cpp:304:2:304:6 | ... = ... | taint.cpp:304:2:304:2 | a [post update] | |
|
||||
| taint.cpp:304:6:304:6 | b | taint.cpp:302:21:302:21 | a | |
|
||||
| taint.cpp:304:6:304:6 | b | taint.cpp:304:2:304:6 | ... = ... | |
|
||||
| taint.cpp:307:21:307:21 | a | taint.cpp:307:21:307:21 | a | |
|
||||
| taint.cpp:307:21:307:21 | a | taint.cpp:309:3:309:3 | a | |
|
||||
| taint.cpp:307:28:307:28 | b | taint.cpp:309:7:309:7 | b | |
|
||||
| taint.cpp:309:2:309:3 | * ... [post update] | taint.cpp:307:21:307:21 | a | |
|
||||
| taint.cpp:309:2:309:3 | * ... [post update] | taint.cpp:309:3:309:3 | a [inner post update] | |
|
||||
| taint.cpp:309:2:309:7 | ... = ... | taint.cpp:309:2:309:3 | * ... [post update] | |
|
||||
| taint.cpp:309:3:309:3 | a | taint.cpp:309:2:309:3 | * ... | TAINT |
|
||||
| taint.cpp:309:7:309:7 | b | taint.cpp:309:2:309:7 | ... = ... | |
|
||||
| taint.cpp:312:21:312:21 | a | taint.cpp:312:21:312:21 | a | |
|
||||
| taint.cpp:312:21:312:21 | a | taint.cpp:317:3:317:3 | a | |
|
||||
| taint.cpp:312:28:312:28 | b | taint.cpp:316:6:316:6 | b | |
|
||||
| taint.cpp:316:6:316:6 | b | taint.cpp:316:6:316:10 | ... + ... | TAINT |
|
||||
| taint.cpp:316:6:316:10 | ... + ... | taint.cpp:316:2:316:10 | ... = ... | |
|
||||
| taint.cpp:316:6:316:10 | ... + ... | taint.cpp:317:7:317:7 | c | |
|
||||
| taint.cpp:316:10:316:10 | 1 | taint.cpp:316:6:316:10 | ... + ... | TAINT |
|
||||
| taint.cpp:317:2:317:3 | * ... [post update] | taint.cpp:312:21:312:21 | a | |
|
||||
| taint.cpp:317:2:317:3 | * ... [post update] | taint.cpp:317:3:317:3 | a [inner post update] | |
|
||||
| taint.cpp:317:2:317:7 | ... = ... | taint.cpp:317:2:317:3 | * ... [post update] | |
|
||||
| taint.cpp:317:3:317:3 | a | taint.cpp:317:2:317:3 | * ... | TAINT |
|
||||
@@ -6113,6 +6125,7 @@
|
||||
| taint.cpp:347:13:347:13 | d | taint.cpp:347:12:347:13 | & ... | |
|
||||
| taint.cpp:348:14:348:14 | ref arg e | taint.cpp:355:7:355:7 | e | |
|
||||
| taint.cpp:348:17:348:17 | ref arg t | taint.cpp:350:7:350:7 | t | |
|
||||
| taint.cpp:367:24:367:29 | source | taint.cpp:367:24:367:29 | source | |
|
||||
| taint.cpp:367:24:367:29 | source | taint.cpp:371:13:371:18 | source | |
|
||||
| taint.cpp:367:24:367:29 | source | taint.cpp:373:14:373:19 | source | |
|
||||
| taint.cpp:371:6:371:11 | call to strdup | taint.cpp:371:2:371:19 | ... = ... | |
|
||||
@@ -6130,6 +6143,7 @@
|
||||
| taint.cpp:383:6:383:12 | call to strndup | taint.cpp:384:7:384:7 | a | |
|
||||
| taint.cpp:383:14:383:27 | hello, world | taint.cpp:383:6:383:12 | call to strndup | TAINT |
|
||||
| taint.cpp:383:30:383:35 | source | taint.cpp:383:6:383:12 | call to strndup | TAINT |
|
||||
| taint.cpp:387:27:387:32 | source | taint.cpp:387:27:387:32 | source | |
|
||||
| taint.cpp:387:27:387:32 | source | taint.cpp:391:13:391:18 | source | |
|
||||
| taint.cpp:391:6:391:11 | call to wcsdup | taint.cpp:391:2:391:19 | ... = ... | |
|
||||
| taint.cpp:391:6:391:11 | call to wcsdup | taint.cpp:393:7:393:7 | a | |
|
||||
@@ -6137,6 +6151,7 @@
|
||||
| taint.cpp:392:6:392:11 | call to wcsdup | taint.cpp:392:2:392:28 | ... = ... | |
|
||||
| taint.cpp:392:6:392:11 | call to wcsdup | taint.cpp:394:7:394:7 | b | |
|
||||
| taint.cpp:392:13:392:27 | hello, world | taint.cpp:392:6:392:11 | call to wcsdup | TAINT |
|
||||
| taint.cpp:397:25:397:30 | source | taint.cpp:397:25:397:30 | source | |
|
||||
| taint.cpp:397:25:397:30 | source | taint.cpp:401:14:401:19 | source | |
|
||||
| taint.cpp:397:25:397:30 | source | taint.cpp:403:15:403:20 | source | |
|
||||
| taint.cpp:401:6:401:12 | call to strdupa | taint.cpp:401:2:401:20 | ... = ... | |
|
||||
@@ -6239,6 +6254,7 @@
|
||||
| taint.cpp:485:6:485:6 | 0 | taint.cpp:493:7:493:7 | y | |
|
||||
| taint.cpp:490:7:490:7 | ref arg x | taint.cpp:492:7:492:7 | x | |
|
||||
| taint.cpp:490:10:490:10 | ref arg y | taint.cpp:493:7:493:7 | y | |
|
||||
| taint.cpp:502:26:502:32 | source1 | taint.cpp:502:26:502:32 | source1 | |
|
||||
| taint.cpp:502:26:502:32 | source1 | taint.cpp:505:28:505:34 | source1 | |
|
||||
| taint.cpp:503:15:503:21 | 0 | taint.cpp:505:12:505:15 | line | |
|
||||
| taint.cpp:503:15:503:21 | 0 | taint.cpp:507:7:507:10 | line | |
|
||||
@@ -6248,29 +6264,39 @@
|
||||
| taint.cpp:505:12:505:15 | line | taint.cpp:505:11:505:15 | & ... | |
|
||||
| taint.cpp:505:18:505:19 | ref arg & ... | taint.cpp:505:19:505:19 | n [inner post update] | |
|
||||
| taint.cpp:505:19:505:19 | n | taint.cpp:505:18:505:19 | & ... | |
|
||||
| taint.cpp:505:28:505:34 | ref arg source1 | taint.cpp:502:26:502:32 | source1 | |
|
||||
| taint.cpp:505:28:505:34 | source1 | taint.cpp:505:11:505:15 | ref arg & ... | TAINT |
|
||||
| taint.cpp:514:24:514:29 | source | taint.cpp:514:24:514:29 | source | |
|
||||
| taint.cpp:514:24:514:29 | source | taint.cpp:516:27:516:32 | source | |
|
||||
| taint.cpp:515:22:515:29 | ,.-;:_ | taint.cpp:516:35:516:39 | delim | |
|
||||
| taint.cpp:515:22:515:29 | ,.-;:_ | taint.cpp:518:7:518:11 | delim | |
|
||||
| taint.cpp:516:20:516:25 | call to strtok | taint.cpp:517:7:517:15 | tokenized | |
|
||||
| taint.cpp:516:27:516:32 | ref arg source | taint.cpp:514:24:514:29 | source | |
|
||||
| taint.cpp:516:27:516:32 | source | taint.cpp:516:20:516:25 | call to strtok | TAINT |
|
||||
| taint.cpp:525:26:525:28 | ptr | taint.cpp:525:26:525:28 | ptr | |
|
||||
| taint.cpp:525:26:525:28 | ptr | taint.cpp:526:10:526:12 | ptr | |
|
||||
| taint.cpp:525:26:525:28 | ptr | taint.cpp:527:7:527:9 | ptr | |
|
||||
| taint.cpp:525:26:525:28 | ptr | taint.cpp:528:8:528:10 | ptr | |
|
||||
| taint.cpp:525:36:525:41 | source | taint.cpp:526:15:526:20 | source | |
|
||||
| taint.cpp:526:10:526:12 | ptr | taint.cpp:526:2:526:8 | call to _strset | |
|
||||
| taint.cpp:526:10:526:12 | ref arg ptr | taint.cpp:525:26:525:28 | ptr | |
|
||||
| taint.cpp:526:10:526:12 | ref arg ptr | taint.cpp:527:7:527:9 | ptr | |
|
||||
| taint.cpp:526:10:526:12 | ref arg ptr | taint.cpp:528:8:528:10 | ptr | |
|
||||
| taint.cpp:526:15:526:20 | source | taint.cpp:526:2:526:8 | call to _strset | TAINT |
|
||||
| taint.cpp:526:15:526:20 | source | taint.cpp:526:10:526:12 | ref arg ptr | |
|
||||
| taint.cpp:527:7:527:9 | ref arg ptr | taint.cpp:525:26:525:28 | ptr | |
|
||||
| taint.cpp:527:7:527:9 | ref arg ptr | taint.cpp:528:8:528:10 | ptr | |
|
||||
| taint.cpp:528:8:528:10 | ptr | taint.cpp:528:7:528:10 | * ... | TAINT |
|
||||
| taint.cpp:531:26:531:31 | source | taint.cpp:531:26:531:31 | source | |
|
||||
| taint.cpp:531:26:531:31 | source | taint.cpp:532:10:532:15 | source | |
|
||||
| taint.cpp:531:26:531:31 | source | taint.cpp:533:7:533:12 | source | |
|
||||
| taint.cpp:532:10:532:15 | ref arg source | taint.cpp:531:26:531:31 | source | |
|
||||
| taint.cpp:532:10:532:15 | ref arg source | taint.cpp:533:7:533:12 | source | |
|
||||
| taint.cpp:532:10:532:15 | source | taint.cpp:532:2:532:8 | call to _strset | |
|
||||
| taint.cpp:532:18:532:18 | 0 | taint.cpp:532:2:532:8 | call to _strset | TAINT |
|
||||
| taint.cpp:532:18:532:18 | 0 | taint.cpp:532:10:532:15 | ref arg source | |
|
||||
| taint.cpp:533:7:533:12 | ref arg source | taint.cpp:531:26:531:31 | source | |
|
||||
| taint.cpp:540:24:540:29 | source | taint.cpp:540:24:540:29 | source | |
|
||||
| taint.cpp:540:24:540:29 | source | taint.cpp:542:14:542:19 | source | |
|
||||
| taint.cpp:541:6:541:6 | x | taint.cpp:542:11:542:11 | x | |
|
||||
| taint.cpp:541:6:541:6 | x | taint.cpp:543:7:543:7 | x | |
|
||||
@@ -6280,6 +6306,7 @@
|
||||
| taint.cpp:542:11:542:11 | x | taint.cpp:542:10:542:11 | & ... | |
|
||||
| taint.cpp:542:14:542:19 | source | taint.cpp:542:2:542:8 | call to mempcpy | TAINT |
|
||||
| taint.cpp:542:14:542:19 | source | taint.cpp:542:10:542:11 | ref arg & ... | TAINT |
|
||||
| taint.cpp:550:24:550:29 | source | taint.cpp:550:24:550:29 | source | |
|
||||
| taint.cpp:550:24:550:29 | source | taint.cpp:552:16:552:21 | source | |
|
||||
| taint.cpp:551:6:551:9 | dest | taint.cpp:552:10:552:13 | dest | |
|
||||
| taint.cpp:551:6:551:9 | dest | taint.cpp:552:35:552:38 | dest | |
|
||||
@@ -6288,29 +6315,42 @@
|
||||
| taint.cpp:552:10:552:13 | ref arg dest | taint.cpp:553:7:553:10 | dest | |
|
||||
| taint.cpp:552:16:552:21 | source | taint.cpp:552:2:552:8 | call to memccpy | TAINT |
|
||||
| taint.cpp:552:16:552:21 | source | taint.cpp:552:10:552:13 | ref arg dest | TAINT |
|
||||
| taint.cpp:560:24:560:28 | dest1 | taint.cpp:560:24:560:28 | dest1 | |
|
||||
| taint.cpp:560:24:560:28 | dest1 | taint.cpp:561:9:561:13 | dest1 | |
|
||||
| taint.cpp:560:24:560:28 | dest1 | taint.cpp:562:7:562:11 | dest1 | |
|
||||
| taint.cpp:560:37:560:41 | dest2 | taint.cpp:560:37:560:41 | dest2 | |
|
||||
| taint.cpp:560:37:560:41 | dest2 | taint.cpp:564:9:564:13 | dest2 | |
|
||||
| taint.cpp:560:37:560:41 | dest2 | taint.cpp:565:7:565:11 | dest2 | |
|
||||
| taint.cpp:560:50:560:54 | clean | taint.cpp:560:50:560:54 | clean | |
|
||||
| taint.cpp:560:50:560:54 | clean | taint.cpp:564:16:564:20 | clean | |
|
||||
| taint.cpp:560:63:560:68 | source | taint.cpp:560:63:560:68 | source | |
|
||||
| taint.cpp:560:63:560:68 | source | taint.cpp:561:16:561:21 | source | |
|
||||
| taint.cpp:561:9:561:13 | dest1 | taint.cpp:561:2:561:7 | call to strcat | |
|
||||
| taint.cpp:561:9:561:13 | dest1 | taint.cpp:561:9:561:13 | ref arg dest1 | TAINT |
|
||||
| taint.cpp:561:9:561:13 | ref arg dest1 | taint.cpp:560:24:560:28 | dest1 | |
|
||||
| taint.cpp:561:9:561:13 | ref arg dest1 | taint.cpp:562:7:562:11 | dest1 | |
|
||||
| taint.cpp:561:16:561:21 | source | taint.cpp:561:9:561:13 | ref arg dest1 | TAINT |
|
||||
| taint.cpp:562:7:562:11 | ref arg dest1 | taint.cpp:560:24:560:28 | dest1 | |
|
||||
| taint.cpp:564:9:564:13 | dest2 | taint.cpp:564:2:564:7 | call to strcat | |
|
||||
| taint.cpp:564:9:564:13 | dest2 | taint.cpp:564:9:564:13 | ref arg dest2 | TAINT |
|
||||
| taint.cpp:564:9:564:13 | ref arg dest2 | taint.cpp:560:37:560:41 | dest2 | |
|
||||
| taint.cpp:564:9:564:13 | ref arg dest2 | taint.cpp:565:7:565:11 | dest2 | |
|
||||
| taint.cpp:564:16:564:20 | clean | taint.cpp:564:9:564:13 | ref arg dest2 | TAINT |
|
||||
| taint.cpp:565:7:565:11 | ref arg dest2 | taint.cpp:560:37:560:41 | dest2 | |
|
||||
| taint.cpp:572:37:572:41 | dest1 | taint.cpp:572:37:572:41 | dest1 | |
|
||||
| taint.cpp:572:37:572:41 | dest1 | taint.cpp:574:36:574:40 | dest1 | |
|
||||
| taint.cpp:572:37:572:41 | dest1 | taint.cpp:575:7:575:11 | dest1 | |
|
||||
| taint.cpp:572:37:572:41 | dest1 | taint.cpp:576:8:576:12 | dest1 | |
|
||||
| taint.cpp:572:65:572:67 | ptr | taint.cpp:572:65:572:67 | ptr | |
|
||||
| taint.cpp:572:65:572:67 | ptr | taint.cpp:574:43:574:45 | ptr | |
|
||||
| taint.cpp:572:65:572:67 | ptr | taint.cpp:580:43:580:45 | ptr | |
|
||||
| taint.cpp:572:85:572:89 | dest3 | taint.cpp:572:85:572:89 | dest3 | |
|
||||
| taint.cpp:572:85:572:89 | dest3 | taint.cpp:580:36:580:40 | dest3 | |
|
||||
| taint.cpp:572:85:572:89 | dest3 | taint.cpp:581:7:581:11 | dest3 | |
|
||||
| taint.cpp:572:85:572:89 | dest3 | taint.cpp:582:8:582:12 | dest3 | |
|
||||
| taint.cpp:573:32:573:36 | clean | taint.cpp:573:32:573:36 | clean | |
|
||||
| taint.cpp:573:32:573:36 | clean | taint.cpp:580:51:580:55 | clean | |
|
||||
| taint.cpp:573:49:573:54 | source | taint.cpp:573:49:573:54 | source | |
|
||||
| taint.cpp:573:49:573:54 | source | taint.cpp:574:51:574:56 | source | |
|
||||
| taint.cpp:573:61:573:61 | n | taint.cpp:574:48:574:48 | n | |
|
||||
| taint.cpp:573:61:573:61 | n | taint.cpp:580:48:580:48 | n | |
|
||||
@@ -6318,11 +6358,14 @@
|
||||
| taint.cpp:574:25:574:34 | call to _mbsncat_l | taint.cpp:578:8:578:12 | dest2 | |
|
||||
| taint.cpp:574:36:574:40 | dest1 | taint.cpp:574:25:574:34 | call to _mbsncat_l | |
|
||||
| taint.cpp:574:36:574:40 | dest1 | taint.cpp:574:36:574:40 | ref arg dest1 | TAINT |
|
||||
| taint.cpp:574:36:574:40 | ref arg dest1 | taint.cpp:572:37:572:41 | dest1 | |
|
||||
| taint.cpp:574:36:574:40 | ref arg dest1 | taint.cpp:575:7:575:11 | dest1 | |
|
||||
| taint.cpp:574:36:574:40 | ref arg dest1 | taint.cpp:576:8:576:12 | dest1 | |
|
||||
| taint.cpp:574:43:574:45 | ptr | taint.cpp:574:36:574:40 | ref arg dest1 | TAINT |
|
||||
| taint.cpp:574:48:574:48 | n | taint.cpp:574:36:574:40 | ref arg dest1 | TAINT |
|
||||
| taint.cpp:574:51:574:56 | ref arg source | taint.cpp:573:49:573:54 | source | |
|
||||
| taint.cpp:574:51:574:56 | source | taint.cpp:574:36:574:40 | ref arg dest1 | TAINT |
|
||||
| taint.cpp:575:7:575:11 | ref arg dest1 | taint.cpp:572:37:572:41 | dest1 | |
|
||||
| taint.cpp:575:7:575:11 | ref arg dest1 | taint.cpp:576:8:576:12 | dest1 | |
|
||||
| taint.cpp:576:8:576:12 | dest1 | taint.cpp:576:7:576:12 | * ... | TAINT |
|
||||
| taint.cpp:577:7:577:11 | ref arg dest2 | taint.cpp:578:8:578:12 | dest2 | |
|
||||
@@ -6331,15 +6374,19 @@
|
||||
| taint.cpp:580:25:580:34 | call to _mbsncat_l | taint.cpp:584:8:584:12 | dest4 | |
|
||||
| taint.cpp:580:36:580:40 | dest3 | taint.cpp:580:25:580:34 | call to _mbsncat_l | |
|
||||
| taint.cpp:580:36:580:40 | dest3 | taint.cpp:580:36:580:40 | ref arg dest3 | TAINT |
|
||||
| taint.cpp:580:36:580:40 | ref arg dest3 | taint.cpp:572:85:572:89 | dest3 | |
|
||||
| taint.cpp:580:36:580:40 | ref arg dest3 | taint.cpp:581:7:581:11 | dest3 | |
|
||||
| taint.cpp:580:36:580:40 | ref arg dest3 | taint.cpp:582:8:582:12 | dest3 | |
|
||||
| taint.cpp:580:43:580:45 | ptr | taint.cpp:580:36:580:40 | ref arg dest3 | TAINT |
|
||||
| taint.cpp:580:48:580:48 | n | taint.cpp:580:36:580:40 | ref arg dest3 | TAINT |
|
||||
| taint.cpp:580:51:580:55 | clean | taint.cpp:580:36:580:40 | ref arg dest3 | TAINT |
|
||||
| taint.cpp:580:51:580:55 | ref arg clean | taint.cpp:573:32:573:36 | clean | |
|
||||
| taint.cpp:581:7:581:11 | ref arg dest3 | taint.cpp:572:85:572:89 | dest3 | |
|
||||
| taint.cpp:581:7:581:11 | ref arg dest3 | taint.cpp:582:8:582:12 | dest3 | |
|
||||
| taint.cpp:582:8:582:12 | dest3 | taint.cpp:582:7:582:12 | * ... | TAINT |
|
||||
| taint.cpp:583:7:583:11 | ref arg dest4 | taint.cpp:584:8:584:12 | dest4 | |
|
||||
| taint.cpp:584:8:584:12 | dest4 | taint.cpp:584:7:584:12 | * ... | TAINT |
|
||||
| taint.cpp:591:24:591:29 | source | taint.cpp:591:24:591:29 | source | |
|
||||
| taint.cpp:591:24:591:29 | source | taint.cpp:594:29:594:34 | source | |
|
||||
| taint.cpp:592:23:592:30 | ,.-;:_ | taint.cpp:594:37:594:41 | delim | |
|
||||
| taint.cpp:594:9:594:17 | tokenized | taint.cpp:594:9:594:42 | ... = ... | |
|
||||
@@ -6347,6 +6394,7 @@
|
||||
| taint.cpp:594:21:594:26 | call to strsep | taint.cpp:595:10:595:18 | tokenized | |
|
||||
| taint.cpp:594:21:594:26 | call to strsep | taint.cpp:596:11:596:19 | tokenized | |
|
||||
| taint.cpp:594:28:594:34 | & ... | taint.cpp:594:21:594:26 | call to strsep | TAINT |
|
||||
| taint.cpp:594:28:594:34 | ref arg & ... | taint.cpp:591:24:591:29 | source | |
|
||||
| taint.cpp:594:28:594:34 | ref arg & ... | taint.cpp:594:29:594:34 | source | |
|
||||
| taint.cpp:594:28:594:34 | ref arg & ... | taint.cpp:594:29:594:34 | source [inner post update] | |
|
||||
| taint.cpp:594:29:594:34 | source | taint.cpp:594:21:594:26 | call to strsep | TAINT |
|
||||
@@ -6354,45 +6402,63 @@
|
||||
| taint.cpp:594:37:594:41 | delim | taint.cpp:594:21:594:26 | call to strsep | TAINT |
|
||||
| taint.cpp:595:10:595:18 | ref arg tokenized | taint.cpp:596:11:596:19 | tokenized | |
|
||||
| taint.cpp:596:11:596:19 | tokenized | taint.cpp:596:10:596:19 | * ... | TAINT |
|
||||
| taint.cpp:606:25:606:30 | source | taint.cpp:606:25:606:30 | source | |
|
||||
| taint.cpp:606:25:606:30 | source | taint.cpp:607:18:607:23 | source | |
|
||||
| taint.cpp:606:39:606:43 | clean | taint.cpp:606:39:606:43 | clean | |
|
||||
| taint.cpp:606:39:606:43 | clean | taint.cpp:611:18:611:22 | clean | |
|
||||
| taint.cpp:606:82:606:87 | locale | taint.cpp:606:82:606:87 | locale | |
|
||||
| taint.cpp:606:82:606:87 | locale | taint.cpp:607:26:607:31 | locale | |
|
||||
| taint.cpp:606:82:606:87 | locale | taint.cpp:611:25:611:30 | locale | |
|
||||
| taint.cpp:607:10:607:16 | call to _strinc | taint.cpp:606:52:606:56 | dest1 | |
|
||||
| taint.cpp:607:10:607:16 | call to _strinc | taint.cpp:607:2:607:32 | ... = ... | |
|
||||
| taint.cpp:607:10:607:16 | call to _strinc | taint.cpp:608:7:608:11 | dest1 | |
|
||||
| taint.cpp:607:10:607:16 | call to _strinc | taint.cpp:609:8:609:12 | dest1 | |
|
||||
| taint.cpp:607:18:607:23 | source | taint.cpp:607:10:607:16 | call to _strinc | TAINT |
|
||||
| taint.cpp:607:26:607:31 | locale | taint.cpp:607:10:607:16 | call to _strinc | TAINT |
|
||||
| taint.cpp:607:26:607:31 | ref arg locale | taint.cpp:606:82:606:87 | locale | |
|
||||
| taint.cpp:607:26:607:31 | ref arg locale | taint.cpp:611:25:611:30 | locale | |
|
||||
| taint.cpp:608:7:608:11 | ref arg dest1 | taint.cpp:606:52:606:56 | dest1 | |
|
||||
| taint.cpp:608:7:608:11 | ref arg dest1 | taint.cpp:609:8:609:12 | dest1 | |
|
||||
| taint.cpp:609:8:609:12 | dest1 | taint.cpp:609:7:609:12 | * ... | TAINT |
|
||||
| taint.cpp:611:10:611:16 | call to _strinc | taint.cpp:606:65:606:69 | dest2 | |
|
||||
| taint.cpp:611:10:611:16 | call to _strinc | taint.cpp:611:2:611:31 | ... = ... | |
|
||||
| taint.cpp:611:10:611:16 | call to _strinc | taint.cpp:612:7:612:11 | dest2 | |
|
||||
| taint.cpp:611:10:611:16 | call to _strinc | taint.cpp:613:8:613:12 | dest2 | |
|
||||
| taint.cpp:611:18:611:22 | clean | taint.cpp:611:10:611:16 | call to _strinc | TAINT |
|
||||
| taint.cpp:611:25:611:30 | locale | taint.cpp:611:10:611:16 | call to _strinc | TAINT |
|
||||
| taint.cpp:611:25:611:30 | ref arg locale | taint.cpp:606:82:606:87 | locale | |
|
||||
| taint.cpp:612:7:612:11 | ref arg dest2 | taint.cpp:606:65:606:69 | dest2 | |
|
||||
| taint.cpp:612:7:612:11 | ref arg dest2 | taint.cpp:613:8:613:12 | dest2 | |
|
||||
| taint.cpp:613:8:613:12 | dest2 | taint.cpp:613:7:613:12 | * ... | TAINT |
|
||||
| taint.cpp:616:34:616:48 | source_unsigned | taint.cpp:616:34:616:48 | source_unsigned | |
|
||||
| taint.cpp:616:34:616:48 | source_unsigned | taint.cpp:617:26:617:40 | source_unsigned | |
|
||||
| taint.cpp:616:57:616:62 | source | taint.cpp:616:57:616:62 | source | |
|
||||
| taint.cpp:616:57:616:62 | source | taint.cpp:621:40:621:45 | source | |
|
||||
| taint.cpp:617:18:617:24 | call to _mbsinc | taint.cpp:616:80:616:92 | dest_unsigned | |
|
||||
| taint.cpp:617:18:617:24 | call to _mbsinc | taint.cpp:617:2:617:41 | ... = ... | |
|
||||
| taint.cpp:617:18:617:24 | call to _mbsinc | taint.cpp:618:7:618:19 | dest_unsigned | |
|
||||
| taint.cpp:617:18:617:24 | call to _mbsinc | taint.cpp:619:8:619:20 | dest_unsigned | |
|
||||
| taint.cpp:617:26:617:40 | source_unsigned | taint.cpp:617:18:617:24 | call to _mbsinc | TAINT |
|
||||
| taint.cpp:618:7:618:19 | ref arg dest_unsigned | taint.cpp:616:80:616:92 | dest_unsigned | |
|
||||
| taint.cpp:618:7:618:19 | ref arg dest_unsigned | taint.cpp:619:8:619:20 | dest_unsigned | |
|
||||
| taint.cpp:619:8:619:20 | dest_unsigned | taint.cpp:619:7:619:20 | * ... | TAINT |
|
||||
| taint.cpp:621:16:621:22 | call to _mbsinc | taint.cpp:616:101:616:104 | dest | |
|
||||
| taint.cpp:621:16:621:22 | call to _mbsinc | taint.cpp:621:2:621:46 | ... = ... | |
|
||||
| taint.cpp:621:16:621:22 | call to _mbsinc | taint.cpp:622:7:622:10 | dest | |
|
||||
| taint.cpp:621:16:621:22 | call to _mbsinc | taint.cpp:623:8:623:11 | dest | |
|
||||
| taint.cpp:621:40:621:45 | source | taint.cpp:621:16:621:22 | call to _mbsinc | TAINT |
|
||||
| taint.cpp:622:7:622:10 | ref arg dest | taint.cpp:616:101:616:104 | dest | |
|
||||
| taint.cpp:622:7:622:10 | ref arg dest | taint.cpp:623:8:623:11 | dest | |
|
||||
| taint.cpp:623:8:623:11 | dest | taint.cpp:623:7:623:11 | * ... | TAINT |
|
||||
| taint.cpp:626:40:626:45 | source | taint.cpp:626:40:626:45 | source | |
|
||||
| taint.cpp:626:40:626:45 | source | taint.cpp:627:18:627:23 | source | |
|
||||
| taint.cpp:626:40:626:45 | source | taint.cpp:627:31:627:36 | source | |
|
||||
| taint.cpp:626:40:626:45 | source | taint.cpp:633:25:633:30 | source | |
|
||||
| taint.cpp:626:40:626:45 | source | taint.cpp:638:18:638:23 | source | |
|
||||
| taint.cpp:626:63:626:67 | clean | taint.cpp:626:63:626:67 | clean | |
|
||||
| taint.cpp:626:63:626:67 | clean | taint.cpp:633:18:633:22 | clean | |
|
||||
| taint.cpp:626:63:626:67 | clean | taint.cpp:638:26:638:30 | clean | |
|
||||
| taint.cpp:627:10:627:16 | call to _strdec | taint.cpp:626:85:626:89 | dest1 | |
|
||||
| taint.cpp:627:10:627:16 | call to _strdec | taint.cpp:627:2:627:37 | ... = ... | |
|
||||
| taint.cpp:627:10:627:16 | call to _strdec | taint.cpp:628:7:628:11 | dest1 | |
|
||||
| taint.cpp:627:10:627:16 | call to _strdec | taint.cpp:629:8:629:12 | dest1 | |
|
||||
@@ -6400,20 +6466,25 @@
|
||||
| taint.cpp:627:18:627:28 | ... + ... | taint.cpp:627:10:627:16 | call to _strdec | TAINT |
|
||||
| taint.cpp:627:27:627:28 | 12 | taint.cpp:627:18:627:28 | ... + ... | TAINT |
|
||||
| taint.cpp:627:31:627:36 | source | taint.cpp:627:10:627:16 | call to _strdec | TAINT |
|
||||
| taint.cpp:628:7:628:11 | ref arg dest1 | taint.cpp:626:85:626:89 | dest1 | |
|
||||
| taint.cpp:628:7:628:11 | ref arg dest1 | taint.cpp:629:8:629:12 | dest1 | |
|
||||
| taint.cpp:629:8:629:12 | dest1 | taint.cpp:629:7:629:12 | * ... | TAINT |
|
||||
| taint.cpp:633:10:633:16 | call to _strdec | taint.cpp:626:107:626:111 | dest2 | |
|
||||
| taint.cpp:633:10:633:16 | call to _strdec | taint.cpp:633:2:633:31 | ... = ... | |
|
||||
| taint.cpp:633:10:633:16 | call to _strdec | taint.cpp:634:7:634:11 | dest2 | |
|
||||
| taint.cpp:633:10:633:16 | call to _strdec | taint.cpp:635:8:635:12 | dest2 | |
|
||||
| taint.cpp:633:18:633:22 | clean | taint.cpp:633:10:633:16 | call to _strdec | TAINT |
|
||||
| taint.cpp:633:25:633:30 | source | taint.cpp:633:10:633:16 | call to _strdec | TAINT |
|
||||
| taint.cpp:634:7:634:11 | ref arg dest2 | taint.cpp:626:107:626:111 | dest2 | |
|
||||
| taint.cpp:634:7:634:11 | ref arg dest2 | taint.cpp:635:8:635:12 | dest2 | |
|
||||
| taint.cpp:635:8:635:12 | dest2 | taint.cpp:635:7:635:12 | * ... | TAINT |
|
||||
| taint.cpp:638:10:638:16 | call to _strdec | taint.cpp:626:129:626:133 | dest3 | |
|
||||
| taint.cpp:638:10:638:16 | call to _strdec | taint.cpp:638:2:638:31 | ... = ... | |
|
||||
| taint.cpp:638:10:638:16 | call to _strdec | taint.cpp:639:7:639:11 | dest3 | |
|
||||
| taint.cpp:638:10:638:16 | call to _strdec | taint.cpp:640:8:640:12 | dest3 | |
|
||||
| taint.cpp:638:18:638:23 | source | taint.cpp:638:10:638:16 | call to _strdec | TAINT |
|
||||
| taint.cpp:638:26:638:30 | clean | taint.cpp:638:10:638:16 | call to _strdec | TAINT |
|
||||
| taint.cpp:639:7:639:11 | ref arg dest3 | taint.cpp:626:129:626:133 | dest3 | |
|
||||
| taint.cpp:639:7:639:11 | ref arg dest3 | taint.cpp:640:8:640:12 | dest3 | |
|
||||
| taint.cpp:640:8:640:12 | dest3 | taint.cpp:640:7:640:12 | * ... | TAINT |
|
||||
| taint.cpp:647:33:647:38 | source | taint.cpp:650:17:650:22 | source | |
|
||||
@@ -6427,19 +6498,23 @@
|
||||
| taint.cpp:653:6:653:14 | call to _strnextc | taint.cpp:654:7:654:7 | c | |
|
||||
| taint.cpp:653:16:653:17 | | taint.cpp:653:6:653:14 | call to _strnextc | TAINT |
|
||||
| taint.cpp:662:9:662:12 | this | taint.cpp:662:25:662:29 | this | |
|
||||
| taint.cpp:665:33:665:38 | source | taint.cpp:665:33:665:38 | source | |
|
||||
| taint.cpp:665:33:665:38 | source | taint.cpp:667:20:667:25 | source | |
|
||||
| taint.cpp:666:30:666:30 | c | taint.cpp:667:10:667:10 | c | |
|
||||
| taint.cpp:666:30:666:30 | c | taint.cpp:668:8:668:8 | c | |
|
||||
| taint.cpp:667:10:667:10 | ref arg c | taint.cpp:668:8:668:8 | c | |
|
||||
| taint.cpp:667:12:667:15 | call to data | taint.cpp:667:3:667:8 | call to memcpy | |
|
||||
| taint.cpp:667:20:667:25 | ref arg source | taint.cpp:665:33:665:38 | source | |
|
||||
| taint.cpp:667:20:667:25 | source | taint.cpp:667:3:667:8 | call to memcpy | TAINT |
|
||||
| taint.cpp:667:20:667:25 | source | taint.cpp:667:12:667:15 | ref arg call to data | TAINT |
|
||||
| taint.cpp:674:9:674:12 | this | taint.cpp:674:31:674:35 | this | |
|
||||
| taint.cpp:677:35:677:40 | source | taint.cpp:677:35:677:40 | source | |
|
||||
| taint.cpp:677:35:677:40 | source | taint.cpp:679:20:679:25 | source | |
|
||||
| taint.cpp:678:27:678:27 | c | taint.cpp:679:10:679:10 | c | |
|
||||
| taint.cpp:678:27:678:27 | c | taint.cpp:680:8:680:8 | c | |
|
||||
| taint.cpp:679:10:679:10 | ref arg c | taint.cpp:680:8:680:8 | c | |
|
||||
| taint.cpp:679:12:679:15 | call to data | taint.cpp:679:3:679:8 | call to memcpy | |
|
||||
| taint.cpp:679:20:679:25 | ref arg source | taint.cpp:677:35:677:40 | source | |
|
||||
| taint.cpp:679:20:679:25 | source | taint.cpp:679:3:679:8 | call to memcpy | TAINT |
|
||||
| taint.cpp:679:20:679:25 | source | taint.cpp:679:12:679:15 | ref arg call to data | TAINT |
|
||||
| taint.cpp:690:14:690:14 | s | taint.cpp:691:18:691:18 | s | |
|
||||
@@ -7782,6 +7857,7 @@
|
||||
| vector.cpp:413:11:413:16 | call to source | vector.cpp:413:2:413:2 | call to operator* [post update] | TAINT |
|
||||
| vector.cpp:413:11:413:16 | call to source | vector.cpp:413:2:413:18 | ... = ... | |
|
||||
| vector.cpp:414:7:414:9 | ref arg v14 | vector.cpp:415:1:415:1 | v14 | |
|
||||
| vector.cpp:417:33:417:45 | source_string | vector.cpp:417:33:417:45 | source_string | |
|
||||
| vector.cpp:417:33:417:45 | source_string | vector.cpp:421:23:421:35 | source_string | |
|
||||
| vector.cpp:417:33:417:45 | source_string | vector.cpp:428:23:428:35 | source_string | |
|
||||
| vector.cpp:417:33:417:45 | source_string | vector.cpp:442:23:442:35 | source_string | |
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
| file://:0:0:0:0 | __wchar_t * | PointerType | Wchar_t, WideCharType |
|
||||
| file://:0:0:0:0 | __wchar_t * | IteratorByPointer, PointerType | Wchar_t, WideCharType |
|
||||
| file://:0:0:0:0 | const __wchar_t | SpecifiedType | Wchar_t, WideCharType |
|
||||
| file://:0:0:0:0 | wchar_t | Wchar_t, WideCharType | |
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
| file://:0:0:0:0 | wchar_t | Wchar_t, WideCharType | |
|
||||
| file://:0:0:0:0 | wchar_t * | PointerType | CTypedefType, Wchar_t |
|
||||
| file://:0:0:0:0 | wchar_t * | IteratorByPointer, PointerType | CTypedefType, Wchar_t |
|
||||
| ms.c:2:24:2:30 | wchar_t | CTypedefType, Wchar_t | |
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
| char8_t | Char8Type | | | | |
|
||||
| char16_t | Char16Type | | | | |
|
||||
| char32_t | Char32Type | | | | |
|
||||
| char * | CharPointerType | | char | | |
|
||||
| char * | CharPointerType, IteratorByPointer | | char | | |
|
||||
| char *[3] | ArrayType | char * | char * | | |
|
||||
| char *[32] | ArrayType | char * | char * | | |
|
||||
| char *[] | ArrayType | char * | char * | | |
|
||||
@@ -48,7 +48,7 @@
|
||||
| const address | SpecifiedType | | address | | |
|
||||
| const address & | LValueReferenceType | | const address | | |
|
||||
| const char | SpecifiedType | | char | | |
|
||||
| const char * | PointerType | | const char | | |
|
||||
| const char * | IteratorByPointer, PointerType | | const char | | |
|
||||
| const char *[3] | ArrayType | const char * | const char * | | |
|
||||
| const char *[] | ArrayType | const char * | const char * | | |
|
||||
| const char[5] | ArrayType | const char | const char | | |
|
||||
@@ -65,7 +65,7 @@
|
||||
| float | FloatType | | | | |
|
||||
| float[3] | ArrayType | float | float | | |
|
||||
| int | IntType, MicrosoftInt32Type | | | | |
|
||||
| int * | IntPointerType | | int | | |
|
||||
| int * | IntPointerType, IteratorByPointer | | int | | |
|
||||
| int[4] | ArrayType | int | int | | |
|
||||
| int[8] | ArrayType | int | int | | |
|
||||
| int[10] | ArrayType | int | int | | |
|
||||
@@ -90,5 +90,5 @@
|
||||
| unsigned long long | LongLongType | | | | unsigned integral |
|
||||
| unsigned short | ShortType | | | | unsigned integral |
|
||||
| void | VoidType | | | | |
|
||||
| void * | VoidPointerType | | void | | |
|
||||
| void * | IteratorByPointer, VoidPointerType | | void | | |
|
||||
| wchar_t | Wchar_t, WideCharType | | | | |
|
||||
|
||||
@@ -1,47 +1,137 @@
|
||||
edges
|
||||
| test.cpp:26:29:26:29 | b | test.cpp:26:29:26:29 | b |
|
||||
| test.cpp:26:29:26:29 | b | test.cpp:27:2:27:2 | b |
|
||||
| test.cpp:30:34:30:34 | b | test.cpp:31:2:31:2 | b |
|
||||
| test.cpp:34:31:34:31 | b | test.cpp:34:31:34:31 | b |
|
||||
| test.cpp:34:31:34:31 | b | test.cpp:35:2:35:2 | b |
|
||||
| test.cpp:38:35:38:35 | d | test.cpp:38:35:38:35 | d |
|
||||
| test.cpp:38:35:38:35 | d | test.cpp:39:2:39:2 | d |
|
||||
| test.cpp:42:40:42:40 | d | test.cpp:43:2:43:2 | d |
|
||||
| test.cpp:46:37:46:37 | d | test.cpp:46:37:46:37 | d |
|
||||
| test.cpp:46:37:46:37 | d | test.cpp:47:2:47:2 | d |
|
||||
| test.cpp:50:31:50:31 | b | test.cpp:51:11:51:11 | b |
|
||||
| test.cpp:57:19:57:19 | d | test.cpp:26:29:26:29 | b |
|
||||
| test.cpp:57:19:57:19 | d | test.cpp:57:19:57:19 | ref arg d |
|
||||
| test.cpp:57:19:57:19 | ref arg d | test.cpp:58:25:58:25 | d |
|
||||
| test.cpp:57:19:57:19 | ref arg d | test.cpp:59:21:59:21 | d |
|
||||
| test.cpp:57:19:57:19 | ref arg d | test.cpp:61:22:61:22 | d |
|
||||
| test.cpp:57:19:57:19 | ref arg d | test.cpp:62:28:62:28 | d |
|
||||
| test.cpp:57:19:57:19 | ref arg d | test.cpp:63:24:63:24 | d |
|
||||
| test.cpp:57:19:57:19 | ref arg d | test.cpp:95:21:95:21 | d |
|
||||
| test.cpp:58:25:58:25 | d | test.cpp:30:34:30:34 | b |
|
||||
| test.cpp:59:21:59:21 | d | test.cpp:34:31:34:31 | b |
|
||||
| test.cpp:59:21:59:21 | d | test.cpp:59:21:59:21 | ref arg d |
|
||||
| test.cpp:59:21:59:21 | ref arg d | test.cpp:61:22:61:22 | d |
|
||||
| test.cpp:59:21:59:21 | ref arg d | test.cpp:62:28:62:28 | d |
|
||||
| test.cpp:59:21:59:21 | ref arg d | test.cpp:63:24:63:24 | d |
|
||||
| test.cpp:59:21:59:21 | ref arg d | test.cpp:95:21:95:21 | d |
|
||||
| test.cpp:61:22:61:22 | d | test.cpp:38:35:38:35 | d |
|
||||
| test.cpp:61:22:61:22 | d | test.cpp:61:22:61:22 | ref arg d |
|
||||
| test.cpp:61:22:61:22 | ref arg d | test.cpp:62:28:62:28 | d |
|
||||
| test.cpp:61:22:61:22 | ref arg d | test.cpp:63:24:63:24 | d |
|
||||
| test.cpp:61:22:61:22 | ref arg d | test.cpp:95:21:95:21 | d |
|
||||
| test.cpp:62:28:62:28 | d | test.cpp:42:40:42:40 | d |
|
||||
| test.cpp:63:24:63:24 | d | test.cpp:46:37:46:37 | d |
|
||||
| test.cpp:63:24:63:24 | d | test.cpp:63:24:63:24 | ref arg d |
|
||||
| test.cpp:63:24:63:24 | ref arg d | test.cpp:95:21:95:21 | d |
|
||||
| test.cpp:74:19:74:21 | dss | test.cpp:26:29:26:29 | b |
|
||||
| test.cpp:74:19:74:21 | dss | test.cpp:74:19:74:21 | ref arg dss |
|
||||
| test.cpp:74:19:74:21 | ref arg dss | test.cpp:75:25:75:27 | dss |
|
||||
| test.cpp:74:19:74:21 | ref arg dss | test.cpp:76:21:76:23 | dss |
|
||||
| test.cpp:74:19:74:21 | ref arg dss | test.cpp:96:21:96:23 | dss |
|
||||
| test.cpp:75:25:75:27 | dss | test.cpp:30:34:30:34 | b |
|
||||
| test.cpp:76:21:76:23 | dss | test.cpp:34:31:34:31 | b |
|
||||
| test.cpp:76:21:76:23 | dss | test.cpp:76:21:76:23 | ref arg dss |
|
||||
| test.cpp:76:21:76:23 | ref arg dss | test.cpp:96:21:96:23 | dss |
|
||||
| test.cpp:86:19:86:20 | d2 | test.cpp:26:29:26:29 | b |
|
||||
| test.cpp:86:19:86:20 | d2 | test.cpp:86:19:86:20 | ref arg d2 |
|
||||
| test.cpp:86:19:86:20 | ref arg d2 | test.cpp:87:25:87:26 | d2 |
|
||||
| test.cpp:86:19:86:20 | ref arg d2 | test.cpp:88:21:88:22 | d2 |
|
||||
| test.cpp:86:19:86:20 | ref arg d2 | test.cpp:90:22:90:23 | d2 |
|
||||
| test.cpp:86:19:86:20 | ref arg d2 | test.cpp:91:28:91:29 | d2 |
|
||||
| test.cpp:86:19:86:20 | ref arg d2 | test.cpp:92:24:92:25 | d2 |
|
||||
| test.cpp:87:25:87:26 | d2 | test.cpp:30:34:30:34 | b |
|
||||
| test.cpp:88:21:88:22 | d2 | test.cpp:34:31:34:31 | b |
|
||||
| test.cpp:88:21:88:22 | d2 | test.cpp:88:21:88:22 | ref arg d2 |
|
||||
| test.cpp:88:21:88:22 | ref arg d2 | test.cpp:90:22:90:23 | d2 |
|
||||
| test.cpp:88:21:88:22 | ref arg d2 | test.cpp:91:28:91:29 | d2 |
|
||||
| test.cpp:88:21:88:22 | ref arg d2 | test.cpp:92:24:92:25 | d2 |
|
||||
| test.cpp:90:22:90:23 | d2 | test.cpp:38:35:38:35 | d |
|
||||
| test.cpp:90:22:90:23 | d2 | test.cpp:90:22:90:23 | ref arg d2 |
|
||||
| test.cpp:90:22:90:23 | ref arg d2 | test.cpp:91:28:91:29 | d2 |
|
||||
| test.cpp:90:22:90:23 | ref arg d2 | test.cpp:92:24:92:25 | d2 |
|
||||
| test.cpp:91:28:91:29 | d2 | test.cpp:42:40:42:40 | d |
|
||||
| test.cpp:92:24:92:25 | d2 | test.cpp:46:37:46:37 | d |
|
||||
| test.cpp:95:21:95:21 | d | test.cpp:50:31:50:31 | b |
|
||||
| test.cpp:96:21:96:23 | dss | test.cpp:50:31:50:31 | b |
|
||||
nodes
|
||||
| test.cpp:26:29:26:29 | b | semmle.label | b |
|
||||
| test.cpp:26:29:26:29 | b | semmle.label | b |
|
||||
| test.cpp:27:2:27:2 | b | semmle.label | b |
|
||||
| test.cpp:30:34:30:34 | b | semmle.label | b |
|
||||
| test.cpp:31:2:31:2 | b | semmle.label | b |
|
||||
| test.cpp:34:31:34:31 | b | semmle.label | b |
|
||||
| test.cpp:34:31:34:31 | b | semmle.label | b |
|
||||
| test.cpp:35:2:35:2 | b | semmle.label | b |
|
||||
| test.cpp:38:35:38:35 | d | semmle.label | d |
|
||||
| test.cpp:38:35:38:35 | d | semmle.label | d |
|
||||
| test.cpp:39:2:39:2 | d | semmle.label | d |
|
||||
| test.cpp:42:40:42:40 | d | semmle.label | d |
|
||||
| test.cpp:43:2:43:2 | d | semmle.label | d |
|
||||
| test.cpp:46:37:46:37 | d | semmle.label | d |
|
||||
| test.cpp:46:37:46:37 | d | semmle.label | d |
|
||||
| test.cpp:47:2:47:2 | d | semmle.label | d |
|
||||
| test.cpp:50:31:50:31 | b | semmle.label | b |
|
||||
| test.cpp:51:11:51:11 | b | semmle.label | b |
|
||||
| test.cpp:57:19:57:19 | d | semmle.label | d |
|
||||
| test.cpp:57:19:57:19 | ref arg d | semmle.label | ref arg d |
|
||||
| test.cpp:58:25:58:25 | d | semmle.label | d |
|
||||
| test.cpp:59:21:59:21 | d | semmle.label | d |
|
||||
| test.cpp:59:21:59:21 | ref arg d | semmle.label | ref arg d |
|
||||
| test.cpp:61:22:61:22 | d | semmle.label | d |
|
||||
| test.cpp:61:22:61:22 | ref arg d | semmle.label | ref arg d |
|
||||
| test.cpp:62:28:62:28 | d | semmle.label | d |
|
||||
| test.cpp:63:24:63:24 | d | semmle.label | d |
|
||||
| test.cpp:63:24:63:24 | ref arg d | semmle.label | ref arg d |
|
||||
| test.cpp:74:19:74:21 | dss | semmle.label | dss |
|
||||
| test.cpp:74:19:74:21 | ref arg dss | semmle.label | ref arg dss |
|
||||
| test.cpp:75:25:75:27 | dss | semmle.label | dss |
|
||||
| test.cpp:76:21:76:23 | dss | semmle.label | dss |
|
||||
| test.cpp:76:21:76:23 | ref arg dss | semmle.label | ref arg dss |
|
||||
| test.cpp:86:19:86:20 | d2 | semmle.label | d2 |
|
||||
| test.cpp:86:19:86:20 | ref arg d2 | semmle.label | ref arg d2 |
|
||||
| test.cpp:87:25:87:26 | d2 | semmle.label | d2 |
|
||||
| test.cpp:88:21:88:22 | d2 | semmle.label | d2 |
|
||||
| test.cpp:88:21:88:22 | ref arg d2 | semmle.label | ref arg d2 |
|
||||
| test.cpp:90:22:90:23 | d2 | semmle.label | d2 |
|
||||
| test.cpp:90:22:90:23 | ref arg d2 | semmle.label | ref arg d2 |
|
||||
| test.cpp:91:28:91:29 | d2 | semmle.label | d2 |
|
||||
| test.cpp:92:24:92:25 | d2 | semmle.label | d2 |
|
||||
| test.cpp:95:21:95:21 | d | semmle.label | d |
|
||||
| test.cpp:96:21:96:23 | dss | semmle.label | dss |
|
||||
subpaths
|
||||
| test.cpp:57:19:57:19 | d | test.cpp:26:29:26:29 | b | test.cpp:26:29:26:29 | b | test.cpp:57:19:57:19 | ref arg d |
|
||||
| test.cpp:59:21:59:21 | d | test.cpp:34:31:34:31 | b | test.cpp:34:31:34:31 | b | test.cpp:59:21:59:21 | ref arg d |
|
||||
| test.cpp:61:22:61:22 | d | test.cpp:38:35:38:35 | d | test.cpp:38:35:38:35 | d | test.cpp:61:22:61:22 | ref arg d |
|
||||
| test.cpp:63:24:63:24 | d | test.cpp:46:37:46:37 | d | test.cpp:46:37:46:37 | d | test.cpp:63:24:63:24 | ref arg d |
|
||||
| test.cpp:74:19:74:21 | dss | test.cpp:26:29:26:29 | b | test.cpp:26:29:26:29 | b | test.cpp:74:19:74:21 | ref arg dss |
|
||||
| test.cpp:76:21:76:23 | dss | test.cpp:34:31:34:31 | b | test.cpp:34:31:34:31 | b | test.cpp:76:21:76:23 | ref arg dss |
|
||||
| test.cpp:86:19:86:20 | d2 | test.cpp:26:29:26:29 | b | test.cpp:26:29:26:29 | b | test.cpp:86:19:86:20 | ref arg d2 |
|
||||
| test.cpp:88:21:88:22 | d2 | test.cpp:34:31:34:31 | b | test.cpp:34:31:34:31 | b | test.cpp:88:21:88:22 | ref arg d2 |
|
||||
| test.cpp:90:22:90:23 | d2 | test.cpp:38:35:38:35 | d | test.cpp:38:35:38:35 | d | test.cpp:90:22:90:23 | ref arg d2 |
|
||||
#select
|
||||
| test.cpp:27:2:27:2 | b | test.cpp:57:19:57:19 | d | test.cpp:27:2:27:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:57:19:57:19 | d | here |
|
||||
| test.cpp:27:2:27:2 | b | test.cpp:74:19:74:21 | dss | test.cpp:27:2:27:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:74:19:74:21 | dss | here |
|
||||
| test.cpp:27:2:27:2 | b | test.cpp:86:19:86:20 | d2 | test.cpp:27:2:27:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:86:19:86:20 | d2 | here |
|
||||
| test.cpp:31:2:31:2 | b | test.cpp:57:19:57:19 | d | test.cpp:31:2:31:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:57:19:57:19 | d | here |
|
||||
| test.cpp:31:2:31:2 | b | test.cpp:58:25:58:25 | d | test.cpp:31:2:31:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:58:25:58:25 | d | here |
|
||||
| test.cpp:31:2:31:2 | b | test.cpp:74:19:74:21 | dss | test.cpp:31:2:31:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:74:19:74:21 | dss | here |
|
||||
| test.cpp:31:2:31:2 | b | test.cpp:75:25:75:27 | dss | test.cpp:31:2:31:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:75:25:75:27 | dss | here |
|
||||
| test.cpp:31:2:31:2 | b | test.cpp:86:19:86:20 | d2 | test.cpp:31:2:31:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:86:19:86:20 | d2 | here |
|
||||
| test.cpp:31:2:31:2 | b | test.cpp:87:25:87:26 | d2 | test.cpp:31:2:31:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:87:25:87:26 | d2 | here |
|
||||
| test.cpp:35:2:35:2 | b | test.cpp:57:19:57:19 | d | test.cpp:35:2:35:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:57:19:57:19 | d | here |
|
||||
| test.cpp:35:2:35:2 | b | test.cpp:59:21:59:21 | d | test.cpp:35:2:35:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:59:21:59:21 | d | here |
|
||||
| test.cpp:35:2:35:2 | b | test.cpp:74:19:74:21 | dss | test.cpp:35:2:35:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:74:19:74:21 | dss | here |
|
||||
| test.cpp:35:2:35:2 | b | test.cpp:76:21:76:23 | dss | test.cpp:35:2:35:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:76:21:76:23 | dss | here |
|
||||
| test.cpp:35:2:35:2 | b | test.cpp:86:19:86:20 | d2 | test.cpp:35:2:35:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:86:19:86:20 | d2 | here |
|
||||
| test.cpp:35:2:35:2 | b | test.cpp:88:21:88:22 | d2 | test.cpp:35:2:35:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:88:21:88:22 | d2 | here |
|
||||
|
||||
@@ -85,6 +85,9 @@ module Public {
|
||||
/** Holds if this stack contains summary component `c`. */
|
||||
predicate contains(SummaryComponent c) { c = this.drop(_).head() }
|
||||
|
||||
/** Gets the bottom element of this stack. */
|
||||
SummaryComponent bottom() { result = this.drop(this.length() - 1).head() }
|
||||
|
||||
/** Gets a textual representation of this stack. */
|
||||
string toString() {
|
||||
exists(SummaryComponent head, SummaryComponentStack tail |
|
||||
@@ -197,6 +200,8 @@ module Private {
|
||||
or
|
||||
tail.(RequiredSummaryComponentStack).required(TParameterSummaryComponent(_)) and
|
||||
head = thisParam()
|
||||
or
|
||||
derivedFluentFlowPush(_, _, _, head, tail, _)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -210,7 +215,7 @@ module Private {
|
||||
c.propagatesFlow(output, input, preservesValue) and
|
||||
preservesValue = true and
|
||||
isCallbackParameter(input) and
|
||||
isContentOfArgument(output)
|
||||
isContentOfArgument(output, _)
|
||||
or
|
||||
// flow from the receiver of a callback into the instance-parameter
|
||||
exists(SummaryComponentStack s, SummaryComponentStack callbackRef |
|
||||
@@ -222,16 +227,81 @@ module Private {
|
||||
output = TConsSummaryComponentStack(thisParam(), input) and
|
||||
preservesValue = true
|
||||
)
|
||||
or
|
||||
exists(SummaryComponentStack arg, SummaryComponentStack return |
|
||||
derivedFluentFlow(c, input, arg, return, preservesValue)
|
||||
|
|
||||
arg.length() = 1 and
|
||||
output = return
|
||||
or
|
||||
exists(SummaryComponent head, SummaryComponentStack tail |
|
||||
derivedFluentFlowPush(c, input, arg, head, tail, 0) and
|
||||
output = SummaryComponentStack::push(head, tail)
|
||||
)
|
||||
)
|
||||
or
|
||||
// Chain together summaries where values get passed into callbacks along the way
|
||||
exists(SummaryComponentStack mid, boolean preservesValue1, boolean preservesValue2 |
|
||||
c.propagatesFlow(input, mid, preservesValue1) and
|
||||
c.propagatesFlow(mid, output, preservesValue2) and
|
||||
mid.drop(mid.length() - 2) =
|
||||
SummaryComponentStack::push(TParameterSummaryComponent(_),
|
||||
SummaryComponentStack::singleton(TArgumentSummaryComponent(_))) and
|
||||
preservesValue = preservesValue1.booleanAnd(preservesValue2)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `c` has a flow summary from `input` to `arg`, where `arg`
|
||||
* writes to (contents of) the `i`th argument, and `c` has a
|
||||
* value-preserving flow summary from the `i`th argument to a return value
|
||||
* (`return`).
|
||||
*
|
||||
* In such a case, we derive flow from `input` to (contents of) the return
|
||||
* value.
|
||||
*
|
||||
* As an example, this simplifies modeling of fluent methods:
|
||||
* for `StringBuilder.append(x)` with a specified value flow from qualifier to
|
||||
* return value and taint flow from argument 0 to the qualifier, then this
|
||||
* allows us to infer taint flow from argument 0 to the return value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate derivedFluentFlow(
|
||||
SummarizedCallable c, SummaryComponentStack input, SummaryComponentStack arg,
|
||||
SummaryComponentStack return, boolean preservesValue
|
||||
) {
|
||||
exists(int i |
|
||||
summary(c, input, arg, preservesValue) and
|
||||
isContentOfArgument(arg, i) and
|
||||
summary(c, SummaryComponentStack::singleton(TArgumentSummaryComponent(i)), return, true) and
|
||||
return.bottom() = TReturnSummaryComponent(_)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate derivedFluentFlowPush(
|
||||
SummarizedCallable c, SummaryComponentStack input, SummaryComponentStack arg,
|
||||
SummaryComponent head, SummaryComponentStack tail, int i
|
||||
) {
|
||||
derivedFluentFlow(c, input, arg, tail, _) and
|
||||
head = arg.drop(i).head() and
|
||||
i = arg.length() - 2
|
||||
or
|
||||
exists(SummaryComponent head0, SummaryComponentStack tail0 |
|
||||
derivedFluentFlowPush(c, input, arg, head0, tail0, i + 1) and
|
||||
head = arg.drop(i).head() and
|
||||
tail = SummaryComponentStack::push(head0, tail0)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isCallbackParameter(SummaryComponentStack s) {
|
||||
s.head() = TParameterSummaryComponent(_) and exists(s.tail())
|
||||
}
|
||||
|
||||
private predicate isContentOfArgument(SummaryComponentStack s) {
|
||||
s.head() = TContentSummaryComponent(_) and isContentOfArgument(s.tail())
|
||||
private predicate isContentOfArgument(SummaryComponentStack s, int i) {
|
||||
s.head() = TContentSummaryComponent(_) and isContentOfArgument(s.tail(), i)
|
||||
or
|
||||
s = TSingletonSummaryComponentStack(TArgumentSummaryComponent(_))
|
||||
s = TSingletonSummaryComponentStack(TArgumentSummaryComponent(i))
|
||||
}
|
||||
|
||||
private predicate outputState(SummarizedCallable c, SummaryComponentStack s) {
|
||||
@@ -508,9 +578,14 @@ module Private {
|
||||
* node, and back out to `p`.
|
||||
*/
|
||||
predicate summaryAllowParameterReturnInSelf(ParamNode p) {
|
||||
exists(SummarizedCallable c, int i |
|
||||
c.clearsContent(i, _) and
|
||||
p.isParameterOf(c, i)
|
||||
exists(SummarizedCallable c, int i | p.isParameterOf(c, i) |
|
||||
c.clearsContent(i, _)
|
||||
or
|
||||
exists(SummaryComponentStack inputContents, SummaryComponentStack outputContents |
|
||||
summary(c, inputContents, outputContents, _) and
|
||||
inputContents.bottom() = pragma[only_bind_into](TArgumentSummaryComponent(i)) and
|
||||
outputContents.bottom() = pragma[only_bind_into](TArgumentSummaryComponent(i))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -534,22 +609,6 @@ module Private {
|
||||
preservesValue = false and not summary(c, inputContents, outputContents, true)
|
||||
)
|
||||
or
|
||||
// If flow through a method updates a parameter from some input A, and that
|
||||
// parameter also is returned through B, then we'd like a combined flow from A
|
||||
// to B as well. As an example, this simplifies modeling of fluent methods:
|
||||
// for `StringBuilder.append(x)` with a specified value flow from qualifier to
|
||||
// return value and taint flow from argument 0 to the qualifier, then this
|
||||
// allows us to infer taint flow from argument 0 to the return value.
|
||||
succ instanceof ParamNode and
|
||||
summaryPostUpdateNode(pred, succ) and
|
||||
preservesValue = true
|
||||
or
|
||||
// Similarly we would like to chain together summaries where values get passed
|
||||
// into callbacks along the way.
|
||||
pred instanceof ArgNode and
|
||||
summaryPostUpdateNode(succ, pred) and
|
||||
preservesValue = true
|
||||
or
|
||||
exists(SummarizedCallable c, int i |
|
||||
pred.(ParamNode).isParameterOf(c, i) and
|
||||
succ = summaryNode(c, TSummaryNodeClearsContentState(i, _)) and
|
||||
|
||||
@@ -174,32 +174,34 @@ module EntityFramework {
|
||||
}
|
||||
}
|
||||
|
||||
private class RawSqlStringSummarizedCallable extends EFSummarizedCallable {
|
||||
private SummaryComponentStack input_;
|
||||
private SummaryComponentStack output_;
|
||||
private boolean preservesValue_;
|
||||
|
||||
RawSqlStringSummarizedCallable() {
|
||||
private class RawSqlStringConstructorSummarizedCallable extends EFSummarizedCallable {
|
||||
RawSqlStringConstructorSummarizedCallable() {
|
||||
exists(RawSqlStringStruct s |
|
||||
this = s.getAConstructor() and
|
||||
input_ = SummaryComponentStack::argument(0) and
|
||||
this.getNumberOfParameters() > 0 and
|
||||
output_ = SummaryComponentStack::return() and
|
||||
preservesValue_ = false
|
||||
or
|
||||
this = s.getAConversionTo() and
|
||||
input_ = SummaryComponentStack::argument(0) and
|
||||
output_ = SummaryComponentStack::return() and
|
||||
preservesValue_ = false
|
||||
this.getNumberOfParameters() > 0
|
||||
)
|
||||
}
|
||||
|
||||
override predicate propagatesFlow(
|
||||
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
||||
) {
|
||||
input = input_ and
|
||||
output = output_ and
|
||||
preservesValue = preservesValue_
|
||||
input = SummaryComponentStack::argument(0) and
|
||||
output = SummaryComponentStack::return() and
|
||||
preservesValue = false
|
||||
}
|
||||
}
|
||||
|
||||
private class RawSqlStringConversionSummarizedCallable extends EFSummarizedCallable {
|
||||
RawSqlStringConversionSummarizedCallable() {
|
||||
exists(RawSqlStringStruct s | this = s.getAConversionTo())
|
||||
}
|
||||
|
||||
override predicate propagatesFlow(
|
||||
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
||||
) {
|
||||
input = SummaryComponentStack::argument(0) and
|
||||
output = SummaryComponentStack::return() and
|
||||
preservesValue = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace My.Qltest
|
||||
|
||||
void M5()
|
||||
{
|
||||
this.StepFieldSetter(new object());
|
||||
Sink(((D)this.StepFieldSetter(new object()).Field2).Field);
|
||||
Sink(this.Field);
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace My.Qltest
|
||||
Sink(d);
|
||||
}, d1, d2);
|
||||
Sink(d1.Field);
|
||||
Sink(d2.Field2); // SPURIOUS FLOW
|
||||
Sink(d2.Field2);
|
||||
}
|
||||
|
||||
object StepArgRes(object x) { return null; }
|
||||
@@ -120,7 +120,7 @@ namespace My.Qltest
|
||||
|
||||
object StepFieldGetter() => throw null;
|
||||
|
||||
void StepFieldSetter(object value) => throw null;
|
||||
D StepFieldSetter(object value) => throw null;
|
||||
|
||||
object Property { get; set; }
|
||||
|
||||
|
||||
@@ -11,8 +11,12 @@ edges
|
||||
| ExternalFlow.cs:30:13:30:16 | [post] this access [field Field] : Object | ExternalFlow.cs:31:18:31:21 | this access [field Field] : Object |
|
||||
| ExternalFlow.cs:30:26:30:37 | object creation of type Object : Object | ExternalFlow.cs:30:13:30:16 | [post] this access [field Field] : Object |
|
||||
| ExternalFlow.cs:31:18:31:21 | this access [field Field] : Object | ExternalFlow.cs:31:18:31:39 | call to method StepFieldGetter |
|
||||
| ExternalFlow.cs:36:13:36:16 | [post] this access [field Field] : Object | ExternalFlow.cs:37:18:37:21 | this access [field Field] : Object |
|
||||
| ExternalFlow.cs:36:34:36:45 | object creation of type Object : Object | ExternalFlow.cs:36:13:36:16 | [post] this access [field Field] : Object |
|
||||
| ExternalFlow.cs:36:19:36:62 | (...) ... [field Field] : Object | ExternalFlow.cs:36:18:36:69 | access to field Field |
|
||||
| ExternalFlow.cs:36:22:36:25 | [post] this access [field Field] : Object | ExternalFlow.cs:37:18:37:21 | this access [field Field] : Object |
|
||||
| ExternalFlow.cs:36:22:36:55 | call to method StepFieldSetter [field Field2, field Field] : Object | ExternalFlow.cs:36:22:36:62 | access to field Field2 [field Field] : Object |
|
||||
| ExternalFlow.cs:36:22:36:62 | access to field Field2 [field Field] : Object | ExternalFlow.cs:36:19:36:62 | (...) ... [field Field] : Object |
|
||||
| ExternalFlow.cs:36:43:36:54 | object creation of type Object : Object | ExternalFlow.cs:36:22:36:25 | [post] this access [field Field] : Object |
|
||||
| ExternalFlow.cs:36:43:36:54 | object creation of type Object : Object | ExternalFlow.cs:36:22:36:55 | call to method StepFieldSetter [field Field2, field Field] : Object |
|
||||
| ExternalFlow.cs:37:18:37:21 | this access [field Field] : Object | ExternalFlow.cs:37:18:37:27 | access to field Field |
|
||||
| ExternalFlow.cs:42:13:42:16 | [post] this access [property Property] : Object | ExternalFlow.cs:43:18:43:21 | this access [property Property] : Object |
|
||||
| ExternalFlow.cs:42:29:42:40 | object creation of type Object : Object | ExternalFlow.cs:42:13:42:16 | [post] this access [property Property] : Object |
|
||||
@@ -48,10 +52,7 @@ edges
|
||||
| ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | ExternalFlow.cs:98:13:98:14 | [post] access to local variable d1 [field Field] : Object |
|
||||
| ExternalFlow.cs:100:20:100:20 | d : Object | ExternalFlow.cs:102:22:102:22 | access to parameter d |
|
||||
| ExternalFlow.cs:103:16:103:17 | access to local variable d1 [field Field] : Object | ExternalFlow.cs:100:20:100:20 | d : Object |
|
||||
| ExternalFlow.cs:103:16:103:17 | access to local variable d1 [field Field] : Object | ExternalFlow.cs:103:20:103:21 | [post] access to local variable d2 [field Field2] : Object |
|
||||
| ExternalFlow.cs:103:20:103:21 | [post] access to local variable d2 [field Field2] : Object | ExternalFlow.cs:105:18:105:19 | access to local variable d2 [field Field2] : Object |
|
||||
| ExternalFlow.cs:104:18:104:19 | access to local variable d1 [field Field] : Object | ExternalFlow.cs:104:18:104:25 | access to field Field |
|
||||
| ExternalFlow.cs:105:18:105:19 | access to local variable d2 [field Field2] : Object | ExternalFlow.cs:105:18:105:26 | access to field Field2 |
|
||||
nodes
|
||||
| ExternalFlow.cs:9:27:9:38 | object creation of type Object : Object | semmle.label | object creation of type Object : Object |
|
||||
| ExternalFlow.cs:10:18:10:33 | call to method StepArgRes | semmle.label | call to method StepArgRes |
|
||||
@@ -69,8 +70,12 @@ nodes
|
||||
| ExternalFlow.cs:30:26:30:37 | object creation of type Object : Object | semmle.label | object creation of type Object : Object |
|
||||
| ExternalFlow.cs:31:18:31:21 | this access [field Field] : Object | semmle.label | this access [field Field] : Object |
|
||||
| ExternalFlow.cs:31:18:31:39 | call to method StepFieldGetter | semmle.label | call to method StepFieldGetter |
|
||||
| ExternalFlow.cs:36:13:36:16 | [post] this access [field Field] : Object | semmle.label | [post] this access [field Field] : Object |
|
||||
| ExternalFlow.cs:36:34:36:45 | object creation of type Object : Object | semmle.label | object creation of type Object : Object |
|
||||
| ExternalFlow.cs:36:18:36:69 | access to field Field | semmle.label | access to field Field |
|
||||
| ExternalFlow.cs:36:19:36:62 | (...) ... [field Field] : Object | semmle.label | (...) ... [field Field] : Object |
|
||||
| ExternalFlow.cs:36:22:36:25 | [post] this access [field Field] : Object | semmle.label | [post] this access [field Field] : Object |
|
||||
| ExternalFlow.cs:36:22:36:55 | call to method StepFieldSetter [field Field2, field Field] : Object | semmle.label | call to method StepFieldSetter [field Field2, field Field] : Object |
|
||||
| ExternalFlow.cs:36:22:36:62 | access to field Field2 [field Field] : Object | semmle.label | access to field Field2 [field Field] : Object |
|
||||
| ExternalFlow.cs:36:43:36:54 | object creation of type Object : Object | semmle.label | object creation of type Object : Object |
|
||||
| ExternalFlow.cs:37:18:37:21 | this access [field Field] : Object | semmle.label | this access [field Field] : Object |
|
||||
| ExternalFlow.cs:37:18:37:27 | access to field Field | semmle.label | access to field Field |
|
||||
| ExternalFlow.cs:42:13:42:16 | [post] this access [property Property] : Object | semmle.label | [post] this access [property Property] : Object |
|
||||
@@ -116,11 +121,8 @@ nodes
|
||||
| ExternalFlow.cs:100:20:100:20 | d : Object | semmle.label | d : Object |
|
||||
| ExternalFlow.cs:102:22:102:22 | access to parameter d | semmle.label | access to parameter d |
|
||||
| ExternalFlow.cs:103:16:103:17 | access to local variable d1 [field Field] : Object | semmle.label | access to local variable d1 [field Field] : Object |
|
||||
| ExternalFlow.cs:103:20:103:21 | [post] access to local variable d2 [field Field2] : Object | semmle.label | [post] access to local variable d2 [field Field2] : Object |
|
||||
| ExternalFlow.cs:104:18:104:19 | access to local variable d1 [field Field] : Object | semmle.label | access to local variable d1 [field Field] : Object |
|
||||
| ExternalFlow.cs:104:18:104:25 | access to field Field | semmle.label | access to field Field |
|
||||
| ExternalFlow.cs:105:18:105:19 | access to local variable d2 [field Field2] : Object | semmle.label | access to local variable d2 [field Field2] : Object |
|
||||
| ExternalFlow.cs:105:18:105:26 | access to field Field2 | semmle.label | access to field Field2 |
|
||||
subpaths
|
||||
invalidModelRow
|
||||
#select
|
||||
@@ -129,7 +131,8 @@ invalidModelRow
|
||||
| ExternalFlow.cs:18:18:18:24 | access to local variable argOut1 | ExternalFlow.cs:16:30:16:41 | object creation of type Object : Object | ExternalFlow.cs:18:18:18:24 | access to local variable argOut1 | $@ | ExternalFlow.cs:16:30:16:41 | object creation of type Object : Object | object creation of type Object : Object |
|
||||
| ExternalFlow.cs:25:18:25:21 | this access | ExternalFlow.cs:23:27:23:38 | object creation of type Object : Object | ExternalFlow.cs:25:18:25:21 | this access | $@ | ExternalFlow.cs:23:27:23:38 | object creation of type Object : Object | object creation of type Object : Object |
|
||||
| ExternalFlow.cs:31:18:31:39 | call to method StepFieldGetter | ExternalFlow.cs:30:26:30:37 | object creation of type Object : Object | ExternalFlow.cs:31:18:31:39 | call to method StepFieldGetter | $@ | ExternalFlow.cs:30:26:30:37 | object creation of type Object : Object | object creation of type Object : Object |
|
||||
| ExternalFlow.cs:37:18:37:27 | access to field Field | ExternalFlow.cs:36:34:36:45 | object creation of type Object : Object | ExternalFlow.cs:37:18:37:27 | access to field Field | $@ | ExternalFlow.cs:36:34:36:45 | object creation of type Object : Object | object creation of type Object : Object |
|
||||
| ExternalFlow.cs:36:18:36:69 | access to field Field | ExternalFlow.cs:36:43:36:54 | object creation of type Object : Object | ExternalFlow.cs:36:18:36:69 | access to field Field | $@ | ExternalFlow.cs:36:43:36:54 | object creation of type Object : Object | object creation of type Object : Object |
|
||||
| ExternalFlow.cs:37:18:37:27 | access to field Field | ExternalFlow.cs:36:43:36:54 | object creation of type Object : Object | ExternalFlow.cs:37:18:37:27 | access to field Field | $@ | ExternalFlow.cs:36:43:36:54 | object creation of type Object : Object | object creation of type Object : Object |
|
||||
| ExternalFlow.cs:43:18:43:42 | call to method StepPropertyGetter | ExternalFlow.cs:42:29:42:40 | object creation of type Object : Object | ExternalFlow.cs:43:18:43:42 | call to method StepPropertyGetter | $@ | ExternalFlow.cs:42:29:42:40 | object creation of type Object : Object | object creation of type Object : Object |
|
||||
| ExternalFlow.cs:49:18:49:30 | access to property Property | ExternalFlow.cs:48:37:48:48 | object creation of type Object : Object | ExternalFlow.cs:49:18:49:30 | access to property Property | $@ | ExternalFlow.cs:48:37:48:48 | object creation of type Object : Object | object creation of type Object : Object |
|
||||
| ExternalFlow.cs:55:18:55:41 | call to method StepElementGetter | ExternalFlow.cs:54:36:54:47 | object creation of type Object : Object | ExternalFlow.cs:55:18:55:41 | call to method StepElementGetter | $@ | ExternalFlow.cs:54:36:54:47 | object creation of type Object : Object | object creation of type Object : Object |
|
||||
@@ -141,4 +144,3 @@ invalidModelRow
|
||||
| ExternalFlow.cs:92:18:92:18 | (...) ... | ExternalFlow.cs:90:21:90:34 | object creation of type String : String | ExternalFlow.cs:92:18:92:18 | (...) ... | $@ | ExternalFlow.cs:90:21:90:34 | object creation of type String : String | object creation of type String : String |
|
||||
| ExternalFlow.cs:102:22:102:22 | access to parameter d | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | ExternalFlow.cs:102:22:102:22 | access to parameter d | $@ | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | object creation of type Object : Object |
|
||||
| ExternalFlow.cs:104:18:104:25 | access to field Field | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | ExternalFlow.cs:104:18:104:25 | access to field Field | $@ | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | object creation of type Object : Object |
|
||||
| ExternalFlow.cs:105:18:105:26 | access to field Field2 | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | ExternalFlow.cs:105:18:105:26 | access to field Field2 | $@ | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | object creation of type Object : Object |
|
||||
|
||||
@@ -17,6 +17,7 @@ class SummaryModelTest extends SummaryModelCsv {
|
||||
"My.Qltest;D;false;StepArgQual;(System.Object);;Argument[0];Argument[-1];taint",
|
||||
"My.Qltest;D;false;StepFieldGetter;();;Field[My.Qltest.D.Field] of Argument[-1];ReturnValue;value",
|
||||
"My.Qltest;D;false;StepFieldSetter;(System.Object);;Argument[0];Field[My.Qltest.D.Field] of Argument[-1];value",
|
||||
"My.Qltest;D;false;StepFieldSetter;(System.Object);;Argument[-1];Field[My.Qltest.D.Field2] of ReturnValue;value",
|
||||
"My.Qltest;D;false;StepPropertyGetter;();;Property[My.Qltest.D.Property] of Argument[-1];ReturnValue;value",
|
||||
"My.Qltest;D;false;StepPropertySetter;(System.Object);;Argument[0];Property[My.Qltest.D.Property] of Argument[-1];value",
|
||||
"My.Qltest;D;false;StepElementGetter;();;Element of Argument[-1];ReturnValue;value",
|
||||
|
||||
@@ -403,6 +403,7 @@
|
||||
| LocalDataFlow.cs:235:9:235:14 | access to local variable sink36 | LocalDataFlow.cs:235:9:235:33 | call to method AppendLine |
|
||||
| LocalDataFlow.cs:235:9:235:14 | access to local variable sink36 | LocalDataFlow.cs:236:15:236:20 | access to local variable sink36 |
|
||||
| LocalDataFlow.cs:235:27:235:32 | access to local variable sink35 | LocalDataFlow.cs:235:9:235:14 | [post] access to local variable sink36 |
|
||||
| LocalDataFlow.cs:235:27:235:32 | access to local variable sink35 | LocalDataFlow.cs:235:9:235:33 | call to method AppendLine |
|
||||
| LocalDataFlow.cs:239:13:239:51 | SSA def(nonSink10) | LocalDataFlow.cs:240:15:240:23 | access to local variable nonSink10 |
|
||||
| LocalDataFlow.cs:239:25:239:51 | object creation of type StringBuilder | LocalDataFlow.cs:239:13:239:51 | SSA def(nonSink10) |
|
||||
| LocalDataFlow.cs:239:43:239:50 | access to local variable nonSink0 | LocalDataFlow.cs:239:25:239:51 | object creation of type StringBuilder |
|
||||
@@ -419,6 +420,7 @@
|
||||
| LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | LocalDataFlow.cs:243:9:243:38 | call to method AppendLine |
|
||||
| LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | LocalDataFlow.cs:244:15:244:23 | access to local variable nonSink10 |
|
||||
| LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | LocalDataFlow.cs:243:9:243:17 | [post] access to local variable nonSink10 |
|
||||
| LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | LocalDataFlow.cs:243:9:243:38 | call to method AppendLine |
|
||||
| LocalDataFlow.cs:247:13:247:52 | SSA def(taintedDataContract) | LocalDataFlow.cs:248:22:248:40 | access to local variable taintedDataContract |
|
||||
| LocalDataFlow.cs:247:13:247:52 | SSA qualifier def(taintedDataContract.AList) | LocalDataFlow.cs:250:22:250:46 | access to property AList |
|
||||
| LocalDataFlow.cs:247:13:247:52 | SSA qualifier def(taintedDataContract.AString) | LocalDataFlow.cs:248:22:248:48 | access to property AString |
|
||||
|
||||
@@ -20,7 +20,10 @@ setup, as outlined in the steps below.
|
||||
If you use Linux, Windows, or macOS version 10.14 ("Mojave") or earlier, simply
|
||||
follow the steps below. For macOS version 10.15 ("Catalina") or newer, steps 1
|
||||
and 4 are slightly different---for further details, see the sections labeled
|
||||
**Information for macOS "Catalina" (or newer) users**.
|
||||
**Information for macOS "Catalina" (or newer) users**. If you are using macOS
|
||||
on Apple Silicon (e.g. Apple M1), ensure that the `Xcode command-line developer
|
||||
tools <https://developer.apple.com/downloads/index.action>`__ and `Rosetta 2
|
||||
<https://support.apple.com/en-us/HT211861>`__ are installed.
|
||||
|
||||
For information about installing the CodeQL CLI in a CI system to create results
|
||||
to display in GitHub as code scanning alerts, see
|
||||
|
||||
2
java/change-notes/2021-11-15-overrides.md
Normal file
2
java/change-notes/2021-11-15-overrides.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* The predicate `Method.overrides(Method)` was accidentally transitive. This has been fixed. This fix also affects `Method.overridesOrInstantiates(Method)` and `Method.getASourceOverriddenMethod()`.
|
||||
@@ -27,7 +27,7 @@ jakarta.ws.rs.container,,9,,,,,,,,,,,,,,,,,,,,,,9,,
|
||||
jakarta.ws.rs.core,2,,149,,,,,,,,,,,,,,,,2,,,,,,94,55
|
||||
java.beans,,,1,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
java.io,3,,31,,3,,,,,,,,,,,,,,,,,,,,30,1
|
||||
java.lang,,,51,,,,,,,,,,,,,,,,,,,,,,41,10
|
||||
java.lang,,,52,,,,,,,,,,,,,,,,,,,,,,42,10
|
||||
java.net,10,3,7,,,,,,,,,,,,10,,,,,,,,,3,7,
|
||||
java.nio,10,,4,,10,,,,,,,,,,,,,,,,,,,,4,
|
||||
java.sql,7,,,,,,,,,,,,,,,,7,,,,,,,,,
|
||||
|
||||
|
@@ -15,9 +15,9 @@ Java framework & library support
|
||||
`Apache HttpComponents <https://hc.apache.org/>`_,"``org.apache.hc.core5.*``, ``org.apache.http``",5,136,28,,,3,,,,25
|
||||
`Google Guava <https://guava.dev/>`_,``com.google.common.*``,,728,6,,6,,,,,
|
||||
`JSON-java <https://github.com/stleary/JSON-java>`_,``org.json``,,236,,,,,,,,
|
||||
Java Standard Library,``java.*``,3,523,30,13,,,7,,,10
|
||||
Java Standard Library,``java.*``,3,524,30,13,,,7,,,10
|
||||
Java extensions,"``javax.*``, ``jakarta.*``",54,552,32,,,4,,1,1,2
|
||||
`Spring <https://spring.io/>`_,``org.springframework.*``,29,469,91,,,,19,14,,29
|
||||
Others,"``cn.hutool.core.codec``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.opensymphony.xwork2.ognl``, ``com.unboundid.ldap.sdk``, ``flexjson``, ``groovy.lang``, ``groovy.util``, ``jodd.json``, ``net.sf.saxon.s9api``, ``ognl``, ``org.apache.commons.codec``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.ibatis.jdbc``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.hibernate``, ``org.jooq``, ``org.mvel2``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``",39,99,151,,,,14,18,,
|
||||
Totals,,175,5368,431,13,6,10,107,33,1,66
|
||||
Totals,,175,5369,431,13,6,10,107,33,1,66
|
||||
|
||||
|
||||
@@ -285,7 +285,20 @@ private predicate overrides(Method m1, Method m2) {
|
||||
or
|
||||
m2.isProtected()
|
||||
or
|
||||
m2.isPackageProtected() and t1.getPackage() = t2.getPackage()
|
||||
m2.isPackageProtected() and
|
||||
pragma[only_bind_out](t1.getPackage()) = pragma[only_bind_out](t2.getPackage())
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate overridesCandidateType(RefType tsup, string sig, RefType t, Method m) {
|
||||
virtualMethodWithSignature(sig, t, m) and
|
||||
t.extendsOrImplements(tsup)
|
||||
or
|
||||
exists(RefType mid |
|
||||
overridesCandidateType(mid, sig, t, m) and
|
||||
mid.extendsOrImplements(tsup) and
|
||||
not virtualMethodWithSignature(sig, mid, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -294,11 +307,10 @@ private predicate overrides(Method m1, Method m2) {
|
||||
* ignoring any access modifiers. Additionally, this predicate binds
|
||||
* `t1` to the type declaring `m1` and `t2` to the type declaring `m2`.
|
||||
*/
|
||||
pragma[noopt]
|
||||
cached
|
||||
predicate overridesIgnoringAccess(Method m1, RefType t1, Method m2, RefType t2) {
|
||||
exists(string sig |
|
||||
virtualMethodWithSignature(sig, t1, m1) and
|
||||
t1.extendsOrImplements+(t2) and
|
||||
overridesCandidateType(t2, sig, t1, m1) and
|
||||
virtualMethodWithSignature(sig, t2, m2)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ predicate hasSubtype(RefType t, Type sub) {
|
||||
arraySubtype(t, sub) and t != sub
|
||||
or
|
||||
// Type parameter containment for parameterized types.
|
||||
parContainmentSubtype(t, sub) and t != sub
|
||||
parContainmentSubtype(t, sub)
|
||||
or
|
||||
// Type variables are subtypes of their upper bounds.
|
||||
typeVarSubtypeBound(t, sub) and t != sub
|
||||
@@ -59,19 +59,23 @@ private predicate arraySubtype(Array sup, Array sub) {
|
||||
* )
|
||||
* ```
|
||||
* For performance several transformations are made. First, the `forex` is
|
||||
* written as a loop where `typeArgumentsContain(_, pt, psub, n)` encode that
|
||||
* the `forex` holds for `i in [0..n]`. Second, the relation is split into two
|
||||
* cases depending on whether `pt.getNumberOfTypeArguments()` is 1 or 2+, as
|
||||
* this allows us to unroll the loop and collapse the first two iterations. The
|
||||
* base case for `typeArgumentsContain` is therefore `n=1` and this allows an
|
||||
* improved join order implemented by `contains01`.
|
||||
* written as a loop where `typePrefixContains(ppt, ppsub)` encode that
|
||||
* `ppt` and `ppsub` are prefixes of `pt` and `ptsub` and that
|
||||
* the `forex` holds for `i in [0..n-1]` where `n` is the length of the prefixes.
|
||||
* Second, the recursive case that determines containment of length `n+1`
|
||||
* prefixes is split into three cases depending on whether there is
|
||||
* non-reflexive type parameter containment:
|
||||
* - only in the length `n` prefix,
|
||||
* - only in the `n`th position,
|
||||
* - both in the length `n` prefix and the `n`th position.
|
||||
*/
|
||||
|
||||
private predicate parContainmentSubtype(ParameterizedType pt, ParameterizedType psub) {
|
||||
pt != psub and
|
||||
typeArgumentsContain(_, pt, psub, pt.getNumberOfTypeArguments() - 1)
|
||||
or
|
||||
typeArgumentsContain0(_, pt, psub)
|
||||
exists(ParameterizedPrefix ppt, ParameterizedPrefix ppsub |
|
||||
typePrefixContains(ppt, ppsub) and
|
||||
ppt.equals(pt) and
|
||||
ppsub.equals(psub)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,100 +98,116 @@ private RefType parameterisationTypeArgumentVarianceCand(
|
||||
varianceCandidate(t)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if every type argument of `s` (up to `n` with `n >= 1`) contains the
|
||||
* corresponding type argument of `t`. Both `s` and `t` are constrained to
|
||||
* being parameterizations of `g`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate typeArgumentsContain(
|
||||
GenericType g, ParameterizedType s, ParameterizedType t, int n
|
||||
) {
|
||||
contains01(g, s, t) and n = 1
|
||||
private newtype TParameterizedPrefix =
|
||||
TGenericType(GenericType g) or
|
||||
TTypeParam(ParameterizedPrefix pp, RefType t) { prefixMatches(pp, t, _, _) }
|
||||
|
||||
/** Holds if `pp` is a length `n` prefix of `pt`. */
|
||||
private predicate prefixMatches(ParameterizedPrefix pp, ParameterizedType pt, int n) {
|
||||
pp = TGenericType(pt.getGenericType()) and n = 0
|
||||
or
|
||||
contains(g, s, t, n) and
|
||||
typeArgumentsContain(g, s, t, n - 1)
|
||||
}
|
||||
|
||||
private predicate typeArgumentsContain0(
|
||||
GenericType g, ParameterizedType sParm, ParameterizedType tParm
|
||||
) {
|
||||
exists(RefType s, RefType t |
|
||||
containsAux0(g, tParm, s, t) and
|
||||
s = parameterisationTypeArgument(g, sParm, 0) and
|
||||
s != t
|
||||
exists(ParameterizedPrefix pp0, RefType t |
|
||||
pp = TTypeParam(pp0, t) and prefixMatches(pp0, t, pt, n - 1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `n`-th type argument of `sParm` contain the `n`-th type
|
||||
* argument of `tParm` for both `n = 0` and `n = 1`, where both `sParm` and
|
||||
* `tParm` are parameterizations of the same generic type `g`.
|
||||
*
|
||||
* This is equivalent to
|
||||
* ```
|
||||
* contains(g, sParm, tParm, 0) and
|
||||
* contains(g, sParm, tParm, 1)
|
||||
* ```
|
||||
* except `contains` is restricted to only include `n >= 2`.
|
||||
* Holds if `pp` is a length `n` prefix of `pt` and `t` is the `n`th type
|
||||
* argument of `pt`.
|
||||
*/
|
||||
private predicate contains01(GenericType g, ParameterizedType sParm, ParameterizedType tParm) {
|
||||
exists(RefType s0, RefType t0, RefType s1, RefType t1 |
|
||||
contains01Aux0(g, tParm, s0, t0, t1) and
|
||||
contains01Aux1(g, sParm, s0, s1, t1)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate contains01Aux0(
|
||||
GenericType g, ParameterizedType tParm, RefType s0, RefType t0, RefType t1
|
||||
) {
|
||||
typeArgumentContains(g, s0, t0, 0) and
|
||||
t0 = parameterisationTypeArgument(g, tParm, 0) and
|
||||
t1 = parameterisationTypeArgument(g, tParm, 1)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate contains01Aux1(
|
||||
GenericType g, ParameterizedType sParm, RefType s0, RefType s1, RefType t1
|
||||
) {
|
||||
typeArgumentContains(g, s1, t1, 1) and
|
||||
s0 = parameterisationTypeArgumentVarianceCand(g, sParm, 0) and
|
||||
s1 = parameterisationTypeArgumentVarianceCand(g, sParm, 1)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate containsAux0(GenericType g, ParameterizedType tParm, RefType s, RefType t) {
|
||||
typeArgumentContains(g, s, t, 0) and
|
||||
t = parameterisationTypeArgument(g, tParm, 0) and
|
||||
g.getNumberOfTypeParameters() = 1
|
||||
private predicate prefixMatches(ParameterizedPrefix pp, RefType t, ParameterizedType pt, int n) {
|
||||
prefixMatches(pp, pt, n) and
|
||||
t = pt.getTypeArgument(n)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `n`-th type argument of `sParm` contain the `n`-th type
|
||||
* argument of `tParm`, where both `sParm` and `tParm` are parameterizations of
|
||||
* the same generic type `g`. The index `n` is restricted to `n >= 2`, the
|
||||
* cases `n < 2` are handled by `contains01`.
|
||||
*
|
||||
* See JLS 4.5.1, Type Arguments of Parameterized Types.
|
||||
* A prefix of a `ParameterizedType`. This encodes the corresponding
|
||||
* `GenericType` and the first `n` type arguments where `n` is the prefix
|
||||
* length.
|
||||
*/
|
||||
private predicate contains(GenericType g, ParameterizedType sParm, ParameterizedType tParm, int n) {
|
||||
exists(RefType s, RefType t |
|
||||
containsAux(g, tParm, n, s, t) and
|
||||
s = parameterisationTypeArgumentVarianceCand(g, sParm, n)
|
||||
private class ParameterizedPrefix extends TParameterizedPrefix {
|
||||
string toString() { result = "ParameterizedPrefix" }
|
||||
|
||||
predicate equals(ParameterizedType pt) { prefixMatches(this, pt, pt.getNumberOfTypeArguments()) }
|
||||
|
||||
/** Holds if this prefix has length `n`, applies to `g`, and equals `TTypeParam(pp, t)`. */
|
||||
predicate split(GenericType g, ParameterizedPrefix pp, RefType t, int n) {
|
||||
this = TTypeParam(pp, t) and
|
||||
(
|
||||
pp = TGenericType(g) and n = 0
|
||||
or
|
||||
pp.split(g, _, _, n - 1)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if every type argument of `pps` contains the corresponding type
|
||||
* argument of `ppt`. Both `pps` and `ppt` are constrained to be equal-length
|
||||
* prefixes of parameterizations of the same `GenericType`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate typePrefixContains(ParameterizedPrefix pps, ParameterizedPrefix ppt) {
|
||||
// Let `pps = TTypeParam(pps0, s)` and `ppt = TTypeParam(ppt0, t)`.
|
||||
// Case 1: pps0 = ppt0 and typeArgumentContains(_, s, t, _)
|
||||
typePrefixContains_base(pps, ppt)
|
||||
or
|
||||
// Case 2: typePrefixContains(pps0, ppt0) and s = t
|
||||
typePrefixContains_ext_eq(pps, ppt)
|
||||
or
|
||||
// Case 3: typePrefixContains(pps0, ppt0) and typeArgumentContains(_, s, t, _)
|
||||
typePrefixContains_ext_neq(pps, ppt)
|
||||
}
|
||||
|
||||
private predicate typePrefixContains_base(ParameterizedPrefix pps, ParameterizedPrefix ppt) {
|
||||
exists(ParameterizedPrefix pp, RefType s |
|
||||
pps = TTypeParam(pp, s) and
|
||||
typePrefixContainsAux2(ppt, pp, s)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate typePrefixContains_ext_eq(ParameterizedPrefix pps, ParameterizedPrefix ppt) {
|
||||
exists(ParameterizedPrefix pps0, ParameterizedPrefix ppt0, RefType t |
|
||||
typePrefixContains(pragma[only_bind_into](pps0), pragma[only_bind_into](ppt0)) and
|
||||
pps = TTypeParam(pragma[only_bind_into](pps0), t) and
|
||||
ppt = TTypeParam(ppt0, t)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate typePrefixContains_ext_neq(ParameterizedPrefix pps, ParameterizedPrefix ppt) {
|
||||
exists(ParameterizedPrefix ppt0, RefType s |
|
||||
typePrefixContainsAux1(pps, ppt0, s) and
|
||||
typePrefixContainsAux2(ppt, ppt0, s)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate containsAux(GenericType g, ParameterizedType tParm, int n, RefType s, RefType t) {
|
||||
typeArgumentContains(g, s, t, n) and
|
||||
t = parameterisationTypeArgument(g, tParm, n) and
|
||||
n >= 2
|
||||
private predicate typePrefixContainsAux1(
|
||||
ParameterizedPrefix pps, ParameterizedPrefix ppt0, RefType s
|
||||
) {
|
||||
exists(ParameterizedPrefix pps0 |
|
||||
typePrefixContains(pps0, ppt0) and
|
||||
pps = TTypeParam(pps0, s) and
|
||||
s instanceof Wildcard // manual magic, implied by `typeArgumentContains(_, s, t, _)`
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate typePrefixContainsAux2(
|
||||
ParameterizedPrefix ppt, ParameterizedPrefix ppt0, RefType s
|
||||
) {
|
||||
exists(GenericType g, int n, RefType t |
|
||||
// Implies `ppt = TTypeParam(ppt0, t)`
|
||||
ppt.split(g, ppt0, t, n) and
|
||||
typeArgumentContains(g, s, t, n)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the type argument `s` contains the type argument `t`, where both
|
||||
* type arguments occur as index `n` in an instantiation of `g`.
|
||||
*
|
||||
* The case `s = t` is not included.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate typeArgumentContains(GenericType g, RefType s, RefType t, int n) {
|
||||
@@ -205,18 +225,18 @@ private predicate typeArgumentContainsAux2(GenericType g, RefType s, RefType t,
|
||||
* Holds if the type argument `s` contains the type argument `t`, where both
|
||||
* type arguments occur as index `n` in some parameterized types.
|
||||
*
|
||||
* The case `s = t` is not included.
|
||||
*
|
||||
* See JLS 4.5.1, Type Arguments of Parameterized Types.
|
||||
*/
|
||||
private predicate typeArgumentContainsAux1(RefType s, RefType t, int n) {
|
||||
exists(int i |
|
||||
s = parameterisationTypeArgumentVarianceCand(_, _, i) and
|
||||
t = parameterisationTypeArgument(_, _, n) and
|
||||
i <= n and
|
||||
n <= i
|
||||
|
|
||||
s = parameterisationTypeArgumentVarianceCand(_, _, pragma[only_bind_into](n)) and
|
||||
t = parameterisationTypeArgument(_, _, pragma[only_bind_into](n)) and
|
||||
s != t and
|
||||
(
|
||||
exists(RefType tUpperBound | tUpperBound = t.(Wildcard).getUpperBound().getType() |
|
||||
// ? extends T <= ? extends S if T <: S
|
||||
hasSubtypeStar(s.(Wildcard).getUpperBound().getType(), tUpperBound)
|
||||
hasSubtypeStar1(s.(Wildcard).getUpperBound().getType(), tUpperBound)
|
||||
or
|
||||
// ? extends T <= ?
|
||||
s.(Wildcard).isUnconstrained()
|
||||
@@ -224,7 +244,7 @@ private predicate typeArgumentContainsAux1(RefType s, RefType t, int n) {
|
||||
or
|
||||
exists(RefType tLowerBound | tLowerBound = t.(Wildcard).getLowerBound().getType() |
|
||||
// ? super T <= ? super S if s <: T
|
||||
hasSubtypeStar(tLowerBound, s.(Wildcard).getLowerBound().getType())
|
||||
hasSubtypeStar2(tLowerBound, s.(Wildcard).getLowerBound().getType())
|
||||
or
|
||||
// ? super T <= ?
|
||||
s.(Wildcard).isUnconstrained()
|
||||
@@ -233,14 +253,14 @@ private predicate typeArgumentContainsAux1(RefType s, RefType t, int n) {
|
||||
wildcardExtendsObject(s)
|
||||
)
|
||||
or
|
||||
// T <= T
|
||||
s = t
|
||||
or
|
||||
// T <= ? extends T
|
||||
hasSubtypeStar(s.(Wildcard).getUpperBound().getType(), t)
|
||||
hasSubtypeStar1(s.(Wildcard).getUpperBound().getType(), t)
|
||||
or
|
||||
// T <= ? super T
|
||||
hasSubtypeStar(t, s.(Wildcard).getLowerBound().getType())
|
||||
hasSubtypeStar2(t, s.(Wildcard).getLowerBound().getType())
|
||||
// or
|
||||
// T <= T
|
||||
// but this case is handled directly in `typePrefixContains`
|
||||
)
|
||||
}
|
||||
|
||||
@@ -249,12 +269,38 @@ private predicate wildcardExtendsObject(Wildcard wc) {
|
||||
wc.getUpperBound().getType() instanceof TypeObject
|
||||
}
|
||||
|
||||
private predicate hasSubtypeStar(RefType t, RefType sub) {
|
||||
sub = t
|
||||
// manual magic for `hasSubtypeStar1`
|
||||
private predicate getAWildcardUpperBound(RefType t) {
|
||||
t = any(Wildcard w).getUpperBound().getType()
|
||||
}
|
||||
|
||||
// manual magic for `hasSubtypeStar2`
|
||||
private predicate getAWildcardLowerBound(RefType t) {
|
||||
t = any(Wildcard w).getLowerBound().getType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `hasSubtype*(t, sub)`, but manual-magic'ed with `getAWildcardUpperBound(t)`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate hasSubtypeStar1(RefType t, RefType sub) {
|
||||
sub = t and getAWildcardUpperBound(t)
|
||||
or
|
||||
hasSubtype(t, sub)
|
||||
hasSubtype(t, sub) and getAWildcardUpperBound(t)
|
||||
or
|
||||
exists(RefType mid | hasSubtypeStar(t, mid) and hasSubtype(mid, sub))
|
||||
exists(RefType mid | hasSubtypeStar1(t, mid) and hasSubtype(mid, sub))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `hasSubtype*(t, sub)`, but manual-magic'ed with `getAWildcardLowerBound(sub)`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate hasSubtypeStar2(RefType t, RefType sub) {
|
||||
sub = t and getAWildcardLowerBound(sub)
|
||||
or
|
||||
hasSubtype(t, sub) and getAWildcardLowerBound(sub)
|
||||
or
|
||||
exists(RefType mid | hasSubtype(t, mid) and hasSubtypeStar2(mid, sub))
|
||||
}
|
||||
|
||||
/** Holds if type `t` declares member `m`. */
|
||||
|
||||
@@ -85,6 +85,9 @@ module Public {
|
||||
/** Holds if this stack contains summary component `c`. */
|
||||
predicate contains(SummaryComponent c) { c = this.drop(_).head() }
|
||||
|
||||
/** Gets the bottom element of this stack. */
|
||||
SummaryComponent bottom() { result = this.drop(this.length() - 1).head() }
|
||||
|
||||
/** Gets a textual representation of this stack. */
|
||||
string toString() {
|
||||
exists(SummaryComponent head, SummaryComponentStack tail |
|
||||
@@ -197,6 +200,8 @@ module Private {
|
||||
or
|
||||
tail.(RequiredSummaryComponentStack).required(TParameterSummaryComponent(_)) and
|
||||
head = thisParam()
|
||||
or
|
||||
derivedFluentFlowPush(_, _, _, head, tail, _)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -210,7 +215,7 @@ module Private {
|
||||
c.propagatesFlow(output, input, preservesValue) and
|
||||
preservesValue = true and
|
||||
isCallbackParameter(input) and
|
||||
isContentOfArgument(output)
|
||||
isContentOfArgument(output, _)
|
||||
or
|
||||
// flow from the receiver of a callback into the instance-parameter
|
||||
exists(SummaryComponentStack s, SummaryComponentStack callbackRef |
|
||||
@@ -222,16 +227,81 @@ module Private {
|
||||
output = TConsSummaryComponentStack(thisParam(), input) and
|
||||
preservesValue = true
|
||||
)
|
||||
or
|
||||
exists(SummaryComponentStack arg, SummaryComponentStack return |
|
||||
derivedFluentFlow(c, input, arg, return, preservesValue)
|
||||
|
|
||||
arg.length() = 1 and
|
||||
output = return
|
||||
or
|
||||
exists(SummaryComponent head, SummaryComponentStack tail |
|
||||
derivedFluentFlowPush(c, input, arg, head, tail, 0) and
|
||||
output = SummaryComponentStack::push(head, tail)
|
||||
)
|
||||
)
|
||||
or
|
||||
// Chain together summaries where values get passed into callbacks along the way
|
||||
exists(SummaryComponentStack mid, boolean preservesValue1, boolean preservesValue2 |
|
||||
c.propagatesFlow(input, mid, preservesValue1) and
|
||||
c.propagatesFlow(mid, output, preservesValue2) and
|
||||
mid.drop(mid.length() - 2) =
|
||||
SummaryComponentStack::push(TParameterSummaryComponent(_),
|
||||
SummaryComponentStack::singleton(TArgumentSummaryComponent(_))) and
|
||||
preservesValue = preservesValue1.booleanAnd(preservesValue2)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `c` has a flow summary from `input` to `arg`, where `arg`
|
||||
* writes to (contents of) the `i`th argument, and `c` has a
|
||||
* value-preserving flow summary from the `i`th argument to a return value
|
||||
* (`return`).
|
||||
*
|
||||
* In such a case, we derive flow from `input` to (contents of) the return
|
||||
* value.
|
||||
*
|
||||
* As an example, this simplifies modeling of fluent methods:
|
||||
* for `StringBuilder.append(x)` with a specified value flow from qualifier to
|
||||
* return value and taint flow from argument 0 to the qualifier, then this
|
||||
* allows us to infer taint flow from argument 0 to the return value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate derivedFluentFlow(
|
||||
SummarizedCallable c, SummaryComponentStack input, SummaryComponentStack arg,
|
||||
SummaryComponentStack return, boolean preservesValue
|
||||
) {
|
||||
exists(int i |
|
||||
summary(c, input, arg, preservesValue) and
|
||||
isContentOfArgument(arg, i) and
|
||||
summary(c, SummaryComponentStack::singleton(TArgumentSummaryComponent(i)), return, true) and
|
||||
return.bottom() = TReturnSummaryComponent(_)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate derivedFluentFlowPush(
|
||||
SummarizedCallable c, SummaryComponentStack input, SummaryComponentStack arg,
|
||||
SummaryComponent head, SummaryComponentStack tail, int i
|
||||
) {
|
||||
derivedFluentFlow(c, input, arg, tail, _) and
|
||||
head = arg.drop(i).head() and
|
||||
i = arg.length() - 2
|
||||
or
|
||||
exists(SummaryComponent head0, SummaryComponentStack tail0 |
|
||||
derivedFluentFlowPush(c, input, arg, head0, tail0, i + 1) and
|
||||
head = arg.drop(i).head() and
|
||||
tail = SummaryComponentStack::push(head0, tail0)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isCallbackParameter(SummaryComponentStack s) {
|
||||
s.head() = TParameterSummaryComponent(_) and exists(s.tail())
|
||||
}
|
||||
|
||||
private predicate isContentOfArgument(SummaryComponentStack s) {
|
||||
s.head() = TContentSummaryComponent(_) and isContentOfArgument(s.tail())
|
||||
private predicate isContentOfArgument(SummaryComponentStack s, int i) {
|
||||
s.head() = TContentSummaryComponent(_) and isContentOfArgument(s.tail(), i)
|
||||
or
|
||||
s = TSingletonSummaryComponentStack(TArgumentSummaryComponent(_))
|
||||
s = TSingletonSummaryComponentStack(TArgumentSummaryComponent(i))
|
||||
}
|
||||
|
||||
private predicate outputState(SummarizedCallable c, SummaryComponentStack s) {
|
||||
@@ -508,9 +578,14 @@ module Private {
|
||||
* node, and back out to `p`.
|
||||
*/
|
||||
predicate summaryAllowParameterReturnInSelf(ParamNode p) {
|
||||
exists(SummarizedCallable c, int i |
|
||||
c.clearsContent(i, _) and
|
||||
p.isParameterOf(c, i)
|
||||
exists(SummarizedCallable c, int i | p.isParameterOf(c, i) |
|
||||
c.clearsContent(i, _)
|
||||
or
|
||||
exists(SummaryComponentStack inputContents, SummaryComponentStack outputContents |
|
||||
summary(c, inputContents, outputContents, _) and
|
||||
inputContents.bottom() = pragma[only_bind_into](TArgumentSummaryComponent(i)) and
|
||||
outputContents.bottom() = pragma[only_bind_into](TArgumentSummaryComponent(i))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -534,22 +609,6 @@ module Private {
|
||||
preservesValue = false and not summary(c, inputContents, outputContents, true)
|
||||
)
|
||||
or
|
||||
// If flow through a method updates a parameter from some input A, and that
|
||||
// parameter also is returned through B, then we'd like a combined flow from A
|
||||
// to B as well. As an example, this simplifies modeling of fluent methods:
|
||||
// for `StringBuilder.append(x)` with a specified value flow from qualifier to
|
||||
// return value and taint flow from argument 0 to the qualifier, then this
|
||||
// allows us to infer taint flow from argument 0 to the return value.
|
||||
succ instanceof ParamNode and
|
||||
summaryPostUpdateNode(pred, succ) and
|
||||
preservesValue = true
|
||||
or
|
||||
// Similarly we would like to chain together summaries where values get passed
|
||||
// into callbacks along the way.
|
||||
pred instanceof ArgNode and
|
||||
summaryPostUpdateNode(succ, pred) and
|
||||
preservesValue = true
|
||||
or
|
||||
exists(SummarizedCallable c, int i |
|
||||
pred.(ParamNode).isParameterOf(c, i) and
|
||||
succ = summaryNode(c, TSummaryNodeClearsContentState(i, _)) and
|
||||
|
||||
@@ -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.overrides(this.getAnImplementedManagedBean().getAMethod())
|
||||
result.overrides+(this.getAnImplementedManagedBean().getAMethod())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ class Struts1ActionEntryPoint extends EntryPoint, Class {
|
||||
exists(Method methodFromAction |
|
||||
methodFromAction.getDeclaringType().hasQualifiedName("org.apache.struts.action", "Action")
|
||||
|
|
||||
result.(Method).overrides(methodFromAction)
|
||||
result.(Method).overrides+(methodFromAction)
|
||||
)
|
||||
or
|
||||
this.getASupertype*().hasQualifiedName("org.apache.struts.actions", "DispatchAction") and
|
||||
|
||||
@@ -54,7 +54,8 @@ private class StringSummaryCsv extends SummaryModelCsv {
|
||||
"java.lang;StringBuffer;true;StringBuffer;(CharSequence);;Argument[0];Argument[-1];taint",
|
||||
"java.lang;StringBuffer;true;StringBuffer;(String);;Argument[0];Argument[-1];taint",
|
||||
"java.lang;StringBuilder;true;StringBuilder;;;Argument[0];Argument[-1];taint",
|
||||
"java.lang;CharSequence;true;subSequence;;;Argument[-1];ReturnValue;taint"
|
||||
"java.lang;CharSequence;true;subSequence;;;Argument[-1];ReturnValue;taint",
|
||||
"java.lang;CharSequence;true;toString;;;Argument[-1];ReturnValue;taint"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class ThriftIface extends Interface {
|
||||
|
||||
Method getAnImplementingMethod() {
|
||||
result.getDeclaringType().(Class).getASupertype+() = this and
|
||||
result.overrides(this.getAMethod()) and
|
||||
result.overrides+(this.getAMethod()) and
|
||||
not result.getFile() = this.getFile()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -572,7 +572,7 @@ class RemoteInterface extends Interface {
|
||||
* abstract methods or overriding within an interface hierarchy.
|
||||
*/
|
||||
Method getARemoteMethodImplementationChecked() {
|
||||
result.overrides(this.getARemoteMethod()) and
|
||||
result.overrides+(this.getARemoteMethod()) and
|
||||
exists(result.getBody())
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,6 @@ where
|
||||
sup.isSynchronized() and
|
||||
not sub.isSynchronized() and
|
||||
not delegatingOverride(sub, sup) and
|
||||
not exists(Method mid | sub.overrides(mid) and mid.overrides(sup)) and
|
||||
supSrc = sup.getDeclaringType().getSourceDeclaration()
|
||||
select sub,
|
||||
"Method '" + sub.getName() + "' overrides a synchronized method in $@ but is not synchronized.",
|
||||
|
||||
@@ -45,7 +45,7 @@ where
|
||||
// which is an access to the object being initialized, ...
|
||||
ma = unqualifiedCallToNonAbstractMethod(c, m) and
|
||||
// ... there exists an overriding method in a subtype,
|
||||
n.overrides(m) and
|
||||
n.overrides+(m) and
|
||||
n.getDeclaringType().getASupertype+() = c.getDeclaringType() and
|
||||
// ... the method is in a supertype of c,
|
||||
m.getDeclaringType() = c.getDeclaringType().getASupertype*() and
|
||||
|
||||
57
java/ql/src/utils/model-generator/CaptureSinkModels.ql
Normal file
57
java/ql/src/utils/model-generator/CaptureSinkModels.ql
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* @name Capture sink models.
|
||||
* @description Finds public methods that act as sinks as they flow into a a known sink.
|
||||
* @id java/utils/model-generator/sink-models
|
||||
*/
|
||||
|
||||
import java
|
||||
private import Telemetry.ExternalAPI
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import ModelGeneratorUtils
|
||||
private import semmle.code.java.dataflow.internal.DataFlowNodes::Private
|
||||
|
||||
class PropagateToSinkConfiguration extends TaintTracking::Configuration {
|
||||
PropagateToSinkConfiguration() { this = "parameters or flowing into sinks" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
(source.asExpr().(FieldAccess).isOwnFieldAccess() or source instanceof DataFlow::ParameterNode) and
|
||||
source.getEnclosingCallable().isPublic() and
|
||||
exists(RefType t |
|
||||
t = source.getEnclosingCallable().getDeclaringType().getAnAncestor() and
|
||||
not t instanceof TypeObject and
|
||||
t.isPublic()
|
||||
) and
|
||||
isRelevantForModels(source.getEnclosingCallable())
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sinkNode(sink, _) }
|
||||
|
||||
override DataFlow::FlowFeature getAFeature() {
|
||||
result instanceof DataFlow::FeatureHasSourceCallContext
|
||||
}
|
||||
}
|
||||
|
||||
string asInputArgument(DataFlow::Node source) {
|
||||
exists(int pos |
|
||||
source.(DataFlow::ParameterNode).isParameterOf(_, pos) and
|
||||
result = "Argument[" + pos + "]"
|
||||
)
|
||||
or
|
||||
source.asExpr() instanceof FieldAccess and
|
||||
result = "Argument[-1]"
|
||||
}
|
||||
|
||||
string captureSink(TargetAPI api) {
|
||||
exists(DataFlow::Node src, DataFlow::Node sink, PropagateToSinkConfiguration config, string kind |
|
||||
config.hasFlow(src, sink) and
|
||||
sinkNode(sink, kind) and
|
||||
api = src.getEnclosingCallable() and
|
||||
result = asSinkModel(api, asInputArgument(src), kind)
|
||||
)
|
||||
}
|
||||
|
||||
from TargetAPI api, string sink
|
||||
where sink = captureSink(api)
|
||||
select sink order by sink
|
||||
53
java/ql/src/utils/model-generator/CaptureSourceModels.ql
Normal file
53
java/ql/src/utils/model-generator/CaptureSourceModels.ql
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* @name Capture source models.
|
||||
* @description Finds APIs that act as sources as they expose already known sources.
|
||||
* @id java/utils/model-generator/sink-models
|
||||
*/
|
||||
|
||||
import java
|
||||
private import Telemetry.ExternalAPI
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import ModelGeneratorUtils
|
||||
private import semmle.code.java.dataflow.internal.FlowSummaryImplSpecific
|
||||
private import semmle.code.java.dataflow.internal.FlowSummaryImpl
|
||||
private import semmle.code.java.dataflow.internal.DataFlowImplCommon
|
||||
private import semmle.code.java.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.java.dataflow.internal.DataFlowNodes::Private
|
||||
|
||||
class FromSourceConfiguration extends TaintTracking::Configuration {
|
||||
FromSourceConfiguration() { this = "FromSourceConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { sourceNode(source, _) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(TargetAPI c |
|
||||
sink instanceof ReturnNodeExt and
|
||||
sink.getEnclosingCallable() = c and
|
||||
c.isPublic() and
|
||||
c.fromSource()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::FlowFeature getAFeature() {
|
||||
result instanceof DataFlow::FeatureHasSinkCallContext
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isRelevantTaintStep(node1, node2)
|
||||
}
|
||||
}
|
||||
|
||||
string captureSource(TargetAPI api) {
|
||||
exists(DataFlow::Node source, DataFlow::Node sink, FromSourceConfiguration config, string kind |
|
||||
config.hasFlow(source, sink) and
|
||||
sourceNode(source, kind) and
|
||||
api = source.getEnclosingCallable() and
|
||||
result = asSourceModel(api, returnNodeAsOutput(api, sink), kind)
|
||||
)
|
||||
}
|
||||
|
||||
from TargetAPI api, string sink
|
||||
where sink = captureSource(api)
|
||||
select sink order by sink
|
||||
232
java/ql/src/utils/model-generator/CaptureSummaryModels.ql
Normal file
232
java/ql/src/utils/model-generator/CaptureSummaryModels.ql
Normal file
@@ -0,0 +1,232 @@
|
||||
/**
|
||||
* @name Capture summary models.
|
||||
* @description Finds applicable summary models to be used by other queries.
|
||||
* @id java/utils/model-generator/summary-models
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.dataflow.internal.DataFlowImplCommon
|
||||
import semmle.code.java.dataflow.internal.DataFlowNodes
|
||||
import semmle.code.java.dataflow.internal.DataFlowPrivate
|
||||
import semmle.code.java.dataflow.InstanceAccess
|
||||
import ModelGeneratorUtils
|
||||
|
||||
string captureFlow(TargetAPI api) {
|
||||
result = captureQualifierFlow(api) or
|
||||
result = captureParameterFlowToReturnValue(api) or
|
||||
result = captureFieldFlowIn(api) or
|
||||
result = captureParameterToParameterFlow(api) or
|
||||
result = captureFieldFlow(api)
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture fluent APIs that return `this`.
|
||||
* Example of a fluent API:
|
||||
* ```
|
||||
* public class Foo {
|
||||
* public Foo someAPI() {
|
||||
* // some side-effect
|
||||
* return this;
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
string captureQualifierFlow(TargetAPI api) {
|
||||
exists(ReturnStmt rtn |
|
||||
rtn.getEnclosingCallable() = api and
|
||||
rtn.getResult().(ThisAccess).isOwnInstanceAccess()
|
||||
) and
|
||||
result = asValueModel(api, "Argument[-1]", "ReturnValue")
|
||||
}
|
||||
|
||||
class FieldToReturnConfig extends TaintTracking::Configuration {
|
||||
FieldToReturnConfig() { this = "FieldToReturnConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source instanceof DataFlow::InstanceParameterNode
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink instanceof ReturnNodeExt and
|
||||
not sink.(ReturnNode).asExpr().(ThisAccess).isOwnInstanceAccess() and
|
||||
not exists(captureQualifierFlow(sink.asExpr().getEnclosingCallable()))
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isRelevantTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
override DataFlow::FlowFeature getAFeature() {
|
||||
result instanceof DataFlow::FeatureEqualSourceSinkCallContext
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture APIs that return tainted instance data.
|
||||
* Example of an API that returns tainted instance data:
|
||||
* ```
|
||||
* public class Foo {
|
||||
* private String tainted;
|
||||
*
|
||||
* public String returnsTainted() {
|
||||
* return tainted;
|
||||
* }
|
||||
*
|
||||
* public void putsTaintIntoParameter(List<String> foo) {
|
||||
* foo.add(tainted);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* Captured Model:
|
||||
* ```
|
||||
* p;Foo;true;returnsTainted;;Argument[-1];ReturnValue;taint
|
||||
* p;Foo;true;putsTaintIntoParameter;(List);Argument[-1];Argument[0];taint
|
||||
* ```
|
||||
*/
|
||||
string captureFieldFlow(TargetAPI api) {
|
||||
exists(FieldToReturnConfig config, ReturnNodeExt returnNodeExt |
|
||||
config.hasFlow(_, returnNodeExt) and
|
||||
returnNodeExt.getEnclosingCallable() = api and
|
||||
not api.getDeclaringType() instanceof EnumType and
|
||||
isRelevantType(returnNodeExt.getType())
|
||||
|
|
||||
result = asTaintModel(api, "Argument[-1]", returnNodeAsOutput(api, returnNodeExt))
|
||||
)
|
||||
}
|
||||
|
||||
class ParameterToFieldConfig extends TaintTracking::Configuration {
|
||||
ParameterToFieldConfig() { this = "ParameterToFieldConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source instanceof DataFlow::ParameterNode and
|
||||
isRelevantType(source.getType())
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
thisAccess(sink.(DataFlow::PostUpdateNode).getPreUpdateNode())
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
store(node1, _, node2, _)
|
||||
}
|
||||
|
||||
override DataFlow::FlowFeature getAFeature() {
|
||||
result instanceof DataFlow::FeatureEqualSourceSinkCallContext
|
||||
}
|
||||
}
|
||||
|
||||
private predicate thisAccess(DataFlow::Node n) {
|
||||
n.asExpr().(InstanceAccess).isOwnInstanceAccess()
|
||||
or
|
||||
n.(DataFlow::ImplicitInstanceAccess).getInstanceAccess() instanceof OwnInstanceAccess
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures APIs that accept input and store them in a field.
|
||||
* Example:
|
||||
* ```
|
||||
* public class Foo {
|
||||
* private String tainted;
|
||||
* public void doSomething(String input) {
|
||||
* tainted = input;
|
||||
* }
|
||||
* ```
|
||||
* Captured Model:
|
||||
* `p;Foo;true;doSomething;(String);Argument[0];Argument[-1];taint`
|
||||
*/
|
||||
string captureFieldFlowIn(TargetAPI api) {
|
||||
exists(DataFlow::Node source, ParameterToFieldConfig config |
|
||||
config.hasFlow(source, _) and
|
||||
source.asParameter().getCallable() = api
|
||||
|
|
||||
result =
|
||||
asTaintModel(api, "Argument[" + source.asParameter().getPosition() + "]", "Argument[-1]")
|
||||
)
|
||||
}
|
||||
|
||||
class ParameterToReturnValueTaintConfig extends TaintTracking::Configuration {
|
||||
ParameterToReturnValueTaintConfig() { this = "ParameterToReturnValueTaintConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(TargetAPI api |
|
||||
api = source.asParameter().getCallable() and
|
||||
isRelevantType(api.getReturnType()) and
|
||||
isRelevantType(source.asParameter().getType())
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof ReturnNode }
|
||||
|
||||
// consider store steps to track taint across objects to model factory methods returning tainted objects
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
store(node1, _, node2, _)
|
||||
}
|
||||
|
||||
override DataFlow::FlowFeature getAFeature() {
|
||||
result instanceof DataFlow::FeatureEqualSourceSinkCallContext
|
||||
}
|
||||
}
|
||||
|
||||
predicate paramFlowToReturnValueExists(Parameter p) {
|
||||
exists(ParameterToReturnValueTaintConfig config, ReturnStmt rtn |
|
||||
config.hasFlow(DataFlow::parameterNode(p), DataFlow::exprNode(rtn.getResult()))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture APIs that return (parts of) data passed in as a parameter.
|
||||
* Example:
|
||||
* ```
|
||||
* public class Foo {
|
||||
*
|
||||
* public String returnData(String tainted) {
|
||||
* return tainted.substring(0,10)
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* Captured Model:
|
||||
* ```
|
||||
* p;Foo;true;returnData;;Argument[0];ReturnValue;taint
|
||||
* ```
|
||||
*/
|
||||
string captureParameterFlowToReturnValue(TargetAPI api) {
|
||||
exists(Parameter p |
|
||||
p = api.getAParameter() and
|
||||
paramFlowToReturnValueExists(p)
|
||||
|
|
||||
result = asTaintModel(api, parameterAccess(p), "ReturnValue")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture APIs that pass tainted data from a parameter to a parameter.
|
||||
* Example:
|
||||
* ```
|
||||
* public class Foo {
|
||||
*
|
||||
* public void addToList(String tainted, List<String> foo) {
|
||||
* foo.add(tainted);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* Captured Model:
|
||||
* ```
|
||||
* p;Foo;true;addToList;;Argument[0];Argument[1];taint
|
||||
* ```
|
||||
*/
|
||||
string captureParameterToParameterFlow(TargetAPI api) {
|
||||
exists(DataFlow::ParameterNode source, DataFlow::PostUpdateNode sink |
|
||||
source.getEnclosingCallable() = api and
|
||||
sink.getPreUpdateNode().asExpr() = api.getAParameter().getAnAccess() and
|
||||
TaintTracking::localTaint(source, sink)
|
||||
|
|
||||
result =
|
||||
asTaintModel(api, parameterAccess(source.asParameter()),
|
||||
parameterAccess(sink.getPreUpdateNode().asExpr().(VarAccess).getVariable().(Parameter)))
|
||||
)
|
||||
}
|
||||
|
||||
from TargetAPI api, string flow
|
||||
where flow = captureFlow(api)
|
||||
select flow order by flow
|
||||
178
java/ql/src/utils/model-generator/GenerateFlowModel.py
Executable file
178
java/ql/src/utils/model-generator/GenerateFlowModel.py
Executable file
@@ -0,0 +1,178 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import json
|
||||
import os
|
||||
import os.path
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
|
||||
def printHelp():
|
||||
print("""Usage:
|
||||
GenerateFlowModel.py <library-database> <outputQll> [--with-sinks] [--with-sources] [--with-summaries]
|
||||
|
||||
This generates summary, source and sink models for the code in the database.
|
||||
The files will be placed in `java/ql/lib/semmle/code/java/frameworks/<outputQll>` where
|
||||
outputQll is the name (and path) of the output QLL file. Usually, models are grouped by their
|
||||
respective frameworks.
|
||||
|
||||
Which models are generated is controlled by the flags:
|
||||
--with-sinks
|
||||
--with-sources
|
||||
--with-summaries
|
||||
If none of these flags are specified, all models are generated.
|
||||
|
||||
Example invocations:
|
||||
$ GenerateFlowModel.py /tmp/dbs/apache_commons-codec_45649c8 "apache/Codec.qll"
|
||||
$ GenerateFlowModel.py /tmp/dbs/jdk15_db "javase/jdk_sinks.qll" --with-sinks
|
||||
|
||||
Requirements: `codeql` should both appear on your path.
|
||||
""")
|
||||
|
||||
|
||||
if any(s == "--help" for s in sys.argv):
|
||||
printHelp()
|
||||
sys.exit(0)
|
||||
|
||||
generateSinks = False
|
||||
generateSources = False
|
||||
generateSummaries = False
|
||||
if "--with-sinks" in sys.argv:
|
||||
sys.argv.remove("--with-sinks")
|
||||
generateSinks = True
|
||||
|
||||
if "--with-sources" in sys.argv:
|
||||
sys.argv.remove("--with-sources")
|
||||
generateSources = True
|
||||
|
||||
if "--with-summaries" in sys.argv:
|
||||
sys.argv.remove("--with-summaries")
|
||||
generateSummaries = True
|
||||
|
||||
if not generateSinks and not generateSources and not generateSummaries:
|
||||
generateSinks = generateSources = generateSummaries = True
|
||||
|
||||
if len(sys.argv) != 3:
|
||||
printHelp()
|
||||
sys.exit(1)
|
||||
|
||||
codeQlRoot = subprocess.check_output(
|
||||
["git", "rev-parse", "--show-toplevel"]).decode("utf-8").strip()
|
||||
targetQll = sys.argv[2]
|
||||
if not targetQll.endswith(".qll"):
|
||||
targetQll += ".qll"
|
||||
filename = os.path.basename(targetQll)
|
||||
shortname = filename[:-4]
|
||||
generatedFrameworks = os.path.join(
|
||||
codeQlRoot, "java/ql/lib/semmle/code/java/frameworks/")
|
||||
frameworkTarget = os.path.join(generatedFrameworks, targetQll)
|
||||
|
||||
workDir = tempfile.mkdtemp()
|
||||
os.makedirs(generatedFrameworks, exist_ok=True)
|
||||
|
||||
|
||||
def runQuery(infoMessage, query):
|
||||
print("########## Querying " + infoMessage + "...")
|
||||
database = sys.argv[1]
|
||||
queryFile = os.path.join(os.path.dirname(
|
||||
__file__), query)
|
||||
resultBqrs = os.path.join(workDir, "out.bqrs")
|
||||
cmd = ['codeql', 'query', 'run', queryFile, '--database',
|
||||
database, '--output', resultBqrs, '--threads', '8']
|
||||
|
||||
ret = subprocess.call(cmd)
|
||||
if ret != 0:
|
||||
print("Failed to generate " + infoMessage +
|
||||
". Failed command was: " + shlex.join(cmd))
|
||||
sys.exit(1)
|
||||
return readRows(resultBqrs)
|
||||
|
||||
|
||||
def readRows(bqrsFile):
|
||||
generatedJson = os.path.join(workDir, "out.json")
|
||||
cmd = ['codeql', 'bqrs', 'decode', bqrsFile,
|
||||
'--format=json', '--output', generatedJson]
|
||||
ret = subprocess.call(cmd)
|
||||
if ret != 0:
|
||||
print("Failed to decode BQRS. Failed command was: " + shlex.join(cmd))
|
||||
sys.exit(1)
|
||||
|
||||
with open(generatedJson) as f:
|
||||
results = json.load(f)
|
||||
|
||||
try:
|
||||
results['#select']['tuples']
|
||||
except KeyError:
|
||||
print('Unexpected JSON output - no tuples found')
|
||||
exit(1)
|
||||
|
||||
rows = ""
|
||||
for (row) in results['#select']['tuples']:
|
||||
rows += " \"" + row[0] + "\",\n"
|
||||
|
||||
return rows[:-2]
|
||||
|
||||
|
||||
def asCsvModel(superclass, kind, rows):
|
||||
classTemplate = """
|
||||
private class {0}{1}Csv extends {2} {{
|
||||
override predicate row(string row) {{
|
||||
row =
|
||||
[
|
||||
{3}
|
||||
]
|
||||
}}
|
||||
}}
|
||||
"""
|
||||
if rows.strip() == "":
|
||||
return ""
|
||||
return classTemplate.format(shortname[0].upper() + shortname[1:], kind.capitalize(), superclass, rows)
|
||||
|
||||
|
||||
if generateSummaries:
|
||||
summaryRows = runQuery("summary models", "CaptureSummaryModels.ql")
|
||||
summaryCsv = asCsvModel("SummaryModelCsv", "summary", summaryRows)
|
||||
else:
|
||||
summaryCsv = ""
|
||||
|
||||
if generateSinks:
|
||||
sinkRows = runQuery("sink models", "CaptureSinkModels.ql")
|
||||
sinkCsv = asCsvModel("SinkModelCsv", "sinks", sinkRows)
|
||||
else:
|
||||
sinkCsv = ""
|
||||
|
||||
if generateSources:
|
||||
sourceRows = runQuery("source models", "CaptureSourceModels.ql")
|
||||
sourceCsv = asCsvModel("SourceModelCsv", "sources", sourceRows)
|
||||
else:
|
||||
sourceCsv = ""
|
||||
|
||||
qllTemplate = """
|
||||
/** Definitions of taint steps in the {0} framework */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
{1}
|
||||
{2}
|
||||
{3}
|
||||
|
||||
"""
|
||||
|
||||
|
||||
qllContents = qllTemplate.format(shortname, sinkCsv, sourceCsv, summaryCsv)
|
||||
|
||||
|
||||
with open(frameworkTarget, "w") as frameworkQll:
|
||||
frameworkQll.write(qllContents)
|
||||
|
||||
cmd = ['codeql', 'query', 'format', '--in-place', frameworkTarget]
|
||||
ret = subprocess.call(cmd)
|
||||
if ret != 0:
|
||||
print("Failed to format query. Failed command was: " + shlex.join(cmd))
|
||||
sys.exit(1)
|
||||
|
||||
print("")
|
||||
print("CSV model written to " + frameworkTarget)
|
||||
173
java/ql/src/utils/model-generator/ModelGeneratorUtils.qll
Normal file
173
java/ql/src/utils/model-generator/ModelGeneratorUtils.qll
Normal file
@@ -0,0 +1,173 @@
|
||||
import java
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.dataflow.internal.ContainerFlow
|
||||
private import semmle.code.java.dataflow.internal.DataFlowImplCommon
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.internal.DataFlowPrivate
|
||||
|
||||
Method superImpl(Method m) {
|
||||
result = m.getAnOverride() and
|
||||
not exists(result.getAnOverride()) and
|
||||
not m instanceof ToStringMethod
|
||||
}
|
||||
|
||||
class TargetAPI extends Callable {
|
||||
TargetAPI() {
|
||||
this.isPublic() and
|
||||
this.fromSource() and
|
||||
(
|
||||
this.getDeclaringType().isPublic() or
|
||||
superImpl(this).getDeclaringType().isPublic()
|
||||
) and
|
||||
isRelevantForModels(this)
|
||||
}
|
||||
}
|
||||
|
||||
private string isExtensible(RefType ref) {
|
||||
if ref.isFinal() then result = "false" else result = "true"
|
||||
}
|
||||
|
||||
predicate isRelevantForModels(Callable api) {
|
||||
not isInTestFile(api.getCompilationUnit().getFile()) and
|
||||
not isJdkInternal(api.getCompilationUnit())
|
||||
}
|
||||
|
||||
private predicate isInTestFile(File file) {
|
||||
file.getAbsolutePath().matches("%src/test/%") or
|
||||
file.getAbsolutePath().matches("%/guava-tests/%") or
|
||||
file.getAbsolutePath().matches("%/guava-testlib/%")
|
||||
}
|
||||
|
||||
private predicate isJdkInternal(CompilationUnit cu) {
|
||||
cu.getPackage().getName().matches("org.graalvm%") or
|
||||
cu.getPackage().getName().matches("com.sun%") or
|
||||
cu.getPackage().getName().matches("javax.swing%") or
|
||||
cu.getPackage().getName().matches("java.awt%") or
|
||||
cu.getPackage().getName().matches("sun%") or
|
||||
cu.getPackage().getName().matches("jdk.%") or
|
||||
cu.getPackage().getName().matches("java2d.%") or
|
||||
cu.getPackage().getName().matches("build.tools.%") or
|
||||
cu.getPackage().getName().matches("propertiesparser.%") or
|
||||
cu.getPackage().getName().matches("org.jcp.%") or
|
||||
cu.getPackage().getName().matches("org.w3c.%") or
|
||||
cu.getPackage().getName().matches("org.ietf.jgss.%") or
|
||||
cu.getPackage().getName().matches("org.xml.sax%") or
|
||||
cu.getPackage().getName() = "compileproperties" or
|
||||
cu.getPackage().getName() = "netscape.javascript" or
|
||||
cu.getPackage().getName() = ""
|
||||
}
|
||||
|
||||
bindingset[input, output]
|
||||
string asTaintModel(TargetAPI api, string input, string output) {
|
||||
result = asSummaryModel(api, input, output, "taint")
|
||||
}
|
||||
|
||||
bindingset[input, output]
|
||||
string asValueModel(TargetAPI api, string input, string output) {
|
||||
result = asSummaryModel(api, input, output, "value")
|
||||
}
|
||||
|
||||
bindingset[input, output, kind]
|
||||
string asSummaryModel(TargetAPI api, string input, string output, string kind) {
|
||||
result =
|
||||
asPartialModel(api) + input + ";" //
|
||||
+ output + ";" //
|
||||
+ kind
|
||||
}
|
||||
|
||||
bindingset[input, kind]
|
||||
string asSinkModel(TargetAPI api, string input, string kind) {
|
||||
result = asPartialModel(api) + input + ";" + kind
|
||||
}
|
||||
|
||||
bindingset[output, kind]
|
||||
string asSourceModel(TargetAPI api, string output, string kind) {
|
||||
result = asPartialModel(api) + output + ";" + kind
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the first 6 columns for CSV rows.
|
||||
*/
|
||||
private string asPartialModel(TargetAPI api) {
|
||||
result =
|
||||
typeAsSummaryModel(api) + ";" //
|
||||
+ isExtensible(bestTypeForModel(api)) + ";" //
|
||||
+ api.getName() + ";" //
|
||||
+ paramsString(api) + ";" //
|
||||
+ /* ext + */ ";" //
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the appropriate type name for the model. Either the type
|
||||
* declaring the method or the supertype introducing the method.
|
||||
*/
|
||||
private string typeAsSummaryModel(TargetAPI api) { result = typeAsModel(bestTypeForModel(api)) }
|
||||
|
||||
private RefType bestTypeForModel(TargetAPI api) {
|
||||
if exists(superImpl(api))
|
||||
then superImpl(api).fromSource() and result = superImpl(api).getDeclaringType()
|
||||
else result = api.getDeclaringType()
|
||||
}
|
||||
|
||||
private string typeAsModel(RefType type) {
|
||||
result = type.getCompilationUnit().getPackage().getName() + ";" + type.nestedName()
|
||||
}
|
||||
|
||||
predicate isRelevantType(Type t) {
|
||||
not t instanceof TypeClass and
|
||||
not t instanceof EnumType and
|
||||
not t instanceof PrimitiveType and
|
||||
not t instanceof BoxedType and
|
||||
not t.(RefType).getAnAncestor().hasQualifiedName("java.lang", "Number") and
|
||||
not t.(RefType).getAnAncestor().hasQualifiedName("java.nio.charset", "Charset") and
|
||||
(
|
||||
not t.(Array).getElementType() instanceof PrimitiveType or
|
||||
isPrimitiveTypeUsedForBulkData(t.(Array).getElementType())
|
||||
) and
|
||||
(
|
||||
not t.(Array).getElementType() instanceof BoxedType or
|
||||
isPrimitiveTypeUsedForBulkData(t.(Array).getElementType())
|
||||
) and
|
||||
(
|
||||
not t.(CollectionType).getElementType() instanceof BoxedType or
|
||||
isPrimitiveTypeUsedForBulkData(t.(CollectionType).getElementType())
|
||||
)
|
||||
}
|
||||
|
||||
predicate isRelevantTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(DataFlow::Content f |
|
||||
readStep(node1, f, node2) and
|
||||
if f instanceof DataFlow::FieldContent
|
||||
then isRelevantType(f.(DataFlow::FieldContent).getField().getType())
|
||||
else any()
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Content f | storeStep(node1, f, node2) |
|
||||
f instanceof DataFlow::ArrayContent or
|
||||
f instanceof DataFlow::CollectionContent or
|
||||
f instanceof DataFlow::MapKeyContent or
|
||||
f instanceof DataFlow::MapValueContent
|
||||
)
|
||||
}
|
||||
|
||||
string returnNodeAsOutput(TargetAPI api, ReturnNodeExt node) {
|
||||
if node.getKind() instanceof ValueReturnKind
|
||||
then result = "ReturnValue"
|
||||
else
|
||||
result = parameterAccess(api.getParameter(node.getKind().(ParamUpdateReturnKind).getPosition()))
|
||||
}
|
||||
|
||||
string parameterAccess(Parameter p) {
|
||||
if
|
||||
p.getType() instanceof Array and
|
||||
not isPrimitiveTypeUsedForBulkData(p.getType().(Array).getElementType())
|
||||
then result = "ArrayElement of Argument[" + p.getPosition() + "]"
|
||||
else
|
||||
if p.getType() instanceof ContainerType
|
||||
then result = "Element of Argument[" + p.getPosition() + "]"
|
||||
else result = "Argument[" + p.getPosition() + "]"
|
||||
}
|
||||
|
||||
predicate isPrimitiveTypeUsedForBulkData(Type t) {
|
||||
t.getName().regexpMatch("byte|char|Byte|Character")
|
||||
}
|
||||
@@ -151,15 +151,7 @@ public class A {
|
||||
|
||||
forEach(new Object[] {source(16)}, x -> sink(x)); // $ flow=16
|
||||
|
||||
// Spurious flow from 17 is reasonable as it would likely
|
||||
// also occur if the lambda body was inlined in a for loop.
|
||||
// It occurs from the combination of being able to observe
|
||||
// the side-effect of the callback on the other argument and
|
||||
// being able to chain summaries that update/read arguments,
|
||||
// e.g. fluent apis.
|
||||
// Spurious flow from 18 is due to not matching call targets
|
||||
// in a return-from-call-to-enter-call flow sequence.
|
||||
forEach(new Object[2][], xs -> { sink(xs[0]); xs[0] = source(17); }); // $ SPURIOUS: flow=17 flow=18
|
||||
forEach(new Object[2][], xs -> { sink(xs[0]); xs[0] = source(17); });
|
||||
|
||||
Object[][] xss = new Object[][] { { null } };
|
||||
forEach(xss, x -> {x[0] = source(18);});
|
||||
|
||||
@@ -9,5 +9,8 @@ public class CharSeq {
|
||||
|
||||
CharSequence seqFromSeq = seq.subSequence(0, 1);
|
||||
sink(seqFromSeq);
|
||||
|
||||
String stringFromSeq = seq.toString();
|
||||
sink(stringFromSeq);
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,7 @@
|
||||
| B.java:15:21:15:27 | taint(...) | B.java:157:10:157:46 | toFile(...) |
|
||||
| CharSeq.java:7:26:7:32 | taint(...) | CharSeq.java:8:12:8:14 | seq |
|
||||
| CharSeq.java:7:26:7:32 | taint(...) | CharSeq.java:11:12:11:21 | seqFromSeq |
|
||||
| CharSeq.java:7:26:7:32 | taint(...) | CharSeq.java:14:12:14:24 | stringFromSeq |
|
||||
| MethodFlow.java:7:22:7:28 | taint(...) | MethodFlow.java:8:10:8:16 | tainted |
|
||||
| MethodFlow.java:9:31:9:37 | taint(...) | MethodFlow.java:10:10:10:17 | tainted2 |
|
||||
| MethodFlow.java:11:35:11:41 | taint(...) | MethodFlow.java:12:10:12:17 | tainted3 |
|
||||
|
||||
@@ -436,7 +436,7 @@ public class Test {
|
||||
sink(y); // $ hasValueFlow=reduce_3 hasValueFlow=reduce_4 hasValueFlow=reduce_5
|
||||
return source("reduce_5");
|
||||
});
|
||||
sink(out); // $ hasValueFlow=reduce_4 hasValueFlow=reduce_5 SPURIOUS: hasValueFlow=reduce_3
|
||||
sink(out); // $ hasValueFlow=reduce_4 hasValueFlow=reduce_5
|
||||
}
|
||||
{
|
||||
// "java.util.stream;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;Argument[0];ReturnValue;value"
|
||||
|
||||
@@ -3,4 +3,3 @@
|
||||
| ConstructedOverrides.java:17:7:17:9 | Sub | usedGeneric(U, String) | Super.class:0:0:0:0 | Super<String> | usedGeneric(U, String) |
|
||||
| ConstructedOverrides.java:23:7:23:10 | Sub2 | unusedGeneric(V, String) | Super.class:0:0:0:0 | Super<String> | unusedGeneric(U, String) |
|
||||
| ConstructedOverrides.java:23:7:23:10 | Sub2 | usedGeneric(V, String) | ConstructedOverrides.java:17:7:17:9 | Sub | usedGeneric(U, String) |
|
||||
| ConstructedOverrides.java:23:7:23:10 | Sub2 | usedGeneric(V, String) | Super.class:0:0:0:0 | Super<String> | usedGeneric(U, String) |
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
| p;PrivateFlowViaPublicInterface$SPI;true;openStream;();;Argument[-1];create-file |
|
||||
| p;Sinks;true;copyFileToDirectory;(Path,Path,CopyOption[]);;Argument[1];create-file |
|
||||
| p;Sinks;true;readUrl;(URL,Charset);;Argument[0];open-url |
|
||||
| p;Sources;true;readUrl;(URL);;Argument[0];open-url |
|
||||
@@ -0,0 +1 @@
|
||||
utils/model-generator/CaptureSinkModels.ql
|
||||
@@ -0,0 +1,4 @@
|
||||
| p;Sources;true;readUrl;(URL);;ReturnValue;remote |
|
||||
| p;Sources;true;socketStream;();;ReturnValue;remote |
|
||||
| p;Sources;true;sourceToParameter;(InputStream[],List);;ArrayElement of Argument[0];remote |
|
||||
| p;Sources;true;sourceToParameter;(InputStream[],List);;Element of Argument[1];remote |
|
||||
@@ -0,0 +1 @@
|
||||
utils/model-generator/CaptureSourceModels.ql
|
||||
@@ -0,0 +1,47 @@
|
||||
| p;Factory;false;create;(String);;Argument[0];ReturnValue;taint |
|
||||
| p;Factory;false;create;(String,int);;Argument[0];ReturnValue;taint |
|
||||
| p;Factory;false;getValue;();;Argument[-1];ReturnValue;taint |
|
||||
| p;FinalClass;false;returnsInput;(String);;Argument[0];ReturnValue;taint |
|
||||
| p;FluentAPI;false;returnsThis;(String);;Argument[-1];ReturnValue;value |
|
||||
| p;ImmutablePojo;false;ImmutablePojo;(String,int);;Argument[0];Argument[-1];taint |
|
||||
| p;ImmutablePojo;false;getValue;();;Argument[-1];ReturnValue;taint |
|
||||
| p;ImmutablePojo;false;or;(String);;Argument[-1];ReturnValue;taint |
|
||||
| p;ImmutablePojo;false;or;(String);;Argument[0];ReturnValue;taint |
|
||||
| p;InnerClasses$CaptureMe;true;yesCm;(String);;Argument[0];ReturnValue;taint |
|
||||
| p;InnerClasses;true;yes;(String);;Argument[0];ReturnValue;taint |
|
||||
| p;InnerHolder;false;append;(String);;Argument[0];Argument[-1];taint |
|
||||
| p;InnerHolder;false;explicitSetContext;(String);;Argument[0];Argument[-1];taint |
|
||||
| p;InnerHolder;false;getValue;();;Argument[-1];ReturnValue;taint |
|
||||
| p;InnerHolder;false;setContext;(String);;Argument[0];Argument[-1];taint |
|
||||
| p;Joiner;false;Joiner;(CharSequence);;Argument[0];Argument[-1];taint |
|
||||
| p;Joiner;false;Joiner;(CharSequence,CharSequence,CharSequence);;Argument[0];Argument[-1];taint |
|
||||
| p;Joiner;false;Joiner;(CharSequence,CharSequence,CharSequence);;Argument[1];Argument[-1];taint |
|
||||
| p;Joiner;false;Joiner;(CharSequence,CharSequence,CharSequence);;Argument[2];Argument[-1];taint |
|
||||
| p;Joiner;false;add;(CharSequence);;Argument[-1];ReturnValue;value |
|
||||
| p;Joiner;false;merge;(Joiner);;Argument[-1];ReturnValue;value |
|
||||
| p;Joiner;false;setEmptyValue;(CharSequence);;Argument[-1];ReturnValue;value |
|
||||
| p;Joiner;false;setEmptyValue;(CharSequence);;Argument[0];Argument[-1];taint |
|
||||
| p;Joiner;false;setEmptyValue;(CharSequence);;Argument[0];ReturnValue;taint |
|
||||
| p;Joiner;false;toString;();;Argument[-1];ReturnValue;taint |
|
||||
| p;MultipleImpls$Strat2;true;getValue;();;Argument[-1];ReturnValue;taint |
|
||||
| p;MultipleImpls$Strategy;true;doSomething;(String);;Argument[0];Argument[-1];taint |
|
||||
| p;MultipleImpls$Strategy;true;doSomething;(String);;Argument[0];ReturnValue;taint |
|
||||
| p;ParamFlow;true;addTo;(String,List);;Argument[0];Element of Argument[1];taint |
|
||||
| p;ParamFlow;true;returnArrayElement;(String[]);;ArrayElement of Argument[0];ReturnValue;taint |
|
||||
| p;ParamFlow;true;returnCollectionElement;(List);;Element of Argument[0];ReturnValue;taint |
|
||||
| p;ParamFlow;true;returnIterableElement;(Iterable);;Element of Argument[0];ReturnValue;taint |
|
||||
| p;ParamFlow;true;returnIteratorElement;(Iterator);;Element of Argument[0];ReturnValue;taint |
|
||||
| p;ParamFlow;true;returnMultipleParameters;(String,String);;Argument[0];ReturnValue;taint |
|
||||
| p;ParamFlow;true;returnMultipleParameters;(String,String);;Argument[1];ReturnValue;taint |
|
||||
| p;ParamFlow;true;returnVarArgElement;(String[]);;ArrayElement of Argument[0];ReturnValue;taint |
|
||||
| p;ParamFlow;true;returnsInput;(String);;Argument[0];ReturnValue;taint |
|
||||
| p;ParamFlow;true;writeChunked;(byte[],OutputStream);;Argument[0];Argument[1];taint |
|
||||
| p;ParamFlow;true;writeChunked;(char[],OutputStream);;Argument[0];Argument[1];taint |
|
||||
| p;Pojo;false;fillIn;(List);;Argument[-1];Element of Argument[0];taint |
|
||||
| p;Pojo;false;getBoxedBytes;();;Argument[-1];ReturnValue;taint |
|
||||
| p;Pojo;false;getBoxedChars;();;Argument[-1];ReturnValue;taint |
|
||||
| p;Pojo;false;getByteArray;();;Argument[-1];ReturnValue;taint |
|
||||
| p;Pojo;false;getCharArray;();;Argument[-1];ReturnValue;taint |
|
||||
| p;Pojo;false;getValue;();;Argument[-1];ReturnValue;taint |
|
||||
| p;Pojo;false;setValue;(String);;Argument[0];Argument[-1];taint |
|
||||
| p;PrivateFlowViaPublicInterface;true;createAnSPI;(File);;Argument[0];ReturnValue;taint |
|
||||
@@ -0,0 +1 @@
|
||||
utils/model-generator/CaptureSummaryModels.ql
|
||||
@@ -0,0 +1,13 @@
|
||||
package p;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
|
||||
public abstract class AbstractImplOfExternalSPI implements FileFilter {
|
||||
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
30
java/ql/test/utils/model-generator/p/Factory.java
Normal file
30
java/ql/test/utils/model-generator/p/Factory.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package p;
|
||||
|
||||
public final class Factory {
|
||||
|
||||
private String value;
|
||||
|
||||
private int intValue;
|
||||
|
||||
public static Factory create(String value, int foo) {
|
||||
return new Factory(value, foo);
|
||||
}
|
||||
|
||||
public static Factory create(String value) {
|
||||
return new Factory(value, 0);
|
||||
}
|
||||
|
||||
private Factory(String value, int intValue) {
|
||||
this.value = value;
|
||||
this.intValue = intValue;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public int getIntValue() {
|
||||
return intValue;
|
||||
}
|
||||
|
||||
}
|
||||
15
java/ql/test/utils/model-generator/p/FinalClass.java
Normal file
15
java/ql/test/utils/model-generator/p/FinalClass.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package p;
|
||||
|
||||
public final class FinalClass {
|
||||
|
||||
private static final String C = "constant";
|
||||
|
||||
public String returnsInput(String input) {
|
||||
return input;
|
||||
}
|
||||
|
||||
public String returnsConstant() {
|
||||
return C;
|
||||
}
|
||||
|
||||
}
|
||||
15
java/ql/test/utils/model-generator/p/FluentAPI.java
Normal file
15
java/ql/test/utils/model-generator/p/FluentAPI.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package p;
|
||||
|
||||
public final class FluentAPI {
|
||||
|
||||
public FluentAPI returnsThis(String input) {
|
||||
return this;
|
||||
}
|
||||
|
||||
public class Inner {
|
||||
public FluentAPI notThis(String input) {
|
||||
return FluentAPI.this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
26
java/ql/test/utils/model-generator/p/ImmutablePojo.java
Normal file
26
java/ql/test/utils/model-generator/p/ImmutablePojo.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package p;
|
||||
|
||||
public final class ImmutablePojo {
|
||||
|
||||
private final String value;
|
||||
|
||||
private final long x;
|
||||
|
||||
public ImmutablePojo(String value, int x) {
|
||||
this.value = value;
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public long getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public String or(String defaultValue) {
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
}
|
||||
19
java/ql/test/utils/model-generator/p/ImplOfExternalSPI.java
Normal file
19
java/ql/test/utils/model-generator/p/ImplOfExternalSPI.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package p;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
||||
public class ImplOfExternalSPI extends AbstractImplOfExternalSPI {
|
||||
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
try {
|
||||
Files.createFile(pathname.toPath());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
21
java/ql/test/utils/model-generator/p/InnerClasses.java
Normal file
21
java/ql/test/utils/model-generator/p/InnerClasses.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package p;
|
||||
|
||||
public class InnerClasses {
|
||||
|
||||
class IgnoreMe {
|
||||
public String no(String input) {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
public class CaptureMe {
|
||||
public String yesCm(String input) {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
public String yes(String input) {
|
||||
return input;
|
||||
}
|
||||
|
||||
}
|
||||
37
java/ql/test/utils/model-generator/p/InnerHolder.java
Normal file
37
java/ql/test/utils/model-generator/p/InnerHolder.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package p;
|
||||
|
||||
public final class InnerHolder {
|
||||
|
||||
private class Context {
|
||||
private String value;
|
||||
|
||||
Context(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private Context context = null;
|
||||
|
||||
private StringBuilder sb = new StringBuilder();
|
||||
|
||||
public void setContext(String value) {
|
||||
context = new Context(value);
|
||||
}
|
||||
|
||||
public void explicitSetContext(String value) {
|
||||
this.context = new Context(value);
|
||||
}
|
||||
|
||||
public void append(String value) {
|
||||
sb.append(value);
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return context.getValue();
|
||||
}
|
||||
|
||||
}
|
||||
118
java/ql/test/utils/model-generator/p/Joiner.java
Normal file
118
java/ql/test/utils/model-generator/p/Joiner.java
Normal file
@@ -0,0 +1,118 @@
|
||||
package p;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class Joiner {
|
||||
private final String prefix;
|
||||
private final String delimiter;
|
||||
private final String suffix;
|
||||
private String[] elts;
|
||||
private int size;
|
||||
private int len;
|
||||
private String emptyValue;
|
||||
public Joiner(CharSequence delimiter) {
|
||||
this(delimiter, "", "");
|
||||
}
|
||||
|
||||
public Joiner(CharSequence delimiter,
|
||||
CharSequence prefix,
|
||||
CharSequence suffix) {
|
||||
Objects.requireNonNull(prefix, "The prefix must not be null");
|
||||
Objects.requireNonNull(delimiter, "The delimiter must not be null");
|
||||
Objects.requireNonNull(suffix, "The suffix must not be null");
|
||||
this.prefix = prefix.toString();
|
||||
this.delimiter = delimiter.toString();
|
||||
this.suffix = suffix.toString();
|
||||
checkAddLength(0, 0);
|
||||
}
|
||||
|
||||
public Joiner setEmptyValue(CharSequence emptyValue) {
|
||||
this.emptyValue = Objects.requireNonNull(emptyValue,
|
||||
"The empty value must not be null").toString();
|
||||
return this;
|
||||
}
|
||||
|
||||
private static int getChars(String s, char[] chars, int start) {
|
||||
int len = s.length();
|
||||
s.getChars(0, len, chars, start);
|
||||
return len;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final String[] elts = this.elts;
|
||||
if (elts == null && emptyValue != null) {
|
||||
return emptyValue;
|
||||
}
|
||||
final int size = this.size;
|
||||
final int addLen = prefix.length() + suffix.length();
|
||||
if (addLen == 0) {
|
||||
compactElts();
|
||||
return size == 0 ? "" : elts[0];
|
||||
}
|
||||
final String delimiter = this.delimiter;
|
||||
final char[] chars = new char[len + addLen];
|
||||
int k = getChars(prefix, chars, 0);
|
||||
if (size > 0) {
|
||||
k += getChars(elts[0], chars, k);
|
||||
for (int i = 1; i < size; i++) {
|
||||
k += getChars(delimiter, chars, k);
|
||||
k += getChars(elts[i], chars, k);
|
||||
}
|
||||
}
|
||||
k += getChars(suffix, chars, k);
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
public Joiner add(CharSequence newElement) {
|
||||
final String elt = String.valueOf(newElement);
|
||||
if (elts == null) {
|
||||
elts = new String[8];
|
||||
} else {
|
||||
if (size == elts.length)
|
||||
elts = Arrays.copyOf(elts, 2 * size);
|
||||
len = checkAddLength(len, delimiter.length());
|
||||
}
|
||||
len = checkAddLength(len, elt.length());
|
||||
elts[size++] = elt;
|
||||
return this;
|
||||
}
|
||||
|
||||
private int checkAddLength(int oldLen, int inc) {
|
||||
long newLen = (long)oldLen + (long)inc;
|
||||
long tmpLen = newLen + (long)prefix.length() + (long)suffix.length();
|
||||
if (tmpLen != (int)tmpLen) {
|
||||
throw new OutOfMemoryError("Requested array size exceeds VM limit");
|
||||
}
|
||||
return (int)newLen;
|
||||
}
|
||||
|
||||
public Joiner merge(Joiner other) {
|
||||
Objects.requireNonNull(other);
|
||||
if (other.elts == null) {
|
||||
return this;
|
||||
}
|
||||
other.compactElts();
|
||||
return add(other.elts[0]);
|
||||
}
|
||||
|
||||
private void compactElts() {
|
||||
if (size > 1) {
|
||||
final char[] chars = new char[len];
|
||||
int i = 1, k = getChars(elts[0], chars, 0);
|
||||
do {
|
||||
k += getChars(delimiter, chars, k);
|
||||
k += getChars(elts[i], chars, k);
|
||||
elts[i] = null;
|
||||
} while (++i < size);
|
||||
size = 1;
|
||||
elts[0] = new String(chars);
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return (size == 0 && emptyValue != null) ? emptyValue.length() :
|
||||
len + prefix.length() + suffix.length();
|
||||
}
|
||||
}
|
||||
38
java/ql/test/utils/model-generator/p/MultipleImpls.java
Normal file
38
java/ql/test/utils/model-generator/p/MultipleImpls.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package p;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class MultipleImpls {
|
||||
|
||||
public static interface Strategy {
|
||||
String doSomething(String value);
|
||||
}
|
||||
|
||||
public static class Strat1 implements Strategy {
|
||||
public String doSomething(String value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
// implements in different library should not count as impl
|
||||
public static class Strat3 implements Callable<String> {
|
||||
|
||||
@Override
|
||||
public String call() throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
public static class Strat2 implements Strategy {
|
||||
private String foo;
|
||||
|
||||
public String doSomething(String value) {
|
||||
this.foo = value;
|
||||
return "none";
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return this.foo;
|
||||
}
|
||||
}
|
||||
}
|
||||
64
java/ql/test/utils/model-generator/p/ParamFlow.java
Normal file
64
java/ql/test/utils/model-generator/p/ParamFlow.java
Normal file
@@ -0,0 +1,64 @@
|
||||
package p;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
|
||||
public class ParamFlow {
|
||||
|
||||
public String returnsInput(String input) {
|
||||
return input;
|
||||
}
|
||||
|
||||
public int ignorePrimitiveReturnValue(String input) {
|
||||
return input.length();
|
||||
}
|
||||
|
||||
public String returnMultipleParameters(String one, String two) {
|
||||
if (System.currentTimeMillis() > 100) {
|
||||
return two;
|
||||
}
|
||||
return one;
|
||||
}
|
||||
|
||||
public String returnArrayElement(String[] input) {
|
||||
return input[0];
|
||||
}
|
||||
|
||||
public String returnVarArgElement(String... input) {
|
||||
return input[0];
|
||||
}
|
||||
|
||||
public String returnCollectionElement(List<String> input) {
|
||||
return input.get(0);
|
||||
}
|
||||
|
||||
public String returnIteratorElement(Iterator<String> input) {
|
||||
return input.next();
|
||||
}
|
||||
|
||||
public String returnIterableElement(Iterable<String> input) {
|
||||
return input.iterator().next();
|
||||
}
|
||||
|
||||
public Class<?> mapType(Class<?> input) {
|
||||
return input;
|
||||
}
|
||||
|
||||
public void writeChunked(byte[] data, OutputStream output)
|
||||
throws IOException {
|
||||
output.write(data, 0, data.length);
|
||||
}
|
||||
|
||||
public void writeChunked(char[] data, OutputStream output)
|
||||
throws IOException {
|
||||
output.write(String.valueOf(data).getBytes(), 0, data.length);
|
||||
}
|
||||
|
||||
public void addTo(String data, List<String> target) {
|
||||
target.add(data);
|
||||
}
|
||||
|
||||
}
|
||||
98
java/ql/test/utils/model-generator/p/Pojo.java
Normal file
98
java/ql/test/utils/model-generator/p/Pojo.java
Normal file
@@ -0,0 +1,98 @@
|
||||
package p;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public final class Pojo {
|
||||
|
||||
private class Holder {
|
||||
private String value;
|
||||
|
||||
Holder(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
int length() {
|
||||
return value.length();
|
||||
}
|
||||
}
|
||||
|
||||
private String value;
|
||||
|
||||
private int intValue = 2;
|
||||
|
||||
private byte[] byteArray = new byte[] {1, 2, 3} ;
|
||||
private float[] floatArray = new float[] {1, 2, 3} ;
|
||||
private char[] charArray = new char[] {'a', 'b', 'c'} ;
|
||||
private List<Character> charList = Arrays.asList('a', 'b', 'c');
|
||||
private Byte[] byteObjectArray = new Byte[] { 1, 2, 3 };
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int doNotSetValue(String value) {
|
||||
Holder h = new Holder(value);
|
||||
return h.length();
|
||||
}
|
||||
|
||||
public int getIntValue() {
|
||||
return intValue;
|
||||
}
|
||||
|
||||
public Integer getBoxedValue() {
|
||||
return Integer.valueOf(intValue);
|
||||
}
|
||||
|
||||
public int[] getPrimitiveArray() {
|
||||
return new int[] { intValue };
|
||||
}
|
||||
|
||||
public char[] getCharArray() {
|
||||
return charArray;
|
||||
}
|
||||
|
||||
public byte[] getByteArray() {
|
||||
return byteArray;
|
||||
}
|
||||
|
||||
public float[] getFloatArray() {
|
||||
return floatArray;
|
||||
}
|
||||
|
||||
public Integer[] getBoxedArray() {
|
||||
return new Integer[] { Integer.valueOf(intValue) };
|
||||
}
|
||||
|
||||
public Collection<Integer> getBoxedCollection() {
|
||||
return List.of(Integer.valueOf(intValue));
|
||||
}
|
||||
|
||||
public List<Character> getBoxedChars() {
|
||||
return charList;
|
||||
}
|
||||
|
||||
public Byte[] getBoxedBytes() {
|
||||
return byteObjectArray;
|
||||
}
|
||||
|
||||
public BigInteger getBigInt() {
|
||||
return BigInteger.valueOf(intValue);
|
||||
}
|
||||
|
||||
public BigDecimal getBigDecimal() {
|
||||
return new BigDecimal(value);
|
||||
}
|
||||
|
||||
public void fillIn(List<String> target) {
|
||||
target.add(value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package p;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class PrivateFlowViaPublicInterface {
|
||||
|
||||
static class RandomPojo {
|
||||
public File someFile = new File("someFile");
|
||||
}
|
||||
public static interface SPI {
|
||||
OutputStream openStream() throws IOException;
|
||||
|
||||
default OutputStream openStreamNone() throws IOException {
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
private static final class PrivateImplWithSink implements SPI {
|
||||
|
||||
private File file;
|
||||
|
||||
public PrivateImplWithSink(File file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream openStream() throws IOException {
|
||||
return new FileOutputStream(file);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class PrivateImplWithRandomField implements SPI {
|
||||
|
||||
public PrivateImplWithRandomField(File file) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream openStream() throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream openStreamNone() throws IOException {
|
||||
return new FileOutputStream(new RandomPojo().someFile);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static SPI createAnSPI(File file) {
|
||||
return new PrivateImplWithSink(file);
|
||||
}
|
||||
|
||||
public static SPI createAnSPIWithoutTrackingFile(File file) {
|
||||
return new PrivateImplWithRandomField(file);
|
||||
}
|
||||
|
||||
}
|
||||
24
java/ql/test/utils/model-generator/p/Sinks.java
Normal file
24
java/ql/test/utils/model-generator/p/Sinks.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package p;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class Sinks {
|
||||
|
||||
public Path copyFileToDirectory(final Path sourceFile, final Path targetFile, final CopyOption... copyOptions) throws IOException {
|
||||
return Files.copy(sourceFile, targetFile, copyOptions);
|
||||
}
|
||||
|
||||
public String readUrl(final URL url, Charset encoding) throws IOException {
|
||||
try (InputStream in = url.openStream()) {
|
||||
byte[] bytes = in.readAllBytes();
|
||||
return new String(bytes, encoding);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
17
java/ql/test/utils/model-generator/p/SomeEnum.java
Normal file
17
java/ql/test/utils/model-generator/p/SomeEnum.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package p;
|
||||
|
||||
enum SomeEnum {
|
||||
|
||||
FOO("input");
|
||||
|
||||
private String input;
|
||||
|
||||
private SomeEnum(String input) {
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return input;
|
||||
}
|
||||
|
||||
}
|
||||
28
java/ql/test/utils/model-generator/p/Sources.java
Normal file
28
java/ql/test/utils/model-generator/p/Sources.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package p;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.URL;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class Sources {
|
||||
|
||||
public InputStream readUrl(final URL url) throws IOException {
|
||||
return url.openConnection().getInputStream();
|
||||
}
|
||||
|
||||
public InputStream socketStream() throws IOException {
|
||||
ServerSocket socket = new ServerSocket(123);
|
||||
return socket.accept().getInputStream();
|
||||
}
|
||||
|
||||
public void sourceToParameter(InputStream[] streams, List<InputStream> otherStreams) throws IOException {
|
||||
ServerSocket socket = new ServerSocket(123);
|
||||
streams[0] = socket.accept().getInputStream();
|
||||
otherStreams.add(socket.accept().getInputStream());
|
||||
}
|
||||
|
||||
}
|
||||
4
javascript/change-notes/2021-08-26-bad-tag-filter.md
Normal file
4
javascript/change-notes/2021-08-26-bad-tag-filter.md
Normal file
@@ -0,0 +1,4 @@
|
||||
lgtm,codescanning
|
||||
* A new query, `js/bad-tag-filter`, has been added to the query suite,
|
||||
highlighting regular expressions that only match a subset of the HTML tags
|
||||
it is supposed to match.
|
||||
@@ -4,6 +4,8 @@
|
||||
* Extracts data about the functions in the database for use in adaptive threat modeling (ATM).
|
||||
*/
|
||||
|
||||
private import EndpointFeatures::NeighborhoodBodies
|
||||
|
||||
module Raw {
|
||||
private import javascript as raw
|
||||
|
||||
@@ -30,6 +32,9 @@ module Raw {
|
||||
entity.getNumBodyStmt() = 0 and not exists(entity.getAReturnedExpr())
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for RawAstNode (could be an alias instead of a newtype)
|
||||
*/
|
||||
newtype WrappedAstNode = TAstNode(RawAstNode rawNode)
|
||||
|
||||
/**
|
||||
@@ -44,6 +49,10 @@ module Raw {
|
||||
|
||||
AstNode getParentNode() { result = TAstNode(rawNode.getParent()) }
|
||||
|
||||
raw::ASTNode getNode() { result = rawNode }
|
||||
|
||||
raw::StmtContainer getContainer() { result = rawNode.getContainer() }
|
||||
|
||||
/**
|
||||
* Holds if the AST node has `result` as its `index`th attribute.
|
||||
*
|
||||
@@ -128,6 +137,11 @@ module Raw {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the `Raw::AstNode` wrapper of `rawNode`
|
||||
*/
|
||||
AstNode astNode(RawAstNode rawNode) { result = TAstNode(rawNode) }
|
||||
|
||||
/**
|
||||
* Holds if `result` is the `index`'th child of the `parent` entity. Such
|
||||
* a node is a root of an AST associated with this entity.
|
||||
@@ -324,6 +338,11 @@ module Wrapped {
|
||||
Raw::Location getLocation() { result = rawNode.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the `Wrapped::AstNode` for a `rawNode` in the context of `entity`
|
||||
*/
|
||||
AstNode astNode(Raw::Entity entity, Raw::AstNode rawNode) { result = TAstNode(entity, rawNode) }
|
||||
|
||||
/**
|
||||
* A synthetic AST node, created to be a leaf for an otherwise non-leaf attribute.
|
||||
*/
|
||||
@@ -383,6 +402,8 @@ module DatabaseFeatures {
|
||||
override Location getLocation() { result = entity.getLocation() }
|
||||
|
||||
UnderlyingFunction getDefinedFunction() { result = entity.getDefinedFunction() }
|
||||
|
||||
Wrapped::Entity getWrappedEntity() { result = entity }
|
||||
}
|
||||
|
||||
class AstNode extends EntityOrAstNode, TAstNode {
|
||||
@@ -392,8 +413,12 @@ module DatabaseFeatures {
|
||||
|
||||
AstNode getChild(int index) { result = TAstNode(rawNode.getChild(index)) }
|
||||
|
||||
AstNode getAChild() { result = this.getChild(_) }
|
||||
|
||||
string getAttribute(int index) { result = rawNode.getAttribute(index) }
|
||||
|
||||
Wrapped::AstNode getRawNode() { result = rawNode }
|
||||
|
||||
override string getType() { result = rawNode.getType() }
|
||||
|
||||
override string toString() { result = this.getType() }
|
||||
@@ -401,6 +426,9 @@ module DatabaseFeatures {
|
||||
override Location getLocation() { result = rawNode.getLocation() }
|
||||
}
|
||||
|
||||
/** Gets the `DatabaseFeatures::AstNode` that wraps `wrapped` */
|
||||
AstNode astNode(Wrapped::AstNode wrapped) { result.getRawNode() = wrapped }
|
||||
|
||||
/** Consistency checks: these predicates should each have no results */
|
||||
module Consistency {
|
||||
query predicate nonLeafAttribute(AstNode node, int index, string attribute) {
|
||||
@@ -423,15 +451,15 @@ module DatabaseFeatures {
|
||||
}
|
||||
|
||||
query predicate astNodes(
|
||||
Entity enclosingEntity, EntityOrAstNode parent, int index, AstNode node, string node_type
|
||||
Entity enclosingEntity, EntityOrAstNode parent, int index, AstNode child, string childType
|
||||
) {
|
||||
node = enclosingEntity.getAstRoot(index) and
|
||||
child = enclosingEntity.getAstRoot(index) and
|
||||
parent = enclosingEntity and
|
||||
node_type = node.getType()
|
||||
childType = child.getType()
|
||||
or
|
||||
astNodes(enclosingEntity, _, _, parent, _) and
|
||||
node = parent.(AstNode).getChild(index) and
|
||||
node_type = node.getType()
|
||||
child = parent.(AstNode).getChild(index) and
|
||||
childType = child.getType()
|
||||
}
|
||||
|
||||
query predicate nodeAttributes(AstNode node, string attr) {
|
||||
|
||||
@@ -8,6 +8,28 @@ import javascript
|
||||
import CodeToFeatures
|
||||
import EndpointScoring
|
||||
|
||||
/** Maximum number of descendants of an AST node to be considered to be in the "neighborhood" of that node */
|
||||
class DescendantsThreshold extends int {
|
||||
DescendantsThreshold() { this = [1 .. 10000] }
|
||||
|
||||
predicate is1024() { this = 1024 }
|
||||
|
||||
predicate is512() { this = 512 }
|
||||
|
||||
predicate is256() { this = 256 }
|
||||
|
||||
predicate is128() { this = 128 }
|
||||
|
||||
predicate is64() { this = 64 }
|
||||
|
||||
predicate is32() { this = 32 }
|
||||
|
||||
int getMaxNumDescendants() { result = this }
|
||||
}
|
||||
|
||||
/** Maximum number of subtokens in a function body */
|
||||
private int maxNumBodySubtokens() { result = 256 }
|
||||
|
||||
/**
|
||||
* Gets the value of the token-based feature named `featureName` for the endpoint `endpoint`.
|
||||
*
|
||||
@@ -25,6 +47,40 @@ private string getTokenFeature(DataFlow::Node endpoint, string featureName) {
|
||||
result = unique(string x | x = FunctionBodies::getBodyTokenFeatureForEntity(entity))
|
||||
)
|
||||
or
|
||||
// A feature containing natural language tokens from the neighborhood around the endpoint
|
||||
// (limited to within the function that encloses the endpoint), in the order they appear
|
||||
// in the source code.
|
||||
exists(
|
||||
Raw::AstNode rootNode, DatabaseFeatures::AstNode rootNodeWrapped, DescendantsThreshold thresh
|
||||
|
|
||||
(
|
||||
featureName = "enclosingFunctionBodyEndpointNeighborhood1024" and
|
||||
thresh.is1024()
|
||||
or
|
||||
featureName = "enclosingFunctionBodyEndpointNeighborhood512" and
|
||||
thresh.is512()
|
||||
or
|
||||
featureName = "enclosingFunctionBodyEndpointNeighborhood256" and
|
||||
thresh.is256()
|
||||
or
|
||||
featureName = "enclosingFunctionBodyEndpointNeighborhood128" and
|
||||
thresh.is128()
|
||||
or
|
||||
featureName = "enclosingFunctionBodyEndpointNeighborhood64" and
|
||||
thresh.is64()
|
||||
or
|
||||
featureName = "enclosingFunctionBodyEndpointNeighborhood32" and
|
||||
thresh.is32()
|
||||
) and
|
||||
rootNode =
|
||||
NeighborhoodBodies::getNeighborhoodAstNode(Raw::astNode(endpoint.getAstNode()), thresh) and
|
||||
rootNodeWrapped = DatabaseFeatures::astNode(Wrapped::astNode(rootNode.getContainer(), rootNode)) and
|
||||
result =
|
||||
unique(string x |
|
||||
x = NeighborhoodBodies::getBodyTokenFeatureForNeighborhoodNode(rootNodeWrapped)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(getACallBasedTokenFeatureComponent(endpoint, _, featureName)) and
|
||||
result =
|
||||
concat(DataFlow::CallNode call, string component |
|
||||
@@ -121,13 +177,17 @@ module FunctionBodies {
|
||||
/**
|
||||
* Gets the body token feature for the specified entity.
|
||||
*
|
||||
* This is a string containing natural language tokens in the order that they appear in the source code for the entity.
|
||||
* This is a string containing natural language tokens in the order that they appear in the source
|
||||
* code for the entity.
|
||||
*
|
||||
* If a function has more than `maxNumBodySubtokens` body subtokens, then featurize it as absent.
|
||||
* This approximates the behavior of the classifer on non-generic body features where large body
|
||||
* features are replaced by the absent token.
|
||||
*/
|
||||
string getBodyTokenFeatureForEntity(DatabaseFeatures::Entity entity) {
|
||||
// If a function has more than 256 body subtokens, then featurize it as absent. This
|
||||
// approximates the behavior of the classifer on non-generic body features where large body
|
||||
// features are replaced by the absent token.
|
||||
if count(DatabaseFeatures::AstNode node, string token | bodyTokens(entity, node, token)) > 256
|
||||
if
|
||||
count(DatabaseFeatures::AstNode node, string token | bodyTokens(entity, node, token)) >
|
||||
maxNumBodySubtokens()
|
||||
then result = ""
|
||||
else
|
||||
result =
|
||||
@@ -147,6 +207,86 @@ module FunctionBodies {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This module provides functionality for getting the local neighborhood around an AST node within
|
||||
* its enclosing function body, providing a locally-scoped version of the `enclosingFunctionBody` feature.
|
||||
*/
|
||||
module NeighborhoodBodies {
|
||||
/**
|
||||
* Return the ancestor of the input AST node that has the largest number of descendants (i.e. the
|
||||
* node nearest the root) but has no more than `maxNumDescendants` descendants.
|
||||
*
|
||||
* TODO: Maybe instead of a threshold on number of descendants, we should instead have a threshold
|
||||
* on the number of leaves in the subtree, which is a closer approximation to the number of tokens
|
||||
* in the subtree.
|
||||
*/
|
||||
Raw::AstNode getNeighborhoodAstNode(Raw::AstNode node, DescendantsThreshold maxNumDescendants) {
|
||||
if
|
||||
node.getParentNode() = getOutermostEnclosingFunction(node) or
|
||||
getNumDescendents(node.getParentNode()) > maxNumDescendants.getMaxNumDescendants()
|
||||
then result = node
|
||||
else result = getNeighborhoodAstNode(node.getParentNode(), maxNumDescendants)
|
||||
}
|
||||
|
||||
/** Count number of descendants of an AST node */
|
||||
int getNumDescendents(Raw::AstNode node) { result = count(node.getAChildNode*()) }
|
||||
|
||||
private ASTNode getContainer(ASTNode node) { result = node.getContainer() }
|
||||
|
||||
/** Return the AST node that is outermost enclosing function (as an AST Node) */
|
||||
Raw::AstNode getOutermostEnclosingFunction(Raw::AstNode node) {
|
||||
result = Raw::astNode(getContainer*(node.getNode())) and
|
||||
result.getContainer() instanceof TopLevel
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `childNode` is an AST node under `rootNode` and `token` is a node attribute associated
|
||||
* with `childNode`. Note that only AST leaves have node attributes.
|
||||
*
|
||||
* TODO we may need to restrict `rootNode` to be a neighborhood root to avoid a potentially big result set.
|
||||
*/
|
||||
private predicate bodyTokens(
|
||||
DatabaseFeatures::AstNode rootNode, DatabaseFeatures::AstNode childNode, string token
|
||||
) {
|
||||
childNode = rootNode.getAChild*() and
|
||||
token = unique(string t | DatabaseFeatures::nodeAttributes(childNode, t))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the body token feature limited to the part of the function body that lies under `rootNode` in the AST.
|
||||
*
|
||||
* This is a string of space-separated natural language tokens (AST leaves) in the order that they
|
||||
* appear in the source code for the AST subtree rooted at `rootNode`. This is equivalent to the
|
||||
* portion of the code that falls under the AST subtree rooted at the given node, except that
|
||||
* non-leaf nodes (such as operators) are excluded.
|
||||
*
|
||||
* If a function has more than `maxNumBodySubtokens` body subtokens, then featurize it as absent.
|
||||
* This approximates the behavior of the classifer on non-generic body features where large body
|
||||
* features are replaced by the absent token.
|
||||
*/
|
||||
string getBodyTokenFeatureForNeighborhoodNode(DatabaseFeatures::AstNode rootNode) {
|
||||
if
|
||||
count(DatabaseFeatures::AstNode node, string token | bodyTokens(rootNode, node, token)) >
|
||||
maxNumBodySubtokens()
|
||||
then result = ""
|
||||
else
|
||||
result =
|
||||
concat(int i, string rankedToken |
|
||||
rankedToken =
|
||||
rank[i](DatabaseFeatures::AstNode node, string token, Location l |
|
||||
bodyTokens(rootNode, node, token) and l = node.getLocation()
|
||||
|
|
||||
token
|
||||
order by
|
||||
l.getFile().getAbsolutePath(), l.getStartLine(), l.getStartColumn(), l.getEndLine(),
|
||||
l.getEndColumn(), token
|
||||
)
|
||||
|
|
||||
rankedToken, " " order by i
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This module provides functionality for getting a representation of the access path of nodes
|
||||
* within the program.
|
||||
@@ -191,7 +331,10 @@ private module AccessPaths {
|
||||
Boolean() { this = true or this = false }
|
||||
}
|
||||
|
||||
/** Get the access path for the node. This includes structural information like `member`, `param`, and `functionalarg` if `includeStructuralInfo` is true. */
|
||||
/**
|
||||
* Get the access path for the node. This includes structural information like `member`, `param`,
|
||||
* and `functionalarg` if `includeStructuralInfo` is true.
|
||||
*/
|
||||
predicate accessPaths(
|
||||
API::Node node, Boolean includeStructuralInfo, string accessPath, string apiName
|
||||
) {
|
||||
@@ -269,7 +412,12 @@ private string getASupportedFeatureName() {
|
||||
result =
|
||||
[
|
||||
"enclosingFunctionName", "calleeName", "receiverName", "argumentIndex", "calleeApiName",
|
||||
"calleeAccessPath", "calleeAccessPathWithStructuralInfo", "enclosingFunctionBody"
|
||||
"calleeAccessPath", "calleeAccessPathWithStructuralInfo", "enclosingFunctionBody",
|
||||
"enclosingFunctionBodyEndpointNeighborhood1024",
|
||||
"enclosingFunctionBodyEndpointNeighborhood512",
|
||||
"enclosingFunctionBodyEndpointNeighborhood256",
|
||||
"enclosingFunctionBodyEndpointNeighborhood128", "enclosingFunctionBodyEndpointNeighborhood64",
|
||||
"enclosingFunctionBodyEndpointNeighborhood32"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ DatabaseFeatures::Entity getRepresentativeEntityForEndpoint(DataFlow::Node endpo
|
||||
// Use the largest entity smaller than the AST node limit, resolving ties using the entity that
|
||||
// appears first in the source archive.
|
||||
result =
|
||||
rank[1](DatabaseFeatures::Entity entity, int numAstNodes, Location l |
|
||||
min(DatabaseFeatures::Entity entity, int numAstNodes, Location l |
|
||||
entity = EndpointToEntity::getAnEntityForEndpoint(endpoint) and
|
||||
numAstNodes = getNumAstNodesInEntity(entity) and
|
||||
numAstNodes <= getMaxNumAstNodes() and
|
||||
@@ -68,7 +68,7 @@ DatabaseFeatures::Entity getRepresentativeEntityForEndpoint(DataFlow::Node endpo
|
||||
// Use the smallest entity, resolving ties using the entity that
|
||||
// appears first in the source archive.
|
||||
result =
|
||||
rank[1](DatabaseFeatures::Entity entity, int numAstNodes, Location l |
|
||||
min(DatabaseFeatures::Entity entity, int numAstNodes, Location l |
|
||||
entity = EndpointToEntity::getAnEntityForEndpoint(endpoint) and
|
||||
numAstNodes = getNumAstNodesInEntity(entity) and
|
||||
l = entity.getLocation()
|
||||
|
||||
@@ -207,7 +207,9 @@ private predicate isAmbientTopLevel(TopLevel tl) {
|
||||
*/
|
||||
class TopLevel extends @toplevel, StmtContainer {
|
||||
/** Holds if this toplevel is minified. */
|
||||
cached
|
||||
predicate isMinified() {
|
||||
Stages::Ast::ref() and
|
||||
// file name contains 'min' (not as part of a longer word)
|
||||
getFile().getBaseName().regexpMatch(".*[^-._]*[-._]min([-._].*)?\\.\\w+")
|
||||
or
|
||||
|
||||
@@ -359,35 +359,6 @@ module DOM {
|
||||
call.getNumArgument() = 1 and
|
||||
unique(InferredType t | t = getArgumentTypeFromJQueryMethodGet(call)) = TTNumber()
|
||||
)
|
||||
or
|
||||
// A `this` node from a callback given to a `$().each(callback)` call.
|
||||
// purposely not using JQuery::MethodCall to avoid `jquery.each()`.
|
||||
exists(DataFlow::CallNode eachCall | eachCall = JQuery::objectRef().getAMethodCall("each") |
|
||||
this = DataFlow::thisNode(eachCall.getCallback(0).getFunction()) or
|
||||
this = eachCall.getABoundCallbackParameter(0, 1)
|
||||
)
|
||||
or
|
||||
// A read of an array-element from a JQuery object. E.g. `$("#foo")[0]`
|
||||
exists(DataFlow::PropRead read |
|
||||
read = this and read = JQuery::objectRef().getAPropertyRead()
|
||||
|
|
||||
unique(InferredType t | t = read.getPropertyNameExpr().analyze().getAType()) = TTNumber()
|
||||
)
|
||||
or
|
||||
// A receiver node of an event handler on a DOM node
|
||||
exists(DataFlow::SourceNode domNode, DataFlow::FunctionNode eventHandler |
|
||||
// NOTE: we do not use `getABoundFunctionValue()`, since bound functions tend to have
|
||||
// a different receiver anyway
|
||||
eventHandler = domNode.getAPropertySource(any(string n | n.matches("on%")))
|
||||
or
|
||||
eventHandler =
|
||||
domNode.getAMethodCall("addEventListener").getArgument(1).getAFunctionValue()
|
||||
|
|
||||
domNode = domValueRef() and
|
||||
this = eventHandler.getReceiver()
|
||||
)
|
||||
or
|
||||
this = DataFlow::thisNode(any(EventHandlerCode evt))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -421,11 +392,6 @@ module DOM {
|
||||
or
|
||||
t.start() and
|
||||
result = domValueRef().getAMethodCall(["item", "namedItem"])
|
||||
or
|
||||
t.startInProp("target") and
|
||||
result = domEventSource()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = domValueRef(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a data flow node that may refer to a value from the DOM. */
|
||||
|
||||
@@ -141,6 +141,19 @@ private DataFlow::Node getAValueExportedByPackage() {
|
||||
result = unique( | | call.getCalleeNode().getAFunctionValue()).getAReturn()
|
||||
)
|
||||
or
|
||||
// the exported value is a function that returns another import.
|
||||
// ```JavaScript
|
||||
// module.exports = function foo() {
|
||||
// return require("./other-module.js");
|
||||
// }
|
||||
// ```
|
||||
exists(DataFlow::FunctionNode func, Module mod |
|
||||
func = getAValueExportedByPackage().getABoundFunctionValue(_)
|
||||
|
|
||||
mod = func.getAReturn().getALocalSource().getEnclosingExpr().(Import).getImportedModule() and
|
||||
result = getAnExportFromModule(mod)
|
||||
)
|
||||
or
|
||||
// *****
|
||||
// Common styles of transforming exported objects.
|
||||
// *****
|
||||
|
||||
@@ -185,12 +185,12 @@ module Promises {
|
||||
/**
|
||||
* Gets the pseudo-field used to describe resolved values in a promise.
|
||||
*/
|
||||
string valueProp() { result = "$PromiseResolveField$" }
|
||||
string valueProp() { none() }
|
||||
|
||||
/**
|
||||
* Gets the pseudo-field used to describe rejected values in a promise.
|
||||
*/
|
||||
string errorProp() { result = "$PromiseRejectField$" }
|
||||
string errorProp() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import javascript
|
||||
private import semmle.javascript.dataflow.InferredTypes
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
|
||||
/**
|
||||
* An element containing a regular expression term, that is, either
|
||||
@@ -955,7 +956,9 @@ private predicate isUsedAsNonMatchObject(DataFlow::MethodCallNode call) {
|
||||
/**
|
||||
* Holds if `source` may be interpreted as a regular expression.
|
||||
*/
|
||||
cached
|
||||
predicate isInterpretedAsRegExp(DataFlow::Node source) {
|
||||
Stages::Taint::ref() and
|
||||
source.analyze().getAType() = TTString() and
|
||||
(
|
||||
// The first argument to an invocation of `RegExp` (with or without `new`).
|
||||
|
||||
@@ -777,10 +777,10 @@ private class AdditionalFlowStepAsSharedStep extends SharedFlowStep {
|
||||
*/
|
||||
module PseudoProperties {
|
||||
bindingset[s]
|
||||
private string pseudoProperty(string s) { result = "$" + s + "$" }
|
||||
private string pseudoProperty(string s) { none() }
|
||||
|
||||
bindingset[s, v]
|
||||
private string pseudoProperty(string s, string v) { result = "$" + s + "|" + v + "$" }
|
||||
private string pseudoProperty(string s, string v) { none() }
|
||||
|
||||
/**
|
||||
* Gets a pseudo-property for the location of elements in a `Set`
|
||||
|
||||
@@ -136,7 +136,7 @@ module Angular2 {
|
||||
|
||||
/** Gets a reference to a `DomSanitizer` object. */
|
||||
DataFlow::SourceNode domSanitizer() {
|
||||
result.hasUnderlyingType(["@angular/platform-browser", "@angular/core"], "DomSanitizer")
|
||||
result.hasUnderlyingType("@angular/platform-browser", "DomSanitizer")
|
||||
}
|
||||
|
||||
/** A value that is about to be promoted to a trusted HTML or CSS value. */
|
||||
|
||||
@@ -864,28 +864,6 @@ module Express {
|
||||
override string getCredentialsKind() { result = kind }
|
||||
}
|
||||
|
||||
/** A call to `response.sendFile`, considered as a file system access. */
|
||||
private class ResponseSendFileAsFileSystemAccess extends FileSystemReadAccess,
|
||||
DataFlow::MethodCallNode {
|
||||
ResponseSendFileAsFileSystemAccess() {
|
||||
exists(string name | name = "sendFile" or name = "sendfile" |
|
||||
calls(any(ResponseExpr res).flow(), name)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getADataNode() { none() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
|
||||
|
||||
override DataFlow::Node getRootPathArgument() {
|
||||
result = this.(DataFlow::CallNode).getOptionArgument(1, "root")
|
||||
}
|
||||
|
||||
override predicate isUpwardNavigationRejected(DataFlow::Node argument) {
|
||||
argument = getAPathArgument()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that flows to a route setup.
|
||||
*/
|
||||
|
||||
@@ -4,23 +4,6 @@
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* A call that can produce a file name.
|
||||
*/
|
||||
abstract private class FileNameProducer extends DataFlow::Node {
|
||||
/**
|
||||
* Gets a file name produced by this producer.
|
||||
*/
|
||||
abstract DataFlow::Node getAFileName();
|
||||
}
|
||||
|
||||
/**
|
||||
* A node that contains a file name, and is produced by a `ProducesFileNames`.
|
||||
*/
|
||||
private class ProducedFileName extends FileNameSource {
|
||||
ProducedFileName() { this = any(FileNameProducer producer).getAFileName() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A file name from the `walk-sync` library.
|
||||
*/
|
||||
@@ -143,338 +126,3 @@ private DataFlow::Node fastGlobFileNameSource(DataFlow::TypeTracker t) {
|
||||
private class FastGlobFileNameSource extends FileNameSource {
|
||||
FastGlobFileNameSource() { this = fastGlobFileNameSource(DataFlow::TypeTracker::end()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Classes and predicates for modelling the `fstream` library (https://www.npmjs.com/package/fstream).
|
||||
*/
|
||||
private module FStream {
|
||||
/**
|
||||
* Gets a reference to a method in the `fstream` library.
|
||||
*/
|
||||
private DataFlow::SourceNode getAnFStreamProperty(boolean writer) {
|
||||
exists(DataFlow::SourceNode mod, string readOrWrite, string subMod |
|
||||
mod = DataFlow::moduleImport("fstream") and
|
||||
(
|
||||
readOrWrite = "Reader" and writer = false
|
||||
or
|
||||
readOrWrite = "Writer" and writer = true
|
||||
) and
|
||||
(subMod = "File" or subMod = "Dir" or subMod = "Link" or subMod = "Proxy")
|
||||
|
|
||||
result = mod.getAPropertyRead(readOrWrite) or
|
||||
result = mod.getAPropertyRead(readOrWrite).getAPropertyRead(subMod) or
|
||||
result = mod.getAPropertyRead(subMod).getAPropertyRead(readOrWrite)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An invocation of a method defined in the `fstream` library.
|
||||
*/
|
||||
private class FStream extends FileSystemAccess, DataFlow::InvokeNode {
|
||||
boolean writer;
|
||||
|
||||
FStream() { this = getAnFStreamProperty(writer).getAnInvocation() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result = getOptionArgument(0, "path")
|
||||
or
|
||||
not exists(getOptionArgument(0, "path")) and
|
||||
result = getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An invocation of an `fstream` method that writes to a file.
|
||||
*/
|
||||
private class FStreamWriter extends FileSystemWriteAccess, FStream {
|
||||
FStreamWriter() { writer = true }
|
||||
|
||||
override DataFlow::Node getADataNode() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An invocation of an `fstream` method that reads a file.
|
||||
*/
|
||||
private class FStreamReader extends FileSystemReadAccess, FStream {
|
||||
FStreamReader() { writer = false }
|
||||
|
||||
override DataFlow::Node getADataNode() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the library `write-file-atomic`.
|
||||
*/
|
||||
private class WriteFileAtomic extends FileSystemWriteAccess, DataFlow::CallNode {
|
||||
WriteFileAtomic() {
|
||||
this = DataFlow::moduleImport("write-file-atomic").getACall()
|
||||
or
|
||||
this = DataFlow::moduleMember("write-file-atomic", "sync").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
|
||||
|
||||
override DataFlow::Node getADataNode() { result = getArgument(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the library `recursive-readdir`.
|
||||
*/
|
||||
private class RecursiveReadDir extends FileSystemAccess, FileNameProducer, DataFlow::CallNode {
|
||||
RecursiveReadDir() { this = DataFlow::moduleImport("recursive-readdir").getACall() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
|
||||
|
||||
override DataFlow::Node getAFileName() { result = trackFileSource(DataFlow::TypeTracker::end()) }
|
||||
|
||||
private DataFlow::SourceNode trackFileSource(DataFlow::TypeTracker t) {
|
||||
t.start() and result = getCallback([1 .. 2]).getParameter(1)
|
||||
or
|
||||
t.startInPromise() and not exists(getCallback([1 .. 2])) and result = this
|
||||
or
|
||||
// Tracking out of a promise
|
||||
exists(DataFlow::TypeTracker t2 |
|
||||
result = PromiseTypeTracking::promiseStep(trackFileSource(t2), t, t2)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Classes and predicates for modelling the `jsonfile` library (https://www.npmjs.com/package/jsonfile).
|
||||
*/
|
||||
private module JSONFile {
|
||||
/**
|
||||
* A reader for JSON files.
|
||||
*/
|
||||
class JSONFileReader extends FileSystemReadAccess, DataFlow::CallNode {
|
||||
JSONFileReader() {
|
||||
this =
|
||||
DataFlow::moduleMember("jsonfile", any(string s | s = "readFile" or s = "readFileSync"))
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
|
||||
|
||||
override DataFlow::Node getADataNode() { result = trackRead(DataFlow::TypeTracker::end()) }
|
||||
|
||||
private DataFlow::SourceNode trackRead(DataFlow::TypeTracker t) {
|
||||
this.getCalleeName() = "readFile" and
|
||||
(
|
||||
t.start() and result = getCallback([1 .. 2]).getParameter(1)
|
||||
or
|
||||
t.startInPromise() and not exists(getCallback([1 .. 2])) and result = this
|
||||
)
|
||||
or
|
||||
t.start() and
|
||||
this.getCalleeName() = "readFileSync" and
|
||||
result = this
|
||||
or
|
||||
// Tracking out of a promise
|
||||
exists(DataFlow::TypeTracker t2 |
|
||||
result = PromiseTypeTracking::promiseStep(trackRead(t2), t, t2)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A writer for JSON files.
|
||||
*/
|
||||
class JSONFileWriter extends FileSystemWriteAccess, DataFlow::CallNode {
|
||||
JSONFileWriter() {
|
||||
this =
|
||||
DataFlow::moduleMember("jsonfile", any(string s | s = "writeFile" or s = "writeFileSync"))
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
|
||||
|
||||
override DataFlow::Node getADataNode() { result = getArgument(1) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the library `load-json-file`.
|
||||
*/
|
||||
private class LoadJsonFile extends FileSystemReadAccess, DataFlow::CallNode {
|
||||
LoadJsonFile() {
|
||||
this = DataFlow::moduleImport("load-json-file").getACall()
|
||||
or
|
||||
this = DataFlow::moduleMember("load-json-file", "sync").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
|
||||
|
||||
override DataFlow::Node getADataNode() { result = trackRead(DataFlow::TypeTracker::end()) }
|
||||
|
||||
private DataFlow::SourceNode trackRead(DataFlow::TypeTracker t) {
|
||||
this.getCalleeName() = "sync" and t.start() and result = this
|
||||
or
|
||||
not this.getCalleeName() = "sync" and t.startInPromise() and result = this
|
||||
or
|
||||
// Tracking out of a promise
|
||||
exists(DataFlow::TypeTracker t2 |
|
||||
result = PromiseTypeTracking::promiseStep(trackRead(t2), t, t2)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the library `write-json-file`.
|
||||
*/
|
||||
private class WriteJsonFile extends FileSystemWriteAccess, DataFlow::CallNode {
|
||||
WriteJsonFile() {
|
||||
this = DataFlow::moduleImport("write-json-file").getACall()
|
||||
or
|
||||
this = DataFlow::moduleMember("write-json-file", "sync").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
|
||||
|
||||
override DataFlow::Node getADataNode() { result = getArgument(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the library `walkdir`.
|
||||
*/
|
||||
private class WalkDir extends FileNameProducer, FileSystemAccess, DataFlow::CallNode {
|
||||
WalkDir() {
|
||||
this = DataFlow::moduleImport("walkdir").getACall()
|
||||
or
|
||||
this = DataFlow::moduleMember("walkdir", "sync").getACall()
|
||||
or
|
||||
this = DataFlow::moduleMember("walkdir", "async").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
|
||||
|
||||
override DataFlow::Node getAFileName() { result = trackFileSource(DataFlow::TypeTracker::end()) }
|
||||
|
||||
private DataFlow::SourceNode trackFileSource(DataFlow::TypeTracker t) {
|
||||
not this.getCalleeName() = any(string s | s = "sync" or s = "async") and
|
||||
t.start() and
|
||||
(
|
||||
result = getCallback(getNumArgument() - 1).getParameter(0)
|
||||
or
|
||||
result = getAMethodCall(EventEmitter::on()).getCallback(1).getParameter(0)
|
||||
)
|
||||
or
|
||||
t.start() and this.getCalleeName() = "sync" and result = this
|
||||
or
|
||||
t.startInPromise() and this.getCalleeName() = "async" and result = this
|
||||
or
|
||||
// Tracking out of a promise
|
||||
exists(DataFlow::TypeTracker t2 |
|
||||
result = PromiseTypeTracking::promiseStep(trackFileSource(t2), t, t2)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the library `globule`.
|
||||
*/
|
||||
private class Globule extends FileNameProducer, FileSystemAccess, DataFlow::CallNode {
|
||||
Globule() {
|
||||
this = DataFlow::moduleMember("globule", "find").getACall()
|
||||
or
|
||||
this = DataFlow::moduleMember("globule", "match").getACall()
|
||||
or
|
||||
this = DataFlow::moduleMember("globule", "isMatch").getACall()
|
||||
or
|
||||
this = DataFlow::moduleMember("globule", "mapping").getACall()
|
||||
or
|
||||
this = DataFlow::moduleMember("globule", "findMapping").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
(this.getCalleeName() = "match" or this.getCalleeName() = "isMatch") and
|
||||
result = getArgument(1)
|
||||
or
|
||||
this.getCalleeName() = "mapping" and
|
||||
(
|
||||
result = getAnArgument() and not exists(result.getALocalSource().getAPropertyWrite("src"))
|
||||
or
|
||||
result = getAnArgument().getALocalSource().getAPropertyWrite("src").getRhs()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAFileName() {
|
||||
result = this and
|
||||
(
|
||||
this.getCalleeName() = "find" or
|
||||
this.getCalleeName() = "match" or
|
||||
this.getCalleeName() = "findMapping" or
|
||||
this.getCalleeName() = "mapping"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A file system access made by a NodeJS library.
|
||||
* This class models multiple NodeJS libraries that access files.
|
||||
*/
|
||||
private class LibraryAccess extends FileSystemAccess, DataFlow::InvokeNode {
|
||||
int pathArgument; // The index of the path argument.
|
||||
|
||||
LibraryAccess() {
|
||||
pathArgument = 0 and
|
||||
(
|
||||
this = DataFlow::moduleImport("path-exists").getACall()
|
||||
or
|
||||
this = DataFlow::moduleImport("rimraf").getACall()
|
||||
or
|
||||
this = DataFlow::moduleImport("readdirp").getACall()
|
||||
or
|
||||
this = DataFlow::moduleImport("walker").getACall()
|
||||
or
|
||||
this =
|
||||
DataFlow::moduleMember("node-dir",
|
||||
any(string s |
|
||||
s = ["readFiles", "readFilesStream", "files", "promiseFiles", "subdirs", "paths"]
|
||||
)).getACall()
|
||||
)
|
||||
or
|
||||
pathArgument = 0 and
|
||||
this =
|
||||
DataFlow::moduleMember("vinyl-fs", any(string s | s = "src" or s = "dest" or s = "symlink"))
|
||||
.getACall()
|
||||
or
|
||||
pathArgument = [0 .. 1] and
|
||||
(
|
||||
this = DataFlow::moduleImport("ncp").getACall() or
|
||||
this = DataFlow::moduleMember("ncp", "ncp").getACall()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = getArgument(pathArgument) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the library [`chokidar`](https://www.npmjs.com/package/chokidar), where a call to `on` receives file names.
|
||||
*/
|
||||
class Chokidar extends FileNameProducer, FileSystemAccess, API::CallNode {
|
||||
Chokidar() { this = API::moduleImport("chokidar").getMember("watch").getACall() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
|
||||
|
||||
override DataFlow::Node getAFileName() {
|
||||
exists(DataFlow::CallNode onCall, int pathIndex |
|
||||
onCall = getAChainedMethodCall("on") and
|
||||
if onCall.getArgument(0).mayHaveStringValue("all") then pathIndex = 1 else pathIndex = 0
|
||||
|
|
||||
result = onCall.getCallback(1).getParameter(pathIndex)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the [`mkdirp`](https://www.npmjs.com/package/mkdirp) library.
|
||||
*/
|
||||
private class Mkdirp extends FileSystemAccess, API::CallNode {
|
||||
Mkdirp() {
|
||||
this = API::moduleImport("mkdirp").getACall()
|
||||
or
|
||||
this = API::moduleImport("mkdirp").getMember("sync").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
|
||||
}
|
||||
|
||||
@@ -6,146 +6,120 @@ import javascript
|
||||
|
||||
module NoSQL {
|
||||
/** An expression that is interpreted as a NoSQL query. */
|
||||
abstract class Query extends Expr {
|
||||
/** Gets an expression that is interpreted as a code operator in this query. */
|
||||
DataFlow::Node getACodeOperator() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value that has been assigned to the "$where" property of an object that flows to `queryArg`.
|
||||
*/
|
||||
private DataFlow::Node getADollarWhereProperty(API::Node queryArg) {
|
||||
result = queryArg.getMember("$where").getARhs()
|
||||
abstract class Query extends Expr { }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modeling the MongoDB library.
|
||||
*/
|
||||
private module MongoDB {
|
||||
/**
|
||||
* Gets an import of MongoDB.
|
||||
*/
|
||||
DataFlow::ModuleImportNode mongodb() { result.getPath() = "mongodb" }
|
||||
|
||||
/**
|
||||
* Gets an access to `mongodb.MongoClient`.
|
||||
*/
|
||||
private API::Node getAMongoClient() {
|
||||
result = API::moduleImport("mongodb").getMember("MongoClient")
|
||||
private DataFlow::SourceNode getAMongoClient(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = mongodb().getAPropertyRead("MongoClient")
|
||||
or
|
||||
result = getAMongoDbCallback().getParameter(1) and
|
||||
not result.getAnImmediateUse().(DataFlow::ParameterNode).getName() = "db" // mongodb v2 provides a `Db` here
|
||||
}
|
||||
|
||||
/** Gets an API-graph node that refers to a `connect` callback. */
|
||||
private API::Node getAMongoDbCallback() {
|
||||
result = getAMongoClient().getMember("connect").getLastParameter()
|
||||
exists(DataFlow::TypeTracker t2 | result = getAMongoClient(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an API-graph node that may refer to a MongoDB database connection.
|
||||
* Gets an access to `mongodb.MongoClient`.
|
||||
*/
|
||||
private API::Node getAMongoDb() {
|
||||
result = getAMongoClient().getMember("db").getReturn()
|
||||
DataFlow::SourceNode getAMongoClient() { result = getAMongoClient(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** Gets a data flow node that leads to a `connect` callback. */
|
||||
private DataFlow::SourceNode getAMongoDbCallback(DataFlow::TypeBackTracker t) {
|
||||
t.start() and
|
||||
result = getAMongoClient().getAMemberCall("connect").getArgument(1).getALocalSource()
|
||||
or
|
||||
result = getAMongoDbCallback().getParameter(1) and
|
||||
not result.getAnImmediateUse().(DataFlow::ParameterNode).getName() = "client" // mongodb v3 provides a `Mongoclient` here
|
||||
exists(DataFlow::TypeBackTracker t2 | result = getAMongoDbCallback(t2).backtrack(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a data flow node that leads to a `connect` callback. */
|
||||
private DataFlow::FunctionNode getAMongoDbCallback() {
|
||||
result = getAMongoDbCallback(DataFlow::TypeBackTracker::end())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an expression that may refer to a MongoDB database connection.
|
||||
*/
|
||||
private DataFlow::SourceNode getAMongoDb(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = getAMongoDbCallback().getParameter(1)
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = getAMongoDb(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an expression that may refer to a MongoDB database connection.
|
||||
*/
|
||||
DataFlow::SourceNode getAMongoDb() { result = getAMongoDb(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/**
|
||||
* A data flow node that may hold a MongoDB collection.
|
||||
*/
|
||||
abstract class Collection extends DataFlow::SourceNode { }
|
||||
|
||||
/**
|
||||
* A collection resulting from calling `Db.collection(...)`.
|
||||
*/
|
||||
private class CollectionFromDb extends Collection {
|
||||
CollectionFromDb() {
|
||||
this = getAMongoDb().getAMethodCall("collection")
|
||||
or
|
||||
this = getAMongoDb().getAMethodCall("collection").getCallback(1).getParameter(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A collection based on the type `mongodb.Collection`.
|
||||
*
|
||||
* Note that this also covers `mongoose` models since they are subtypes
|
||||
* of `mongodb.Collection`.
|
||||
*/
|
||||
private class CollectionFromType extends Collection {
|
||||
CollectionFromType() { hasUnderlyingType("mongodb", "Collection") }
|
||||
}
|
||||
|
||||
/** Gets a data flow node referring to a MongoDB collection. */
|
||||
private API::Node getACollection() {
|
||||
// A collection resulting from calling `Db.collection(...)`.
|
||||
exists(API::Node collection | collection = getAMongoDb().getMember("collection").getReturn() |
|
||||
result = collection
|
||||
or
|
||||
result = collection.getParameter(1).getParameter(0)
|
||||
)
|
||||
private DataFlow::SourceNode getACollection(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof Collection
|
||||
or
|
||||
// note that this also covers `mongoose` models since they are subtypes of `mongodb.Collection`
|
||||
result = API::Node::ofType("mongodb", "Collection")
|
||||
exists(DataFlow::TypeTracker t2 | result = getACollection(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a data flow node referring to a MongoDB collection. */
|
||||
DataFlow::SourceNode getACollection() { result = getACollection(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** A call to a MongoDB query method. */
|
||||
private class QueryCall extends DatabaseAccess, API::CallNode {
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
int queryArgIdx;
|
||||
|
||||
QueryCall() {
|
||||
exists(string method |
|
||||
CollectionMethodSignatures::interpretsArgumentAsQuery(method, queryArgIdx) and
|
||||
this = getACollection().getMember(method).getACall()
|
||||
exists(string m | this = getACollection().getAMethodCall(m) |
|
||||
m = "count" and queryArgIdx = 0
|
||||
or
|
||||
m = "distinct" and queryArgIdx = 1
|
||||
or
|
||||
m = "find" and queryArgIdx = 0
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = getArgument(queryArgIdx) }
|
||||
|
||||
DataFlow::Node getACodeOperator() {
|
||||
result = getADollarWhereProperty(getParameter(queryArgIdx))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is interpreted as a MongoDB query.
|
||||
*/
|
||||
class Query extends NoSQL::Query {
|
||||
QueryCall qc;
|
||||
|
||||
Query() { this = qc.getAQueryArgument().asExpr() }
|
||||
|
||||
override DataFlow::Node getACodeOperator() { result = qc.getACodeOperator() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides signatures for the Collection methods.
|
||||
*/
|
||||
module CollectionMethodSignatures {
|
||||
/**
|
||||
* Holds if Collection method `name` interprets parameter `n` as a query.
|
||||
*/
|
||||
predicate interpretsArgumentAsQuery(string name, int n) {
|
||||
// FilterQuery
|
||||
(
|
||||
name = "aggregate" and n = 0
|
||||
or
|
||||
name = "count" and n = 0
|
||||
or
|
||||
name = "countDocuments" and n = 0
|
||||
or
|
||||
name = "deleteMany" and n = 0
|
||||
or
|
||||
name = "deleteOne" and n = 0
|
||||
or
|
||||
name = "distinct" and n = 1
|
||||
or
|
||||
name = "find" and n = 0
|
||||
or
|
||||
name = "findOne" and n = 0
|
||||
or
|
||||
name = "findOneAndDelete" and n = 0
|
||||
or
|
||||
name = "findOneAndRemove" and n = 0
|
||||
or
|
||||
name = "findOneAndReplace" and n = 0
|
||||
or
|
||||
name = "findOneAndUpdate" and n = 0
|
||||
or
|
||||
name = "remove" and n = 0
|
||||
or
|
||||
name = "replaceOne" and n = 0
|
||||
or
|
||||
name = "update" and n = 0
|
||||
or
|
||||
name = "updateMany" and n = 0
|
||||
or
|
||||
name = "updateOne" and n = 0
|
||||
)
|
||||
or
|
||||
// UpdateQuery
|
||||
(
|
||||
name = "findOneAndUpdate" and n = 1
|
||||
or
|
||||
name = "update" and n = 1
|
||||
or
|
||||
name = "updateMany" and n = 1
|
||||
or
|
||||
name = "updateOne" and n = 1
|
||||
)
|
||||
}
|
||||
Query() { this = any(QueryCall qc).getAQueryArgument().asExpr() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,345 +130,20 @@ private module Mongoose {
|
||||
/**
|
||||
* Gets an import of Mongoose.
|
||||
*/
|
||||
API::Node getAMongooseInstance() { result = API::moduleImport("mongoose") }
|
||||
DataFlow::ModuleImportNode getAMongooseInstance() { result.getPath() = "mongoose" }
|
||||
|
||||
/**
|
||||
* Gets a reference to `mongoose.createConnection`.
|
||||
* Gets a call to `mongoose.createConnection`.
|
||||
*/
|
||||
API::Node createConnection() { result = getAMongooseInstance().getMember("createConnection") }
|
||||
|
||||
/**
|
||||
* A Mongoose function.
|
||||
*/
|
||||
abstract private class MongooseFunction extends API::Node {
|
||||
/**
|
||||
* Gets the API-graph node for the result from this function (if the function returns a `Query`).
|
||||
*/
|
||||
abstract API::Node getQueryReturn();
|
||||
|
||||
/**
|
||||
* Holds if this function returns a `Query` that evaluates to one or
|
||||
* more Documents (`asArray` is false if it evaluates to a single
|
||||
* Document).
|
||||
*/
|
||||
abstract predicate returnsDocumentQuery(boolean asArray);
|
||||
|
||||
/**
|
||||
* Gets an argument that this function interprets as a query.
|
||||
*/
|
||||
abstract API::Node getQueryArgument();
|
||||
DataFlow::CallNode createConnection() {
|
||||
result = getAMongooseInstance().getAMemberCall("createConnection")
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modeling the Mongoose Model class
|
||||
* A Mongoose collection object.
|
||||
*/
|
||||
module Model {
|
||||
private class ModelFunction extends MongooseFunction {
|
||||
string methodName;
|
||||
|
||||
ModelFunction() { this = getModelObject().getMember(methodName) }
|
||||
|
||||
override API::Node getQueryReturn() {
|
||||
MethodSignatures::returnsQuery(methodName) and result = this.getReturn()
|
||||
}
|
||||
|
||||
override predicate returnsDocumentQuery(boolean asArray) {
|
||||
MethodSignatures::returnsDocumentQuery(methodName, asArray)
|
||||
}
|
||||
|
||||
override API::Node getQueryArgument() {
|
||||
exists(int n |
|
||||
MethodSignatures::interpretsArgumentAsQuery(methodName, n) and
|
||||
result = this.getParameter(n)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a API-graph node referring to a Mongoose Model object.
|
||||
*/
|
||||
private API::Node getModelObject() {
|
||||
result = getAMongooseInstance().getMember("model").getReturn()
|
||||
or
|
||||
exists(API::Node conn | conn = createConnection().getReturn() |
|
||||
result = conn.getMember("model").getReturn() or
|
||||
result = conn.getMember("models").getAMember()
|
||||
)
|
||||
or
|
||||
result = API::Node::ofType("mongoose", "Model")
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides signatures for the Model methods.
|
||||
*/
|
||||
module MethodSignatures {
|
||||
/**
|
||||
* Holds if Model method `name` interprets parameter `n` as a query.
|
||||
*/
|
||||
predicate interpretsArgumentAsQuery(string name, int n) {
|
||||
// implement lots of the MongoDB collection interface
|
||||
MongoDB::CollectionMethodSignatures::interpretsArgumentAsQuery(name, n)
|
||||
or
|
||||
name = "find" + ["ById", "One"] + "AndUpdate" and n = 1
|
||||
or
|
||||
name in ["delete" + ["Many", "One"], "geoSearch", "remove", "replaceOne", "where"] and
|
||||
n = 0
|
||||
or
|
||||
name in [
|
||||
"find" + ["", "ById", "One"],
|
||||
"find" + ["ById", "One"] + "And" + ["Delete", "Remove", "Update"],
|
||||
"update" + ["", "Many", "One"]
|
||||
] and
|
||||
n = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if Model method `name` returns a Query.
|
||||
*/
|
||||
predicate returnsQuery(string name) {
|
||||
name =
|
||||
[
|
||||
"$where", "count", "findOne", "findOneAndDelete", "findOneAndRemove",
|
||||
"findOneAndReplace", "findOneAndUpdate", "geosearch", "remove", "replaceOne", "update",
|
||||
"updateMany", "countDocuments", "updateOne", "where", "deleteMany", "deleteOne", "find",
|
||||
"findById", "findByIdAndDelete", "findByIdAndRemove", "findByIdAndUpdate"
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if Document method `name` returns a query that results in
|
||||
* one or more documents, the documents are wrapped in an array
|
||||
* if `asArray` is true.
|
||||
*/
|
||||
predicate returnsDocumentQuery(string name, boolean asArray) {
|
||||
asArray = false and name = "findOne"
|
||||
or
|
||||
asArray = true and name = "find"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modeling the Mongoose Query class
|
||||
*/
|
||||
module Query {
|
||||
private class QueryFunction extends MongooseFunction {
|
||||
string methodName;
|
||||
|
||||
QueryFunction() { this = getAMongooseQuery().getMember(methodName) }
|
||||
|
||||
override API::Node getQueryReturn() {
|
||||
MethodSignatures::returnsQuery(methodName) and result = this.getReturn()
|
||||
}
|
||||
|
||||
override predicate returnsDocumentQuery(boolean asArray) {
|
||||
MethodSignatures::returnsDocumentQuery(methodName, asArray)
|
||||
}
|
||||
|
||||
override API::Node getQueryArgument() {
|
||||
exists(int n |
|
||||
MethodSignatures::interpretsArgumentAsQuery(methodName, n) and
|
||||
result = this.getParameter(n)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class NewQueryFunction extends MongooseFunction {
|
||||
NewQueryFunction() { this = getAMongooseInstance().getMember("Query") }
|
||||
|
||||
override API::Node getQueryReturn() { result = this.getInstance() }
|
||||
|
||||
override predicate returnsDocumentQuery(boolean asArray) { none() }
|
||||
|
||||
override API::Node getQueryArgument() { result = this.getParameter(2) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data flow node referring to a Mongoose query object.
|
||||
*/
|
||||
API::Node getAMongooseQuery() {
|
||||
result = any(MongooseFunction f).getQueryReturn()
|
||||
or
|
||||
result = API::Node::ofType("mongoose", "Query")
|
||||
or
|
||||
result =
|
||||
getAMongooseQuery()
|
||||
.getMember(any(string name | MethodSignatures::returnsQuery(name)))
|
||||
.getReturn()
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides signatures for the Query methods.
|
||||
*/
|
||||
module MethodSignatures {
|
||||
/**
|
||||
* Holds if Query method `name` interprets parameter `n` as a query.
|
||||
*/
|
||||
predicate interpretsArgumentAsQuery(string name, int n) {
|
||||
n = 0 and
|
||||
name =
|
||||
[
|
||||
"and", "count", "findOneAndReplace", "findOneAndUpdate", "merge", "nor", "or", "remove",
|
||||
"replaceOne", "setQuery", "setUpdate", "update", "countDocuments", "updateMany",
|
||||
"updateOne", "where", "deleteMany", "deleteOne", "elemMatch", "find", "findOne",
|
||||
"findOneAndDelete", "findOneAndRemove"
|
||||
]
|
||||
or
|
||||
n = 1 and
|
||||
name = ["distinct", "findOneAndUpdate", "update", "updateMany", "updateOne"]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if Query method `name` returns a Query.
|
||||
*/
|
||||
predicate returnsQuery(string name) {
|
||||
name =
|
||||
[
|
||||
"$where", "J", "comment", "count", "countDocuments", "distinct", "elemMatch", "equals",
|
||||
"error", "estimatedDocumentCount", "exists", "explain", "all", "find", "findById",
|
||||
"findOne", "findOneAndRemove", "findOneAndUpdate", "geometry", "get", "gt", "gte",
|
||||
"hint", "and", "in", "intersects", "lean", "limit", "lt", "lte", "map", "map",
|
||||
"maxDistance", "maxTimeMS", "batchsize", "maxscan", "mod", "ne", "near", "nearSphere",
|
||||
"nin", "or", "orFail", "polygon", "populate", "box", "read", "readConcern", "regexp",
|
||||
"remove", "select", "session", "set", "setOptions", "setQuery", "setUpdate", "center",
|
||||
"size", "skip", "slaveOk", "slice", "snapshot", "sort", "update", "w", "where",
|
||||
"within", "centerSphere", "wtimeout", "circle", "collation"
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if Query method `name` returns a query that results in
|
||||
* one or more documents, the documents are wrapped in an array
|
||||
* if `asArray` is true.
|
||||
*/
|
||||
predicate returnsDocumentQuery(string name, boolean asArray) {
|
||||
asArray = false and name = "findOne"
|
||||
or
|
||||
asArray = true and name = "find"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modeling the Mongoose Document class
|
||||
*/
|
||||
module Document {
|
||||
private class DocumentFunction extends MongooseFunction {
|
||||
string methodName;
|
||||
|
||||
DocumentFunction() { this = getAMongooseDocument().getMember(methodName) }
|
||||
|
||||
override API::Node getQueryReturn() {
|
||||
MethodSignatures::returnsQuery(methodName) and result = this.getReturn()
|
||||
}
|
||||
|
||||
override predicate returnsDocumentQuery(boolean asArray) {
|
||||
MethodSignatures::returnsDocumentQuery(methodName, asArray)
|
||||
}
|
||||
|
||||
override API::Node getQueryArgument() {
|
||||
exists(int n |
|
||||
MethodSignatures::interpretsArgumentAsQuery(methodName, n) and
|
||||
result = this.getParameter(n)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Mongoose Document that is retrieved from the backing database.
|
||||
*/
|
||||
class RetrievedDocument extends API::Node {
|
||||
RetrievedDocument() {
|
||||
exists(boolean asArray, API::Node param |
|
||||
exists(MongooseFunction func |
|
||||
func.returnsDocumentQuery(asArray) and
|
||||
param = func.getLastParameter().getParameter(1)
|
||||
)
|
||||
or
|
||||
exists(API::Node f |
|
||||
f = Query::getAMongooseQuery().getMember("then") and
|
||||
param = f.getParameter(0).getParameter(0)
|
||||
or
|
||||
f = Query::getAMongooseQuery().getMember("exec") and
|
||||
param = f.getParameter(0).getParameter(1)
|
||||
|
|
||||
exists(DataFlow::MethodCallNode pred |
|
||||
// limitation: look at the previous method call
|
||||
Query::MethodSignatures::returnsDocumentQuery(pred.getMethodName(), asArray) and
|
||||
pred.getAMethodCall() = f.getACall()
|
||||
)
|
||||
)
|
||||
|
|
||||
asArray = false and this = param
|
||||
or
|
||||
asArray = true and
|
||||
// limitation: look for direct accesses
|
||||
this = param.getUnknownMember()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data flow node referring to a Mongoose Document object.
|
||||
*/
|
||||
private API::Node getAMongooseDocument() {
|
||||
result instanceof RetrievedDocument
|
||||
or
|
||||
result = API::Node::ofType("mongoose", "Document")
|
||||
or
|
||||
result =
|
||||
getAMongooseDocument()
|
||||
.getMember(any(string name | MethodSignatures::returnsDocument(name)))
|
||||
.getReturn()
|
||||
}
|
||||
|
||||
private module MethodSignatures {
|
||||
/**
|
||||
* Holds if Document method `name` returns a Query.
|
||||
*/
|
||||
predicate returnsQuery(string name) {
|
||||
// Documents are subtypes of Models
|
||||
Model::MethodSignatures::returnsQuery(name) or
|
||||
name = "replaceOne" or
|
||||
name = "update" or
|
||||
name = "updateOne"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if Document method `name` interprets parameter `n` as a query.
|
||||
*/
|
||||
predicate interpretsArgumentAsQuery(string name, int n) {
|
||||
// Documents are subtypes of Models
|
||||
Model::MethodSignatures::interpretsArgumentAsQuery(name, n)
|
||||
or
|
||||
n = 0 and
|
||||
(
|
||||
name = "replaceOne" or
|
||||
name = "update" or
|
||||
name = "updateOne"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if Document method `name` returns a query that results in
|
||||
* one or more documents, the documents are wrapped in an array
|
||||
* if `asArray` is true.
|
||||
*/
|
||||
predicate returnsDocumentQuery(string name, boolean asArray) {
|
||||
// Documents are subtypes of Models
|
||||
Model::MethodSignatures::returnsDocumentQuery(name, asArray)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if Document method `name` returns a Document.
|
||||
*/
|
||||
predicate returnsDocument(string name) {
|
||||
name = "depopulate" or
|
||||
name = "init" or
|
||||
name = "populate" or
|
||||
name = "overwrite"
|
||||
}
|
||||
}
|
||||
class Model extends MongoDB::Collection {
|
||||
Model() { this = getAMongooseInstance().getAMemberCall("model") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -504,9 +153,7 @@ private module Mongoose {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
exists(string prop |
|
||||
this = createConnection().getParameter(3).getMember(prop).getARhs().asExpr()
|
||||
|
|
||||
exists(string prop | this = createConnection().getOptionArgument(3, prop).asExpr() |
|
||||
prop = "user" and kind = "user name"
|
||||
or
|
||||
prop = "pass" and kind = "password"
|
||||
@@ -515,222 +162,4 @@ private module Mongoose {
|
||||
|
||||
override string getCredentialsKind() { result = kind }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is interpreted as a (part of a) MongoDB query.
|
||||
*/
|
||||
class MongoDBQueryPart extends NoSQL::Query {
|
||||
MongooseFunction f;
|
||||
|
||||
MongoDBQueryPart() { this = f.getQueryArgument().getARhs().asExpr() }
|
||||
|
||||
override DataFlow::Node getACodeOperator() {
|
||||
result = getADollarWhereProperty(f.getQueryArgument())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An evaluation of a MongoDB query.
|
||||
*/
|
||||
class ShorthandQueryEvaluation extends DatabaseAccess, DataFlow::InvokeNode {
|
||||
MongooseFunction f;
|
||||
|
||||
ShorthandQueryEvaluation() {
|
||||
this = f.getACall() and
|
||||
// shorthand for execution: provide a callback
|
||||
exists(f.getQueryReturn()) and
|
||||
exists(this.getCallback(this.getNumArgument() - 1))
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
// NB: the complete information is not easily accessible for deeply chained calls
|
||||
f.getQueryArgument().getARhs() = result
|
||||
}
|
||||
}
|
||||
|
||||
class ExplicitQueryEvaluation extends DatabaseAccess {
|
||||
ExplicitQueryEvaluation() {
|
||||
// explicit execution using a Query method call
|
||||
Query::getAMongooseQuery().getMember(["exec", "then", "catch"]).getACall() = this
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
// NB: the complete information is not easily accessible for deeply chained calls
|
||||
none()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modeling the Minimongo library.
|
||||
*/
|
||||
private module Minimongo {
|
||||
/**
|
||||
* Provides signatures for the Collection methods.
|
||||
*/
|
||||
module CollectionMethodSignatures {
|
||||
/**
|
||||
* Holds if Collection method `name` interprets parameter `n` as a query.
|
||||
*/
|
||||
predicate interpretsArgumentAsQuery(string m, int queryArgIdx) {
|
||||
// implements most of the MongoDB interface
|
||||
MongoDB::CollectionMethodSignatures::interpretsArgumentAsQuery(m, queryArgIdx)
|
||||
}
|
||||
}
|
||||
|
||||
/** A call to a Minimongo query method. */
|
||||
private class QueryCall extends DatabaseAccess, API::CallNode {
|
||||
int queryArgIdx;
|
||||
|
||||
QueryCall() {
|
||||
exists(string m |
|
||||
this =
|
||||
API::moduleImport("minimongo")
|
||||
.getAMember()
|
||||
.getReturn()
|
||||
.getAMember()
|
||||
.getMember(m)
|
||||
.getACall() and
|
||||
CollectionMethodSignatures::interpretsArgumentAsQuery(m, queryArgIdx)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = getArgument(queryArgIdx) }
|
||||
|
||||
DataFlow::Node getACodeOperator() {
|
||||
result = getADollarWhereProperty(getParameter(queryArgIdx))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is interpreted as a Minimongo query.
|
||||
*/
|
||||
class Query extends NoSQL::Query {
|
||||
QueryCall qc;
|
||||
|
||||
Query() { this = qc.getAQueryArgument().asExpr() }
|
||||
|
||||
override DataFlow::Node getACodeOperator() { result = qc.getACodeOperator() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modeling the MarsDB library.
|
||||
*/
|
||||
private module MarsDB {
|
||||
/** A call to a MarsDB query method. */
|
||||
private class QueryCall extends DatabaseAccess, API::CallNode {
|
||||
int queryArgIdx;
|
||||
|
||||
QueryCall() {
|
||||
exists(string m |
|
||||
this =
|
||||
API::moduleImport("marsdb").getMember("Collection").getInstance().getMember(m).getACall() and
|
||||
// implements parts of the Minimongo interface
|
||||
Minimongo::CollectionMethodSignatures::interpretsArgumentAsQuery(m, queryArgIdx)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = getArgument(queryArgIdx) }
|
||||
|
||||
DataFlow::Node getACodeOperator() {
|
||||
result = getADollarWhereProperty(getParameter(queryArgIdx))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is interpreted as a MarsDB query.
|
||||
*/
|
||||
class Query extends NoSQL::Query {
|
||||
QueryCall qc;
|
||||
|
||||
Query() { this = qc.getAQueryArgument().asExpr() }
|
||||
|
||||
override DataFlow::Node getACodeOperator() { result = qc.getACodeOperator() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modeling the `Node Redis` library.
|
||||
*
|
||||
* Redis is an in-memory key-value store and not a database,
|
||||
* but `Node Redis` can be exploited similarly to a NoSQL database by giving a method an array as argument instead of a string.
|
||||
* As an example the below two invocations of `client.set` are equivalent:
|
||||
*
|
||||
* ```
|
||||
* const redis = require("redis");
|
||||
* const client = redis.createClient();
|
||||
* client.set("key", "value");
|
||||
* client.set(["key", "value"]);
|
||||
* ```
|
||||
*
|
||||
* ioredis is a very similar library. However, ioredis does not support array arguments in the same way, and is therefore not vulnerable to the same kind of type confusion.
|
||||
*/
|
||||
private module Redis {
|
||||
/**
|
||||
* Gets a `Node Redis` client.
|
||||
*/
|
||||
private API::Node client() {
|
||||
result = API::moduleImport("redis").getMember("createClient").getReturn()
|
||||
or
|
||||
result = API::moduleImport("redis").getMember("RedisClient").getInstance()
|
||||
or
|
||||
result = client().getMember("duplicate").getReturn()
|
||||
or
|
||||
result = client().getMember("duplicate").getLastParameter().getParameter(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a (possibly chained) reference to a batch operation object.
|
||||
* These have the same API as a redis client, except the calls are chained, and the sequence is terminated with a `.exec` call.
|
||||
*/
|
||||
private API::Node multi() {
|
||||
result = client().getMember(["multi", "batch"]).getReturn()
|
||||
or
|
||||
result = multi().getAMember().getReturn()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a `Node Redis` client instance. Either a client created using `createClient()`, or a batch operation object.
|
||||
*/
|
||||
private API::Node redis() { result = [client(), multi()] }
|
||||
|
||||
/**
|
||||
* Provides signatures for the query methods from Node Redis.
|
||||
*/
|
||||
module QuerySignatures {
|
||||
/**
|
||||
* Holds if `method` interprets parameter `argIndex` as a key, and a later parameter determines a value/field.
|
||||
* Thereby the method is vulnerable if parameter `argIndex` is unexpectedly an array instead of a string, as an attacker can control arguments to Redis that the attacker was not supposed to control.
|
||||
*
|
||||
* Only setters and similar methods are included.
|
||||
* For getter-like methods it is not generally possible to gain access "outside" of where you are supposed to have access,
|
||||
* it is at most possible to get a Redis call to return more results than expected (e.g. by adding more members to [`geohash`](https://redis.io/commands/geohash)).
|
||||
*/
|
||||
predicate argumentIsAmbiguousKey(string method, int argIndex) {
|
||||
method =
|
||||
[
|
||||
"set", "publish", "append", "bitfield", "decrby", "getset", "hincrby", "hincrbyfloat",
|
||||
"hset", "hsetnx", "incrby", "incrbyfloat", "linsert", "lpush", "lpushx", "lset", "ltrim",
|
||||
"rename", "renamenx", "rpushx", "setbit", "setex", "smove", "zincrby", "zinterstore",
|
||||
"hdel", "lpush", "pfadd", "rpush", "sadd", "sdiffstore", "srem"
|
||||
] and
|
||||
argIndex = 0
|
||||
or
|
||||
method = ["bitop", "hmset", "mset", "msetnx", "geoadd"] and
|
||||
argIndex in [0 .. any(DataFlow::InvokeNode invk).getNumArgument() - 1]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is interpreted as a key in a Node Redis call.
|
||||
*/
|
||||
class RedisKeyArgument extends NoSQL::Query {
|
||||
RedisKeyArgument() {
|
||||
exists(string method, int argIndex |
|
||||
QuerySignatures::argumentIsAmbiguousKey(method, argIndex) and
|
||||
this = redis().getMember(method).getParameter(argIndex).getARhs().asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,56 +448,11 @@ module NodeJSLib {
|
||||
*/
|
||||
module FS {
|
||||
/**
|
||||
* A member `member` from module `fs` or its drop-in replacements `graceful-fs`, `fs-extra`, `original-fs`.
|
||||
* A member `member` from module `fs`.
|
||||
*/
|
||||
DataFlow::SourceNode moduleMember(string member) {
|
||||
result = fsModule(DataFlow::TypeTracker::end()).getAPropertyRead(member)
|
||||
}
|
||||
|
||||
private DataFlow::SourceNode fsModule(DataFlow::TypeTracker t) {
|
||||
exists(string moduleName |
|
||||
moduleName = ["mz/fs", "original-fs", "fs-extra", "graceful-fs", "fs"]
|
||||
|
|
||||
result = DataFlow::moduleImport(moduleName)
|
||||
or
|
||||
// extra support for flexible names
|
||||
result.asExpr().(Require).getArgument(0).mayHaveStringValue(moduleName)
|
||||
) and
|
||||
t.start()
|
||||
or
|
||||
t.start() and
|
||||
result = DataFlow::moduleMember("fs", "promises")
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2, DataFlow::SourceNode pred | pred = fsModule(t2) |
|
||||
result = pred.track(t2, t)
|
||||
or
|
||||
t.continue() = t2 and
|
||||
exists(Promisify::PromisifyAllCall promisifyAllCall |
|
||||
result = promisifyAllCall and
|
||||
pred.flowsTo(promisifyAllCall.getArgument(0))
|
||||
)
|
||||
or
|
||||
// const fs = require('fs');
|
||||
// let fs_copy = methods.reduce((obj, method) => {
|
||||
// obj[method] = fs[method];
|
||||
// return obj;
|
||||
// }, {});
|
||||
t.continue() = t2 and
|
||||
exists(
|
||||
DataFlow::MethodCallNode call, DataFlow::ParameterNode obj, DataFlow::SourceNode method
|
||||
|
|
||||
call.getMethodName() = "reduce" and
|
||||
result = call and
|
||||
obj = call.getABoundCallbackParameter(0, 0) and
|
||||
obj.flowsTo(any(DataFlow::FunctionNode f).getAReturn()) and
|
||||
exists(DataFlow::PropWrite write, DataFlow::PropRead read |
|
||||
write = obj.getAPropertyWrite() and
|
||||
method.flowsToExpr(write.getPropertyNameExpr()) and
|
||||
method.flowsToExpr(read.getPropertyNameExpr()) and
|
||||
read.getBase().getALocalSource() = fsModule(t2) and
|
||||
write.getRhs() = maybePromisified(read)
|
||||
)
|
||||
)
|
||||
exists(string moduleName | moduleName = ["fs"] |
|
||||
result = DataFlow::moduleMember(moduleName, member)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -508,7 +463,7 @@ module NodeJSLib {
|
||||
private class NodeJSFileSystemAccess extends FileSystemAccess, DataFlow::CallNode {
|
||||
string methodName;
|
||||
|
||||
NodeJSFileSystemAccess() { this = maybePromisified(FS::moduleMember(methodName)).getACall() }
|
||||
NodeJSFileSystemAccess() { this = FS::moduleMember(methodName).getACall() }
|
||||
|
||||
/**
|
||||
* Gets the name of the called method.
|
||||
|
||||
@@ -28,54 +28,38 @@ module SQL {
|
||||
* Provides classes modelling the (API compatible) `mysql` and `mysql2` packages.
|
||||
*/
|
||||
private module MySql {
|
||||
private string moduleName() { result = ["mysql", "mysql2", "mysql2/promise"] }
|
||||
private DataFlow::SourceNode mysql() { result = DataFlow::moduleImport(["mysql", "mysql2"]) }
|
||||
|
||||
/** Gets the package name `mysql` or `mysql2`. */
|
||||
API::Node mysql() { result = API::moduleImport(moduleName()) }
|
||||
private DataFlow::CallNode createPool() { result = mysql().getAMemberCall("createPool") }
|
||||
|
||||
/** Gets a reference to `mysql.createConnection`. */
|
||||
API::Node createConnection() {
|
||||
result = mysql().getMember(["createConnection", "createConnectionPromise"])
|
||||
/** Gets a reference to a MySQL pool. */
|
||||
private DataFlow::SourceNode pool(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = createPool()
|
||||
}
|
||||
|
||||
/** Gets a reference to `mysql.createPool`. */
|
||||
API::Node createPool() { result = mysql().getMember(["createPool", "createPoolCluster"]) }
|
||||
/** Gets a reference to a MySQL pool. */
|
||||
private DataFlow::SourceNode pool() { result = pool(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** Gets a node that contains a MySQL pool created using `mysql.createPool()`. */
|
||||
API::Node pool() {
|
||||
result = createPool().getReturn()
|
||||
or
|
||||
result = pool().getMember("on").getReturn()
|
||||
or
|
||||
result = API::Node::ofType(moduleName(), ["Pool", "PoolCluster"])
|
||||
}
|
||||
/** Gets a call to `mysql.createConnection`. */
|
||||
DataFlow::CallNode createConnection() { result = mysql().getAMemberCall("createConnection") }
|
||||
|
||||
/** Gets a data flow node that contains a freshly created MySQL connection instance. */
|
||||
API::Node connection() {
|
||||
result = createConnection().getReturn()
|
||||
or
|
||||
result = createConnection().getReturn().getPromised()
|
||||
or
|
||||
result = pool().getMember("getConnection").getParameter(0).getParameter(1)
|
||||
or
|
||||
result = pool().getMember("getConnection").getPromised()
|
||||
or
|
||||
exists(API::CallNode call |
|
||||
call = pool().getMember("on").getACall() and
|
||||
call.getArgument(0).getStringValue() = ["connection", "acquire", "release"] and
|
||||
result = call.getParameter(1).getParameter(0)
|
||||
/** Gets a reference to a MySQL connection instance. */
|
||||
private DataFlow::SourceNode connection(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
(
|
||||
result = createConnection()
|
||||
or
|
||||
result = pool().getAMethodCall("getConnection").getABoundCallbackParameter(0, 1)
|
||||
)
|
||||
or
|
||||
result = API::Node::ofType(moduleName(), ["Connection", "PoolConnection"])
|
||||
}
|
||||
|
||||
/** Gets a reference to a MySQL connection instance. */
|
||||
DataFlow::SourceNode connection() { result = connection(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** A call to the MySql `query` method. */
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
QueryCall() {
|
||||
exists(API::Node recv | recv = pool() or recv = connection() |
|
||||
this = recv.getMember(["query", "execute"]).getACall()
|
||||
)
|
||||
}
|
||||
QueryCall() { this = [pool(), connection()].getAMethodCall("query") }
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = getArgument(0) }
|
||||
}
|
||||
@@ -88,7 +72,7 @@ private module MySql {
|
||||
/** A call to the `escape` or `escapeId` method that performs SQL sanitization. */
|
||||
class EscapingSanitizer extends SQL::SqlSanitizer, MethodCallExpr {
|
||||
EscapingSanitizer() {
|
||||
this = [mysql(), pool(), connection()].getMember(["escape", "escapeId"]).getACall().asExpr() and
|
||||
this = [mysql(), pool(), connection()].getAMethodCall(["escape", "escapeId"]).asExpr() and
|
||||
input = this.getArgument(0) and
|
||||
output = this
|
||||
}
|
||||
@@ -99,9 +83,8 @@ private module MySql {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
exists(API::Node callee, string prop |
|
||||
callee in [createConnection(), createPool()] and
|
||||
this = callee.getParameter(0).getMember(prop).getARhs().asExpr() and
|
||||
exists(string prop |
|
||||
this = [createConnection(), createPool()].getOptionArgument(0, prop).asExpr() and
|
||||
(
|
||||
prop = "user" and kind = "user name"
|
||||
or
|
||||
@@ -118,61 +101,23 @@ private module MySql {
|
||||
* Provides classes modelling the PostgreSQL packages, such as `pg` and `pg-promise`.
|
||||
*/
|
||||
private module Postgres {
|
||||
API::Node pg() {
|
||||
result = API::moduleImport("pg")
|
||||
or
|
||||
result = pgpMain().getMember("pg")
|
||||
}
|
||||
|
||||
/** Gets a reference to the `Client` constructor in the `pg` package, for example `require('pg').Client`. */
|
||||
API::Node newClient() { result = pg().getMember("Client") }
|
||||
|
||||
/** Gets a freshly created Postgres client instance. */
|
||||
API::Node client() {
|
||||
result = newClient().getInstance()
|
||||
or
|
||||
// pool.connect(function(err, client) { ... })
|
||||
result = pool().getMember("connect").getParameter(0).getParameter(1)
|
||||
or
|
||||
// await pool.connect()
|
||||
result = pool().getMember("connect").getReturn().getPromised()
|
||||
or
|
||||
result = pgpConnection().getMember("client")
|
||||
or
|
||||
exists(API::CallNode call |
|
||||
call = pool().getMember("on").getACall() and
|
||||
call.getArgument(0).getStringValue() = ["connect", "acquire"] and
|
||||
result = call.getParameter(1).getParameter(0)
|
||||
)
|
||||
or
|
||||
result = client().getMember("on").getReturn()
|
||||
or
|
||||
result = API::Node::ofType("pg", ["Client", "PoolClient"])
|
||||
}
|
||||
|
||||
/** Gets a constructor that when invoked constructs a new connection pool. */
|
||||
API::Node newPool() {
|
||||
/** Gets an expression that constructs a new connection pool. */
|
||||
DataFlow::InvokeNode newPool() {
|
||||
// new require('pg').Pool()
|
||||
result = pg().getMember("Pool")
|
||||
result = DataFlow::moduleImport("pg").getAConstructorInvocation("Pool")
|
||||
or
|
||||
// new require('pg-pool')
|
||||
result = API::moduleImport("pg-pool")
|
||||
result = DataFlow::moduleImport("pg-pool").getAnInstantiation()
|
||||
}
|
||||
|
||||
/** Gets an API node that refers to a connection pool. */
|
||||
API::Node pool() {
|
||||
result = newPool().getInstance()
|
||||
or
|
||||
result = pgpDatabase().getMember("$pool")
|
||||
or
|
||||
result = pool().getMember("on").getReturn()
|
||||
or
|
||||
result = API::Node::ofType("pg", "Pool")
|
||||
/** Gets a creation of a Postgres client. */
|
||||
DataFlow::InvokeNode newClient() {
|
||||
result = DataFlow::moduleImport("pg").getAConstructorInvocation("Client")
|
||||
}
|
||||
|
||||
/** A call to the Postgres `query` method. */
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
QueryCall() { this = [client(), pool()].getMember("query").getACall() }
|
||||
QueryCall() { this = [newClient(), newPool()].getAMethodCall("query") }
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = getArgument(0) }
|
||||
}
|
||||
@@ -191,11 +136,7 @@ private module Postgres {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
exists(string prop |
|
||||
this = [newClient(), newPool()].getParameter(0).getMember(prop).getARhs().asExpr()
|
||||
or
|
||||
this = pgPromise().getParameter(0).getMember(prop).getARhs().asExpr()
|
||||
|
|
||||
exists(string prop | this = [newClient(), newPool()].getOptionArgument(0, prop).asExpr() |
|
||||
prop = "user" and kind = "user name"
|
||||
or
|
||||
prop = "password" and kind = prop
|
||||
@@ -335,35 +276,30 @@ private module Postgres {
|
||||
*/
|
||||
private module Sqlite {
|
||||
/** Gets a reference to the `sqlite3` module. */
|
||||
API::Node sqlite() {
|
||||
result = API::moduleImport("sqlite3")
|
||||
DataFlow::SourceNode sqlite() {
|
||||
result = DataFlow::moduleImport("sqlite3")
|
||||
or
|
||||
result = sqlite().getMember("verbose").getReturn()
|
||||
result = sqlite().getAMemberCall("verbose")
|
||||
}
|
||||
|
||||
/** Gets an expression that constructs or returns a Sqlite database instance. */
|
||||
API::Node database() {
|
||||
/** Gets an expression that constructs a Sqlite database instance. */
|
||||
DataFlow::SourceNode newDb() {
|
||||
// new require('sqlite3').Database()
|
||||
result = sqlite().getMember("Database").getInstance()
|
||||
or
|
||||
// chained call
|
||||
result = getAChainingQueryCall()
|
||||
or
|
||||
result = API::Node::ofType("sqlite3", "Database")
|
||||
result = sqlite().getAConstructorInvocation("Database")
|
||||
}
|
||||
|
||||
/** A call to a query method on a Sqlite database instance that returns the same instance. */
|
||||
private API::Node getAChainingQueryCall() {
|
||||
result = database().getMember(["all", "each", "exec", "get", "run"]).getReturn()
|
||||
/** Gets a data flow node referring to a Sqlite database instance. */
|
||||
private DataFlow::SourceNode db(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = newDb()
|
||||
}
|
||||
|
||||
/** Gets a data flow node referring to a Sqlite database instance. */
|
||||
DataFlow::SourceNode db() { result = db(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** A call to a Sqlite query method. */
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
QueryCall() {
|
||||
this = getAChainingQueryCall().getAnImmediateUse()
|
||||
or
|
||||
this = database().getMember("prepare").getACall()
|
||||
}
|
||||
QueryCall() { this = db().getAMethodCall(["all", "each", "exec", "get", "prepare", "run"]) }
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = getArgument(0) }
|
||||
}
|
||||
@@ -373,276 +309,3 @@ private module Sqlite {
|
||||
QueryString() { this = any(QueryCall qc).getAQueryArgument().asExpr() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modelling the `mssql` package.
|
||||
*/
|
||||
private module MsSql {
|
||||
/** Gets a reference to the `mssql` module. */
|
||||
API::Node mssql() { result = API::moduleImport("mssql") }
|
||||
|
||||
/** Gets a node referring to an instance of the given class. */
|
||||
API::Node mssqlClass(string name) {
|
||||
result = mssql().getMember(name).getInstance()
|
||||
or
|
||||
result = API::Node::ofType("mssql", name)
|
||||
}
|
||||
|
||||
/** Gets an API node referring to a Request object. */
|
||||
API::Node request() {
|
||||
result = mssqlClass("Request")
|
||||
or
|
||||
result = request().getMember(["input", "replaceInput", "output", "replaceOutput"]).getReturn()
|
||||
or
|
||||
result = [transaction(), pool()].getMember("request").getReturn()
|
||||
}
|
||||
|
||||
/** Gets an API node referring to a Transaction object. */
|
||||
API::Node transaction() {
|
||||
result = mssqlClass("Transaction")
|
||||
or
|
||||
result = pool().getMember("transaction").getReturn()
|
||||
}
|
||||
|
||||
/** Gets a API node referring to a ConnectionPool object. */
|
||||
API::Node pool() { result = mssqlClass("ConnectionPool") }
|
||||
|
||||
/** A tagged template evaluated as a query. */
|
||||
private class QueryTemplateExpr extends DatabaseAccess, DataFlow::ValueNode {
|
||||
override TaggedTemplateExpr astNode;
|
||||
|
||||
QueryTemplateExpr() {
|
||||
mssql().getMember("query").getAUse() = DataFlow::valueNode(astNode.getTag())
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
result = DataFlow::valueNode(astNode.getTemplate().getAnElement())
|
||||
}
|
||||
}
|
||||
|
||||
/** A call to a MsSql query method. */
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
QueryCall() { this = [mssql(), request()].getMember(["query", "batch"]).getACall() }
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = getArgument(0) }
|
||||
}
|
||||
|
||||
/** An expression that is passed to a method that interprets it as SQL. */
|
||||
class QueryString extends SQL::SqlString {
|
||||
QueryString() {
|
||||
exists(DatabaseAccess dba | dba instanceof QueryTemplateExpr or dba instanceof QueryCall |
|
||||
this = dba.getAQueryArgument().asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An element of a query template, which is automatically sanitized. */
|
||||
class QueryTemplateSanitizer extends SQL::SqlSanitizer {
|
||||
QueryTemplateSanitizer() {
|
||||
this = any(QueryTemplateExpr qte).getAQueryArgument().asExpr() and
|
||||
input = this and
|
||||
output = this
|
||||
}
|
||||
}
|
||||
|
||||
/** An expression that is passed as user name or password when creating a client or a pool. */
|
||||
class Credentials extends CredentialsExpr {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
exists(API::Node callee, string prop |
|
||||
(
|
||||
callee = mssql().getMember("connect")
|
||||
or
|
||||
callee = mssql().getMember("ConnectionPool")
|
||||
) and
|
||||
this = callee.getParameter(0).getMember(prop).getARhs().asExpr() and
|
||||
(
|
||||
prop = "user" and kind = "user name"
|
||||
or
|
||||
prop = "password" and kind = prop
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override string getCredentialsKind() { result = kind }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modelling the `sequelize` package.
|
||||
*/
|
||||
private module Sequelize {
|
||||
/** Gets an import of the `sequelize` module or one that re-exports it. */
|
||||
API::Node sequelize() { result = API::moduleImport(["sequelize", "sequelize-typescript"]) }
|
||||
|
||||
/** Gets an expression that creates an instance of the `Sequelize` class. */
|
||||
API::Node instance() {
|
||||
result = [sequelize(), sequelize().getMember("Sequelize")].getInstance()
|
||||
or
|
||||
result = API::Node::ofType(["sequelize", "sequelize-typescript"], ["Sequelize", "default"])
|
||||
}
|
||||
|
||||
/** A call to `Sequelize.query`. */
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
QueryCall() { this = instance().getMember("query").getACall() }
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
result = getArgument(0)
|
||||
or
|
||||
result = getOptionArgument(0, "query")
|
||||
}
|
||||
}
|
||||
|
||||
/** An expression that is passed to `Sequelize.query` method and hence interpreted as SQL. */
|
||||
class QueryString extends SQL::SqlString {
|
||||
QueryString() {
|
||||
this = any(QueryCall qc).getAQueryArgument().asExpr()
|
||||
or
|
||||
this = sequelize().getMember(["literal", "asIs"]).getParameter(0).getARhs().asExpr()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is passed as user name or password when creating an instance of the
|
||||
* `Sequelize` class.
|
||||
*/
|
||||
class Credentials extends CredentialsExpr {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
exists(NewExpr ne, string prop |
|
||||
ne = sequelize().getAnInstantiation().asExpr() and
|
||||
(
|
||||
this = ne.getArgument(1) and prop = "username"
|
||||
or
|
||||
this = ne.getArgument(2) and prop = "password"
|
||||
or
|
||||
ne.hasOptionArgument(ne.getNumArgument() - 1, prop, this)
|
||||
) and
|
||||
(
|
||||
prop = "username" and kind = "user name"
|
||||
or
|
||||
prop = "password" and kind = prop
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override string getCredentialsKind() { result = kind }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modelling the Google Cloud Spanner library.
|
||||
*/
|
||||
private module Spanner {
|
||||
/**
|
||||
* Gets a node that refers to the `Spanner` class
|
||||
*/
|
||||
API::Node spanner() {
|
||||
// older versions
|
||||
result = API::moduleImport("@google-cloud/spanner")
|
||||
or
|
||||
// newer versions
|
||||
result = API::moduleImport("@google-cloud/spanner").getMember("Spanner")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that refers to an instance of the `Database` class.
|
||||
*/
|
||||
API::Node database() {
|
||||
result =
|
||||
spanner().getReturn().getMember("instance").getReturn().getMember("database").getReturn()
|
||||
or
|
||||
result = API::Node::ofType("@google-cloud/spanner", "Database")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that refers to an instance of the `v1.SpannerClient` class.
|
||||
*/
|
||||
API::Node v1SpannerClient() {
|
||||
result = spanner().getMember("v1").getMember("SpannerClient").getInstance()
|
||||
or
|
||||
result = API::Node::ofType("@google-cloud/spanner", "v1.SpannerClient")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that refers to a transaction object.
|
||||
*/
|
||||
API::Node transaction() {
|
||||
result =
|
||||
database()
|
||||
.getMember(["runTransaction", "runTransactionAsync"])
|
||||
.getParameter([0, 1])
|
||||
.getParameter(1)
|
||||
or
|
||||
result = API::Node::ofType("@google-cloud/spanner", "Transaction")
|
||||
}
|
||||
|
||||
/** Gets an API node referring to a `BatchTransaction` object. */
|
||||
API::Node batchTransaction() {
|
||||
result = database().getMember("batchTransaction").getReturn()
|
||||
or
|
||||
result = database().getMember("createBatchTransaction").getReturn().getPromised()
|
||||
or
|
||||
result = API::Node::ofType("@google-cloud/spanner", "BatchTransaction")
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a Spanner method that executes a SQL query.
|
||||
*/
|
||||
abstract class SqlExecution extends DatabaseAccess, DataFlow::InvokeNode { }
|
||||
|
||||
/**
|
||||
* A SQL execution that takes the input directly in the first argument or in the `sql` option.
|
||||
*/
|
||||
class SqlExecutionDirect extends SqlExecution {
|
||||
SqlExecutionDirect() {
|
||||
this = database().getMember(["run", "runPartitionedUpdate", "runStream"]).getACall()
|
||||
or
|
||||
this = transaction().getMember(["run", "runStream", "runUpdate"]).getACall()
|
||||
or
|
||||
this = batchTransaction().getMember("createQueryPartitions").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
result = getArgument(0)
|
||||
or
|
||||
result = getOptionArgument(0, "sql")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A SQL execution that takes an array of SQL strings or { sql: string } objects.
|
||||
*/
|
||||
class SqlExecutionBatch extends SqlExecution, API::CallNode {
|
||||
SqlExecutionBatch() { this = transaction().getMember("batchUpdate").getACall() }
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
// just use the whole array as the query argument, as arrays becomes tainted if one of the elements
|
||||
// are tainted
|
||||
result = getArgument(0)
|
||||
or
|
||||
result = getParameter(0).getUnknownMember().getMember("sql").getARhs()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A SQL execution that only takes the input in the `sql` option, and do not accept query strings
|
||||
* directly.
|
||||
*/
|
||||
class SqlExecutionWithOption extends SqlExecution {
|
||||
SqlExecutionWithOption() {
|
||||
this = v1SpannerClient().getMember(["executeSql", "executeStreamingSql"]).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = getOptionArgument(0, "sql") }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is interpreted as a SQL string.
|
||||
*/
|
||||
class QueryString extends SQL::SqlString {
|
||||
QueryString() { this = any(SqlExecution se).getAQueryArgument().asExpr() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,25 +36,7 @@ module ParseTorrent {
|
||||
* An access to user-controlled torrent information.
|
||||
*/
|
||||
class UserControlledTorrentInfo extends RemoteFlowSource {
|
||||
UserControlledTorrentInfo() {
|
||||
exists(DataFlow::SourceNode ref, DataFlow::PropRead read |
|
||||
ref = parsedTorrentRef() and
|
||||
read = ref.getAPropertyRead() and
|
||||
this = read
|
||||
|
|
||||
exists(string prop |
|
||||
not (
|
||||
prop = "private" or
|
||||
prop = "infoHash" or
|
||||
prop = "length"
|
||||
// "pieceLength" and "lastPieceLength" are not guaranteed to be numbers as of commit ae3ad15d
|
||||
) and
|
||||
read.getPropertyName() = prop
|
||||
)
|
||||
or
|
||||
not exists(read.getPropertyName())
|
||||
)
|
||||
}
|
||||
UserControlledTorrentInfo() { none() }
|
||||
|
||||
override string getSourceType() { result = "torrent information" }
|
||||
}
|
||||
|
||||
@@ -484,8 +484,6 @@ module JQuery {
|
||||
private DataFlow::SourceNode dollar(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = dollarSource()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = dollar(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -519,14 +517,6 @@ module JQuery {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `this` node in a JQuery plugin function, which is a JQuery object.
|
||||
*/
|
||||
private class JQueryPluginThisObject extends Range {
|
||||
JQueryPluginThisObject() {
|
||||
this = DataFlow::thisNode(any(JQueryPluginMethod method).getFunction())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** A source of jQuery objects from the AST-based `JQueryObject` class. */
|
||||
|
||||
@@ -260,6 +260,8 @@ module Stages {
|
||||
exists(RemoteFlowSource r)
|
||||
or
|
||||
exists(Exports::getALibraryInputParameter())
|
||||
or
|
||||
any(RegExpTerm t).isUsedAsRegExp()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
/**
|
||||
* Provides precicates for reasoning about bad tag filter vulnerabilities.
|
||||
*/
|
||||
|
||||
import performance.ReDoSUtil
|
||||
|
||||
/**
|
||||
* A module for determining if a regexp matches a given string,
|
||||
* and reasoning about which capture groups are filled by a given string.
|
||||
*/
|
||||
private module RegexpMatching {
|
||||
/**
|
||||
* A class to test whether a regular expression matches a string.
|
||||
* Override this class and extend `test`/`testWithGroups` to configure which strings should be tested for acceptance by this regular expression.
|
||||
* The result can afterwards be read from the `matches` predicate.
|
||||
*
|
||||
* Strings in the `testWithGroups` predicate are also tested for which capture groups are filled by the given string.
|
||||
* The result is available in the `fillCaptureGroup` predicate.
|
||||
*/
|
||||
abstract class MatchedRegExp extends RegExpTerm {
|
||||
MatchedRegExp() { this.isRootTerm() }
|
||||
|
||||
/**
|
||||
* Holds if it should be tested whether this regular expression matches `str`.
|
||||
*
|
||||
* If `ignorePrefix` is true, then a regexp without a start anchor will be treated as if it had a start anchor.
|
||||
* E.g. a regular expression `/foo$/` will match any string that ends with "foo",
|
||||
* but if `ignorePrefix` is true, it will only match "foo".
|
||||
*/
|
||||
predicate test(string str, boolean ignorePrefix) {
|
||||
none() // maybe overriden in subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `test(..)`, but where the `fillsCaptureGroup` afterwards tells which capture groups were filled by the given string.
|
||||
*/
|
||||
predicate testWithGroups(string str, boolean ignorePrefix) {
|
||||
none() // maybe overriden in subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this RegExp matches `str`, where `str` is either in the `test` or `testWithGroups` predicate.
|
||||
*/
|
||||
final predicate matches(string str) {
|
||||
exists(State state | state = getAState(this, str.length() - 1, str, _) |
|
||||
epsilonSucc*(state) = Accept(_)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if matching `str` may fill capture group number `g`.
|
||||
* Only holds if `str` is in the `testWithGroups` predicate.
|
||||
*/
|
||||
final predicate fillsCaptureGroup(string str, int g) {
|
||||
exists(State s |
|
||||
s = getAStateThatReachesAccept(this, _, str, _) and
|
||||
g = group(s.getRepr())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a state the regular expression `reg` can be in after matching the `i`th char in `str`.
|
||||
* The regular expression is modelled as a non-determistic finite automaton,
|
||||
* the regular expression can therefore be in multiple states after matching a character.
|
||||
*
|
||||
* It's a forward search to all possible states, and there is thus no guarantee that the state is on a path to an accepting state.
|
||||
*/
|
||||
private State getAState(MatchedRegExp reg, int i, string str, boolean ignorePrefix) {
|
||||
// start state, the -1 position before any chars have been matched
|
||||
i = -1 and
|
||||
(
|
||||
reg.test(str, ignorePrefix)
|
||||
or
|
||||
reg.testWithGroups(str, ignorePrefix)
|
||||
) and
|
||||
result.getRepr().getRootTerm() = reg and
|
||||
isStartState(result)
|
||||
or
|
||||
// recursive case
|
||||
result = getAStateAfterMatching(reg, _, str, i, _, ignorePrefix)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next state after the `prev` state from `reg`.
|
||||
* `prev` is the state after matching `fromIndex` chars in `str`,
|
||||
* and the result is the state after matching `toIndex` chars in `str`.
|
||||
*
|
||||
* This predicate is used as a step relation in the forwards search (`getAState`),
|
||||
* and also as a step relation in the later backwards search (`getAStateThatReachesAccept`).
|
||||
*/
|
||||
private State getAStateAfterMatching(
|
||||
MatchedRegExp reg, State prev, string str, int toIndex, int fromIndex, boolean ignorePrefix
|
||||
) {
|
||||
// the basic recursive case - outlined into a noopt helper to make performance work out.
|
||||
result = getAStateAfterMatchingAux(reg, prev, str, toIndex, fromIndex, ignorePrefix)
|
||||
or
|
||||
// we can skip past word boundaries if the next char is a non-word char.
|
||||
fromIndex = toIndex and
|
||||
prev.getRepr() instanceof RegExpWordBoundary and
|
||||
prev = getAState(reg, toIndex, str, ignorePrefix) and
|
||||
after(prev.getRepr()) = result and
|
||||
str.charAt(toIndex + 1).regexpMatch("\\W") // \W matches any non-word char.
|
||||
}
|
||||
|
||||
pragma[noopt]
|
||||
private State getAStateAfterMatchingAux(
|
||||
MatchedRegExp reg, State prev, string str, int toIndex, int fromIndex, boolean ignorePrefix
|
||||
) {
|
||||
prev = getAState(reg, fromIndex, str, ignorePrefix) and
|
||||
fromIndex = toIndex - 1 and
|
||||
exists(string char | char = str.charAt(toIndex) | specializedDeltaClosed(prev, char, result)) and
|
||||
not discardedPrefixStep(prev, result, ignorePrefix)
|
||||
}
|
||||
|
||||
/** Holds if a step from `prev` to `next` should be discarded when the `ignorePrefix` flag is set. */
|
||||
private predicate discardedPrefixStep(State prev, State next, boolean ignorePrefix) {
|
||||
prev = mkMatch(any(RegExpRoot r)) and
|
||||
ignorePrefix = true and
|
||||
next = prev
|
||||
}
|
||||
|
||||
// The `deltaClosed` relation specialized to the chars that exists in strings tested by a `MatchedRegExp`.
|
||||
private predicate specializedDeltaClosed(State prev, string char, State next) {
|
||||
deltaClosed(prev, specializedGetAnInputSymbolMatching(char), next)
|
||||
}
|
||||
|
||||
// The `getAnInputSymbolMatching` relation specialized to the chars that exists in strings tested by a `MatchedRegExp`.
|
||||
pragma[noinline]
|
||||
private InputSymbol specializedGetAnInputSymbolMatching(string char) {
|
||||
exists(string s, MatchedRegExp r |
|
||||
r.test(s, _)
|
||||
or
|
||||
r.testWithGroups(s, _)
|
||||
|
|
||||
char = s.charAt(_)
|
||||
) and
|
||||
result = getAnInputSymbolMatching(char)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `i`th state on a path to the accepting state when `reg` matches `str`.
|
||||
* Starts with an accepting state as found by `getAState` and searches backwards
|
||||
* to the start state through the reachable states (as found by `getAState`).
|
||||
*
|
||||
* This predicate holds the invariant that the result state can be reached with `i` steps from a start state,
|
||||
* and an accepting state can be found after (`str.length() - 1 - i`) steps from the result.
|
||||
* The result state is therefore always on a valid path where `reg` accepts `str`.
|
||||
*
|
||||
* This predicate is only used to find which capture groups a regular expression has filled,
|
||||
* and thus the search is only performed for the strings in the `testWithGroups(..)` predicate.
|
||||
*/
|
||||
private State getAStateThatReachesAccept(
|
||||
MatchedRegExp reg, int i, string str, boolean ignorePrefix
|
||||
) {
|
||||
// base case, reaches an accepting state from the last state in `getAState(..)`
|
||||
reg.testWithGroups(str, ignorePrefix) and
|
||||
i = str.length() - 1 and
|
||||
result = getAState(reg, i, str, ignorePrefix) and
|
||||
epsilonSucc*(result) = Accept(_)
|
||||
or
|
||||
// recursive case. `next` is the next state to be matched after matching `prev`.
|
||||
// this predicate is doing a backwards search, so `prev` is the result we are looking for.
|
||||
exists(State next, State prev, int fromIndex, int toIndex |
|
||||
next = getAStateThatReachesAccept(reg, toIndex, str, ignorePrefix) and
|
||||
next = getAStateAfterMatching(reg, prev, str, toIndex, fromIndex, ignorePrefix) and
|
||||
i = fromIndex and
|
||||
result = prev
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the capture group number that `term` belongs to. */
|
||||
private int group(RegExpTerm term) {
|
||||
exists(RegExpGroup grp | grp.getNumber() = result | term.getParent*() = grp)
|
||||
}
|
||||
}
|
||||
|
||||
/** A class to test whether a regular expression matches certain HTML tags. */
|
||||
class HTMLMatchingRegExp extends RegexpMatching::MatchedRegExp {
|
||||
HTMLMatchingRegExp() {
|
||||
// the regexp must mention "<" and ">" explicitly.
|
||||
forall(string angleBracket | angleBracket = ["<", ">"] |
|
||||
any(RegExpConstant term | term.getValue().matches("%" + angleBracket + "%")).getRootTerm() =
|
||||
this
|
||||
)
|
||||
}
|
||||
|
||||
override predicate testWithGroups(string str, boolean ignorePrefix) {
|
||||
ignorePrefix = true and
|
||||
str = ["<!-- foo -->", "<!-- foo --!>", "<!- foo ->", "<foo>", "<script>"]
|
||||
}
|
||||
|
||||
override predicate test(string str, boolean ignorePrefix) {
|
||||
ignorePrefix = true and
|
||||
str =
|
||||
[
|
||||
"<!-- foo -->", "<!- foo ->", "<!-- foo --!>", "<!-- foo\n -->", "<script>foo</script>",
|
||||
"<script \n>foo</script>", "<script >foo\n</script>", "<foo ></foo>", "<foo>",
|
||||
"<foo src=\"foo\"></foo>", "<script>", "<script src=\"foo\"></script>",
|
||||
"<script src='foo'></script>", "<SCRIPT>foo</SCRIPT>", "<script\tsrc=\"foo\"/>",
|
||||
"<script\tsrc='foo'></script>", "<sCrIpT>foo</ScRiPt>", "<script src=\"foo\">foo</script >",
|
||||
"<script src=\"foo\">foo</script foo=\"bar\">", "<script src=\"foo\">foo</script\t\n bar>"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `regexp` matches some HTML tags, but misses some HTML tags that it should match.
|
||||
*
|
||||
* When adding a new case to this predicate, make sure the test string used in `matches(..)` calls are present in `HTMLMatchingRegExp::test` / `HTMLMatchingRegExp::testWithGroups`.
|
||||
*/
|
||||
predicate isBadRegexpFilter(HTMLMatchingRegExp regexp, string msg) {
|
||||
// CVE-2021-33829 - matching both "<!-- foo -->" and "<!-- foo --!>", but in different capture groups
|
||||
regexp.matches("<!-- foo -->") and
|
||||
regexp.matches("<!-- foo --!>") and
|
||||
exists(int a, int b | a != b |
|
||||
regexp.fillsCaptureGroup("<!-- foo -->", a) and
|
||||
// <!-- foo --> might be ambigously parsed (matching both capture groups), and that is ok here.
|
||||
regexp.fillsCaptureGroup("<!-- foo --!>", b) and
|
||||
not regexp.fillsCaptureGroup("<!-- foo --!>", a) and
|
||||
msg =
|
||||
"Comments ending with --> are matched differently from comments ending with --!>. The first is matched with capture group "
|
||||
+ a + " and comments ending with --!> are matched with capture group " +
|
||||
strictconcat(int i | regexp.fillsCaptureGroup("<!-- foo --!>", i) | i.toString(), ", ") +
|
||||
"."
|
||||
)
|
||||
or
|
||||
// CVE-2020-17480 - matching "<!-- foo -->" and other tags, but not "<!-- foo --!>".
|
||||
exists(int group, int other |
|
||||
group != other and
|
||||
regexp.fillsCaptureGroup("<!-- foo -->", group) and
|
||||
regexp.fillsCaptureGroup("<foo>", other) and
|
||||
not regexp.matches("<!-- foo --!>") and
|
||||
not regexp.fillsCaptureGroup("<!-- foo -->", any(int i | i != group)) and
|
||||
not regexp.fillsCaptureGroup("<!- foo ->", group) and
|
||||
not regexp.fillsCaptureGroup("<foo>", group) and
|
||||
not regexp.fillsCaptureGroup("<script>", group) and
|
||||
msg =
|
||||
"This regular expression only parses --> (capture group " + group +
|
||||
") and not --!> as a HTML comment end tag."
|
||||
)
|
||||
or
|
||||
regexp.matches("<!-- foo -->") and
|
||||
not regexp.matches("<!-- foo\n -->") and
|
||||
not regexp.matches("<!- foo ->") and
|
||||
not regexp.matches("<foo>") and
|
||||
not regexp.matches("<script>") and
|
||||
msg = "This regular expression does not match comments containing newlines."
|
||||
or
|
||||
regexp.matches("<script>foo</script>") and
|
||||
regexp.matches("<script src=\"foo\"></script>") and
|
||||
not regexp.matches("<foo ></foo>") and
|
||||
(
|
||||
not regexp.matches("<script \n>foo</script>") and
|
||||
msg = "This regular expression matches <script></script>, but not <script \\n></script>"
|
||||
or
|
||||
not regexp.matches("<script >foo\n</script>") and
|
||||
msg = "This regular expression matches <script>...</script>, but not <script >...\\n</script>"
|
||||
)
|
||||
or
|
||||
regexp.matches("<script>foo</script>") and
|
||||
regexp.matches("<script src=\"foo\"></script>") and
|
||||
not regexp.matches("<script src='foo'></script>") and
|
||||
not regexp.matches("<foo>") and
|
||||
msg = "This regular expression does not match script tags where the attribute uses single-quotes."
|
||||
or
|
||||
regexp.matches("<script>foo</script>") and
|
||||
regexp.matches("<script src='foo'></script>") and
|
||||
not regexp.matches("<script src=\"foo\"></script>") and
|
||||
not regexp.matches("<foo>") and
|
||||
msg = "This regular expression does not match script tags where the attribute uses double-quotes."
|
||||
or
|
||||
regexp.matches("<script>foo</script>") and
|
||||
regexp.matches("<script src='foo'></script>") and
|
||||
not regexp.matches("<script\tsrc='foo'></script>") and
|
||||
not regexp.matches("<foo>") and
|
||||
not regexp.matches("<foo src=\"foo\"></foo>") and
|
||||
msg = "This regular expression does not match script tags where tabs are used between attributes."
|
||||
or
|
||||
regexp.matches("<script>foo</script>") and
|
||||
not RegExpFlags::isIgnoreCase(regexp) and
|
||||
not regexp.matches("<foo>") and
|
||||
not regexp.matches("<foo ></foo>") and
|
||||
(
|
||||
not regexp.matches("<SCRIPT>foo</SCRIPT>") and
|
||||
msg = "This regular expression does not match upper case <SCRIPT> tags."
|
||||
or
|
||||
not regexp.matches("<sCrIpT>foo</ScRiPt>") and
|
||||
regexp.matches("<SCRIPT>foo</SCRIPT>") and
|
||||
msg = "This regular expression does not match mixed case <sCrIpT> tags."
|
||||
)
|
||||
or
|
||||
regexp.matches("<script src=\"foo\"></script>") and
|
||||
not regexp.matches("<foo>") and
|
||||
not regexp.matches("<foo ></foo>") and
|
||||
(
|
||||
not regexp.matches("<script src=\"foo\">foo</script >") and
|
||||
msg = "This regular expression does not match script end tags like </script >."
|
||||
or
|
||||
not regexp.matches("<script src=\"foo\">foo</script foo=\"bar\">") and
|
||||
msg = "This regular expression does not match script end tags like </script foo=\"bar\">."
|
||||
or
|
||||
not regexp.matches("<script src=\"foo\">foo</script\t\n bar>") and
|
||||
msg = "This regular expression does not match script end tags like </script\\t\\n bar>."
|
||||
)
|
||||
}
|
||||
@@ -229,14 +229,8 @@ module CodeInjection {
|
||||
}
|
||||
|
||||
/**
|
||||
* A code operator of a NoSQL query as a code injection sink.
|
||||
*/
|
||||
class NoSQLCodeInjectionSink extends Sink {
|
||||
NoSQLCodeInjectionSink() { any(NoSQL::Query q).getACodeOperator() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
* The first argument to `Module.prototype._compile`, considered as a code-injection sink.
|
||||
* The first argument to `Module.prototype._compile` from the Node.js built-in module `module`,
|
||||
* considered as a code-injection sink.
|
||||
*/
|
||||
class ModuleCompileSink extends Sink {
|
||||
ModuleCompileSink() {
|
||||
|
||||
@@ -55,7 +55,7 @@ module TaintedPath {
|
||||
* There are currently four flow labels, representing the different combinations of
|
||||
* normalization and absoluteness.
|
||||
*/
|
||||
abstract class PosixPath extends DataFlow::FlowLabel {
|
||||
class PosixPath extends DataFlow::FlowLabel {
|
||||
Normalization normalization;
|
||||
Relativeness relativeness;
|
||||
|
||||
@@ -113,7 +113,7 @@ module TaintedPath {
|
||||
/**
|
||||
* A flow label representing an array of path elements that may include "..".
|
||||
*/
|
||||
abstract class SplitPath extends DataFlow::FlowLabel {
|
||||
class SplitPath extends DataFlow::FlowLabel {
|
||||
SplitPath() { this = "splitPath" }
|
||||
}
|
||||
}
|
||||
@@ -218,12 +218,12 @@ module TaintedPath {
|
||||
output = this
|
||||
or
|
||||
// non-global replace or replace of something other than /\.\./g, /[/]/g, or /[\.]/g.
|
||||
this instanceof StringReplaceCall and
|
||||
this.getCalleeName() = "replace" and
|
||||
input = getReceiver() and
|
||||
output = this and
|
||||
not exists(RegExpLiteral literal, RegExpTerm term |
|
||||
this.(StringReplaceCall).getRegExp().asExpr() = literal and
|
||||
this.(StringReplaceCall).isGlobal() and
|
||||
getArgument(0).getALocalSource().asExpr() = literal and
|
||||
literal.isGlobal() and
|
||||
literal.getRoot() = term
|
||||
|
|
||||
term.getAMatchedString() = "/" or
|
||||
@@ -247,15 +247,16 @@ module TaintedPath {
|
||||
/**
|
||||
* A call that removes all instances of "../" in the prefix of the string.
|
||||
*/
|
||||
class DotDotSlashPrefixRemovingReplace extends StringReplaceCall {
|
||||
class DotDotSlashPrefixRemovingReplace extends DataFlow::CallNode {
|
||||
DataFlow::Node input;
|
||||
DataFlow::Node output;
|
||||
|
||||
DotDotSlashPrefixRemovingReplace() {
|
||||
this.getCalleeName() = "replace" and
|
||||
input = getReceiver() and
|
||||
output = this and
|
||||
exists(RegExpLiteral literal, RegExpTerm term |
|
||||
getRegExp().asExpr() = literal and
|
||||
getArgument(0).getALocalSource().asExpr() = literal and
|
||||
(term instanceof RegExpStar or term instanceof RegExpPlus) and
|
||||
term.getChild(0) = getADotDotSlashMatcher()
|
||||
|
|
||||
@@ -297,16 +298,17 @@ module TaintedPath {
|
||||
/**
|
||||
* A call that removes all "." or ".." from a path, without also removing all forward slashes.
|
||||
*/
|
||||
class DotRemovingReplaceCall extends StringReplaceCall {
|
||||
class DotRemovingReplaceCall extends DataFlow::CallNode {
|
||||
DataFlow::Node input;
|
||||
DataFlow::Node output;
|
||||
|
||||
DotRemovingReplaceCall() {
|
||||
this.getCalleeName() = "replace" and
|
||||
input = getReceiver() and
|
||||
output = this and
|
||||
isGlobal() and
|
||||
exists(RegExpLiteral literal, RegExpTerm term |
|
||||
getRegExp().asExpr() = literal and
|
||||
getArgument(0).getALocalSource().asExpr() = literal and
|
||||
literal.isGlobal() and
|
||||
literal.getRoot() = term and
|
||||
not term.getAMatchedString() = "/"
|
||||
|
|
||||
@@ -606,8 +608,6 @@ module TaintedPath {
|
||||
(
|
||||
this = fileSystemAccess.getAPathArgument() and
|
||||
not exists(fileSystemAccess.getRootPathArgument())
|
||||
or
|
||||
this = fileSystemAccess.getRootPathArgument()
|
||||
) and
|
||||
not this = any(ResolvingPathCall call).getInput()
|
||||
}
|
||||
@@ -648,74 +648,6 @@ module TaintedPath {
|
||||
AngularJSTemplateUrlSink() { this = any(AngularJS::CustomDirective d).getMember("templateUrl") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The path argument of a [send](https://www.npmjs.com/package/send) call, viewed as a sink.
|
||||
*/
|
||||
class SendPathSink extends Sink, DataFlow::ValueNode {
|
||||
SendPathSink() { this = DataFlow::moduleImport("send").getACall().getArgument(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A path argument given to a `Page` in puppeteer, specifying where a pdf/screenshot should be saved.
|
||||
*/
|
||||
private class PuppeteerPath extends TaintedPath::Sink {
|
||||
PuppeteerPath() {
|
||||
this =
|
||||
Puppeteer::page()
|
||||
.getMember(["pdf", "screenshot"])
|
||||
.getParameter(0)
|
||||
.getMember("path")
|
||||
.getARhs()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument given to the `prettier` library specifying the location of a config file.
|
||||
*/
|
||||
private class PrettierFileSink extends TaintedPath::Sink {
|
||||
PrettierFileSink() {
|
||||
this =
|
||||
API::moduleImport("prettier")
|
||||
.getMember(["resolveConfig", "resolveConfigFile", "getFileInfo"])
|
||||
.getACall()
|
||||
.getArgument(0)
|
||||
or
|
||||
this =
|
||||
API::moduleImport("prettier")
|
||||
.getMember("resolveConfig")
|
||||
.getACall()
|
||||
.getParameter(1)
|
||||
.getMember("config")
|
||||
.getARhs()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `cwd` option for the `read-pkg` library.
|
||||
*/
|
||||
private class ReadPkgCwdSink extends TaintedPath::Sink {
|
||||
ReadPkgCwdSink() {
|
||||
this =
|
||||
API::moduleImport("read-pkg")
|
||||
.getMember(["readPackageAsync", "readPackageSync"])
|
||||
.getParameter(0)
|
||||
.getMember("cwd")
|
||||
.getARhs()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `cwd` option to a shell execution.
|
||||
*/
|
||||
private class ShellCwdSink extends TaintedPath::Sink {
|
||||
ShellCwdSink() {
|
||||
exists(SystemCommandExecution sys, API::Node opts |
|
||||
opts.getARhs() = sys.getOptionsArg() and // assuming that an API::Node exists here.
|
||||
this = opts.getMember("cwd").getARhs()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a step `src -> dst` mapping `srclabel` to `dstlabel` relevant for path traversal vulnerabilities.
|
||||
*/
|
||||
|
||||
@@ -183,17 +183,6 @@ module DomBasedXss {
|
||||
this = any(Typeahead::TypeaheadSuggestionFunction f).getAReturn()
|
||||
or
|
||||
this = any(Handlebars::SafeString s).getAnArgument()
|
||||
or
|
||||
this = any(JQuery::MethodCall call | call.getMethodName() = "jGrowl").getArgument(0)
|
||||
or
|
||||
// A construction of a JSDOM object (server side DOM), where scripts are allowed.
|
||||
exists(DataFlow::NewNode instance |
|
||||
instance = API::moduleImport("jsdom").getMember("JSDOM").getInstance().getAnImmediateUse() and
|
||||
this = instance.getArgument(0) and
|
||||
instance.getOptionArgument(1, "runScripts").mayHaveStringValue("dangerously")
|
||||
)
|
||||
or
|
||||
MooTools::interpretsNodeAsHtml(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -542,7 +542,7 @@ private State before(RegExpTerm t) { result = Match(t, 0) }
|
||||
/**
|
||||
* Gets a state the NFA may be in after matching `t`.
|
||||
*/
|
||||
private State after(RegExpTerm t) {
|
||||
State after(RegExpTerm t) {
|
||||
exists(RegExpAlt alt | t = alt.getAChild() | result = after(alt))
|
||||
or
|
||||
exists(RegExpSequence seq, int i | t = seq.getChild(i) |
|
||||
@@ -671,7 +671,7 @@ RegExpRoot getRoot(RegExpTerm term) {
|
||||
/**
|
||||
* A state in the NFA.
|
||||
*/
|
||||
private newtype TState =
|
||||
newtype TState =
|
||||
/**
|
||||
* A state representing that the NFA is about to match a term.
|
||||
* `i` is used to index into multi-char literals.
|
||||
@@ -801,29 +801,26 @@ InputSymbol getAnInputSymbolMatching(string char) {
|
||||
result = Any()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `state` is a start state.
|
||||
*/
|
||||
predicate isStartState(State state) {
|
||||
state = mkMatch(any(RegExpRoot r))
|
||||
or
|
||||
exists(RegExpCaret car | state = after(car))
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicates for constructing a prefix string that leads to a given state.
|
||||
*/
|
||||
private module PrefixConstruction {
|
||||
/**
|
||||
* Holds if `state` starts the string matched by the regular expression.
|
||||
*/
|
||||
private predicate isStartState(State state) {
|
||||
state instanceof StateInPumpableRegexp and
|
||||
(
|
||||
state = Match(any(RegExpRoot r), _)
|
||||
or
|
||||
exists(RegExpCaret car | state = after(car))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `state` is the textually last start state for the regular expression.
|
||||
*/
|
||||
private predicate lastStartState(State state) {
|
||||
exists(RegExpRoot root |
|
||||
state =
|
||||
max(State s, Location l |
|
||||
max(StateInPumpableRegexp s, Location l |
|
||||
isStartState(s) and getRoot(s.getRepr()) = root and l = s.getRepr().getLocation()
|
||||
|
|
||||
s
|
||||
|
||||
@@ -20,12 +20,7 @@ module RegExpFlags {
|
||||
/**
|
||||
* Holds if `root` has the `i` flag for case-insensitive matching.
|
||||
*/
|
||||
predicate isIgnoreCase(RegExpTerm root) {
|
||||
root.isRootTerm() and
|
||||
exists(DataFlow::RegExpCreationNode node | node.getRoot() = root |
|
||||
RegExp::isIgnoreCase(node.getFlags())
|
||||
)
|
||||
}
|
||||
predicate isIgnoreCase(RegExpTerm root) { RegExp::isIgnoreCase(getFlags(root)) }
|
||||
|
||||
/**
|
||||
* Gets the flags for `root`, or the empty string if `root` has no flags.
|
||||
@@ -38,15 +33,14 @@ module RegExpFlags {
|
||||
not exists(node.getFlags()) and
|
||||
result = ""
|
||||
)
|
||||
or
|
||||
exists(RegExpPatternSource source | source.getRegExpTerm() = root |
|
||||
result = source.getARegExpObject().(DataFlow::RegExpCreationNode).getFlags()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `root` has the `s` flag for multi-line matching.
|
||||
*/
|
||||
predicate isDotAll(RegExpTerm root) {
|
||||
root.isRootTerm() and
|
||||
exists(DataFlow::RegExpCreationNode node | node.getRoot() = root |
|
||||
RegExp::isDotAll(node.getFlags())
|
||||
)
|
||||
}
|
||||
predicate isDotAll(RegExpTerm root) { RegExp::isDotAll(getFlags(root)) }
|
||||
}
|
||||
|
||||
54
javascript/ql/src/Security/CWE-116/BadTagFilter.qhelp
Normal file
54
javascript/ql/src/Security/CWE-116/BadTagFilter.qhelp
Normal file
@@ -0,0 +1,54 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
It is possible to match some single HTML tags using regular expressions (parsing general HTML using
|
||||
regular expressions is impossible). However, if the regular expression is not written well it might
|
||||
be possible to circumvent it, which can lead to cross-site scripting or other security issues.
|
||||
</p>
|
||||
<p>
|
||||
Some of these mistakes are caused by browsers having very forgiving HTML parsers, and
|
||||
will often render invalid HTML containing syntax errors.
|
||||
Regular expressions that attempt to match HTML should also recognize tags containing such syntax errors.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Use a well-tested sanitization or parser library if at all possible. These libraries are much more
|
||||
likely to handle corner cases correctly than a custom implementation.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The following example attempts to filters out all <code><script></code> tags.
|
||||
</p>
|
||||
|
||||
<sample src="examples/BadTagFilter.js" />
|
||||
|
||||
<p>
|
||||
The above sanitizer does not filter out all <code><script></code> tags.
|
||||
Browsers will not only accept <code></script></code> as script end tags, but also tags such as <code></script foo="bar"></code> even though it is a parser error.
|
||||
This means that an attack string such as <code><script>alert(1)</script foo="bar"></code> will not be filtered by
|
||||
the function, and <code>alert(1)</code> will be executed by a browser if the string is rendered as HTML.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Other corner cases include that HTML comments can end with <code>--!></code>,
|
||||
and that HTML tag names can contain upper case characters.
|
||||
</p>
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>Securitum: <a href="https://research.securitum.com/the-curious-case-of-copy-paste/">The Curious Case of Copy & Paste</a>.</li>
|
||||
<li>stackoverflow.com: <a href="https://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags#answer-1732454">You can't parse [X]HTML with regex</a>.</li>
|
||||
<li>HTML Standard: <a href="https://html.spec.whatwg.org/multipage/parsing.html#comment-end-bang-state">Comment end bang state</a>.</li>
|
||||
<li>stackoverflow.com: <a href="https://stackoverflow.com/questions/25559999/why-arent-browsers-strict-about-html">Why aren't browsers strict about HTML?</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
|
||||
19
javascript/ql/src/Security/CWE-116/BadTagFilter.ql
Normal file
19
javascript/ql/src/Security/CWE-116/BadTagFilter.ql
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @name Bad HTML filtering regexp
|
||||
* @description Matching HTML tags using regular expressions is hard to do right, and can easily lead to security issues.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 7.8
|
||||
* @precision high
|
||||
* @id js/bad-tag-filter
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-116
|
||||
* external/cwe/cwe-020
|
||||
*/
|
||||
|
||||
import semmle.javascript.security.BadTagFilterQuery
|
||||
|
||||
from HTMLMatchingRegExp regexp, string msg
|
||||
where msg = min(string m | isBadRegexpFilter(regexp, m) | m order by m.length(), m) // there might be multiple, we arbitrarily pick the shortest one
|
||||
select regexp, msg
|
||||
@@ -0,0 +1,8 @@
|
||||
function filterScript(html) {
|
||||
var scriptRegex = /<script\b[^>]*>([\s\S]*?)<\/script>/gi;
|
||||
var match;
|
||||
while ((match = scriptRegex.exec(html)) !== null) {
|
||||
html = html.replace(match[0], match[1]);
|
||||
}
|
||||
return html;
|
||||
}
|
||||
@@ -28,6 +28,7 @@
|
||||
| jsonschema.js:15:23:15:29 | (a?a?)* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding (a?a?)*b |
|
||||
| jsonschema.js:20:18:20:24 | (a?a?)* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding (a?a?)*b |
|
||||
| lib/closure.js:4:6:4:7 | u* | Strings with many repetitions of 'u' can start matching anywhere after the start of the preceeding u*o |
|
||||
| lib/indirect.js:2:6:2:7 | k* | Strings with many repetitions of 'k' can start matching anywhere after the start of the preceeding k*h |
|
||||
| lib/lib.js:1:15:1:16 | a* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding a*b |
|
||||
| lib/lib.js:8:3:8:4 | f* | Strings with many repetitions of 'f' can start matching anywhere after the start of the preceeding f*g |
|
||||
| lib/moduleLib/moduleLib.js:2:3:2:4 | a* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding a*b |
|
||||
|
||||
@@ -3,6 +3,10 @@ nodes
|
||||
| lib/closure.js:3:21:3:21 | x |
|
||||
| lib/closure.js:4:16:4:16 | x |
|
||||
| lib/closure.js:4:16:4:16 | x |
|
||||
| lib/indirect.js:1:32:1:32 | x |
|
||||
| lib/indirect.js:1:32:1:32 | x |
|
||||
| lib/indirect.js:2:16:2:16 | x |
|
||||
| lib/indirect.js:2:16:2:16 | x |
|
||||
| lib/lib.js:3:28:3:31 | name |
|
||||
| lib/lib.js:3:28:3:31 | name |
|
||||
| lib/lib.js:4:14:4:17 | name |
|
||||
@@ -170,6 +174,10 @@ edges
|
||||
| lib/closure.js:3:21:3:21 | x | lib/closure.js:4:16:4:16 | x |
|
||||
| lib/closure.js:3:21:3:21 | x | lib/closure.js:4:16:4:16 | x |
|
||||
| lib/closure.js:3:21:3:21 | x | lib/closure.js:4:16:4:16 | x |
|
||||
| lib/indirect.js:1:32:1:32 | x | lib/indirect.js:2:16:2:16 | x |
|
||||
| lib/indirect.js:1:32:1:32 | x | lib/indirect.js:2:16:2:16 | x |
|
||||
| lib/indirect.js:1:32:1:32 | x | lib/indirect.js:2:16:2:16 | x |
|
||||
| lib/indirect.js:1:32:1:32 | x | lib/indirect.js:2:16:2:16 | x |
|
||||
| lib/lib.js:3:28:3:31 | name | lib/lib.js:4:14:4:17 | name |
|
||||
| lib/lib.js:3:28:3:31 | name | lib/lib.js:4:14:4:17 | name |
|
||||
| lib/lib.js:3:28:3:31 | name | lib/lib.js:4:14:4:17 | name |
|
||||
@@ -329,6 +337,7 @@ edges
|
||||
| polynomial-redos.js:123:13:123:20 | replaced | polynomial-redos.js:123:3:123:20 | result |
|
||||
#select
|
||||
| lib/closure.js:4:5:4:17 | /u*o/.test(x) | lib/closure.js:3:21:3:21 | x | lib/closure.js:4:16:4:16 | x | This $@ that depends on $@ may run slow on strings with many repetitions of 'u'. | lib/closure.js:4:6:4:7 | u* | regular expression | lib/closure.js:3:21:3:21 | x | library input |
|
||||
| lib/indirect.js:2:5:2:17 | /k*h/.test(x) | lib/indirect.js:1:32:1:32 | x | lib/indirect.js:2:16:2:16 | x | This $@ that depends on $@ may run slow on strings with many repetitions of 'k'. | lib/indirect.js:2:6:2:7 | k* | regular expression | lib/indirect.js:1:32:1:32 | x | library input |
|
||||
| lib/lib.js:4:2:4:18 | regexp.test(name) | lib/lib.js:3:28:3:31 | name | lib/lib.js:4:14:4:17 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'a'. | lib/lib.js:1:15:1:16 | a* | regular expression | lib/lib.js:3:28:3:31 | name | library input |
|
||||
| lib/lib.js:8:2:8:17 | /f*g/.test(name) | lib/lib.js:7:19:7:22 | name | lib/lib.js:8:13:8:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'f'. | lib/lib.js:8:3:8:4 | f* | regular expression | lib/lib.js:7:19:7:22 | name | library input |
|
||||
| lib/moduleLib/moduleLib.js:2:2:2:17 | /a*b/.test(name) | lib/moduleLib/moduleLib.js:1:28:1:31 | name | lib/moduleLib/moduleLib.js:2:13:2:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'a'. | lib/moduleLib/moduleLib.js:2:3:2:4 | a* | regular expression | lib/moduleLib/moduleLib.js:1:28:1:31 | name | library input |
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports.foo = function (x) {
|
||||
/k*h/.test(x); // NOT OK
|
||||
}
|
||||
@@ -12,4 +12,8 @@ if (typeof define !== 'undefined' && define.amd) { // AMD
|
||||
define([], function () {return bar});
|
||||
}
|
||||
|
||||
module.exports.closure = require("./closure")
|
||||
module.exports.closure = require("./closure")
|
||||
|
||||
module.exports.func = function (conf) {
|
||||
return require("./indirect")
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
| tst.js:2:6:2:29 | <script.*?>.*?<\\/script> | This regular expression does not match script end tags like </script >. |
|
||||
| tst.js:3:6:3:29 | <script.*?>.*?<\\/script> | This regular expression does not match script end tags like </script >. |
|
||||
| tst.js:7:6:7:16 | <!--.*--!?> | This regular expression does not match comments containing newlines. |
|
||||
| tst.js:8:6:8:39 | <script.*?>(.\|\\s)*?<\\/script[^>]*> | This regular expression matches <script></script>, but not <script \\n></script> |
|
||||
| tst.js:9:6:9:37 | <script[^>]*?>.*?<\\/script[^>]*> | This regular expression matches <script>...</script>, but not <script >...\\n</script> |
|
||||
| tst.js:10:6:10:44 | <script(\\s\|\\w\|=\|")*?>.*?<\\/script[^>]*> | This regular expression does not match script tags where the attribute uses single-quotes. |
|
||||
| tst.js:11:6:11:44 | <script(\\s\|\\w\|=\|')*?>.*?<\\/script[^>]*> | This regular expression does not match script tags where the attribute uses double-quotes. |
|
||||
| tst.js:12:6:12:48 | <script( \|\\n\|\\w\|=\|'\|")*?>.*?<\\/script[^>]*> | This regular expression does not match script tags where tabs are used between attributes. |
|
||||
| tst.js:13:6:13:34 | <script.*?>.*?<\\/script[^>]*> | This regular expression does not match upper case <SCRIPT> tags. |
|
||||
| tst.js:14:6:14:52 | <(script\|SCRIPT).*?>.*?<\\/(script\|SCRIPT)[^>]*> | This regular expression does not match mixed case <sCrIpT> tags. |
|
||||
| tst.js:15:6:15:39 | <script[^>]*?>[\\s\\S]*?<\\/script.*> | This regular expression does not match script end tags like </script\\t\\n bar>. |
|
||||
| tst.js:17:6:17:40 | <script\\b[^>]*>([\\s\\S]*?)<\\/script> | This regular expression does not match script end tags like </script >. |
|
||||
| tst.js:18:6:18:48 | <(?:!--([\\S\|\\s]*?)-->)\|([^\\/\\s>]+)[\\S\\s]*?> | Comments ending with --> are matched differently from comments ending with --!>. The first is matched with capture group 1 and comments ending with --!> are matched with capture group 2. |
|
||||
| tst.js:19:6:19:147 | <(?:(?:\\/([^>]+)>)\|(?:!--([\\S\|\\s]*?)-->)\|(?:([^\\/\\s>]+)((?:\\s+[\\w\\-:.]+(?:\\s*=\\s*?(?:(?:"[^"]*")\|(?:'[^']*')\|[^\\s"'\\/>]+))?)*)[\\S\\s]*?(\\/?)>)) | Comments ending with --> are matched differently from comments ending with --!>. The first is matched with capture group 2 and comments ending with --!> are matched with capture group 3, 4. |
|
||||
| tst.js:20:3:20:57 | (<[a-z\\/!$]("[^"]*"\|'[^']*'\|[^'">])*>\|<!(--.*?--\\s*)+>) | Comments ending with --> are matched differently from comments ending with --!>. The first is matched with capture group 3 and comments ending with --!> are matched with capture group 1. |
|
||||
| tst.js:21:6:21:249 | <(?:(?:!--([\\w\\W]*?)-->)\|(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)\|(?:!DOCTYPE([\\w\\W]*?)>)\|(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)\|(?:\\/([A-Za-z][A-Za-z0-9\\-_\\:\\.]*)>)\|(?:([A-Za-z][A-Za-z0-9\\-_\\:\\.]*)((?:\\s+[^"'>]+(?:(?:"[^"]*")\|(?:'[^']*')\|[^>]*))*\|\\/\|\\s+)>)) | This regular expression only parses --> (capture group 1) and not --!> as a HTML comment end tag. |
|
||||
| tst.js:22:6:22:33 | <!--([\\w\\W]*?)-->\|<([^>]*?)> | Comments ending with --> are matched differently from comments ending with --!>. The first is matched with capture group 1 and comments ending with --!> are matched with capture group 2. |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE-116/BadTagFilter.ql
|
||||
@@ -0,0 +1,28 @@
|
||||
var filters = [
|
||||
/<script.*?>.*?<\/script>/i, // NOT OK - doesn't match newlines or `</script >`
|
||||
/<script.*?>.*?<\/script>/is, // NOT OK - doesn't match `</script >`
|
||||
/<script.*?>.*?<\/script[^>]*>/is, // OK
|
||||
/<!--.*-->/is, // OK - we don't care regexps that only match comments
|
||||
/<!--.*--!?>/is, // OK
|
||||
/<!--.*--!?>/i, // NOT OK, does not match newlines
|
||||
/<script.*?>(.|\s)*?<\/script[^>]*>/i, // NOT OK - doesn't match inside the script tag
|
||||
/<script[^>]*?>.*?<\/script[^>]*>/i, // NOT OK - doesn't match newlines inside the content
|
||||
/<script(\s|\w|=|")*?>.*?<\/script[^>]*>/is, // NOT OK - does not match single quotes for attribute values
|
||||
/<script(\s|\w|=|')*?>.*?<\/script[^>]*>/is, // NOT OK - does not match double quotes for attribute values
|
||||
/<script( |\n|\w|=|'|")*?>.*?<\/script[^>]*>/is, // NOT OK - does not match tabs between attributes
|
||||
/<script.*?>.*?<\/script[^>]*>/s, // NOT OK - does not match uppercase SCRIPT tags
|
||||
/<(script|SCRIPT).*?>.*?<\/(script|SCRIPT)[^>]*>/s, // NOT OK - does not match mixed case script tags
|
||||
/<script[^>]*?>[\s\S]*?<\/script.*>/i, // NOT OK - doesn't match newlines in the end tag
|
||||
/<script[^>]*?>[\s\S]*?<\/script[^>]*?>/i, // OK
|
||||
/<script\b[^>]*>([\s\S]*?)<\/script>/gi, // NOT OK - too strict matching on the end tag
|
||||
/<(?:!--([\S|\s]*?)-->)|([^\/\s>]+)[\S\s]*?>/, // NOT OK - doesn't match comments with the right capture groups
|
||||
/<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\/\s>]+)((?:\s+[\w\-:.]+(?:\s*=\s*?(?:(?:"[^"]*")|(?:'[^']*')|[^\s"'\/>]+))?)*)[\S\s]*?(\/?)>))/, // NOT OK - capture groups
|
||||
/(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi, // NOT OK - capture groups
|
||||
/<(?:(?:!--([\w\W]*?)-->)|(?:!\[CDATA\[([\w\W]*?)\]\]>)|(?:!DOCTYPE([\w\W]*?)>)|(?:\?([^\s\/<>]+) ?([\w\W]*?)[?/]>)|(?:\/([A-Za-z][A-Za-z0-9\-_\:\.]*)>)|(?:([A-Za-z][A-Za-z0-9\-_\:\.]*)((?:\s+[^"'>]+(?:(?:"[^"]*")|(?:'[^']*')|[^>]*))*|\/|\s+)>))/g, // NOT OK - capture groups
|
||||
/<!--([\w\W]*?)-->|<([^>]*?)>/g, // NOT OK - capture groups
|
||||
]
|
||||
|
||||
doFilters(filters)
|
||||
|
||||
var strip = '<script([^>]*)>([\\S\\s]*?)<\/script([^>]*)>'; // OK - it's used with the ignorecase flag
|
||||
new RegExp(strip, 'gi');
|
||||
4
python/change-notes/2021-08-26-bad-tag-filter.md
Normal file
4
python/change-notes/2021-08-26-bad-tag-filter.md
Normal file
@@ -0,0 +1,4 @@
|
||||
lgtm,codescanning
|
||||
* A new query, `py/bad-tag-filter`, has been added to the query suite,
|
||||
highlighting regular expressions that only match a subset of the HTML tags
|
||||
it is supposed to match.
|
||||
@@ -467,9 +467,10 @@ class RegExpEscape extends RegExpNormalChar {
|
||||
or
|
||||
this.getUnescaped() = "t" and result = "\t"
|
||||
or
|
||||
// TODO: Find a way to include a formfeed character
|
||||
// this.getUnescaped() = "f" and result = ""
|
||||
// or
|
||||
this.getUnescaped() = "f" and result = 12.toUnicode()
|
||||
or
|
||||
this.getUnescaped() = "v" and result = 11.toUnicode()
|
||||
or
|
||||
this.isUnicode() and
|
||||
result = this.getUnicode()
|
||||
}
|
||||
@@ -480,7 +481,7 @@ class RegExpEscape extends RegExpNormalChar {
|
||||
override string getPrimaryQLClass() { result = "RegExpEscape" }
|
||||
|
||||
/** Gets the part of the term following the escape character. That is e.g. "w" if the term is "\w". */
|
||||
private string getUnescaped() { result = this.getText().suffix(1) }
|
||||
string getUnescaped() { result = this.getText().suffix(1) }
|
||||
|
||||
/**
|
||||
* Gets the text for this escape. That is e.g. "\w".
|
||||
@@ -535,6 +536,13 @@ private int toHex(string hex) {
|
||||
result = 15 and hex = ["f", "F"]
|
||||
}
|
||||
|
||||
/**
|
||||
* A word boundary, that is, a regular expression term of the form `\b`.
|
||||
*/
|
||||
class RegExpWordBoundary extends RegExpEscape {
|
||||
RegExpWordBoundary() { this.getUnescaped() = "b" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A character class escape in a regular expression.
|
||||
* That is, an escaped charachter that denotes multiple characters.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user