mirror of
https://github.com/github/codeql.git
synced 2026-06-06 05:57:07 +02:00
Compare commits
224 Commits
v1.25.0
...
geoffw0-pa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2a4fbc522 | ||
|
|
768e5190a1 | ||
|
|
89c2b6dc4b | ||
|
|
e9a36b2524 | ||
|
|
8cbd4974ae | ||
|
|
e01e702f46 | ||
|
|
9556937840 | ||
|
|
8d49ad7325 | ||
|
|
4b336e9b01 | ||
|
|
82f9826966 | ||
|
|
ed06604b46 | ||
|
|
a6bcbe7974 | ||
|
|
498b350add | ||
|
|
a839f1fae5 | ||
|
|
49d2f66ddb | ||
|
|
f343eb9143 | ||
|
|
5d7f771933 | ||
|
|
de87f8fc42 | ||
|
|
93f95b1c22 | ||
|
|
ecbbcc2f61 | ||
|
|
7349333006 | ||
|
|
3c0e7a709f | ||
|
|
732a8fa4c9 | ||
|
|
6c60589dbd | ||
|
|
3469ad7ca6 | ||
|
|
cca2d9d825 | ||
|
|
2c7bb8c51f | ||
|
|
a1a1218f95 | ||
|
|
dc5c0f8e7a | ||
|
|
f5abf74e0f | ||
|
|
8891ae70b6 | ||
|
|
5e5a112c36 | ||
|
|
2655616a0a | ||
|
|
9719da8643 | ||
|
|
d444778535 | ||
|
|
aa6cb51bba | ||
|
|
6f83c55ebd | ||
|
|
66541f260b | ||
|
|
56ff8cf084 | ||
|
|
aa9dfa0d6f | ||
|
|
b99ca60154 | ||
|
|
b4679cb8cf | ||
|
|
93d8d8eb1d | ||
|
|
6b6172fa5b | ||
|
|
1ee96a4b4f | ||
|
|
e80cc63219 | ||
|
|
a655124213 | ||
|
|
50558257fc | ||
|
|
128b8328b9 | ||
|
|
f62ad75048 | ||
|
|
cf6f530823 | ||
|
|
a57dfd6b67 | ||
|
|
f824a893ca | ||
|
|
030ab4f626 | ||
|
|
2ea25b9d90 | ||
|
|
e1d4b98923 | ||
|
|
8a65dd2cd6 | ||
|
|
656ff9c441 | ||
|
|
21246624b4 | ||
|
|
dd4d00293d | ||
|
|
394991164f | ||
|
|
f834d71bab | ||
|
|
2c5de7f50e | ||
|
|
0476b97f63 | ||
|
|
12dfc4afd9 | ||
|
|
3929e01350 | ||
|
|
681657f070 | ||
|
|
5a3acc231e | ||
|
|
7bd5464b01 | ||
|
|
4dcaa7be57 | ||
|
|
dcfbb86674 | ||
|
|
5da37f5cf4 | ||
|
|
dc5167bbe7 | ||
|
|
1f432dc45f | ||
|
|
a963f15100 | ||
|
|
85de5aa16b | ||
|
|
959c6315c4 | ||
|
|
410b696562 | ||
|
|
639d914a47 | ||
|
|
7c4e10df17 | ||
|
|
02478774c3 | ||
|
|
5b7c7f933c | ||
|
|
f6d6f91a42 | ||
|
|
aff4535965 | ||
|
|
5874ecc28b | ||
|
|
d84294df3d | ||
|
|
3cf11eca2a | ||
|
|
aab2e6f803 | ||
|
|
7670e7da97 | ||
|
|
7d491afaeb | ||
|
|
b7d2e0ca63 | ||
|
|
1b0cfc96b3 | ||
|
|
0ba59210fc | ||
|
|
e3a12c5fea | ||
|
|
c20d763490 | ||
|
|
c8911ab973 | ||
|
|
77db87efb7 | ||
|
|
c177eff3d8 | ||
|
|
f9de8eb3b4 | ||
|
|
05e956b374 | ||
|
|
0e54b498b7 | ||
|
|
f477e09190 | ||
|
|
3ae3a879d2 | ||
|
|
3682a902de | ||
|
|
6e18be43f3 | ||
|
|
0281456948 | ||
|
|
0534c69c76 | ||
|
|
0b5b7fa095 | ||
|
|
b3f3f6d95a | ||
|
|
cbf30e37ed | ||
|
|
a7564c9e0e | ||
|
|
f16c263393 | ||
|
|
3db1ceeb70 | ||
|
|
614103c3b6 | ||
|
|
7cc877cbbb | ||
|
|
408db412dc | ||
|
|
5a819422c1 | ||
|
|
7f7ad88dea | ||
|
|
205dd1aead | ||
|
|
b821f918e5 | ||
|
|
9a8eed8440 | ||
|
|
1011325cf7 | ||
|
|
a1411407c1 | ||
|
|
0c09d66d43 | ||
|
|
f1dc36244c | ||
|
|
e642808a75 | ||
|
|
5f635aca36 | ||
|
|
a89624698d | ||
|
|
81ad4552c9 | ||
|
|
cc5ef4d5e1 | ||
|
|
b43d410ab1 | ||
|
|
f70cb2e7b3 | ||
|
|
5a3f67a682 | ||
|
|
9e78341e43 | ||
|
|
67c4320287 | ||
|
|
016bdc1614 | ||
|
|
32d9d270fc | ||
|
|
ea0896c78b | ||
|
|
aa27eaf7e0 | ||
|
|
9f5c37ccaa | ||
|
|
5727e6f9f8 | ||
|
|
cf3f275aa1 | ||
|
|
d7c08f732d | ||
|
|
0867c5567e | ||
|
|
5a96ee1a7b | ||
|
|
368572f1f0 | ||
|
|
7928a02424 | ||
|
|
e0c081a2af | ||
|
|
c52064af78 | ||
|
|
ff0dacf1d7 | ||
|
|
b65a033302 | ||
|
|
ff58abb7d3 | ||
|
|
4990d00498 | ||
|
|
b88ef56cb4 | ||
|
|
a5dab4e768 | ||
|
|
81de1b14d9 | ||
|
|
64f4613a3f | ||
|
|
9e74c183fe | ||
|
|
6f845b0044 | ||
|
|
7923c480af | ||
|
|
83e9d052d9 | ||
|
|
d32e2772a0 | ||
|
|
488a7f4d01 | ||
|
|
eab64f125b | ||
|
|
5520504658 | ||
|
|
a91cc9b7ec | ||
|
|
7f911f00ee | ||
|
|
248628b11e | ||
|
|
3a23451395 | ||
|
|
38acea633f | ||
|
|
01fb51829c | ||
|
|
3320061178 | ||
|
|
2c42d3cca5 | ||
|
|
57e7411c0a | ||
|
|
0d5f9113a3 | ||
|
|
55473c65f1 | ||
|
|
9d7d6b39cb | ||
|
|
c2733ad22e | ||
|
|
f94055fa2c | ||
|
|
33526f61a8 | ||
|
|
b705f7f3e9 | ||
|
|
4570444c7e | ||
|
|
5387294168 | ||
|
|
c4940aaa86 | ||
|
|
3e0481b889 | ||
|
|
896cdf9b12 | ||
|
|
37158f46ed | ||
|
|
3f6d8490e0 | ||
|
|
646efe2a20 | ||
|
|
c585b2e483 | ||
|
|
61178c5330 | ||
|
|
48e540fa9a | ||
|
|
db6d5c329f | ||
|
|
c739c733fe | ||
|
|
b3bb4cbf54 | ||
|
|
b147be6fea | ||
|
|
782573ed43 | ||
|
|
4ad6357cd7 | ||
|
|
7435dac3d2 | ||
|
|
641c5df79f | ||
|
|
b66f391c31 | ||
|
|
fed506a12f | ||
|
|
6e6921b11e | ||
|
|
05a4798b5e | ||
|
|
5f560e0465 | ||
|
|
3b9daa2db2 | ||
|
|
d201c4ba8a | ||
|
|
d6e9b07a9e | ||
|
|
b242a61701 | ||
|
|
6d329bce6e | ||
|
|
263f00784f | ||
|
|
25bfc3a168 | ||
|
|
259654b1a4 | ||
|
|
e1130a2bfa | ||
|
|
3fdd11a9b5 | ||
|
|
f462156cdf | ||
|
|
3016798101 | ||
|
|
c57c016ced | ||
|
|
0552f9b0cc | ||
|
|
dd19ee47a1 | ||
|
|
9affa157b6 | ||
|
|
f051f46ee9 | ||
|
|
908d789f1b | ||
|
|
e44229435c |
23
change-notes/1.26/analysis-cpp.md
Normal file
23
change-notes/1.26/analysis-cpp.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Improvements to C/C++ analysis
|
||||
|
||||
The following changes in version 1.26 affect C/C++ analysis in all applications.
|
||||
|
||||
## General improvements
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|----------------------------|------------------------|------------------------------------------------------------------|
|
||||
| Inconsistent direction of for loop (`cpp/inconsistent-loop-direction`) | Fewer false positive results | The query now accounts for intentional wrapping of an unsigned loop counter. |
|
||||
| Comparison result is always the same (`cpp/constant-comparison`) | More correct results | Bounds on expressions involving multiplication can now be determined in more cases. |
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
* The models library now models more taint flows through `std::string`.
|
||||
* The `SimpleRangeAnalysis` library now supports multiplications of the form
|
||||
`e1 * e2` when `e1` and `e2` are unsigned.
|
||||
30
change-notes/1.26/analysis-javascript.md
Normal file
30
change-notes/1.26/analysis-javascript.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Improvements to JavaScript analysis
|
||||
|
||||
## General improvements
|
||||
|
||||
* Support for the following frameworks and libraries has been improved:
|
||||
- [fast-json-stable-stringify](https://www.npmjs.com/package/fast-json-stable-stringify)
|
||||
- [fast-safe-stringify](https://www.npmjs.com/package/fast-safe-stringify)
|
||||
- [javascript-stringify](https://www.npmjs.com/package/javascript-stringify)
|
||||
- [js-stringify](https://www.npmjs.com/package/js-stringify)
|
||||
- [json-stable-stringify](https://www.npmjs.com/package/json-stable-stringify)
|
||||
- [json-stringify-safe](https://www.npmjs.com/package/json-stringify-safe)
|
||||
- [json3](https://www.npmjs.com/package/json3)
|
||||
- [object-inspect](https://www.npmjs.com/package/object-inspect)
|
||||
- [pretty-format](https://www.npmjs.com/package/pretty-format)
|
||||
- [stringify-object](https://www.npmjs.com/package/stringify-object)
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|---------------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|--------------------------------|------------------------------|---------------------------------------------------------------------------|
|
||||
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
@@ -50,7 +50,12 @@ predicate illDefinedDecrForStmt(
|
||||
DataFlow::localFlowStep(DataFlow::exprNode(initialCondition), DataFlow::exprNode(lesserOperand)) and
|
||||
// `initialCondition` < `terminalCondition`
|
||||
(
|
||||
upperBound(initialCondition) < lowerBound(terminalCondition)
|
||||
upperBound(initialCondition) < lowerBound(terminalCondition) and
|
||||
(
|
||||
// exclude cases where the loop counter is `unsigned` (where wrapping behaviour can be used deliberately)
|
||||
v.getUnspecifiedType().(IntegralType).isSigned() or
|
||||
initialCondition.getValue().toInt() = 0
|
||||
)
|
||||
or
|
||||
(forstmt.conditionAlwaysFalse() or forstmt.conditionAlwaysTrue())
|
||||
)
|
||||
|
||||
@@ -18,6 +18,9 @@ Expr getTest() {
|
||||
or
|
||||
// boost tests; http://www.boost.org/
|
||||
result.(FunctionCall).getTarget().hasQualifiedName("boost::unit_test", "make_test_case")
|
||||
or
|
||||
// googletest tests; https://github.com/google/googletest/
|
||||
result.(FunctionCall).getTarget().hasQualifiedName("testing::internal", "MakeAndRegisterTestInfo")
|
||||
}
|
||||
|
||||
from File f, int n
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
/**
|
||||
* Provides classes for identifying and reasoning about Microsoft source code
|
||||
* annotation language (SAL) macros.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* A SAL macro defined in `sal.h` or a similar header file.
|
||||
*/
|
||||
class SALMacro extends Macro {
|
||||
SALMacro() {
|
||||
exists(string filename | filename = this.getFile().getBaseName() |
|
||||
@@ -20,27 +28,34 @@ class SALMacro extends Macro {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate isTopLevelMacroAccess(MacroAccess ma) { not exists(ma.getParentInvocation()) }
|
||||
private predicate isTopLevelMacroAccess(MacroAccess ma) { not exists(ma.getParentInvocation()) }
|
||||
|
||||
/**
|
||||
* An invocation of a SAL macro (excluding invocations inside other macros).
|
||||
*/
|
||||
class SALAnnotation extends MacroInvocation {
|
||||
SALAnnotation() {
|
||||
this.getMacro() instanceof SALMacro and
|
||||
isTopLevelMacroAccess(this)
|
||||
}
|
||||
|
||||
/** Returns the `Declaration` annotated by `this`. */
|
||||
/** Gets the `Declaration` annotated by `this`. */
|
||||
Declaration getDeclaration() {
|
||||
annotatesAt(this, result.getADeclarationEntry(), _, _) and
|
||||
not result instanceof Type // exclude typedefs
|
||||
}
|
||||
|
||||
/** Returns the `DeclarationEntry` annotated by `this`. */
|
||||
/** Gets the `DeclarationEntry` annotated by `this`. */
|
||||
DeclarationEntry getDeclarationEntry() {
|
||||
annotatesAt(this, result, _, _) and
|
||||
not result instanceof TypeDeclarationEntry // exclude typedefs
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A SAL macro indicating that the return value of a function should always be
|
||||
* checked.
|
||||
*/
|
||||
class SALCheckReturn extends SALAnnotation {
|
||||
SALCheckReturn() {
|
||||
exists(SALMacro m | m = this.getMacro() |
|
||||
@@ -50,6 +65,10 @@ class SALCheckReturn extends SALAnnotation {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A SAL macro indicating that a pointer variable or return value should not be
|
||||
* `NULL`.
|
||||
*/
|
||||
class SALNotNull extends SALAnnotation {
|
||||
SALNotNull() {
|
||||
exists(SALMacro m | m = this.getMacro() |
|
||||
@@ -69,6 +88,9 @@ class SALNotNull extends SALAnnotation {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A SAL macro indicating that a value may be `NULL`.
|
||||
*/
|
||||
class SALMaybeNull extends SALAnnotation {
|
||||
SALMaybeNull() {
|
||||
exists(SALMacro m | m = this.getMacro() |
|
||||
@@ -79,13 +101,29 @@ class SALMaybeNull extends SALAnnotation {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter annotated by one or more SAL annotations.
|
||||
*/
|
||||
class SALParameter extends Parameter {
|
||||
/** One of this parameter's annotations. */
|
||||
SALAnnotation a;
|
||||
|
||||
SALParameter() { annotatesAt(a, this.getADeclarationEntry(), _, _) }
|
||||
|
||||
predicate isIn() { a.getMacroName().toLowerCase().matches("%\\_in%") }
|
||||
|
||||
predicate isOut() { a.getMacroName().toLowerCase().matches("%\\_out%") }
|
||||
|
||||
predicate isInOut() { a.getMacroName().toLowerCase().matches("%\\_inout%") }
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Implementation details
|
||||
/**
|
||||
* Holds if `a` annotates the declaration entry `d` and
|
||||
* its start position is the `idx`th position in `file` that holds a SAL element.
|
||||
*/
|
||||
predicate annotatesAt(SALAnnotation a, DeclarationEntry d, File file, int idx) {
|
||||
private predicate annotatesAt(SALAnnotation a, DeclarationEntry d, File file, int idx) {
|
||||
annotatesAtPosition(a.(SALElement).getStartPosition(), d, file, idx)
|
||||
}
|
||||
|
||||
@@ -109,22 +147,6 @@ private predicate annotatesAtPosition(SALPosition pos, DeclarationEntry d, File
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter annotated by one or more SAL annotations.
|
||||
*/
|
||||
class SALParameter extends Parameter {
|
||||
/** One of this parameter's annotations. */
|
||||
SALAnnotation a;
|
||||
|
||||
SALParameter() { annotatesAt(a, this.getADeclarationEntry(), _, _) }
|
||||
|
||||
predicate isIn() { a.getMacroName().toLowerCase().matches("%\\_in%") }
|
||||
|
||||
predicate isOut() { a.getMacroName().toLowerCase().matches("%\\_out%") }
|
||||
|
||||
predicate isInOut() { a.getMacroName().toLowerCase().matches("%\\_inout%") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A SAL element, that is, a SAL annotation or a declaration entry
|
||||
* that may have SAL annotations.
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
///// Library routines /////
|
||||
|
||||
int scanf(const char *format, ...);
|
||||
int sscanf(const char *str, const char *format, ...);
|
||||
int fscanf(const char *str, const char *format, ...);
|
||||
|
||||
///// EXAMPLES /////
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
||||
// BAD, do not use scanf without specifying a length first
|
||||
char buf1[10];
|
||||
scanf("%s", buf1);
|
||||
|
||||
// GOOD, length is specified. The length should be one less than the size of the buffer, since the last character is the NULL terminator.
|
||||
char buf2[10];
|
||||
sscanf(buf2, "%9s");
|
||||
|
||||
// BAD, do not use scanf without specifying a length first
|
||||
char file[10];
|
||||
fscanf(file, "%s", buf2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>It is bad practice to use any of the <code>scanf</code> functions without including a specified length within the format parameter, as it will be vulnerable to buffer overflows.</p>
|
||||
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
|
||||
<p>Specify a length within the format string parameter, and make this length one less than the size of the buffer, since the last character should be reserved for the NULL terminator.</p>
|
||||
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>The following example demonstrates safe and unsafe uses of <code>scanf</code> type functions.</p>
|
||||
<sample src="MemoryUnsafeFunctionScan.cpp" />
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @name Scanf function without a specified length
|
||||
* @description Use of one of the scanf functions without a specified length.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id cpp/memory-unsafe-function-scan
|
||||
* @tags reliability
|
||||
* security
|
||||
* external/cwe/cwe-120
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.Scanf
|
||||
|
||||
from FunctionCall call, ScanfFunction sff
|
||||
where
|
||||
call.getTarget() = sff and
|
||||
call.getArgument(sff.getFormatParameterIndex()).getValue().regexpMatch(".*%l?s.*")
|
||||
select call, "Dangerous use of one of the scanf functions"
|
||||
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Private data that is stored in a file or buffer unencrypted is accessible to an attacker who gains access to the
|
||||
storage.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>Ensure that private data is always encrypted before being stored.
|
||||
It may be wise to encrypt information before it is put into a buffer that may be readable in memory.</p>
|
||||
|
||||
<p>In general, decrypt private data only at the point where it is necessary for it to be used in
|
||||
cleartext.</p>
|
||||
|
||||
</recommendation>
|
||||
|
||||
<references>
|
||||
|
||||
<li><a href="https://owasp.org/www-project-top-ten/OWASP_Top_Ten_2017/Top_10-2017_A3-Sensitive_Data_Exposure">OWASP Sensitive_Data_Exposure</a></li>
|
||||
<li>M. Dowd, J. McDonald and J. Schuhm, <i>The Art of Software Security Assessment</i>, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.</li>
|
||||
<li>M. Howard and D. LeBlanc, <i>Writing Secure Code</i>, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.</li>
|
||||
|
||||
|
||||
|
||||
<!-- LocalWords: CWE
|
||||
-->
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @name Exposure of private information
|
||||
* @description If private information is written to an external location, it may be accessible by
|
||||
* unauthorized persons.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @id cpp/private-cleartext-write
|
||||
* @tags security
|
||||
* external/cwe/cwe-359
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import experimental.semmle.code.cpp.security.PrivateCleartextWrite
|
||||
import experimental.semmle.code.cpp.security.PrivateCleartextWrite::PrivateCleartextWrite
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from WriteConfig b, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where b.hasFlowPath(source, sink)
|
||||
select sink.getNode(),
|
||||
"This write into the external location '" + sink + "' may contain unencrypted data from $@",
|
||||
source, "this source."
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Provides a taint-tracking configuration for reasoning about private information flowing unencrypted to an external location.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
import experimental.semmle.code.cpp.security.PrivateData
|
||||
import semmle.code.cpp.security.FileWrite
|
||||
import semmle.code.cpp.security.BufferWrite
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
|
||||
module PrivateCleartextWrite {
|
||||
/**
|
||||
* A data flow source for private information flowing unencrypted to an external location.
|
||||
*/
|
||||
abstract class Source extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A data flow sink for private information flowing unencrypted to an external location.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A sanitizer for private information flowing unencrypted to an external location.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/** A call to any method whose name suggests that it encodes or encrypts the parameter. */
|
||||
class ProtectSanitizer extends Sanitizer {
|
||||
ProtectSanitizer() {
|
||||
exists(Function m, string s |
|
||||
this.getExpr().(FunctionCall).getTarget() = m and
|
||||
m.getName().regexpMatch("(?i).*" + s + ".*")
|
||||
|
|
||||
s = "protect" or s = "encode" or s = "encrypt"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class WriteConfig extends TaintTracking::Configuration {
|
||||
WriteConfig() { this = "Write configuration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
class PrivateDataSource extends Source {
|
||||
PrivateDataSource() { this.getExpr() instanceof PrivateDataExpr }
|
||||
}
|
||||
|
||||
class WriteSink extends Sink {
|
||||
WriteSink() {
|
||||
exists(FileWrite f, BufferWrite b |
|
||||
this.asExpr() = f.getASource()
|
||||
or
|
||||
this.asExpr() = b.getAChild()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Provides classes and predicates for identifying private data and functions for security.
|
||||
*
|
||||
* 'Private' data in general is anything that would compromise user privacy if exposed. This
|
||||
* library tries to guess where private data may either be stored in a variable or produced by a
|
||||
* function.
|
||||
*
|
||||
* This library is not concerned with credentials. See `SensitiveActions` for expressions related
|
||||
* to credentials.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/** A string for `match` that identifies strings that look like they represent private data. */
|
||||
private string privateNames() {
|
||||
// Inspired by the list on https://cwe.mitre.org/data/definitions/359.html
|
||||
// Government identifiers, such as Social Security Numbers
|
||||
result = "%social%security%number%" or
|
||||
// Contact information, such as home addresses and telephone numbers
|
||||
result = "%postcode%" or
|
||||
result = "%zipcode%" or
|
||||
// result = "%telephone%" or
|
||||
// Geographic location - where the user is (or was)
|
||||
result = "%latitude%" or
|
||||
result = "%longitude%" or
|
||||
// Financial data - such as credit card numbers, salary, bank accounts, and debts
|
||||
result = "%creditcard%" or
|
||||
result = "%salary%" or
|
||||
result = "%bankaccount%" or
|
||||
// Communications - e-mail addresses, private e-mail messages, SMS text messages, chat logs, etc.
|
||||
// result = "%email%" or
|
||||
// result = "%mobile%" or
|
||||
result = "%employer%" or
|
||||
// Health - medical conditions, insurance status, prescription records
|
||||
result = "%medical%"
|
||||
}
|
||||
|
||||
/** An expression that might contain private data. */
|
||||
abstract class PrivateDataExpr extends Expr { }
|
||||
|
||||
/** A functiond call that might produce private data. */
|
||||
class PrivateFunctionCall extends PrivateDataExpr, FunctionCall {
|
||||
PrivateFunctionCall() {
|
||||
exists(string s | this.getTarget().getName().toLowerCase() = s | s.matches(privateNames()))
|
||||
}
|
||||
}
|
||||
|
||||
/** An access to a variable that might contain private data. */
|
||||
class PrivateVariableAccess extends PrivateDataExpr, VariableAccess {
|
||||
PrivateVariableAccess() {
|
||||
exists(string s | this.getTarget().getName().toLowerCase() = s | s.matches(privateNames()))
|
||||
}
|
||||
}
|
||||
@@ -214,6 +214,9 @@ abstract class ImplicitConversionFunction extends MemberFunction {
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: as of C++11 this class does not correspond perfectly with the
|
||||
* language definition of a converting constructor.
|
||||
*
|
||||
* A C++ constructor that also defines an implicit conversion. For example the
|
||||
* function `MyClass` in the following code is a `ConversionConstructor`:
|
||||
* ```
|
||||
@@ -225,15 +228,16 @@ abstract class ImplicitConversionFunction extends MemberFunction {
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class ConversionConstructor extends Constructor, ImplicitConversionFunction {
|
||||
deprecated class ConversionConstructor extends Constructor, ImplicitConversionFunction {
|
||||
ConversionConstructor() {
|
||||
strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
|
||||
not hasSpecifier("explicit") and
|
||||
not this instanceof CopyConstructor
|
||||
not hasSpecifier("explicit")
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() {
|
||||
not this instanceof MoveConstructor and result = "ConversionConstructor"
|
||||
not this instanceof CopyConstructor and
|
||||
not this instanceof MoveConstructor and
|
||||
result = "ConversionConstructor"
|
||||
}
|
||||
|
||||
/** Gets the type this `ConversionConstructor` takes as input. */
|
||||
|
||||
@@ -65,6 +65,15 @@ predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeT
|
||||
// tracking. The flow from expression `x` into `x++` etc. is handled in the
|
||||
// case above.
|
||||
exprTo = DataFlow::getAnAccessToAssignedVariable(exprFrom.(PostfixCrementOperation))
|
||||
or
|
||||
// In `for (char c : s) { ... c ... }`, this rule propagates taint from `s`
|
||||
// to `c`.
|
||||
exists(RangeBasedForStmt rbf |
|
||||
exprFrom = rbf.getRange() and
|
||||
// It's guaranteed up to at least C++20 that the range-based for loop
|
||||
// desugars to a variable with an initializer.
|
||||
exprTo = rbf.getVariable().getInitializer().getExpr()
|
||||
)
|
||||
)
|
||||
or
|
||||
// Taint can flow through modeled functions
|
||||
|
||||
@@ -7,9 +7,17 @@ import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
|
||||
/**
|
||||
* Model for C++ conversion constructors.
|
||||
* Model for C++ conversion constructors. As of C++11 this does not correspond
|
||||
* perfectly with the language definition of a converting constructor, however,
|
||||
* it does correspond with the constructors we are confident taint should flow
|
||||
* through.
|
||||
*/
|
||||
class ConversionConstructorModel extends ConversionConstructor, TaintFunction {
|
||||
class ConversionConstructorModel extends Constructor, TaintFunction {
|
||||
ConversionConstructorModel() {
|
||||
strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
|
||||
not hasSpecifier("explicit")
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// taint flow from the first constructor argument to the returned object
|
||||
input.isParameter(0) and
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
|
||||
/**
|
||||
* The `std::basic_string` template class.
|
||||
*/
|
||||
class StdBasicString extends TemplateClass {
|
||||
StdBasicString() { this.hasQualifiedName("std", "basic_string") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard function `std::string.c_str`.
|
||||
*/
|
||||
@@ -12,3 +19,51 @@ class StdStringCStr extends TaintFunction {
|
||||
output.isReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::string` function `operator+`.
|
||||
*/
|
||||
class StdStringPlus extends TaintFunction {
|
||||
StdStringPlus() {
|
||||
this.hasQualifiedName("std", "operator+") and
|
||||
this.getUnspecifiedType() = any(StdBasicString s).getAnInstantiation()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from parameters to return value
|
||||
(
|
||||
input.isParameterDeref(0) or
|
||||
input.isParameterDeref(1)
|
||||
) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::string` functions `operator+=` and `append`.
|
||||
*/
|
||||
class StdStringAppend extends TaintFunction {
|
||||
StdStringAppend() {
|
||||
this.hasQualifiedName("std", "basic_string", "operator+=") or
|
||||
this.hasQualifiedName("std", "basic_string", "append")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is a string (or
|
||||
* character).
|
||||
*/
|
||||
int getAStringParameter() {
|
||||
getParameter(result).getType() instanceof PointerType or
|
||||
getParameter(result).getType() instanceof ReferenceType or
|
||||
getParameter(result).getType() = getDeclaringType().getTemplateArgument(0) // i.e. `std::basic_string::CharT`
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from parameter to string itself (qualifier) and return value
|
||||
input.isParameterDeref(getAStringParameter()) and
|
||||
(
|
||||
output.isQualifierObject() or
|
||||
output.isReturnValueDeref()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,6 +156,10 @@ float safeFloor(float v) {
|
||||
result = v
|
||||
}
|
||||
|
||||
private class UnsignedMulExpr extends MulExpr {
|
||||
UnsignedMulExpr() { this.getType().(IntegralType).isUnsigned() }
|
||||
}
|
||||
|
||||
/** Set of expressions which we know how to analyze. */
|
||||
private predicate analyzableExpr(Expr e) {
|
||||
// The type of the expression must be arithmetic. We reuse the logic in
|
||||
@@ -178,6 +182,8 @@ private predicate analyzableExpr(Expr e) {
|
||||
or
|
||||
e instanceof SubExpr
|
||||
or
|
||||
e instanceof UnsignedMulExpr
|
||||
or
|
||||
e instanceof AssignExpr
|
||||
or
|
||||
e instanceof AssignAddExpr
|
||||
@@ -278,6 +284,10 @@ private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, StackVaria
|
||||
or
|
||||
exists(SubExpr subExpr | e = subExpr | exprDependsOnDef(subExpr.getAnOperand(), srcDef, srcVar))
|
||||
or
|
||||
exists(UnsignedMulExpr mulExpr | e = mulExpr |
|
||||
exprDependsOnDef(mulExpr.getAnOperand(), srcDef, srcVar)
|
||||
)
|
||||
or
|
||||
exists(AssignExpr addExpr | e = addExpr | exprDependsOnDef(addExpr.getRValue(), srcDef, srcVar))
|
||||
or
|
||||
exists(AssignAddExpr addExpr | e = addExpr |
|
||||
@@ -625,6 +635,13 @@ private float getLowerBoundsImpl(Expr expr) {
|
||||
result = addRoundingDown(xLow, -yHigh)
|
||||
)
|
||||
or
|
||||
exists(UnsignedMulExpr mulExpr, float xLow, float yLow |
|
||||
expr = mulExpr and
|
||||
xLow = getFullyConvertedLowerBounds(mulExpr.getLeftOperand()) and
|
||||
yLow = getFullyConvertedLowerBounds(mulExpr.getRightOperand()) and
|
||||
result = xLow * yLow
|
||||
)
|
||||
or
|
||||
exists(AssignExpr assign |
|
||||
expr = assign and
|
||||
result = getFullyConvertedLowerBounds(assign.getRValue())
|
||||
@@ -794,6 +811,13 @@ private float getUpperBoundsImpl(Expr expr) {
|
||||
result = addRoundingUp(xHigh, -yLow)
|
||||
)
|
||||
or
|
||||
exists(UnsignedMulExpr mulExpr, float xHigh, float yHigh |
|
||||
expr = mulExpr and
|
||||
xHigh = getFullyConvertedUpperBounds(mulExpr.getLeftOperand()) and
|
||||
yHigh = getFullyConvertedUpperBounds(mulExpr.getRightOperand()) and
|
||||
result = xHigh * yHigh
|
||||
)
|
||||
or
|
||||
exists(AssignExpr assign |
|
||||
expr = assign and
|
||||
result = getFullyConvertedUpperBounds(assign.getRValue())
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
edges
|
||||
| test.cpp:77:16:77:22 | medical | test.cpp:78:24:78:27 | temp |
|
||||
| test.cpp:81:17:81:20 | call to func | test.cpp:82:24:82:28 | buff5 |
|
||||
| test.cpp:81:22:81:28 | medical | test.cpp:81:17:81:20 | call to func |
|
||||
nodes
|
||||
| test.cpp:57:9:57:18 | theZipcode | semmle.label | theZipcode |
|
||||
| test.cpp:74:24:74:30 | medical | semmle.label | medical |
|
||||
| test.cpp:77:16:77:22 | medical | semmle.label | medical |
|
||||
| test.cpp:78:24:78:27 | temp | semmle.label | temp |
|
||||
| test.cpp:81:17:81:20 | call to func | semmle.label | call to func |
|
||||
| test.cpp:81:22:81:28 | medical | semmle.label | medical |
|
||||
| test.cpp:82:24:82:28 | buff5 | semmle.label | buff5 |
|
||||
| test.cpp:96:37:96:46 | theZipcode | semmle.label | theZipcode |
|
||||
| test.cpp:99:42:99:51 | theZipcode | semmle.label | theZipcode |
|
||||
#select
|
||||
| test.cpp:57:9:57:18 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@ | test.cpp:57:9:57:18 | theZipcode | this source. |
|
||||
| test.cpp:74:24:74:30 | medical | This write into the external location 'medical' may contain unencrypted data from $@ | test.cpp:74:24:74:30 | medical | this source. |
|
||||
| test.cpp:78:24:78:27 | temp | This write into the external location 'temp' may contain unencrypted data from $@ | test.cpp:77:16:77:22 | medical | this source. |
|
||||
| test.cpp:82:24:82:28 | buff5 | This write into the external location 'buff5' may contain unencrypted data from $@ | test.cpp:81:22:81:28 | medical | this source. |
|
||||
| test.cpp:96:37:96:46 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@ | test.cpp:96:37:96:46 | theZipcode | this source. |
|
||||
| test.cpp:99:42:99:51 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@ | test.cpp:99:42:99:51 | theZipcode | this source. |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-359/PrivateCleartextWrite.ql
|
||||
@@ -0,0 +1,105 @@
|
||||
#define FILE int
|
||||
#define wchar char
|
||||
#define size_t int
|
||||
typedef int streamsize;
|
||||
|
||||
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
|
||||
int fputs(const char *s, FILE *stream);
|
||||
int fputc(int c, FILE *stream);
|
||||
int fprintf(FILE *stream, const char *format, ...);
|
||||
int sprintf(char *s, const char *format, ...);
|
||||
size_t strlen(const char *s);
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <class charT>
|
||||
struct char_traits;
|
||||
|
||||
template <class charT, class traits = char_traits<charT>>
|
||||
class basic_ostream /*: virtual public basic_ios<charT,traits> - not needed for this test */
|
||||
{
|
||||
public:
|
||||
typedef charT char_type;
|
||||
basic_ostream<charT, traits> &write(const char_type *s, streamsize n);
|
||||
};
|
||||
|
||||
template <class charT, class traits = char_traits<charT>>
|
||||
class basic_ofstream : public basic_ostream<charT, traits>
|
||||
{
|
||||
public:
|
||||
};
|
||||
|
||||
template <class charT, class traits>
|
||||
basic_ostream<charT, traits> &operator<<(basic_ostream<charT, traits> &, const charT *);
|
||||
|
||||
typedef basic_ostream<char> ostream;
|
||||
typedef basic_ofstream<char> ofstream;
|
||||
}; // namespace std
|
||||
|
||||
using namespace std;
|
||||
|
||||
char *encrypt(char *buffer)
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
char *func(char *buffer)
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// test for CleartextFileWrite
|
||||
void file()
|
||||
{
|
||||
char *theZipcode = "cleartext zipcode!";
|
||||
FILE *file;
|
||||
|
||||
// BAD: write zipcode to file in cleartext
|
||||
fputs(theZipcode, file);
|
||||
|
||||
// GOOD: encrypt first
|
||||
char *encrypted = encrypt(theZipcode);
|
||||
fwrite(encrypted, sizeof(encrypted), 1, file);
|
||||
}
|
||||
|
||||
// test for CleartextBufferWrite
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char *medical = "medical";
|
||||
char *buff1;
|
||||
char *buff2;
|
||||
char *buff3;
|
||||
char *buff4;
|
||||
|
||||
// BAD: write medical to buffer in cleartext
|
||||
sprintf(buff1, "%s", medical);
|
||||
|
||||
// BAD: write medical to buffer in cleartext
|
||||
char *temp = medical;
|
||||
sprintf(buff2, "%s", temp);
|
||||
|
||||
// BAD: write medical to buffer in cleartext
|
||||
char *buff5 = func(medical);
|
||||
sprintf(buff3, "%s", buff5);
|
||||
|
||||
char *buff6 = encrypt(medical);
|
||||
// GOOD: encrypt first
|
||||
sprintf(buff4, "%s", buff6);
|
||||
}
|
||||
|
||||
// test for CleartextFileWrite
|
||||
void stream()
|
||||
{
|
||||
char *theZipcode = "cleartext zipcode!";
|
||||
ofstream mystream;
|
||||
|
||||
// BAD: write zipcode to file in cleartext
|
||||
mystream << "the zipcode is: " << theZipcode;
|
||||
|
||||
// BAD: write zipcode to file in cleartext
|
||||
(mystream << "the zipcode is: ").write(theZipcode, strlen(theZipcode));
|
||||
|
||||
// GOOD: encrypt first
|
||||
char *encrypted = encrypt(theZipcode);
|
||||
mystream << "the zipcode is: " << encrypted;
|
||||
(mystream << "the zipcode is: ").write(encrypted, strlen(encrypted));
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
///// Library routines /////
|
||||
|
||||
int scanf(const char *format, ...);
|
||||
int sscanf(const char *str, const char *format, ...);
|
||||
int fscanf(const char *str, const char *format, ...);
|
||||
|
||||
///// Test code /////
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
||||
// BAD, do not use scanf without specifying a length first
|
||||
char buf1[10];
|
||||
scanf("%s", buf1);
|
||||
|
||||
// GOOD, length is specified
|
||||
char buf2[10];
|
||||
sscanf(buf2, "%9s");
|
||||
|
||||
// BAD, do not use scanf without specifying a length first
|
||||
char file[10];
|
||||
fscanf(file, "%s", buf2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
| MemoryUnsafeFunctionScan.cpp:14:5:14:9 | call to scanf | Dangerous use of one of the scanf functions |
|
||||
| MemoryUnsafeFunctionScan.cpp:22:5:22:10 | call to fscanf | Dangerous use of one of the scanf functions |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-120/MemoryUnsafeFunctionScan.ql
|
||||
@@ -308,99 +308,230 @@
|
||||
| movableclass.cpp:65:13:65:18 | call to source | movableclass.cpp:65:13:65:20 | call to MyMovableClass | TAINT |
|
||||
| movableclass.cpp:65:13:65:20 | call to MyMovableClass | movableclass.cpp:65:8:65:9 | ref arg s3 | TAINT |
|
||||
| movableclass.cpp:65:13:65:20 | call to MyMovableClass | movableclass.cpp:65:11:65:11 | call to operator= | TAINT |
|
||||
| stl.cpp:67:12:67:17 | call to source | stl.cpp:71:7:71:7 | a | |
|
||||
| stl.cpp:68:16:68:20 | 123 | stl.cpp:68:16:68:21 | call to basic_string | TAINT |
|
||||
| stl.cpp:68:16:68:21 | call to basic_string | stl.cpp:72:7:72:7 | b | |
|
||||
| stl.cpp:68:16:68:21 | call to basic_string | stl.cpp:74:7:74:7 | b | |
|
||||
| stl.cpp:69:16:69:21 | call to source | stl.cpp:69:16:69:24 | call to basic_string | TAINT |
|
||||
| stl.cpp:69:16:69:24 | call to basic_string | stl.cpp:73:7:73:7 | c | |
|
||||
| stl.cpp:69:16:69:24 | call to basic_string | stl.cpp:75:7:75:7 | c | |
|
||||
| stl.cpp:74:7:74:7 | b | stl.cpp:74:9:74:13 | call to c_str | TAINT |
|
||||
| stl.cpp:75:7:75:7 | c | stl.cpp:75:9:75:13 | call to c_str | TAINT |
|
||||
| stl.cpp:80:20:80:22 | call to basic_stringstream | stl.cpp:83:2:83:4 | ss1 | |
|
||||
| stl.cpp:80:20:80:22 | call to basic_stringstream | stl.cpp:89:7:89:9 | ss1 | |
|
||||
| stl.cpp:80:20:80:22 | call to basic_stringstream | stl.cpp:94:7:94:9 | ss1 | |
|
||||
| stl.cpp:80:25:80:27 | call to basic_stringstream | stl.cpp:84:2:84:4 | ss2 | |
|
||||
| stl.cpp:80:25:80:27 | call to basic_stringstream | stl.cpp:90:7:90:9 | ss2 | |
|
||||
| stl.cpp:80:25:80:27 | call to basic_stringstream | stl.cpp:95:7:95:9 | ss2 | |
|
||||
| stl.cpp:80:30:80:32 | call to basic_stringstream | stl.cpp:85:2:85:4 | ss3 | |
|
||||
| stl.cpp:80:30:80:32 | call to basic_stringstream | stl.cpp:91:7:91:9 | ss3 | |
|
||||
| stl.cpp:80:30:80:32 | call to basic_stringstream | stl.cpp:96:7:96:9 | ss3 | |
|
||||
| stl.cpp:80:35:80:37 | call to basic_stringstream | stl.cpp:86:2:86:4 | ss4 | |
|
||||
| stl.cpp:80:35:80:37 | call to basic_stringstream | stl.cpp:92:7:92:9 | ss4 | |
|
||||
| stl.cpp:80:35:80:37 | call to basic_stringstream | stl.cpp:97:7:97:9 | ss4 | |
|
||||
| stl.cpp:80:40:80:42 | call to basic_stringstream | stl.cpp:87:2:87:4 | ss5 | |
|
||||
| stl.cpp:80:40:80:42 | call to basic_stringstream | stl.cpp:93:7:93:9 | ss5 | |
|
||||
| stl.cpp:80:40:80:42 | call to basic_stringstream | stl.cpp:98:7:98:9 | ss5 | |
|
||||
| stl.cpp:81:16:81:21 | call to source | stl.cpp:81:16:81:24 | call to basic_string | TAINT |
|
||||
| stl.cpp:81:16:81:24 | call to basic_string | stl.cpp:87:9:87:9 | t | |
|
||||
| stl.cpp:83:2:83:4 | ref arg ss1 | stl.cpp:89:7:89:9 | ss1 | |
|
||||
| stl.cpp:83:2:83:4 | ref arg ss1 | stl.cpp:94:7:94:9 | ss1 | |
|
||||
| stl.cpp:84:2:84:4 | ref arg ss2 | stl.cpp:90:7:90:9 | ss2 | |
|
||||
| stl.cpp:84:2:84:4 | ref arg ss2 | stl.cpp:95:7:95:9 | ss2 | |
|
||||
| stl.cpp:85:2:85:4 | ref arg ss3 | stl.cpp:91:7:91:9 | ss3 | |
|
||||
| stl.cpp:85:2:85:4 | ref arg ss3 | stl.cpp:96:7:96:9 | ss3 | |
|
||||
| stl.cpp:86:2:86:4 | ref arg ss4 | stl.cpp:92:7:92:9 | ss4 | |
|
||||
| stl.cpp:86:2:86:4 | ref arg ss4 | stl.cpp:97:7:97:9 | ss4 | |
|
||||
| stl.cpp:87:2:87:4 | ref arg ss5 | stl.cpp:93:7:93:9 | ss5 | |
|
||||
| stl.cpp:87:2:87:4 | ref arg ss5 | stl.cpp:98:7:98:9 | ss5 | |
|
||||
| stl.cpp:101:32:101:37 | source | stl.cpp:106:9:106:14 | source | |
|
||||
| stl.cpp:103:20:103:22 | call to basic_stringstream | stl.cpp:105:2:105:4 | ss1 | |
|
||||
| stl.cpp:103:20:103:22 | call to basic_stringstream | stl.cpp:108:7:108:9 | ss1 | |
|
||||
| stl.cpp:103:20:103:22 | call to basic_stringstream | stl.cpp:110:7:110:9 | ss1 | |
|
||||
| stl.cpp:103:25:103:27 | call to basic_stringstream | stl.cpp:106:2:106:4 | ss2 | |
|
||||
| stl.cpp:103:25:103:27 | call to basic_stringstream | stl.cpp:109:7:109:9 | ss2 | |
|
||||
| stl.cpp:103:25:103:27 | call to basic_stringstream | stl.cpp:111:7:111:9 | ss2 | |
|
||||
| stl.cpp:105:2:105:4 | ref arg ss1 | stl.cpp:108:7:108:9 | ss1 | |
|
||||
| stl.cpp:105:2:105:4 | ref arg ss1 | stl.cpp:110:7:110:9 | ss1 | |
|
||||
| stl.cpp:106:2:106:4 | ref arg ss2 | stl.cpp:109:7:109:9 | ss2 | |
|
||||
| stl.cpp:106:2:106:4 | ref arg ss2 | stl.cpp:111:7:111:9 | ss2 | |
|
||||
| stl.cpp:124:16:124:28 | call to basic_string | stl.cpp:125:7:125:11 | path1 | |
|
||||
| stl.cpp:124:17:124:26 | call to user_input | stl.cpp:124:16:124:28 | call to basic_string | TAINT |
|
||||
| stl.cpp:125:7:125:11 | path1 | stl.cpp:125:13:125:17 | call to c_str | TAINT |
|
||||
| stl.cpp:128:10:128:19 | call to user_input | stl.cpp:128:10:128:21 | call to basic_string | TAINT |
|
||||
| stl.cpp:128:10:128:21 | call to basic_string | stl.cpp:128:2:128:21 | ... = ... | |
|
||||
| stl.cpp:128:10:128:21 | call to basic_string | stl.cpp:129:7:129:11 | path2 | |
|
||||
| stl.cpp:129:7:129:11 | path2 | stl.cpp:129:13:129:17 | call to c_str | TAINT |
|
||||
| stl.cpp:131:15:131:24 | call to user_input | stl.cpp:131:15:131:27 | call to basic_string | TAINT |
|
||||
| stl.cpp:131:15:131:27 | call to basic_string | stl.cpp:132:7:132:11 | path3 | |
|
||||
| stl.cpp:132:7:132:11 | path3 | stl.cpp:132:13:132:17 | call to c_str | TAINT |
|
||||
| stl.cpp:137:19:137:24 | call to source | stl.cpp:140:17:140:18 | cs | |
|
||||
| stl.cpp:137:19:137:24 | call to source | stl.cpp:142:7:142:8 | cs | |
|
||||
| stl.cpp:140:17:140:18 | cs | stl.cpp:140:17:140:19 | call to basic_string | TAINT |
|
||||
| stl.cpp:140:17:140:19 | call to basic_string | stl.cpp:143:7:143:8 | ss | |
|
||||
| stl.cpp:148:19:148:24 | call to source | stl.cpp:151:17:151:18 | cs | |
|
||||
| stl.cpp:151:17:151:18 | cs | stl.cpp:151:17:151:19 | call to basic_string | TAINT |
|
||||
| stl.cpp:151:17:151:19 | call to basic_string | stl.cpp:154:7:154:8 | ss | |
|
||||
| stl.cpp:151:17:151:19 | call to basic_string | stl.cpp:157:7:157:8 | ss | |
|
||||
| stl.cpp:154:7:154:8 | ss | stl.cpp:154:10:154:14 | call to c_str | TAINT |
|
||||
| stl.cpp:154:10:154:14 | call to c_str | stl.cpp:154:2:154:16 | ... = ... | |
|
||||
| stl.cpp:154:10:154:14 | call to c_str | stl.cpp:156:7:156:8 | cs | |
|
||||
| stl.cpp:163:18:163:24 | hello | stl.cpp:163:18:163:25 | call to basic_string | TAINT |
|
||||
| stl.cpp:163:18:163:25 | call to basic_string | stl.cpp:168:8:168:9 | s1 | |
|
||||
| stl.cpp:164:19:164:26 | call to basic_string | stl.cpp:169:8:169:9 | s2 | |
|
||||
| stl.cpp:164:20:164:26 | hello | stl.cpp:164:19:164:26 | call to basic_string | TAINT |
|
||||
| stl.cpp:166:8:166:14 | call to basic_string | stl.cpp:166:3:166:14 | ... = ... | |
|
||||
| stl.cpp:166:8:166:14 | call to basic_string | stl.cpp:170:8:170:9 | s3 | |
|
||||
| stl.cpp:166:8:166:14 | hello | stl.cpp:166:8:166:14 | call to basic_string | TAINT |
|
||||
| stl.cpp:174:18:174:23 | call to source | stl.cpp:174:18:174:26 | call to basic_string | TAINT |
|
||||
| stl.cpp:174:18:174:26 | call to basic_string | stl.cpp:179:8:179:9 | s1 | |
|
||||
| stl.cpp:175:19:175:27 | call to basic_string | stl.cpp:180:8:180:9 | s2 | |
|
||||
| stl.cpp:175:20:175:25 | call to source | stl.cpp:175:19:175:27 | call to basic_string | TAINT |
|
||||
| stl.cpp:177:8:177:13 | call to source | stl.cpp:177:8:177:15 | call to basic_string | TAINT |
|
||||
| stl.cpp:177:8:177:15 | call to basic_string | stl.cpp:177:3:177:15 | ... = ... | |
|
||||
| stl.cpp:177:8:177:15 | call to basic_string | stl.cpp:181:8:181:9 | s3 | |
|
||||
| stl.cpp:185:15:185:16 | call to basic_string | stl.cpp:186:20:186:21 | s1 | |
|
||||
| stl.cpp:185:15:185:16 | call to basic_string | stl.cpp:188:8:188:9 | s1 | |
|
||||
| stl.cpp:185:15:185:16 | call to basic_string | stl.cpp:190:8:190:9 | s1 | |
|
||||
| stl.cpp:186:20:186:21 | s1 | stl.cpp:191:8:191:9 | s2 | |
|
||||
| stl.cpp:188:8:188:9 | s1 | stl.cpp:188:3:188:9 | ... = ... | |
|
||||
| stl.cpp:188:8:188:9 | s1 | stl.cpp:192:8:192:9 | s3 | |
|
||||
| stl.cpp:196:19:196:40 | call to basic_string | stl.cpp:200:8:200:9 | s1 | |
|
||||
| stl.cpp:196:32:196:37 | call to source | stl.cpp:196:19:196:40 | call to basic_string | TAINT |
|
||||
| stl.cpp:198:8:198:28 | call to basic_string | stl.cpp:198:3:198:28 | ... = ... | |
|
||||
| stl.cpp:198:8:198:28 | call to basic_string | stl.cpp:201:8:201:9 | s2 | |
|
||||
| stl.cpp:198:20:198:25 | call to source | stl.cpp:198:8:198:28 | call to basic_string | TAINT |
|
||||
| string.cpp:24:12:24:17 | call to source | string.cpp:28:7:28:7 | a | |
|
||||
| string.cpp:25:16:25:20 | 123 | string.cpp:25:16:25:21 | call to basic_string | TAINT |
|
||||
| string.cpp:25:16:25:21 | call to basic_string | string.cpp:29:7:29:7 | b | |
|
||||
| string.cpp:25:16:25:21 | call to basic_string | string.cpp:31:7:31:7 | b | |
|
||||
| string.cpp:26:16:26:21 | call to source | string.cpp:26:16:26:24 | call to basic_string | TAINT |
|
||||
| string.cpp:26:16:26:24 | call to basic_string | string.cpp:30:7:30:7 | c | |
|
||||
| string.cpp:26:16:26:24 | call to basic_string | string.cpp:32:7:32:7 | c | |
|
||||
| string.cpp:31:7:31:7 | b | string.cpp:31:9:31:13 | call to c_str | TAINT |
|
||||
| string.cpp:32:7:32:7 | c | string.cpp:32:9:32:13 | call to c_str | TAINT |
|
||||
| string.cpp:37:16:37:28 | call to basic_string | string.cpp:38:7:38:11 | path1 | |
|
||||
| string.cpp:37:17:37:26 | call to user_input | string.cpp:37:16:37:28 | call to basic_string | TAINT |
|
||||
| string.cpp:38:7:38:11 | path1 | string.cpp:38:13:38:17 | call to c_str | TAINT |
|
||||
| string.cpp:41:10:41:19 | call to user_input | string.cpp:41:10:41:21 | call to basic_string | TAINT |
|
||||
| string.cpp:41:10:41:21 | call to basic_string | string.cpp:41:2:41:21 | ... = ... | |
|
||||
| string.cpp:41:10:41:21 | call to basic_string | string.cpp:42:7:42:11 | path2 | |
|
||||
| string.cpp:42:7:42:11 | path2 | string.cpp:42:13:42:17 | call to c_str | TAINT |
|
||||
| string.cpp:44:15:44:24 | call to user_input | string.cpp:44:15:44:27 | call to basic_string | TAINT |
|
||||
| string.cpp:44:15:44:27 | call to basic_string | string.cpp:45:7:45:11 | path3 | |
|
||||
| string.cpp:45:7:45:11 | path3 | string.cpp:45:13:45:17 | call to c_str | TAINT |
|
||||
| string.cpp:50:19:50:24 | call to source | string.cpp:53:17:53:18 | cs | |
|
||||
| string.cpp:50:19:50:24 | call to source | string.cpp:55:7:55:8 | cs | |
|
||||
| string.cpp:53:17:53:18 | cs | string.cpp:53:17:53:19 | call to basic_string | TAINT |
|
||||
| string.cpp:53:17:53:19 | call to basic_string | string.cpp:56:7:56:8 | ss | |
|
||||
| string.cpp:61:19:61:24 | call to source | string.cpp:64:17:64:18 | cs | |
|
||||
| string.cpp:64:17:64:18 | cs | string.cpp:64:17:64:19 | call to basic_string | TAINT |
|
||||
| string.cpp:64:17:64:19 | call to basic_string | string.cpp:67:7:67:8 | ss | |
|
||||
| string.cpp:64:17:64:19 | call to basic_string | string.cpp:70:7:70:8 | ss | |
|
||||
| string.cpp:67:7:67:8 | ss | string.cpp:67:10:67:14 | call to c_str | TAINT |
|
||||
| string.cpp:67:10:67:14 | call to c_str | string.cpp:67:2:67:16 | ... = ... | |
|
||||
| string.cpp:67:10:67:14 | call to c_str | string.cpp:69:7:69:8 | cs | |
|
||||
| string.cpp:76:18:76:24 | hello | string.cpp:76:18:76:25 | call to basic_string | TAINT |
|
||||
| string.cpp:76:18:76:25 | call to basic_string | string.cpp:81:8:81:9 | s1 | |
|
||||
| string.cpp:77:19:77:26 | call to basic_string | string.cpp:82:8:82:9 | s2 | |
|
||||
| string.cpp:77:20:77:26 | hello | string.cpp:77:19:77:26 | call to basic_string | TAINT |
|
||||
| string.cpp:79:8:79:14 | call to basic_string | string.cpp:79:3:79:14 | ... = ... | |
|
||||
| string.cpp:79:8:79:14 | call to basic_string | string.cpp:83:8:83:9 | s3 | |
|
||||
| string.cpp:79:8:79:14 | hello | string.cpp:79:8:79:14 | call to basic_string | TAINT |
|
||||
| string.cpp:87:18:87:23 | call to source | string.cpp:87:18:87:26 | call to basic_string | TAINT |
|
||||
| string.cpp:87:18:87:26 | call to basic_string | string.cpp:92:8:92:9 | s1 | |
|
||||
| string.cpp:88:19:88:27 | call to basic_string | string.cpp:93:8:93:9 | s2 | |
|
||||
| string.cpp:88:20:88:25 | call to source | string.cpp:88:19:88:27 | call to basic_string | TAINT |
|
||||
| string.cpp:90:8:90:13 | call to source | string.cpp:90:8:90:15 | call to basic_string | TAINT |
|
||||
| string.cpp:90:8:90:15 | call to basic_string | string.cpp:90:3:90:15 | ... = ... | |
|
||||
| string.cpp:90:8:90:15 | call to basic_string | string.cpp:94:8:94:9 | s3 | |
|
||||
| string.cpp:98:15:98:16 | call to basic_string | string.cpp:99:20:99:21 | s1 | |
|
||||
| string.cpp:98:15:98:16 | call to basic_string | string.cpp:101:8:101:9 | s1 | |
|
||||
| string.cpp:98:15:98:16 | call to basic_string | string.cpp:103:8:103:9 | s1 | |
|
||||
| string.cpp:99:20:99:21 | s1 | string.cpp:104:8:104:9 | s2 | |
|
||||
| string.cpp:101:8:101:9 | s1 | string.cpp:101:3:101:9 | ... = ... | |
|
||||
| string.cpp:101:8:101:9 | s1 | string.cpp:105:8:105:9 | s3 | |
|
||||
| string.cpp:109:19:109:40 | call to basic_string | string.cpp:113:8:113:9 | s1 | |
|
||||
| string.cpp:109:32:109:37 | call to source | string.cpp:109:19:109:40 | call to basic_string | TAINT |
|
||||
| string.cpp:111:8:111:28 | call to basic_string | string.cpp:111:3:111:28 | ... = ... | |
|
||||
| string.cpp:111:8:111:28 | call to basic_string | string.cpp:114:8:114:9 | s2 | |
|
||||
| string.cpp:111:20:111:25 | call to source | string.cpp:111:8:111:28 | call to basic_string | TAINT |
|
||||
| string.cpp:119:16:119:21 | call to source | string.cpp:119:16:119:24 | call to basic_string | TAINT |
|
||||
| string.cpp:119:16:119:24 | call to basic_string | string.cpp:120:15:120:15 | s | |
|
||||
| string.cpp:119:16:119:24 | call to basic_string | string.cpp:124:33:124:33 | s | |
|
||||
| string.cpp:119:16:119:24 | call to basic_string | string.cpp:124:50:124:50 | s | |
|
||||
| string.cpp:119:16:119:24 | call to basic_string | string.cpp:128:16:128:16 | s | |
|
||||
| string.cpp:120:15:120:15 | call to begin | string.cpp:120:15:120:15 | (__begin) | |
|
||||
| string.cpp:120:15:120:15 | call to begin | string.cpp:120:15:120:15 | (__begin) | |
|
||||
| string.cpp:120:15:120:15 | call to begin | string.cpp:120:15:120:15 | (__begin) | |
|
||||
| string.cpp:120:15:120:15 | call to end | string.cpp:120:15:120:15 | (__end) | |
|
||||
| string.cpp:120:15:120:15 | call to operator* | string.cpp:121:8:121:8 | c | |
|
||||
| string.cpp:120:15:120:15 | ref arg (__begin) | string.cpp:120:15:120:15 | (__begin) | |
|
||||
| string.cpp:120:15:120:15 | ref arg (__begin) | string.cpp:120:15:120:15 | (__begin) | |
|
||||
| string.cpp:120:15:120:15 | ref arg (__begin) | string.cpp:120:15:120:15 | (__begin) | |
|
||||
| string.cpp:120:15:120:15 | ref arg (__range) | string.cpp:120:15:120:15 | (__range) | |
|
||||
| string.cpp:120:15:120:15 | s | string.cpp:120:15:120:15 | (__range) | |
|
||||
| string.cpp:120:15:120:15 | s | string.cpp:120:15:120:15 | (__range) | |
|
||||
| string.cpp:120:15:120:15 | s | string.cpp:120:15:120:15 | call to operator* | TAINT |
|
||||
| string.cpp:124:33:124:33 | ref arg s | string.cpp:124:50:124:50 | s | |
|
||||
| string.cpp:124:33:124:33 | ref arg s | string.cpp:128:16:128:16 | s | |
|
||||
| string.cpp:124:35:124:39 | call to begin | string.cpp:124:44:124:45 | it | |
|
||||
| string.cpp:124:35:124:39 | call to begin | string.cpp:124:61:124:62 | it | |
|
||||
| string.cpp:124:35:124:39 | call to begin | string.cpp:125:9:125:10 | it | |
|
||||
| string.cpp:124:50:124:50 | ref arg s | string.cpp:124:50:124:50 | s | |
|
||||
| string.cpp:124:50:124:50 | ref arg s | string.cpp:128:16:128:16 | s | |
|
||||
| string.cpp:124:61:124:62 | ref arg it | string.cpp:124:44:124:45 | it | |
|
||||
| string.cpp:124:61:124:62 | ref arg it | string.cpp:124:61:124:62 | it | |
|
||||
| string.cpp:124:61:124:62 | ref arg it | string.cpp:125:9:125:10 | it | |
|
||||
| string.cpp:128:16:128:16 | call to begin | string.cpp:128:16:128:16 | (__begin) | |
|
||||
| string.cpp:128:16:128:16 | call to begin | string.cpp:128:16:128:16 | (__begin) | |
|
||||
| string.cpp:128:16:128:16 | call to begin | string.cpp:128:16:128:16 | (__begin) | |
|
||||
| string.cpp:128:16:128:16 | call to end | string.cpp:128:16:128:16 | (__end) | |
|
||||
| string.cpp:128:16:128:16 | call to operator* | string.cpp:129:8:129:8 | c | |
|
||||
| string.cpp:128:16:128:16 | ref arg (__begin) | string.cpp:128:16:128:16 | (__begin) | |
|
||||
| string.cpp:128:16:128:16 | ref arg (__begin) | string.cpp:128:16:128:16 | (__begin) | |
|
||||
| string.cpp:128:16:128:16 | ref arg (__begin) | string.cpp:128:16:128:16 | (__begin) | |
|
||||
| string.cpp:128:16:128:16 | ref arg (__range) | string.cpp:128:16:128:16 | (__range) | |
|
||||
| string.cpp:128:16:128:16 | s | string.cpp:128:16:128:16 | (__range) | |
|
||||
| string.cpp:128:16:128:16 | s | string.cpp:128:16:128:16 | (__range) | |
|
||||
| string.cpp:128:16:128:16 | s | string.cpp:128:16:128:16 | call to operator* | TAINT |
|
||||
| string.cpp:132:28:132:33 | call to source | string.cpp:132:28:132:36 | call to basic_string | TAINT |
|
||||
| string.cpp:132:28:132:36 | call to basic_string | string.cpp:133:22:133:28 | const_s | |
|
||||
| string.cpp:133:22:133:22 | call to begin | string.cpp:133:22:133:22 | (__begin) | |
|
||||
| string.cpp:133:22:133:22 | call to begin | string.cpp:133:22:133:22 | (__begin) | |
|
||||
| string.cpp:133:22:133:22 | call to begin | string.cpp:133:22:133:22 | (__begin) | |
|
||||
| string.cpp:133:22:133:22 | call to end | string.cpp:133:22:133:22 | (__end) | |
|
||||
| string.cpp:133:22:133:22 | call to operator* | string.cpp:134:8:134:8 | c | |
|
||||
| string.cpp:133:22:133:22 | ref arg (__begin) | string.cpp:133:22:133:22 | (__begin) | |
|
||||
| string.cpp:133:22:133:22 | ref arg (__begin) | string.cpp:133:22:133:22 | (__begin) | |
|
||||
| string.cpp:133:22:133:22 | ref arg (__begin) | string.cpp:133:22:133:22 | (__begin) | |
|
||||
| string.cpp:133:22:133:28 | const_s | string.cpp:133:22:133:22 | (__range) | |
|
||||
| string.cpp:133:22:133:28 | const_s | string.cpp:133:22:133:22 | (__range) | |
|
||||
| string.cpp:133:22:133:28 | const_s | string.cpp:133:22:133:22 | call to operator* | TAINT |
|
||||
| string.cpp:140:18:140:24 | hello | string.cpp:140:18:140:25 | call to basic_string | TAINT |
|
||||
| string.cpp:140:18:140:25 | call to basic_string | string.cpp:143:8:143:9 | s1 | |
|
||||
| string.cpp:140:18:140:25 | call to basic_string | string.cpp:143:13:143:14 | s1 | |
|
||||
| string.cpp:140:18:140:25 | call to basic_string | string.cpp:144:8:144:9 | s1 | |
|
||||
| string.cpp:140:18:140:25 | call to basic_string | string.cpp:145:13:145:14 | s1 | |
|
||||
| string.cpp:140:18:140:25 | call to basic_string | string.cpp:148:8:148:9 | s1 | |
|
||||
| string.cpp:140:18:140:25 | call to basic_string | string.cpp:149:8:149:9 | s1 | |
|
||||
| string.cpp:141:18:141:23 | call to source | string.cpp:141:18:141:26 | call to basic_string | TAINT |
|
||||
| string.cpp:141:18:141:26 | call to basic_string | string.cpp:144:13:144:14 | s2 | |
|
||||
| string.cpp:141:18:141:26 | call to basic_string | string.cpp:145:8:145:9 | s2 | |
|
||||
| string.cpp:141:18:141:26 | call to basic_string | string.cpp:146:8:146:9 | s2 | |
|
||||
| string.cpp:141:18:141:26 | call to basic_string | string.cpp:146:13:146:14 | s2 | |
|
||||
| string.cpp:143:8:143:9 | s1 | string.cpp:143:11:143:11 | call to operator+ | TAINT |
|
||||
| string.cpp:143:13:143:14 | s1 | string.cpp:143:11:143:11 | call to operator+ | TAINT |
|
||||
| string.cpp:144:8:144:9 | s1 | string.cpp:144:11:144:11 | call to operator+ | TAINT |
|
||||
| string.cpp:144:13:144:14 | s2 | string.cpp:144:11:144:11 | call to operator+ | TAINT |
|
||||
| string.cpp:145:8:145:9 | s2 | string.cpp:145:11:145:11 | call to operator+ | TAINT |
|
||||
| string.cpp:145:13:145:14 | s1 | string.cpp:145:11:145:11 | call to operator+ | TAINT |
|
||||
| string.cpp:146:8:146:9 | s2 | string.cpp:146:11:146:11 | call to operator+ | TAINT |
|
||||
| string.cpp:146:13:146:14 | s2 | string.cpp:146:11:146:11 | call to operator+ | TAINT |
|
||||
| string.cpp:148:8:148:9 | s1 | string.cpp:148:11:148:11 | call to operator+ | TAINT |
|
||||
| string.cpp:148:13:148:20 | world | string.cpp:148:11:148:11 | call to operator+ | TAINT |
|
||||
| string.cpp:149:8:149:9 | s1 | string.cpp:149:11:149:11 | call to operator+ | TAINT |
|
||||
| string.cpp:149:13:149:18 | call to source | string.cpp:149:11:149:11 | call to operator+ | TAINT |
|
||||
| string.cpp:153:18:153:22 | abc | string.cpp:153:18:153:23 | call to basic_string | TAINT |
|
||||
| string.cpp:153:18:153:23 | call to basic_string | string.cpp:157:8:157:9 | s3 | |
|
||||
| string.cpp:153:18:153:23 | call to basic_string | string.cpp:160:8:160:9 | s3 | |
|
||||
| string.cpp:153:18:153:23 | call to basic_string | string.cpp:164:8:164:9 | s3 | |
|
||||
| string.cpp:153:18:153:23 | call to basic_string | string.cpp:169:8:169:9 | s3 | |
|
||||
| string.cpp:153:18:153:23 | call to basic_string | string.cpp:173:8:173:9 | s3 | |
|
||||
| string.cpp:154:18:154:23 | call to source | string.cpp:154:18:154:26 | call to basic_string | TAINT |
|
||||
| string.cpp:154:18:154:26 | call to basic_string | string.cpp:157:13:157:14 | s4 | |
|
||||
| string.cpp:154:18:154:26 | call to basic_string | string.cpp:161:9:161:10 | s4 | |
|
||||
| string.cpp:154:18:154:26 | call to basic_string | string.cpp:170:13:170:14 | s4 | |
|
||||
| string.cpp:157:8:157:9 | s3 | string.cpp:157:11:157:11 | call to operator+ | TAINT |
|
||||
| string.cpp:157:11:157:11 | call to operator+ | string.cpp:157:3:157:14 | ... = ... | |
|
||||
| string.cpp:157:11:157:11 | call to operator+ | string.cpp:158:8:158:9 | s5 | |
|
||||
| string.cpp:157:13:157:14 | s4 | string.cpp:157:11:157:11 | call to operator+ | TAINT |
|
||||
| string.cpp:160:8:160:9 | s3 | string.cpp:160:3:160:9 | ... = ... | |
|
||||
| string.cpp:160:8:160:9 | s3 | string.cpp:161:3:161:4 | s6 | |
|
||||
| string.cpp:160:8:160:9 | s3 | string.cpp:162:8:162:9 | s6 | |
|
||||
| string.cpp:161:3:161:4 | ref arg s6 | string.cpp:162:8:162:9 | s6 | |
|
||||
| string.cpp:161:9:161:10 | s4 | string.cpp:161:3:161:4 | ref arg s6 | TAINT |
|
||||
| string.cpp:161:9:161:10 | s4 | string.cpp:161:6:161:6 | call to operator+= | TAINT |
|
||||
| string.cpp:164:8:164:9 | s3 | string.cpp:164:3:164:9 | ... = ... | |
|
||||
| string.cpp:164:8:164:9 | s3 | string.cpp:165:3:165:4 | s7 | |
|
||||
| string.cpp:164:8:164:9 | s3 | string.cpp:166:3:166:4 | s7 | |
|
||||
| string.cpp:164:8:164:9 | s3 | string.cpp:167:8:167:9 | s7 | |
|
||||
| string.cpp:165:3:165:4 | ref arg s7 | string.cpp:166:3:166:4 | s7 | |
|
||||
| string.cpp:165:3:165:4 | ref arg s7 | string.cpp:167:8:167:9 | s7 | |
|
||||
| string.cpp:165:9:165:14 | call to source | string.cpp:165:3:165:4 | ref arg s7 | TAINT |
|
||||
| string.cpp:165:9:165:14 | call to source | string.cpp:165:6:165:6 | call to operator+= | TAINT |
|
||||
| string.cpp:166:3:166:4 | ref arg s7 | string.cpp:167:8:167:9 | s7 | |
|
||||
| string.cpp:166:9:166:11 | | string.cpp:166:3:166:4 | ref arg s7 | TAINT |
|
||||
| string.cpp:166:9:166:11 | | string.cpp:166:6:166:6 | call to operator+= | TAINT |
|
||||
| string.cpp:169:8:169:9 | s3 | string.cpp:169:3:169:9 | ... = ... | |
|
||||
| string.cpp:169:8:169:9 | s3 | string.cpp:170:3:170:4 | s8 | |
|
||||
| string.cpp:169:8:169:9 | s3 | string.cpp:171:8:171:9 | s8 | |
|
||||
| string.cpp:170:3:170:4 | ref arg s8 | string.cpp:171:8:171:9 | s8 | |
|
||||
| string.cpp:170:13:170:14 | s4 | string.cpp:170:3:170:4 | ref arg s8 | TAINT |
|
||||
| string.cpp:170:13:170:14 | s4 | string.cpp:170:6:170:11 | call to append | TAINT |
|
||||
| string.cpp:173:8:173:9 | s3 | string.cpp:173:3:173:9 | ... = ... | |
|
||||
| string.cpp:173:8:173:9 | s3 | string.cpp:174:3:174:4 | s9 | |
|
||||
| string.cpp:173:8:173:9 | s3 | string.cpp:175:3:175:4 | s9 | |
|
||||
| string.cpp:173:8:173:9 | s3 | string.cpp:176:8:176:9 | s9 | |
|
||||
| string.cpp:174:3:174:4 | ref arg s9 | string.cpp:175:3:175:4 | s9 | |
|
||||
| string.cpp:174:3:174:4 | ref arg s9 | string.cpp:176:8:176:9 | s9 | |
|
||||
| string.cpp:174:13:174:18 | call to source | string.cpp:174:3:174:4 | ref arg s9 | TAINT |
|
||||
| string.cpp:174:13:174:18 | call to source | string.cpp:174:6:174:11 | call to append | TAINT |
|
||||
| string.cpp:175:3:175:4 | ref arg s9 | string.cpp:176:8:176:9 | s9 | |
|
||||
| string.cpp:175:13:175:15 | | string.cpp:175:3:175:4 | ref arg s9 | TAINT |
|
||||
| string.cpp:175:13:175:15 | | string.cpp:175:6:175:11 | call to append | TAINT |
|
||||
| string.cpp:180:19:180:23 | abc | string.cpp:180:19:180:24 | call to basic_string | TAINT |
|
||||
| string.cpp:180:19:180:24 | call to basic_string | string.cpp:183:3:183:5 | s10 | |
|
||||
| string.cpp:180:19:180:24 | call to basic_string | string.cpp:184:8:184:10 | s10 | |
|
||||
| string.cpp:181:12:181:26 | call to source | string.cpp:183:17:183:17 | c | |
|
||||
| string.cpp:183:3:183:5 | ref arg s10 | string.cpp:184:8:184:10 | s10 | |
|
||||
| string.cpp:183:17:183:17 | c | string.cpp:183:3:183:5 | ref arg s10 | TAINT |
|
||||
| string.cpp:183:17:183:17 | c | string.cpp:183:7:183:12 | call to append | TAINT |
|
||||
| stringstream.cpp:13:20:13:22 | call to basic_stringstream | stringstream.cpp:16:2:16:4 | ss1 | |
|
||||
| stringstream.cpp:13:20:13:22 | call to basic_stringstream | stringstream.cpp:22:7:22:9 | ss1 | |
|
||||
| stringstream.cpp:13:20:13:22 | call to basic_stringstream | stringstream.cpp:27:7:27:9 | ss1 | |
|
||||
| stringstream.cpp:13:25:13:27 | call to basic_stringstream | stringstream.cpp:17:2:17:4 | ss2 | |
|
||||
| stringstream.cpp:13:25:13:27 | call to basic_stringstream | stringstream.cpp:23:7:23:9 | ss2 | |
|
||||
| stringstream.cpp:13:25:13:27 | call to basic_stringstream | stringstream.cpp:28:7:28:9 | ss2 | |
|
||||
| stringstream.cpp:13:30:13:32 | call to basic_stringstream | stringstream.cpp:18:2:18:4 | ss3 | |
|
||||
| stringstream.cpp:13:30:13:32 | call to basic_stringstream | stringstream.cpp:24:7:24:9 | ss3 | |
|
||||
| stringstream.cpp:13:30:13:32 | call to basic_stringstream | stringstream.cpp:29:7:29:9 | ss3 | |
|
||||
| stringstream.cpp:13:35:13:37 | call to basic_stringstream | stringstream.cpp:19:2:19:4 | ss4 | |
|
||||
| stringstream.cpp:13:35:13:37 | call to basic_stringstream | stringstream.cpp:25:7:25:9 | ss4 | |
|
||||
| stringstream.cpp:13:35:13:37 | call to basic_stringstream | stringstream.cpp:30:7:30:9 | ss4 | |
|
||||
| stringstream.cpp:13:40:13:42 | call to basic_stringstream | stringstream.cpp:20:2:20:4 | ss5 | |
|
||||
| stringstream.cpp:13:40:13:42 | call to basic_stringstream | stringstream.cpp:26:7:26:9 | ss5 | |
|
||||
| stringstream.cpp:13:40:13:42 | call to basic_stringstream | stringstream.cpp:31:7:31:9 | ss5 | |
|
||||
| stringstream.cpp:14:16:14:21 | call to source | stringstream.cpp:14:16:14:24 | call to basic_string | TAINT |
|
||||
| stringstream.cpp:14:16:14:24 | call to basic_string | stringstream.cpp:20:9:20:9 | t | |
|
||||
| stringstream.cpp:16:2:16:4 | ref arg ss1 | stringstream.cpp:22:7:22:9 | ss1 | |
|
||||
| stringstream.cpp:16:2:16:4 | ref arg ss1 | stringstream.cpp:27:7:27:9 | ss1 | |
|
||||
| stringstream.cpp:17:2:17:4 | ref arg ss2 | stringstream.cpp:23:7:23:9 | ss2 | |
|
||||
| stringstream.cpp:17:2:17:4 | ref arg ss2 | stringstream.cpp:28:7:28:9 | ss2 | |
|
||||
| stringstream.cpp:18:2:18:4 | ref arg ss3 | stringstream.cpp:24:7:24:9 | ss3 | |
|
||||
| stringstream.cpp:18:2:18:4 | ref arg ss3 | stringstream.cpp:29:7:29:9 | ss3 | |
|
||||
| stringstream.cpp:19:2:19:4 | ref arg ss4 | stringstream.cpp:25:7:25:9 | ss4 | |
|
||||
| stringstream.cpp:19:2:19:4 | ref arg ss4 | stringstream.cpp:30:7:30:9 | ss4 | |
|
||||
| stringstream.cpp:20:2:20:4 | ref arg ss5 | stringstream.cpp:26:7:26:9 | ss5 | |
|
||||
| stringstream.cpp:20:2:20:4 | ref arg ss5 | stringstream.cpp:31:7:31:9 | ss5 | |
|
||||
| stringstream.cpp:34:32:34:37 | source | stringstream.cpp:39:9:39:14 | source | |
|
||||
| stringstream.cpp:36:20:36:22 | call to basic_stringstream | stringstream.cpp:38:2:38:4 | ss1 | |
|
||||
| stringstream.cpp:36:20:36:22 | call to basic_stringstream | stringstream.cpp:41:7:41:9 | ss1 | |
|
||||
| stringstream.cpp:36:20:36:22 | call to basic_stringstream | stringstream.cpp:43:7:43:9 | ss1 | |
|
||||
| stringstream.cpp:36:25:36:27 | call to basic_stringstream | stringstream.cpp:39:2:39:4 | ss2 | |
|
||||
| stringstream.cpp:36:25:36:27 | call to basic_stringstream | stringstream.cpp:42:7:42:9 | ss2 | |
|
||||
| stringstream.cpp:36:25:36:27 | call to basic_stringstream | stringstream.cpp:44:7:44:9 | ss2 | |
|
||||
| stringstream.cpp:38:2:38:4 | ref arg ss1 | stringstream.cpp:41:7:41:9 | ss1 | |
|
||||
| stringstream.cpp:38:2:38:4 | ref arg ss1 | stringstream.cpp:43:7:43:9 | ss1 | |
|
||||
| stringstream.cpp:39:2:39:4 | ref arg ss2 | stringstream.cpp:42:7:42:9 | ss2 | |
|
||||
| stringstream.cpp:39:2:39:4 | ref arg ss2 | stringstream.cpp:44:7:44:9 | ss2 | |
|
||||
| structlikeclass.cpp:5:7:5:7 | Unknown literal | structlikeclass.cpp:5:7:5:7 | constructor init of field v | TAINT |
|
||||
| structlikeclass.cpp:5:7:5:7 | Unknown literal | structlikeclass.cpp:5:7:5:7 | constructor init of field v | TAINT |
|
||||
| structlikeclass.cpp:5:7:5:7 | this | structlikeclass.cpp:5:7:5:7 | constructor init of field v [pre-this] | |
|
||||
@@ -1199,3 +1330,57 @@
|
||||
| taint.cpp:483:18:483:19 | ref arg & ... | taint.cpp:483:19:483:19 | n [inner post update] | |
|
||||
| taint.cpp:483:19:483:19 | n | taint.cpp:483:18:483:19 | & ... | |
|
||||
| taint.cpp:483:28:483:34 | source1 | taint.cpp:483:11:483:15 | ref arg & ... | TAINT |
|
||||
| vector.cpp:8:43:8:49 | source1 | vector.cpp:12:21:12:27 | source1 | |
|
||||
| vector.cpp:8:43:8:49 | source1 | vector.cpp:26:33:26:39 | source1 | |
|
||||
| vector.cpp:12:21:12:27 | source1 | vector.cpp:12:21:12:28 | call to vector | TAINT |
|
||||
| vector.cpp:12:21:12:28 | call to vector | vector.cpp:14:14:14:14 | v | |
|
||||
| vector.cpp:12:21:12:28 | call to vector | vector.cpp:18:38:18:38 | v | |
|
||||
| vector.cpp:12:21:12:28 | call to vector | vector.cpp:18:55:18:55 | v | |
|
||||
| vector.cpp:12:21:12:28 | call to vector | vector.cpp:22:15:22:15 | v | |
|
||||
| vector.cpp:14:14:14:14 | call to begin | vector.cpp:14:14:14:14 | (__begin) | |
|
||||
| vector.cpp:14:14:14:14 | call to begin | vector.cpp:14:14:14:14 | (__begin) | |
|
||||
| vector.cpp:14:14:14:14 | call to begin | vector.cpp:14:14:14:14 | (__begin) | |
|
||||
| vector.cpp:14:14:14:14 | call to end | vector.cpp:14:14:14:14 | (__end) | |
|
||||
| vector.cpp:14:14:14:14 | call to operator* | vector.cpp:15:8:15:8 | x | |
|
||||
| vector.cpp:14:14:14:14 | ref arg (__begin) | vector.cpp:14:14:14:14 | (__begin) | |
|
||||
| vector.cpp:14:14:14:14 | ref arg (__begin) | vector.cpp:14:14:14:14 | (__begin) | |
|
||||
| vector.cpp:14:14:14:14 | ref arg (__begin) | vector.cpp:14:14:14:14 | (__begin) | |
|
||||
| vector.cpp:14:14:14:14 | ref arg (__range) | vector.cpp:14:14:14:14 | (__range) | |
|
||||
| vector.cpp:14:14:14:14 | v | vector.cpp:14:14:14:14 | (__range) | |
|
||||
| vector.cpp:14:14:14:14 | v | vector.cpp:14:14:14:14 | (__range) | |
|
||||
| vector.cpp:14:14:14:14 | v | vector.cpp:14:14:14:14 | call to operator* | TAINT |
|
||||
| vector.cpp:18:38:18:38 | ref arg v | vector.cpp:18:55:18:55 | v | |
|
||||
| vector.cpp:18:38:18:38 | ref arg v | vector.cpp:22:15:22:15 | v | |
|
||||
| vector.cpp:18:40:18:44 | call to begin | vector.cpp:18:49:18:50 | it | |
|
||||
| vector.cpp:18:40:18:44 | call to begin | vector.cpp:18:66:18:67 | it | |
|
||||
| vector.cpp:18:40:18:44 | call to begin | vector.cpp:19:9:19:10 | it | |
|
||||
| vector.cpp:18:55:18:55 | ref arg v | vector.cpp:18:55:18:55 | v | |
|
||||
| vector.cpp:18:55:18:55 | ref arg v | vector.cpp:22:15:22:15 | v | |
|
||||
| vector.cpp:18:66:18:67 | ref arg it | vector.cpp:18:49:18:50 | it | |
|
||||
| vector.cpp:18:66:18:67 | ref arg it | vector.cpp:18:66:18:67 | it | |
|
||||
| vector.cpp:18:66:18:67 | ref arg it | vector.cpp:19:9:19:10 | it | |
|
||||
| vector.cpp:22:15:22:15 | call to begin | vector.cpp:22:15:22:15 | (__begin) | |
|
||||
| vector.cpp:22:15:22:15 | call to begin | vector.cpp:22:15:22:15 | (__begin) | |
|
||||
| vector.cpp:22:15:22:15 | call to begin | vector.cpp:22:15:22:15 | (__begin) | |
|
||||
| vector.cpp:22:15:22:15 | call to end | vector.cpp:22:15:22:15 | (__end) | |
|
||||
| vector.cpp:22:15:22:15 | call to operator* | vector.cpp:23:8:23:8 | x | |
|
||||
| vector.cpp:22:15:22:15 | ref arg (__begin) | vector.cpp:22:15:22:15 | (__begin) | |
|
||||
| vector.cpp:22:15:22:15 | ref arg (__begin) | vector.cpp:22:15:22:15 | (__begin) | |
|
||||
| vector.cpp:22:15:22:15 | ref arg (__begin) | vector.cpp:22:15:22:15 | (__begin) | |
|
||||
| vector.cpp:22:15:22:15 | ref arg (__range) | vector.cpp:22:15:22:15 | (__range) | |
|
||||
| vector.cpp:22:15:22:15 | v | vector.cpp:22:15:22:15 | (__range) | |
|
||||
| vector.cpp:22:15:22:15 | v | vector.cpp:22:15:22:15 | (__range) | |
|
||||
| vector.cpp:22:15:22:15 | v | vector.cpp:22:15:22:15 | call to operator* | TAINT |
|
||||
| vector.cpp:26:33:26:39 | source1 | vector.cpp:26:33:26:40 | call to vector | TAINT |
|
||||
| vector.cpp:26:33:26:40 | call to vector | vector.cpp:27:21:27:27 | const_v | |
|
||||
| vector.cpp:27:21:27:21 | call to begin | vector.cpp:27:21:27:21 | (__begin) | |
|
||||
| vector.cpp:27:21:27:21 | call to begin | vector.cpp:27:21:27:21 | (__begin) | |
|
||||
| vector.cpp:27:21:27:21 | call to begin | vector.cpp:27:21:27:21 | (__begin) | |
|
||||
| vector.cpp:27:21:27:21 | call to end | vector.cpp:27:21:27:21 | (__end) | |
|
||||
| vector.cpp:27:21:27:21 | call to operator* | vector.cpp:28:8:28:8 | x | |
|
||||
| vector.cpp:27:21:27:21 | ref arg (__begin) | vector.cpp:27:21:27:21 | (__begin) | |
|
||||
| vector.cpp:27:21:27:21 | ref arg (__begin) | vector.cpp:27:21:27:21 | (__begin) | |
|
||||
| vector.cpp:27:21:27:21 | ref arg (__begin) | vector.cpp:27:21:27:21 | (__begin) | |
|
||||
| vector.cpp:27:21:27:27 | const_v | vector.cpp:27:21:27:21 | (__range) | |
|
||||
| vector.cpp:27:21:27:27 | const_v | vector.cpp:27:21:27:21 | (__range) | |
|
||||
| vector.cpp:27:21:27:27 | const_v | vector.cpp:27:21:27:21 | call to operator* | TAINT |
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
|
||||
typedef unsigned long size_t;
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<class charT> struct char_traits;
|
||||
|
||||
typedef size_t streamsize;
|
||||
|
||||
template <class T> class allocator {
|
||||
public:
|
||||
allocator() throw();
|
||||
};
|
||||
|
||||
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
|
||||
class basic_string {
|
||||
public:
|
||||
explicit basic_string(const Allocator& a = Allocator());
|
||||
basic_string(const charT* s, const Allocator& a = Allocator());
|
||||
|
||||
const charT* c_str() const;
|
||||
};
|
||||
|
||||
typedef basic_string<char> string;
|
||||
|
||||
template <class charT, class traits = char_traits<charT> >
|
||||
class basic_istream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
|
||||
public:
|
||||
basic_istream<charT,traits>& operator>>(int& n);
|
||||
};
|
||||
|
||||
template <class charT, class traits = char_traits<charT> >
|
||||
class basic_ostream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
|
||||
public:
|
||||
typedef charT char_type;
|
||||
basic_ostream<charT,traits>& write(const char_type* s, streamsize n);
|
||||
|
||||
basic_ostream<charT, traits>& operator<<(int n);
|
||||
};
|
||||
|
||||
template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*);
|
||||
template<class charT, class traits, class Allocator> basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>& os, const basic_string<charT, traits, Allocator>& str);
|
||||
|
||||
template<class charT, class traits = char_traits<charT>>
|
||||
class basic_iostream : public basic_istream<charT, traits>, public basic_ostream<charT, traits> {
|
||||
public:
|
||||
};
|
||||
|
||||
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT>>
|
||||
class basic_stringstream : public basic_iostream<charT, traits> {
|
||||
public:
|
||||
explicit basic_stringstream(/*ios_base::openmode which = ios_base::out|ios_base::in - not needed for this test*/);
|
||||
|
||||
basic_string<charT, traits, Allocator> str() const;
|
||||
};
|
||||
|
||||
using stringstream = basic_stringstream<char>;
|
||||
}
|
||||
|
||||
char *source();
|
||||
void sink(const char *s) {};
|
||||
void sink(const std::string &s) {};
|
||||
void sink(const std::stringstream &s) {};
|
||||
|
||||
void test_string()
|
||||
{
|
||||
char *a = source();
|
||||
std::string b("123");
|
||||
std::string c(source());
|
||||
|
||||
sink(a); // tainted
|
||||
sink(b);
|
||||
sink(c); // tainted
|
||||
sink(b.c_str());
|
||||
sink(c.c_str()); // tainted
|
||||
}
|
||||
|
||||
void test_stringstream()
|
||||
{
|
||||
std::stringstream ss1, ss2, ss3, ss4, ss5;
|
||||
std::string t(source());
|
||||
|
||||
ss1 << "1234";
|
||||
ss2 << source();
|
||||
ss3 << "123" << source();
|
||||
ss4 << source() << "456";
|
||||
ss5 << t;
|
||||
|
||||
sink(ss1);
|
||||
sink(ss2); // tainted [NOT DETECTED]
|
||||
sink(ss3); // tainted [NOT DETECTED]
|
||||
sink(ss4); // tainted [NOT DETECTED]
|
||||
sink(ss5); // tainted [NOT DETECTED]
|
||||
sink(ss1.str());
|
||||
sink(ss2.str()); // tainted [NOT DETECTED]
|
||||
sink(ss3.str()); // tainted [NOT DETECTED]
|
||||
sink(ss4.str()); // tainted [NOT DETECTED]
|
||||
sink(ss5.str()); // tainted [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test_stringstream_int(int source)
|
||||
{
|
||||
std::stringstream ss1, ss2;
|
||||
|
||||
ss1 << 1234;
|
||||
ss2 << source;
|
||||
|
||||
sink(ss1);
|
||||
sink(ss2); // tainted [NOT DETECTED]
|
||||
sink(ss1.str());
|
||||
sink(ss2.str()); // tainted [NOT DETECTED]
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
char *user_input() {
|
||||
return source();
|
||||
}
|
||||
|
||||
void sink(const char *filename, const char *mode);
|
||||
|
||||
void test_strings2()
|
||||
{
|
||||
string path1 = user_input();
|
||||
sink(path1.c_str(), "r"); // tainted
|
||||
|
||||
string path2;
|
||||
path2 = user_input();
|
||||
sink(path2.c_str(), "r"); // tainted
|
||||
|
||||
string path3(user_input());
|
||||
sink(path3.c_str(), "r"); // tainted
|
||||
}
|
||||
|
||||
void test_string3()
|
||||
{
|
||||
const char *cs = source();
|
||||
|
||||
// convert char * -> std::string
|
||||
std::string ss(cs);
|
||||
|
||||
sink(cs); // tainted
|
||||
sink(ss); // tainted
|
||||
}
|
||||
|
||||
void test_string4()
|
||||
{
|
||||
const char *cs = source();
|
||||
|
||||
// convert char * -> std::string
|
||||
std::string ss(cs);
|
||||
|
||||
// convert back std::string -> char *
|
||||
cs = ss.c_str();
|
||||
|
||||
sink(cs); // tainted
|
||||
sink(ss); // tainted
|
||||
}
|
||||
|
||||
void test_string_constructors_assignments()
|
||||
{
|
||||
{
|
||||
std::string s1("hello");
|
||||
std::string s2 = "hello";
|
||||
std::string s3;
|
||||
s3 = "hello";
|
||||
|
||||
sink(s1);
|
||||
sink(s2);
|
||||
sink(s3);
|
||||
}
|
||||
|
||||
{
|
||||
std::string s1(source());
|
||||
std::string s2 = source();
|
||||
std::string s3;
|
||||
s3 = source();
|
||||
|
||||
sink(s1); // tainted
|
||||
sink(s2); // tainted
|
||||
sink(s3); // tainted
|
||||
}
|
||||
|
||||
{
|
||||
std::string s1;
|
||||
std::string s2 = s1;
|
||||
std::string s3;
|
||||
s3 = s1;
|
||||
|
||||
sink(s1);
|
||||
sink(s2);
|
||||
sink(s3);
|
||||
}
|
||||
|
||||
{
|
||||
std::string s1 = std::string(source());
|
||||
std::string s2;
|
||||
s2 = std::string(source());
|
||||
|
||||
sink(s1); // tainted
|
||||
sink(s2); // tainted
|
||||
}
|
||||
}
|
||||
|
||||
126
cpp/ql/test/library-tests/dataflow/taint-tests/stl.h
Normal file
126
cpp/ql/test/library-tests/dataflow/taint-tests/stl.h
Normal file
@@ -0,0 +1,126 @@
|
||||
|
||||
typedef unsigned long size_t;
|
||||
|
||||
// --- string ---
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<class charT> struct char_traits;
|
||||
|
||||
typedef size_t streamsize;
|
||||
|
||||
struct ptrdiff_t;
|
||||
|
||||
template <class iterator_category,
|
||||
class value_type,
|
||||
class difference_type = ptrdiff_t,
|
||||
class pointer_type = value_type*,
|
||||
class reference_type = value_type&>
|
||||
struct iterator {
|
||||
iterator &operator++();
|
||||
iterator operator++(int);
|
||||
bool operator==(iterator other) const;
|
||||
bool operator!=(iterator other) const;
|
||||
reference_type operator*() const;
|
||||
};
|
||||
|
||||
struct input_iterator_tag {};
|
||||
struct forward_iterator_tag : public input_iterator_tag {};
|
||||
struct bidirectional_iterator_tag : public forward_iterator_tag {};
|
||||
struct random_access_iterator_tag : public bidirectional_iterator_tag {};
|
||||
|
||||
template <class T> class allocator {
|
||||
public:
|
||||
allocator() throw();
|
||||
typedef size_t size_type;
|
||||
};
|
||||
|
||||
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
|
||||
class basic_string {
|
||||
public:
|
||||
typedef typename Allocator::size_type size_type;
|
||||
|
||||
explicit basic_string(const Allocator& a = Allocator());
|
||||
basic_string(const charT* s, const Allocator& a = Allocator());
|
||||
|
||||
const charT* c_str() const;
|
||||
|
||||
typedef std::iterator<random_access_iterator_tag, charT> iterator;
|
||||
typedef std::iterator<random_access_iterator_tag, const charT> const_iterator;
|
||||
|
||||
iterator begin();
|
||||
iterator end();
|
||||
const_iterator begin() const;
|
||||
const_iterator end() const;
|
||||
const_iterator cbegin() const;
|
||||
const_iterator cend() const;
|
||||
|
||||
template<class T> basic_string& operator+=(const T& t);
|
||||
basic_string& operator+=(const charT* s);
|
||||
basic_string& append(const basic_string& str);
|
||||
basic_string& append(const charT* s);
|
||||
basic_string& append(size_type n, charT c);
|
||||
};
|
||||
|
||||
template<class charT, class traits, class Allocator> basic_string<charT, traits, Allocator> operator+(const basic_string<charT, traits, Allocator>& lhs, const basic_string<charT, traits, Allocator>& rhs);
|
||||
template<class charT, class traits, class Allocator> basic_string<charT, traits, Allocator> operator+(const basic_string<charT, traits, Allocator>& lhs, const charT* rhs);
|
||||
|
||||
typedef basic_string<char> string;
|
||||
|
||||
template <class charT, class traits = char_traits<charT> >
|
||||
class basic_istream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
|
||||
public:
|
||||
basic_istream<charT,traits>& operator>>(int& n);
|
||||
};
|
||||
|
||||
template <class charT, class traits = char_traits<charT> >
|
||||
class basic_ostream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
|
||||
public:
|
||||
typedef charT char_type;
|
||||
basic_ostream<charT,traits>& write(const char_type* s, streamsize n);
|
||||
|
||||
basic_ostream<charT, traits>& operator<<(int n);
|
||||
};
|
||||
|
||||
template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*);
|
||||
template<class charT, class traits, class Allocator> basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>& os, const basic_string<charT, traits, Allocator>& str);
|
||||
|
||||
template<class charT, class traits = char_traits<charT>>
|
||||
class basic_iostream : public basic_istream<charT, traits>, public basic_ostream<charT, traits> {
|
||||
public:
|
||||
};
|
||||
|
||||
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT>>
|
||||
class basic_stringstream : public basic_iostream<charT, traits> {
|
||||
public:
|
||||
explicit basic_stringstream(/*ios_base::openmode which = ios_base::out|ios_base::in - not needed for this test*/);
|
||||
|
||||
basic_string<charT, traits, Allocator> str() const;
|
||||
};
|
||||
|
||||
using stringstream = basic_stringstream<char>;
|
||||
}
|
||||
|
||||
// --- vector ---
|
||||
|
||||
namespace std {
|
||||
template <class T>
|
||||
class vector {
|
||||
private:
|
||||
void *data_;
|
||||
public:
|
||||
vector(int size);
|
||||
|
||||
T& operator[](int idx);
|
||||
const T& operator[](int idx) const;
|
||||
|
||||
typedef std::iterator<random_access_iterator_tag, T> iterator;
|
||||
typedef std::iterator<random_access_iterator_tag, const T> const_iterator;
|
||||
|
||||
iterator begin() noexcept;
|
||||
iterator end() noexcept;
|
||||
|
||||
const_iterator begin() const noexcept;
|
||||
const_iterator end() const noexcept;
|
||||
};
|
||||
}
|
||||
186
cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp
Normal file
186
cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
|
||||
#include "stl.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
char *source();
|
||||
|
||||
namespace ns_char
|
||||
{
|
||||
char source();
|
||||
}
|
||||
|
||||
char *user_input() {
|
||||
return source();
|
||||
}
|
||||
|
||||
void sink(const char *s) {};
|
||||
void sink(const std::string &s) {};
|
||||
void sink(const char *filename, const char *mode);
|
||||
void sink(char) {}
|
||||
|
||||
void test_string()
|
||||
{
|
||||
char *a = source();
|
||||
std::string b("123");
|
||||
std::string c(source());
|
||||
|
||||
sink(a); // tainted
|
||||
sink(b);
|
||||
sink(c); // tainted
|
||||
sink(b.c_str());
|
||||
sink(c.c_str()); // tainted
|
||||
}
|
||||
|
||||
void test_strings2()
|
||||
{
|
||||
string path1 = user_input();
|
||||
sink(path1.c_str(), "r"); // tainted
|
||||
|
||||
string path2;
|
||||
path2 = user_input();
|
||||
sink(path2.c_str(), "r"); // tainted
|
||||
|
||||
string path3(user_input());
|
||||
sink(path3.c_str(), "r"); // tainted
|
||||
}
|
||||
|
||||
void test_string3()
|
||||
{
|
||||
const char *cs = source();
|
||||
|
||||
// convert char * -> std::string
|
||||
std::string ss(cs);
|
||||
|
||||
sink(cs); // tainted
|
||||
sink(ss); // tainted
|
||||
}
|
||||
|
||||
void test_string4()
|
||||
{
|
||||
const char *cs = source();
|
||||
|
||||
// convert char * -> std::string
|
||||
std::string ss(cs);
|
||||
|
||||
// convert back std::string -> char *
|
||||
cs = ss.c_str();
|
||||
|
||||
sink(cs); // tainted
|
||||
sink(ss); // tainted
|
||||
}
|
||||
|
||||
void test_string_constructors_assignments()
|
||||
{
|
||||
{
|
||||
std::string s1("hello");
|
||||
std::string s2 = "hello";
|
||||
std::string s3;
|
||||
s3 = "hello";
|
||||
|
||||
sink(s1);
|
||||
sink(s2);
|
||||
sink(s3);
|
||||
}
|
||||
|
||||
{
|
||||
std::string s1(source());
|
||||
std::string s2 = source();
|
||||
std::string s3;
|
||||
s3 = source();
|
||||
|
||||
sink(s1); // tainted
|
||||
sink(s2); // tainted
|
||||
sink(s3); // tainted
|
||||
}
|
||||
|
||||
{
|
||||
std::string s1;
|
||||
std::string s2 = s1;
|
||||
std::string s3;
|
||||
s3 = s1;
|
||||
|
||||
sink(s1);
|
||||
sink(s2);
|
||||
sink(s3);
|
||||
}
|
||||
|
||||
{
|
||||
std::string s1 = std::string(source());
|
||||
std::string s2;
|
||||
s2 = std::string(source());
|
||||
|
||||
sink(s1); // tainted
|
||||
sink(s2); // tainted
|
||||
}
|
||||
}
|
||||
|
||||
void test_range_based_for_loop_string() {
|
||||
std::string s(source());
|
||||
for(char c : s) {
|
||||
sink(c); // tainted [NOT DETECTED by IR]
|
||||
}
|
||||
|
||||
for(std::string::iterator it = s.begin(); it != s.end(); ++it) {
|
||||
sink(*it); // tainted [NOT DETECTED]
|
||||
}
|
||||
|
||||
for(char& c : s) {
|
||||
sink(c); // tainted [NOT DETECTED by IR]
|
||||
}
|
||||
|
||||
const std::string const_s(source());
|
||||
for(const char& c : const_s) {
|
||||
sink(c); // tainted [NOT DETECTED by IR]
|
||||
}
|
||||
}
|
||||
|
||||
void test_string_append() {
|
||||
{
|
||||
std::string s1("hello");
|
||||
std::string s2(source());
|
||||
|
||||
sink(s1 + s1);
|
||||
sink(s1 + s2); // tainted
|
||||
sink(s2 + s1); // tainted
|
||||
sink(s2 + s2); // tainted
|
||||
|
||||
sink(s1 + " world");
|
||||
sink(s1 + source()); // tainted
|
||||
}
|
||||
|
||||
{
|
||||
std::string s3("abc");
|
||||
std::string s4(source());
|
||||
std::string s5, s6, s7, s8, s9;
|
||||
|
||||
s5 = s3 + s4;
|
||||
sink(s5); // tainted
|
||||
|
||||
s6 = s3;
|
||||
s6 += s4;
|
||||
sink(s6); // tainted
|
||||
|
||||
s7 = s3;
|
||||
s7 += source();
|
||||
s7 += " ";
|
||||
sink(s7); // tainted
|
||||
|
||||
s8 = s3;
|
||||
s8.append(s4);
|
||||
sink(s8); // tainted
|
||||
|
||||
s9 = s3;
|
||||
s9.append(source());
|
||||
s9.append(" ");
|
||||
sink(s9); // tainted
|
||||
}
|
||||
|
||||
{
|
||||
std::string s10("abc");
|
||||
char c = ns_char::source();
|
||||
|
||||
s10.append(1, c);
|
||||
sink(s10); // tainted
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
|
||||
#include "stl.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
char *source();
|
||||
|
||||
void sink(const std::string &s) {};
|
||||
void sink(const std::stringstream &s) {};
|
||||
|
||||
void test_stringstream()
|
||||
{
|
||||
std::stringstream ss1, ss2, ss3, ss4, ss5;
|
||||
std::string t(source());
|
||||
|
||||
ss1 << "1234";
|
||||
ss2 << source();
|
||||
ss3 << "123" << source();
|
||||
ss4 << source() << "456";
|
||||
ss5 << t;
|
||||
|
||||
sink(ss1);
|
||||
sink(ss2); // tainted [NOT DETECTED]
|
||||
sink(ss3); // tainted [NOT DETECTED]
|
||||
sink(ss4); // tainted [NOT DETECTED]
|
||||
sink(ss5); // tainted [NOT DETECTED]
|
||||
sink(ss1.str());
|
||||
sink(ss2.str()); // tainted [NOT DETECTED]
|
||||
sink(ss3.str()); // tainted [NOT DETECTED]
|
||||
sink(ss4.str()); // tainted [NOT DETECTED]
|
||||
sink(ss5.str()); // tainted [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test_stringstream_int(int source)
|
||||
{
|
||||
std::stringstream ss1, ss2;
|
||||
|
||||
ss1 << 1234;
|
||||
ss2 << source;
|
||||
|
||||
sink(ss1);
|
||||
sink(ss2); // tainted [NOT DETECTED]
|
||||
sink(ss1.str());
|
||||
sink(ss2.str()); // tainted [NOT DETECTED]
|
||||
}
|
||||
@@ -32,21 +32,34 @@
|
||||
| movableclass.cpp:55:8:55:9 | s2 | movableclass.cpp:52:23:52:28 | call to source |
|
||||
| movableclass.cpp:64:8:64:9 | s2 | movableclass.cpp:23:55:23:60 | call to source |
|
||||
| movableclass.cpp:65:11:65:11 | call to operator= | movableclass.cpp:65:13:65:18 | call to source |
|
||||
| stl.cpp:71:7:71:7 | a | stl.cpp:67:12:67:17 | call to source |
|
||||
| stl.cpp:73:7:73:7 | c | stl.cpp:69:16:69:21 | call to source |
|
||||
| stl.cpp:75:9:75:13 | call to c_str | stl.cpp:69:16:69:21 | call to source |
|
||||
| stl.cpp:125:13:125:17 | call to c_str | stl.cpp:117:10:117:15 | call to source |
|
||||
| stl.cpp:129:13:129:17 | call to c_str | stl.cpp:117:10:117:15 | call to source |
|
||||
| stl.cpp:132:13:132:17 | call to c_str | stl.cpp:117:10:117:15 | call to source |
|
||||
| stl.cpp:142:7:142:8 | cs | stl.cpp:137:19:137:24 | call to source |
|
||||
| stl.cpp:143:7:143:8 | ss | stl.cpp:137:19:137:24 | call to source |
|
||||
| stl.cpp:156:7:156:8 | cs | stl.cpp:148:19:148:24 | call to source |
|
||||
| stl.cpp:157:7:157:8 | ss | stl.cpp:148:19:148:24 | call to source |
|
||||
| stl.cpp:179:8:179:9 | s1 | stl.cpp:174:18:174:23 | call to source |
|
||||
| stl.cpp:180:8:180:9 | s2 | stl.cpp:175:20:175:25 | call to source |
|
||||
| stl.cpp:181:8:181:9 | s3 | stl.cpp:177:8:177:13 | call to source |
|
||||
| stl.cpp:200:8:200:9 | s1 | stl.cpp:196:32:196:37 | call to source |
|
||||
| stl.cpp:201:8:201:9 | s2 | stl.cpp:198:20:198:25 | call to source |
|
||||
| string.cpp:28:7:28:7 | a | string.cpp:24:12:24:17 | call to source |
|
||||
| string.cpp:30:7:30:7 | c | string.cpp:26:16:26:21 | call to source |
|
||||
| string.cpp:32:9:32:13 | call to c_str | string.cpp:26:16:26:21 | call to source |
|
||||
| string.cpp:38:13:38:17 | call to c_str | string.cpp:14:10:14:15 | call to source |
|
||||
| string.cpp:42:13:42:17 | call to c_str | string.cpp:14:10:14:15 | call to source |
|
||||
| string.cpp:45:13:45:17 | call to c_str | string.cpp:14:10:14:15 | call to source |
|
||||
| string.cpp:55:7:55:8 | cs | string.cpp:50:19:50:24 | call to source |
|
||||
| string.cpp:56:7:56:8 | ss | string.cpp:50:19:50:24 | call to source |
|
||||
| string.cpp:69:7:69:8 | cs | string.cpp:61:19:61:24 | call to source |
|
||||
| string.cpp:70:7:70:8 | ss | string.cpp:61:19:61:24 | call to source |
|
||||
| string.cpp:92:8:92:9 | s1 | string.cpp:87:18:87:23 | call to source |
|
||||
| string.cpp:93:8:93:9 | s2 | string.cpp:88:20:88:25 | call to source |
|
||||
| string.cpp:94:8:94:9 | s3 | string.cpp:90:8:90:13 | call to source |
|
||||
| string.cpp:113:8:113:9 | s1 | string.cpp:109:32:109:37 | call to source |
|
||||
| string.cpp:114:8:114:9 | s2 | string.cpp:111:20:111:25 | call to source |
|
||||
| string.cpp:121:8:121:8 | c | string.cpp:119:16:119:21 | call to source |
|
||||
| string.cpp:129:8:129:8 | c | string.cpp:119:16:119:21 | call to source |
|
||||
| string.cpp:134:8:134:8 | c | string.cpp:132:28:132:33 | call to source |
|
||||
| string.cpp:144:11:144:11 | call to operator+ | string.cpp:141:18:141:23 | call to source |
|
||||
| string.cpp:145:11:145:11 | call to operator+ | string.cpp:141:18:141:23 | call to source |
|
||||
| string.cpp:146:11:146:11 | call to operator+ | string.cpp:141:18:141:23 | call to source |
|
||||
| string.cpp:149:11:149:11 | call to operator+ | string.cpp:149:13:149:18 | call to source |
|
||||
| string.cpp:158:8:158:9 | s5 | string.cpp:154:18:154:23 | call to source |
|
||||
| string.cpp:162:8:162:9 | s6 | string.cpp:154:18:154:23 | call to source |
|
||||
| string.cpp:167:8:167:9 | s7 | string.cpp:165:9:165:14 | call to source |
|
||||
| string.cpp:171:8:171:9 | s8 | string.cpp:154:18:154:23 | call to source |
|
||||
| string.cpp:176:8:176:9 | s9 | string.cpp:174:13:174:18 | call to source |
|
||||
| string.cpp:184:8:184:10 | s10 | string.cpp:181:12:181:26 | call to source |
|
||||
| structlikeclass.cpp:35:8:35:9 | s1 | structlikeclass.cpp:29:22:29:27 | call to source |
|
||||
| structlikeclass.cpp:36:8:36:9 | s2 | structlikeclass.cpp:30:24:30:29 | call to source |
|
||||
| structlikeclass.cpp:37:8:37:9 | s3 | structlikeclass.cpp:29:22:29:27 | call to source |
|
||||
@@ -153,3 +166,6 @@
|
||||
| taint.cpp:470:7:470:7 | x | taint.cpp:462:6:462:11 | call to source |
|
||||
| taint.cpp:471:7:471:7 | y | taint.cpp:462:6:462:11 | call to source |
|
||||
| taint.cpp:485:7:485:10 | line | taint.cpp:480:26:480:32 | source1 |
|
||||
| vector.cpp:15:8:15:8 | x | vector.cpp:8:43:8:49 | source1 |
|
||||
| vector.cpp:23:8:23:8 | x | vector.cpp:8:43:8:49 | source1 |
|
||||
| vector.cpp:28:8:28:8 | x | vector.cpp:8:43:8:49 | source1 |
|
||||
|
||||
@@ -30,20 +30,33 @@
|
||||
| movableclass.cpp:55:8:55:9 | movableclass.cpp:52:23:52:28 | AST only |
|
||||
| movableclass.cpp:64:8:64:9 | movableclass.cpp:23:55:23:60 | AST only |
|
||||
| movableclass.cpp:65:11:65:11 | movableclass.cpp:65:13:65:18 | AST only |
|
||||
| stl.cpp:73:7:73:7 | stl.cpp:69:16:69:21 | AST only |
|
||||
| stl.cpp:75:9:75:13 | stl.cpp:69:16:69:21 | AST only |
|
||||
| stl.cpp:125:13:125:17 | stl.cpp:117:10:117:15 | AST only |
|
||||
| stl.cpp:129:13:129:17 | stl.cpp:117:10:117:15 | AST only |
|
||||
| stl.cpp:132:13:132:17 | stl.cpp:117:10:117:15 | AST only |
|
||||
| stl.cpp:142:7:142:8 | stl.cpp:137:19:137:26 | IR only |
|
||||
| stl.cpp:143:7:143:8 | stl.cpp:137:19:137:24 | AST only |
|
||||
| stl.cpp:156:7:156:8 | stl.cpp:148:19:148:24 | AST only |
|
||||
| stl.cpp:157:7:157:8 | stl.cpp:148:19:148:24 | AST only |
|
||||
| stl.cpp:179:8:179:9 | stl.cpp:174:18:174:23 | AST only |
|
||||
| stl.cpp:180:8:180:9 | stl.cpp:175:20:175:25 | AST only |
|
||||
| stl.cpp:181:8:181:9 | stl.cpp:177:8:177:13 | AST only |
|
||||
| stl.cpp:200:8:200:9 | stl.cpp:196:32:196:37 | AST only |
|
||||
| stl.cpp:201:8:201:9 | stl.cpp:198:20:198:25 | AST only |
|
||||
| string.cpp:30:7:30:7 | string.cpp:26:16:26:21 | AST only |
|
||||
| string.cpp:32:9:32:13 | string.cpp:26:16:26:21 | AST only |
|
||||
| string.cpp:38:13:38:17 | string.cpp:14:10:14:15 | AST only |
|
||||
| string.cpp:42:13:42:17 | string.cpp:14:10:14:15 | AST only |
|
||||
| string.cpp:45:13:45:17 | string.cpp:14:10:14:15 | AST only |
|
||||
| string.cpp:55:7:55:8 | string.cpp:50:19:50:26 | IR only |
|
||||
| string.cpp:56:7:56:8 | string.cpp:50:19:50:24 | AST only |
|
||||
| string.cpp:69:7:69:8 | string.cpp:61:19:61:24 | AST only |
|
||||
| string.cpp:70:7:70:8 | string.cpp:61:19:61:24 | AST only |
|
||||
| string.cpp:92:8:92:9 | string.cpp:87:18:87:23 | AST only |
|
||||
| string.cpp:93:8:93:9 | string.cpp:88:20:88:25 | AST only |
|
||||
| string.cpp:94:8:94:9 | string.cpp:90:8:90:13 | AST only |
|
||||
| string.cpp:113:8:113:9 | string.cpp:109:32:109:37 | AST only |
|
||||
| string.cpp:114:8:114:9 | string.cpp:111:20:111:25 | AST only |
|
||||
| string.cpp:121:8:121:8 | string.cpp:119:16:119:21 | AST only |
|
||||
| string.cpp:129:8:129:8 | string.cpp:119:16:119:21 | AST only |
|
||||
| string.cpp:134:8:134:8 | string.cpp:132:28:132:33 | AST only |
|
||||
| string.cpp:144:11:144:11 | string.cpp:141:18:141:23 | AST only |
|
||||
| string.cpp:145:11:145:11 | string.cpp:141:18:141:23 | AST only |
|
||||
| string.cpp:146:11:146:11 | string.cpp:141:18:141:23 | AST only |
|
||||
| string.cpp:149:11:149:11 | string.cpp:149:13:149:18 | AST only |
|
||||
| string.cpp:158:8:158:9 | string.cpp:154:18:154:23 | AST only |
|
||||
| string.cpp:162:8:162:9 | string.cpp:154:18:154:23 | AST only |
|
||||
| string.cpp:167:8:167:9 | string.cpp:165:9:165:14 | AST only |
|
||||
| string.cpp:171:8:171:9 | string.cpp:154:18:154:23 | AST only |
|
||||
| string.cpp:176:8:176:9 | string.cpp:174:13:174:18 | AST only |
|
||||
| string.cpp:184:8:184:10 | string.cpp:181:12:181:26 | AST only |
|
||||
| structlikeclass.cpp:35:8:35:9 | structlikeclass.cpp:29:22:29:27 | AST only |
|
||||
| structlikeclass.cpp:36:8:36:9 | structlikeclass.cpp:30:24:30:29 | AST only |
|
||||
| structlikeclass.cpp:37:8:37:9 | structlikeclass.cpp:29:22:29:27 | AST only |
|
||||
@@ -88,3 +101,6 @@
|
||||
| taint.cpp:446:7:446:7 | taint.cpp:445:14:445:28 | AST only |
|
||||
| taint.cpp:447:9:447:17 | taint.cpp:445:14:445:28 | AST only |
|
||||
| taint.cpp:471:7:471:7 | taint.cpp:462:6:462:11 | AST only |
|
||||
| vector.cpp:15:8:15:8 | vector.cpp:8:43:8:49 | AST only |
|
||||
| vector.cpp:23:8:23:8 | vector.cpp:8:43:8:49 | AST only |
|
||||
| vector.cpp:28:8:28:8 | vector.cpp:8:43:8:49 | AST only |
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
| format.cpp:157:7:157:22 | (int)... | format.cpp:147:12:147:25 | call to source |
|
||||
| format.cpp:157:7:157:22 | access to array | format.cpp:147:12:147:25 | call to source |
|
||||
| format.cpp:158:7:158:27 | ... + ... | format.cpp:148:16:148:30 | call to source |
|
||||
| stl.cpp:71:7:71:7 | (const char *)... | stl.cpp:67:12:67:17 | call to source |
|
||||
| stl.cpp:71:7:71:7 | a | stl.cpp:67:12:67:17 | call to source |
|
||||
| stl.cpp:142:7:142:8 | cs | stl.cpp:137:19:137:24 | call to source |
|
||||
| stl.cpp:142:7:142:8 | cs | stl.cpp:137:19:137:26 | (const char *)... |
|
||||
| string.cpp:28:7:28:7 | (const char *)... | string.cpp:24:12:24:17 | call to source |
|
||||
| string.cpp:28:7:28:7 | a | string.cpp:24:12:24:17 | call to source |
|
||||
| string.cpp:55:7:55:8 | cs | string.cpp:50:19:50:24 | call to source |
|
||||
| string.cpp:55:7:55:8 | cs | string.cpp:50:19:50:26 | (const char *)... |
|
||||
| structlikeclass.cpp:38:8:38:9 | s4 | structlikeclass.cpp:33:8:33:13 | call to source |
|
||||
| structlikeclass.cpp:61:8:61:9 | s2 | structlikeclass.cpp:58:24:58:29 | call to source |
|
||||
| structlikeclass.cpp:62:8:62:20 | ... = ... | structlikeclass.cpp:62:13:62:18 | call to source |
|
||||
|
||||
30
cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
Normal file
30
cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
#include "stl.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
void sink(int);
|
||||
|
||||
void test_range_based_for_loop_vector(int source1) {
|
||||
// Tainting the vector by allocating a tainted length. This doesn't represent
|
||||
// how a vector would typically get tainted, but it allows this test to avoid
|
||||
// being concerned with std::vector modeling.
|
||||
std::vector<int> v(source1);
|
||||
|
||||
for(int x : v) {
|
||||
sink(x); // tainted [NOT DETECTED by IR]
|
||||
}
|
||||
|
||||
for(std::vector<int>::iterator it = v.begin(); it != v.end(); ++it) {
|
||||
sink(*it); // tainted [NOT DETECTED]
|
||||
}
|
||||
|
||||
for(int& x : v) {
|
||||
sink(x); // tainted [NOT DETECTED by IR]
|
||||
}
|
||||
|
||||
const std::vector<int> const_v(source1);
|
||||
for(const int& x : const_v) {
|
||||
sink(x); // tainted [NOT DETECTED by IR]
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
| functions.cpp:23:7:23:11 | Table | Class | functions.cpp:30:8:30:13 | insert | |
|
||||
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:33:7:33:7 | operator= | |
|
||||
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:36:2:36:8 | MyClass | Constructor, NoArgConstructor, getAConstructor() |
|
||||
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:37:2:37:8 | MyClass | Constructor, ConversionConstructor, getAConstructor() |
|
||||
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:37:2:37:8 | MyClass | Constructor, getAConstructor() |
|
||||
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:38:2:38:8 | MyClass | Constructor, CopyConstructor, getAConstructor() |
|
||||
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:39:2:39:8 | MyClass | Constructor, ConversionConstructor, MoveConstructor, getAConstructor() |
|
||||
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:39:2:39:8 | MyClass | Constructor, MoveConstructor, getAConstructor() |
|
||||
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:40:2:40:13 | operator int | ConversionOperator |
|
||||
|
||||
@@ -19,9 +19,6 @@ string describe(Class c, MemberFunction f) {
|
||||
f instanceof Destructor and
|
||||
result = "Destructor"
|
||||
or
|
||||
f instanceof ConversionConstructor and
|
||||
result = "ConversionConstructor"
|
||||
or
|
||||
f instanceof CopyConstructor and
|
||||
result = "CopyConstructor"
|
||||
or
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
|
||||
from VariableAccess expr
|
||||
select expr, lowerBound(expr)
|
||||
select expr, lowerBound(expr).toString()
|
||||
|
||||
@@ -463,3 +463,30 @@ int test_unsigned_mult02(unsigned b) {
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
unsigned long mult_rounding() {
|
||||
unsigned long x, y, xy;
|
||||
x = y = 1000000003UL; // 1e9 + 3
|
||||
xy = x * y;
|
||||
return xy; // BUG: upper bound should be >= 1000000006000000009UL
|
||||
}
|
||||
|
||||
unsigned long mult_overflow() {
|
||||
unsigned long x, y, xy;
|
||||
x = 274177UL;
|
||||
y = 67280421310721UL;
|
||||
xy = x * y;
|
||||
return xy; // BUG: lower bound should be <= 18446744073709551617UL
|
||||
}
|
||||
|
||||
unsigned long mult_lower_bound(unsigned int ui, unsigned long ul) {
|
||||
if (ui >= 10) {
|
||||
unsigned long result = (unsigned long)ui * ui;
|
||||
return result; // BUG: upper bound should be >= 18446744065119617025 (possibly a pretty-printing bug)
|
||||
}
|
||||
if (ul >= 10) {
|
||||
unsigned long result = ul * ul;
|
||||
return result; // lower bound is correctly 0 (overflow is possible)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
|
||||
from VariableAccess expr
|
||||
select expr, upperBound(expr)
|
||||
select expr, upperBound(expr).toString()
|
||||
|
||||
@@ -385,10 +385,39 @@ void bitwise_ands()
|
||||
|
||||
void unsigned_mult(unsigned int x, unsigned int y) {
|
||||
if(x < 13 && y < 35) {
|
||||
if(x * y > 1024) {} // always false [NOT DETECTED]
|
||||
if(x * y > 1024) {} // always false
|
||||
if(x * y < 204) {}
|
||||
if(x >= 3 && y >= 2) {
|
||||
if(x * y < 5) {} // always false [NOT DETECTED]
|
||||
if(x * y < 5) {} // always false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mult_rounding() {
|
||||
unsigned long x, y, xy;
|
||||
x = y = 1000000003UL; // 1e9 + 3
|
||||
xy = 1000000006000000009UL; // x * y, precisely
|
||||
// Even though the range analysis wrongly considers x*y to be xy - 9, there
|
||||
// are no PointlessComparison false positives in these tests because alerts
|
||||
// are suppressed when ulp() < 1, which roughly means that the number is
|
||||
// larger than 2^53.
|
||||
if (x * y < xy) {} // always false [NOT DETECTED]
|
||||
if (x * y > xy) {} // always false [NOT DETECTED]
|
||||
}
|
||||
|
||||
void mult_overflow() {
|
||||
unsigned long x, y;
|
||||
// The following two numbers multiply to 2^64 + 1, which is 1 when truncated
|
||||
// to 64-bit unsigned.
|
||||
x = 274177UL;
|
||||
y = 67280421310721UL;
|
||||
if (x * y == 1) {} // always true [BUG: reported as always false]
|
||||
|
||||
// This bug appears to be caused by
|
||||
// `RangeAnalysisUtils::typeUpperBound(unsigned long)` having a result of
|
||||
// 2**64 + 384, making the range analysis think that the multiplication can't
|
||||
// overflow. The correct `typeUpperBound` would be 2**64 - 1, but we can't
|
||||
// represent that with a QL float or int. We could make `typeUpperBound`
|
||||
// exclusive instead of inclusive, but there is no exclusive upper bound for
|
||||
// floats.
|
||||
}
|
||||
|
||||
@@ -41,6 +41,9 @@
|
||||
| PointlessComparison.c:372:6:372:16 | ... >= ... | Comparison is always true because ... >> ... >= 1. |
|
||||
| PointlessComparison.c:373:6:373:16 | ... >= ... | Comparison is always false because ... >> ... <= 1. |
|
||||
| PointlessComparison.c:383:6:383:17 | ... >= ... | Comparison is always false because ... & ... <= 2. |
|
||||
| PointlessComparison.c:388:10:388:21 | ... > ... | Comparison is always false because ... * ... <= 408. |
|
||||
| PointlessComparison.c:391:12:391:20 | ... < ... | Comparison is always false because ... * ... >= 6. |
|
||||
| PointlessComparison.c:414:7:414:16 | ... == ... | Comparison is always false because ... * ... >= 18446744073709552000. |
|
||||
| PointlessComparison.cpp:36:6:36:33 | ... >= ... | Comparison is always false because ... >> ... <= 9223372036854776000. |
|
||||
| PointlessComparison.cpp:41:6:41:29 | ... >= ... | Comparison is always false because ... >> ... <= 140737488355327.5. |
|
||||
| PointlessComparison.cpp:42:6:42:29 | ... >= ... | Comparison is always false because ... >> ... <= 140737488355327.5. |
|
||||
|
||||
@@ -177,4 +177,43 @@ void FalseNegativeTestCases()
|
||||
for (int i = 100; i > 0; i += 2) {}
|
||||
// For comparison
|
||||
for (int i = 100; i > 0; i ++ ) {} // BUG
|
||||
}
|
||||
}
|
||||
|
||||
void IntendedOverflow(unsigned char p)
|
||||
{
|
||||
const unsigned char m = 10;
|
||||
unsigned char i;
|
||||
signed char s;
|
||||
|
||||
for (i = 63; i < 64; i--) {} // GOOD (legitimate way to count down with an unsigned)
|
||||
for (i = 63; i < 128; i--) {} // DUBIOUS (could still be a typo?)
|
||||
for (i = 63; i < 255; i--) {} // GOOD
|
||||
|
||||
for (i = m - 1; i < m; i--) {} // GOOD
|
||||
for (i = m - 2; i < m; i--) {} // DUBIOUS
|
||||
for (i = m; i < m + 1; i--) {} // GOOD
|
||||
|
||||
for (s = 63; s < 64; s--) {} // BAD (signed numbers don't wrap at 0 / at all)
|
||||
for (s = m + 1; s < m; s--) {} // BAD (never runs)
|
||||
|
||||
for (i = p - 1; i < p; i--) {} // GOOD
|
||||
for (s = p - 1; s < p; s--) {} // BAD [NOT DETECTED]
|
||||
|
||||
{
|
||||
int n;
|
||||
|
||||
n = 64;
|
||||
for (i = n - 1; i < n; i--) {} // GOOD
|
||||
n = 64;
|
||||
for (i = n - 1; i < 64; i--) {} // GOOD
|
||||
n = 64;
|
||||
for (i = 63; i < n; i--) {} // GOOD
|
||||
|
||||
n = 64;
|
||||
for (s = n - 1; s < n; s--) {} // BAD [NOT DETECTED]
|
||||
n = 64;
|
||||
for (s = n - 1; s < 64; s--) {} // BAD
|
||||
n = 64;
|
||||
for (s = 63; s < n; s--) {} // BAD [NOT DETECTED]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,3 +20,6 @@
|
||||
| inconsistentLoopDirection.cpp:140:5:142:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts upward from a value (200), but the terminal condition is lower (0). |
|
||||
| inconsistentLoopDirection.cpp:175:5:175:36 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts downward from a value (0), but the terminal condition is higher (10). |
|
||||
| inconsistentLoopDirection.cpp:179:5:179:38 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts upward from a value (100), but the terminal condition is lower (0). |
|
||||
| inconsistentLoopDirection.cpp:196:5:196:32 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "s" counts downward from a value (63), but the terminal condition is higher (64). |
|
||||
| inconsistentLoopDirection.cpp:197:5:197:34 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "s" counts downward from a value (... + ...), but the terminal condition is always false. |
|
||||
| inconsistentLoopDirection.cpp:215:3:215:33 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "s" counts downward from a value (... - ...), but the terminal condition is higher (64). |
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>The <code>DataSet</code> and <code>DataTable</code> types are legacy .NET components that you can use to represent data sets as managed objects.</p>
|
||||
|
||||
<p>While <code>DataSet</code> and <code>DataTable</code> do impose default limitations on the types that are allowed to be present while deserializing XML payloads, <code>DataSet</code> and <code>DataTable</code> are in general not safe when populated with untrusted input.</p>
|
||||
|
||||
<p>Please visit <a href="https://go.microsoft.com/fwlink/?linkid=2132227">DataSet and DataTable security guidance</a> for more details.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>Please review the <a href="https://go.microsoft.com/fwlink/?linkid=2132227">DataSet and DataTable security guidance</a> before making use of these types for serialization.</p>
|
||||
|
||||
</recommendation>
|
||||
<references>
|
||||
|
||||
<li>Microsoft Docs<a href="https://go.microsoft.com/fwlink/?linkid=2132227">DataSet and DataTable security guidance</a>.</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Provides classes for `DataSet` or `DataTable` deserialization queries.
|
||||
*
|
||||
* Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
|
||||
/**
|
||||
* Abstract class that depends or inherits from `DataSet` or `DataTable` types.
|
||||
*/
|
||||
abstract class DataSetOrTableRelatedClass extends Class { }
|
||||
|
||||
/**
|
||||
* `DataSet`, `DataTable` types, or any types derived from them.
|
||||
*/
|
||||
class DataSetOrTable extends DataSetOrTableRelatedClass {
|
||||
DataSetOrTable() {
|
||||
this.getABaseType*().getQualifiedName() = "System.Data.DataTable" or
|
||||
this.getABaseType*().getQualifiedName() = "System.Data.DataSet"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Class that include a property or generic collection of type `DataSet` and `DataTable`
|
||||
*/
|
||||
class ClassWithDataSetOrTableMember extends DataSetOrTableRelatedClass {
|
||||
ClassWithDataSetOrTableMember() {
|
||||
this.getAMember().(AssignableMember).getType() instanceof DataSetOrTable
|
||||
or
|
||||
exists(Property p | p = this.getAProperty() |
|
||||
p.getType() instanceof DataSetOrTable or
|
||||
p.getType().(ConstructedGeneric).getATypeArgument() instanceof DataSetOrTable
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializable types
|
||||
*/
|
||||
class SerializableClass extends Class {
|
||||
SerializableClass() {
|
||||
(
|
||||
this.getABaseType*().getQualifiedName() = "System.Xml.Serialization.XmlSerializer" or
|
||||
this.getABaseType*().getQualifiedName() = "System.Runtime.Serialization.ISerializable" or
|
||||
this.getABaseType*().getQualifiedName() = "System.Runtime.Serialization.XmlObjectSerializer" or
|
||||
this.getABaseType*().getQualifiedName() =
|
||||
"System.Runtime.Serialization.ISerializationSurrogateProvider" or
|
||||
this.getABaseType*().getQualifiedName() =
|
||||
"System.Runtime.Serialization.XmlSerializableServices" or
|
||||
this.getABaseType*().getQualifiedName() = "System.Xml.Serialization.IXmlSerializable"
|
||||
)
|
||||
or
|
||||
exists(Attribute a | a = this.getAnAttribute() |
|
||||
a.getType().getQualifiedName() = "System.SerializableAttribute"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the serializable class `c` has a property or field `m` that is of `DataSet` or `DataTable` related type
|
||||
*/
|
||||
predicate isClassUnsafeXmlSerializerImplementation(SerializableClass c, AssignableMember am) {
|
||||
am = c.getAMember() and
|
||||
am.getType() instanceof DataSetOrTableRelatedClass
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializable class that has a property or field that is of `DataSet` or `DataTable` related type
|
||||
*/
|
||||
class UnsafeXmlSerializerImplementation extends SerializableClass {
|
||||
UnsafeXmlSerializerImplementation() { isClassUnsafeXmlSerializerImplementation(this, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that may be unsafe when used to deserialize DataSet and DataTable related types
|
||||
*/
|
||||
class UnsafeXmlReadMethod extends Method {
|
||||
UnsafeXmlReadMethod() {
|
||||
this.getQualifiedName() = "System.Data.DataTable.ReadXml"
|
||||
or
|
||||
this.getQualifiedName() = "System.Data.DataTable.ReadXmlSchema"
|
||||
or
|
||||
this.getQualifiedName() = "System.Data.DataSet.ReadXml"
|
||||
or
|
||||
this.getQualifiedName() = "System.Data.DataSet.ReadXmlSchema"
|
||||
or
|
||||
this.getName().matches("ReadXml%") and
|
||||
exists(Class c | c.getAMethod() = this |
|
||||
c.getABaseType*() instanceof DataSetOrTableRelatedClass
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MethodCall that may be unsafe when used to deserialize DataSet and DataTable related types
|
||||
*/
|
||||
class UnsafeXmlReadMethodCall extends MethodCall {
|
||||
UnsafeXmlReadMethodCall() { exists(UnsafeXmlReadMethod uxrm | uxrm.getACall() = this) }
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<include src="DataSetSerialization.qhelp" /></qhelp>
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @name Defining a class that inherits or has a property derived from the obsolete DataSet or DataTable types
|
||||
* @description Defining a class that inherits or has a property derived from the obsolete DataSet or DataTable types may lead to the usage of dangerous functionality. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id cs/dataset-serialization/defining-dataset-related-type
|
||||
* @tags security
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import DataSetSerialization
|
||||
|
||||
from DataSetOrTableRelatedClass dstc
|
||||
where dstc.fromSource()
|
||||
select dstc,
|
||||
"Defining a class that inherits or has a property derived from the obsolete DataSet or DataTable types. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details."
|
||||
@@ -0,0 +1,5 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<include src="DataSetSerialization.qhelp" /></qhelp>
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @name Defining a potentially unsafe XML serializer
|
||||
* @description Defining an XML serializable class that includes members that derive from DataSet or DataTable type may lead to a security problem. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @precision medium
|
||||
* @id cs/dataset-serialization/defining-potentially-unsafe-xml-serializer
|
||||
* @tags security
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import DataSetSerialization
|
||||
|
||||
from UnsafeXmlSerializerImplementation c, Member m
|
||||
where
|
||||
c.fromSource() and
|
||||
isClassUnsafeXmlSerializerImplementation(c, m)
|
||||
select m,
|
||||
"Defining an serializable class $@ that has member $@ of a type that is derived from DataSet or DataTable types and may lead to a security problem. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details.",
|
||||
c, c.toString(), m, m.toString()
|
||||
@@ -0,0 +1,5 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<include src="DataSetSerialization.qhelp" /></qhelp>
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* @name Unsafe type is used in data contract serializer
|
||||
* @description Unsafe type is used in data contract serializer. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details."
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @precision medium
|
||||
* @id cs/dataset-serialization/unsafe-type-used-data-contract-serializer
|
||||
* @tags security
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import DataSetSerialization
|
||||
|
||||
predicate xmlSerializerConstructorArgument(Expr e) {
|
||||
exists(ObjectCreation oc, Constructor c | e = oc.getArgument(0) |
|
||||
c = oc.getTarget() and
|
||||
c.getDeclaringType().getABaseType*().hasQualifiedName("System.Xml.Serialization.XmlSerializer")
|
||||
)
|
||||
}
|
||||
|
||||
predicate unsafeDataContractTypeCreation(Expr e) {
|
||||
exists(MethodCall gt |
|
||||
gt.getTarget().getName() = "GetType" and
|
||||
e = gt and
|
||||
gt.getQualifier().getType() instanceof DataSetOrTableRelatedClass
|
||||
)
|
||||
or
|
||||
e.(TypeofExpr).getTypeAccess().getTarget() instanceof DataSetOrTableRelatedClass
|
||||
}
|
||||
|
||||
class Conf extends DataFlow::Configuration {
|
||||
Conf() { this = "FlowToDataSerializerConstructor" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) { unsafeDataContractTypeCreation(node.asExpr()) }
|
||||
|
||||
override predicate isSink(DataFlow::Node node) { xmlSerializerConstructorArgument(node.asExpr()) }
|
||||
}
|
||||
|
||||
from Conf conf, DataFlow::Node source, DataFlow::Node sink
|
||||
where conf.hasFlow(source, sink)
|
||||
select sink,
|
||||
"Unsafe type is used in data contract serializer. Make sure $@ comes from the trusted source.",
|
||||
source, source.toString()
|
||||
@@ -0,0 +1,5 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<include src="DataSetSerialization.qhelp" /></qhelp>
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @name XML deserialization with a type type derived from DataSet or DataTable
|
||||
* @description Making an XML deserialization call with a type derived from DataSet or DataTable types and may lead to a security problem. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details."
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @precision medium
|
||||
* @id cs/dataset-serialization/xml-deserialization-with-dataset
|
||||
* @tags security
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import DataSetSerialization
|
||||
|
||||
from UnsafeXmlReadMethodCall mc
|
||||
select mc,
|
||||
"Making an XML deserialization call with a type derived from DataSet or DataTable types and may lead to a security problem. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details."
|
||||
@@ -0,0 +1,2 @@
|
||||
| test0.cs:13:18:13:43 | DerivesFromDeprecatedType1 | Defining a class that inherits or has a property derived from the obsolete DataSet or DataTable types. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details. |
|
||||
| test0.cs:59:18:59:38 | AttributeSerializer01 | Defining a class that inherits or has a property derived from the obsolete DataSet or DataTable types. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details. |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security Features/Serialization/DefiningDatasetRelatedType.ql
|
||||
@@ -0,0 +1,2 @@
|
||||
| test0.cs:15:24:15:32 | MyDataSet | Defining an serializable class $@ that has member $@ of a type that is derived from DataSet or DataTable types and may lead to a security problem. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details. | test0.cs:13:18:13:43 | DerivesFromDeprecatedType1 | DerivesFromDeprecatedType1 | test0.cs:15:24:15:32 | MyDataSet | MyDataSet |
|
||||
| test0.cs:61:25:61:33 | MyDataSet | Defining an serializable class $@ that has member $@ of a type that is derived from DataSet or DataTable types and may lead to a security problem. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details. | test0.cs:59:18:59:38 | AttributeSerializer01 | AttributeSerializer01 | test0.cs:61:25:61:33 | MyDataSet | MyDataSet |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security Features/Serialization/DefiningPotentiallyUnsafeXmlSerializer.ql
|
||||
@@ -0,0 +1,2 @@
|
||||
| test0.cs:95:49:95:63 | typeof(...) | Unsafe type is used in data contract serializer. Make sure $@ comes from the trusted source. | test0.cs:95:49:95:63 | typeof(...) | typeof(...) |
|
||||
| test0.cs:96:49:96:77 | typeof(...) | Unsafe type is used in data contract serializer. Make sure $@ comes from the trusted source. | test0.cs:96:49:96:77 | typeof(...) | typeof(...) |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security Features/Serialization/UnsafeTypeUsedDataContractSerializer.ql
|
||||
@@ -0,0 +1 @@
|
||||
| test0.cs:88:17:88:46 | call to method ReadXmlSchema | Making an XML deserialization call with a type derived from DataSet or DataTable types and may lead to a security problem. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details. |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security Features/Serialization/XmlDeserializationWithDataSet.ql
|
||||
@@ -0,0 +1,101 @@
|
||||
// semmle-extractor-options: /r:System.Data.Common.dll /r:System.Runtime.WindowsRuntime.dll /r:System.Xml.XmlSerializer.dll /r:System.Runtime.Serialization.Xml.dll /r:System.Runtime.Serialization.Xml.dll /r:System.Collections.dll /r:System.Private.Xml.dll /r:System.Private.DataContractSerialization.dll /r:System.Runtime.Extensions.dll /r:System.ComponentModel.TypeConverter.dll /r:System.Xml.ReaderWriter.dll /r:System.IO.FileSystem.dll
|
||||
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Xml.Serialization;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Xml;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DataSetSerializationTest
|
||||
{
|
||||
public class DerivesFromDeprecatedType1 : XmlSerializer // warning:DefiningDatasetRelatedType.ql
|
||||
{
|
||||
public DataSet MyDataSet { get; set; } // bug:DefiningPotentiallyUnsafeXmlSerializer.ql
|
||||
|
||||
public DerivesFromDeprecatedType1()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: I cannot use DataContract on a QL unit test
|
||||
*
|
||||
[DataContract(Name = "Customer", Namespace = "http://www.contoso.com")]
|
||||
public class PatternDataContractSerializer : XmlObjectSerializer
|
||||
{
|
||||
[DataMember()]
|
||||
public DataSet MyDataSet { get; set; }
|
||||
[DataMember()]
|
||||
public DataTable MyDataTable { get; set; }
|
||||
|
||||
PatternDataContractSerializer() { }
|
||||
private ExtensionDataObject extensionData_Value;
|
||||
public ExtensionDataObject ExtensionData
|
||||
{
|
||||
get
|
||||
{
|
||||
return extensionData_Value;
|
||||
}
|
||||
set
|
||||
{
|
||||
extensionData_Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteObject(System.IO.Stream stream, object graph) { }
|
||||
public override void WriteObjectContent(System.Xml.XmlDictionaryWriter writer, object graph) { }
|
||||
public override bool IsStartObject(System.Xml.XmlDictionaryReader reader) { return false; }
|
||||
public override void WriteStartObject(System.Xml.XmlDictionaryWriter writer, object graph) { }
|
||||
public override void WriteEndObject(System.Xml.XmlWriter writer) { }
|
||||
public override void WriteEndObject(XmlDictionaryWriter writer) { }
|
||||
public override object ReadObject(System.IO.Stream stream) { return null; }
|
||||
public override object ReadObject(XmlDictionaryReader reader, bool b) { return null; }
|
||||
}
|
||||
*/
|
||||
|
||||
[Serializable()]
|
||||
public class AttributeSerializer01 // warning:DefiningDatasetRelatedType.ql
|
||||
{
|
||||
private DataSet MyDataSet; // bug:DefiningPotentiallyUnsafeXmlSerializer.ql
|
||||
|
||||
AttributeSerializer01()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class Program
|
||||
{
|
||||
static string GetSerializedDataSet(DataSet dataSet)
|
||||
{
|
||||
DataTable dataTable = new DataTable("MyTable");
|
||||
dataTable.Columns.Add("FirstName", typeof(string));
|
||||
dataTable.Columns.Add("LastName", typeof(string));
|
||||
dataTable.Columns.Add("Age", typeof(int));
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
dataSet.WriteXml(writer, XmlWriteMode.DiffGram);
|
||||
return writer.ToString();
|
||||
}
|
||||
|
||||
static void datatable_readxmlschema_01(string fileName)
|
||||
{
|
||||
using (FileStream fs = File.OpenRead(fileName))
|
||||
{
|
||||
DataTable newTable = new DataTable();
|
||||
System.Xml.XmlTextReader reader = new System.Xml.XmlTextReader(fs);
|
||||
newTable.ReadXmlSchema(reader); //bug:XmlDeserializationWithDataSet.ql
|
||||
}
|
||||
}
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
|
||||
XmlSerializer x = new XmlSerializer(typeof(DataSet)); // bug:UnsafeTypeUsedDataContractSerializer.ql
|
||||
XmlSerializer y = new XmlSerializer(typeof(AttributeSerializer01)); //bug:UnsafeTypeUsedDataContractSerializer.ql
|
||||
|
||||
Console.WriteLine("Hello World!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -157,6 +157,8 @@ If your query is a security query, use one or more `@tags` to associate it with
|
||||
|
||||
When you tag a query like this, the associated CWE pages from [MITRE.org](http://cwe.mitre.org/index.html) will automatically appear in the reference section of its associated qhelp file.
|
||||
|
||||
Security relevant queries that will be run in Code Scanning should also have a `@security-severity` tag. See https://github.com/github/cwe-scores for more information about this tag.
|
||||
|
||||
## QL area
|
||||
|
||||
### Alert messages
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
* @precision very-high
|
||||
* @id java/integer-multiplication-cast-to-long
|
||||
* @tags reliability
|
||||
* security
|
||||
* correctness
|
||||
* types
|
||||
* external/cwe/cwe-190
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
public class XSS extends HttpServlet {
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
// BAD: a request parameter is written directly to an error response page
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND,
|
||||
"The page \"" + request.getParameter("page") + "\" was not found.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
public class SQLInjection extends HttpServlet {
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
StringBuilder sqlQueryBuilder = new StringBuilder();
|
||||
sqlQueryBuilder.append("SELECT * FROM user WHERE user_id='");
|
||||
sqlQueryBuilder.append(request.getParameter("user_id"));
|
||||
sqlQueryBuilder.append("'");
|
||||
|
||||
// ...
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using unsanitized untrusted data in an external API can cause a variety of security issues. This query reports
|
||||
all external APIs that are used with untrusted data, along with how frequently the API is used, and how many
|
||||
unique sources of untrusted data flow to this API. This query is designed primarily to help identify which APIs
|
||||
may be relevant for security analysis of this application.</p>
|
||||
|
||||
<p>An external API is defined as a method call to a method that is not defined in the source code, not overridden
|
||||
in the source code, and is not modeled as a taint step in the default taint library. External APIs may be from the
|
||||
Java standard library, third party dependencies or from internal dependencies. The query will report the method
|
||||
signature with a fully qualified name, along with either <code>[param x]</code>, where <code>x</code> indicates the
|
||||
position of the parameter receiving the untrusted data or <code>[qualifier]</code> indicating the untrusted data is
|
||||
used as the qualifier to the method call.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>For each result:</p>
|
||||
|
||||
<ul>
|
||||
<li>If the result highlights a known sink, no action is required.</li>
|
||||
<li>If the result highlights an unknown sink for a problem, then add modeling for the sink to the relevant query.</li>
|
||||
<li>If the result represents a call to an external API which transfers taint, add the appropriate modeling, and
|
||||
re-run the query to determine what new results have appeared due to this additional modeling.</li>
|
||||
</ul>
|
||||
|
||||
<p>Otherwise, the result is likely uninteresting. Custom versions of this query can extend the <code>SafeExternalAPIMethod</code>
|
||||
class to exclude known safe external APIs from future analysis.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>If the query were to return the API <code>javax.servlet.http.HttpServletResponse.sendError(int, java.lang.String) [param 1]</code>
|
||||
then we should first consider whether this a security relevant sink. In this case, this is writing to a HTTP response, so we should
|
||||
consider whether this is an XSS sink. If it is, we should confirm that it is handled by the XSS query.</p>
|
||||
|
||||
<p>If the query were to return the API <code>java.lang.StringBuilder.append(java.lang.String) [param 0]</code>, then this should be
|
||||
reviewed as a possible taint step, because tainted data would flow from the 0th argument to the qualifier of the call.</p>
|
||||
|
||||
<p>Note that both examples are correctly handled by the standard taint tracking library and XSS query.</p>
|
||||
</example>
|
||||
<references>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @name Frequency counts for external APIs that are used with untrusted data
|
||||
* @description This reports the external APIs that are used with untrusted data, along with how
|
||||
* frequently the API is called, and how many unique sources of untrusted data flow
|
||||
* to it.
|
||||
* @id java/count-untrusted-data-external-api
|
||||
* @kind table
|
||||
* @tags security external/cwe/cwe-20
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.security.ExternalAPIs
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
|
||||
from ExternalAPIUsedWithUntrustedData externalAPI
|
||||
select externalAPI, count(externalAPI.getUntrustedDataNode()) as numberOfUses,
|
||||
externalAPI.getNumberOfUntrustedSources() as numberOfUntrustedSources order by
|
||||
numberOfUntrustedSources desc
|
||||
@@ -0,0 +1,57 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using unsanitized untrusted data in an external API can cause a variety of security issues. This query reports
|
||||
external APIs that use untrusted data. The results are not filtered so that you can audit all examples. The query provides data for security reviews of the application and you can also use it to identify external APIs that should be modeled as either taint steps, or sinks for specific problems.</p>
|
||||
|
||||
<p>An external API is defined as a method call to a method that is not defined in the source code, not overridden
|
||||
in the source code, and is not modeled as a taint step in the default taint library. External APIs may be from the
|
||||
Java standard library, third-party dependencies or from internal dependencies. The query reports uses of
|
||||
untrusted data in either the qualifier or as one of the arguments of external APIs.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>For each result:</p>
|
||||
|
||||
<ul>
|
||||
<li>If the result highlights a known sink, confirm that the result is reported by the relevant query, or
|
||||
that the result is a false positive because this data is sanitized.</li>
|
||||
<li>If the result highlights an unknown sink for a problem, then add modeling for the sink to the relevant query,
|
||||
and confirm that the result is either found, or is safe due to appropriate sanitization.</li>
|
||||
<li>If the result represents a call to an external API that transfers taint, add the appropriate modeling, and
|
||||
re-run the query to determine what new results have appeared due to this additional modeling.</li>
|
||||
</ul>
|
||||
|
||||
<p>Otherwise, the result is likely uninteresting. Custom versions of this query can extend the <code>SafeExternalAPIMethod</code>
|
||||
class to exclude known safe external APIs from future analysis.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>In this first example, a request parameter is read from <code>HttpServletRequest</code> and then ultimately used in a call to the
|
||||
<code>HttpServletResponse.sendError</code> external API:</p>
|
||||
|
||||
<sample src="ExternalAPISinkExample.java" />
|
||||
|
||||
<p>This is an XSS sink. The XSS query should therefore be reviewed to confirm that this sink is appropriately modeled,
|
||||
and if it is, to confirm that the query reports this particular result, or that the result is a false positive due to
|
||||
some existing sanitization.</p>
|
||||
|
||||
<p>In this second example, again a request parameter is read from <code>HttpServletRequest</code>.</p>
|
||||
|
||||
<sample src="ExternalAPITaintStepExample.java" />
|
||||
|
||||
<p>If the query reported the call to <code>StringBuilder.append</code> on line 7, this would suggest that this external API is
|
||||
not currently modeled as a taint step in the taint tracking library. The next step would be to model this as a taint step, then
|
||||
re-run the query to determine what additional results might be found. In this example, it seems likely that the result of the
|
||||
<code>StringBuilder</code> will be executed as an SQL query, potentially leading to an SQL injection vulnerability.</p>
|
||||
|
||||
<p>Note that both examples are correctly handled by the standard taint tracking library and XSS query.</p>
|
||||
</example>
|
||||
<references>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @name Untrusted data passed to external API
|
||||
* @description Data provided remotely is used in this external API without sanitization, which could be a security risk.
|
||||
* @id java/untrusted-data-to-external-api
|
||||
* @kind path-problem
|
||||
* @precision low
|
||||
* @problem.severity error
|
||||
* @tags security external/cwe/cwe-20
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.security.ExternalAPIs
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from UntrustedDataToExternalAPIConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where config.hasFlowPath(source, sink)
|
||||
select sink, source, sink,
|
||||
"Call to " + sink.getNode().(ExternalAPIDataNode).getMethodDescription() +
|
||||
" with untrusted data from $@.", source, source.toString()
|
||||
@@ -1,74 +0,0 @@
|
||||
import java
|
||||
import semmle.code.java.controlflow.Guards
|
||||
|
||||
abstract class PathCreation extends Expr {
|
||||
abstract Expr getInput();
|
||||
}
|
||||
|
||||
class PathsGet extends PathCreation, MethodAccess {
|
||||
PathsGet() {
|
||||
exists(Method m | m = this.getMethod() |
|
||||
m.getDeclaringType() instanceof TypePaths and
|
||||
m.getName() = "get"
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getInput() { result = this.getAnArgument() }
|
||||
}
|
||||
|
||||
class FileSystemGetPath extends PathCreation, MethodAccess {
|
||||
FileSystemGetPath() {
|
||||
exists(Method m | m = this.getMethod() |
|
||||
m.getDeclaringType() instanceof TypeFileSystem and
|
||||
m.getName() = "getPath"
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getInput() { result = this.getAnArgument() }
|
||||
}
|
||||
|
||||
class FileCreation extends PathCreation, ClassInstanceExpr {
|
||||
FileCreation() { this.getConstructedType() instanceof TypeFile }
|
||||
|
||||
override Expr getInput() {
|
||||
result = this.getAnArgument() and
|
||||
// Relevant arguments include those that are not a `File`.
|
||||
not result.getType() instanceof TypeFile
|
||||
}
|
||||
}
|
||||
|
||||
class FileWriterCreation extends PathCreation, ClassInstanceExpr {
|
||||
FileWriterCreation() { this.getConstructedType().getQualifiedName() = "java.io.FileWriter" }
|
||||
|
||||
override Expr getInput() {
|
||||
result = this.getAnArgument() and
|
||||
// Relevant arguments are those of type `String`.
|
||||
result.getType() instanceof TypeString
|
||||
}
|
||||
}
|
||||
|
||||
predicate inWeakCheck(Expr e) {
|
||||
// None of these are sufficient to guarantee that a string is safe.
|
||||
exists(MethodAccess m, Method def | m.getQualifier() = e and m.getMethod() = def |
|
||||
def.getName() = "startsWith" or
|
||||
def.getName() = "endsWith" or
|
||||
def.getName() = "isEmpty" or
|
||||
def.getName() = "equals"
|
||||
)
|
||||
or
|
||||
// Checking against `null` has no bearing on path traversal.
|
||||
exists(EqualityTest b | b.getAnOperand() = e | b.getAnOperand() instanceof NullLiteral)
|
||||
}
|
||||
|
||||
// Ignore cases where the variable has been checked somehow,
|
||||
// but allow some particularly obviously bad cases.
|
||||
predicate guarded(VarAccess e) {
|
||||
exists(PathCreation p | e = p.getInput()) and
|
||||
exists(ConditionBlock cb, Expr c |
|
||||
cb.getCondition().getAChildExpr*() = c and
|
||||
c = e.getVariable().getAnAccess() and
|
||||
cb.controls(e.getBasicBlock(), true) and
|
||||
// Disallow a few obviously bad checks.
|
||||
not inWeakCheck(c)
|
||||
)
|
||||
}
|
||||
@@ -14,8 +14,9 @@
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import PathsCommon
|
||||
import semmle.code.java.security.PathCreation
|
||||
import DataFlow::PathGraph
|
||||
import TaintedPathCommon
|
||||
|
||||
class ContainsDotDotSanitizer extends DataFlow::BarrierGuard {
|
||||
ContainsDotDotSanitizer() {
|
||||
@@ -34,7 +35,7 @@ class TaintedPathConfig extends TaintTracking::Configuration {
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(Expr e | e = sink.asExpr() | e = any(PathCreation p).getInput() and not guarded(e))
|
||||
exists(Expr e | e = sink.asExpr() | e = any(PathCreation p).getAnInput() and not guarded(e))
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
@@ -48,7 +49,7 @@ class TaintedPathConfig extends TaintTracking::Configuration {
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, PathCreation p, TaintedPathConfig conf
|
||||
where
|
||||
sink.getNode().asExpr() = p.getInput() and
|
||||
sink.getNode().asExpr() = p.getAnInput() and
|
||||
conf.hasFlowPath(source, sink)
|
||||
select p, source, sink, "$@ flows to here and is used in a path.", source.getNode(),
|
||||
"User-provided value"
|
||||
|
||||
33
java/ql/src/Security/CWE/CWE-022/TaintedPathCommon.qll
Normal file
33
java/ql/src/Security/CWE/CWE-022/TaintedPathCommon.qll
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Models a very basic guard for the tainted path queries.
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.controlflow.Guards
|
||||
import semmle.code.java.security.PathCreation
|
||||
|
||||
private predicate inWeakCheck(Expr e) {
|
||||
// None of these are sufficient to guarantee that a string is safe.
|
||||
exists(MethodAccess m, Method def | m.getQualifier() = e and m.getMethod() = def |
|
||||
def.getName() = "startsWith" or
|
||||
def.getName() = "endsWith" or
|
||||
def.getName() = "isEmpty" or
|
||||
def.getName() = "equals"
|
||||
)
|
||||
or
|
||||
// Checking against `null` has no bearing on path traversal.
|
||||
exists(EqualityTest b | b.getAnOperand() = e | b.getAnOperand() instanceof NullLiteral)
|
||||
}
|
||||
|
||||
// Ignore cases where the variable has been checked somehow,
|
||||
// but allow some particularly obviously bad cases.
|
||||
predicate guarded(VarAccess e) {
|
||||
exists(PathCreation p | e = p.getAnInput()) and
|
||||
exists(ConditionBlock cb, Expr c |
|
||||
cb.getCondition().getAChildExpr*() = c and
|
||||
c = e.getVariable().getAnAccess() and
|
||||
cb.controls(e.getBasicBlock(), true) and
|
||||
// Disallow a few obviously bad checks.
|
||||
not inWeakCheck(c)
|
||||
)
|
||||
}
|
||||
@@ -14,15 +14,18 @@
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import PathsCommon
|
||||
import semmle.code.java.security.PathCreation
|
||||
import DataFlow::PathGraph
|
||||
import TaintedPathCommon
|
||||
|
||||
class TaintedPathLocalConfig extends TaintTracking::Configuration {
|
||||
TaintedPathLocalConfig() { this = "TaintedPathLocalConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink.asExpr() = any(PathCreation p).getInput() }
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = any(PathCreation p).getAnInput()
|
||||
}
|
||||
}
|
||||
|
||||
from
|
||||
@@ -30,7 +33,7 @@ from
|
||||
TaintedPathLocalConfig conf
|
||||
where
|
||||
e = sink.getNode().asExpr() and
|
||||
e = p.getInput() and
|
||||
e = p.getAnInput() and
|
||||
conf.hasFlowPath(source, sink) and
|
||||
not guarded(e)
|
||||
select p, source, sink, "$@ flows to here and is used in a path.", source.getNode(),
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import DataFlow
|
||||
import semmle.code.java.frameworks.Jndi
|
||||
import semmle.code.java.frameworks.UnboundId
|
||||
import semmle.code.java.frameworks.SpringLdap
|
||||
import semmle.code.java.frameworks.ApacheLdap
|
||||
import semmle.code.java.security.LdapInjection
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for unvalidated user input that is used to construct LDAP queries.
|
||||
@@ -16,391 +13,9 @@ class LdapInjectionFlowConfig extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof LdapInjectionSink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType
|
||||
}
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof LdapInjectionSanitizer }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
ldapNameStep(node1, node2) or
|
||||
ldapNameAddAllStep(node1, node2) or
|
||||
ldapNameGetCloneStep(node1, node2) or
|
||||
filterStep(node1, node2) or
|
||||
filterToStringStep(node1, node2) or
|
||||
unboundIdSearchRequestStep(node1, node2) or
|
||||
unboundIdSearchRequestDuplicateStep(node1, node2) or
|
||||
unboundIdSearchRequestSetStep(node1, node2) or
|
||||
ldapQueryStep(node1, node2) or
|
||||
ldapQueryBaseStep(node1, node2) or
|
||||
ldapQueryBuilderStep(node1, node2) or
|
||||
hardcodedFilterStep(node1, node2) or
|
||||
springLdapFilterToStringStep(node1, node2) or
|
||||
ldapNameBuilderStep(node1, node2) or
|
||||
ldapNameBuilderBuildStep(node1, node2) or
|
||||
ldapUtilsStep(node1, node2) or
|
||||
apacheSearchRequestStep(node1, node2) or
|
||||
apacheSearchRequestGetStep(node1, node2) or
|
||||
apacheLdapDnStep(node1, node2) or
|
||||
apacheLdapDnGetStep(node1, node2)
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(LdapInjectionAdditionalTaintStep a).step(pred, succ)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JNDI sink for LDAP injection vulnerabilities, i.e. 1st (DN) or 2nd (filter) argument to
|
||||
* `search` method from `DirContext`.
|
||||
*/
|
||||
predicate jndiLdapInjectionSinkMethod(Method m, int index) {
|
||||
m.getDeclaringType().getAnAncestor() instanceof TypeDirContext and
|
||||
m.hasName("search") and
|
||||
index in [0 .. 1]
|
||||
}
|
||||
|
||||
/**
|
||||
* UnboundID sink for LDAP injection vulnerabilities,
|
||||
* i.e. LDAPConnection.search, LDAPConnection.asyncSearch or LDAPConnection.searchForEntry method.
|
||||
*/
|
||||
predicate unboundIdLdapInjectionSinkMethod(Method m, int index) {
|
||||
exists(Parameter param | m.getParameter(index) = param and not param.isVarargs() |
|
||||
m instanceof MethodUnboundIdLDAPConnectionSearch or
|
||||
m instanceof MethodUnboundIdLDAPConnectionAsyncSearch or
|
||||
m instanceof MethodUnboundIdLDAPConnectionSearchForEntry
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Spring LDAP sink for LDAP injection vulnerabilities,
|
||||
* i.e. LdapTemplate.authenticate, LdapTemplate.find* or LdapTemplate.search* method.
|
||||
*/
|
||||
predicate springLdapInjectionSinkMethod(Method m, int index) {
|
||||
// LdapTemplate.authenticate, LdapTemplate.find* or LdapTemplate.search* method
|
||||
(
|
||||
m instanceof MethodSpringLdapTemplateAuthenticate or
|
||||
m instanceof MethodSpringLdapTemplateFind or
|
||||
m instanceof MethodSpringLdapTemplateFindOne or
|
||||
m instanceof MethodSpringLdapTemplateSearch or
|
||||
m instanceof MethodSpringLdapTemplateSearchForContext or
|
||||
m instanceof MethodSpringLdapTemplateSearchForObject
|
||||
) and
|
||||
(
|
||||
// Parameter index is 1 (DN or query) or 2 (filter) if method is not authenticate
|
||||
index in [0 .. 1] and
|
||||
not m instanceof MethodSpringLdapTemplateAuthenticate
|
||||
or
|
||||
// But it's not the last parameter in case of authenticate method (last param is password)
|
||||
index in [0 .. 1] and
|
||||
index < m.getNumberOfParameters() - 1 and
|
||||
m instanceof MethodSpringLdapTemplateAuthenticate
|
||||
)
|
||||
}
|
||||
|
||||
/** Apache LDAP API sink for LDAP injection vulnerabilities, i.e. LdapConnection.search method. */
|
||||
predicate apacheLdapInjectionSinkMethod(Method m, int index) {
|
||||
exists(Parameter param | m.getParameter(index) = param and not param.isVarargs() |
|
||||
m.getDeclaringType().getAnAncestor() instanceof TypeApacheLdapConnection and
|
||||
m.hasName("search")
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if parameter at index `index` in method `m` is LDAP injection sink. */
|
||||
predicate ldapInjectionSinkMethod(Method m, int index) {
|
||||
jndiLdapInjectionSinkMethod(m, index) or
|
||||
unboundIdLdapInjectionSinkMethod(m, index) or
|
||||
springLdapInjectionSinkMethod(m, index) or
|
||||
apacheLdapInjectionSinkMethod(m, index)
|
||||
}
|
||||
|
||||
/** A data flow sink for unvalidated user input that is used to construct LDAP queries. */
|
||||
class LdapInjectionSink extends DataFlow::ExprNode {
|
||||
LdapInjectionSink() {
|
||||
exists(MethodAccess ma, Method m, int index |
|
||||
ma.getMethod() = m and
|
||||
ma.getArgument(index) = this.getExpr() and
|
||||
ldapInjectionSinkMethod(m, index)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and `LdapName`,
|
||||
* i.e. `new LdapName(tainted)`.
|
||||
*/
|
||||
predicate ldapNameStep(ExprNode n1, ExprNode n2) {
|
||||
exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeLdapName |
|
||||
n1.asExpr() = cc.getAnArgument() and
|
||||
n2.asExpr() = cc
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `List<Rdn>` and `LdapName`,
|
||||
* i.e. `new LdapName().addAll(tainted)`.
|
||||
*/
|
||||
predicate ldapNameAddAllStep(ExprNode n1, ExprNode n2) {
|
||||
exists(MethodAccess ma |
|
||||
n1.asExpr() = ma.getAnArgument() and
|
||||
(n2.asExpr() = ma or n2.asExpr() = ma.getQualifier())
|
||||
|
|
||||
ma.getMethod() instanceof MethodLdapNameAddAll
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `LdapName` and `LdapName` or
|
||||
* `String`, i.e. `taintedLdapName.clone()`, `taintedLdapName.getAll()`,
|
||||
* `taintedLdapName.getRdns()` or `taintedLdapName.toString()`.
|
||||
*/
|
||||
predicate ldapNameGetCloneStep(ExprNode n1, ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getQualifier() and
|
||||
n2.asExpr() = ma and
|
||||
ma.getMethod() = m
|
||||
|
|
||||
m instanceof MethodLdapNameClone or
|
||||
m instanceof MethodLdapNameGetAll or
|
||||
m instanceof MethodLdapNameGetRdns or
|
||||
m instanceof MethodLdapNameToString
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and UnboundID `Filter`,
|
||||
* i.e. `Filter.create*(tainted)`.
|
||||
*/
|
||||
predicate filterStep(ExprNode n1, ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getAnArgument() and
|
||||
n2.asExpr() = ma and
|
||||
ma.getMethod() = m
|
||||
|
|
||||
m instanceof MethodUnboundIdFilterCreate or
|
||||
m instanceof MethodUnboundIdFilterCreateANDFilter or
|
||||
m instanceof MethodUnboundIdFilterCreateNOTFilter or
|
||||
m instanceof MethodUnboundIdFilterCreateORFilter or
|
||||
m instanceof MethodUnboundIdFilterSimplifyFilter
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between UnboundID `Filter` and `String`,
|
||||
* i.e. `taintedFilter.toString()` or `taintedFilter.toString(buffer)`.
|
||||
*/
|
||||
predicate filterToStringStep(ExprNode n1, ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getQualifier() and
|
||||
(n2.asExpr() = ma or n2.asExpr() = ma.getAnArgument())
|
||||
|
|
||||
ma.getMethod() = m and
|
||||
m.getDeclaringType() instanceof TypeUnboundIdLdapFilter and
|
||||
(m.hasName("toString") or m.hasName("toNormalizedString"))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and UnboundID
|
||||
* `SearchRequest`, i.e. `new SearchRequest(tainted)`.
|
||||
*/
|
||||
predicate unboundIdSearchRequestStep(ExprNode n1, ExprNode n2) {
|
||||
exists(ConstructorCall cc, int index, Parameter param |
|
||||
cc.getConstructedType() instanceof TypeUnboundIdSearchRequest
|
||||
|
|
||||
n1.asExpr() = cc.getArgument(index) and
|
||||
n2.asExpr() = cc and
|
||||
cc.getConstructor().getParameter(index) = param and
|
||||
not param.isVarargs()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between UnboundID `SearchRequest`
|
||||
* and UnboundID `SearchRequest`, i.e. `taintedSearchRequest.duplicate()`.
|
||||
*/
|
||||
predicate unboundIdSearchRequestDuplicateStep(ExprNode n1, ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m | n1.asExpr() = ma.getQualifier() and n2.asExpr() = ma |
|
||||
ma.getMethod() = m and
|
||||
m.getDeclaringType().getAnAncestor() instanceof TypeUnboundIdReadOnlySearchRequest and
|
||||
m.hasName("duplicate")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between DN or filter and UnboundID
|
||||
* `SearchRequest`, i.e. `searchRequest.setBaseDN(tainted)` or `searchRequest.setFilter(tainted)`.
|
||||
*/
|
||||
predicate unboundIdSearchRequestSetStep(ExprNode n1, ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getAnArgument() and
|
||||
n2.asExpr() = ma.getQualifier() and
|
||||
ma.getMethod() = m
|
||||
|
|
||||
m instanceof MethodUnboundIdSearchRequestSetBaseDN or
|
||||
m instanceof MethodUnboundIdSearchRequestSetFilter
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and Spring `LdapQuery`,
|
||||
* i.e. `LdapQueryBuilder.query().filter(tainted)` or `LdapQueryBuilder.query().base(tainted)`.
|
||||
*/
|
||||
predicate ldapQueryStep(ExprNode n1, ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m, int index |
|
||||
n1.asExpr() = ma.getArgument(index) and
|
||||
n2.asExpr() = ma and
|
||||
ma.getMethod() = m and
|
||||
index = 0
|
||||
|
|
||||
m instanceof MethodSpringLdapQueryBuilderFilter or
|
||||
m instanceof MethodSpringLdapQueryBuilderBase
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between Spring `LdapQueryBuilder` and
|
||||
* `Name`, i.e. `taintedLdapQueryBuilder.base()`.
|
||||
*/
|
||||
predicate ldapQueryBaseStep(ExprNode n1, ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getQualifier() and
|
||||
n2.asExpr() = ma and
|
||||
ma.getMethod() = m
|
||||
|
|
||||
m instanceof MethodSpringLdapQueryBuilderBase and
|
||||
m.getNumberOfParameters() = 0
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between Spring `LdapQueryBuilder`,
|
||||
* `ConditionCriteria` or `ContainerCriteria`, i.e. when the query is built, for example
|
||||
* `query().base(tainted).where("objectclass").is("person")`.
|
||||
*/
|
||||
predicate ldapQueryBuilderStep(ExprNode n1, ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getQualifier() and
|
||||
n2.asExpr() = ma and
|
||||
ma.getMethod() = m
|
||||
|
|
||||
(
|
||||
m.getDeclaringType() instanceof TypeSpringLdapQueryBuilder or
|
||||
m.getDeclaringType() instanceof TypeSpringConditionCriteria or
|
||||
m.getDeclaringType() instanceof TypeSpringContainerCriteria
|
||||
) and
|
||||
(
|
||||
m.getReturnType() instanceof TypeSpringLdapQueryBuilder or
|
||||
m.getReturnType() instanceof TypeSpringConditionCriteria or
|
||||
m.getReturnType() instanceof TypeSpringContainerCriteria
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and Spring
|
||||
* `HardcodedFilter`, i.e. `new HardcodedFilter(tainted)`.
|
||||
*/
|
||||
predicate hardcodedFilterStep(ExprNode n1, ExprNode n2) {
|
||||
exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeSpringHardcodedFilter |
|
||||
n1.asExpr() = cc.getAnArgument() and
|
||||
n2.asExpr() = cc
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between Spring `Filter` and
|
||||
* `String`, i.e. `taintedFilter.toString()`, `taintedFilter.encode()` or
|
||||
* `taintedFilter.encode(buffer)`.
|
||||
*/
|
||||
predicate springLdapFilterToStringStep(ExprNode n1, ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getQualifier() and
|
||||
(n2.asExpr() = ma or n2.asExpr() = ma.getAnArgument()) and
|
||||
ma.getMethod() = m
|
||||
|
|
||||
m.getDeclaringType().getAnAncestor() instanceof TypeSpringLdapFilter and
|
||||
(m.hasName("encode") or m.hasName("toString"))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and Spring
|
||||
* `LdapNameBuilder`, i.e. `LdapNameBuilder.newInstance(tainted)` or
|
||||
* `LdapNameBuilder.newInstance().add(tainted)`.
|
||||
*/
|
||||
predicate ldapNameBuilderStep(ExprNode n1, ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getAnArgument() and
|
||||
(n2.asExpr() = ma or n2.asExpr() = ma.getQualifier()) and
|
||||
ma.getMethod() = m and
|
||||
m.getNumberOfParameters() = 1
|
||||
|
|
||||
m instanceof MethodSpringLdapNameBuilderNewInstance or
|
||||
m instanceof MethodSpringLdapNameBuilderAdd
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between tainted Spring `LdapNameBuilder`
|
||||
* and `LdapName`, `LdapNameBuilder.build()`.
|
||||
*/
|
||||
predicate ldapNameBuilderBuildStep(ExprNode n1, ExprNode n2) {
|
||||
exists(MethodAccess ma | n1.asExpr() = ma.getQualifier() and n2.asExpr() = ma |
|
||||
ma.getMethod() instanceof MethodSpringLdapNameBuilderBuild
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and `LdapName` via
|
||||
* Spring `LdapUtils.newLdapName`, i.e. `LdapUtils.newLdapName(tainted)`.
|
||||
*/
|
||||
predicate ldapUtilsStep(ExprNode n1, ExprNode n2) {
|
||||
exists(MethodAccess ma | n1.asExpr() = ma.getAnArgument() and n2.asExpr() = ma |
|
||||
ma.getMethod() instanceof MethodSpringLdapUtilsNewLdapName
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and Apache LDAP API
|
||||
* `SearchRequest`, i.e. `searchRequest.setFilter(tainted)` or `searchRequest.setBase(tainted)`.
|
||||
*/
|
||||
predicate apacheSearchRequestStep(ExprNode n1, ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getAnArgument() and
|
||||
n2.asExpr() = ma.getQualifier()
|
||||
|
|
||||
ma.getMethod() = m and
|
||||
m.getDeclaringType().getAnAncestor() instanceof TypeApacheSearchRequest and
|
||||
(m.hasName("setFilter") or m.hasName("setBase"))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between Apache LDAP API `SearchRequest`
|
||||
* and filter or DN i.e. `tainterSearchRequest.getFilter()` or `taintedSearchRequest.getBase()`.
|
||||
*/
|
||||
predicate apacheSearchRequestGetStep(ExprNode n1, ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m | n1.asExpr() = ma.getQualifier() and n2.asExpr() = ma |
|
||||
ma.getMethod() = m and
|
||||
m.getDeclaringType().getAnAncestor() instanceof TypeApacheSearchRequest and
|
||||
(m.hasName("getFilter") or m.hasName("getBase"))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and Apache LDAP API
|
||||
* `Dn`, i.e. `new Dn(tainted)`.
|
||||
*/
|
||||
predicate apacheLdapDnStep(ExprNode n1, ExprNode n2) {
|
||||
exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeApacheDn |
|
||||
n1.asExpr() = cc.getAnArgument() and
|
||||
n2.asExpr() = cc
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between Apache LDAP API `Dn`
|
||||
* and `String` i.e. `taintedDn.getName()`, `taintedDn.getNormName()` or `taintedDn.toString()`.
|
||||
*/
|
||||
predicate apacheLdapDnGetStep(ExprNode n1, ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m | n1.asExpr() = ma.getQualifier() and n2.asExpr() = ma |
|
||||
ma.getMethod() = m and
|
||||
m.getDeclaringType().getAnAncestor() instanceof TypeApacheDn and
|
||||
(m.hasName("getName") or m.hasName("getNormName") or m.hasName("toString"))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import ResponseSplitting
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.security.ResponseSplitting
|
||||
import DataFlow::PathGraph
|
||||
|
||||
class ResponseSplittingConfig extends TaintTracking::Configuration {
|
||||
@@ -19,7 +20,7 @@ class ResponseSplittingConfig extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source instanceof RemoteFlowSource and
|
||||
not source instanceof WhitelistedSource
|
||||
not source instanceof SafeHeaderSplittingSource
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof HeaderSplittingSink }
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import java
|
||||
import semmle.code.java.frameworks.Servlets
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
/**
|
||||
* Header-splitting sinks. Expressions that end up in an HTTP header.
|
||||
*/
|
||||
class HeaderSplittingSink extends DataFlow::ExprNode {
|
||||
HeaderSplittingSink() {
|
||||
exists(ResponseAddCookieMethod m, MethodAccess ma |
|
||||
ma.getMethod() = m and
|
||||
this.getExpr() = ma.getArgument(0)
|
||||
)
|
||||
or
|
||||
exists(ResponseAddHeaderMethod m, MethodAccess ma |
|
||||
ma.getMethod() = m and
|
||||
this.getExpr() = ma.getAnArgument()
|
||||
)
|
||||
or
|
||||
exists(ResponseSetHeaderMethod m, MethodAccess ma |
|
||||
ma.getMethod() = m and
|
||||
this.getExpr() = ma.getAnArgument()
|
||||
)
|
||||
or
|
||||
exists(JaxRsResponseBuilder builder, Method m |
|
||||
m = builder.getAMethod() and m.getName() = "header"
|
||||
|
|
||||
this.getExpr() = m.getAReference().getArgument(1)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class WhitelistedSource extends DataFlow::ExprNode {
|
||||
WhitelistedSource() {
|
||||
this.asExpr().(MethodAccess).getMethod() instanceof HttpServletRequestGetHeaderMethod or
|
||||
this.asExpr().(MethodAccess).getMethod() instanceof CookieGetNameMethod
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import ResponseSplitting
|
||||
import semmle.code.java.security.ResponseSplitting
|
||||
import DataFlow::PathGraph
|
||||
|
||||
class ResponseSplittingLocalConfig extends TaintTracking::Configuration {
|
||||
|
||||
@@ -22,7 +22,10 @@ import semmle.code.java.security.XSS
|
||||
*/
|
||||
class PrintStackTraceMethod extends Method {
|
||||
PrintStackTraceMethod() {
|
||||
getDeclaringType().hasQualifiedName("java.lang", "Throwable") and
|
||||
getDeclaringType()
|
||||
.getSourceDeclaration()
|
||||
.getASourceSupertype*()
|
||||
.hasQualifiedName("java.lang", "Throwable") and
|
||||
getName() = "printStackTrace"
|
||||
}
|
||||
}
|
||||
@@ -96,7 +99,8 @@ class StackTraceStringToXssSinkFlowConfig extends TaintTracking2::Configuration
|
||||
*/
|
||||
predicate printsStackExternally(MethodAccess call, Expr stackTrace) {
|
||||
printsStackToWriter(call) and
|
||||
call.getQualifier() = stackTrace
|
||||
call.getQualifier() = stackTrace and
|
||||
not call.getQualifier() instanceof SuperAccess
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @description Using broken or weak cryptographic algorithms can allow an attacker to compromise security.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @precision high
|
||||
* @id java/weak-cryptographic-algorithm
|
||||
* @tags security
|
||||
* external/cwe/cwe-327
|
||||
|
||||
@@ -17,7 +17,7 @@ import PathGraph
|
||||
*/
|
||||
private string getACredentialRegex() {
|
||||
result = "(?i).*challenge|pass(wd|word|code|phrase)(?!.*question).*" or
|
||||
result = "(?i)(.*username|url).*"
|
||||
result = "(?i)(.*username|.*secret|url).*"
|
||||
}
|
||||
|
||||
/** Variable keeps sensitive information judging by its name * */
|
||||
@@ -31,8 +31,12 @@ class CredentialExpr extends Expr {
|
||||
class LoggerType extends RefType {
|
||||
LoggerType() {
|
||||
this.hasQualifiedName("org.apache.log4j", "Category") or //Log4J
|
||||
this.hasQualifiedName("org.apache.logging.log4j", "Logger") or //Log4J 2
|
||||
this.hasQualifiedName("org.slf4j", "Logger") or //SLF4j and Gradle Logging
|
||||
this.hasQualifiedName("org.jboss.logging", "BasicLogger") //JBoss Logging
|
||||
this.hasQualifiedName("org.jboss.logging", "BasicLogger") or //JBoss Logging
|
||||
this.hasQualifiedName("org.jboss.logging", "Logger") or //JBoss Logging (`org.jboss.logging.Logger` in some implementations like JBoss Application Server 4.0.4 did not implement `BasicLogger`)
|
||||
this.hasQualifiedName("org.apache.commons.logging", "Log") or //Apache Commons Logging
|
||||
this.hasQualifiedName("org.scijava.log", "Logger") //SciJava Logging
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +46,8 @@ predicate isSensitiveLoggingSink(DataFlow::Node sink) {
|
||||
(
|
||||
ma.getMethod().hasName("debug") or
|
||||
ma.getMethod().hasName("trace") or
|
||||
ma.getMethod().hasName("debugf")
|
||||
ma.getMethod().hasName("debugf") or
|
||||
ma.getMethod().hasName("debugv")
|
||||
) and //Check low priority log levels which are more likely to be real issues to reduce false positives
|
||||
sink.asExpr() = ma.getAnArgument()
|
||||
)
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
public class InsecureBasicAuth {
|
||||
/**
|
||||
* Test basic authentication with Apache HTTP request.
|
||||
*/
|
||||
public void testApacheHttpRequest(String username, String password) {
|
||||
{
|
||||
// BAD: basic authentication over HTTP
|
||||
String url = "http://www.example.com/rest/getuser.do?uid=abcdx";
|
||||
}
|
||||
|
||||
{
|
||||
// GOOD: basic authentication over HTTPS
|
||||
String url = "https://www.example.com/rest/getuser.do?uid=abcdx";
|
||||
}
|
||||
|
||||
HttpPost post = new HttpPost(url);
|
||||
post.setHeader("Accept", "application/json");
|
||||
post.setHeader("Content-type", "application/json");
|
||||
|
||||
String authString = username + ":" + password;
|
||||
byte[] authEncBytes = Base64.getEncoder().encode(authString.getBytes());
|
||||
String authStringEnc = new String(authEncBytes);
|
||||
|
||||
post.addHeader("Authorization", "Basic " + authStringEnc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test basic authentication with Java HTTP URL connection.
|
||||
*/
|
||||
public void testHttpUrlConnection(String username, String password) {
|
||||
{
|
||||
// BAD: basic authentication over HTTP
|
||||
String urlStr = "http://www.example.com/rest/getuser.do?uid=abcdx";
|
||||
}
|
||||
|
||||
{
|
||||
// GOOD: basic authentication over HTTPS
|
||||
String urlStr = "https://www.example.com/rest/getuser.do?uid=abcdx";
|
||||
}
|
||||
|
||||
String authString = username + ":" + password;
|
||||
String encoding = Base64.getEncoder().encodeToString(authString.getBytes("UTF-8"));
|
||||
URL url = new URL(urlStr);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setDoOutput(true);
|
||||
conn.setRequestProperty("Authorization", "Basic " + encoding);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>Basic authentication only obfuscates username/password in Base64 encoding, which can be easily recognized and reversed, thus it must not be transmitted over the cleartext HTTP channel. Transmission of sensitive information not in HTTPS is vulnerable to packet sniffing.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>Either use a more secure authentication mechanism like digest authentication or federated authentication, or use the HTTPS communication protocol.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>The following example shows two ways of using basic authentication. In the 'BAD' case, the credentials are transmitted over HTTP. In the 'GOOD' case, the credentials are transmitted over HTTPS.</p>
|
||||
<sample src="InsecureBasicAuth.java" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
<a href="https://cwe.mitre.org/data/definitions/522">CWE-522</a>
|
||||
</li>
|
||||
<li>
|
||||
SonarSource rule:
|
||||
<a href="https://rules.sonarsource.com/java/tag/owasp/RSPEC-2647">Basic authentication should not be used</a>
|
||||
</li>
|
||||
<li>
|
||||
Acunetix:
|
||||
<a href="https://www.acunetix.com/vulnerabilities/web/basic-authentication-over-http/">WEB VULNERABILITIES INDEX - Basic authentication over HTTP</a>
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,263 @@
|
||||
/**
|
||||
* @name Insecure basic authentication
|
||||
* @description Basic authentication only obfuscates username/password in Base64 encoding, which can be easily recognized and reversed. Transmission of sensitive information not over HTTPS is vulnerable to packet sniffing.
|
||||
* @kind path-problem
|
||||
* @id java/insecure-basic-auth
|
||||
* @tags security
|
||||
* external/cwe-522
|
||||
* external/cwe-319
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.frameworks.Networking
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* Gets a regular expression for matching private hosts, which only matches the host portion therefore checking for port is not necessary.
|
||||
*/
|
||||
private string getPrivateHostRegex() {
|
||||
result =
|
||||
"(?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\\]?(?:[:/?#].*)?"
|
||||
}
|
||||
|
||||
/**
|
||||
* The Java class `org.apache.http.client.methods.HttpRequestBase`. Popular subclasses include `HttpGet`, `HttpPost`, and `HttpPut`.
|
||||
* And the Java class `org.apache.http.message.BasicHttpRequest`.
|
||||
*/
|
||||
class ApacheHttpRequest extends RefType {
|
||||
ApacheHttpRequest() {
|
||||
this
|
||||
.getASourceSupertype*()
|
||||
.hasQualifiedName("org.apache.http.client.methods", "HttpRequestBase") or
|
||||
this.getASourceSupertype*().hasQualifiedName("org.apache.http.message", "BasicHttpRequest")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class of Java URL constructor.
|
||||
*/
|
||||
class URLConstructor extends ClassInstanceExpr {
|
||||
URLConstructor() { this.getConstructor().getDeclaringType() instanceof TypeUrl }
|
||||
|
||||
predicate hasHttpStringArg() {
|
||||
this.getConstructor().getParameter(0).getType() instanceof TypeString and
|
||||
(
|
||||
// URLs constructed with any of the three string constructors below:
|
||||
// `URL(String protocol, String host, int port, String file)`,
|
||||
// `URL(String protocol, String host, int port, String file, URLStreamHandler handler)`,
|
||||
// `URL(String protocol, String host, String file)`
|
||||
this.getConstructor().getNumberOfParameters() > 1 and
|
||||
concatHttpString(getArgument(0), this.getArgument(1)) // First argument contains the protocol part and the second argument contains the host part.
|
||||
or
|
||||
// URLs constructed with the string constructor `URL(String spec)`
|
||||
this.getConstructor().getNumberOfParameters() = 1 and
|
||||
this.getArgument(0) instanceof HttpString // First argument contains the whole spec.
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class of Java URI constructor.
|
||||
*/
|
||||
class URIConstructor extends ClassInstanceExpr {
|
||||
URIConstructor() { this.getConstructor().getDeclaringType().hasQualifiedName("java.net", "URI") }
|
||||
|
||||
predicate hasHttpStringArg() {
|
||||
(
|
||||
this.getNumArgument() = 1 and
|
||||
this.getArgument(0) instanceof HttpString // `URI(String str)`
|
||||
or
|
||||
this.getNumArgument() = 4 and
|
||||
concatHttpString(this.getArgument(0), this.getArgument(1)) // `URI(String scheme, String host, String path, String fragment)`
|
||||
or
|
||||
this.getNumArgument() = 5 and
|
||||
concatHttpString(this.getArgument(0), this.getArgument(1)) // `URI(String scheme, String authority, String path, String query, String fragment)` without user-info in authority
|
||||
or
|
||||
this.getNumArgument() = 7 and
|
||||
concatHttpString(this.getArgument(0), this.getArgument(2)) // `URI(String scheme, String userInfo, String host, int port, String path, String query, String fragment)`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* String of HTTP URLs not in private domains.
|
||||
*/
|
||||
class HttpStringLiteral extends StringLiteral {
|
||||
HttpStringLiteral() {
|
||||
// Match URLs with the HTTP protocol and without private IP addresses to reduce false positives.
|
||||
exists(string s | this.getRepresentedString() = s |
|
||||
s.regexpMatch("(?i)http://[\\[a-zA-Z0-9].*") and
|
||||
not s.substring(7, s.length()).regexpMatch(getPrivateHostRegex())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks both parts of protocol and host.
|
||||
*/
|
||||
predicate concatHttpString(Expr protocol, Expr host) {
|
||||
(
|
||||
protocol.(CompileTimeConstantExpr).getStringValue().regexpMatch("(?i)http(://)?") or
|
||||
protocol
|
||||
.(VarAccess)
|
||||
.getVariable()
|
||||
.getAnAssignedValue()
|
||||
.(CompileTimeConstantExpr)
|
||||
.getStringValue()
|
||||
.regexpMatch("(?i)http(://)?")
|
||||
) and
|
||||
not exists(string hostString |
|
||||
hostString = host.(CompileTimeConstantExpr).getStringValue() or
|
||||
hostString =
|
||||
host.(VarAccess).getVariable().getAnAssignedValue().(CompileTimeConstantExpr).getStringValue()
|
||||
|
|
||||
hostString.length() = 0 or // Empty host is loopback address
|
||||
hostString.regexpMatch(getPrivateHostRegex())
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the leftmost operand in a concatenated string */
|
||||
Expr getLeftmostConcatOperand(Expr expr) {
|
||||
if expr instanceof AddExpr
|
||||
then result = getLeftmostConcatOperand(expr.(AddExpr).getLeftOperand())
|
||||
else result = expr
|
||||
}
|
||||
|
||||
/**
|
||||
* String concatenated with `HttpStringLiteral`.
|
||||
*/
|
||||
class HttpString extends Expr {
|
||||
HttpString() {
|
||||
this instanceof HttpStringLiteral
|
||||
or
|
||||
concatHttpString(this.(AddExpr).getLeftOperand(),
|
||||
getLeftmostConcatOperand(this.(AddExpr).getRightOperand()))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* String pattern of basic authentication.
|
||||
*/
|
||||
class BasicAuthString extends StringLiteral {
|
||||
BasicAuthString() { exists(string s | this.getRepresentedString() = s | s.matches("Basic %")) }
|
||||
}
|
||||
|
||||
/**
|
||||
* String concatenated with `BasicAuthString`.
|
||||
*/
|
||||
predicate builtFromBasicAuthStringConcat(Expr expr) {
|
||||
expr instanceof BasicAuthString
|
||||
or
|
||||
builtFromBasicAuthStringConcat(expr.(AddExpr).getLeftOperand())
|
||||
or
|
||||
exists(Expr other | builtFromBasicAuthStringConcat(other) |
|
||||
exists(Variable var | var.getAnAssignedValue() = other and var.getAnAccess() = expr)
|
||||
)
|
||||
}
|
||||
|
||||
/** The `openConnection` method of Java URL. Not to include `openStream` since it won't be used in this query. */
|
||||
class HttpURLOpenMethod extends Method {
|
||||
HttpURLOpenMethod() {
|
||||
this.getDeclaringType() instanceof TypeUrl and
|
||||
this.getName() = "openConnection"
|
||||
}
|
||||
}
|
||||
|
||||
/** Constructor of `ApacheHttpRequest` */
|
||||
predicate apacheHttpRequest(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(ConstructorCall cc |
|
||||
cc.getConstructedType() instanceof ApacheHttpRequest and
|
||||
node2.asExpr() = cc and
|
||||
cc.getAnArgument() = node1.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/** `URI` methods */
|
||||
predicate createURI(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(
|
||||
URIConstructor cc // new URI
|
||||
|
|
||||
node2.asExpr() = cc and
|
||||
cc.getArgument(0) = node1.asExpr()
|
||||
)
|
||||
or
|
||||
exists(
|
||||
StaticMethodAccess ma // URI.create
|
||||
|
|
||||
ma.getMethod().getDeclaringType().hasQualifiedName("java.net", "URI") and
|
||||
ma.getMethod().hasName("create") and
|
||||
node1.asExpr() = ma.getArgument(0) and
|
||||
node2.asExpr() = ma
|
||||
)
|
||||
}
|
||||
|
||||
/** Constructors of `URL` */
|
||||
predicate createURL(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(URLConstructor cc |
|
||||
node2.asExpr() = cc and
|
||||
cc.getArgument(0) = node1.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/** Method call of `HttpURLOpenMethod` */
|
||||
predicate urlOpen(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof HttpURLOpenMethod and
|
||||
node1.asExpr() = ma.getQualifier() and
|
||||
ma = node2.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/** Constructor of `BasicRequestLine` */
|
||||
predicate basicRequestLine(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(ConstructorCall mcc |
|
||||
mcc.getConstructedType().hasQualifiedName("org.apache.http.message", "BasicRequestLine") and
|
||||
mcc.getArgument(1) = node1.asExpr() and // `BasicRequestLine(String method, String uri, ProtocolVersion version)
|
||||
node2.asExpr() = mcc
|
||||
)
|
||||
}
|
||||
|
||||
class BasicAuthFlowConfig extends TaintTracking::Configuration {
|
||||
BasicAuthFlowConfig() { this = "InsecureBasicAuth::BasicAuthFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) {
|
||||
src.asExpr() instanceof HttpString
|
||||
or
|
||||
exists(URLConstructor uc |
|
||||
uc.hasHttpStringArg() and
|
||||
src.asExpr() = uc.getArgument(0)
|
||||
)
|
||||
or
|
||||
exists(URIConstructor uc |
|
||||
uc.hasHttpStringArg() and
|
||||
src.asExpr() = uc.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodAccess ma |
|
||||
sink.asExpr() = ma.getQualifier() and
|
||||
(
|
||||
ma.getMethod().hasName("addHeader") or
|
||||
ma.getMethod().hasName("setHeader") or
|
||||
ma.getMethod().hasName("setRequestProperty")
|
||||
) and
|
||||
ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "Authorization" and
|
||||
builtFromBasicAuthStringConcat(ma.getArgument(1))
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
apacheHttpRequest(node1, node2) or
|
||||
createURI(node1, node2) or
|
||||
basicRequestLine(node1, node2) or
|
||||
createURL(node1, node2) or
|
||||
urlOpen(node1, node2)
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, BasicAuthFlowConfig config
|
||||
where config.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Insecure basic authentication from $@.", source.getNode(),
|
||||
"HTTP url"
|
||||
@@ -1344,10 +1344,7 @@ class VarAccess extends Expr, @varaccess {
|
||||
*/
|
||||
predicate isLValue() {
|
||||
exists(Assignment a | a.getDest() = this) or
|
||||
exists(PreIncExpr e | e.getExpr() = this) or
|
||||
exists(PreDecExpr e | e.getExpr() = this) or
|
||||
exists(PostIncExpr e | e.getExpr() = this) or
|
||||
exists(PostDecExpr e | e.getExpr() = this)
|
||||
exists(UnaryAssignExpr e | e.getExpr() = this)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -194,7 +194,16 @@ class TypeVariable extends BoundedType, @typevariable {
|
||||
* and the second wildcard has a lower bound of `Float`.
|
||||
*/
|
||||
class Wildcard extends BoundedType, @wildcard {
|
||||
/** Holds if this wildcard has an upper bound. */
|
||||
/**
|
||||
* Holds if this wildcard is either unconstrained (i.e. `?`) or
|
||||
* has a type bound.
|
||||
*/
|
||||
override predicate hasTypeBound() { BoundedType.super.hasTypeBound() }
|
||||
|
||||
/**
|
||||
* Holds if this wildcard is either unconstrained (i.e. `?`) or
|
||||
* has an upper bound.
|
||||
*/
|
||||
predicate hasUpperBound() { wildcards(this, _, 1) }
|
||||
|
||||
/** Holds if this wildcard has a lower bound. */
|
||||
|
||||
146
java/ql/src/semmle/code/java/security/ExternalAPIs.qll
Normal file
146
java/ql/src/semmle/code/java/security/ExternalAPIs.qll
Normal file
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* Definitions for reasoning about untrusted data used in APIs defined outside the
|
||||
* database.
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
|
||||
/**
|
||||
* A `Method` that is considered a "safe" external API from a security perspective.
|
||||
*/
|
||||
abstract class SafeExternalAPIMethod extends Method { }
|
||||
|
||||
/** The default set of "safe" external APIs. */
|
||||
private class DefaultSafeExternalAPIMethod extends SafeExternalAPIMethod {
|
||||
DefaultSafeExternalAPIMethod() {
|
||||
this instanceof EqualsMethod
|
||||
or
|
||||
getName().regexpMatch("size|length|compareTo|getClass|lastIndexOf")
|
||||
or
|
||||
this.getDeclaringType().hasQualifiedName("org.apache.commons.lang3", "Validate")
|
||||
or
|
||||
getQualifiedName() = "Objects.equals"
|
||||
or
|
||||
getDeclaringType() instanceof TypeString and getName() = "equals"
|
||||
or
|
||||
getDeclaringType().hasQualifiedName("com.google.common.base", "Preconditions")
|
||||
or
|
||||
getDeclaringType().getPackage().getName().matches("org.junit%")
|
||||
or
|
||||
getDeclaringType().hasQualifiedName("com.google.common.base", "Strings") and
|
||||
getName() = "isNullOrEmpty"
|
||||
or
|
||||
getDeclaringType().hasQualifiedName("org.apache.commons.lang3", "StringUtils") and
|
||||
getName() = "isNotEmpty"
|
||||
or
|
||||
getDeclaringType().hasQualifiedName("java.lang", "Character") and
|
||||
getName() = "isDigit"
|
||||
or
|
||||
getDeclaringType().hasQualifiedName("java.lang", "String") and
|
||||
getName().regexpMatch("equalsIgnoreCase|regionMatches")
|
||||
or
|
||||
getDeclaringType().hasQualifiedName("java.lang", "Boolean") and
|
||||
getName() = "parseBoolean"
|
||||
or
|
||||
getDeclaringType().hasQualifiedName("org.apache.commons.io", "IOUtils") and
|
||||
getName() = "closeQuietly"
|
||||
or
|
||||
getDeclaringType().hasQualifiedName("org.springframework.util", "StringUtils") and
|
||||
getName().regexpMatch("hasText|isEmpty")
|
||||
}
|
||||
}
|
||||
|
||||
/** A node representing data being passed to an external API. */
|
||||
class ExternalAPIDataNode extends DataFlow::Node {
|
||||
Call call;
|
||||
int i;
|
||||
|
||||
ExternalAPIDataNode() {
|
||||
(
|
||||
// Argument to call to a method
|
||||
this.asExpr() = call.getArgument(i)
|
||||
or
|
||||
// Qualifier to call to a method which returns non trivial value
|
||||
this.asExpr() = call.getQualifier() and
|
||||
i = -1 and
|
||||
not call.getCallee().getReturnType() instanceof VoidType and
|
||||
not call.getCallee().getReturnType() instanceof BooleanType
|
||||
) and
|
||||
// Defined outside the source archive
|
||||
not call.getCallee().fromSource() and
|
||||
// Not a call to an method which is overridden in source
|
||||
not exists(Method m |
|
||||
m.getASourceOverriddenMethod() = call.getCallee().getSourceDeclaration() and
|
||||
m.fromSource()
|
||||
) and
|
||||
// Not already modeled as a taint step
|
||||
not exists(DataFlow::Node next | TaintTracking::localTaintStep(this, next)) and
|
||||
// Not a call to a known safe external API
|
||||
not call.getCallee() instanceof SafeExternalAPIMethod
|
||||
}
|
||||
|
||||
/** Gets the called API `Method`. */
|
||||
Method getMethod() { result = call.getCallee() }
|
||||
|
||||
/** Gets the index which is passed untrusted data (where -1 indicates the qualifier). */
|
||||
int getIndex() { result = i }
|
||||
|
||||
/** Gets the description of the method being called. */
|
||||
string getMethodDescription() {
|
||||
result = getMethod().getDeclaringType().getPackage() + "." + getMethod().getQualifiedName()
|
||||
}
|
||||
}
|
||||
|
||||
/** A configuration for tracking flow from `RemoteFlowSource`s to `ExternalAPIDataNode`s. */
|
||||
class UntrustedDataToExternalAPIConfig extends TaintTracking::Configuration {
|
||||
UntrustedDataToExternalAPIConfig() { this = "UntrustedDataToExternalAPIConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof ExternalAPIDataNode }
|
||||
}
|
||||
|
||||
/** A node representing untrusted data being passed to an external API. */
|
||||
class UntrustedExternalAPIDataNode extends ExternalAPIDataNode {
|
||||
UntrustedExternalAPIDataNode() { any(UntrustedDataToExternalAPIConfig c).hasFlow(_, this) }
|
||||
|
||||
/** Gets a source of untrusted data which is passed to this external API data node. */
|
||||
DataFlow::Node getAnUntrustedSource() {
|
||||
any(UntrustedDataToExternalAPIConfig c).hasFlow(result, this)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TExternalAPI =
|
||||
TExternalAPIParameter(Method m, int index) {
|
||||
exists(UntrustedExternalAPIDataNode n |
|
||||
m = n.getMethod() and
|
||||
index = n.getIndex()
|
||||
)
|
||||
}
|
||||
|
||||
/** An external API which is used with untrusted data. */
|
||||
class ExternalAPIUsedWithUntrustedData extends TExternalAPI {
|
||||
/** Gets a possibly untrusted use of this external API. */
|
||||
UntrustedExternalAPIDataNode getUntrustedDataNode() {
|
||||
this = TExternalAPIParameter(result.getMethod(), result.getIndex())
|
||||
}
|
||||
|
||||
/** Gets the number of untrusted sources used with this external API. */
|
||||
int getNumberOfUntrustedSources() {
|
||||
result = count(getUntrustedDataNode().getAnUntrustedSource())
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
exists(Method m, int index, string indexString |
|
||||
if index = -1 then indexString = "qualifier" else indexString = "param " + index
|
||||
|
|
||||
this = TExternalAPIParameter(m, index) and
|
||||
result =
|
||||
m.getDeclaringType().(RefType).getQualifiedName() + "." + m.getSignature() + " [" +
|
||||
indexString + "]"
|
||||
)
|
||||
}
|
||||
}
|
||||
413
java/ql/src/semmle/code/java/security/LdapInjection.qll
Normal file
413
java/ql/src/semmle/code/java/security/LdapInjection.qll
Normal file
@@ -0,0 +1,413 @@
|
||||
/** Provides classes to reason about LDAP injection attacks. */
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.frameworks.Jndi
|
||||
import semmle.code.java.frameworks.UnboundId
|
||||
import semmle.code.java.frameworks.SpringLdap
|
||||
import semmle.code.java.frameworks.ApacheLdap
|
||||
|
||||
/** A data flow sink for unvalidated user input that is used to construct LDAP queries. */
|
||||
abstract class LdapInjectionSink extends DataFlow::Node { }
|
||||
|
||||
/** A sanitizer that prevents LDAP injection attacks. */
|
||||
abstract class LdapInjectionSanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A unit class for adding additional taint steps.
|
||||
*
|
||||
* Extend this class to add additional taint steps that should apply to the `LdapInjectionFlowConfig`.
|
||||
*/
|
||||
class LdapInjectionAdditionalTaintStep extends TaintTracking::Unit {
|
||||
/**
|
||||
* Holds if the step from `node1` to `node2` should be considered a taint
|
||||
* step for the `LdapInjectionFlowConfig` configuration.
|
||||
*/
|
||||
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
|
||||
}
|
||||
|
||||
/** Default sink for LDAP injection vulnerabilities. */
|
||||
private class DefaultLdapInjectionSink extends LdapInjectionSink {
|
||||
DefaultLdapInjectionSink() {
|
||||
exists(MethodAccess ma, Method m, int index |
|
||||
ma.getMethod() = m and
|
||||
ma.getArgument(index) = this.asExpr() and
|
||||
ldapInjectionSinkMethod(m, index)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if the method parameter at `index` is susceptible to an LDAP injection attack. */
|
||||
private predicate ldapInjectionSinkMethod(Method m, int index) {
|
||||
jndiLdapInjectionSinkMethod(m, index) or
|
||||
unboundIdLdapInjectionSinkMethod(m, index) or
|
||||
springLdapInjectionSinkMethod(m, index) or
|
||||
apacheLdapInjectionSinkMethod(m, index)
|
||||
}
|
||||
|
||||
/** Holds if the JNDI method parameter at `index` is susceptible to an LDAP injection attack. */
|
||||
private predicate jndiLdapInjectionSinkMethod(Method m, int index) {
|
||||
m.getDeclaringType().getAnAncestor() instanceof TypeDirContext and
|
||||
m.hasName("search") and
|
||||
index in [0 .. 1]
|
||||
}
|
||||
|
||||
/** Holds if the UnboundID method parameter at `index` is susceptible to an LDAP injection attack. */
|
||||
private predicate unboundIdLdapInjectionSinkMethod(Method m, int index) {
|
||||
exists(Parameter param | m.getParameter(index) = param and not param.isVarargs() |
|
||||
m instanceof MethodUnboundIdLDAPConnectionSearch or
|
||||
m instanceof MethodUnboundIdLDAPConnectionAsyncSearch or
|
||||
m instanceof MethodUnboundIdLDAPConnectionSearchForEntry
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the Spring method parameter at `index` is susceptible to an LDAP injection attack. */
|
||||
private predicate springLdapInjectionSinkMethod(Method m, int index) {
|
||||
// LdapTemplate.authenticate, LdapTemplate.find* or LdapTemplate.search* method
|
||||
(
|
||||
m instanceof MethodSpringLdapTemplateAuthenticate or
|
||||
m instanceof MethodSpringLdapTemplateFind or
|
||||
m instanceof MethodSpringLdapTemplateFindOne or
|
||||
m instanceof MethodSpringLdapTemplateSearch or
|
||||
m instanceof MethodSpringLdapTemplateSearchForContext or
|
||||
m instanceof MethodSpringLdapTemplateSearchForObject
|
||||
) and
|
||||
(
|
||||
// Parameter index is 1 (DN or query) or 2 (filter) if method is not authenticate
|
||||
index in [0 .. 1] and
|
||||
not m instanceof MethodSpringLdapTemplateAuthenticate
|
||||
or
|
||||
// But it's not the last parameter in case of authenticate method (last param is password)
|
||||
index in [0 .. 1] and
|
||||
index < m.getNumberOfParameters() - 1 and
|
||||
m instanceof MethodSpringLdapTemplateAuthenticate
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the Apache LDAP API method parameter at `index` is susceptible to an LDAP injection attack. */
|
||||
private predicate apacheLdapInjectionSinkMethod(Method m, int index) {
|
||||
exists(Parameter param | m.getParameter(index) = param and not param.isVarargs() |
|
||||
m.getDeclaringType().getAnAncestor() instanceof TypeApacheLdapConnection and
|
||||
m.hasName("search")
|
||||
)
|
||||
}
|
||||
|
||||
/** A sanitizer that clears the taint on (boxed) primitive types. */
|
||||
private class DefaultLdapSanitizer extends LdapInjectionSanitizer {
|
||||
DefaultLdapSanitizer() {
|
||||
this.getType() instanceof PrimitiveType or
|
||||
this.getType() instanceof BoxedType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and `LdapName`,
|
||||
* i.e. `new LdapName(tainted)`.
|
||||
*/
|
||||
private predicate ldapNameStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeLdapName |
|
||||
n1.asExpr() = cc.getAnArgument() and
|
||||
n2.asExpr() = cc
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `List<Rdn>` and `LdapName`,
|
||||
* i.e. `new LdapName().addAll(tainted)`.
|
||||
*/
|
||||
private predicate ldapNameAddAllStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma |
|
||||
n1.asExpr() = ma.getAnArgument() and
|
||||
(n2.asExpr() = ma or n2.asExpr() = ma.getQualifier())
|
||||
|
|
||||
ma.getMethod() instanceof MethodLdapNameAddAll
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `LdapName` and `LdapName` or
|
||||
* `String`, i.e. `taintedLdapName.clone()`, `taintedLdapName.getAll()`,
|
||||
* `taintedLdapName.getRdns()` or `taintedLdapName.toString()`.
|
||||
*/
|
||||
private predicate ldapNameGetCloneStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getQualifier() and
|
||||
n2.asExpr() = ma and
|
||||
ma.getMethod() = m
|
||||
|
|
||||
m instanceof MethodLdapNameClone or
|
||||
m instanceof MethodLdapNameGetAll or
|
||||
m instanceof MethodLdapNameGetRdns or
|
||||
m instanceof MethodLdapNameToString
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and UnboundID `Filter`,
|
||||
* i.e. `Filter.create*(tainted)`.
|
||||
*/
|
||||
private predicate filterStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getAnArgument() and
|
||||
n2.asExpr() = ma and
|
||||
ma.getMethod() = m
|
||||
|
|
||||
m instanceof MethodUnboundIdFilterCreate or
|
||||
m instanceof MethodUnboundIdFilterCreateANDFilter or
|
||||
m instanceof MethodUnboundIdFilterCreateNOTFilter or
|
||||
m instanceof MethodUnboundIdFilterCreateORFilter or
|
||||
m instanceof MethodUnboundIdFilterSimplifyFilter
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between UnboundID `Filter` and `String`,
|
||||
* i.e. `taintedFilter.toString()` or `taintedFilter.toString(buffer)`.
|
||||
*/
|
||||
private predicate filterToStringStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getQualifier() and
|
||||
(n2.asExpr() = ma or n2.asExpr() = ma.getAnArgument())
|
||||
|
|
||||
ma.getMethod() = m and
|
||||
m.getDeclaringType() instanceof TypeUnboundIdLdapFilter and
|
||||
(m.hasName("toString") or m.hasName("toNormalizedString"))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and UnboundID
|
||||
* `SearchRequest`, i.e. `new SearchRequest(tainted)`.
|
||||
*/
|
||||
private predicate unboundIdSearchRequestStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(ConstructorCall cc, int index, Parameter param |
|
||||
cc.getConstructedType() instanceof TypeUnboundIdSearchRequest
|
||||
|
|
||||
n1.asExpr() = cc.getArgument(index) and
|
||||
n2.asExpr() = cc and
|
||||
cc.getConstructor().getParameter(index) = param and
|
||||
not param.isVarargs()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between UnboundID `SearchRequest`
|
||||
* and UnboundID `SearchRequest`, i.e. `taintedSearchRequest.duplicate()`.
|
||||
*/
|
||||
private predicate unboundIdSearchRequestDuplicateStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m | n1.asExpr() = ma.getQualifier() and n2.asExpr() = ma |
|
||||
ma.getMethod() = m and
|
||||
m.getDeclaringType().getAnAncestor() instanceof TypeUnboundIdReadOnlySearchRequest and
|
||||
m.hasName("duplicate")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between DN or filter and UnboundID
|
||||
* `SearchRequest`, i.e. `searchRequest.setBaseDN(tainted)` or `searchRequest.setFilter(tainted)`.
|
||||
*/
|
||||
private predicate unboundIdSearchRequestSetStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getAnArgument() and
|
||||
n2.asExpr() = ma.getQualifier() and
|
||||
ma.getMethod() = m
|
||||
|
|
||||
m instanceof MethodUnboundIdSearchRequestSetBaseDN or
|
||||
m instanceof MethodUnboundIdSearchRequestSetFilter
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and Spring `LdapQuery`,
|
||||
* i.e. `LdapQueryBuilder.query().filter(tainted)` or `LdapQueryBuilder.query().base(tainted)`.
|
||||
*/
|
||||
private predicate ldapQueryStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m, int index |
|
||||
n1.asExpr() = ma.getArgument(index) and
|
||||
n2.asExpr() = ma and
|
||||
ma.getMethod() = m and
|
||||
index = 0
|
||||
|
|
||||
m instanceof MethodSpringLdapQueryBuilderFilter or
|
||||
m instanceof MethodSpringLdapQueryBuilderBase
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between Spring `LdapQueryBuilder` and
|
||||
* `Name`, i.e. `taintedLdapQueryBuilder.base()`.
|
||||
*/
|
||||
private predicate ldapQueryBaseStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getQualifier() and
|
||||
n2.asExpr() = ma and
|
||||
ma.getMethod() = m
|
||||
|
|
||||
m instanceof MethodSpringLdapQueryBuilderBase and
|
||||
m.getNumberOfParameters() = 0
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between Spring `LdapQueryBuilder`,
|
||||
* `ConditionCriteria` or `ContainerCriteria`, i.e. when the query is built, for example
|
||||
* `query().base(tainted).where("objectclass").is("person")`.
|
||||
*/
|
||||
private predicate ldapQueryBuilderStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getQualifier() and
|
||||
n2.asExpr() = ma and
|
||||
ma.getMethod() = m
|
||||
|
|
||||
(
|
||||
m.getDeclaringType() instanceof TypeSpringLdapQueryBuilder or
|
||||
m.getDeclaringType() instanceof TypeSpringConditionCriteria or
|
||||
m.getDeclaringType() instanceof TypeSpringContainerCriteria
|
||||
) and
|
||||
(
|
||||
m.getReturnType() instanceof TypeSpringLdapQueryBuilder or
|
||||
m.getReturnType() instanceof TypeSpringConditionCriteria or
|
||||
m.getReturnType() instanceof TypeSpringContainerCriteria
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and Spring
|
||||
* `HardcodedFilter`, i.e. `new HardcodedFilter(tainted)`.
|
||||
*/
|
||||
private predicate hardcodedFilterStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeSpringHardcodedFilter |
|
||||
n1.asExpr() = cc.getAnArgument() and
|
||||
n2.asExpr() = cc
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between Spring `Filter` and
|
||||
* `String`, i.e. `taintedFilter.toString()`, `taintedFilter.encode()` or
|
||||
* `taintedFilter.encode(buffer)`.
|
||||
*/
|
||||
private predicate springLdapFilterToStringStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getQualifier() and
|
||||
(n2.asExpr() = ma or n2.asExpr() = ma.getAnArgument()) and
|
||||
ma.getMethod() = m
|
||||
|
|
||||
m.getDeclaringType().getAnAncestor() instanceof TypeSpringLdapFilter and
|
||||
(m.hasName("encode") or m.hasName("toString"))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and Spring
|
||||
* `LdapNameBuilder`, i.e. `LdapNameBuilder.newInstance(tainted)` or
|
||||
* `LdapNameBuilder.newInstance().add(tainted)`.
|
||||
*/
|
||||
private predicate ldapNameBuilderStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getAnArgument() and
|
||||
(n2.asExpr() = ma or n2.asExpr() = ma.getQualifier()) and
|
||||
ma.getMethod() = m and
|
||||
m.getNumberOfParameters() = 1
|
||||
|
|
||||
m instanceof MethodSpringLdapNameBuilderNewInstance or
|
||||
m instanceof MethodSpringLdapNameBuilderAdd
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between tainted Spring `LdapNameBuilder`
|
||||
* and `LdapName`, `LdapNameBuilder.build()`.
|
||||
*/
|
||||
private predicate ldapNameBuilderBuildStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma | n1.asExpr() = ma.getQualifier() and n2.asExpr() = ma |
|
||||
ma.getMethod() instanceof MethodSpringLdapNameBuilderBuild
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and `LdapName` via
|
||||
* Spring `LdapUtils.newLdapName`, i.e. `LdapUtils.newLdapName(tainted)`.
|
||||
*/
|
||||
private predicate ldapUtilsStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma | n1.asExpr() = ma.getAnArgument() and n2.asExpr() = ma |
|
||||
ma.getMethod() instanceof MethodSpringLdapUtilsNewLdapName
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and Apache LDAP API
|
||||
* `SearchRequest`, i.e. `searchRequest.setFilter(tainted)` or `searchRequest.setBase(tainted)`.
|
||||
*/
|
||||
private predicate apacheSearchRequestStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getAnArgument() and
|
||||
n2.asExpr() = ma.getQualifier()
|
||||
|
|
||||
ma.getMethod() = m and
|
||||
m.getDeclaringType().getAnAncestor() instanceof TypeApacheSearchRequest and
|
||||
(m.hasName("setFilter") or m.hasName("setBase"))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between Apache LDAP API `SearchRequest`
|
||||
* and filter or DN i.e. `tainterSearchRequest.getFilter()` or `taintedSearchRequest.getBase()`.
|
||||
*/
|
||||
private predicate apacheSearchRequestGetStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m | n1.asExpr() = ma.getQualifier() and n2.asExpr() = ma |
|
||||
ma.getMethod() = m and
|
||||
m.getDeclaringType().getAnAncestor() instanceof TypeApacheSearchRequest and
|
||||
(m.hasName("getFilter") or m.hasName("getBase"))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and Apache LDAP API
|
||||
* `Dn`, i.e. `new Dn(tainted)`.
|
||||
*/
|
||||
private predicate apacheLdapDnStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeApacheDn |
|
||||
n1.asExpr() = cc.getAnArgument() and
|
||||
n2.asExpr() = cc
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between Apache LDAP API `Dn`
|
||||
* and `String` i.e. `taintedDn.getName()`, `taintedDn.getNormName()` or `taintedDn.toString()`.
|
||||
*/
|
||||
private predicate apacheLdapDnGetStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m | n1.asExpr() = ma.getQualifier() and n2.asExpr() = ma |
|
||||
ma.getMethod() = m and
|
||||
m.getDeclaringType().getAnAncestor() instanceof TypeApacheDn and
|
||||
(m.hasName("getName") or m.hasName("getNormName") or m.hasName("toString"))
|
||||
)
|
||||
}
|
||||
|
||||
/** A set of additional taint steps to consider when taint tracking LDAP related data flows. */
|
||||
private class DefaultLdapInjectionAdditionalTaintStep extends LdapInjectionAdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
ldapNameStep(node1, node2) or
|
||||
ldapNameAddAllStep(node1, node2) or
|
||||
ldapNameGetCloneStep(node1, node2) or
|
||||
filterStep(node1, node2) or
|
||||
filterToStringStep(node1, node2) or
|
||||
unboundIdSearchRequestStep(node1, node2) or
|
||||
unboundIdSearchRequestDuplicateStep(node1, node2) or
|
||||
unboundIdSearchRequestSetStep(node1, node2) or
|
||||
ldapQueryStep(node1, node2) or
|
||||
ldapQueryBaseStep(node1, node2) or
|
||||
ldapQueryBuilderStep(node1, node2) or
|
||||
hardcodedFilterStep(node1, node2) or
|
||||
springLdapFilterToStringStep(node1, node2) or
|
||||
ldapNameBuilderStep(node1, node2) or
|
||||
ldapNameBuilderBuildStep(node1, node2) or
|
||||
ldapUtilsStep(node1, node2) or
|
||||
apacheSearchRequestStep(node1, node2) or
|
||||
apacheSearchRequestGetStep(node1, node2) or
|
||||
apacheLdapDnStep(node1, node2) or
|
||||
apacheLdapDnGetStep(node1, node2)
|
||||
}
|
||||
}
|
||||
141
java/ql/src/semmle/code/java/security/PathCreation.qll
Normal file
141
java/ql/src/semmle/code/java/security/PathCreation.qll
Normal file
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* Models the different ways to create paths. Either by using `java.io.File`-related APIs or `java.nio.file.Path`-related APIs.
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
/** Models the creation of a path. */
|
||||
abstract class PathCreation extends Expr {
|
||||
/**
|
||||
* Gets an input that is used in the creation of this path.
|
||||
* This excludes inputs of type `File` and `Path`.
|
||||
*/
|
||||
abstract Expr getAnInput();
|
||||
}
|
||||
|
||||
/** Models the `java.nio.file.Paths.get` method. */
|
||||
private class PathsGet extends PathCreation, MethodAccess {
|
||||
PathsGet() {
|
||||
exists(Method m | m = this.getMethod() |
|
||||
m.getDeclaringType() instanceof TypePaths and
|
||||
m.getName() = "get"
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getAnInput() { result = this.getAnArgument() }
|
||||
}
|
||||
|
||||
/** Models the `java.nio.file.FileSystem.getPath` method. */
|
||||
private class FileSystemGetPath extends PathCreation, MethodAccess {
|
||||
FileSystemGetPath() {
|
||||
exists(Method m | m = this.getMethod() |
|
||||
m.getDeclaringType() instanceof TypeFileSystem and
|
||||
m.getName() = "getPath"
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getAnInput() { result = this.getAnArgument() }
|
||||
}
|
||||
|
||||
/** Models the `new java.io.File(...)` constructor. */
|
||||
private class FileCreation extends PathCreation, ClassInstanceExpr {
|
||||
FileCreation() { this.getConstructedType() instanceof TypeFile }
|
||||
|
||||
override Expr getAnInput() {
|
||||
result = this.getAnArgument() and
|
||||
// Relevant arguments include those that are not a `File`.
|
||||
not result.getType() instanceof TypeFile
|
||||
}
|
||||
}
|
||||
|
||||
/** Models the `java.nio.file.Path.resolveSibling` method. */
|
||||
private class PathResolveSiblingCreation extends PathCreation, MethodAccess {
|
||||
PathResolveSiblingCreation() {
|
||||
exists(Method m | m = this.getMethod() |
|
||||
m.getDeclaringType() instanceof TypePath and
|
||||
m.getName() = "resolveSibling"
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getAnInput() {
|
||||
result = this.getAnArgument() and
|
||||
// Relevant arguments are those of type `String`.
|
||||
result.getType() instanceof TypeString
|
||||
}
|
||||
}
|
||||
|
||||
/** Models the `java.nio.file.Path.resolve` method. */
|
||||
private class PathResolveCreation extends PathCreation, MethodAccess {
|
||||
PathResolveCreation() {
|
||||
exists(Method m | m = this.getMethod() |
|
||||
m.getDeclaringType() instanceof TypePath and
|
||||
m.getName() = "resolve"
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getAnInput() {
|
||||
result = this.getAnArgument() and
|
||||
// Relevant arguments are those of type `String`.
|
||||
result.getType() instanceof TypeString
|
||||
}
|
||||
}
|
||||
|
||||
/** Models the `java.nio.file.Path.of` method. */
|
||||
private class PathOfCreation extends PathCreation, MethodAccess {
|
||||
PathOfCreation() {
|
||||
exists(Method m | m = this.getMethod() |
|
||||
m.getDeclaringType() instanceof TypePath and
|
||||
m.getName() = "of"
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getAnInput() { result = this.getAnArgument() }
|
||||
}
|
||||
|
||||
/** Models the `new java.io.FileWriter(...)` constructor. */
|
||||
private class FileWriterCreation extends PathCreation, ClassInstanceExpr {
|
||||
FileWriterCreation() { this.getConstructedType().hasQualifiedName("java.io", "FileWriter") }
|
||||
|
||||
override Expr getAnInput() {
|
||||
result = this.getAnArgument() and
|
||||
// Relevant arguments are those of type `String`.
|
||||
result.getType() instanceof TypeString
|
||||
}
|
||||
}
|
||||
|
||||
/** Models the `new java.io.FileReader(...)` constructor. */
|
||||
private class FileReaderCreation extends PathCreation, ClassInstanceExpr {
|
||||
FileReaderCreation() { this.getConstructedType().hasQualifiedName("java.io", "FileReader") }
|
||||
|
||||
override Expr getAnInput() {
|
||||
result = this.getAnArgument() and
|
||||
// Relevant arguments are those of type `String`.
|
||||
result.getType() instanceof TypeString
|
||||
}
|
||||
}
|
||||
|
||||
/** Models the `new java.io.FileInputStream(...)` constructor. */
|
||||
private class FileInputStreamCreation extends PathCreation, ClassInstanceExpr {
|
||||
FileInputStreamCreation() {
|
||||
this.getConstructedType().hasQualifiedName("java.io", "FileInputStream")
|
||||
}
|
||||
|
||||
override Expr getAnInput() {
|
||||
result = this.getAnArgument() and
|
||||
// Relevant arguments are those of type `String`.
|
||||
result.getType() instanceof TypeString
|
||||
}
|
||||
}
|
||||
|
||||
/** Models the `new java.io.FileOutputStream(...)` constructor. */
|
||||
private class FileOutputStreamCreation extends PathCreation, ClassInstanceExpr {
|
||||
FileOutputStreamCreation() {
|
||||
this.getConstructedType().hasQualifiedName("java.io", "FileOutputStream")
|
||||
}
|
||||
|
||||
override Expr getAnInput() {
|
||||
result = this.getAnArgument() and
|
||||
// Relevant arguments are those of type `String`.
|
||||
result.getType() instanceof TypeString
|
||||
}
|
||||
}
|
||||
49
java/ql/src/semmle/code/java/security/ResponseSplitting.qll
Normal file
49
java/ql/src/semmle/code/java/security/ResponseSplitting.qll
Normal file
@@ -0,0 +1,49 @@
|
||||
/** Provides classes to reason about header splitting attacks. */
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.frameworks.Servlets
|
||||
import semmle.code.java.frameworks.JaxWS
|
||||
|
||||
/** A sink that is vulnerable to an HTTP header splitting attack. */
|
||||
abstract class HeaderSplittingSink extends DataFlow::Node { }
|
||||
|
||||
/** A source that introduces data considered safe to use by a header splitting source. */
|
||||
abstract class SafeHeaderSplittingSource extends DataFlow::Node {
|
||||
SafeHeaderSplittingSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/** A sink that identifies a Java Servlet or JaxWs method that is vulnerable to an HTTP header splitting attack. */
|
||||
private class ServletHeaderSplittingSink extends HeaderSplittingSink {
|
||||
ServletHeaderSplittingSink() {
|
||||
exists(ResponseAddCookieMethod m, MethodAccess ma |
|
||||
ma.getMethod() = m and
|
||||
this.asExpr() = ma.getArgument(0)
|
||||
)
|
||||
or
|
||||
exists(ResponseAddHeaderMethod m, MethodAccess ma |
|
||||
ma.getMethod() = m and
|
||||
this.asExpr() = ma.getAnArgument()
|
||||
)
|
||||
or
|
||||
exists(ResponseSetHeaderMethod m, MethodAccess ma |
|
||||
ma.getMethod() = m and
|
||||
this.asExpr() = ma.getAnArgument()
|
||||
)
|
||||
or
|
||||
exists(JaxRsResponseBuilder builder, Method m |
|
||||
m = builder.getAMethod() and m.getName() = "header"
|
||||
|
|
||||
this.asExpr() = m.getAReference().getArgument(1)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A default source that introduces data considered safe to use by a header splitting source. */
|
||||
private class DefaultSafeHeaderSplittingSource extends SafeHeaderSplittingSource {
|
||||
DefaultSafeHeaderSplittingSource() {
|
||||
this.asExpr().(MethodAccess).getMethod() instanceof HttpServletRequestGetHeaderMethod or
|
||||
this.asExpr().(MethodAccess).getMethod() instanceof CookieGetNameMethod
|
||||
}
|
||||
}
|
||||
@@ -97,6 +97,7 @@ class WritingMethod extends Method {
|
||||
(
|
||||
this.getName().matches("print%") or
|
||||
this.getName() = "append" or
|
||||
this.getName() = "format" or
|
||||
this.getName() = "write"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
edges
|
||||
| InsecureBasicAuth.java:20:39:20:52 | ... + ... : String | InsecureBasicAuth.java:28:3:28:6 | post |
|
||||
| InsecureBasicAuth.java:35:19:35:64 | "http://www.example.com:8000/payment/retrieve" : String | InsecureBasicAuth.java:38:3:38:5 | get |
|
||||
| InsecureBasicAuth.java:45:19:45:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" : String | InsecureBasicAuth.java:54:3:54:6 | post |
|
||||
| InsecureBasicAuth.java:61:19:61:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" : String | InsecureBasicAuth.java:71:3:71:6 | post |
|
||||
| InsecureBasicAuth.java:78:47:78:52 | "http" : String | InsecureBasicAuth.java:86:3:86:6 | post |
|
||||
| InsecureBasicAuth.java:93:19:93:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" : String | InsecureBasicAuth.java:102:3:102:6 | post |
|
||||
| InsecureBasicAuth.java:109:19:109:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" : String | InsecureBasicAuth.java:119:3:119:6 | post |
|
||||
| InsecureBasicAuth.java:126:19:126:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" : String | InsecureBasicAuth.java:130:28:130:67 | (...)... : URLConnection |
|
||||
| InsecureBasicAuth.java:130:28:130:67 | (...)... : URLConnection | InsecureBasicAuth.java:133:3:133:6 | conn |
|
||||
| InsecureBasicAuth.java:145:21:145:28 | protocol : String | InsecureBasicAuth.java:146:28:146:67 | (...)... : URLConnection |
|
||||
| InsecureBasicAuth.java:146:28:146:67 | (...)... : URLConnection | InsecureBasicAuth.java:149:3:149:6 | conn |
|
||||
nodes
|
||||
| InsecureBasicAuth.java:20:39:20:52 | ... + ... : String | semmle.label | ... + ... : String |
|
||||
| InsecureBasicAuth.java:28:3:28:6 | post | semmle.label | post |
|
||||
| InsecureBasicAuth.java:35:19:35:64 | "http://www.example.com:8000/payment/retrieve" : String | semmle.label | "http://www.example.com:8000/payment/retrieve" : String |
|
||||
| InsecureBasicAuth.java:38:3:38:5 | get | semmle.label | get |
|
||||
| InsecureBasicAuth.java:45:19:45:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" : String | semmle.label | "http://www.example.com/rest/getuser.do?uid=abcdx" : String |
|
||||
| InsecureBasicAuth.java:54:3:54:6 | post | semmle.label | post |
|
||||
| InsecureBasicAuth.java:61:19:61:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" : String | semmle.label | "http://www.example.com/rest/getuser.do?uid=abcdx" : String |
|
||||
| InsecureBasicAuth.java:71:3:71:6 | post | semmle.label | post |
|
||||
| InsecureBasicAuth.java:78:47:78:52 | "http" : String | semmle.label | "http" : String |
|
||||
| InsecureBasicAuth.java:86:3:86:6 | post | semmle.label | post |
|
||||
| InsecureBasicAuth.java:93:19:93:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" : String | semmle.label | "http://www.example.com/rest/getuser.do?uid=abcdx" : String |
|
||||
| InsecureBasicAuth.java:102:3:102:6 | post | semmle.label | post |
|
||||
| InsecureBasicAuth.java:109:19:109:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" : String | semmle.label | "http://www.example.com/rest/getuser.do?uid=abcdx" : String |
|
||||
| InsecureBasicAuth.java:119:3:119:6 | post | semmle.label | post |
|
||||
| InsecureBasicAuth.java:126:19:126:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" : String | semmle.label | "http://www.example.com/rest/getuser.do?uid=abcdx" : String |
|
||||
| InsecureBasicAuth.java:130:28:130:67 | (...)... : URLConnection | semmle.label | (...)... : URLConnection |
|
||||
| InsecureBasicAuth.java:133:3:133:6 | conn | semmle.label | conn |
|
||||
| InsecureBasicAuth.java:145:21:145:28 | protocol : String | semmle.label | protocol : String |
|
||||
| InsecureBasicAuth.java:146:28:146:67 | (...)... : URLConnection | semmle.label | (...)... : URLConnection |
|
||||
| InsecureBasicAuth.java:149:3:149:6 | conn | semmle.label | conn |
|
||||
#select
|
||||
| InsecureBasicAuth.java:28:3:28:6 | post | InsecureBasicAuth.java:20:39:20:52 | ... + ... : String | InsecureBasicAuth.java:28:3:28:6 | post | Insecure basic authentication from $@. | InsecureBasicAuth.java:20:39:20:52 | ... + ... | HTTP url |
|
||||
| InsecureBasicAuth.java:38:3:38:5 | get | InsecureBasicAuth.java:35:19:35:64 | "http://www.example.com:8000/payment/retrieve" : String | InsecureBasicAuth.java:38:3:38:5 | get | Insecure basic authentication from $@. | InsecureBasicAuth.java:35:19:35:64 | "http://www.example.com:8000/payment/retrieve" | HTTP url |
|
||||
| InsecureBasicAuth.java:54:3:54:6 | post | InsecureBasicAuth.java:45:19:45:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" : String | InsecureBasicAuth.java:54:3:54:6 | post | Insecure basic authentication from $@. | InsecureBasicAuth.java:45:19:45:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" | HTTP url |
|
||||
| InsecureBasicAuth.java:71:3:71:6 | post | InsecureBasicAuth.java:61:19:61:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" : String | InsecureBasicAuth.java:71:3:71:6 | post | Insecure basic authentication from $@. | InsecureBasicAuth.java:61:19:61:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" | HTTP url |
|
||||
| InsecureBasicAuth.java:86:3:86:6 | post | InsecureBasicAuth.java:78:47:78:52 | "http" : String | InsecureBasicAuth.java:86:3:86:6 | post | Insecure basic authentication from $@. | InsecureBasicAuth.java:78:47:78:52 | "http" | HTTP url |
|
||||
| InsecureBasicAuth.java:102:3:102:6 | post | InsecureBasicAuth.java:93:19:93:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" : String | InsecureBasicAuth.java:102:3:102:6 | post | Insecure basic authentication from $@. | InsecureBasicAuth.java:93:19:93:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" | HTTP url |
|
||||
| InsecureBasicAuth.java:119:3:119:6 | post | InsecureBasicAuth.java:109:19:109:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" : String | InsecureBasicAuth.java:119:3:119:6 | post | Insecure basic authentication from $@. | InsecureBasicAuth.java:109:19:109:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" | HTTP url |
|
||||
| InsecureBasicAuth.java:133:3:133:6 | conn | InsecureBasicAuth.java:126:19:126:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" : String | InsecureBasicAuth.java:133:3:133:6 | conn | Insecure basic authentication from $@. | InsecureBasicAuth.java:126:19:126:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" | HTTP url |
|
||||
| InsecureBasicAuth.java:149:3:149:6 | conn | InsecureBasicAuth.java:145:21:145:28 | protocol : String | InsecureBasicAuth.java:149:3:149:6 | conn | Insecure basic authentication from $@. | InsecureBasicAuth.java:145:21:145:28 | protocol | HTTP url |
|
||||
@@ -0,0 +1,164 @@
|
||||
import org.apache.http.RequestLine;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.message.BasicHttpRequest;
|
||||
import org.apache.http.message.BasicRequestLine;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Base64;
|
||||
|
||||
public class InsecureBasicAuth {
|
||||
/**
|
||||
* Test basic authentication with Apache HTTP POST request using string constructor.
|
||||
*/
|
||||
public void testApacheHttpRequest(String username, String password) {
|
||||
String host = "www.example.com";
|
||||
HttpRequestBase post = new HttpPost("http://"+host+"/rest/getuser.do?uid=abcdx");
|
||||
post.setHeader("Accept", "application/json");
|
||||
post.setHeader("Content-type", "application/json");
|
||||
|
||||
String authString = username + ":" + password;
|
||||
byte[] authEncBytes = Base64.getEncoder().encode(authString.getBytes());
|
||||
String authStringEnc = new String(authEncBytes);
|
||||
|
||||
post.addHeader("Authorization", "Basic " + authStringEnc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test basic authentication with Apache HTTP GET request.
|
||||
*/
|
||||
public void testApacheHttpRequest2(String url) throws java.io.IOException {
|
||||
String urlStr = "http://www.example.com:8000/payment/retrieve";
|
||||
HttpGet get = new HttpGet(urlStr);
|
||||
get.setHeader("Accept", "application/json");
|
||||
get.setHeader("Authorization", "Basic " + new String(Base64.getEncoder().encode("admin:test".getBytes())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test basic authentication with Apache HTTP POST request using URI create method.
|
||||
*/
|
||||
public void testApacheHttpRequest3(String username, String password) {
|
||||
String uriStr = "http://www.example.com/rest/getuser.do?uid=abcdx";
|
||||
HttpRequestBase post = new HttpPost(URI.create(uriStr));
|
||||
post.setHeader("Accept", "application/json");
|
||||
post.setHeader("Content-type", "application/json");
|
||||
|
||||
String authString = username + ":" + password;
|
||||
byte[] authEncBytes = Base64.getEncoder().encode(authString.getBytes());
|
||||
String authStringEnc = new String(authEncBytes);
|
||||
|
||||
post.addHeader("Authorization", "Basic " + authStringEnc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test basic authentication with Apache HTTP POST request using the URI constructor with one argument.
|
||||
*/
|
||||
public void testApacheHttpRequest4(String username, String password) {
|
||||
String uriStr = "http://www.example.com/rest/getuser.do?uid=abcdx";
|
||||
URI uri = new URI(uriStr);
|
||||
HttpRequestBase post = new HttpPost(uri);
|
||||
post.setHeader("Accept", "application/json");
|
||||
post.setHeader("Content-type", "application/json");
|
||||
|
||||
String authString = username + ":" + password;
|
||||
byte[] authEncBytes = Base64.getEncoder().encode(authString.getBytes());
|
||||
String authStringEnc = new String(authEncBytes);
|
||||
|
||||
post.addHeader("Authorization", "Basic " + authStringEnc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test basic authentication with Apache HTTP POST request using a URI constructor with multiple arguments.
|
||||
*/
|
||||
public void testApacheHttpRequest5(String username, String password) {
|
||||
HttpRequestBase post = new HttpPost(new URI("http", "www.example.com", "/test", "abc=123", null));
|
||||
post.setHeader("Accept", "application/json");
|
||||
post.setHeader("Content-type", "application/json");
|
||||
|
||||
String authString = username + ":" + password;
|
||||
byte[] authEncBytes = Base64.getEncoder().encode(authString.getBytes());
|
||||
String authStringEnc = new String(authEncBytes);
|
||||
|
||||
post.addHeader("Authorization", "Basic " + authStringEnc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test basic authentication with Apache HTTP `BasicHttpRequest` using string constructor.
|
||||
*/
|
||||
public void testApacheHttpRequest6(String username, String password) {
|
||||
String uriStr = "http://www.example.com/rest/getuser.do?uid=abcdx";
|
||||
BasicHttpRequest post = new BasicHttpRequest("POST", uriStr);
|
||||
post.setHeader("Accept", "application/json");
|
||||
post.setHeader("Content-type", "application/json");
|
||||
|
||||
String authString = username + ":" + password;
|
||||
byte[] authEncBytes = Base64.getEncoder().encode(authString.getBytes());
|
||||
String authStringEnc = new String(authEncBytes);
|
||||
|
||||
post.addHeader("Authorization", "Basic " + authStringEnc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test basic authentication with Apache HTTP `BasicHttpRequest` using `RequestLine`.
|
||||
*/
|
||||
public void testApacheHttpRequest7(String username, String password) {
|
||||
String uriStr = "http://www.example.com/rest/getuser.do?uid=abcdx";
|
||||
RequestLine requestLine = new BasicRequestLine("POST", uriStr, null);
|
||||
BasicHttpRequest post = new BasicHttpRequest(requestLine);
|
||||
post.setHeader("Accept", "application/json");
|
||||
post.setHeader("Content-type", "application/json");
|
||||
|
||||
String authString = username + ":" + password;
|
||||
byte[] authEncBytes = Base64.getEncoder().encode(authString.getBytes());
|
||||
String authStringEnc = new String(authEncBytes);
|
||||
|
||||
post.addHeader("Authorization", "Basic " + authStringEnc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test basic authentication with Java HTTP URL connection using the `URL(String spec)` constructor.
|
||||
*/
|
||||
public void testHttpUrlConnection(String username, String password) {
|
||||
String urlStr = "http://www.example.com/rest/getuser.do?uid=abcdx";
|
||||
String authString = username + ":" + password;
|
||||
String encoding = Base64.getEncoder().encodeToString(authString.getBytes("UTF-8"));
|
||||
URL url = new URL(urlStr);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setDoOutput(true);
|
||||
conn.setRequestProperty("Authorization", "Basic " + encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test basic authentication with Java HTTP URL connection using the `URL(String protocol, String host, String file)` constructor.
|
||||
*/
|
||||
public void testHttpUrlConnection2(String username, String password) {
|
||||
String host = "www.example.com";
|
||||
String path = "/rest/getuser.do?uid=abcdx";
|
||||
String protocol = "http";
|
||||
String authString = username + ":" + password;
|
||||
String encoding = Base64.getEncoder().encodeToString(authString.getBytes("UTF-8"));
|
||||
URL url = new URL(protocol, host, path);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setDoOutput(true);
|
||||
conn.setRequestProperty("Authorization", "Basic " + encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test basic authentication with Java HTTP URL connection using a constructor with private URL.
|
||||
*/
|
||||
public void testHttpUrlConnection3(String username, String password) {
|
||||
String host = "LOCALHOST";
|
||||
String authString = username + ":" + password;
|
||||
String encoding = Base64.getEncoder().encodeToString(authString.getBytes("UTF-8"));
|
||||
HttpURLConnection conn = (HttpURLConnection) new URL("http://"+(((host+"/rest/getuser.do")+"?uid=abcdx"))).openConnection();
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setDoOutput(true);
|
||||
conn.setRequestProperty("Authorization", "Basic " + encoding);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-522/InsecureBasicAuth.ql
|
||||
@@ -0,0 +1 @@
|
||||
// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/apache-http-4.4.13
|
||||
@@ -0,0 +1,25 @@
|
||||
| PathCreation.java:13:18:13:32 | new File(...) | PathCreation.java:13:27:13:31 | "dir" |
|
||||
| PathCreation.java:14:19:14:40 | new File(...) | PathCreation.java:14:28:14:32 | "dir" |
|
||||
| PathCreation.java:14:19:14:40 | new File(...) | PathCreation.java:14:35:14:39 | "sub" |
|
||||
| PathCreation.java:18:18:18:49 | new File(...) | PathCreation.java:18:44:18:48 | "sub" |
|
||||
| PathCreation.java:18:27:18:41 | new File(...) | PathCreation.java:18:36:18:40 | "dir" |
|
||||
| PathCreation.java:22:18:22:41 | new File(...) | PathCreation.java:22:27:22:40 | new URI(...) |
|
||||
| PathCreation.java:26:18:26:31 | of(...) | PathCreation.java:26:26:26:30 | "dir" |
|
||||
| PathCreation.java:27:19:27:39 | of(...) | PathCreation.java:27:27:27:31 | "dir" |
|
||||
| PathCreation.java:27:19:27:39 | of(...) | PathCreation.java:27:34:27:38 | "sub" |
|
||||
| PathCreation.java:31:18:31:40 | of(...) | PathCreation.java:31:26:31:39 | new URI(...) |
|
||||
| PathCreation.java:35:18:35:33 | get(...) | PathCreation.java:35:28:35:32 | "dir" |
|
||||
| PathCreation.java:36:19:36:41 | get(...) | PathCreation.java:36:29:36:33 | "dir" |
|
||||
| PathCreation.java:36:19:36:41 | get(...) | PathCreation.java:36:36:36:40 | "sub" |
|
||||
| PathCreation.java:40:18:40:42 | get(...) | PathCreation.java:40:28:40:41 | new URI(...) |
|
||||
| PathCreation.java:44:18:44:56 | getPath(...) | PathCreation.java:44:51:44:55 | "dir" |
|
||||
| PathCreation.java:45:19:45:64 | getPath(...) | PathCreation.java:45:52:45:56 | "dir" |
|
||||
| PathCreation.java:45:19:45:64 | getPath(...) | PathCreation.java:45:59:45:63 | "sub" |
|
||||
| PathCreation.java:49:18:49:31 | of(...) | PathCreation.java:49:26:49:30 | "dir" |
|
||||
| PathCreation.java:49:18:49:53 | resolveSibling(...) | PathCreation.java:49:48:49:52 | "sub" |
|
||||
| PathCreation.java:53:18:53:31 | of(...) | PathCreation.java:53:26:53:30 | "dir" |
|
||||
| PathCreation.java:53:18:53:46 | resolve(...) | PathCreation.java:53:41:53:45 | "sub" |
|
||||
| PathCreation.java:57:25:57:45 | new FileWriter(...) | PathCreation.java:57:40:57:44 | "dir" |
|
||||
| PathCreation.java:61:25:61:45 | new FileReader(...) | PathCreation.java:61:40:61:44 | "dir" |
|
||||
| PathCreation.java:65:32:65:58 | new FileOutputStream(...) | PathCreation.java:65:53:65:57 | "dir" |
|
||||
| PathCreation.java:69:31:69:56 | new FileInputStream(...) | PathCreation.java:69:51:69:55 | "dir" |
|
||||
71
java/ql/test/library-tests/pathcreation/PathCreation.java
Normal file
71
java/ql/test/library-tests/pathcreation/PathCreation.java
Normal file
@@ -0,0 +1,71 @@
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.net.URI;
|
||||
|
||||
class PathCreation {
|
||||
public void testNewFileWithString() {
|
||||
File f = new File("dir");
|
||||
File f2 = new File("dir", "sub");
|
||||
}
|
||||
|
||||
public void testNewFileWithFileString() {
|
||||
File f = new File(new File("dir"), "sub");
|
||||
}
|
||||
|
||||
public void testNewFileWithURI() {
|
||||
File f = new File(new URI("dir"));
|
||||
}
|
||||
|
||||
public void testPathOfWithString() {
|
||||
Path p = Path.of("dir");
|
||||
Path p2 = Path.of("dir", "sub");
|
||||
}
|
||||
|
||||
public void testPathOfWithURI() {
|
||||
Path p = Path.of(new URI("dir"));
|
||||
}
|
||||
|
||||
public void testPathsGetWithString() {
|
||||
Path p = Paths.get("dir");
|
||||
Path p2 = Paths.get("dir", "sub");
|
||||
}
|
||||
|
||||
public void testPathsGetWithURI() {
|
||||
Path p = Paths.get(new URI("dir"));
|
||||
}
|
||||
|
||||
public void testFileSystemGetPathWithString() {
|
||||
Path p = FileSystems.getDefault().getPath("dir");
|
||||
Path p2 = FileSystems.getDefault().getPath("dir", "sub");
|
||||
}
|
||||
|
||||
public void testPathResolveSiblingWithString() {
|
||||
Path p = Path.of("dir").resolveSibling("sub");
|
||||
}
|
||||
|
||||
public void testPathResolveWithString() {
|
||||
Path p = Path.of("dir").resolve("sub");
|
||||
}
|
||||
|
||||
public void testNewFileWriterWithString() {
|
||||
FileWriter fw = new FileWriter("dir");
|
||||
}
|
||||
|
||||
public void testNewFileReaderWithString() {
|
||||
FileReader fr = new FileReader("dir");
|
||||
}
|
||||
|
||||
public void testNewFileOutputStreamWithString() {
|
||||
FileOutputStream fos = new FileOutputStream("dir");
|
||||
}
|
||||
|
||||
public void testNewFileInputStreamWithString() {
|
||||
FileInputStream fis = new FileInputStream("dir");
|
||||
}
|
||||
}
|
||||
5
java/ql/test/library-tests/pathcreation/PathCreation.ql
Normal file
5
java/ql/test/library-tests/pathcreation/PathCreation.ql
Normal file
@@ -0,0 +1,5 @@
|
||||
import java
|
||||
import semmle.code.java.security.PathCreation
|
||||
|
||||
from PathCreation path
|
||||
select path, path.getAnInput()
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/Header.java $
|
||||
* $Revision: 569636 $
|
||||
* $Date: 2007-08-25 00:34:47 -0700 (Sat, 25 Aug 2007) $
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.http;
|
||||
|
||||
/**
|
||||
* Represents an HTTP header field.
|
||||
*
|
||||
* <p>
|
||||
* The HTTP header fields follow the same generic format as that given in
|
||||
* Section 3.1 of RFC 822. Each header field consists of a name followed by a
|
||||
* colon (":") and the field value. Field names are case-insensitive. The field
|
||||
* value MAY be preceded by any amount of LWS, though a single SP is preferred.
|
||||
*
|
||||
* <pre>
|
||||
* message-header = field-name ":" [ field-value ]
|
||||
* field-name = token
|
||||
* field-value = *( field-content | LWS )
|
||||
* field-content = <the OCTETs making up the field-value
|
||||
* and consisting of either *TEXT or combinations
|
||||
* of token, separators, and quoted-string>
|
||||
* </pre>
|
||||
*
|
||||
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
|
||||
* @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
|
||||
* @version $Revision: 569636 $
|
||||
*
|
||||
* @deprecated Please use {@link java.net.URL#openConnection} instead. Please
|
||||
* visit <a href=
|
||||
* "http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this
|
||||
* webpage</a> for further details.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface Header {
|
||||
|
||||
String getName();
|
||||
|
||||
String getValue();
|
||||
|
||||
HeaderElement[] getElements() throws ParseException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HeaderElement.java $
|
||||
* $Revision: 569828 $
|
||||
* $Date: 2007-08-26 08:49:38 -0700 (Sun, 26 Aug 2007) $
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.http;
|
||||
|
||||
/**
|
||||
* One element of an HTTP {@link Header header} value.
|
||||
*
|
||||
* @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
|
||||
*
|
||||
*
|
||||
* <!-- empty lines above to avoid 'svn diff' context problems -->
|
||||
* @version $Revision: 569828 $ $Date: 2007-08-26 08:49:38 -0700 (Sun, 26 Aug
|
||||
* 2007) $
|
||||
*
|
||||
* @since 4.0
|
||||
*
|
||||
* @deprecated Please use {@link java.net.URL#openConnection} instead. Please
|
||||
* visit <a href=
|
||||
* "http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this
|
||||
* webpage</a> for further details.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface HeaderElement {
|
||||
|
||||
String getName();
|
||||
|
||||
String getValue();
|
||||
|
||||
NameValuePair[] getParameters();
|
||||
|
||||
NameValuePair getParameterByName(String name);
|
||||
|
||||
int getParameterCount();
|
||||
|
||||
NameValuePair getParameter(int index);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user