mirror of
https://github.com/github/codeql.git
synced 2026-05-01 19:55:15 +02:00
Merge branch 'main' into use-range-analysis-in-buffer-write
This commit is contained in:
2
cpp/change-notes/2021-11-09-use-of-http.md
Normal file
2
cpp/change-notes/2021-11-09-use-of-http.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* A new query `cpp/non-https-url` has been added for C/C++. The query flags uses of `http` URLs that might be better replaced with `https`.
|
||||
9
cpp/ql/src/Security/CWE/CWE-319/UseOfHttp.cpp
Normal file
9
cpp/ql/src/Security/CWE/CWE-319/UseOfHttp.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
void openUrl(char *url)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
openUrl("http://example.com"); // BAD
|
||||
|
||||
openUrl("https://example.com"); // GOOD: Opening a connection to a URL using HTTPS enforces SSL.
|
||||
35
cpp/ql/src/Security/CWE/CWE-319/UseOfHttp.qhelp
Normal file
35
cpp/ql/src/Security/CWE/CWE-319/UseOfHttp.qhelp
Normal file
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
|
||||
<p>Constructing URLs with the HTTP protocol can lead to unsecured connections.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>When you construct a URL, ensure that you use an HTTPS URL rather than an HTTP URL. Then, any connections that are made using that URL are secure SSL connections.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>The following example shows two ways of opening a connection using a URL. When the connection is
|
||||
opened using an HTTP URL rather than an HTTPS URL, the connection is unsecured. When the connection is opened using an HTTPS URL, the connection is a secure SSL connection.</p>
|
||||
|
||||
<sample src="UseOfHttp.cpp" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
OWASP:
|
||||
<a href="https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html">Transport Layer Protection Cheat Sheet</a>.
|
||||
</li>
|
||||
<li>
|
||||
OWASP Top 10:
|
||||
<a href="https://owasp.org/Top10/A08_2021-Software_and_Data_Integrity_Failures/">A08:2021 - Software and Data Integrity Failures</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
85
cpp/ql/src/Security/CWE/CWE-319/UseOfHttp.ql
Normal file
85
cpp/ql/src/Security/CWE/CWE-319/UseOfHttp.ql
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* @name Failure to use HTTPS URLs
|
||||
* @description Non-HTTPS connections can be intercepted by third parties.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @id cpp/non-https-url
|
||||
* @tags security
|
||||
* external/cwe/cwe-319
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* A string matching private host names of IPv4 and IPv6, which only matches
|
||||
* the host portion therefore checking for port is not necessary.
|
||||
* Several examples are localhost, reserved IPv4 IP addresses including
|
||||
* 127.0.0.1, 10.x.x.x, 172.16.x,x, 192.168.x,x, and reserved IPv6 addresses
|
||||
* including [0:0:0:0:0:0:0:1] and [::1]
|
||||
*/
|
||||
class PrivateHostName extends string {
|
||||
bindingset[this]
|
||||
PrivateHostName() {
|
||||
this.regexpMatch("(?i)localhost(?:[:/?#].*)?|127\\.0\\.0\\.1(?:[:/?#].*)?|10(?:\\.[0-9]+){3}(?:[:/?#].*)?|172\\.16(?:\\.[0-9]+){2}(?:[:/?#].*)?|192.168(?:\\.[0-9]+){2}(?:[:/?#].*)?|\\[?0:0:0:0:0:0:0:1\\]?(?:[:/?#].*)?|\\[?::1\\]?(?:[:/?#].*)?")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A string containing an HTTP URL not in a private domain.
|
||||
*/
|
||||
class HttpStringLiteral extends StringLiteral {
|
||||
HttpStringLiteral() {
|
||||
exists(string s | this.getValue() = s |
|
||||
s = "http"
|
||||
or
|
||||
exists(string tail |
|
||||
tail = s.regexpCapture("http://(.*)", 1) and not tail instanceof PrivateHostName
|
||||
) and
|
||||
not TaintTracking::localExprTaint(any(StringLiteral p |
|
||||
p.getValue() instanceof PrivateHostName
|
||||
), this.getParent*())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint tracking configuration for HTTP connections.
|
||||
*/
|
||||
class HttpStringToUrlOpenConfig extends TaintTracking::Configuration {
|
||||
HttpStringToUrlOpenConfig() { this = "HttpStringToUrlOpenConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) {
|
||||
// Sources are strings containing an HTTP URL not in a private domain.
|
||||
src.asExpr() instanceof HttpStringLiteral
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
// Sinks can be anything that demonstrates the string is likely to be
|
||||
// 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
|
||||
sink.asExpr() = fc.getArgument(0)
|
||||
or
|
||||
fc.getTarget().hasGlobalOrStdName(["send", "URLDownloadToFile", "URLDownloadToCacheFile"]) and
|
||||
sink.asExpr() = fc.getArgument(1)
|
||||
or
|
||||
fc.getTarget().hasGlobalOrStdName(["curl_easy_setopt", "getnameinfo"]) and
|
||||
sink.asExpr() = fc.getArgument(2)
|
||||
or
|
||||
fc.getTarget().hasGlobalOrStdName(["ShellExecute", "ShellExecuteA", "ShellExecuteW"]) and
|
||||
sink.asExpr() = fc.getArgument(3)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from
|
||||
HttpStringToUrlOpenConfig config, DataFlow::PathNode source, DataFlow::PathNode sink,
|
||||
HttpStringLiteral str
|
||||
where
|
||||
config.hasFlowPath(source, sink) and
|
||||
str = source.getNode().asExpr()
|
||||
select str, source, sink, "A URL may be constructed with the HTTP protocol."
|
||||
@@ -0,0 +1,14 @@
|
||||
...
|
||||
fp = fopen("/tmp/name.tmp","w"); // BAD
|
||||
...
|
||||
char filename = tmpnam(NULL);
|
||||
fp = fopen(filename,"w"); // BAD
|
||||
...
|
||||
|
||||
strcat (filename, "/tmp/name.XXXXXX");
|
||||
fd = mkstemp(filename);
|
||||
if ( fd < 0 ) {
|
||||
return error;
|
||||
}
|
||||
fp = fdopen(fd,"w") // GOOD
|
||||
...
|
||||
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Working with a file, without checking its existence and its rights, as well as working with names that can be predicted, may not be safe. Requires the attention of developers.</p>
|
||||
|
||||
</overview>
|
||||
|
||||
<example>
|
||||
<p>The following example demonstrates erroneous and corrected work with file.</p>
|
||||
<sample src="InsecureTemporaryFile.cpp" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/CON33-C.+Avoid+race+conditions+when+using+library+functions">CON33-C. Avoid race conditions when using library functions</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* @name Insecure generation of filenames.
|
||||
* @description Using a predictable filename when creating a temporary file can lead to an attacker-controlled input.
|
||||
* @kind problem
|
||||
* @id cpp/insecure-generation-of-filename
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-377
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
/** Holds for a function `f` that has an argument at index `apos` used to read the file. */
|
||||
predicate numberArgumentRead(Function f, int apos) {
|
||||
f.hasGlobalOrStdName("fgets") and apos = 2
|
||||
or
|
||||
f.hasGlobalOrStdName("fread") and apos = 3
|
||||
or
|
||||
f.hasGlobalOrStdName("read") and apos = 0
|
||||
or
|
||||
f.hasGlobalOrStdName("fscanf") and apos = 0
|
||||
}
|
||||
|
||||
/** Holds for a function `f` that has an argument at index `apos` used to write to file */
|
||||
predicate numberArgumentWrite(Function f, int apos) {
|
||||
f.hasGlobalOrStdName("fprintf") and apos = 0
|
||||
or
|
||||
f.hasGlobalOrStdName("fputs") and apos = 1
|
||||
or
|
||||
f.hasGlobalOrStdName("write") and apos = 0
|
||||
or
|
||||
f.hasGlobalOrStdName("fwrite") and apos = 3
|
||||
or
|
||||
f.hasGlobalOrStdName("fflush") and apos = 0
|
||||
}
|
||||
|
||||
from FunctionCall fc, string msg
|
||||
where
|
||||
// search for functions for generating a name, without a guarantee of the absence of a file during the period of work with it.
|
||||
(
|
||||
fc.getTarget().hasGlobalOrStdName("tmpnam") or
|
||||
fc.getTarget().hasGlobalOrStdName("tmpnam_s") or
|
||||
fc.getTarget().hasGlobalOrStdName("tmpnam_r")
|
||||
) and
|
||||
not exists(FunctionCall fctmp |
|
||||
(
|
||||
fctmp.getTarget().hasGlobalOrStdName("mktemp") or
|
||||
fctmp.getTarget().hasGlobalOrStdName("mkstemp") or
|
||||
fctmp.getTarget().hasGlobalOrStdName("mkstemps") or
|
||||
fctmp.getTarget().hasGlobalOrStdName("mkdtemp")
|
||||
) and
|
||||
(
|
||||
fc.getBasicBlock().getASuccessor*() = fctmp.getBasicBlock() or
|
||||
fctmp.getBasicBlock().getASuccessor*() = fc.getBasicBlock()
|
||||
)
|
||||
) and
|
||||
msg =
|
||||
"Finding the name of a file that does not exist does not mean that it will not be exist at the next operation."
|
||||
or
|
||||
// finding places to work with a file without setting permissions, but with predictable names.
|
||||
(
|
||||
fc.getTarget().hasGlobalOrStdName("fopen") or
|
||||
fc.getTarget().hasGlobalOrStdName("open")
|
||||
) and
|
||||
fc.getNumberOfArguments() = 2 and
|
||||
exists(FunctionCall fctmp, int i |
|
||||
numberArgumentWrite(fctmp.getTarget(), i) and
|
||||
globalValueNumber(fc) = globalValueNumber(fctmp.getArgument(i))
|
||||
) and
|
||||
not exists(FunctionCall fctmp, int i |
|
||||
numberArgumentRead(fctmp.getTarget(), i) and
|
||||
globalValueNumber(fc) = globalValueNumber(fctmp.getArgument(i))
|
||||
) and
|
||||
exists(FunctionCall fctmp |
|
||||
(
|
||||
fctmp.getTarget().hasGlobalOrStdName("strcat") or
|
||||
fctmp.getTarget().hasGlobalOrStdName("strcpy")
|
||||
) and
|
||||
globalValueNumber(fc.getArgument(0)) = globalValueNumber(fctmp.getAnArgument())
|
||||
or
|
||||
fctmp.getTarget().hasGlobalOrStdName("getenv") and
|
||||
globalValueNumber(fc.getArgument(0)) = globalValueNumber(fctmp)
|
||||
or
|
||||
(
|
||||
fctmp.getTarget().hasGlobalOrStdName("asprintf") or
|
||||
fctmp.getTarget().hasGlobalOrStdName("vasprintf") or
|
||||
fctmp.getTarget().hasGlobalOrStdName("xasprintf") or
|
||||
fctmp.getTarget().hasGlobalOrStdName("xvasprintf ")
|
||||
) and
|
||||
exists(Variable vrtmp |
|
||||
vrtmp = fc.getArgument(0).(VariableAccess).getTarget() and
|
||||
vrtmp = fctmp.getArgument(0).(AddressOfExpr).getAddressable().(Variable) and
|
||||
not vrtmp instanceof Field
|
||||
)
|
||||
) and
|
||||
not exists(FunctionCall fctmp |
|
||||
(
|
||||
fctmp.getTarget().hasGlobalOrStdName("umask") or
|
||||
fctmp.getTarget().hasGlobalOrStdName("fchmod") or
|
||||
fctmp.getTarget().hasGlobalOrStdName("chmod")
|
||||
) and
|
||||
(
|
||||
fc.getBasicBlock().getASuccessor*() = fctmp.getBasicBlock() or
|
||||
fctmp.getBasicBlock().getASuccessor*() = fc.getBasicBlock()
|
||||
)
|
||||
) and
|
||||
msg =
|
||||
"Creating a file for writing without evaluating its existence and setting permissions can be unsafe."
|
||||
select fc, msg
|
||||
@@ -0,0 +1,2 @@
|
||||
| test.cpp:16:20:16:25 | call to tmpnam | Finding the name of a file that does not exist does not mean that it will not be exist at the next operation. |
|
||||
| test.cpp:42:8:42:12 | call to fopen | Creating a file for writing without evaluating its existence and setting permissions can be unsafe. |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-377/InsecureTemporaryFile.ql
|
||||
@@ -0,0 +1,68 @@
|
||||
typedef int FILE;
|
||||
#define NULL (0)
|
||||
FILE *fopen(char *filename, const char *mode);
|
||||
FILE *fdopen(int handle, char *mode);
|
||||
char * tmpnam(char * name);
|
||||
int mkstemp(char * name);
|
||||
char * strcat(char *str1, const char *str2);
|
||||
int umask(int pmode);
|
||||
int chmod(char * filename,int pmode);
|
||||
int fprintf(FILE *fp,const char *fmt, ...);
|
||||
int fclose(FILE *stream);
|
||||
|
||||
int funcTest1()
|
||||
{
|
||||
FILE *fp;
|
||||
char *filename = tmpnam(NULL); // BAD
|
||||
fp = fopen(filename,"w");
|
||||
fprintf(fp,"%s\n","data to file");
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int funcTest2()
|
||||
{
|
||||
FILE *fp;
|
||||
int fd;
|
||||
char filename[80];
|
||||
strcat (filename, "/tmp/name.XXXXXX");
|
||||
fd = mkstemp(filename);
|
||||
if ( fd < 0 ) {
|
||||
return 1;
|
||||
}
|
||||
fp = fdopen(fd,"w"); // GOOD
|
||||
return 0;
|
||||
}
|
||||
|
||||
int funcTest3()
|
||||
{
|
||||
FILE *fp;
|
||||
char filename[80];
|
||||
strcat(filename, "/tmp/tmp.name");
|
||||
fp = fopen(filename,"w"); // BAD
|
||||
fprintf(fp,"%s\n","data to file");
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int funcTest4()
|
||||
{
|
||||
FILE *fp;
|
||||
char filename[80];
|
||||
umask(0022);
|
||||
strcat(filename, "/tmp/tmp.name");
|
||||
fp = fopen(filename,"w"); // GOOD
|
||||
chmod(filename,0666);
|
||||
fprintf(fp,"%s\n","data to file");
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
funcTest1();
|
||||
funcTest2();
|
||||
funcTest3();
|
||||
funcTest4();
|
||||
return 0;
|
||||
}
|
||||
@@ -594,12 +594,6 @@
|
||||
| map.cpp:105:31:105:32 | call to map | map.cpp:136:7:136:8 | m2 | |
|
||||
| map.cpp:105:31:105:32 | call to map | map.cpp:152:12:152:13 | m2 | |
|
||||
| map.cpp:105:31:105:32 | call to map | map.cpp:152:30:152:31 | m2 | |
|
||||
| map.cpp:105:31:105:32 | call to map | map.cpp:182:7:182:8 | m2 | |
|
||||
| map.cpp:105:31:105:32 | call to map | map.cpp:183:7:183:8 | m2 | |
|
||||
| map.cpp:105:31:105:32 | call to map | map.cpp:184:7:184:8 | m2 | |
|
||||
| map.cpp:105:31:105:32 | call to map | map.cpp:185:7:185:8 | m2 | |
|
||||
| map.cpp:105:31:105:32 | call to map | map.cpp:186:7:186:8 | m2 | |
|
||||
| map.cpp:105:31:105:32 | call to map | map.cpp:187:7:187:8 | m2 | |
|
||||
| map.cpp:105:31:105:32 | call to map | map.cpp:252:1:252:1 | m2 | |
|
||||
| map.cpp:105:35:105:36 | call to map | map.cpp:109:7:109:8 | m3 | |
|
||||
| map.cpp:105:35:105:36 | call to map | map.cpp:115:7:115:8 | m3 | |
|
||||
@@ -643,12 +637,6 @@
|
||||
| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:136:7:136:8 | m2 | |
|
||||
| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:152:12:152:13 | m2 | |
|
||||
| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:152:30:152:31 | m2 | |
|
||||
| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:182:7:182:8 | m2 | |
|
||||
| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:183:7:183:8 | m2 | |
|
||||
| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:184:7:184:8 | m2 | |
|
||||
| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:185:7:185:8 | m2 | |
|
||||
| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:186:7:186:8 | m2 | |
|
||||
| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | |
|
||||
| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | |
|
||||
| map.cpp:108:17:108:30 | call to make_pair | map.cpp:108:17:108:47 | call to pair | TAINT |
|
||||
| map.cpp:108:17:108:47 | call to pair | map.cpp:108:7:108:8 | ref arg m2 | TAINT |
|
||||
@@ -717,12 +705,6 @@
|
||||
| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:136:7:136:8 | m2 | |
|
||||
| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:152:12:152:13 | m2 | |
|
||||
| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:152:30:152:31 | m2 | |
|
||||
| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:182:7:182:8 | m2 | |
|
||||
| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:183:7:183:8 | m2 | |
|
||||
| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:184:7:184:8 | m2 | |
|
||||
| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:185:7:185:8 | m2 | |
|
||||
| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:186:7:186:8 | m2 | |
|
||||
| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | |
|
||||
| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | |
|
||||
| map.cpp:121:7:121:8 | m3 | map.cpp:121:10:121:13 | call to find | TAINT |
|
||||
| map.cpp:121:7:121:8 | ref arg m3 | map.cpp:127:7:127:8 | m3 | |
|
||||
@@ -748,12 +730,6 @@
|
||||
| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:136:7:136:8 | m2 | |
|
||||
| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:152:12:152:13 | m2 | |
|
||||
| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:152:30:152:31 | m2 | |
|
||||
| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:182:7:182:8 | m2 | |
|
||||
| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:183:7:183:8 | m2 | |
|
||||
| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:184:7:184:8 | m2 | |
|
||||
| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:185:7:185:8 | m2 | |
|
||||
| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:186:7:186:8 | m2 | |
|
||||
| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | |
|
||||
| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | |
|
||||
| map.cpp:127:7:127:8 | m3 | map.cpp:127:10:127:13 | call to find | TAINT |
|
||||
| map.cpp:127:7:127:8 | ref arg m3 | map.cpp:158:12:158:13 | m3 | |
|
||||
@@ -830,12 +806,6 @@
|
||||
| map.cpp:150:8:150:9 | ref arg i1 | map.cpp:150:8:150:9 | i1 | |
|
||||
| map.cpp:152:12:152:13 | m2 | map.cpp:152:15:152:19 | call to begin | TAINT |
|
||||
| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:152:30:152:31 | m2 | |
|
||||
| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:182:7:182:8 | m2 | |
|
||||
| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:183:7:183:8 | m2 | |
|
||||
| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:184:7:184:8 | m2 | |
|
||||
| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:185:7:185:8 | m2 | |
|
||||
| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:186:7:186:8 | m2 | |
|
||||
| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:187:7:187:8 | m2 | |
|
||||
| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:252:1:252:1 | m2 | |
|
||||
| map.cpp:152:15:152:19 | call to begin | map.cpp:152:7:152:21 | ... = ... | |
|
||||
| map.cpp:152:15:152:19 | call to begin | map.cpp:152:24:152:25 | i2 | |
|
||||
@@ -845,12 +815,6 @@
|
||||
| map.cpp:152:15:152:19 | call to begin | map.cpp:156:8:156:9 | i2 | |
|
||||
| map.cpp:152:30:152:31 | m2 | map.cpp:152:33:152:35 | call to end | TAINT |
|
||||
| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:152:30:152:31 | m2 | |
|
||||
| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:182:7:182:8 | m2 | |
|
||||
| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:183:7:183:8 | m2 | |
|
||||
| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:184:7:184:8 | m2 | |
|
||||
| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:185:7:185:8 | m2 | |
|
||||
| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:186:7:186:8 | m2 | |
|
||||
| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:187:7:187:8 | m2 | |
|
||||
| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:252:1:252:1 | m2 | |
|
||||
| map.cpp:152:40:152:41 | i2 | map.cpp:152:42:152:42 | call to operator++ | |
|
||||
| map.cpp:152:40:152:41 | ref arg i2 | map.cpp:152:24:152:25 | i2 | |
|
||||
@@ -962,59 +926,89 @@
|
||||
| map.cpp:177:27:177:29 | call to map | map.cpp:179:2:179:4 | m14 | |
|
||||
| map.cpp:177:27:177:29 | call to map | map.cpp:180:2:180:4 | m14 | |
|
||||
| map.cpp:177:27:177:29 | call to map | map.cpp:181:2:181:4 | m14 | |
|
||||
| map.cpp:177:27:177:29 | call to map | map.cpp:182:7:182:9 | m14 | |
|
||||
| map.cpp:177:27:177:29 | call to map | map.cpp:183:7:183:9 | m14 | |
|
||||
| map.cpp:177:27:177:29 | call to map | map.cpp:184:7:184:9 | m14 | |
|
||||
| map.cpp:177:27:177:29 | call to map | map.cpp:185:7:185:9 | m14 | |
|
||||
| map.cpp:177:27:177:29 | call to map | map.cpp:186:7:186:9 | m14 | |
|
||||
| map.cpp:177:27:177:29 | call to map | map.cpp:187:7:187:9 | m14 | |
|
||||
| map.cpp:177:27:177:29 | call to map | map.cpp:252:1:252:1 | m14 | |
|
||||
| map.cpp:178:2:178:4 | ref arg m14 | map.cpp:179:2:179:4 | m14 | |
|
||||
| map.cpp:178:2:178:4 | ref arg m14 | map.cpp:180:2:180:4 | m14 | |
|
||||
| map.cpp:178:2:178:4 | ref arg m14 | map.cpp:181:2:181:4 | m14 | |
|
||||
| map.cpp:178:2:178:4 | ref arg m14 | map.cpp:182:7:182:9 | m14 | |
|
||||
| map.cpp:178:2:178:4 | ref arg m14 | map.cpp:183:7:183:9 | m14 | |
|
||||
| map.cpp:178:2:178:4 | ref arg m14 | map.cpp:184:7:184:9 | m14 | |
|
||||
| map.cpp:178:2:178:4 | ref arg m14 | map.cpp:185:7:185:9 | m14 | |
|
||||
| map.cpp:178:2:178:4 | ref arg m14 | map.cpp:186:7:186:9 | m14 | |
|
||||
| map.cpp:178:2:178:4 | ref arg m14 | map.cpp:187:7:187:9 | m14 | |
|
||||
| map.cpp:178:2:178:4 | ref arg m14 | map.cpp:252:1:252:1 | m14 | |
|
||||
| map.cpp:178:13:178:26 | call to make_pair | map.cpp:178:13:178:36 | call to pair | TAINT |
|
||||
| map.cpp:178:13:178:36 | call to pair | map.cpp:178:2:178:4 | ref arg m14 | TAINT |
|
||||
| map.cpp:178:13:178:36 | call to pair | map.cpp:178:6:178:11 | call to insert | TAINT |
|
||||
| map.cpp:179:2:179:4 | ref arg m14 | map.cpp:180:2:180:4 | m14 | |
|
||||
| map.cpp:179:2:179:4 | ref arg m14 | map.cpp:181:2:181:4 | m14 | |
|
||||
| map.cpp:179:2:179:4 | ref arg m14 | map.cpp:182:7:182:9 | m14 | |
|
||||
| map.cpp:179:2:179:4 | ref arg m14 | map.cpp:183:7:183:9 | m14 | |
|
||||
| map.cpp:179:2:179:4 | ref arg m14 | map.cpp:184:7:184:9 | m14 | |
|
||||
| map.cpp:179:2:179:4 | ref arg m14 | map.cpp:185:7:185:9 | m14 | |
|
||||
| map.cpp:179:2:179:4 | ref arg m14 | map.cpp:186:7:186:9 | m14 | |
|
||||
| map.cpp:179:2:179:4 | ref arg m14 | map.cpp:187:7:187:9 | m14 | |
|
||||
| map.cpp:179:2:179:4 | ref arg m14 | map.cpp:252:1:252:1 | m14 | |
|
||||
| map.cpp:179:13:179:26 | call to make_pair | map.cpp:179:13:179:41 | call to pair | TAINT |
|
||||
| map.cpp:179:13:179:41 | call to pair | map.cpp:179:2:179:4 | ref arg m14 | TAINT |
|
||||
| map.cpp:179:13:179:41 | call to pair | map.cpp:179:6:179:11 | call to insert | TAINT |
|
||||
| map.cpp:180:2:180:4 | ref arg m14 | map.cpp:181:2:181:4 | m14 | |
|
||||
| map.cpp:180:2:180:4 | ref arg m14 | map.cpp:182:7:182:9 | m14 | |
|
||||
| map.cpp:180:2:180:4 | ref arg m14 | map.cpp:183:7:183:9 | m14 | |
|
||||
| map.cpp:180:2:180:4 | ref arg m14 | map.cpp:184:7:184:9 | m14 | |
|
||||
| map.cpp:180:2:180:4 | ref arg m14 | map.cpp:185:7:185:9 | m14 | |
|
||||
| map.cpp:180:2:180:4 | ref arg m14 | map.cpp:186:7:186:9 | m14 | |
|
||||
| map.cpp:180:2:180:4 | ref arg m14 | map.cpp:187:7:187:9 | m14 | |
|
||||
| map.cpp:180:2:180:4 | ref arg m14 | map.cpp:252:1:252:1 | m14 | |
|
||||
| map.cpp:180:13:180:26 | call to make_pair | map.cpp:180:13:180:41 | call to pair | TAINT |
|
||||
| map.cpp:180:13:180:41 | call to pair | map.cpp:180:2:180:4 | ref arg m14 | TAINT |
|
||||
| map.cpp:180:13:180:41 | call to pair | map.cpp:180:6:180:11 | call to insert | TAINT |
|
||||
| map.cpp:181:2:181:4 | ref arg m14 | map.cpp:182:7:182:9 | m14 | |
|
||||
| map.cpp:181:2:181:4 | ref arg m14 | map.cpp:183:7:183:9 | m14 | |
|
||||
| map.cpp:181:2:181:4 | ref arg m14 | map.cpp:184:7:184:9 | m14 | |
|
||||
| map.cpp:181:2:181:4 | ref arg m14 | map.cpp:185:7:185:9 | m14 | |
|
||||
| map.cpp:181:2:181:4 | ref arg m14 | map.cpp:186:7:186:9 | m14 | |
|
||||
| map.cpp:181:2:181:4 | ref arg m14 | map.cpp:187:7:187:9 | m14 | |
|
||||
| map.cpp:181:2:181:4 | ref arg m14 | map.cpp:252:1:252:1 | m14 | |
|
||||
| map.cpp:181:13:181:26 | call to make_pair | map.cpp:181:13:181:36 | call to pair | TAINT |
|
||||
| map.cpp:181:13:181:36 | call to pair | map.cpp:181:2:181:4 | ref arg m14 | TAINT |
|
||||
| map.cpp:181:13:181:36 | call to pair | map.cpp:181:6:181:11 | call to insert | TAINT |
|
||||
| map.cpp:182:7:182:8 | m2 | map.cpp:182:10:182:20 | call to lower_bound | TAINT |
|
||||
| map.cpp:182:7:182:8 | ref arg m2 | map.cpp:183:7:183:8 | m2 | |
|
||||
| map.cpp:182:7:182:8 | ref arg m2 | map.cpp:184:7:184:8 | m2 | |
|
||||
| map.cpp:182:7:182:8 | ref arg m2 | map.cpp:185:7:185:8 | m2 | |
|
||||
| map.cpp:182:7:182:8 | ref arg m2 | map.cpp:186:7:186:8 | m2 | |
|
||||
| map.cpp:182:7:182:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | |
|
||||
| map.cpp:182:7:182:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | |
|
||||
| map.cpp:183:7:183:8 | m2 | map.cpp:183:10:183:20 | call to upper_bound | TAINT |
|
||||
| map.cpp:183:7:183:8 | ref arg m2 | map.cpp:184:7:184:8 | m2 | |
|
||||
| map.cpp:183:7:183:8 | ref arg m2 | map.cpp:185:7:185:8 | m2 | |
|
||||
| map.cpp:183:7:183:8 | ref arg m2 | map.cpp:186:7:186:8 | m2 | |
|
||||
| map.cpp:183:7:183:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | |
|
||||
| map.cpp:183:7:183:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | |
|
||||
| map.cpp:184:7:184:8 | m2 | map.cpp:184:10:184:20 | call to equal_range | TAINT |
|
||||
| map.cpp:184:7:184:8 | ref arg m2 | map.cpp:185:7:185:8 | m2 | |
|
||||
| map.cpp:184:7:184:8 | ref arg m2 | map.cpp:186:7:186:8 | m2 | |
|
||||
| map.cpp:184:7:184:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | |
|
||||
| map.cpp:184:7:184:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | |
|
||||
| map.cpp:184:27:184:31 | first | map.cpp:184:7:184:31 | call to iterator | |
|
||||
| map.cpp:185:7:185:8 | m2 | map.cpp:185:10:185:20 | call to equal_range | TAINT |
|
||||
| map.cpp:185:7:185:8 | ref arg m2 | map.cpp:186:7:186:8 | m2 | |
|
||||
| map.cpp:185:7:185:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | |
|
||||
| map.cpp:185:7:185:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | |
|
||||
| map.cpp:185:27:185:32 | second | map.cpp:185:7:185:32 | call to iterator | |
|
||||
| map.cpp:186:7:186:8 | m2 | map.cpp:186:10:186:20 | call to upper_bound | TAINT |
|
||||
| map.cpp:186:7:186:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | |
|
||||
| map.cpp:186:7:186:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | |
|
||||
| map.cpp:187:7:187:8 | m2 | map.cpp:187:10:187:20 | call to equal_range | TAINT |
|
||||
| map.cpp:187:7:187:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | |
|
||||
| map.cpp:187:27:187:32 | second | map.cpp:187:7:187:32 | call to iterator | |
|
||||
| map.cpp:182:7:182:9 | m14 | map.cpp:182:11:182:21 | call to lower_bound | TAINT |
|
||||
| map.cpp:182:7:182:9 | ref arg m14 | map.cpp:183:7:183:9 | m14 | |
|
||||
| map.cpp:182:7:182:9 | ref arg m14 | map.cpp:184:7:184:9 | m14 | |
|
||||
| map.cpp:182:7:182:9 | ref arg m14 | map.cpp:185:7:185:9 | m14 | |
|
||||
| map.cpp:182:7:182:9 | ref arg m14 | map.cpp:186:7:186:9 | m14 | |
|
||||
| map.cpp:182:7:182:9 | ref arg m14 | map.cpp:187:7:187:9 | m14 | |
|
||||
| map.cpp:182:7:182:9 | ref arg m14 | map.cpp:252:1:252:1 | m14 | |
|
||||
| map.cpp:183:7:183:9 | m14 | map.cpp:183:11:183:21 | call to upper_bound | TAINT |
|
||||
| map.cpp:183:7:183:9 | ref arg m14 | map.cpp:184:7:184:9 | m14 | |
|
||||
| map.cpp:183:7:183:9 | ref arg m14 | map.cpp:185:7:185:9 | m14 | |
|
||||
| map.cpp:183:7:183:9 | ref arg m14 | map.cpp:186:7:186:9 | m14 | |
|
||||
| map.cpp:183:7:183:9 | ref arg m14 | map.cpp:187:7:187:9 | m14 | |
|
||||
| map.cpp:183:7:183:9 | ref arg m14 | map.cpp:252:1:252:1 | m14 | |
|
||||
| map.cpp:184:7:184:9 | m14 | map.cpp:184:11:184:21 | call to equal_range | TAINT |
|
||||
| map.cpp:184:7:184:9 | ref arg m14 | map.cpp:185:7:185:9 | m14 | |
|
||||
| map.cpp:184:7:184:9 | ref arg m14 | map.cpp:186:7:186:9 | m14 | |
|
||||
| map.cpp:184:7:184:9 | ref arg m14 | map.cpp:187:7:187:9 | m14 | |
|
||||
| map.cpp:184:7:184:9 | ref arg m14 | map.cpp:252:1:252:1 | m14 | |
|
||||
| map.cpp:184:28:184:32 | first | map.cpp:184:7:184:32 | call to iterator | |
|
||||
| map.cpp:185:7:185:9 | m14 | map.cpp:185:11:185:21 | call to equal_range | TAINT |
|
||||
| map.cpp:185:7:185:9 | ref arg m14 | map.cpp:186:7:186:9 | m14 | |
|
||||
| map.cpp:185:7:185:9 | ref arg m14 | map.cpp:187:7:187:9 | m14 | |
|
||||
| map.cpp:185:7:185:9 | ref arg m14 | map.cpp:252:1:252:1 | m14 | |
|
||||
| map.cpp:185:28:185:33 | second | map.cpp:185:7:185:33 | call to iterator | |
|
||||
| map.cpp:186:7:186:9 | m14 | map.cpp:186:11:186:21 | call to upper_bound | TAINT |
|
||||
| map.cpp:186:7:186:9 | ref arg m14 | map.cpp:187:7:187:9 | m14 | |
|
||||
| map.cpp:186:7:186:9 | ref arg m14 | map.cpp:252:1:252:1 | m14 | |
|
||||
| map.cpp:187:7:187:9 | m14 | map.cpp:187:11:187:21 | call to equal_range | TAINT |
|
||||
| map.cpp:187:7:187:9 | ref arg m14 | map.cpp:252:1:252:1 | m14 | |
|
||||
| map.cpp:187:28:187:33 | second | map.cpp:187:7:187:33 | call to iterator | |
|
||||
| map.cpp:190:27:190:29 | call to map | map.cpp:191:2:191:4 | m15 | |
|
||||
| map.cpp:190:27:190:29 | call to map | map.cpp:193:7:193:9 | m15 | |
|
||||
| map.cpp:190:27:190:29 | call to map | map.cpp:197:2:197:4 | m15 | |
|
||||
@@ -1315,9 +1309,6 @@
|
||||
| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:288:7:288:8 | m2 | |
|
||||
| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:304:12:304:13 | m2 | |
|
||||
| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:304:30:304:31 | m2 | |
|
||||
| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:334:7:334:8 | m2 | |
|
||||
| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:335:7:335:8 | m2 | |
|
||||
| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:336:7:336:8 | m2 | |
|
||||
| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:438:1:438:1 | m2 | |
|
||||
| map.cpp:257:45:257:46 | call to unordered_map | map.cpp:261:7:261:8 | m3 | |
|
||||
| map.cpp:257:45:257:46 | call to unordered_map | map.cpp:267:7:267:8 | m3 | |
|
||||
@@ -1361,9 +1352,6 @@
|
||||
| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:288:7:288:8 | m2 | |
|
||||
| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:304:12:304:13 | m2 | |
|
||||
| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:304:30:304:31 | m2 | |
|
||||
| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:334:7:334:8 | m2 | |
|
||||
| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:335:7:335:8 | m2 | |
|
||||
| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:336:7:336:8 | m2 | |
|
||||
| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:438:1:438:1 | m2 | |
|
||||
| map.cpp:260:17:260:30 | call to make_pair | map.cpp:260:17:260:47 | call to pair | TAINT |
|
||||
| map.cpp:260:17:260:47 | call to pair | map.cpp:260:7:260:8 | ref arg m2 | TAINT |
|
||||
@@ -1432,9 +1420,6 @@
|
||||
| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:288:7:288:8 | m2 | |
|
||||
| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:304:12:304:13 | m2 | |
|
||||
| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:304:30:304:31 | m2 | |
|
||||
| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:334:7:334:8 | m2 | |
|
||||
| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:335:7:335:8 | m2 | |
|
||||
| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:336:7:336:8 | m2 | |
|
||||
| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:438:1:438:1 | m2 | |
|
||||
| map.cpp:273:7:273:8 | m3 | map.cpp:273:10:273:13 | call to find | TAINT |
|
||||
| map.cpp:273:7:273:8 | ref arg m3 | map.cpp:279:7:279:8 | m3 | |
|
||||
@@ -1460,9 +1445,6 @@
|
||||
| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:288:7:288:8 | m2 | |
|
||||
| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:304:12:304:13 | m2 | |
|
||||
| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:304:30:304:31 | m2 | |
|
||||
| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:334:7:334:8 | m2 | |
|
||||
| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:335:7:335:8 | m2 | |
|
||||
| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:336:7:336:8 | m2 | |
|
||||
| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:438:1:438:1 | m2 | |
|
||||
| map.cpp:279:7:279:8 | m3 | map.cpp:279:10:279:13 | call to find | TAINT |
|
||||
| map.cpp:279:7:279:8 | ref arg m3 | map.cpp:310:12:310:13 | m3 | |
|
||||
@@ -1539,9 +1521,6 @@
|
||||
| map.cpp:302:8:302:9 | ref arg i1 | map.cpp:302:8:302:9 | i1 | |
|
||||
| map.cpp:304:12:304:13 | m2 | map.cpp:304:15:304:19 | call to begin | TAINT |
|
||||
| map.cpp:304:12:304:13 | ref arg m2 | map.cpp:304:30:304:31 | m2 | |
|
||||
| map.cpp:304:12:304:13 | ref arg m2 | map.cpp:334:7:334:8 | m2 | |
|
||||
| map.cpp:304:12:304:13 | ref arg m2 | map.cpp:335:7:335:8 | m2 | |
|
||||
| map.cpp:304:12:304:13 | ref arg m2 | map.cpp:336:7:336:8 | m2 | |
|
||||
| map.cpp:304:12:304:13 | ref arg m2 | map.cpp:438:1:438:1 | m2 | |
|
||||
| map.cpp:304:15:304:19 | call to begin | map.cpp:304:7:304:21 | ... = ... | |
|
||||
| map.cpp:304:15:304:19 | call to begin | map.cpp:304:24:304:25 | i2 | |
|
||||
@@ -1551,9 +1530,6 @@
|
||||
| map.cpp:304:15:304:19 | call to begin | map.cpp:308:8:308:9 | i2 | |
|
||||
| map.cpp:304:30:304:31 | m2 | map.cpp:304:33:304:35 | call to end | TAINT |
|
||||
| map.cpp:304:30:304:31 | ref arg m2 | map.cpp:304:30:304:31 | m2 | |
|
||||
| map.cpp:304:30:304:31 | ref arg m2 | map.cpp:334:7:334:8 | m2 | |
|
||||
| map.cpp:304:30:304:31 | ref arg m2 | map.cpp:335:7:335:8 | m2 | |
|
||||
| map.cpp:304:30:304:31 | ref arg m2 | map.cpp:336:7:336:8 | m2 | |
|
||||
| map.cpp:304:30:304:31 | ref arg m2 | map.cpp:438:1:438:1 | m2 | |
|
||||
| map.cpp:304:40:304:41 | i2 | map.cpp:304:42:304:42 | call to operator++ | |
|
||||
| map.cpp:304:40:304:41 | ref arg i2 | map.cpp:304:24:304:25 | i2 | |
|
||||
@@ -1665,41 +1641,56 @@
|
||||
| map.cpp:329:37:329:39 | call to unordered_map | map.cpp:331:2:331:4 | m14 | |
|
||||
| map.cpp:329:37:329:39 | call to unordered_map | map.cpp:332:2:332:4 | m14 | |
|
||||
| map.cpp:329:37:329:39 | call to unordered_map | map.cpp:333:2:333:4 | m14 | |
|
||||
| map.cpp:329:37:329:39 | call to unordered_map | map.cpp:334:7:334:9 | m14 | |
|
||||
| map.cpp:329:37:329:39 | call to unordered_map | map.cpp:335:7:335:9 | m14 | |
|
||||
| map.cpp:329:37:329:39 | call to unordered_map | map.cpp:336:7:336:9 | m14 | |
|
||||
| map.cpp:329:37:329:39 | call to unordered_map | map.cpp:438:1:438:1 | m14 | |
|
||||
| map.cpp:330:2:330:4 | ref arg m14 | map.cpp:331:2:331:4 | m14 | |
|
||||
| map.cpp:330:2:330:4 | ref arg m14 | map.cpp:332:2:332:4 | m14 | |
|
||||
| map.cpp:330:2:330:4 | ref arg m14 | map.cpp:333:2:333:4 | m14 | |
|
||||
| map.cpp:330:2:330:4 | ref arg m14 | map.cpp:334:7:334:9 | m14 | |
|
||||
| map.cpp:330:2:330:4 | ref arg m14 | map.cpp:335:7:335:9 | m14 | |
|
||||
| map.cpp:330:2:330:4 | ref arg m14 | map.cpp:336:7:336:9 | m14 | |
|
||||
| map.cpp:330:2:330:4 | ref arg m14 | map.cpp:438:1:438:1 | m14 | |
|
||||
| map.cpp:330:13:330:26 | call to make_pair | map.cpp:330:13:330:36 | call to pair | TAINT |
|
||||
| map.cpp:330:13:330:36 | call to pair | map.cpp:330:2:330:4 | ref arg m14 | TAINT |
|
||||
| map.cpp:330:13:330:36 | call to pair | map.cpp:330:6:330:11 | call to insert | TAINT |
|
||||
| map.cpp:331:2:331:4 | ref arg m14 | map.cpp:332:2:332:4 | m14 | |
|
||||
| map.cpp:331:2:331:4 | ref arg m14 | map.cpp:333:2:333:4 | m14 | |
|
||||
| map.cpp:331:2:331:4 | ref arg m14 | map.cpp:334:7:334:9 | m14 | |
|
||||
| map.cpp:331:2:331:4 | ref arg m14 | map.cpp:335:7:335:9 | m14 | |
|
||||
| map.cpp:331:2:331:4 | ref arg m14 | map.cpp:336:7:336:9 | m14 | |
|
||||
| map.cpp:331:2:331:4 | ref arg m14 | map.cpp:438:1:438:1 | m14 | |
|
||||
| map.cpp:331:13:331:26 | call to make_pair | map.cpp:331:13:331:41 | call to pair | TAINT |
|
||||
| map.cpp:331:13:331:41 | call to pair | map.cpp:331:2:331:4 | ref arg m14 | TAINT |
|
||||
| map.cpp:331:13:331:41 | call to pair | map.cpp:331:6:331:11 | call to insert | TAINT |
|
||||
| map.cpp:332:2:332:4 | ref arg m14 | map.cpp:333:2:333:4 | m14 | |
|
||||
| map.cpp:332:2:332:4 | ref arg m14 | map.cpp:334:7:334:9 | m14 | |
|
||||
| map.cpp:332:2:332:4 | ref arg m14 | map.cpp:335:7:335:9 | m14 | |
|
||||
| map.cpp:332:2:332:4 | ref arg m14 | map.cpp:336:7:336:9 | m14 | |
|
||||
| map.cpp:332:2:332:4 | ref arg m14 | map.cpp:438:1:438:1 | m14 | |
|
||||
| map.cpp:332:13:332:26 | call to make_pair | map.cpp:332:13:332:41 | call to pair | TAINT |
|
||||
| map.cpp:332:13:332:41 | call to pair | map.cpp:332:2:332:4 | ref arg m14 | TAINT |
|
||||
| map.cpp:332:13:332:41 | call to pair | map.cpp:332:6:332:11 | call to insert | TAINT |
|
||||
| map.cpp:333:2:333:4 | ref arg m14 | map.cpp:334:7:334:9 | m14 | |
|
||||
| map.cpp:333:2:333:4 | ref arg m14 | map.cpp:335:7:335:9 | m14 | |
|
||||
| map.cpp:333:2:333:4 | ref arg m14 | map.cpp:336:7:336:9 | m14 | |
|
||||
| map.cpp:333:2:333:4 | ref arg m14 | map.cpp:438:1:438:1 | m14 | |
|
||||
| map.cpp:333:13:333:26 | call to make_pair | map.cpp:333:13:333:36 | call to pair | TAINT |
|
||||
| map.cpp:333:13:333:36 | call to pair | map.cpp:333:2:333:4 | ref arg m14 | TAINT |
|
||||
| map.cpp:333:13:333:36 | call to pair | map.cpp:333:6:333:11 | call to insert | TAINT |
|
||||
| map.cpp:334:7:334:8 | m2 | map.cpp:334:10:334:20 | call to equal_range | TAINT |
|
||||
| map.cpp:334:7:334:8 | ref arg m2 | map.cpp:335:7:335:8 | m2 | |
|
||||
| map.cpp:334:7:334:8 | ref arg m2 | map.cpp:336:7:336:8 | m2 | |
|
||||
| map.cpp:334:7:334:8 | ref arg m2 | map.cpp:438:1:438:1 | m2 | |
|
||||
| map.cpp:334:27:334:31 | first | map.cpp:334:7:334:31 | call to iterator | |
|
||||
| map.cpp:335:7:335:8 | m2 | map.cpp:335:10:335:20 | call to equal_range | TAINT |
|
||||
| map.cpp:335:7:335:8 | ref arg m2 | map.cpp:336:7:336:8 | m2 | |
|
||||
| map.cpp:335:7:335:8 | ref arg m2 | map.cpp:438:1:438:1 | m2 | |
|
||||
| map.cpp:335:27:335:32 | second | map.cpp:335:7:335:32 | call to iterator | |
|
||||
| map.cpp:336:7:336:8 | m2 | map.cpp:336:10:336:20 | call to equal_range | TAINT |
|
||||
| map.cpp:336:7:336:8 | ref arg m2 | map.cpp:438:1:438:1 | m2 | |
|
||||
| map.cpp:336:27:336:32 | second | map.cpp:336:7:336:32 | call to iterator | |
|
||||
| map.cpp:334:7:334:9 | m14 | map.cpp:334:11:334:21 | call to equal_range | TAINT |
|
||||
| map.cpp:334:7:334:9 | ref arg m14 | map.cpp:335:7:335:9 | m14 | |
|
||||
| map.cpp:334:7:334:9 | ref arg m14 | map.cpp:336:7:336:9 | m14 | |
|
||||
| map.cpp:334:7:334:9 | ref arg m14 | map.cpp:438:1:438:1 | m14 | |
|
||||
| map.cpp:334:28:334:32 | first | map.cpp:334:7:334:32 | call to iterator | |
|
||||
| map.cpp:335:7:335:9 | m14 | map.cpp:335:11:335:21 | call to equal_range | TAINT |
|
||||
| map.cpp:335:7:335:9 | ref arg m14 | map.cpp:336:7:336:9 | m14 | |
|
||||
| map.cpp:335:7:335:9 | ref arg m14 | map.cpp:438:1:438:1 | m14 | |
|
||||
| map.cpp:335:28:335:33 | second | map.cpp:335:7:335:33 | call to iterator | |
|
||||
| map.cpp:336:7:336:9 | m14 | map.cpp:336:11:336:21 | call to equal_range | TAINT |
|
||||
| map.cpp:336:7:336:9 | ref arg m14 | map.cpp:438:1:438:1 | m14 | |
|
||||
| map.cpp:336:28:336:33 | second | map.cpp:336:7:336:33 | call to iterator | |
|
||||
| map.cpp:339:37:339:39 | call to unordered_map | map.cpp:340:2:340:4 | m15 | |
|
||||
| map.cpp:339:37:339:39 | call to unordered_map | map.cpp:342:7:342:9 | m15 | |
|
||||
| map.cpp:339:37:339:39 | call to unordered_map | map.cpp:346:2:346:4 | m15 | |
|
||||
|
||||
@@ -179,12 +179,12 @@ void test_map()
|
||||
m14.insert(std::make_pair("b", source()));
|
||||
m14.insert(std::make_pair("c", source()));
|
||||
m14.insert(std::make_pair("d", "d"));
|
||||
sink(m2.lower_bound("b")); // $ ast,ir
|
||||
sink(m2.upper_bound("b")); // $ ast,ir
|
||||
sink(m2.equal_range("b").first); // $ MISSING: ast,ir
|
||||
sink(m2.equal_range("b").second); // $ MISSING: ast,ir
|
||||
sink(m2.upper_bound("c")); // $ SPURIOUS: ast,ir
|
||||
sink(m2.equal_range("c").second);
|
||||
sink(m14.lower_bound("b")); // $ ast,ir=179:33 ast,ir=180:33
|
||||
sink(m14.upper_bound("b")); // $ ast,ir=179:33 ast,ir=180:33
|
||||
sink(m14.equal_range("b").first); // $ MISSING: ast,ir
|
||||
sink(m14.equal_range("b").second); // $ MISSING: ast,ir
|
||||
sink(m14.upper_bound("c")); // $ SPURIOUS: ast,ir=179:33 ast,ir=180:33
|
||||
sink(m14.equal_range("c").second);
|
||||
|
||||
// swap
|
||||
std::map<char *, char *> m15, m16, m17, m18;
|
||||
@@ -331,9 +331,9 @@ void test_unordered_map()
|
||||
m14.insert(std::make_pair("b", source()));
|
||||
m14.insert(std::make_pair("c", source()));
|
||||
m14.insert(std::make_pair("d", "d"));
|
||||
sink(m2.equal_range("b").first);
|
||||
sink(m2.equal_range("b").second); // $ MISSING: ast,ir
|
||||
sink(m2.equal_range("c").second);
|
||||
sink(m14.equal_range("b").first);
|
||||
sink(m14.equal_range("b").second); // $ MISSING: ast,ir
|
||||
sink(m14.equal_range("c").second);
|
||||
|
||||
// swap
|
||||
std::unordered_map<char *, char *> m15, m16, m17, m18;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
edges
|
||||
| test.cpp:11:26:11:28 | url | test.cpp:15:30:15:32 | url |
|
||||
| test.cpp:28:10:28:29 | http://example.com | test.cpp:11:26:11:28 | url |
|
||||
| test.cpp:35:23:35:42 | http://example.com | test.cpp:39:11:39:15 | url_l |
|
||||
| test.cpp:36:26:36:45 | http://example.com | test.cpp:40:11:40:17 | access to array |
|
||||
| test.cpp:39:11:39:15 | url_l | test.cpp:11:26:11:28 | url |
|
||||
| test.cpp:40:11:40:17 | access to array | test.cpp:11:26:11:28 | url |
|
||||
| test.cpp:46:18:46:26 | http:// | test.cpp:49:11:49:16 | buffer |
|
||||
| test.cpp:49:11:49:16 | buffer | test.cpp:11:26:11:28 | url |
|
||||
nodes
|
||||
| test.cpp:11:26:11:28 | url | semmle.label | url |
|
||||
| test.cpp:15:30:15:32 | url | semmle.label | url |
|
||||
| test.cpp:28:10:28:29 | http://example.com | semmle.label | http://example.com |
|
||||
| test.cpp:35:23:35:42 | http://example.com | semmle.label | http://example.com |
|
||||
| test.cpp:36:26:36:45 | http://example.com | semmle.label | http://example.com |
|
||||
| test.cpp:39:11:39:15 | url_l | semmle.label | url_l |
|
||||
| test.cpp:40:11:40:17 | access to array | semmle.label | access to array |
|
||||
| test.cpp:46:18:46:26 | http:// | semmle.label | http:// |
|
||||
| test.cpp:49:11:49:16 | buffer | semmle.label | buffer |
|
||||
subpaths
|
||||
#select
|
||||
| test.cpp:28:10:28:29 | http://example.com | test.cpp:28:10:28:29 | http://example.com | test.cpp:15:30:15:32 | url | A URL may be constructed with the HTTP protocol. |
|
||||
| test.cpp:35:23:35:42 | http://example.com | test.cpp:35:23:35:42 | http://example.com | test.cpp:15:30:15:32 | url | A URL may be constructed with the HTTP protocol. |
|
||||
| test.cpp:36:26:36:45 | http://example.com | test.cpp:36:26:36:45 | http://example.com | test.cpp:15:30:15:32 | url | A URL may be constructed with the HTTP protocol. |
|
||||
| test.cpp:46:18:46:26 | http:// | test.cpp:46:18:46:26 | http:// | test.cpp:15:30:15:32 | url | A URL may be constructed with the HTTP protocol. |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-319/UseOfHttp.ql
|
||||
@@ -0,0 +1,60 @@
|
||||
|
||||
struct host
|
||||
{
|
||||
// ...
|
||||
};
|
||||
|
||||
host gethostbyname(const char *str);
|
||||
char *strcpy(char *s1, const char *s2);
|
||||
char *strcat(char *s1, const char *s2);
|
||||
|
||||
void openUrl(const char *url)
|
||||
{
|
||||
// ...
|
||||
|
||||
host myHost = gethostbyname(url);
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
void doNothing(char *url)
|
||||
{
|
||||
}
|
||||
|
||||
const char *url_g = "http://example.com"; // BAD [NOT DETECTED]
|
||||
|
||||
void test()
|
||||
{
|
||||
openUrl("http://example.com"); // BAD
|
||||
openUrl("https://example.com"); // GOOD (https)
|
||||
openUrl("http://localhost/example"); // GOOD (localhost)
|
||||
openUrl("https://localhost/example"); // GOOD (https, localhost)
|
||||
doNothing("http://example.com"); // GOOD (URL not used)
|
||||
|
||||
{
|
||||
const char *url_l = "http://example.com"; // BAD
|
||||
const char *urls[] = { "http://example.com" }; // BAD
|
||||
|
||||
openUrl(url_g);
|
||||
openUrl(url_l);
|
||||
openUrl(urls[0]);
|
||||
}
|
||||
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
strcpy(buffer, "http://"); // BAD
|
||||
strcat(buffer, "example.com");
|
||||
|
||||
openUrl(buffer);
|
||||
}
|
||||
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
strcpy(buffer, "https://"); // GOOD (https)
|
||||
strcat(buffer, "example.com");
|
||||
|
||||
openUrl(buffer);
|
||||
}
|
||||
}
|
||||
@@ -6,13 +6,25 @@ import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl
|
||||
import semmle.code.csharp.controlflow.internal.Splitting
|
||||
import Consistency
|
||||
|
||||
private predicate splitBB(ControlFlow::BasicBlock bb) {
|
||||
exists(ControlFlow::Node first |
|
||||
first = bb.getFirstNode() and
|
||||
first.isJoin() and
|
||||
strictcount(first.getAPredecessor().getElement()) = 1
|
||||
)
|
||||
}
|
||||
|
||||
private class RelevantBasicBlock extends ControlFlow::BasicBlock {
|
||||
RelevantBasicBlock() { not splitBB(this) }
|
||||
}
|
||||
|
||||
predicate bbStartInconsistency(ControlFlowElement cfe) {
|
||||
exists(ControlFlow::BasicBlock bb | bb.getFirstNode() = cfe.getAControlFlowNode()) and
|
||||
exists(RelevantBasicBlock bb | bb.getFirstNode() = cfe.getAControlFlowNode()) and
|
||||
not cfe = any(PreBasicBlock bb).getFirstElement()
|
||||
}
|
||||
|
||||
predicate bbSuccInconsistency(ControlFlowElement pred, ControlFlowElement succ) {
|
||||
exists(ControlFlow::BasicBlock predBB, ControlFlow::BasicBlock succBB |
|
||||
exists(RelevantBasicBlock predBB, RelevantBasicBlock succBB |
|
||||
predBB.getLastNode() = pred.getAControlFlowNode() and
|
||||
succBB = predBB.getASuccessor() and
|
||||
succBB.getFirstNode() = succ.getAControlFlowNode()
|
||||
6
csharp/ql/consistency-queries/qlpack.yml
Normal file
6
csharp/ql/consistency-queries/qlpack.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
name: codeql-csharp-consistency-queries
|
||||
version: 0.0.0
|
||||
libraryPathDependencies:
|
||||
- codeql/csharp-all
|
||||
- codeql/csharp-queries
|
||||
extractor: csharp
|
||||
@@ -924,7 +924,8 @@ module Consistency {
|
||||
succSplits(pred, predSplits, succ, succSplits, c) and
|
||||
split.hasEntry(pred, succ, c) and
|
||||
not split.getKind() = predSplits.getASplit().getKind() and
|
||||
not split = succSplits.getASplit()
|
||||
not split = succSplits.getASplit() and
|
||||
split.getKind().isEnabled(succ)
|
||||
}
|
||||
|
||||
query predicate breakInvariant5(
|
||||
|
||||
@@ -29,17 +29,22 @@ predicate returnsCollection(Callable c, Field f) {
|
||||
not c.(Modifiable).isStatic()
|
||||
}
|
||||
|
||||
predicate mayWriteToCollection(Expr modified) {
|
||||
modified instanceof CollectionModificationAccess
|
||||
predicate nodeMayWriteToCollection(Node modified) {
|
||||
modified.asExpr() instanceof CollectionModificationAccess
|
||||
or
|
||||
exists(Expr mid | mayWriteToCollection(mid) | localExprFlow(modified, mid))
|
||||
exists(Node mid | nodeMayWriteToCollection(mid) | localFlowStep(modified, mid))
|
||||
or
|
||||
exists(MethodCall mid, Callable c | mayWriteToCollection(mid) |
|
||||
mid.getTarget() = c and
|
||||
c.canReturn(modified)
|
||||
exists(Node mid, MethodCall mc, Callable c | nodeMayWriteToCollection(mid) |
|
||||
mc = mid.asExpr() and
|
||||
mc.getTarget() = c and
|
||||
c.canReturn(modified.asExpr())
|
||||
)
|
||||
}
|
||||
|
||||
predicate mayWriteToCollection(Expr modified) {
|
||||
nodeMayWriteToCollection(any(ExprNode n | n.getExpr() = modified))
|
||||
}
|
||||
|
||||
predicate modificationAfter(Expr before, Expr after) {
|
||||
mayWriteToCollection(after) and
|
||||
localFlowStep+(exprNode(before), exprNode(after))
|
||||
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
@@ -1 +1 @@
|
||||
semmle/code/csharp/PrintAst.ql
|
||||
shared/PrintAst.ql
|
||||
0
csharp/ql/test/shared/PrintAst.expected
Normal file
0
csharp/ql/test/shared/PrintAst.expected
Normal file
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import PrintAst
|
||||
import semmle.code.csharp.PrintAst
|
||||
|
||||
/**
|
||||
* Temporarily tweak this class or make a copy to control which functions are
|
||||
@@ -164,10 +164,12 @@ Python built-in support
|
||||
Name, Category
|
||||
aiohttp.web, Web framework
|
||||
Django, Web framework
|
||||
djangorestframework, Web framework
|
||||
FastAPI, Web framework
|
||||
Flask, Web framework
|
||||
Tornado, Web framework
|
||||
Twisted, Web framework
|
||||
Flask-Admin, Web framework
|
||||
starlette, Asynchronous Server Gateway Interface (ASGI)
|
||||
dill, Serialization
|
||||
PyYAML, Serialization
|
||||
@@ -183,6 +185,8 @@ Python built-in support
|
||||
pydantic, Utility library
|
||||
yarl, Utility library
|
||||
aioch, Database
|
||||
aiomysql, Database
|
||||
aiopg, Database
|
||||
asyncpg, Database
|
||||
clickhouse-driver, Database
|
||||
mysql-connector-python, Database
|
||||
|
||||
@@ -83,6 +83,7 @@ private module Frameworks {
|
||||
private import semmle.code.java.frameworks.android.XssSinks
|
||||
private import semmle.code.java.frameworks.ApacheHttp
|
||||
private import semmle.code.java.frameworks.apache.Collections
|
||||
private import semmle.code.java.frameworks.apache.IO
|
||||
private import semmle.code.java.frameworks.apache.Lang
|
||||
private import semmle.code.java.frameworks.Flexjson
|
||||
private import semmle.code.java.frameworks.guava.Guava
|
||||
@@ -322,33 +323,11 @@ private predicate summaryModelCsv(string row) {
|
||||
"org.apache.commons.codec;BinaryDecoder;true;decode;(byte[]);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.codec;StringEncoder;true;encode;(String);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.codec;StringDecoder;true;decode;(String);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.io;IOUtils;false;buffer;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.io;IOUtils;false;readLines;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.io;IOUtils;false;readFully;(InputStream,int);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.io;IOUtils;false;toBufferedInputStream;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.io;IOUtils;false;toBufferedReader;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.io;IOUtils;false;toByteArray;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.io;IOUtils;false;toCharArray;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.io;IOUtils;false;toInputStream;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.io;IOUtils;false;toString;;;Argument[0];ReturnValue;taint",
|
||||
"java.net;URLDecoder;false;decode;;;Argument[0];ReturnValue;taint",
|
||||
"java.net;URI;false;create;;;Argument[0];ReturnValue;taint",
|
||||
"javax.xml.transform.sax;SAXSource;false;sourceToInputSource;;;Argument[0];ReturnValue;taint",
|
||||
// arg to arg
|
||||
"java.lang;System;false;arraycopy;;;Argument[0];Argument[2];taint",
|
||||
"org.apache.commons.io;IOUtils;false;copy;;;Argument[0];Argument[1];taint",
|
||||
"org.apache.commons.io;IOUtils;false;copyLarge;;;Argument[0];Argument[1];taint",
|
||||
"org.apache.commons.io;IOUtils;false;read;;;Argument[0];Argument[1];taint",
|
||||
"org.apache.commons.io;IOUtils;false;readFully;(InputStream,byte[]);;Argument[0];Argument[1];taint",
|
||||
"org.apache.commons.io;IOUtils;false;readFully;(InputStream,byte[],int,int);;Argument[0];Argument[1];taint",
|
||||
"org.apache.commons.io;IOUtils;false;readFully;(InputStream,ByteBuffer);;Argument[0];Argument[1];taint",
|
||||
"org.apache.commons.io;IOUtils;false;readFully;(ReadableByteChannel,ByteBuffer);;Argument[0];Argument[1];taint",
|
||||
"org.apache.commons.io;IOUtils;false;readFully;(Reader,char[]);;Argument[0];Argument[1];taint",
|
||||
"org.apache.commons.io;IOUtils;false;readFully;(Reader,char[],int,int);;Argument[0];Argument[1];taint",
|
||||
"org.apache.commons.io;IOUtils;false;write;;;Argument[0];Argument[1];taint",
|
||||
"org.apache.commons.io;IOUtils;false;writeChunked;;;Argument[0];Argument[1];taint",
|
||||
"org.apache.commons.io;IOUtils;false;writeLines;;;Argument[0];Argument[2];taint",
|
||||
"org.apache.commons.io;IOUtils;false;writeLines;;;Argument[1];Argument[2];taint",
|
||||
// constructor flow
|
||||
"java.io;File;false;File;;;Argument[0];Argument[-1];taint",
|
||||
"java.io;File;false;File;;;Argument[1];Argument[-1];taint",
|
||||
|
||||
@@ -11,6 +11,7 @@ private import semmle.code.java.dataflow.DataFlow
|
||||
*/
|
||||
private module Frameworks {
|
||||
private import semmle.code.java.frameworks.jackson.JacksonSerializability
|
||||
private import semmle.code.java.frameworks.android.AsyncTask
|
||||
private import semmle.code.java.frameworks.android.Intent
|
||||
private import semmle.code.java.frameworks.android.SQLite
|
||||
private import semmle.code.java.frameworks.Guice
|
||||
@@ -64,6 +65,20 @@ class AdditionalTaintStep extends Unit {
|
||||
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
|
||||
}
|
||||
|
||||
/**
|
||||
* A unit class for adding additional value steps.
|
||||
*
|
||||
* Extend this class to add additional value-preserving steps that should apply
|
||||
* to all data flow configurations.
|
||||
*/
|
||||
class AdditionalValueStep extends Unit {
|
||||
/**
|
||||
* Holds if the step from `node1` to `node2` is a value-preserving step and
|
||||
* should apply to all data flow configurations.
|
||||
*/
|
||||
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
|
||||
}
|
||||
|
||||
/**
|
||||
* A method or constructor that preserves taint.
|
||||
*
|
||||
|
||||
@@ -5,6 +5,7 @@ private import DataFlowDispatch
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import semmle.code.java.dataflow.SSA
|
||||
private import ContainerFlow
|
||||
private import semmle.code.java.dataflow.FlowSteps
|
||||
private import semmle.code.java.dataflow.FlowSummary
|
||||
private import FlowSummaryImpl as FlowSummaryImpl
|
||||
import DataFlowNodes::Private
|
||||
@@ -73,9 +74,14 @@ private predicate variableCaptureStep(Node node1, ExprNode node2) {
|
||||
* variable capture.
|
||||
*/
|
||||
predicate jumpStep(Node node1, Node node2) {
|
||||
staticFieldStep(node1, node2) or
|
||||
variableCaptureStep(node1, node2) or
|
||||
staticFieldStep(node1, node2)
|
||||
or
|
||||
variableCaptureStep(node1, node2)
|
||||
or
|
||||
variableCaptureStep(node1.(PostUpdateNode).getPreUpdateNode(), node2)
|
||||
or
|
||||
any(AdditionalValueStep a).step(node1, node2) and
|
||||
node1.getEnclosingCallable() != node2.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -158,6 +158,10 @@ predicate simpleLocalFlowStep(Node node1, Node node2) {
|
||||
)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(node1, node2, true)
|
||||
or
|
||||
any(AdditionalValueStep a).step(node1, node2) and
|
||||
pragma[only_bind_out](node1.getEnclosingCallable()) =
|
||||
pragma[only_bind_out](node2.getEnclosingCallable())
|
||||
}
|
||||
|
||||
private newtype TContent =
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/** Provides classes and predicates to reason about `AsyncTask`s in Android. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.FlowSteps
|
||||
|
||||
/**
|
||||
* Models the value-preserving step from `asyncTask.execute(params)` to `AsyncTask::doInBackground(params)`.
|
||||
*/
|
||||
private class AsyncTaskAdditionalValueStep extends AdditionalValueStep {
|
||||
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(ExecuteAsyncTaskMethodAccess ma, AsyncTaskRunInBackgroundMethod m |
|
||||
DataFlow::getInstanceArgument(ma).getType() = m.getDeclaringType() and
|
||||
node1.asExpr() = ma.getParamsArgument() and
|
||||
node2.asParameter() = m.getParameter(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Android class `android.os.AsyncTask`.
|
||||
*/
|
||||
private class AsyncTask extends RefType {
|
||||
AsyncTask() { this.hasQualifiedName("android.os", "AsyncTask") }
|
||||
}
|
||||
|
||||
/** A call to the `execute` or `executeOnExecutor` methods of the `android.os.AsyncTask` class. */
|
||||
private class ExecuteAsyncTaskMethodAccess extends MethodAccess {
|
||||
Argument paramsArgument;
|
||||
|
||||
ExecuteAsyncTaskMethodAccess() {
|
||||
exists(Method m |
|
||||
this.getMethod() = m and
|
||||
m.getDeclaringType().getSourceDeclaration().getASourceSupertype*() instanceof AsyncTask
|
||||
|
|
||||
m.getName() = "execute" and not m.isStatic() and paramsArgument = this.getArgument(0)
|
||||
or
|
||||
m.getName() = "executeOnExecutor" and paramsArgument = this.getArgument(1)
|
||||
)
|
||||
}
|
||||
|
||||
/** Returns the `params` argument of this call. */
|
||||
Argument getParamsArgument() { result = paramsArgument }
|
||||
}
|
||||
|
||||
/** The `doInBackground` method of the `android.os.AsyncTask` class. */
|
||||
private class AsyncTaskRunInBackgroundMethod extends Method {
|
||||
AsyncTaskRunInBackgroundMethod() {
|
||||
this.getDeclaringType().getSourceDeclaration().getASourceSupertype*() instanceof AsyncTask and
|
||||
this.getName() = "doInBackground"
|
||||
}
|
||||
}
|
||||
34
java/ql/lib/semmle/code/java/frameworks/apache/IO.qll
Normal file
34
java/ql/lib/semmle/code/java/frameworks/apache/IO.qll
Normal file
@@ -0,0 +1,34 @@
|
||||
/** Definitions related to the Apache Commons IO library. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
private class CommonsIOSummaryCsv extends SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
"org.apache.commons.io;IOUtils;false;buffer;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.io;IOUtils;false;copy;;;Argument[0];Argument[1];taint",
|
||||
"org.apache.commons.io;IOUtils;false;copyLarge;;;Argument[0];Argument[1];taint",
|
||||
"org.apache.commons.io;IOUtils;false;read;;;Argument[0];Argument[1];taint",
|
||||
"org.apache.commons.io;IOUtils;false;readFully;(InputStream,byte[],int,int);;Argument[0];Argument[1];taint",
|
||||
"org.apache.commons.io;IOUtils;false;readFully;(InputStream,byte[]);;Argument[0];Argument[1];taint",
|
||||
"org.apache.commons.io;IOUtils;false;readFully;(InputStream,ByteBuffer);;Argument[0];Argument[1];taint",
|
||||
"org.apache.commons.io;IOUtils;false;readFully;(InputStream,int);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.io;IOUtils;false;readFully;(ReadableByteChannel,ByteBuffer);;Argument[0];Argument[1];taint",
|
||||
"org.apache.commons.io;IOUtils;false;readFully;(Reader,char[],int,int);;Argument[0];Argument[1];taint",
|
||||
"org.apache.commons.io;IOUtils;false;readFully;(Reader,char[]);;Argument[0];Argument[1];taint",
|
||||
"org.apache.commons.io;IOUtils;false;readLines;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.io;IOUtils;false;toBufferedInputStream;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.io;IOUtils;false;toBufferedReader;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.io;IOUtils;false;toByteArray;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.io;IOUtils;false;toCharArray;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.io;IOUtils;false;toInputStream;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.io;IOUtils;false;toString;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.io;IOUtils;false;write;;;Argument[0];Argument[1];taint",
|
||||
"org.apache.commons.io;IOUtils;false;writeChunked;;;Argument[0];Argument[1];taint",
|
||||
"org.apache.commons.io;IOUtils;false;writeLines;;;Argument[0];Argument[2];taint",
|
||||
"org.apache.commons.io;IOUtils;false;writeLines;;;Argument[1];Argument[2];taint"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,17 @@ edges
|
||||
| FileService.java:21:28:21:64 | getStringExtra(...) : Object | FileService.java:25:42:25:50 | localPath : Object |
|
||||
| FileService.java:25:13:25:51 | makeParamsToExecute(...) : Object[] | FileService.java:40:41:40:55 | params : Object[] |
|
||||
| FileService.java:25:13:25:51 | makeParamsToExecute(...) [[]] : Object | FileService.java:25:13:25:51 | makeParamsToExecute(...) : Object[] |
|
||||
| FileService.java:25:13:25:51 | makeParamsToExecute(...) [[]] : Object | FileService.java:40:41:40:55 | params [[]] : Object |
|
||||
| FileService.java:25:42:25:50 | localPath : Object | FileService.java:25:13:25:51 | makeParamsToExecute(...) [[]] : Object |
|
||||
| FileService.java:25:42:25:50 | localPath : Object | FileService.java:32:13:32:28 | sourceUri : Object |
|
||||
| FileService.java:32:13:32:28 | sourceUri : Object | FileService.java:35:17:35:25 | sourceUri : Object |
|
||||
| FileService.java:34:20:36:13 | {...} [[]] : Object | FileService.java:34:20:36:13 | new Object[] [[]] : Object |
|
||||
| FileService.java:35:17:35:25 | sourceUri : Object | FileService.java:34:20:36:13 | {...} [[]] : Object |
|
||||
| FileService.java:40:41:40:55 | params : Object[] | FileService.java:44:33:44:52 | (...)... : Object |
|
||||
| FileService.java:40:41:40:55 | params [[]] : Object | FileService.java:44:44:44:49 | params [[]] : Object |
|
||||
| FileService.java:44:33:44:52 | (...)... : Object | FileService.java:45:53:45:59 | ...[...] |
|
||||
| FileService.java:44:44:44:49 | params [[]] : Object | FileService.java:44:44:44:52 | ...[...] : Object |
|
||||
| FileService.java:44:44:44:52 | ...[...] : Object | FileService.java:44:33:44:52 | (...)... : Object |
|
||||
| LeakFileActivity2.java:15:13:15:18 | intent : Intent | LeakFileActivity2.java:16:26:16:31 | intent : Intent |
|
||||
| LeakFileActivity2.java:16:26:16:31 | intent : Intent | FileService.java:20:31:20:43 | intent : Intent |
|
||||
| LeakFileActivity.java:14:35:14:38 | data : Intent | LeakFileActivity.java:18:40:18:59 | contentIntent : Intent |
|
||||
@@ -30,7 +34,10 @@ nodes
|
||||
| FileService.java:34:20:36:13 | {...} [[]] : Object | semmle.label | {...} [[]] : Object |
|
||||
| FileService.java:35:17:35:25 | sourceUri : Object | semmle.label | sourceUri : Object |
|
||||
| FileService.java:40:41:40:55 | params : Object[] | semmle.label | params : Object[] |
|
||||
| FileService.java:40:41:40:55 | params [[]] : Object | semmle.label | params [[]] : Object |
|
||||
| FileService.java:44:33:44:52 | (...)... : Object | semmle.label | (...)... : Object |
|
||||
| FileService.java:44:44:44:49 | params [[]] : Object | semmle.label | params [[]] : Object |
|
||||
| FileService.java:44:44:44:52 | ...[...] : Object | semmle.label | ...[...] : Object |
|
||||
| FileService.java:45:53:45:59 | ...[...] | semmle.label | ...[...] |
|
||||
| LeakFileActivity2.java:15:13:15:18 | intent : Intent | semmle.label | intent : Intent |
|
||||
| LeakFileActivity2.java:16:26:16:31 | intent : Intent | semmle.label | intent : Intent |
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import android.os.AsyncTask;
|
||||
|
||||
public class Test {
|
||||
|
||||
private static Object source(String kind) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void sink(Object o) {}
|
||||
|
||||
public void test() {
|
||||
TestAsyncTask t = new TestAsyncTask();
|
||||
t.execute(source("execute"));
|
||||
t.executeOnExecutor(null, source("executeOnExecutor"));
|
||||
SafeAsyncTask t2 = new SafeAsyncTask();
|
||||
t2.execute("safe");
|
||||
}
|
||||
|
||||
private class TestAsyncTask extends AsyncTask<Object, Object, Object> {
|
||||
@Override
|
||||
protected Object doInBackground(Object... params) {
|
||||
sink(params); // $ hasValueFlow=execute hasValueFlow=executeOnExecutor
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private class SafeAsyncTask extends AsyncTask<Object, Object, Object> {
|
||||
@Override
|
||||
protected Object doInBackground(Object... params) {
|
||||
sink(params); // Safe
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/google-android-9.0.0
|
||||
@@ -0,0 +1,6 @@
|
||||
import java
|
||||
import TestUtilities.InlineFlowTest
|
||||
|
||||
class AsyncTaskTest extends InlineFlowTest {
|
||||
override TaintTracking::Configuration getTaintFlowConfig() { none() }
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
lgtm,codescanning
|
||||
* The `js/prototype-polluting-assignment` query now flags assignments that may modify
|
||||
the built-in Object prototype where the property name originates from library input.
|
||||
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* The `js/insufficient-key-size` query has been added. It highlights the creation of cryptographic keys with a short key size.
|
||||
2
javascript/change-notes/2021-11-02-session-fixation.md
Normal file
2
javascript/change-notes/2021-11-02-session-fixation.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* The `js/session-fixation` query has been added. It highlights servers that reuse a session after a user has logged in.
|
||||
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* The `js/sensitive-get-query` query has been added. It highlights GET requests that read sensitive information from the query string.
|
||||
@@ -411,7 +411,7 @@ module API {
|
||||
any(Type t).hasUnderlyingType(moduleName, exportName)
|
||||
} or
|
||||
MkSyntheticCallbackArg(DataFlow::Node src, int bound, DataFlow::InvokeNode nd) {
|
||||
trackUseNode(src, true, bound).flowsTo(nd.getCalleeNode())
|
||||
trackUseNode(src, true, bound, "").flowsTo(nd.getCalleeNode())
|
||||
}
|
||||
|
||||
class TDef = MkModuleDef or TNonModuleDef;
|
||||
@@ -528,7 +528,7 @@ module API {
|
||||
*/
|
||||
private predicate argumentPassing(TApiNode base, int i, DataFlow::Node arg) {
|
||||
exists(DataFlow::Node use, DataFlow::SourceNode pred, int bound |
|
||||
use(base, use) and pred = trackUseNode(use, _, bound)
|
||||
use(base, use) and pred = trackUseNode(use, _, bound, "")
|
||||
|
|
||||
arg = pred.getAnInvocation().getArgument(i - bound)
|
||||
or
|
||||
@@ -556,6 +556,32 @@ module API {
|
||||
nd = MkDef(rhs)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ref` is a read of a property described by `lbl` on `pred`, and
|
||||
* `propDesc` is compatible with that property, meaning it is either the
|
||||
* name of the property itself or the empty string.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate propertyRead(
|
||||
DataFlow::SourceNode pred, string propDesc, string lbl, DataFlow::Node ref
|
||||
) {
|
||||
ref = pred.getAPropertyRead() and
|
||||
lbl = Label::memberFromRef(ref) and
|
||||
(
|
||||
lbl = Label::member(propDesc)
|
||||
or
|
||||
propDesc = ""
|
||||
)
|
||||
or
|
||||
PromiseFlow::loadStep(pred.getALocalUse(), ref, Promises::valueProp()) and
|
||||
lbl = Label::promised() and
|
||||
(propDesc = Promises::valueProp() or propDesc = "")
|
||||
or
|
||||
PromiseFlow::loadStep(pred.getALocalUse(), ref, Promises::errorProp()) and
|
||||
lbl = Label::promisedError() and
|
||||
(propDesc = Promises::errorProp() or propDesc = "")
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ref` is a use of a node that should have an incoming edge from `base` labeled
|
||||
* `lbl` in the API graph.
|
||||
@@ -567,26 +593,25 @@ module API {
|
||||
base = MkRoot() and
|
||||
ref = lbl.(EntryPoint).getAUse()
|
||||
or
|
||||
// property reads
|
||||
exists(DataFlow::SourceNode src, DataFlow::SourceNode pred, string propDesc |
|
||||
use(base, src) and
|
||||
pred = trackUseNode(src, false, 0, propDesc) and
|
||||
propertyRead(pred, propDesc, lbl, ref) and
|
||||
// `module.exports` is special: it is a use of a def-node, not a use-node,
|
||||
// so we want to exclude it here
|
||||
(base instanceof TNonModuleDef or base instanceof TUse)
|
||||
)
|
||||
or
|
||||
// invocations
|
||||
exists(DataFlow::SourceNode src, DataFlow::SourceNode pred |
|
||||
use(base, src) and pred = trackUseNode(src)
|
||||
|
|
||||
// `module.exports` is special: it is a use of a def-node, not a use-node,
|
||||
// so we want to exclude it here
|
||||
(base instanceof TNonModuleDef or base instanceof TUse) and
|
||||
lbl = Label::memberFromRef(ref) and
|
||||
ref = pred.getAPropertyRead()
|
||||
or
|
||||
lbl = Label::instance() and
|
||||
ref = pred.getAnInstantiation()
|
||||
or
|
||||
lbl = Label::return() and
|
||||
ref = pred.getAnInvocation()
|
||||
or
|
||||
lbl = Label::promised() and
|
||||
PromiseFlow::loadStep(pred.getALocalUse(), ref, Promises::valueProp())
|
||||
or
|
||||
lbl = Label::promisedError() and
|
||||
PromiseFlow::loadStep(pred.getALocalUse(), ref, Promises::errorProp())
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Node def, DataFlow::FunctionNode fn |
|
||||
@@ -680,36 +705,58 @@ module API {
|
||||
)
|
||||
}
|
||||
|
||||
private import semmle.javascript.dataflow.TypeTracking
|
||||
|
||||
/**
|
||||
* Gets a data-flow node to which `nd`, which is a use of an API-graph node, flows.
|
||||
*
|
||||
* The flow from `nd` to that node may be inter-procedural. If `promisified` is `true`, the
|
||||
* flow goes through a promisification, and `boundArgs` indicates how many arguments have been
|
||||
* bound throughout the flow. (To ensure termination, we somewhat arbitrarily constrain the
|
||||
* number of bound arguments to be at most ten.)
|
||||
* The flow from `nd` to that node may be inter-procedural, and is further described by three
|
||||
* flags:
|
||||
*
|
||||
* - `promisified`: if true `true`, the flow goes through a promisification;
|
||||
* - `boundArgs`: for function values, tracks how many arguments have been bound throughout
|
||||
* the flow. To ensure termination, we somewhat arbitrarily constrain the number of bound
|
||||
* arguments to be at most ten.
|
||||
* - `prop`: if non-empty, the flow is only guaranteed to preserve the value of this property,
|
||||
* and not necessarily the entire object.
|
||||
*/
|
||||
private DataFlow::SourceNode trackUseNode(
|
||||
DataFlow::SourceNode nd, boolean promisified, int boundArgs, DataFlow::TypeTracker t
|
||||
DataFlow::SourceNode nd, boolean promisified, int boundArgs, string prop,
|
||||
DataFlow::TypeTracker t
|
||||
) {
|
||||
t.start() and
|
||||
use(_, nd) and
|
||||
result = nd and
|
||||
promisified = false and
|
||||
boundArgs = 0
|
||||
boundArgs = 0 and
|
||||
prop = ""
|
||||
or
|
||||
exists(Promisify::PromisifyCall promisify |
|
||||
trackUseNode(nd, false, boundArgs, t.continue()).flowsTo(promisify.getArgument(0)) and
|
||||
trackUseNode(nd, false, boundArgs, prop, t.continue()).flowsTo(promisify.getArgument(0)) and
|
||||
promisified = true and
|
||||
prop = "" and
|
||||
result = promisify
|
||||
)
|
||||
or
|
||||
exists(DataFlow::PartialInvokeNode pin, DataFlow::Node pred, int predBoundArgs |
|
||||
trackUseNode(nd, promisified, predBoundArgs, t.continue()).flowsTo(pred) and
|
||||
trackUseNode(nd, promisified, predBoundArgs, prop, t.continue()).flowsTo(pred) and
|
||||
prop = "" and
|
||||
result = pin.getBoundFunction(pred, boundArgs - predBoundArgs) and
|
||||
boundArgs in [0 .. 10]
|
||||
)
|
||||
or
|
||||
t = useStep(nd, promisified, boundArgs, result)
|
||||
exists(DataFlow::Node pred, string preprop |
|
||||
trackUseNode(nd, promisified, boundArgs, preprop, t.continue()).flowsTo(pred) and
|
||||
promisified = false and
|
||||
boundArgs = 0 and
|
||||
SharedTypeTrackingStep::loadStoreStep(pred, result, prop)
|
||||
|
|
||||
prop = preprop
|
||||
or
|
||||
preprop = ""
|
||||
)
|
||||
or
|
||||
t = useStep(nd, promisified, boundArgs, prop, result)
|
||||
}
|
||||
|
||||
private import semmle.javascript.dataflow.internal.StepSummary
|
||||
@@ -723,19 +770,19 @@ module API {
|
||||
*/
|
||||
pragma[noopt]
|
||||
private DataFlow::TypeTracker useStep(
|
||||
DataFlow::Node nd, boolean promisified, int boundArgs, DataFlow::Node res
|
||||
DataFlow::Node nd, boolean promisified, int boundArgs, string prop, DataFlow::Node res
|
||||
) {
|
||||
exists(DataFlow::TypeTracker t, StepSummary summary, DataFlow::SourceNode prev |
|
||||
prev = trackUseNode(nd, promisified, boundArgs, t) and
|
||||
prev = trackUseNode(nd, promisified, boundArgs, prop, t) and
|
||||
StepSummary::step(prev, res, summary) and
|
||||
result = t.append(summary)
|
||||
)
|
||||
}
|
||||
|
||||
private DataFlow::SourceNode trackUseNode(
|
||||
DataFlow::SourceNode nd, boolean promisified, int boundArgs
|
||||
DataFlow::SourceNode nd, boolean promisified, int boundArgs, string prop
|
||||
) {
|
||||
result = trackUseNode(nd, promisified, boundArgs, DataFlow::TypeTracker::end())
|
||||
result = trackUseNode(nd, promisified, boundArgs, prop, DataFlow::TypeTracker::end())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -743,7 +790,7 @@ module API {
|
||||
*/
|
||||
cached
|
||||
DataFlow::SourceNode trackUseNode(DataFlow::SourceNode nd) {
|
||||
result = trackUseNode(nd, false, 0)
|
||||
result = trackUseNode(nd, false, 0, "")
|
||||
}
|
||||
|
||||
private DataFlow::SourceNode trackDefNode(DataFlow::Node nd, DataFlow::TypeBackTracker t) {
|
||||
|
||||
@@ -426,6 +426,17 @@ module AccessPath {
|
||||
result = AccessPath::getAReferenceTo(root, accessPath)
|
||||
)
|
||||
or
|
||||
// step over extend calls. Handle aliasing both ways through the extend call.
|
||||
exists(
|
||||
DataFlow::SourceNode rootOne, DataFlow::SourceNode rootTwo, string accessPath,
|
||||
ExtendCall extendCall
|
||||
|
|
||||
rootOne = [extendCall, extendCall.getAnOperand().getALocalSource()] and
|
||||
rootTwo = [extendCall, extendCall.getAnOperand().getALocalSource()] and
|
||||
node = pragma[only_bind_into](AccessPath::getAReferenceTo(rootOne, accessPath)) and
|
||||
result = AccessPath::getAReferenceTo(rootTwo, accessPath)
|
||||
)
|
||||
or
|
||||
result = node.getALocalSource()
|
||||
}
|
||||
|
||||
|
||||
@@ -11,11 +11,36 @@ private import semmle.javascript.internal.CachedStages
|
||||
* Gets a parameter that is a library input to a top-level package.
|
||||
*/
|
||||
cached
|
||||
DataFlow::ParameterNode getALibraryInputParameter() {
|
||||
DataFlow::SourceNode getALibraryInputParameter() {
|
||||
Stages::Taint::ref() and
|
||||
exists(int bound, DataFlow::FunctionNode func |
|
||||
func = getAValueExportedByPackage().getABoundFunctionValue(bound) and
|
||||
func = getAValueExportedByPackage().getABoundFunctionValue(bound)
|
||||
|
|
||||
result = func.getParameter(any(int arg | arg >= bound))
|
||||
or
|
||||
result = getAnArgumentsRead(func.getFunction())
|
||||
)
|
||||
}
|
||||
|
||||
private DataFlow::SourceNode getAnArgumentsRead(Function func) {
|
||||
exists(DataFlow::PropRead read |
|
||||
not read.getPropertyName() = "length" and
|
||||
result = read
|
||||
|
|
||||
read.getBase() = func.getArgumentsVariable().getAnAccess().flow()
|
||||
or
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call =
|
||||
DataFlow::globalVarRef("Array")
|
||||
.getAPropertyRead("prototype")
|
||||
.getAPropertyRead("slice")
|
||||
.getAMethodCall("call")
|
||||
or
|
||||
call = DataFlow::globalVarRef("Array").getAMethodCall("from")
|
||||
|
|
||||
call.getArgument(0) = func.getArgumentsVariable().getAnAccess().flow() and
|
||||
call.flowsTo(read.getBase())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -425,6 +425,14 @@ module PromiseFlow {
|
||||
prop = errorProp() and
|
||||
pred = call.getCallback(0).getAReturn()
|
||||
)
|
||||
or
|
||||
// return from `async` function
|
||||
exists(DataFlow::FunctionNode f | f.getFunction().isAsync() |
|
||||
// ordinary return
|
||||
prop = valueProp() and
|
||||
pred = f.getAReturn() and
|
||||
succ = f.getReturnNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +79,12 @@ module CallGraph {
|
||||
cls.getAClassReference(t.continue()) = result
|
||||
)
|
||||
or
|
||||
exists(DataFlow::ObjectLiteralNode object, string prop |
|
||||
function = object.getAPropertySource(prop) and
|
||||
result = getAnObjectLiteralRef(object).getAPropertyRead(prop) and
|
||||
t.start()
|
||||
)
|
||||
or
|
||||
exists(DataFlow::FunctionNode outer |
|
||||
result = getAFunctionReference(outer, 0, t.continue()).getAnInvocation() and
|
||||
locallyReturnedFunction(outer, function)
|
||||
@@ -197,11 +203,39 @@ module CallGraph {
|
||||
)
|
||||
or
|
||||
exists(DataFlow::ObjectLiteralNode object, string name |
|
||||
ref = object.getAPropertyRead(name) and
|
||||
ref = getAnObjectLiteralRef(object).getAPropertyRead(name) and
|
||||
result = object.getPropertyGetter(name)
|
||||
or
|
||||
ref = object.getAPropertyWrite(name) and
|
||||
ref = getAnObjectLiteralRef(object).getAPropertyWrite(name) and
|
||||
result = object.getPropertySetter(name)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate shouldTrackObjectLiteral(DataFlow::ObjectLiteralNode node) {
|
||||
(
|
||||
node.getAPropertySource() instanceof DataFlow::FunctionNode
|
||||
or
|
||||
exists(node.getPropertyGetter(_))
|
||||
or
|
||||
exists(node.getPropertySetter(_))
|
||||
) and
|
||||
not node.getTopLevel().isExterns()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a step summary for tracking object literals.
|
||||
*
|
||||
* To avoid false flow from callbacks passed in via "named parameters", we only track object
|
||||
* literals out of returns, not into calls.
|
||||
*/
|
||||
private StepSummary objectLiteralStep() { result = LevelStep() or result = ReturnStep() }
|
||||
|
||||
/** Gets a node that refers to the given object literal, via a limited form of type tracking. */
|
||||
cached
|
||||
DataFlow::SourceNode getAnObjectLiteralRef(DataFlow::ObjectLiteralNode node) {
|
||||
shouldTrackObjectLiteral(node) and
|
||||
result = node
|
||||
or
|
||||
StepSummary::step(getAnObjectLiteralRef(node), result, objectLiteralStep())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,49 @@ private predicate looksLikeExterns(TopLevel tl) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` contains generated or minified code.
|
||||
*/
|
||||
predicate isGeneratedCodeFile(File f) { isGenerated(f.getATopLevel()) }
|
||||
|
||||
/**
|
||||
* Holds if `f` contains test code.
|
||||
*/
|
||||
predicate isTestFile(File f) {
|
||||
exists(Test t | t.getFile() = f)
|
||||
or
|
||||
exists(string stemExt | stemExt = "test" or stemExt = "spec" |
|
||||
f = getTestFile(any(File orig), stemExt)
|
||||
)
|
||||
or
|
||||
f.getAbsolutePath().regexpMatch(".*/__(mocks|tests)__/.*")
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` contains externs declarations.
|
||||
*/
|
||||
predicate isExternsFile(File f) {
|
||||
(f.getATopLevel().isExterns() or looksLikeExterns(f.getATopLevel()))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` contains library code.
|
||||
*/
|
||||
predicate isLibaryFile(File f) { f.getATopLevel() instanceof FrameworkLibraryInstance }
|
||||
|
||||
/**
|
||||
* Holds if `f` contains template code.
|
||||
*/
|
||||
predicate isTemplateFile(File f) {
|
||||
exists(JSParseError err | maybeCausedByTemplate(err) | f = err.getFile())
|
||||
or
|
||||
// Polymer templates
|
||||
exists(HTML::Element elt | elt.getName() = "template" |
|
||||
f = elt.getFile() and
|
||||
not f.getExtension() = "vue"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is classified as belonging to `category`.
|
||||
*
|
||||
@@ -55,33 +98,15 @@ private predicate looksLikeExterns(TopLevel tl) {
|
||||
* - `"library"`: `f` contains library code;
|
||||
* - `"template"`: `f` contains template code.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate classify(File f, string category) {
|
||||
isGenerated(f.getATopLevel()) and category = "generated"
|
||||
isGeneratedCodeFile(f) and category = "generated"
|
||||
or
|
||||
(
|
||||
exists(Test t | t.getFile() = f)
|
||||
or
|
||||
exists(string stemExt | stemExt = "test" or stemExt = "spec" |
|
||||
f = getTestFile(any(File orig), stemExt)
|
||||
)
|
||||
or
|
||||
f.getAbsolutePath().regexpMatch(".*/__(mocks|tests)__/.*")
|
||||
) and
|
||||
category = "test"
|
||||
isTestFile(f) and category = "test"
|
||||
or
|
||||
(f.getATopLevel().isExterns() or looksLikeExterns(f.getATopLevel())) and
|
||||
category = "externs"
|
||||
isExternsFile(f) and category = "externs"
|
||||
or
|
||||
f.getATopLevel() instanceof FrameworkLibraryInstance and category = "library"
|
||||
isLibaryFile(f) and category = "library"
|
||||
or
|
||||
exists(JSParseError err | maybeCausedByTemplate(err) |
|
||||
f = err.getFile() and category = "template"
|
||||
)
|
||||
or
|
||||
// Polymer templates
|
||||
exists(HTML::Element elt | elt.getName() = "template" |
|
||||
f = elt.getFile() and
|
||||
category = "template" and
|
||||
not f.getExtension() = "vue"
|
||||
)
|
||||
isTemplateFile(f) and category = "template"
|
||||
}
|
||||
|
||||
@@ -406,12 +406,9 @@ module ClientRequest {
|
||||
*/
|
||||
class GotUrlRequest extends ClientRequest::Range {
|
||||
GotUrlRequest() {
|
||||
exists(string moduleName, DataFlow::SourceNode callee | this = callee.getACall() |
|
||||
moduleName = "got" and
|
||||
(
|
||||
callee = DataFlow::moduleImport(moduleName) or
|
||||
callee = DataFlow::moduleMember(moduleName, "stream")
|
||||
)
|
||||
exists(API::Node callee, API::Node got | this = callee.getACall() |
|
||||
got = [API::moduleImport("got"), API::moduleImport("got").getMember("extend").getReturn()] and
|
||||
callee = [got, got.getMember(["stream", "get", "post", "put", "patch", "head", "delete"])]
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,26 @@ abstract class CryptographicOperation extends Expr {
|
||||
*/
|
||||
abstract class CryptographicKey extends DataFlow::ValueNode { }
|
||||
|
||||
/**
|
||||
* The creation of a cryptographic key.
|
||||
*/
|
||||
abstract class CryptographicKeyCreation extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the algorithm used to create the key.
|
||||
*/
|
||||
abstract CryptographicAlgorithm getAlgorithm();
|
||||
|
||||
/**
|
||||
* Gets the size of the key.
|
||||
*/
|
||||
abstract int getSize();
|
||||
|
||||
/**
|
||||
* Gets whether the key is symmetric.
|
||||
*/
|
||||
abstract predicate isSymmetricKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* A key used in a cryptographic algorithm, viewed as a `CredentialsExpr`.
|
||||
*/
|
||||
@@ -141,14 +161,9 @@ private module NodeJSCrypto {
|
||||
* Also matches `createHash`, `createHmac`, `createSign` instead of `createCipher`.
|
||||
*/
|
||||
|
||||
exists(DataFlow::SourceNode mod, string createSuffix |
|
||||
createSuffix = "Hash" or
|
||||
createSuffix = "Hmac" or
|
||||
createSuffix = "Sign" or
|
||||
createSuffix = "Cipher"
|
||||
|
|
||||
exists(DataFlow::SourceNode mod |
|
||||
mod = DataFlow::moduleImport("crypto") and
|
||||
this = mod.getAMemberCall("create" + createSuffix) and
|
||||
this = mod.getAMemberCall("create" + ["Hash", "Hmac", "Sign", "Cipher"]) and
|
||||
algorithm.matchesName(getArgument(0).getStringValue())
|
||||
)
|
||||
}
|
||||
@@ -156,6 +171,52 @@ private module NodeJSCrypto {
|
||||
CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
||||
}
|
||||
|
||||
private class CreateKey extends CryptographicKeyCreation, DataFlow::CallNode {
|
||||
boolean symmetric;
|
||||
|
||||
CreateKey() {
|
||||
// crypto.generateKey(type, options, callback)
|
||||
// crypto.generateKeyPair(type, options, callback)
|
||||
// crypto.generateKeyPairSync(type, options)
|
||||
// crypto.generateKeySync(type, options)
|
||||
exists(DataFlow::SourceNode mod, string keyType |
|
||||
keyType = "Key" and symmetric = true
|
||||
or
|
||||
keyType = "KeyPair" and symmetric = false
|
||||
|
|
||||
mod = DataFlow::moduleImport("crypto") and
|
||||
this = mod.getAMemberCall("generate" + keyType + ["", "Sync"])
|
||||
)
|
||||
}
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() {
|
||||
result.matchesName(getArgument(0).getStringValue())
|
||||
}
|
||||
|
||||
override int getSize() {
|
||||
symmetric = true and
|
||||
result = getOptionArgument(1, "length").getIntValue()
|
||||
or
|
||||
symmetric = false and
|
||||
result = getOptionArgument(1, "modulusLength").getIntValue()
|
||||
}
|
||||
|
||||
override predicate isSymmetricKey() { symmetric = true }
|
||||
}
|
||||
|
||||
private class CreateDiffieHellmanKey extends CryptographicKeyCreation, DataFlow::CallNode {
|
||||
// require("crypto").createDiffieHellman(prime_length);
|
||||
CreateDiffieHellmanKey() {
|
||||
this = DataFlow::moduleMember("crypto", "createDiffieHellman").getACall()
|
||||
}
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() { none() }
|
||||
|
||||
override int getSize() { result = getArgument(0).getIntValue() }
|
||||
|
||||
override predicate isSymmetricKey() { none() }
|
||||
}
|
||||
|
||||
private class Apply extends CryptographicOperation, MethodCallExpr {
|
||||
InstantiatedAlgorithm instantiation;
|
||||
|
||||
@@ -282,6 +343,35 @@ private module CryptoJS {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class CreateKey extends CryptographicKeyCreation, DataFlow::CallNode {
|
||||
string algorithm;
|
||||
int optionArg;
|
||||
|
||||
CreateKey() {
|
||||
// var key = CryptoJS.PBKDF2(password, salt, { keySize: 8 });
|
||||
this =
|
||||
getAlgorithmExpr(any(CryptographicAlgorithm algo | algo.getName() = algorithm)).getACall() and
|
||||
optionArg = 2
|
||||
or
|
||||
// var key = CryptoJS.algo.PBKDF2.create({ keySize: 8 });
|
||||
this =
|
||||
DataFlow::moduleMember("crypto-js", "algo")
|
||||
.getAPropertyRead(algorithm)
|
||||
.getAMethodCall("create") and
|
||||
optionArg = 0
|
||||
}
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() { result.matchesName(algorithm) }
|
||||
|
||||
override int getSize() {
|
||||
result = getOptionArgument(optionArg, "keySize").getIntValue() * 32 // size is in words
|
||||
or
|
||||
result = getArgument(optionArg).getIntValue() * 32 // size is in words
|
||||
}
|
||||
|
||||
override predicate isSymmetricKey() { any() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -467,6 +557,39 @@ private module Forge {
|
||||
private class Key extends CryptographicKey {
|
||||
Key() { this = any(KeyCipher cipher).getKey() }
|
||||
}
|
||||
|
||||
private class CreateKey extends CryptographicKeyCreation, DataFlow::CallNode {
|
||||
CryptographicAlgorithm algorithm;
|
||||
|
||||
CreateKey() {
|
||||
// var cipher = forge.rc2.createEncryptionCipher(key, 128);
|
||||
this =
|
||||
getAnImportNode()
|
||||
.getAPropertyRead(any(string s | algorithm.matchesName(s)))
|
||||
.getAMemberCall("createEncryptionCipher")
|
||||
or
|
||||
// var key = forge.random.getBytesSync(16);
|
||||
// var cipher = forge.cipher.createCipher('AES-CBC', key);
|
||||
this =
|
||||
getAnImportNode()
|
||||
.getAPropertyRead("cipher")
|
||||
.getAMemberCall(["createCipher", "createDecipher"]) and
|
||||
algorithm.matchesName(this.getArgument(0).getStringValue())
|
||||
}
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
||||
|
||||
override int getSize() {
|
||||
result = this.getArgument(1).getIntValue()
|
||||
or
|
||||
exists(DataFlow::CallNode call | call.getCalleeName() = ["getBytes", "getBytesSync"] |
|
||||
getArgument(1).getALocalSource() = call and
|
||||
result = call.getArgument(0).getIntValue() * 8 // bytes to bits
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSymmetricKey() { any() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -556,13 +679,38 @@ private module Hasha {
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes for working with the `express-jwt` package (https://github.com/auth0/express-jwt);
|
||||
*/
|
||||
module ExpressJwt {
|
||||
private class Key extends CryptographicKey {
|
||||
Key() { this = DataFlow::moduleMember("express-jwt", "sign").getACall().getArgument(1) }
|
||||
}
|
||||
/**
|
||||
* Provides classes for working with the `express-jwt` package (https://github.com/auth0/express-jwt);
|
||||
*/
|
||||
private module ExpressJwt {
|
||||
private class Key extends CryptographicKey {
|
||||
Key() { this = DataFlow::moduleMember("express-jwt", "sign").getACall().getArgument(1) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes for working with the `node-rsa` package (https://www.npmjs.com/package/node-rsa)
|
||||
*/
|
||||
private module NodeRsa {
|
||||
private class CreateKey extends CryptographicKeyCreation, API::InvokeNode {
|
||||
CryptographicAlgorithm algorithm;
|
||||
|
||||
CreateKey() {
|
||||
this = API::moduleImport("node-rsa").getAnInstantiation()
|
||||
or
|
||||
this = API::moduleImport("node-rsa").getInstance().getMember("generateKeyPair").getACall()
|
||||
}
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() { result.matchesName("rsa") }
|
||||
|
||||
override int getSize() {
|
||||
result = this.getArgument(0).getIntValue()
|
||||
or
|
||||
result = this.getOptionArgument(0, "b").getIntValue()
|
||||
}
|
||||
|
||||
override predicate isSymmetricKey() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ private module AlgorithmNames {
|
||||
name = ["ARGON2", "PBKDF2", "BCRYPT", "SCRYPT"]
|
||||
}
|
||||
|
||||
predicate isWeakPasswordHashingAlgorithm(string name) { none() }
|
||||
predicate isWeakPasswordHashingAlgorithm(string name) { name = "EVPKDF" }
|
||||
}
|
||||
|
||||
private import AlgorithmNames
|
||||
@@ -85,11 +85,13 @@ abstract class CryptographicAlgorithm extends TCryptographicAlgorithm {
|
||||
|
||||
/**
|
||||
* Holds if the name of this algorithm matches `name` modulo case,
|
||||
* white space, dashes, and underscores.
|
||||
* white space, dashes, underscores, and anything after a dash in the name
|
||||
* (to ignore modes of operation, such as CBC or ECB).
|
||||
*/
|
||||
bindingset[name]
|
||||
predicate matchesName(string name) {
|
||||
name.toUpperCase().regexpReplaceAll("[-_ ]", "") = getName()
|
||||
[name.toUpperCase(), name.toUpperCase().regexpCapture("^(\\w+)(?:-.*)?$", 1)]
|
||||
.regexpReplaceAll("[-_ ]", "") = getName()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,7 +13,12 @@ module PrototypePollutingAssignment {
|
||||
/**
|
||||
* A data flow source for untrusted data from which the special `__proto__` property name may be arise.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
abstract class Source extends DataFlow::Node {
|
||||
/**
|
||||
* Gets a string that describes the type of source.
|
||||
*/
|
||||
abstract string describe();
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow sink for prototype-polluting assignments or untrusted property names.
|
||||
@@ -44,6 +49,8 @@ module PrototypePollutingAssignment {
|
||||
this = any(DataFlow::PropWrite write).getBase()
|
||||
or
|
||||
this = any(ExtendCall c).getDestinationOperand()
|
||||
or
|
||||
this = any(DeleteExpr del).getOperand().flow().(DataFlow::PropRef).getBase()
|
||||
}
|
||||
|
||||
override DataFlow::FlowLabel getAFlowLabel() { result instanceof ObjectPrototype }
|
||||
@@ -52,5 +59,18 @@ module PrototypePollutingAssignment {
|
||||
/** A remote flow source or location.{hash,search} as a taint source. */
|
||||
private class DefaultSource extends Source {
|
||||
DefaultSource() { this instanceof RemoteFlowSource }
|
||||
|
||||
override string describe() { result = "user controlled input" }
|
||||
}
|
||||
|
||||
import semmle.javascript.PackageExports as Exports
|
||||
|
||||
/**
|
||||
* A parameter of an exported function, seen as a source prototype-polluting assignment.
|
||||
*/
|
||||
class ExternalInputSource extends Source, DataFlow::SourceNode {
|
||||
ExternalInputSource() { this = Exports::getALibraryInputParameter() }
|
||||
|
||||
override string describe() { result = "library input" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
private import javascript
|
||||
private import semmle.javascript.DynamicPropertyAccess
|
||||
private import semmle.javascript.dataflow.InferredTypes
|
||||
private import PrototypePollutingAssignmentCustomizations::PrototypePollutingAssignment
|
||||
import PrototypePollutingAssignmentCustomizations::PrototypePollutingAssignment
|
||||
private import semmle.javascript.filters.ClassifyFiles as ClassifyFiles
|
||||
|
||||
// Materialize flow labels
|
||||
private class ConcreteObjectPrototype extends ObjectPrototype {
|
||||
@@ -31,7 +32,10 @@ class Configuration extends TaintTracking::Configuration {
|
||||
node instanceof Sanitizer
|
||||
or
|
||||
// Concatenating with a string will in practice prevent the string `__proto__` from arising.
|
||||
node instanceof StringOps::ConcatenationRoot
|
||||
exists(StringOps::ConcatenationRoot root | node = root |
|
||||
// Exclude the string coercion `"" + node` from this filter.
|
||||
not node.(StringOps::ConcatenationNode).isCoercion()
|
||||
)
|
||||
or
|
||||
node instanceof DataFlow::ThisNode
|
||||
or
|
||||
@@ -79,6 +83,29 @@ class Configuration extends TaintTracking::Configuration {
|
||||
inlbl.isTaint() and
|
||||
outlbl instanceof ObjectPrototype
|
||||
)
|
||||
or
|
||||
DataFlow::localFieldStep(pred, succ) and inlbl = outlbl
|
||||
}
|
||||
|
||||
override predicate hasFlowPath(DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink) {
|
||||
super.hasFlowPath(source, sink) and
|
||||
// require that there is a path without unmatched return steps
|
||||
DataFlow::hasPathWithoutUnmatchedReturn(source, sink) and
|
||||
// filter away paths that start with library inputs and end with a write to a fixed property.
|
||||
not exists(ExternalInputSource src, Sink snk, DataFlow::PropWrite write |
|
||||
source.getNode() = src and sink.getNode() = snk
|
||||
|
|
||||
snk = write.getBase() and
|
||||
(
|
||||
// fixed property name
|
||||
exists(write.getPropertyName())
|
||||
or
|
||||
// non-string property name (likely number)
|
||||
exists(Expr prop | prop = write.getPropertyNameExpr() |
|
||||
not prop.analyze().getAType() = TTString()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isLabeledBarrier(DataFlow::Node node, DataFlow::FlowLabel lbl) {
|
||||
@@ -95,7 +122,8 @@ class Configuration extends TaintTracking::Configuration {
|
||||
guard instanceof InstanceofCheck or
|
||||
guard instanceof IsArrayCheck or
|
||||
guard instanceof TypeofCheck or
|
||||
guard instanceof EqualityCheck
|
||||
guard instanceof EqualityCheck or
|
||||
guard instanceof IncludesCheck
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +136,8 @@ private DataFlow::SourceNode prototypeLessObject(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
// We assume the argument to Object.create is not Object.prototype, since most
|
||||
// users wouldn't bother to call Object.create in that case.
|
||||
result = DataFlow::globalVarRef("Object").getAMemberCall("create")
|
||||
result = DataFlow::globalVarRef("Object").getAMemberCall("create") and
|
||||
not result.getFile() instanceof TestFile
|
||||
or
|
||||
// Allow use of SharedFlowSteps to track a bit further
|
||||
exists(DataFlow::Node mid |
|
||||
@@ -119,6 +148,14 @@ private DataFlow::SourceNode prototypeLessObject(DataFlow::TypeTracker t) {
|
||||
exists(DataFlow::TypeTracker t2 | result = prototypeLessObject(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
* A test file.
|
||||
* Objects created in such files are ignored in the `prototypeLessObject` predicate.
|
||||
*/
|
||||
private class TestFile extends File {
|
||||
TestFile() { ClassifyFiles::isTestFile(this) }
|
||||
}
|
||||
|
||||
/** Holds if `Object.prototype` has a member named `prop`. */
|
||||
private predicate isPropertyPresentOnObjectPrototype(string prop) {
|
||||
exists(ExternalInstanceMemberDecl decl |
|
||||
@@ -215,3 +252,15 @@ private class EqualityCheck extends TaintTracking::SanitizerGuardNode, DataFlow:
|
||||
outcome = astNode.getPolarity().booleanNot()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizer guard of the form `x.includes("__proto__")`.
|
||||
*/
|
||||
private class IncludesCheck extends TaintTracking::LabeledSanitizerGuardNode, InclusionTest {
|
||||
IncludesCheck() { this.getContainedNode().mayHaveStringValue("__proto__") }
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) {
|
||||
e = getContainerNode().asExpr() and
|
||||
outcome = getPolarity().booleanNot()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,14 +50,14 @@ module UnsafeShellCommandConstruction {
|
||||
/**
|
||||
* A parameter of an exported function, seen as a source for shell command constructed from library input.
|
||||
*/
|
||||
class ExternalInputSource extends Source, DataFlow::ParameterNode {
|
||||
class ExternalInputSource extends Source, DataFlow::SourceNode {
|
||||
ExternalInputSource() {
|
||||
this = Exports::getALibraryInputParameter() and
|
||||
not (
|
||||
// looks to be on purpose.
|
||||
this.getName() = ["cmd", "command"]
|
||||
this.(DataFlow::ParameterNode).getName() = ["cmd", "command"]
|
||||
or
|
||||
this.getName().regexpMatch(".*(Cmd|Command)$") // ends with "Cmd" or "Command"
|
||||
this.(DataFlow::ParameterNode).getName().regexpMatch(".*(Cmd|Command)$") // ends with "Cmd" or "Command"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ module PolynomialReDoS {
|
||||
/**
|
||||
* A parameter of an exported function, seen as a source for polynomial-redos.
|
||||
*/
|
||||
class ExternalInputSource extends Source, DataFlow::ParameterNode {
|
||||
class ExternalInputSource extends Source, DataFlow::SourceNode {
|
||||
ExternalInputSource() { this = Exports::getALibraryInputParameter() }
|
||||
|
||||
override string getKind() { result = "library" }
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
* external/cwe/cwe-312
|
||||
* external/cwe/cwe-315
|
||||
* external/cwe/cwe-359
|
||||
* external/cwe/cwe-532
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
43
javascript/ql/src/Security/CWE-326/InsufficientKeySize.qhelp
Normal file
43
javascript/ql/src/Security/CWE-326/InsufficientKeySize.qhelp
Normal file
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Modern encryption relies on it being computationally infeasible to break the cipher and decode a message without the key.
|
||||
As computational power increases, the ability to break ciphers grows and keys need to become larger.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
An encryption key should be at least 2048-bit long when using RSA encryption, and 128-bit long when using
|
||||
symmetric encryption.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
Wikipedia:
|
||||
<a href="https://en.wikipedia.org/wiki/RSA_(cryptosystem)">RSA</a>.
|
||||
</li>
|
||||
<li>
|
||||
Wikipedia:
|
||||
<a href="https://en.wikipedia.org/wiki/Advanced_Encryption_Standard">AES</a>.
|
||||
</li>
|
||||
<li>
|
||||
NodeJS:
|
||||
<a href="https://nodejs.org/api/crypto.html">Crypto</a>.
|
||||
</li>
|
||||
<li>
|
||||
NIST:
|
||||
<a href="https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar1.pdf">
|
||||
Recommendation for Transitioning the Use of Cryptographic Algorithms and Key Lengths</a>.
|
||||
</li>
|
||||
<li>
|
||||
Wikipedia:
|
||||
<a href="https://en.wikipedia.org/wiki/Key_size">Key size</a>
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
36
javascript/ql/src/Security/CWE-326/InsufficientKeySize.ql
Normal file
36
javascript/ql/src/Security/CWE-326/InsufficientKeySize.ql
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @name Use of a weak cryptographic key
|
||||
* @description Using a weak cryptographic key can allow an attacker to compromise security.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 7.5
|
||||
* @precision high
|
||||
* @id js/insufficient-key-size
|
||||
* @tags security
|
||||
* external/cwe/cwe-326
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
from CryptographicKeyCreation key, int size, string msg, string algo
|
||||
where
|
||||
size = key.getSize() and
|
||||
(
|
||||
algo = key.getAlgorithm() + " "
|
||||
or
|
||||
not exists(key.getAlgorithm()) and algo = ""
|
||||
) and
|
||||
(
|
||||
size < 128 and
|
||||
key.isSymmetricKey() and
|
||||
msg =
|
||||
"Creation of an symmetric " + algo + "key uses " + size +
|
||||
" bits, which is below 128 and considered breakable."
|
||||
or
|
||||
size < 2048 and
|
||||
not key.isSymmetricKey() and
|
||||
msg =
|
||||
"Creation of an asymmetric " + algo + "key uses " + size +
|
||||
" bits, which is below 2048 and considered breakable."
|
||||
)
|
||||
select key, msg
|
||||
48
javascript/ql/src/Security/CWE-384/SessionFixation.qhelp
Normal file
48
javascript/ql/src/Security/CWE-384/SessionFixation.qhelp
Normal file
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
Reusing a session could allow an attacker to gain unauthorized access to another account. Always
|
||||
ensure that, when a user logs in or out, the current session is abandoned so that a new
|
||||
session may be started.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
Always use <code>req.session.regenerate(...);</code> to start a new session when
|
||||
a user logs in or out.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>
|
||||
The following example shows the previous session being used after authentication.
|
||||
This would allow a previous user to use the new user's account.
|
||||
</p>
|
||||
|
||||
<sample src="examples/SessionFixation.js" />
|
||||
|
||||
<p>
|
||||
This code example solves the problem by not reusing the session, and instead calling <code>req.session.regenerate()</code>
|
||||
to ensure that the session is not reused.
|
||||
</p>
|
||||
<sample src="examples/SessionFixationFixed.js" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
<li>
|
||||
OWASP: <a href="https://www.owasp.org/index.php/Session_fixation">Session fixation</a>
|
||||
</li>
|
||||
<li>
|
||||
Stack Overflow: <a href="https://stackoverflow.com/questions/22209354/creating-a-new-session-after-authentication-with-passport/30468384#30468384">Creating a new session after authentication with Passport</a>
|
||||
</li>
|
||||
<li>
|
||||
jscrambler.com: <a href="https://blog.jscrambler.com/best-practices-for-secure-session-management-in-node">Best practices for secure session management in Node</a>
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
58
javascript/ql/src/Security/CWE-384/SessionFixation.ql
Normal file
58
javascript/ql/src/Security/CWE-384/SessionFixation.ql
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* @name Failure to abandon session
|
||||
* @description Reusing an existing session as a different user could allow
|
||||
* an attacker to access someone else's account by using
|
||||
* their session.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5
|
||||
* @precision medium
|
||||
* @id js/session-fixation
|
||||
* @tags security
|
||||
* external/cwe/cwe-384
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* Holds if `setup` uses express-session (or similar) to log in a user.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate isLoginSetup(Express::RouteSetup setup) {
|
||||
// either some path that contains "login" with a write to `req.session`
|
||||
setup.getPath().matches("%login%") and
|
||||
exists(
|
||||
setup
|
||||
.getARouteHandler()
|
||||
.(Express::RouteHandler)
|
||||
.getARequestSource()
|
||||
.ref()
|
||||
.getAPropertyRead("session")
|
||||
.getAPropertyWrite()
|
||||
)
|
||||
or
|
||||
// or an authentication method is used (e.g. `passport.authenticate`)
|
||||
setup.getARouteHandler().(DataFlow::CallNode).getCalleeName() = "authenticate"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `handler` regenerates its session using `req.session.regenerate`.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate regeneratesSession(Express::RouteSetup setup) {
|
||||
exists(
|
||||
setup
|
||||
.getARouteHandler()
|
||||
.(Express::RouteHandler)
|
||||
.getARequestSource()
|
||||
.ref()
|
||||
.getAPropertyRead("session")
|
||||
.getAPropertyRead("regenerate")
|
||||
)
|
||||
}
|
||||
|
||||
from Express::RouteSetup setup
|
||||
where
|
||||
isLoginSetup(setup) and
|
||||
not regeneratesSession(setup)
|
||||
select setup, "Route handler does not invalidate session following login"
|
||||
@@ -0,0 +1,18 @@
|
||||
const express = require('express');
|
||||
const session = require('express-session');
|
||||
var bodyParser = require('body-parser')
|
||||
const app = express();
|
||||
app.use(bodyParser.urlencoded({ extended: false }))
|
||||
app.use(session({
|
||||
secret: 'keyboard cat'
|
||||
}));
|
||||
|
||||
app.post('/login', function (req, res) {
|
||||
// Check that username password matches
|
||||
if (req.body.username === 'admin' && req.body.password === 'admin') {
|
||||
req.session.authenticated = true;
|
||||
res.redirect('/');
|
||||
} else {
|
||||
res.redirect('/login');
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
const express = require('express');
|
||||
const session = require('express-session');
|
||||
var bodyParser = require('body-parser')
|
||||
const app = express();
|
||||
app.use(bodyParser.urlencoded({ extended: false }))
|
||||
app.use(session({
|
||||
secret: 'keyboard cat'
|
||||
}));
|
||||
|
||||
app.post('/login', function (req, res) {
|
||||
// Check that username password matches
|
||||
if (req.body.username === 'admin' && req.body.password === 'admin') {
|
||||
req.session.regenerate(function (err) {
|
||||
if (err) {
|
||||
res.send('Error');
|
||||
} else {
|
||||
req.session.authenticated = true;
|
||||
res.redirect('/');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
res.redirect('/login');
|
||||
}
|
||||
});
|
||||
42
javascript/ql/src/Security/CWE-598/SensitiveGetQuery.qhelp
Normal file
42
javascript/ql/src/Security/CWE-598/SensitiveGetQuery.qhelp
Normal file
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
Sensitive information such as user passwords should not be transmitted within the query string of the requested URL.
|
||||
Sensitive information within URLs may be logged in various locations, including the user's browser, the web server,
|
||||
and any forward or reverse proxy servers between the two endpoints. URLs may also be displayed on-screen, bookmarked
|
||||
or emailed around by users. They may be disclosed to third parties via the Referer header when any off-site links are
|
||||
followed. Placing sensitive information into the URL therefore increases the risk that it will be captured by an attacker.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Use HTTP POST to send sensitive information as part of the request body; for example, as form data.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The following example shows two route handlers that both receive a username and a password.
|
||||
The first receives this sensitive information from the query parameters of a GET request, which is
|
||||
transmitted in the URL. The second receives this sensitive information from the request body of a POST request.
|
||||
</p>
|
||||
<sample src="examples/SensitiveGet.js" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
CWE:
|
||||
<a href="https://cwe.mitre.org/data/definitions/598.html">CWE-598: Use of GET Request Method with Sensitive Query Strings</a>
|
||||
</li>
|
||||
<li>
|
||||
PortSwigger (Burp):
|
||||
<a href="https://portswigger.net/kb/issues/00400300_password-submitted-using-get-method">Password Submitted using GET Method</a>
|
||||
</li>
|
||||
<li>
|
||||
OWASP:
|
||||
<a href="https://owasp.org/www-community/vulnerabilities/Information_exposure_through_query_strings_in_url">Information Exposure through Query Strings in URL</a>
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
27
javascript/ql/src/Security/CWE-598/SensitiveGetQuery.ql
Normal file
27
javascript/ql/src/Security/CWE-598/SensitiveGetQuery.ql
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @name Sensitive data read from GET request
|
||||
* @description Placing sensitive data in a GET request increases the risk of
|
||||
* the data being exposed to an attacker.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 6.5
|
||||
* @precision high
|
||||
* @id js/sensitive-get-query
|
||||
* @tags security
|
||||
* external/cwe/cwe-598
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
from
|
||||
Express::RouteSetup setup, Express::RouteHandler handler, Express::RequestInputAccess input,
|
||||
SensitiveExpr sensitive
|
||||
where
|
||||
setup.getRequestMethod() = "GET" and
|
||||
handler = setup.getARouteHandler() and
|
||||
input.getRouteHandler() = handler and
|
||||
input.getKind() = "parameter" and
|
||||
input.(DataFlow::SourceNode).flowsToExpr(sensitive) and
|
||||
not sensitive.getClassification() = SensitiveDataClassification::id()
|
||||
select input, "$@ for GET requests uses query parameter as sensitive data.", handler,
|
||||
"Route handler"
|
||||
25
javascript/ql/src/Security/CWE-598/examples/SensitiveGet.js
Normal file
25
javascript/ql/src/Security/CWE-598/examples/SensitiveGet.js
Normal file
@@ -0,0 +1,25 @@
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
app.use(require('body-parser').urlencoded({ extended: false }))
|
||||
|
||||
// bad: sensitive information is read from query parameters
|
||||
app.get('/login1', (req, res) => {
|
||||
const user = req.query.user;
|
||||
const password = req.query.password;
|
||||
if (checkUser(user, password)) {
|
||||
res.send('Welcome');
|
||||
} else {
|
||||
res.send('Access denied');
|
||||
}
|
||||
});
|
||||
|
||||
// good: sensitive information is read from post body
|
||||
app.post('/login2', (req, res) => {
|
||||
const user = req.body.user;
|
||||
const password = req.body.password;
|
||||
if (checkUser(user, password)) {
|
||||
res.send('Welcome');
|
||||
} else {
|
||||
res.send('Access denied');
|
||||
}
|
||||
});
|
||||
@@ -24,4 +24,4 @@ from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
select sink, source, sink,
|
||||
"This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@.",
|
||||
source.getNode(), "here"
|
||||
source.getNode(), source.getNode().(Source).describe()
|
||||
|
||||
9
javascript/ql/test/ApiGraphs/async-await/tst.ts
Normal file
9
javascript/ql/test/ApiGraphs/async-await/tst.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { readFile } from 'fs/promises';
|
||||
|
||||
async function readFileUtf8(path: string): Promise<string> {
|
||||
return readFile(path, { encoding: 'utf8' });
|
||||
}
|
||||
|
||||
async function test(path: string) {
|
||||
await readFileUtf8(path); /* use (promised (return (member readFile (member exports (module fs/promises))))) */
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user