Merge branch 'main' into use-range-analysis-in-buffer-write

This commit is contained in:
Mathias Vorreiter Pedersen
2021-11-16 09:14:02 +00:00
189 changed files with 4201 additions and 457 deletions

View 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`.

View 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.

View 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>

View 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."

View File

@@ -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
...

View File

@@ -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>

View File

@@ -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

View File

@@ -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. |

View File

@@ -0,0 +1 @@
experimental/Security/CWE/CWE-377/InsecureTemporaryFile.ql

View File

@@ -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;
}

View File

@@ -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 | |

View File

@@ -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;

View File

@@ -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. |

View File

@@ -0,0 +1 @@
Security/CWE/CWE-319/UseOfHttp.ql

View File

@@ -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);
}
}

View File

@@ -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()

View File

@@ -0,0 +1,6 @@
name: codeql-csharp-consistency-queries
version: 0.0.0
libraryPathDependencies:
- codeql/csharp-all
- codeql/csharp-queries
extractor: csharp

View File

@@ -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(

View File

@@ -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))

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

@@ -1 +1 @@
semmle/code/csharp/PrintAst.ql
shared/PrintAst.ql

View File

View 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

View File

@@ -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

View File

@@ -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",

View File

@@ -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.
*

View File

@@ -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()
}
/**

View File

@@ -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 =

View File

@@ -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"
}
}

View 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"
]
}
}

View File

@@ -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 |

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/google-android-9.0.0

View File

@@ -0,0 +1,6 @@
import java
import TestUtilities.InlineFlowTest
class AsyncTaskTest extends InlineFlowTest {
override TaintTracking::Configuration getTaintFlowConfig() { none() }
}

View File

@@ -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.

View File

@@ -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.

View 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.

View File

@@ -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.

View File

@@ -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) {

View File

@@ -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()
}

View File

@@ -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())
)
)
}

View File

@@ -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()
)
}
}

View File

@@ -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())
}
}

View File

@@ -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"
}

View File

@@ -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"])]
)
}

View File

@@ -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() }
}
}

View File

@@ -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()
}
/**

View File

@@ -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" }
}
}

View File

@@ -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()
}
}

View File

@@ -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"
)
}
}

View File

@@ -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" }

View File

@@ -11,6 +11,7 @@
* external/cwe/cwe-312
* external/cwe/cwe-315
* external/cwe/cwe-359
* external/cwe/cwe-532
*/
import javascript

View 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>

View 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

View 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>

View 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"

View File

@@ -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');
}
});

View File

@@ -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');
}
});

View 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>

View 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"

View 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');
}
});

View File

@@ -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()

View 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