mirror of
https://github.com/github/codeql.git
synced 2026-07-07 04:25:30 +02:00
Compare commits
195 Commits
igfoo/unde
...
experiment
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb3d0f5b0e | ||
|
|
09cf8e8b01 | ||
|
|
bd8212c090 | ||
|
|
f106d186e4 | ||
|
|
e2c84407b4 | ||
|
|
67b15125c7 | ||
|
|
caf763a969 | ||
|
|
4f8f5048f3 | ||
|
|
2366679d9b | ||
|
|
66399c055e | ||
|
|
85c02a430e | ||
|
|
29945b8ed0 | ||
|
|
a8ef1bc32a | ||
|
|
0781a138af | ||
|
|
6fd67c4d8e | ||
|
|
89747ecf83 | ||
|
|
c013e3f9c3 | ||
|
|
3b14b27635 | ||
|
|
2ae32be934 | ||
|
|
6647f6b9c4 | ||
|
|
41ceb291de | ||
|
|
615418d2e3 | ||
|
|
0ba76f7d0e | ||
|
|
d97a10ef8a | ||
|
|
2d618d6b92 | ||
|
|
e1d0bbb021 | ||
|
|
6fd4a8afff | ||
|
|
be2fe6e171 | ||
|
|
8d2768b2ce | ||
|
|
701e815368 | ||
|
|
cd310eb9d5 | ||
|
|
992a4df12f | ||
|
|
996cda9b97 | ||
|
|
80d5b17900 | ||
|
|
cae0060a89 | ||
|
|
46197e6e69 | ||
|
|
595bdedb22 | ||
|
|
652e8b4872 | ||
|
|
c9c4c067b6 | ||
|
|
a335bb0115 | ||
|
|
ad267404c9 | ||
|
|
d7f0b9a7fa | ||
|
|
749db379ca | ||
|
|
dbb3d3dc17 | ||
|
|
8adaee05b6 | ||
|
|
6109ef5e88 | ||
|
|
7d300b53d7 | ||
|
|
d42a01cb3a | ||
|
|
e5160929eb | ||
|
|
30ba69d991 | ||
|
|
036e181bc1 | ||
|
|
716568ebd1 | ||
|
|
9820116734 | ||
|
|
52a2260dc7 | ||
|
|
c738f387b1 | ||
|
|
cf5f760ecd | ||
|
|
a790eb8110 | ||
|
|
a8cbdc92b9 | ||
|
|
551a7ce9e5 | ||
|
|
c069c3384e | ||
|
|
cb9a9db356 | ||
|
|
2ac1e60406 | ||
|
|
51bab81f56 | ||
|
|
99dd5330c2 | ||
|
|
a9527fd913 | ||
|
|
2faf52b6bd | ||
|
|
4cf0b8e725 | ||
|
|
f372274857 | ||
|
|
2373bf2dfb | ||
|
|
1cf30d2a9e | ||
|
|
ab58cb3d44 | ||
|
|
f0491af64c | ||
|
|
0c724a8427 | ||
|
|
03b12dbc6d | ||
|
|
365b4d722d | ||
|
|
903f364dab | ||
|
|
073a43ce74 | ||
|
|
461d4e45af | ||
|
|
c9f54ea1ad | ||
|
|
ee13ff71d6 | ||
|
|
26cddc7d04 | ||
|
|
69973d0fa2 | ||
|
|
a66083d685 | ||
|
|
fd4e8f8282 | ||
|
|
61880ba90a | ||
|
|
e22ec50dee | ||
|
|
2d615ef503 | ||
|
|
ffcb345916 | ||
|
|
9a41c80626 | ||
|
|
695b02a94c | ||
|
|
2c1cc9ead6 | ||
|
|
f45916efda | ||
|
|
8382e85901 | ||
|
|
f07d844362 | ||
|
|
98001c494f | ||
|
|
41b89669a9 | ||
|
|
bc49bc7095 | ||
|
|
e0e58b24ea | ||
|
|
224d3790b5 | ||
|
|
b11703cc74 | ||
|
|
5eb1f8abbd | ||
|
|
0ebb24ebeb | ||
|
|
667b26b5d9 | ||
|
|
a5f4d43d61 | ||
|
|
7045597139 | ||
|
|
c194598d37 | ||
|
|
e852540254 | ||
|
|
c777f1d8d7 | ||
|
|
a23d8deb10 | ||
|
|
32500c834d | ||
|
|
acc28df785 | ||
|
|
564a6873f8 | ||
|
|
c4ab6fb7b4 | ||
|
|
f07030ba97 | ||
|
|
a9566728b5 | ||
|
|
7119eda009 | ||
|
|
86bc0eb853 | ||
|
|
b43989e6a1 | ||
|
|
2850b8e952 | ||
|
|
cbfa5ad303 | ||
|
|
cee1a12489 | ||
|
|
c926a47d50 | ||
|
|
cca38a64be | ||
|
|
53def60e4f | ||
|
|
1ce7c3448f | ||
|
|
fd7cbd0c96 | ||
|
|
8fa3fb0561 | ||
|
|
314839fc09 | ||
|
|
c1651ad30c | ||
|
|
125d1465c8 | ||
|
|
a3421e7ab2 | ||
|
|
20416ae034 | ||
|
|
c96ee8671e | ||
|
|
a1fab8ac52 | ||
|
|
36bdee0e8b | ||
|
|
95ac2c8edd | ||
|
|
f08a0e5653 | ||
|
|
ab3edf37d7 | ||
|
|
43306f4700 | ||
|
|
8c8e4e6a70 | ||
|
|
068a9d88e7 | ||
|
|
c541390c1b | ||
|
|
e9e93c0eea | ||
|
|
85ecfe2723 | ||
|
|
49d1937dc4 | ||
|
|
d4877a9038 | ||
|
|
57784dc746 | ||
|
|
4f9b6d1192 | ||
|
|
bc5b477f79 | ||
|
|
0cc8eaf3b4 | ||
|
|
51c27de049 | ||
|
|
b9788eb53c | ||
|
|
649286995a | ||
|
|
fb004bacc3 | ||
|
|
67835ee273 | ||
|
|
23df459c16 | ||
|
|
fe76b0849b | ||
|
|
92839123ae | ||
|
|
a8284d5b97 | ||
|
|
244966e216 | ||
|
|
62de15cd22 | ||
|
|
f8bbda0cdc | ||
|
|
9db235ac36 | ||
|
|
35f294f096 | ||
|
|
93500bd95a | ||
|
|
95937c9ac7 | ||
|
|
0b21b273ed | ||
|
|
937a620f4d | ||
|
|
3db5dd4661 | ||
|
|
92e0e195a4 | ||
|
|
c6e7b8d4fd | ||
|
|
4100d68a71 | ||
|
|
725122decc | ||
|
|
b466f0515d | ||
|
|
0ce08617ba | ||
|
|
e345064a53 | ||
|
|
d20a0c9e82 | ||
|
|
5e59f6d558 | ||
|
|
3d49b8cb91 | ||
|
|
3b82452d76 | ||
|
|
a9af135d7e | ||
|
|
993999f64f | ||
|
|
7a0bfd1a69 | ||
|
|
f800bf243f | ||
|
|
1534b387bb | ||
|
|
405c1f3fc7 | ||
|
|
fa2ae1420a | ||
|
|
347cbe422d | ||
|
|
0c0556bb38 | ||
|
|
6ca425f033 | ||
|
|
79d6731ed8 | ||
|
|
b3ff3f7ee7 | ||
|
|
8f467003d2 | ||
|
|
63b732ce1f | ||
|
|
4d856d4461 |
@@ -70,4 +70,3 @@
|
||||
|
||||
## Changes to libraries
|
||||
* The predicate `TypeAnnotation.hasQualifiedName` now works in more cases when the imported library was not present during extraction.
|
||||
* The class `DomBasedXss::Configuration` has been deprecated, as it has been split into `DomBasedXss::HtmlInjectionConfiguration` and `DomBasedXss::JQueryHtmlOrSelectorInjectionConfiguration`. Unless specifically working with jQuery sinks, subclasses should instead be based on `HtmlInjectionConfiguration`. To use both configurations in a query, see [Xss.ql](https://github.com/github/codeql/blob/main/javascript/ql/src/Security/CWE-079/Xss.ql) for an example.
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* The 'Assignment where comparison was intended' (cpp/assign-where-compare-meant) query has been improved to flag fewer benign assignments in conditionals.
|
||||
@@ -54,7 +54,7 @@ class BooleanControllingAssignmentInExpr extends BooleanControllingAssignment {
|
||||
override predicate isWhitelisted() {
|
||||
this.getConversion().(ParenthesisExpr).isParenthesised()
|
||||
or
|
||||
// whitelist this assignment if all comparison operations in the expression that this
|
||||
// Allow this assignment if all comparison operations in the expression that this
|
||||
// assignment is part of, are not parenthesized. In that case it seems like programmer
|
||||
// is fine with unparenthesized comparison operands to binary logical operators, and
|
||||
// the parenthesis around this assignment was used to call it out as an assignment.
|
||||
@@ -62,6 +62,21 @@ class BooleanControllingAssignmentInExpr extends BooleanControllingAssignment {
|
||||
forex(ComparisonOperation op | op = getComparisonOperand*(this.getParent+()) |
|
||||
not op.isParenthesised()
|
||||
)
|
||||
or
|
||||
// Match a pattern like:
|
||||
// ```
|
||||
// if((a = b) && use_value(a)) { ... }
|
||||
// ```
|
||||
// where the assignment is meant to update the value of `a` before it's used in some other boolean
|
||||
// subexpression that is guarenteed to be evaluate _after_ the assignment.
|
||||
this.isParenthesised() and
|
||||
exists(LogicalAndExpr parent, Variable var, VariableAccess access |
|
||||
var = this.getLValue().(VariableAccess).getTarget() and
|
||||
access = var.getAnAccess() and
|
||||
not access.isUsedAsLValue() and
|
||||
parent.getRightOperand() = access.getParent*() and
|
||||
parent.getLeftOperand() = this.getParent*()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ class QuotedCommandInCreateProcessFunctionConfiguration extends DataFlow2::Confi
|
||||
|
||||
bindingset[s]
|
||||
predicate isQuotedOrNoSpaceApplicationNameOnCmd(string s) {
|
||||
s.regexpMatch("\"([^\"])*\"(\\s|.)*") // The first element (path) is quoted
|
||||
s.regexpMatch("\"([^\"])*\"[\\s\\S]*") // The first element (path) is quoted
|
||||
or
|
||||
s.regexpMatch("[^\\s]+") // There are no spaces in the string
|
||||
}
|
||||
|
||||
@@ -72,9 +72,9 @@ class WrongCheckErrorOperatorNew extends FunctionCall {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(std::nothrow)` exists in call `operator new`.
|
||||
* Holds if `(std::nothrow)` or `(std::noexcept)` exists in call `operator new`.
|
||||
*/
|
||||
predicate isExistsNothrow() { this.getAChild().toString() = "nothrow" }
|
||||
predicate isExistsNothrow() { getTarget().isNoExcept() or getTarget().isNoThrow() }
|
||||
}
|
||||
|
||||
from WrongCheckErrorOperatorNew op
|
||||
|
||||
@@ -72,6 +72,7 @@ class Location extends @location {
|
||||
}
|
||||
|
||||
/** Holds if `this` comes on a line strictly before `l`. */
|
||||
pragma[inline]
|
||||
predicate isBefore(Location l) {
|
||||
this.getFile() = l.getFile() and this.getEndLine() < l.getStartLine()
|
||||
}
|
||||
|
||||
@@ -300,6 +300,14 @@ class FunctionCall extends Call, @funbindexpr {
|
||||
}
|
||||
}
|
||||
|
||||
/** A _user-defined_ unary `operator*` function. */
|
||||
class OverloadedPointerDereferenceFunction extends Function {
|
||||
OverloadedPointerDereferenceFunction() {
|
||||
this.hasName("operator*") and
|
||||
this.getEffectiveNumberOfParameters() = 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance of a _user-defined_ unary `operator*` applied to its argument.
|
||||
* ```
|
||||
@@ -309,8 +317,7 @@ class FunctionCall extends Call, @funbindexpr {
|
||||
*/
|
||||
class OverloadedPointerDereferenceExpr extends FunctionCall {
|
||||
OverloadedPointerDereferenceExpr() {
|
||||
getTarget().hasName("operator*") and
|
||||
getTarget().getEffectiveNumberOfParameters() = 1
|
||||
this.getTarget() instanceof OverloadedPointerDereferenceFunction
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "OverloadedPointerDereferenceExpr" }
|
||||
|
||||
@@ -1,10 +1,35 @@
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.PointerWrapper
|
||||
|
||||
/**
|
||||
* The `std::shared_ptr` and `std::unique_ptr` template classes.
|
||||
*/
|
||||
private class UniqueOrSharedPtr extends Class {
|
||||
private class UniqueOrSharedPtr extends Class, PointerWrapper {
|
||||
UniqueOrSharedPtr() { this.hasQualifiedName(["std", "bsl"], ["shared_ptr", "unique_ptr"]) }
|
||||
|
||||
override MemberFunction getAnUnwrapperFunction() {
|
||||
result.(OverloadedPointerDereferenceFunction).getDeclaringType() = this
|
||||
or
|
||||
result.getClassAndName(["operator->", "get"]) = this
|
||||
}
|
||||
}
|
||||
|
||||
/** Any function that unwraps a pointer wrapper class to reveal the underlying pointer. */
|
||||
private class PointerWrapperDataFlow extends DataFlowFunction {
|
||||
PointerWrapperDataFlow() {
|
||||
this = any(PointerWrapper wrapper).getAnUnwrapperFunction() and
|
||||
not this.getUnspecifiedType() instanceof ReferenceType
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierAddress() and output.isReturnValue()
|
||||
or
|
||||
input.isQualifierObject() and output.isReturnValueDeref()
|
||||
or
|
||||
input.isReturnValueDeref() and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/** Provides classes for modeling pointer wrapper types and expressions. */
|
||||
|
||||
private import cpp
|
||||
|
||||
/** A class that wraps a pointer type. For example, `std::unique_ptr` and `std::shared_ptr`. */
|
||||
abstract class PointerWrapper extends Class {
|
||||
/**
|
||||
* Gets a member function of this class that returns the wrapped pointer, if any.
|
||||
*
|
||||
* This includes both functions that return the wrapped pointer by value, and functions
|
||||
* that return a reference to the pointed-to object.
|
||||
*/
|
||||
abstract MemberFunction getAnUnwrapperFunction();
|
||||
}
|
||||
@@ -14,8 +14,8 @@ using namespace std;
|
||||
|
||||
void* operator new(std::size_t _Size);
|
||||
void* operator new[](std::size_t _Size);
|
||||
void* operator new( std::size_t count, const std::nothrow_t& tag );
|
||||
void* operator new[]( std::size_t count, const std::nothrow_t& tag );
|
||||
void* operator new( std::size_t count, const std::nothrow_t& tag ) noexcept;
|
||||
void* operator new[]( std::size_t count, const std::nothrow_t& tag ) noexcept;
|
||||
|
||||
void badNew_0_0()
|
||||
{
|
||||
|
||||
@@ -3260,15 +3260,59 @@
|
||||
| smart_pointer.cpp:47:11:47:11 | p | smart_pointer.cpp:47:10:47:10 | call to operator* | TAINT |
|
||||
| smart_pointer.cpp:51:30:51:50 | call to make_shared | smart_pointer.cpp:52:10:52:10 | p | |
|
||||
| smart_pointer.cpp:51:52:51:57 | call to source | smart_pointer.cpp:51:30:51:50 | call to make_shared | TAINT |
|
||||
| smart_pointer.cpp:52:10:52:10 | p | smart_pointer.cpp:52:12:52:14 | call to get | TAINT |
|
||||
| smart_pointer.cpp:52:10:52:10 | p | smart_pointer.cpp:52:12:52:14 | call to get | |
|
||||
| smart_pointer.cpp:52:12:52:14 | ref arg call to get | smart_pointer.cpp:52:10:52:10 | ref arg p | |
|
||||
| smart_pointer.cpp:56:30:56:50 | call to make_unique | smart_pointer.cpp:57:10:57:10 | p | |
|
||||
| smart_pointer.cpp:56:52:56:57 | call to source | smart_pointer.cpp:56:30:56:50 | call to make_unique | TAINT |
|
||||
| smart_pointer.cpp:57:10:57:10 | p | smart_pointer.cpp:57:12:57:14 | call to get | TAINT |
|
||||
| smart_pointer.cpp:57:10:57:10 | p | smart_pointer.cpp:57:12:57:14 | call to get | |
|
||||
| smart_pointer.cpp:57:12:57:14 | ref arg call to get | smart_pointer.cpp:57:10:57:10 | ref arg p | |
|
||||
| smart_pointer.cpp:65:28:65:46 | call to make_unique | smart_pointer.cpp:66:10:66:10 | p | |
|
||||
| smart_pointer.cpp:65:28:65:46 | call to make_unique | smart_pointer.cpp:67:10:67:10 | p | |
|
||||
| smart_pointer.cpp:65:48:65:53 | call to source | smart_pointer.cpp:65:28:65:46 | call to make_unique | TAINT |
|
||||
| smart_pointer.cpp:65:58:65:58 | 0 | smart_pointer.cpp:65:28:65:46 | call to make_unique | TAINT |
|
||||
| smart_pointer.cpp:66:10:66:10 | p | smart_pointer.cpp:66:11:66:11 | call to operator-> | |
|
||||
| smart_pointer.cpp:66:10:66:10 | ref arg p | smart_pointer.cpp:67:10:67:10 | p | |
|
||||
| smart_pointer.cpp:67:10:67:10 | p | smart_pointer.cpp:67:11:67:11 | call to operator-> | |
|
||||
| smart_pointer.cpp:76:45:76:45 | p | smart_pointer.cpp:77:3:77:3 | p | |
|
||||
| smart_pointer.cpp:76:45:76:45 | p | smart_pointer.cpp:78:8:78:8 | p | |
|
||||
| smart_pointer.cpp:76:45:76:45 | p | smart_pointer.cpp:79:8:79:8 | p | |
|
||||
| smart_pointer.cpp:76:67:76:67 | q | smart_pointer.cpp:81:3:81:3 | q | |
|
||||
| smart_pointer.cpp:76:67:76:67 | q | smart_pointer.cpp:82:8:82:8 | q | |
|
||||
| smart_pointer.cpp:76:67:76:67 | q | smart_pointer.cpp:83:8:83:8 | q | |
|
||||
| smart_pointer.cpp:76:67:76:67 | q | smart_pointer.cpp:84:8:84:8 | q | |
|
||||
| smart_pointer.cpp:77:3:77:3 | p | smart_pointer.cpp:77:4:77:4 | call to operator-> | |
|
||||
| smart_pointer.cpp:77:3:77:3 | ref arg p | smart_pointer.cpp:78:8:78:8 | p | |
|
||||
| smart_pointer.cpp:77:3:77:3 | ref arg p | smart_pointer.cpp:79:8:79:8 | p | |
|
||||
| smart_pointer.cpp:77:3:77:17 | ... = ... | smart_pointer.cpp:77:6:77:6 | x [post update] | |
|
||||
| smart_pointer.cpp:77:3:77:17 | ... = ... | smart_pointer.cpp:78:11:78:11 | x | |
|
||||
| smart_pointer.cpp:77:4:77:4 | call to operator-> [post update] | smart_pointer.cpp:77:3:77:3 | ref arg p | |
|
||||
| smart_pointer.cpp:77:10:77:15 | call to source | smart_pointer.cpp:77:3:77:17 | ... = ... | |
|
||||
| smart_pointer.cpp:78:8:78:8 | p | smart_pointer.cpp:78:9:78:9 | call to operator-> | |
|
||||
| smart_pointer.cpp:78:8:78:8 | ref arg p | smart_pointer.cpp:79:8:79:8 | p | |
|
||||
| smart_pointer.cpp:79:8:79:8 | p | smart_pointer.cpp:79:9:79:9 | call to operator-> | |
|
||||
| smart_pointer.cpp:81:3:81:3 | q | smart_pointer.cpp:81:4:81:4 | call to operator-> | |
|
||||
| smart_pointer.cpp:81:3:81:3 | ref arg q | smart_pointer.cpp:82:8:82:8 | q | |
|
||||
| smart_pointer.cpp:81:3:81:3 | ref arg q | smart_pointer.cpp:83:8:83:8 | q | |
|
||||
| smart_pointer.cpp:81:3:81:3 | ref arg q | smart_pointer.cpp:84:8:84:8 | q | |
|
||||
| smart_pointer.cpp:81:3:81:20 | ... = ... | smart_pointer.cpp:81:9:81:9 | x [post update] | |
|
||||
| smart_pointer.cpp:81:3:81:20 | ... = ... | smart_pointer.cpp:82:14:82:14 | x | |
|
||||
| smart_pointer.cpp:81:4:81:4 | call to operator-> [post update] | smart_pointer.cpp:81:3:81:3 | ref arg q | |
|
||||
| smart_pointer.cpp:81:13:81:18 | call to source | smart_pointer.cpp:81:3:81:20 | ... = ... | |
|
||||
| smart_pointer.cpp:82:8:82:8 | q | smart_pointer.cpp:82:9:82:9 | call to operator-> | |
|
||||
| smart_pointer.cpp:82:8:82:8 | ref arg q | smart_pointer.cpp:83:8:83:8 | q | |
|
||||
| smart_pointer.cpp:82:8:82:8 | ref arg q | smart_pointer.cpp:84:8:84:8 | q | |
|
||||
| smart_pointer.cpp:83:8:83:8 | q | smart_pointer.cpp:83:9:83:9 | call to operator-> | |
|
||||
| smart_pointer.cpp:83:8:83:8 | ref arg q | smart_pointer.cpp:84:8:84:8 | q | |
|
||||
| smart_pointer.cpp:84:8:84:8 | q | smart_pointer.cpp:84:9:84:9 | call to operator-> | |
|
||||
| smart_pointer.cpp:87:17:87:18 | pa | smart_pointer.cpp:88:5:88:6 | pa | |
|
||||
| smart_pointer.cpp:88:5:88:20 | ... = ... | smart_pointer.cpp:88:9:88:9 | x [post update] | |
|
||||
| smart_pointer.cpp:88:13:88:18 | call to source | smart_pointer.cpp:88:5:88:20 | ... = ... | |
|
||||
| smart_pointer.cpp:92:25:92:50 | call to unique_ptr | smart_pointer.cpp:93:11:93:11 | p | |
|
||||
| smart_pointer.cpp:92:25:92:50 | call to unique_ptr | smart_pointer.cpp:94:8:94:8 | p | |
|
||||
| smart_pointer.cpp:93:11:93:11 | p | smart_pointer.cpp:93:13:93:15 | call to get | |
|
||||
| smart_pointer.cpp:93:11:93:11 | ref arg p | smart_pointer.cpp:94:8:94:8 | p | |
|
||||
| smart_pointer.cpp:93:13:93:15 | ref arg call to get | smart_pointer.cpp:93:11:93:11 | ref arg p | |
|
||||
| smart_pointer.cpp:94:8:94:8 | p | smart_pointer.cpp:94:9:94:9 | call to operator-> | |
|
||||
| standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:39:45:39:51 | source1 | |
|
||||
| standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:40:11:40:17 | source1 | |
|
||||
| standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:41:12:41:18 | source1 | |
|
||||
@@ -5213,6 +5257,7 @@
|
||||
| swap1.cpp:14:17:14:17 | t | swap1.cpp:14:17:14:17 | t | |
|
||||
| swap1.cpp:14:17:14:17 | t | swap1.cpp:14:17:14:17 | t | |
|
||||
| swap1.cpp:14:17:14:17 | t | swap1.cpp:14:56:14:56 | t | |
|
||||
| swap1.cpp:14:17:14:17 | t | swap1.cpp:14:56:14:56 | t | |
|
||||
| swap1.cpp:24:9:24:13 | this | swap1.cpp:24:31:24:34 | this | |
|
||||
| swap1.cpp:24:23:24:26 | that | swap1.cpp:24:23:24:26 | that | |
|
||||
| swap1.cpp:24:23:24:26 | that | swap1.cpp:24:36:24:39 | that | |
|
||||
@@ -5378,6 +5423,7 @@
|
||||
| swap2.cpp:14:17:14:17 | t | swap2.cpp:14:17:14:17 | t | |
|
||||
| swap2.cpp:14:17:14:17 | t | swap2.cpp:14:17:14:17 | t | |
|
||||
| swap2.cpp:14:17:14:17 | t | swap2.cpp:14:56:14:56 | t | |
|
||||
| swap2.cpp:14:17:14:17 | t | swap2.cpp:14:56:14:56 | t | |
|
||||
| swap2.cpp:24:9:24:13 | this | swap2.cpp:24:31:24:34 | this | |
|
||||
| swap2.cpp:24:23:24:26 | that | swap2.cpp:24:23:24:26 | that | |
|
||||
| swap2.cpp:24:23:24:26 | that | swap2.cpp:24:36:24:39 | that | |
|
||||
|
||||
@@ -65,4 +65,31 @@ void test_shared_field_member() {
|
||||
std::unique_ptr<A> p = std::make_unique<A>(source(), 0);
|
||||
sink(p->x); // $ MISSING: ast,ir
|
||||
sink(p->y); // not tainted
|
||||
}
|
||||
|
||||
struct B {
|
||||
A a1;
|
||||
A a2;
|
||||
int z;
|
||||
};
|
||||
|
||||
void test_operator_arrow(std::unique_ptr<A> p, std::unique_ptr<B> q) {
|
||||
p->x = source();
|
||||
sink(p->x); // $ ast MISSING: ir
|
||||
sink(p->y);
|
||||
|
||||
q->a1.x = source();
|
||||
sink(q->a1.x); // $ ast MISSING: ir
|
||||
sink(q->a1.y);
|
||||
sink(q->a2.x);
|
||||
}
|
||||
|
||||
void taint_x(A* pa) {
|
||||
pa->x = source();
|
||||
}
|
||||
|
||||
void reverse_taint_smart_pointer() {
|
||||
std::unique_ptr<A> p = std::unique_ptr<A>(new A);
|
||||
taint_x(p.get());
|
||||
sink(p->x); // $ ast MISSING: ir
|
||||
}
|
||||
@@ -10,3 +10,4 @@
|
||||
| test.cpp:89:18:89:23 | call to malloc | This memory is never freed |
|
||||
| test.cpp:156:3:156:26 | new | This memory is never freed |
|
||||
| test.cpp:157:3:157:26 | new[] | This memory is never freed |
|
||||
| test.cpp:167:14:167:19 | call to strdup | This memory is never freed |
|
||||
|
||||
@@ -156,3 +156,15 @@ int overloadedNew() {
|
||||
new(std::nothrow) int(3); // BAD
|
||||
new(std::nothrow) int[2]; // BAD
|
||||
}
|
||||
|
||||
// --- strdup ---
|
||||
|
||||
char *strdup(const char *s1);
|
||||
void output_msg(const char *msg);
|
||||
|
||||
void test_strdup() {
|
||||
char msg[] = "OctoCat";
|
||||
char *cpy = strdup(msg); // BAD
|
||||
|
||||
output_msg(cpy);
|
||||
}
|
||||
|
||||
@@ -19,3 +19,7 @@
|
||||
| test.cpp:144:32:144:36 | ... = ... | Use of '=' where '==' may have been intended. |
|
||||
| test.cpp:150:32:150:36 | ... = ... | Use of '=' where '==' may have been intended. |
|
||||
| test.cpp:153:46:153:50 | ... = ... | Use of '=' where '==' may have been intended. |
|
||||
| test.cpp:166:22:166:27 | ... = ... | Use of '=' where '==' may have been intended. |
|
||||
| test.cpp:168:24:168:29 | ... = ... | Use of '=' where '==' may have been intended. |
|
||||
| test.cpp:169:23:169:28 | ... = ... | Use of '=' where '==' may have been intended. |
|
||||
| test.cpp:171:7:171:12 | ... = ... | Use of '=' where '==' may have been intended. |
|
||||
|
||||
@@ -153,3 +153,21 @@ void f3(int x, int y) {
|
||||
if((x == 10) || ((z == z) && (x == 1)) && (y = 2)) { // BAD
|
||||
}
|
||||
}
|
||||
|
||||
bool use(int);
|
||||
|
||||
void f4(int x, bool b) {
|
||||
if((x = 10) && use(x)) {} // GOOD: This is likely just a short-hand way of writing an assignment
|
||||
// followed by a boolean check.
|
||||
if((x = 10) && b && use(x)) {} // GOOD: Same reason as above
|
||||
if((x = 10) && use(x) && b) {} // GOOD: Same reason as above
|
||||
if((x = 10) && (use(x) && b)) {} // GOOD: Same reason as above
|
||||
|
||||
if(use(x) && b && (x = 10)) {} // BAD: The assignment is the last thing that happens in the comparison.
|
||||
// This doesn't match the usual pattern.
|
||||
if((use(x) && b) && (x = 10)) {} // BAD: Same reason as above
|
||||
if(use(x) && (b && (x = 10))) {} // BAD: Same reason as above
|
||||
|
||||
if((x = 10) || use(x)) {} // BAD: This doesn't follow the usual style of writing an assignment in
|
||||
// a boolean check.
|
||||
}
|
||||
|
||||
@@ -7,3 +7,4 @@
|
||||
| test.cpp:303:11:303:18 | call to try_lock | This lock might not be unlocked or might be locked more times than it is unlocked. |
|
||||
| test.cpp:313:11:313:18 | call to try_lock | This lock might not be unlocked or might be locked more times than it is unlocked. |
|
||||
| test.cpp:442:8:442:17 | call to mutex_lock | This lock might not be unlocked or might be locked more times than it is unlocked. |
|
||||
| test.cpp:482:2:482:19 | call to pthread_mutex_lock | This lock might not be unlocked or might be locked more times than it is unlocked. |
|
||||
|
||||
@@ -445,3 +445,46 @@ bool test_mutex(data_t *data)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
struct pthread_mutex
|
||||
{
|
||||
// ...
|
||||
};
|
||||
|
||||
void pthread_mutex_lock(pthread_mutex *m);
|
||||
void pthread_mutex_unlock(pthread_mutex *m);
|
||||
|
||||
class MyClass
|
||||
{
|
||||
public:
|
||||
pthread_mutex lock;
|
||||
};
|
||||
|
||||
bool maybe();
|
||||
|
||||
int test_MyClass_good(MyClass *obj)
|
||||
{
|
||||
pthread_mutex_lock(&obj->lock);
|
||||
|
||||
if (maybe()) {
|
||||
pthread_mutex_unlock(&obj->lock);
|
||||
return -1; // GOOD
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&obj->lock); // GOOD
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_MyClass_bad(MyClass *obj)
|
||||
{
|
||||
pthread_mutex_lock(&obj->lock);
|
||||
|
||||
if (maybe()) {
|
||||
return -1; // BAD
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&obj->lock); // GOOD
|
||||
return 0;
|
||||
}
|
||||
|
||||
2
csharp/change-notes/2021-04-09-dapper-support.md
Normal file
2
csharp/change-notes/2021-04-09-dapper-support.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* Support for the Dapper ORM library has been added to the SQL injection checks.
|
||||
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* The extractor has been improved to store default argument values for parameters that are extracted from referenced assemblies.
|
||||
@@ -164,6 +164,39 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a generated expression for a default argument value.
|
||||
/// </summary>
|
||||
public static Expression? CreateGenerated(Context cx, IParameterSymbol parameter, IExpressionParentEntity parent,
|
||||
int childIndex, Extraction.Entities.Location location)
|
||||
{
|
||||
if (!parameter.HasExplicitDefaultValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var defaultValue = parameter.ExplicitDefaultValue;
|
||||
|
||||
if (parameter.Type is INamedTypeSymbol nt && nt.EnumUnderlyingType is not null)
|
||||
{
|
||||
// = (MyEnum)1, = MyEnum.Value1, = default(MyEnum), = new MyEnum()
|
||||
// we're generating a (MyEnum)value cast expression:
|
||||
defaultValue ??= 0;
|
||||
Action<Expression, int> createChild = (parent, index) => Literal.CreateGenerated(cx, parent, index, nt.EnumUnderlyingType, defaultValue, location);
|
||||
return Cast.CreateGenerated(cx, parent, childIndex, parameter.Type, defaultValue, createChild, location);
|
||||
}
|
||||
|
||||
if (defaultValue is null)
|
||||
{
|
||||
// = null, = default, = default(T), = new MyStruct()
|
||||
// we're generating a default expression:
|
||||
return Default.CreateGenerated(cx, parent, childIndex, location, parameter.Type.IsReferenceType ? ValueAsString(null) : null);
|
||||
}
|
||||
|
||||
// const literal:
|
||||
return Literal.CreateGenerated(cx, parent, childIndex, parameter.Type, defaultValue, location);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adapt the operator kind depending on whether it's a dynamic call or a user-operator call.
|
||||
/// </summary>
|
||||
|
||||
@@ -14,5 +14,20 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
|
||||
{
|
||||
TypeAccess.Create(Context, Syntax.Type, this, 0);
|
||||
}
|
||||
|
||||
public static Expression CreateGenerated(Context cx, IExpressionParentEntity parent, int childIndex, Extraction.Entities.Location location, string? value)
|
||||
{
|
||||
var info = new ExpressionInfo(
|
||||
cx,
|
||||
null,
|
||||
location,
|
||||
ExprKind.DEFAULT,
|
||||
parent,
|
||||
childIndex,
|
||||
true,
|
||||
value);
|
||||
|
||||
return new Expression(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Semmle.Extraction.Entities;
|
||||
using System.IO;
|
||||
using System;
|
||||
|
||||
namespace Semmle.Extraction.CSharp.Entities
|
||||
{
|
||||
@@ -124,6 +125,17 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
trapFile.param_location(this, Context.CreateLocation());
|
||||
}
|
||||
|
||||
if (Symbol.HasExplicitDefaultValue && Context.Defines(Symbol))
|
||||
{
|
||||
var defaultValueSyntax = GetDefaultValueFromSyntax(Symbol);
|
||||
|
||||
Action defaultValueExpressionCreation = defaultValueSyntax is not null
|
||||
? () => Expression.Create(Context, defaultValueSyntax.Value, this, 0)
|
||||
: () => Expression.CreateGenerated(Context, Symbol, this, 0, Location);
|
||||
|
||||
Context.PopulateLater(defaultValueExpressionCreation);
|
||||
}
|
||||
|
||||
if (!IsSourceDeclaration || !Symbol.FromSource())
|
||||
return;
|
||||
|
||||
@@ -139,36 +151,28 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
TypeMention.Create(Context, syntax.Type!, this, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Symbol.HasExplicitDefaultValue && Context.Defines(Symbol))
|
||||
private static EqualsValueClauseSyntax? GetDefaultValueFromSyntax(IParameterSymbol symbol)
|
||||
{
|
||||
// This is a slight bug in the dbscheme
|
||||
// We should really define param_default(param, string)
|
||||
// And use parameter child #0 to encode the default expression.
|
||||
var defaultValue = GetParameterDefaultValue(symbol);
|
||||
if (defaultValue is null)
|
||||
{
|
||||
// This is a slight bug in the dbscheme
|
||||
// We should really define param_default(param, string)
|
||||
// And use parameter child #0 to encode the default expression.
|
||||
var defaultValue = GetParameterDefaultValue(Symbol);
|
||||
if (defaultValue is null)
|
||||
// In case this parameter belongs to an accessor of an indexer, we need
|
||||
// to get the default value from the corresponding parameter belonging
|
||||
// to the indexer itself
|
||||
if (symbol.ContainingSymbol is IMethodSymbol method)
|
||||
{
|
||||
// In case this parameter belongs to an accessor of an indexer, we need
|
||||
// to get the default value from the corresponding parameter belonging
|
||||
// to the indexer itself
|
||||
var method = (IMethodSymbol)Symbol.ContainingSymbol;
|
||||
if (method is not null)
|
||||
{
|
||||
var i = method.Parameters.IndexOf(Symbol);
|
||||
var indexer = (IPropertySymbol?)method.AssociatedSymbol;
|
||||
if (indexer is not null)
|
||||
defaultValue = GetParameterDefaultValue(indexer.Parameters[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (defaultValue is not null)
|
||||
{
|
||||
Context.PopulateLater(() =>
|
||||
{
|
||||
Expression.Create(Context, defaultValue.Value, this, 0);
|
||||
});
|
||||
var i = method.Parameters.IndexOf(symbol);
|
||||
if (method.AssociatedSymbol is IPropertySymbol indexer)
|
||||
defaultValue = GetParameterDefaultValue(indexer.Parameters[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public override bool IsSourceDeclaration => Symbol.IsSourceDeclaration();
|
||||
|
||||
@@ -43,12 +43,6 @@ if not foundCS:
|
||||
print("Test directory does not contain .cs files. Please specify a working qltest directory.")
|
||||
exit(1)
|
||||
|
||||
cmd = ['odasa', 'selfTest']
|
||||
print('Running ' + ' '.join(cmd))
|
||||
if subprocess.check_call(cmd):
|
||||
print("odasa selfTest failed. Ensure odasa is on your current path.")
|
||||
exit(1)
|
||||
|
||||
csharpQueries = os.path.abspath(os.path.dirname(sys.argv[0]))
|
||||
outputFile = os.path.join(testDir, 'stubs.cs')
|
||||
|
||||
@@ -58,56 +52,75 @@ if os.path.isfile(outputFile):
|
||||
os.remove(outputFile) # It would interfere with the test.
|
||||
print("Removed previous", outputFile)
|
||||
|
||||
cmd = ['odasa', 'qltest', '--optimize', '--leave-temp-files', testDir]
|
||||
cmd = ['codeql', 'test', 'run', '--keep-databases', testDir]
|
||||
print('Running ' + ' '.join(cmd))
|
||||
if subprocess.check_call(cmd):
|
||||
print("qltest failed. Please fix up the test before proceeding.")
|
||||
print("codeql test failed. Please fix up the test before proceeding.")
|
||||
exit(1)
|
||||
|
||||
dbDir = os.path.join(testDir, os.path.basename(testDir) + ".testproj", "db-csharp")
|
||||
dbDir = os.path.join(testDir, os.path.basename(testDir) + ".testproj")
|
||||
|
||||
if not os.path.isdir(dbDir):
|
||||
print("Expected database directory " + dbDir + " not found. Please contact Semmle.")
|
||||
print("Expected database directory " + dbDir + " not found.")
|
||||
exit(1)
|
||||
|
||||
cmd = ['odasa', 'runQuery', '--query', os.path.join(csharpQueries, 'MinimalStubsFromSource.ql'), '--db', dbDir, '--output-file', outputFile]
|
||||
cmd = ['codeql', 'query', 'run', os.path.join(
|
||||
csharpQueries, 'MinimalStubsFromSource.ql'), '--database', dbDir, '--output', outputFile]
|
||||
print('Running ' + ' '.join(cmd))
|
||||
if subprocess.check_call(cmd):
|
||||
print('Failed to run the query to generate output file. Please contact Semmle.')
|
||||
print('Failed to run the query to generate output file.')
|
||||
exit(1)
|
||||
|
||||
# Remove the leading " and trailing " bytes from the file
|
||||
len = os.stat(outputFile).st_size
|
||||
f = open(outputFile, "rb")
|
||||
try:
|
||||
quote = f.read(1)
|
||||
if quote != b'"':
|
||||
print("Unexpected character in file. Please contact Semmle.")
|
||||
contents = f.read(len-3)
|
||||
quote = f.read(1)
|
||||
if quote != b'"':
|
||||
print("Unexpected end character. Please contact Semmle.", quote)
|
||||
finally:
|
||||
f.close()
|
||||
# Remove the leading and trailing bytes from the file
|
||||
length = os.stat(outputFile).st_size
|
||||
if length < 20:
|
||||
contents = b''
|
||||
else:
|
||||
f = open(outputFile, "rb")
|
||||
try:
|
||||
countTillSlash = 0
|
||||
foundSlash = False
|
||||
slash = f.read(1)
|
||||
while slash != b'':
|
||||
if slash == b'/':
|
||||
foundSlash = True
|
||||
break
|
||||
countTillSlash += 1
|
||||
slash = f.read(1)
|
||||
|
||||
if not foundSlash:
|
||||
countTillSlash = 0
|
||||
|
||||
f.seek(0)
|
||||
quote = f.read(countTillSlash)
|
||||
print("Start characters in file skipped.", quote)
|
||||
post = b'\x0e\x01\x08#select\x01\x01\x00s\x00'
|
||||
contents = f.read(length - len(post) - countTillSlash)
|
||||
quote = f.read(len(post))
|
||||
if quote != post:
|
||||
print("Unexpected end character in file.", quote)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
f = open(outputFile, "wb")
|
||||
f.write(contents)
|
||||
f.close()
|
||||
|
||||
cmd = ['odasa', 'qltest', '--optimize', testDir]
|
||||
cmd = ['codeql', 'test', 'run', testDir]
|
||||
print('Running ' + ' '.join(cmd))
|
||||
if subprocess.check_call(cmd):
|
||||
print('\nTest failed. You may need to fix up', outputFile)
|
||||
print('It may help to view', outputFile, ' in Visual Studio')
|
||||
print("Next steps:")
|
||||
print('1. Look at the compilation errors, and fix up', outputFile, 'so that the test compiles')
|
||||
print('2. Re-run odasa qltest --optimize "' + testDir + '"')
|
||||
print('3. git add "' + outputFile + '"')
|
||||
exit(1)
|
||||
print('\nTest failed. You may need to fix up', outputFile)
|
||||
print('It may help to view', outputFile, ' in Visual Studio')
|
||||
print("Next steps:")
|
||||
print('1. Look at the compilation errors, and fix up',
|
||||
outputFile, 'so that the test compiles')
|
||||
print('2. Re-run codeql test run "' + testDir + '"')
|
||||
print('3. git add "' + outputFile + '"')
|
||||
exit(1)
|
||||
|
||||
print("\nStub generation successful! Next steps:")
|
||||
print('1. Edit "semmle-extractor-options" in the .cs files to remove unused references')
|
||||
print('2. Re-run odasa qltest --optimize "' + testDir + '"')
|
||||
print('2. Re-run codeql test run "' + testDir + '"')
|
||||
print('3. git add "' + outputFile + '"')
|
||||
print('4. Commit your changes.')
|
||||
|
||||
|
||||
@@ -2,5 +2,3 @@
|
||||
- qlpack: codeql-csharp
|
||||
- apply: code-scanning-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
- apply: codeql-suites/exclude-dependency-queries.yml
|
||||
from: codeql-csharp
|
||||
|
||||
@@ -2,5 +2,3 @@
|
||||
- qlpack: codeql-csharp
|
||||
- apply: security-and-quality-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
- apply: codeql-suites/exclude-dependency-queries.yml
|
||||
from: codeql-csharp
|
||||
|
||||
@@ -2,5 +2,3 @@
|
||||
- qlpack: codeql-csharp
|
||||
- apply: security-extended-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
- apply: codeql-suites/exclude-dependency-queries.yml
|
||||
from: codeql-csharp
|
||||
|
||||
@@ -321,10 +321,9 @@ private module SsaDefReaches {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private BasicBlock getAMaybeLiveSuccessor(Definition def, BasicBlock bb) {
|
||||
result = getABasicBlockSuccessor(bb) and
|
||||
not defOccursInBlock(_, bb, def.getSourceVariable()) and
|
||||
ssaDefReachesEndOfBlock(bb, def, _)
|
||||
private predicate ssaDefReachesThroughBlock(Definition def, BasicBlock bb) {
|
||||
ssaDefReachesEndOfBlock(bb, def, _) and
|
||||
not defOccursInBlock(_, bb, def.getSourceVariable())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -337,7 +336,11 @@ private module SsaDefReaches {
|
||||
defOccursInBlock(def, bb1, _) and
|
||||
bb2 = getABasicBlockSuccessor(bb1)
|
||||
or
|
||||
exists(BasicBlock mid | varBlockReaches(def, bb1, mid) | bb2 = getAMaybeLiveSuccessor(def, mid))
|
||||
exists(BasicBlock mid |
|
||||
varBlockReaches(def, bb1, mid) and
|
||||
ssaDefReachesThroughBlock(def, mid) and
|
||||
bb2 = getABasicBlockSuccessor(mid)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -355,17 +358,10 @@ private module SsaDefReaches {
|
||||
|
||||
private import SsaDefReaches
|
||||
|
||||
pragma[noinline]
|
||||
private predicate ssaDefReachesEndOfBlockRec(BasicBlock bb, Definition def, SourceVariable v) {
|
||||
exists(BasicBlock idom | ssaDefReachesEndOfBlock(idom, def, v) |
|
||||
// The construction of SSA form ensures that each read of a variable is
|
||||
// dominated by its definition. An SSA definition therefore reaches a
|
||||
// control flow node if it is the _closest_ SSA definition that dominates
|
||||
// the node. If two definitions dominate a node then one must dominate the
|
||||
// other, so therefore the definition of _closest_ is given by the dominator
|
||||
// tree. Thus, reaching definitions can be calculated in terms of dominance.
|
||||
idom = getImmediateBasicBlockDominator(bb)
|
||||
)
|
||||
pragma[nomagic]
|
||||
predicate liveThrough(BasicBlock bb, SourceVariable v) {
|
||||
liveAtExit(bb, v) and
|
||||
not ssaRef(bb, _, v, SsaDef())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -382,9 +378,14 @@ predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable
|
||||
liveAtExit(bb, v)
|
||||
)
|
||||
or
|
||||
ssaDefReachesEndOfBlockRec(bb, def, v) and
|
||||
liveAtExit(bb, v) and
|
||||
not ssaRef(bb, _, v, SsaDef())
|
||||
// The construction of SSA form ensures that each read of a variable is
|
||||
// dominated by its definition. An SSA definition therefore reaches a
|
||||
// control flow node if it is the _closest_ SSA definition that dominates
|
||||
// the node. If two definitions dominate a node then one must dominate the
|
||||
// other, so therefore the definition of _closest_ is given by the dominator
|
||||
// tree. Thus, reaching definitions can be calculated in terms of dominance.
|
||||
ssaDefReachesEndOfBlock(getImmediateBasicBlockDominator(bb), def, pragma[only_bind_into](v)) and
|
||||
liveThrough(bb, pragma[only_bind_into](v))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,9 +27,6 @@ class Element extends DotNet::Element, @element {
|
||||
/** Gets a location of this element, including sources and assemblies. */
|
||||
override Location getALocation() { none() }
|
||||
|
||||
/** Holds if this element is from an assembly. */
|
||||
predicate fromLibrary() { this.getFile().fromLibrary() }
|
||||
|
||||
/** Gets the parent of this element, if any. */
|
||||
Element getParent() { result.getAChild() = this }
|
||||
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Provides efficient cached predicates for finding enclosing statements and callables.
|
||||
*
|
||||
* There are a number of difficulties. There can be expressions without
|
||||
* enclosing statements (for example initialisers for fields and constructors)
|
||||
* or enclosing callables (even if we consider constructor initialisers
|
||||
* to be enclosed by constructors, field initialisers don't have callables).
|
||||
*
|
||||
* The only cases where a `Stmt` has an `Expr` parent are delegate and lambda
|
||||
* expressions, which are both callable.
|
||||
*/
|
||||
|
||||
import Stmt
|
||||
private import semmle.code.csharp.ExprOrStmtParent
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
cached
|
||||
module Internal {
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Holds if `c` is the enclosing callable of statement `s`.
|
||||
*/
|
||||
cached
|
||||
predicate enclosingCallable(Stmt s, Callable c) {
|
||||
// Compute the enclosing callable for a statement. This walks up through
|
||||
// enclosing statements until it hits a callable. It's unambiguous, since
|
||||
// if a statement has no parent statement, it's either the method body
|
||||
// or the body of an anonymous function declaration, in each of which cases the
|
||||
// non-statement parent is in fact the enclosing callable.
|
||||
c.getAChildStmt+() = s
|
||||
}
|
||||
|
||||
private Expr getAChildExpr(ExprOrStmtParent p) {
|
||||
result = p.getAChildExpr() or
|
||||
result = p.(AssignOperation).getExpandedAssignment()
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Holds if `s` is the enclosing statement of expression `e`.
|
||||
*/
|
||||
cached
|
||||
predicate enclosingStmt(Expr e, Stmt s) {
|
||||
// Compute the enclosing statement for an expression. Note that this need
|
||||
// not exist, since expressions can occur in contexts where they have no
|
||||
// enclosing statement (examples include field initialisers, both inline
|
||||
// and explicit on constructor definitions, and annotation arguments).
|
||||
getAChildExpr+(s) = e
|
||||
}
|
||||
|
||||
private predicate childExprOfCallable(Callable parent, Expr child) {
|
||||
child = getAChildExpr(parent)
|
||||
or
|
||||
exists(Expr mid | childExprOfCallable(parent, mid) |
|
||||
not mid instanceof Callable and
|
||||
child = getAChildExpr(mid)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Holds if `c` is the enclosing callable of expression `e`.
|
||||
*/
|
||||
cached
|
||||
predicate exprEnclosingCallable(Expr e, Callable c) {
|
||||
// Compute the enclosing callable of an expression. Note that expressions in
|
||||
// lambda functions should have the lambdas as enclosing callables, and their
|
||||
// enclosing statement may be the same as the enclosing statement of the
|
||||
// lambda; thus, it is *not* safe to go up to the enclosing statement and
|
||||
// take its own enclosing callable.
|
||||
childExprOfCallable(c, e)
|
||||
or
|
||||
not childExprOfCallable(_, e) and
|
||||
exists(Stmt s | enclosingStmt(e, s) | enclosingCallable(s, c))
|
||||
}
|
||||
}
|
||||
@@ -138,6 +138,54 @@ private module Cached {
|
||||
)
|
||||
else expr_parent(child, i, parent)
|
||||
}
|
||||
|
||||
private Expr getAChildExpr(ExprOrStmtParent parent) {
|
||||
result = parent.getAChildExpr() or
|
||||
result = parent.(AssignOperation).getExpandedAssignment()
|
||||
}
|
||||
|
||||
private ControlFlowElement getAChild(ExprOrStmtParent parent) {
|
||||
result = getAChildExpr(parent)
|
||||
or
|
||||
result = parent.getAChildStmt()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private ControlFlowElement enclosingStart(ControlFlowElement cfe) {
|
||||
result = cfe
|
||||
or
|
||||
getAChild(result).(AnonymousFunctionExpr) = cfe
|
||||
}
|
||||
|
||||
private predicate parent(ControlFlowElement child, ExprOrStmtParent parent) {
|
||||
child = getAChild(parent) and
|
||||
not child instanceof Callable
|
||||
}
|
||||
|
||||
/** Holds if the enclosing body of `cfe` is `body`. */
|
||||
cached
|
||||
predicate enclosingBody(ControlFlowElement cfe, ControlFlowElement body) {
|
||||
body = any(Callable c).getBody() and
|
||||
parent*(enclosingStart(cfe), body)
|
||||
}
|
||||
|
||||
/** Holds if the enclosing callable of `cfe` is `c`. */
|
||||
cached
|
||||
predicate enclosingCallable(ControlFlowElement cfe, Callable c) {
|
||||
enclosingBody(cfe, c.getBody())
|
||||
or
|
||||
parent*(enclosingStart(cfe), c.(Constructor).getInitializer())
|
||||
}
|
||||
|
||||
/** Holds if the enclosing statement of expression `e` is `s`. */
|
||||
cached
|
||||
predicate enclosingStmt(Expr e, Stmt s) {
|
||||
// Compute the enclosing statement for an expression. Note that this need
|
||||
// not exist, since expressions can occur in contexts where they have no
|
||||
// enclosing statement (examples include field initialisers, both inline
|
||||
// and explicit on constructor definitions, and annotation arguments).
|
||||
getAChildExpr+(s) = e
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
|
||||
@@ -8,7 +8,7 @@ import Element
|
||||
import Location
|
||||
import Member
|
||||
import exprs.Expr
|
||||
private import semmle.code.csharp.Enclosing::Internal
|
||||
private import semmle.code.csharp.ExprOrStmtParent
|
||||
private import semmle.code.csharp.frameworks.System
|
||||
private import TypeRef
|
||||
|
||||
|
||||
@@ -321,10 +321,9 @@ private module SsaDefReaches {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private BasicBlock getAMaybeLiveSuccessor(Definition def, BasicBlock bb) {
|
||||
result = getABasicBlockSuccessor(bb) and
|
||||
not defOccursInBlock(_, bb, def.getSourceVariable()) and
|
||||
ssaDefReachesEndOfBlock(bb, def, _)
|
||||
private predicate ssaDefReachesThroughBlock(Definition def, BasicBlock bb) {
|
||||
ssaDefReachesEndOfBlock(bb, def, _) and
|
||||
not defOccursInBlock(_, bb, def.getSourceVariable())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -337,7 +336,11 @@ private module SsaDefReaches {
|
||||
defOccursInBlock(def, bb1, _) and
|
||||
bb2 = getABasicBlockSuccessor(bb1)
|
||||
or
|
||||
exists(BasicBlock mid | varBlockReaches(def, bb1, mid) | bb2 = getAMaybeLiveSuccessor(def, mid))
|
||||
exists(BasicBlock mid |
|
||||
varBlockReaches(def, bb1, mid) and
|
||||
ssaDefReachesThroughBlock(def, mid) and
|
||||
bb2 = getABasicBlockSuccessor(mid)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -355,17 +358,10 @@ private module SsaDefReaches {
|
||||
|
||||
private import SsaDefReaches
|
||||
|
||||
pragma[noinline]
|
||||
private predicate ssaDefReachesEndOfBlockRec(BasicBlock bb, Definition def, SourceVariable v) {
|
||||
exists(BasicBlock idom | ssaDefReachesEndOfBlock(idom, def, v) |
|
||||
// The construction of SSA form ensures that each read of a variable is
|
||||
// dominated by its definition. An SSA definition therefore reaches a
|
||||
// control flow node if it is the _closest_ SSA definition that dominates
|
||||
// the node. If two definitions dominate a node then one must dominate the
|
||||
// other, so therefore the definition of _closest_ is given by the dominator
|
||||
// tree. Thus, reaching definitions can be calculated in terms of dominance.
|
||||
idom = getImmediateBasicBlockDominator(bb)
|
||||
)
|
||||
pragma[nomagic]
|
||||
predicate liveThrough(BasicBlock bb, SourceVariable v) {
|
||||
liveAtExit(bb, v) and
|
||||
not ssaRef(bb, _, v, SsaDef())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -382,9 +378,14 @@ predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable
|
||||
liveAtExit(bb, v)
|
||||
)
|
||||
or
|
||||
ssaDefReachesEndOfBlockRec(bb, def, v) and
|
||||
liveAtExit(bb, v) and
|
||||
not ssaRef(bb, _, v, SsaDef())
|
||||
// The construction of SSA form ensures that each read of a variable is
|
||||
// dominated by its definition. An SSA definition therefore reaches a
|
||||
// control flow node if it is the _closest_ SSA definition that dominates
|
||||
// the node. If two definitions dominate a node then one must dominate the
|
||||
// other, so therefore the definition of _closest_ is given by the dominator
|
||||
// tree. Thus, reaching definitions can be calculated in terms of dominance.
|
||||
ssaDefReachesEndOfBlock(getImmediateBasicBlockDominator(bb), def, pragma[only_bind_into](v)) and
|
||||
liveThrough(bb, pragma[only_bind_into](v))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,14 +47,14 @@ class Node extends TNode {
|
||||
cached
|
||||
final DataFlowCallable getEnclosingCallable() {
|
||||
Stages::DataFlowStage::forceCachingInSameStage() and
|
||||
result = unique(DataFlowCallable c | c = this.(NodeImpl).getEnclosingCallableImpl() | c)
|
||||
result = this.(NodeImpl).getEnclosingCallableImpl()
|
||||
}
|
||||
|
||||
/** Gets the control flow node corresponding to this node, if any. */
|
||||
cached
|
||||
final ControlFlow::Node getControlFlowNode() {
|
||||
Stages::DataFlowStage::forceCachingInSameStage() and
|
||||
result = unique(ControlFlow::Node n | n = this.(NodeImpl).getControlFlowNodeImpl() | n)
|
||||
result = this.(NodeImpl).getControlFlowNodeImpl()
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this node. */
|
||||
|
||||
@@ -321,10 +321,9 @@ private module SsaDefReaches {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private BasicBlock getAMaybeLiveSuccessor(Definition def, BasicBlock bb) {
|
||||
result = getABasicBlockSuccessor(bb) and
|
||||
not defOccursInBlock(_, bb, def.getSourceVariable()) and
|
||||
ssaDefReachesEndOfBlock(bb, def, _)
|
||||
private predicate ssaDefReachesThroughBlock(Definition def, BasicBlock bb) {
|
||||
ssaDefReachesEndOfBlock(bb, def, _) and
|
||||
not defOccursInBlock(_, bb, def.getSourceVariable())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -337,7 +336,11 @@ private module SsaDefReaches {
|
||||
defOccursInBlock(def, bb1, _) and
|
||||
bb2 = getABasicBlockSuccessor(bb1)
|
||||
or
|
||||
exists(BasicBlock mid | varBlockReaches(def, bb1, mid) | bb2 = getAMaybeLiveSuccessor(def, mid))
|
||||
exists(BasicBlock mid |
|
||||
varBlockReaches(def, bb1, mid) and
|
||||
ssaDefReachesThroughBlock(def, mid) and
|
||||
bb2 = getABasicBlockSuccessor(mid)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -355,17 +358,10 @@ private module SsaDefReaches {
|
||||
|
||||
private import SsaDefReaches
|
||||
|
||||
pragma[noinline]
|
||||
private predicate ssaDefReachesEndOfBlockRec(BasicBlock bb, Definition def, SourceVariable v) {
|
||||
exists(BasicBlock idom | ssaDefReachesEndOfBlock(idom, def, v) |
|
||||
// The construction of SSA form ensures that each read of a variable is
|
||||
// dominated by its definition. An SSA definition therefore reaches a
|
||||
// control flow node if it is the _closest_ SSA definition that dominates
|
||||
// the node. If two definitions dominate a node then one must dominate the
|
||||
// other, so therefore the definition of _closest_ is given by the dominator
|
||||
// tree. Thus, reaching definitions can be calculated in terms of dominance.
|
||||
idom = getImmediateBasicBlockDominator(bb)
|
||||
)
|
||||
pragma[nomagic]
|
||||
predicate liveThrough(BasicBlock bb, SourceVariable v) {
|
||||
liveAtExit(bb, v) and
|
||||
not ssaRef(bb, _, v, SsaDef())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -382,9 +378,14 @@ predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable
|
||||
liveAtExit(bb, v)
|
||||
)
|
||||
or
|
||||
ssaDefReachesEndOfBlockRec(bb, def, v) and
|
||||
liveAtExit(bb, v) and
|
||||
not ssaRef(bb, _, v, SsaDef())
|
||||
// The construction of SSA form ensures that each read of a variable is
|
||||
// dominated by its definition. An SSA definition therefore reaches a
|
||||
// control flow node if it is the _closest_ SSA definition that dominates
|
||||
// the node. If two definitions dominate a node then one must dominate the
|
||||
// other, so therefore the definition of _closest_ is given by the dominator
|
||||
// tree. Thus, reaching definitions can be calculated in terms of dominance.
|
||||
ssaDefReachesEndOfBlock(getImmediateBasicBlockDominator(bb), def, pragma[only_bind_into](v)) and
|
||||
liveThrough(bb, pragma[only_bind_into](v))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -321,10 +321,9 @@ private module SsaDefReaches {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private BasicBlock getAMaybeLiveSuccessor(Definition def, BasicBlock bb) {
|
||||
result = getABasicBlockSuccessor(bb) and
|
||||
not defOccursInBlock(_, bb, def.getSourceVariable()) and
|
||||
ssaDefReachesEndOfBlock(bb, def, _)
|
||||
private predicate ssaDefReachesThroughBlock(Definition def, BasicBlock bb) {
|
||||
ssaDefReachesEndOfBlock(bb, def, _) and
|
||||
not defOccursInBlock(_, bb, def.getSourceVariable())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -337,7 +336,11 @@ private module SsaDefReaches {
|
||||
defOccursInBlock(def, bb1, _) and
|
||||
bb2 = getABasicBlockSuccessor(bb1)
|
||||
or
|
||||
exists(BasicBlock mid | varBlockReaches(def, bb1, mid) | bb2 = getAMaybeLiveSuccessor(def, mid))
|
||||
exists(BasicBlock mid |
|
||||
varBlockReaches(def, bb1, mid) and
|
||||
ssaDefReachesThroughBlock(def, mid) and
|
||||
bb2 = getABasicBlockSuccessor(mid)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -355,17 +358,10 @@ private module SsaDefReaches {
|
||||
|
||||
private import SsaDefReaches
|
||||
|
||||
pragma[noinline]
|
||||
private predicate ssaDefReachesEndOfBlockRec(BasicBlock bb, Definition def, SourceVariable v) {
|
||||
exists(BasicBlock idom | ssaDefReachesEndOfBlock(idom, def, v) |
|
||||
// The construction of SSA form ensures that each read of a variable is
|
||||
// dominated by its definition. An SSA definition therefore reaches a
|
||||
// control flow node if it is the _closest_ SSA definition that dominates
|
||||
// the node. If two definitions dominate a node then one must dominate the
|
||||
// other, so therefore the definition of _closest_ is given by the dominator
|
||||
// tree. Thus, reaching definitions can be calculated in terms of dominance.
|
||||
idom = getImmediateBasicBlockDominator(bb)
|
||||
)
|
||||
pragma[nomagic]
|
||||
predicate liveThrough(BasicBlock bb, SourceVariable v) {
|
||||
liveAtExit(bb, v) and
|
||||
not ssaRef(bb, _, v, SsaDef())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -382,9 +378,14 @@ predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable
|
||||
liveAtExit(bb, v)
|
||||
)
|
||||
or
|
||||
ssaDefReachesEndOfBlockRec(bb, def, v) and
|
||||
liveAtExit(bb, v) and
|
||||
not ssaRef(bb, _, v, SsaDef())
|
||||
// The construction of SSA form ensures that each read of a variable is
|
||||
// dominated by its definition. An SSA definition therefore reaches a
|
||||
// control flow node if it is the _closest_ SSA definition that dominates
|
||||
// the node. If two definitions dominate a node then one must dominate the
|
||||
// other, so therefore the definition of _closest_ is given by the dominator
|
||||
// tree. Thus, reaching definitions can be calculated in terms of dominance.
|
||||
ssaDefReachesEndOfBlock(getImmediateBasicBlockDominator(bb), def, pragma[only_bind_into](v)) and
|
||||
liveThrough(bb, pragma[only_bind_into](v))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -636,31 +636,41 @@ private module Internal {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stepExpr0(Expr succ, Expr pred) {
|
||||
Steps::stepOpen(pred, succ)
|
||||
or
|
||||
exists(Assignable a |
|
||||
a instanceof Field or
|
||||
a instanceof Property
|
||||
|
|
||||
succ.(AssignableRead) = a.getAnAccess() and
|
||||
pred = a.getAnAssignedValue() and
|
||||
a = any(Modifiable m | not m.isEffectivelyPublic())
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stepExpr(Expr succ, Expr pred) {
|
||||
stepExpr0(succ, pred) and
|
||||
private predicate stepExpr(Expr pred, Expr succ) {
|
||||
Steps::stepOpen(pred, succ) and
|
||||
// Do not step through down casts
|
||||
not downCast(succ) and
|
||||
// Only step when we may learn more about the actual type
|
||||
typeMayBeImprecise(succ.getType())
|
||||
}
|
||||
|
||||
private predicate stepTC(Expr succ, Expr pred) = fastTC(stepExpr/2)(succ, pred)
|
||||
private class AnalyzableFieldOrProperty extends Assignable, Modifiable {
|
||||
AnalyzableFieldOrProperty() {
|
||||
(
|
||||
this instanceof Field or
|
||||
this instanceof Property
|
||||
) and
|
||||
not this.isEffectivelyPublic() and
|
||||
exists(this.getAnAssignedValue())
|
||||
}
|
||||
|
||||
AssignableRead getARead() { result = this.getAnAccess() }
|
||||
}
|
||||
|
||||
private class Source extends Expr {
|
||||
Source() { not stepExpr(this, _) }
|
||||
Source() {
|
||||
not stepExpr(_, this) and
|
||||
not this = any(AnalyzableFieldOrProperty a).getARead()
|
||||
}
|
||||
|
||||
Type getType(boolean isExact) {
|
||||
result = this.getType() and
|
||||
if
|
||||
this instanceof ObjectCreation or
|
||||
this instanceof BaseAccess
|
||||
then isExact = true
|
||||
else isExact = false
|
||||
}
|
||||
}
|
||||
|
||||
private class Sink extends Expr {
|
||||
@@ -680,24 +690,38 @@ private module Internal {
|
||||
this = any(DispatchCallImpl c).getArgument(_)
|
||||
}
|
||||
|
||||
Source getASource() { stepTC(this, result) }
|
||||
pragma[nomagic]
|
||||
Expr getAPred() { stepExpr*(result, this) }
|
||||
|
||||
pragma[nomagic]
|
||||
AnalyzableFieldOrProperty getAPredRead() { this.getAPred() = result.getARead() }
|
||||
}
|
||||
|
||||
/** Holds if the expression `e` has an exact type. */
|
||||
private predicate hasExactType(Expr e) {
|
||||
e instanceof ObjectCreation or
|
||||
e instanceof BaseAccess
|
||||
/** Gets a source type for sink expression `e`, using simple data flow. */
|
||||
Type getASourceType(Sink sink, boolean isExact) {
|
||||
result = sink.getAPred().(Source).getType(isExact)
|
||||
or
|
||||
result = sink.getAPredRead().(RelevantFieldOrProperty).getASourceType(isExact)
|
||||
}
|
||||
|
||||
/** Gets a source type for expression `e`, using simple data flow. */
|
||||
Type getASourceType(Sink e, boolean isExact) {
|
||||
exists(Source s |
|
||||
s = e.getASource() or
|
||||
s = e
|
||||
|
|
||||
result = s.getType() and
|
||||
if hasExactType(s) then isExact = true else isExact = false
|
||||
)
|
||||
private class RelevantFieldOrProperty extends AnalyzableFieldOrProperty {
|
||||
RelevantFieldOrProperty() {
|
||||
this = any(Sink s).getAPredRead()
|
||||
or
|
||||
this = any(RelevantFieldOrProperty a).getAPredRead()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
Expr getAPred() { stepExpr*(result, this.getAnAssignedValue()) }
|
||||
|
||||
pragma[nomagic]
|
||||
AnalyzableFieldOrProperty getAPredRead() { this.getAPred() = result.getARead() }
|
||||
|
||||
Type getASourceType(boolean isExact) {
|
||||
result = this.getAPred().(Source).getType(isExact)
|
||||
or
|
||||
result = this.getAPredRead().(RelevantFieldOrProperty).getASourceType(isExact)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import semmle.code.csharp.Location
|
||||
import semmle.code.csharp.Stmt
|
||||
import semmle.code.csharp.Type
|
||||
private import dotnet
|
||||
private import semmle.code.csharp.Enclosing::Internal
|
||||
private import semmle.code.csharp.ExprOrStmtParent
|
||||
private import semmle.code.csharp.frameworks.System
|
||||
private import semmle.code.csharp.TypeRef
|
||||
|
||||
@@ -55,7 +55,7 @@ class Expr extends DotNet::Expr, ControlFlowElement, @expr {
|
||||
final Stmt getEnclosingStmt() { enclosingStmt(this, result) }
|
||||
|
||||
/** Gets the enclosing callable of this expression, if any. */
|
||||
override Callable getEnclosingCallable() { exprEnclosingCallable(this, result) }
|
||||
override Callable getEnclosingCallable() { enclosingCallable(this, result) }
|
||||
|
||||
/**
|
||||
* Holds if this expression is generated by the compiler and does not appear
|
||||
|
||||
42
csharp/ql/src/semmle/code/csharp/frameworks/Dapper.qll
Normal file
42
csharp/ql/src/semmle/code/csharp/frameworks/Dapper.qll
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Classes for modeling Dapper.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.frameworks.system.Data
|
||||
|
||||
/** Definitions relating to the `Dapper` package. */
|
||||
module Dapper {
|
||||
/** The namespace `Dapper`. */
|
||||
class DapperNamespace extends Namespace {
|
||||
DapperNamespace() { this.hasQualifiedName("Dapper") }
|
||||
}
|
||||
|
||||
/** A class in `Dapper`. */
|
||||
class DapperClass extends Class {
|
||||
DapperClass() { this.getParent() instanceof DapperNamespace }
|
||||
}
|
||||
|
||||
/** A struct in `Dapper`. */
|
||||
class DapperStruct extends Struct {
|
||||
DapperStruct() { this.getParent() instanceof DapperNamespace }
|
||||
}
|
||||
|
||||
/** The `Dapper.SqlMapper` class. */
|
||||
class SqlMapperClass extends DapperClass {
|
||||
SqlMapperClass() { this.hasName("SqlMapper") }
|
||||
|
||||
/** Gets a DB query method. */
|
||||
ExtensionMethod getAQueryMethod() {
|
||||
result = this.getAMethod() and
|
||||
result.getName().regexpMatch("Query.*|Execute.*") and
|
||||
result.getExtendedType() instanceof SystemDataIDbConnectionInterface and
|
||||
result.isPublic()
|
||||
}
|
||||
}
|
||||
|
||||
/** The `Dapper.CommandDefinition` struct. */
|
||||
class CommandDefinitionStruct extends DapperStruct {
|
||||
CommandDefinitionStruct() { this.hasName("CommandDefinition") }
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ private import semmle.code.csharp.frameworks.system.Data
|
||||
private import semmle.code.csharp.frameworks.system.data.SqlClient
|
||||
private import semmle.code.csharp.frameworks.EntityFramework
|
||||
private import semmle.code.csharp.frameworks.NHibernate
|
||||
private import semmle.code.csharp.frameworks.Dapper
|
||||
private import semmle.code.csharp.dataflow.DataFlow4
|
||||
|
||||
/** An expression containing a SQL command. */
|
||||
abstract class SqlExpr extends Expr {
|
||||
@@ -83,3 +85,37 @@ class MicrosoftSqlHelperMethodCallSqlExpr extends SqlExpr, MethodCall {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A `Dapper.SqlMapper` method that is taking a SQL string argument. */
|
||||
class DapperSqlMethodCallSqlExpr extends SqlExpr, MethodCall {
|
||||
DapperSqlMethodCallSqlExpr() {
|
||||
this.getTarget() = any(Dapper::SqlMapperClass c).getAQueryMethod()
|
||||
}
|
||||
|
||||
override Expr getSql() { result = this.getArgumentForName("sql") }
|
||||
}
|
||||
|
||||
/** A `Dapper.CommandDefinition` creation that is taking a SQL string argument and is passed to a `Dapper.SqlMapper` method. */
|
||||
class DapperCommandDefinitionMethodCallSqlExpr extends SqlExpr, ObjectCreation {
|
||||
DapperCommandDefinitionMethodCallSqlExpr() {
|
||||
this.getObjectType() instanceof Dapper::CommandDefinitionStruct and
|
||||
exists(Conf c | c.hasFlow(DataFlow::exprNode(this), _))
|
||||
}
|
||||
|
||||
override Expr getSql() { result = this.getArgumentForName("commandText") }
|
||||
}
|
||||
|
||||
private class Conf extends DataFlow4::Configuration {
|
||||
Conf() { this = "DapperCommandDefinitionFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) {
|
||||
node.asExpr().(ObjectCreation).getObjectType() instanceof Dapper::CommandDefinitionStruct
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
exists(MethodCall mc |
|
||||
mc.getTarget() = any(Dapper::SqlMapperClass c).getAQueryMethod() and
|
||||
node.asExpr() = mc.getArgumentForName("command")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,9 @@ class Element extends @dotnet_element {
|
||||
/** Holds if this element is from source code. */
|
||||
predicate fromSource() { this.getFile().fromSource() }
|
||||
|
||||
/** Holds if this element is from an assembly. */
|
||||
predicate fromLibrary() { this.getFile().fromLibrary() }
|
||||
|
||||
/**
|
||||
* Gets the "language" of this program element, as defined by the extension of the filename.
|
||||
* For example, C# has language "cs", and Visual Basic has language "vb".
|
||||
|
||||
@@ -11,9 +11,15 @@ class SourceControlFlowElement extends ControlFlowElement {
|
||||
}
|
||||
|
||||
class SourceControlFlowNode extends ControlFlow::Node {
|
||||
SourceControlFlowNode() { not this.getLocation().getFile() instanceof StubFile }
|
||||
SourceControlFlowNode() {
|
||||
not this.getLocation().getFile() instanceof StubFile and
|
||||
not this.getLocation().getFile().fromLibrary()
|
||||
}
|
||||
}
|
||||
|
||||
class SourceBasicBlock extends ControlFlow::BasicBlock {
|
||||
SourceBasicBlock() { not this.getLocation().getFile() instanceof StubFile }
|
||||
SourceBasicBlock() {
|
||||
not this.getLocation().getFile() instanceof StubFile and
|
||||
not this.getLocation().getFile().fromLibrary()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import csharp
|
||||
|
||||
query predicate countSplits(ControlFlowElement cfe, int i) {
|
||||
not cfe.fromLibrary() and
|
||||
i = strictcount(ControlFlow::Nodes::ElementNode n | n.getElement() = cfe)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import csharp
|
||||
|
||||
from DefaultValueExpr l
|
||||
where l.fromSource()
|
||||
select l, l.getValue()
|
||||
|
||||
@@ -2,5 +2,7 @@ import csharp
|
||||
import semmle.code.csharp.dataflow.TaintTracking
|
||||
|
||||
from DataFlow::Node pred, DataFlow::Node succ
|
||||
where TaintTracking::localTaintStep(pred, succ)
|
||||
where
|
||||
TaintTracking::localTaintStep(pred, succ) and
|
||||
not pred.asExpr().fromLibrary()
|
||||
select pred, succ
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import csharp
|
||||
|
||||
from DataFlow::Node pred, DataFlow::Node succ
|
||||
where DataFlow::localFlowStep(pred, succ)
|
||||
where not pred.asExpr().fromLibrary() and DataFlow::localFlowStep(pred, succ)
|
||||
select pred, succ
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import csharp
|
||||
|
||||
from DataFlow::Node pred, DataFlow::Node succ
|
||||
where TaintTracking::localTaintStep(pred, succ)
|
||||
where
|
||||
not pred.asExpr().fromLibrary() and
|
||||
TaintTracking::localTaintStep(pred, succ)
|
||||
select pred, succ
|
||||
|
||||
@@ -4,5 +4,7 @@ import semmle.code.csharp.dataflow.ModulusAnalysis
|
||||
import semmle.code.csharp.dataflow.Bound
|
||||
|
||||
from ControlFlow::Nodes::ExprNode e, Bound b, int delta, int mod
|
||||
where exprModulus(e, b, delta, mod)
|
||||
where
|
||||
not e.getExpr().fromLibrary() and
|
||||
exprModulus(e, b, delta, mod)
|
||||
select e, b.toString(), delta, mod
|
||||
|
||||
@@ -18,4 +18,5 @@ string getASignString(ControlFlow::Nodes::ExprNode e) {
|
||||
}
|
||||
|
||||
from ControlFlow::Nodes::ExprNode e
|
||||
where not e.getExpr().fromLibrary()
|
||||
select e, strictconcat(string s | s = getASignString(e) | s, " ")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import csharp
|
||||
|
||||
from DataFlow::Node pred, DataFlow::Node succ
|
||||
where DataFlow::localFlowStep(pred, succ)
|
||||
where not pred.asExpr().fromLibrary() and DataFlow::localFlowStep(pred, succ)
|
||||
select pred, succ
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import csharp
|
||||
|
||||
from Parameter p
|
||||
where p.fromSource()
|
||||
select p, p.getDefaultValue()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import csharp
|
||||
|
||||
query predicate edges(ControlFlow::Node node, ControlFlow::Node successor, string attr, string val) {
|
||||
not node.getElement().fromLibrary() and
|
||||
exists(ControlFlow::SuccessorType t | successor = node.getASuccessorByType(t) |
|
||||
attr = "semmle.label" and
|
||||
val = t.toString()
|
||||
|
||||
17
csharp/ql/test/library-tests/parameters/Parameters.cs
Normal file
17
csharp/ql/test/library-tests/parameters/Parameters.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
public class Parameters
|
||||
{
|
||||
public void M1(int a, object b, string c) => throw null;
|
||||
public void M2(int a, object b = null, string c = "default string") => throw null;
|
||||
public void M3(int a = 1, object b = null, string c = "null") => throw null;
|
||||
public void M4(int a = default, object b = default) => throw null;
|
||||
public void M5(int a = new int(), object b = default) => throw null;
|
||||
public void M6(MyStruct s1, MyStruct s2 = default(MyStruct), MyStruct s3 = new MyStruct()) => throw null;
|
||||
public void M7(MyEnum e1, MyEnum e2 = default(MyEnum), MyEnum e3 = new MyEnum(), MyEnum e4 = MyEnum.A, MyEnum e5 = (MyEnum)5) => throw null;
|
||||
|
||||
public void M8<T>(T t = default) => throw null;
|
||||
public void M9<T>(T t = default) where T : struct => throw null;
|
||||
public void M10<T>(T t = default) where T : class => throw null;
|
||||
|
||||
public struct MyStruct { }
|
||||
public enum MyEnum { A = 1, B = 2 }
|
||||
}
|
||||
17
csharp/ql/test/library-tests/parameters/Parameters.cs_
Normal file
17
csharp/ql/test/library-tests/parameters/Parameters.cs_
Normal file
@@ -0,0 +1,17 @@
|
||||
public class ParametersDll
|
||||
{
|
||||
public void M1(int a, object b, string c) => throw null;
|
||||
public void M2(int a, object b = null, string c = "default string") => throw null;
|
||||
public void M3(int a = 1, object b = null, string c = "null") => throw null;
|
||||
public void M4(int a = default, object b = default) => throw null;
|
||||
public void M5(int a = new int(), object b = default) => throw null;
|
||||
public void M6(MyStruct s1, MyStruct s2 = default(MyStruct), MyStruct s3 = new MyStruct()) => throw null;
|
||||
public void M7(MyEnum e1, MyEnum e2 = default(MyEnum), MyEnum e3 = new MyEnum(), MyEnum e4 = MyEnum.A, MyEnum e5 = (MyEnum)5) => throw null;
|
||||
|
||||
public void M8<T>(T t = default) => throw null;
|
||||
public void M9<T>(T t = default) where T : struct => throw null;
|
||||
public void M10<T>(T t = default) where T : class => throw null;
|
||||
|
||||
public struct MyStruct { }
|
||||
public enum MyEnum { A = 1, B = 2 }
|
||||
}
|
||||
BIN
csharp/ql/test/library-tests/parameters/Parameters.dll
Normal file
BIN
csharp/ql/test/library-tests/parameters/Parameters.dll
Normal file
Binary file not shown.
50
csharp/ql/test/library-tests/parameters/Parameters.expected
Normal file
50
csharp/ql/test/library-tests/parameters/Parameters.expected
Normal file
@@ -0,0 +1,50 @@
|
||||
noDefaultValue
|
||||
| Parameters.cs:3:17:3:18 | M1 | Parameters.cs:3:24:3:24 | a | 0 |
|
||||
| Parameters.cs:3:17:3:18 | M1 | Parameters.cs:3:34:3:34 | b | 1 |
|
||||
| Parameters.cs:3:17:3:18 | M1 | Parameters.cs:3:44:3:44 | c | 2 |
|
||||
| Parameters.cs:4:17:4:18 | M2 | Parameters.cs:4:24:4:24 | a | 0 |
|
||||
| Parameters.cs:8:17:8:18 | M6 | Parameters.cs:8:29:8:30 | s1 | 0 |
|
||||
| Parameters.cs:9:17:9:18 | M7 | Parameters.cs:9:27:9:28 | e1 | 0 |
|
||||
| Parameters.dll:0:0:0:0 | M1 | Parameters.dll:0:0:0:0 | a | 0 |
|
||||
| Parameters.dll:0:0:0:0 | M1 | Parameters.dll:0:0:0:0 | b | 1 |
|
||||
| Parameters.dll:0:0:0:0 | M1 | Parameters.dll:0:0:0:0 | c | 2 |
|
||||
| Parameters.dll:0:0:0:0 | M2 | Parameters.dll:0:0:0:0 | a | 0 |
|
||||
| Parameters.dll:0:0:0:0 | M6 | Parameters.dll:0:0:0:0 | s1 | 0 |
|
||||
| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e1 | 0 |
|
||||
withDefaultValue
|
||||
| Parameters.cs:4:17:4:18 | M2 | Parameters.cs:4:34:4:34 | b | 1 | Parameters.cs:4:38:4:41 | null | null |
|
||||
| Parameters.cs:4:17:4:18 | M2 | Parameters.cs:4:51:4:51 | c | 2 | Parameters.cs:4:55:4:70 | "default string" | default string |
|
||||
| Parameters.cs:5:17:5:18 | M3 | Parameters.cs:5:24:5:24 | a | 0 | Parameters.cs:5:28:5:28 | 1 | 1 |
|
||||
| Parameters.cs:5:17:5:18 | M3 | Parameters.cs:5:38:5:38 | b | 1 | Parameters.cs:5:42:5:45 | null | null |
|
||||
| Parameters.cs:5:17:5:18 | M3 | Parameters.cs:5:55:5:55 | c | 2 | Parameters.cs:5:59:5:64 | "null" | null |
|
||||
| Parameters.cs:6:17:6:18 | M4 | Parameters.cs:6:24:6:24 | a | 0 | Parameters.cs:6:28:6:34 | (...) ... | 0 |
|
||||
| Parameters.cs:6:17:6:18 | M4 | Parameters.cs:6:44:6:44 | b | 1 | Parameters.cs:6:48:6:54 | default | null |
|
||||
| Parameters.cs:7:17:7:18 | M5 | Parameters.cs:7:24:7:24 | a | 0 | Parameters.cs:7:28:7:36 | object creation of type Int32 | 0 |
|
||||
| Parameters.cs:7:17:7:18 | M5 | Parameters.cs:7:46:7:46 | b | 1 | Parameters.cs:7:50:7:56 | default | null |
|
||||
| Parameters.cs:8:17:8:18 | M6 | Parameters.cs:8:42:8:43 | s2 | 1 | Parameters.cs:8:47:8:63 | default(...) | - |
|
||||
| Parameters.cs:8:17:8:18 | M6 | Parameters.cs:8:75:8:76 | s3 | 2 | Parameters.cs:8:80:8:93 | object creation of type MyStruct | - |
|
||||
| Parameters.cs:9:17:9:18 | M7 | Parameters.cs:9:38:9:39 | e2 | 1 | Parameters.cs:9:43:9:57 | default(...) | 0 |
|
||||
| Parameters.cs:9:17:9:18 | M7 | Parameters.cs:9:67:9:68 | e3 | 2 | Parameters.cs:9:72:9:83 | object creation of type MyEnum | 0 |
|
||||
| Parameters.cs:9:17:9:18 | M7 | Parameters.cs:9:93:9:94 | e4 | 3 | Parameters.cs:9:98:9:105 | access to constant A | 1 |
|
||||
| Parameters.cs:9:17:9:18 | M7 | Parameters.cs:9:115:9:116 | e5 | 4 | Parameters.cs:9:120:9:128 | (...) ... | 5 |
|
||||
| Parameters.cs:11:17:11:21 | M8 | Parameters.cs:11:25:11:25 | t | 0 | Parameters.cs:11:29:11:35 | (...) ... | - |
|
||||
| Parameters.cs:12:17:12:21 | M9 | Parameters.cs:12:25:12:25 | t | 0 | Parameters.cs:12:29:12:35 | (...) ... | - |
|
||||
| Parameters.cs:13:17:13:22 | M10 | Parameters.cs:13:26:13:26 | t | 0 | Parameters.cs:13:30:13:36 | (...) ... | null |
|
||||
| Parameters.dll:0:0:0:0 | M2 | Parameters.dll:0:0:0:0 | b | 1 | Parameters.dll:0:0:0:0 | default | null |
|
||||
| Parameters.dll:0:0:0:0 | M2 | Parameters.dll:0:0:0:0 | c | 2 | Parameters.dll:0:0:0:0 | "default string" | default string |
|
||||
| Parameters.dll:0:0:0:0 | M3 | Parameters.dll:0:0:0:0 | a | 0 | Parameters.dll:0:0:0:0 | 1 | 1 |
|
||||
| Parameters.dll:0:0:0:0 | M3 | Parameters.dll:0:0:0:0 | b | 1 | Parameters.dll:0:0:0:0 | default | null |
|
||||
| Parameters.dll:0:0:0:0 | M3 | Parameters.dll:0:0:0:0 | c | 2 | Parameters.dll:0:0:0:0 | "null" | null |
|
||||
| Parameters.dll:0:0:0:0 | M4 | Parameters.dll:0:0:0:0 | a | 0 | Parameters.dll:0:0:0:0 | 0 | 0 |
|
||||
| Parameters.dll:0:0:0:0 | M4 | Parameters.dll:0:0:0:0 | b | 1 | Parameters.dll:0:0:0:0 | default | null |
|
||||
| Parameters.dll:0:0:0:0 | M5 | Parameters.dll:0:0:0:0 | a | 0 | Parameters.dll:0:0:0:0 | 0 | 0 |
|
||||
| Parameters.dll:0:0:0:0 | M5 | Parameters.dll:0:0:0:0 | b | 1 | Parameters.dll:0:0:0:0 | default | null |
|
||||
| Parameters.dll:0:0:0:0 | M6 | Parameters.dll:0:0:0:0 | s2 | 1 | Parameters.dll:0:0:0:0 | default | - |
|
||||
| Parameters.dll:0:0:0:0 | M6 | Parameters.dll:0:0:0:0 | s3 | 2 | Parameters.dll:0:0:0:0 | default | - |
|
||||
| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e2 | 1 | Parameters.dll:0:0:0:0 | (...) ... | 0 |
|
||||
| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e3 | 2 | Parameters.dll:0:0:0:0 | (...) ... | 0 |
|
||||
| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e4 | 3 | Parameters.dll:0:0:0:0 | (...) ... | 1 |
|
||||
| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e5 | 4 | Parameters.dll:0:0:0:0 | (...) ... | 5 |
|
||||
| Parameters.dll:0:0:0:0 | M8 | Parameters.dll:0:0:0:0 | t | 0 | Parameters.dll:0:0:0:0 | default | - |
|
||||
| Parameters.dll:0:0:0:0 | M9 | Parameters.dll:0:0:0:0 | t | 0 | Parameters.dll:0:0:0:0 | default | - |
|
||||
| Parameters.dll:0:0:0:0 | M10 | Parameters.dll:0:0:0:0 | t | 0 | Parameters.dll:0:0:0:0 | default | null |
|
||||
19
csharp/ql/test/library-tests/parameters/Parameters.ql
Normal file
19
csharp/ql/test/library-tests/parameters/Parameters.ql
Normal file
@@ -0,0 +1,19 @@
|
||||
import csharp
|
||||
|
||||
private predicate fromTestLocation(Element e) {
|
||||
e.fromSource() or e.getFile().getStem() = "Parameters"
|
||||
}
|
||||
|
||||
query predicate noDefaultValue(Parameterizable container, Parameter p, int i) {
|
||||
fromTestLocation(container) and
|
||||
not p.hasDefaultValue() and
|
||||
container.getParameter(i) = p
|
||||
}
|
||||
|
||||
query predicate withDefaultValue(Parameterizable container, Parameter p, int i, Expr e, string value) {
|
||||
fromTestLocation(container) and
|
||||
p.hasDefaultValue() and
|
||||
container.getParameter(i) = p and
|
||||
p.getDefaultValue() = e and
|
||||
if exists(e.getValue()) then value = e.getValue() else value = "-"
|
||||
}
|
||||
@@ -17,4 +17,6 @@ class UnknownLocalVariableDeclExpr extends LocalVariableDeclAndInitExpr {
|
||||
override string toString() { result = "(unknown type) " + this.getName() }
|
||||
}
|
||||
|
||||
query predicate edges(ControlFlow::Node n1, ControlFlow::Node n2) { n2 = n1.getASuccessor() }
|
||||
query predicate edges(ControlFlow::Node n1, ControlFlow::Node n2) {
|
||||
not n1.getElement().fromLibrary() and n2 = n1.getASuccessor()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// semmle-extractor-options: /r:System.ComponentModel.Primitives.dll /r:System.ComponentModel.TypeConverter.dll /r:System.Data.Common.dll ${testdir}/../../../resources/stubs/EntityFramework.cs ${testdir}/../../../resources/stubs/System.Data.cs ${testdir}/../../../resources/stubs/System.Windows.cs
|
||||
// semmle-extractor-options: /r:System.ComponentModel.Primitives.dll /r:System.ComponentModel.TypeConverter.dll /r:System.Data.Common.dll ${testdir}/../../../resources/stubs/EntityFramework.cs ${testdir}/../../../resources/stubs/System.Data.cs ${testdir}/../../../resources/stubs/System.Windows.cs ${testdir}/../../../resources/stubs/Dapper.cs /r:System.Linq.Expressions.dll
|
||||
|
||||
using System;
|
||||
|
||||
|
||||
@@ -5,6 +5,13 @@ edges
|
||||
| SqlInjection.cs:73:33:73:52 | access to property Text : String | SqlInjection.cs:74:56:74:61 | access to local variable query1 |
|
||||
| SqlInjection.cs:73:33:73:52 | access to property Text : String | SqlInjection.cs:75:55:75:60 | access to local variable query1 |
|
||||
| SqlInjection.cs:87:21:87:29 | access to property Text : String | SqlInjection.cs:88:50:88:55 | access to local variable query1 |
|
||||
| SqlInjectionDapper.cs:20:86:20:94 | access to property Text : String | SqlInjectionDapper.cs:21:55:21:59 | access to local variable query |
|
||||
| SqlInjectionDapper.cs:29:86:29:94 | access to property Text : String | SqlInjectionDapper.cs:30:66:30:70 | access to local variable query |
|
||||
| SqlInjectionDapper.cs:38:86:38:94 | access to property Text : String | SqlInjectionDapper.cs:39:63:39:67 | access to local variable query |
|
||||
| SqlInjectionDapper.cs:47:86:47:94 | access to property Text : String | SqlInjectionDapper.cs:49:47:49:51 | access to local variable query |
|
||||
| SqlInjectionDapper.cs:57:86:57:94 | access to property Text : String | SqlInjectionDapper.cs:58:42:58:46 | access to local variable query |
|
||||
| SqlInjectionDapper.cs:66:86:66:94 | access to property Text : String | SqlInjectionDapper.cs:67:42:67:46 | access to local variable query |
|
||||
| SqlInjectionDapper.cs:75:86:75:94 | access to property Text : String | SqlInjectionDapper.cs:77:52:77:56 | access to local variable query |
|
||||
nodes
|
||||
| SqlInjection.cs:38:21:38:35 | access to field categoryTextBox : TextBox | semmle.label | access to field categoryTextBox : TextBox |
|
||||
| SqlInjection.cs:38:21:38:40 | access to property Text : String | semmle.label | access to property Text : String |
|
||||
@@ -15,8 +22,29 @@ nodes
|
||||
| SqlInjection.cs:75:55:75:60 | access to local variable query1 | semmle.label | access to local variable query1 |
|
||||
| SqlInjection.cs:87:21:87:29 | access to property Text : String | semmle.label | access to property Text : String |
|
||||
| SqlInjection.cs:88:50:88:55 | access to local variable query1 | semmle.label | access to local variable query1 |
|
||||
| SqlInjectionDapper.cs:20:86:20:94 | access to property Text : String | semmle.label | access to property Text : String |
|
||||
| SqlInjectionDapper.cs:21:55:21:59 | access to local variable query | semmle.label | access to local variable query |
|
||||
| SqlInjectionDapper.cs:29:86:29:94 | access to property Text : String | semmle.label | access to property Text : String |
|
||||
| SqlInjectionDapper.cs:30:66:30:70 | access to local variable query | semmle.label | access to local variable query |
|
||||
| SqlInjectionDapper.cs:38:86:38:94 | access to property Text : String | semmle.label | access to property Text : String |
|
||||
| SqlInjectionDapper.cs:39:63:39:67 | access to local variable query | semmle.label | access to local variable query |
|
||||
| SqlInjectionDapper.cs:47:86:47:94 | access to property Text : String | semmle.label | access to property Text : String |
|
||||
| SqlInjectionDapper.cs:49:47:49:51 | access to local variable query | semmle.label | access to local variable query |
|
||||
| SqlInjectionDapper.cs:57:86:57:94 | access to property Text : String | semmle.label | access to property Text : String |
|
||||
| SqlInjectionDapper.cs:58:42:58:46 | access to local variable query | semmle.label | access to local variable query |
|
||||
| SqlInjectionDapper.cs:66:86:66:94 | access to property Text : String | semmle.label | access to property Text : String |
|
||||
| SqlInjectionDapper.cs:67:42:67:46 | access to local variable query | semmle.label | access to local variable query |
|
||||
| SqlInjectionDapper.cs:75:86:75:94 | access to property Text : String | semmle.label | access to property Text : String |
|
||||
| SqlInjectionDapper.cs:77:52:77:56 | access to local variable query | semmle.label | access to local variable query |
|
||||
#select
|
||||
| SqlInjection.cs:39:50:39:55 | access to local variable query1 | SqlInjection.cs:38:21:38:35 | access to field categoryTextBox : TextBox | SqlInjection.cs:39:50:39:55 | access to local variable query1 | Query might include code from $@. | SqlInjection.cs:38:21:38:35 | access to field categoryTextBox : TextBox | this ASP.NET user input |
|
||||
| SqlInjection.cs:74:56:74:61 | access to local variable query1 | SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | SqlInjection.cs:74:56:74:61 | access to local variable query1 | Query might include code from $@. | SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | this ASP.NET user input |
|
||||
| SqlInjection.cs:75:55:75:60 | access to local variable query1 | SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | SqlInjection.cs:75:55:75:60 | access to local variable query1 | Query might include code from $@. | SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | this ASP.NET user input |
|
||||
| SqlInjection.cs:88:50:88:55 | access to local variable query1 | SqlInjection.cs:87:21:87:29 | access to property Text : String | SqlInjection.cs:88:50:88:55 | access to local variable query1 | Query might include code from $@. | SqlInjection.cs:87:21:87:29 | access to property Text : String | this TextBox text |
|
||||
| SqlInjectionDapper.cs:21:55:21:59 | access to local variable query | SqlInjectionDapper.cs:20:86:20:94 | access to property Text : String | SqlInjectionDapper.cs:21:55:21:59 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:20:86:20:94 | access to property Text : String | this TextBox text |
|
||||
| SqlInjectionDapper.cs:30:66:30:70 | access to local variable query | SqlInjectionDapper.cs:29:86:29:94 | access to property Text : String | SqlInjectionDapper.cs:30:66:30:70 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:29:86:29:94 | access to property Text : String | this TextBox text |
|
||||
| SqlInjectionDapper.cs:39:63:39:67 | access to local variable query | SqlInjectionDapper.cs:38:86:38:94 | access to property Text : String | SqlInjectionDapper.cs:39:63:39:67 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:38:86:38:94 | access to property Text : String | this TextBox text |
|
||||
| SqlInjectionDapper.cs:49:47:49:51 | access to local variable query | SqlInjectionDapper.cs:47:86:47:94 | access to property Text : String | SqlInjectionDapper.cs:49:47:49:51 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:47:86:47:94 | access to property Text : String | this TextBox text |
|
||||
| SqlInjectionDapper.cs:58:42:58:46 | access to local variable query | SqlInjectionDapper.cs:57:86:57:94 | access to property Text : String | SqlInjectionDapper.cs:58:42:58:46 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:57:86:57:94 | access to property Text : String | this TextBox text |
|
||||
| SqlInjectionDapper.cs:67:42:67:46 | access to local variable query | SqlInjectionDapper.cs:66:86:66:94 | access to property Text : String | SqlInjectionDapper.cs:67:42:67:46 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:66:86:66:94 | access to property Text : String | this TextBox text |
|
||||
| SqlInjectionDapper.cs:77:52:77:56 | access to local variable query | SqlInjectionDapper.cs:75:86:75:94 | access to property Text : String | SqlInjectionDapper.cs:77:52:77:56 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:75:86:75:94 | access to property Text : String | this TextBox text |
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
|
||||
namespace Test
|
||||
{
|
||||
using System.Data;
|
||||
using System.Data.Entity;
|
||||
using System.Data.SqlClient;
|
||||
using System.Web.UI.WebControls;
|
||||
using System.Threading.Tasks;
|
||||
using Dapper;
|
||||
|
||||
class SqlInjectionDapper
|
||||
{
|
||||
string connectionString;
|
||||
|
||||
public void Bad01()
|
||||
{
|
||||
using (var connection = new SqlConnection(connectionString))
|
||||
{
|
||||
var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE";
|
||||
var result = connection.Query<object>(query);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Bad02()
|
||||
{
|
||||
using (var connection = new SqlConnection(connectionString))
|
||||
{
|
||||
var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE";
|
||||
var result = await connection.QueryAsync<object>(query);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Bad03()
|
||||
{
|
||||
using (var connection = new SqlConnection(connectionString))
|
||||
{
|
||||
var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE";
|
||||
var result = await connection.QueryFirstAsync(query);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Bad04()
|
||||
{
|
||||
using (var connection = new SqlConnection(connectionString))
|
||||
{
|
||||
var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE";
|
||||
|
||||
await connection.ExecuteAsync(query);
|
||||
}
|
||||
}
|
||||
|
||||
public void Bad05()
|
||||
{
|
||||
using (var connection = new SqlConnection(connectionString))
|
||||
{
|
||||
var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE";
|
||||
connection.ExecuteScalar(query);
|
||||
}
|
||||
}
|
||||
|
||||
public void Bad06()
|
||||
{
|
||||
using (var connection = new SqlConnection(connectionString))
|
||||
{
|
||||
var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE";
|
||||
connection.ExecuteReader(query);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Bad07()
|
||||
{
|
||||
using (var connection = new SqlConnection(connectionString))
|
||||
{
|
||||
var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE";
|
||||
|
||||
var comDef = new CommandDefinition(query);
|
||||
var result = await connection.QueryFirstAsync(comDef);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Ok07()
|
||||
{
|
||||
using (var connection = new SqlConnection(connectionString))
|
||||
{
|
||||
var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE";
|
||||
|
||||
var comDef = new CommandDefinition(query);
|
||||
// no call to any query method
|
||||
}
|
||||
}
|
||||
|
||||
System.Windows.Forms.TextBox box1;
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
34
csharp/ql/test/resources/stubs/Dapper.cs
Normal file
34
csharp/ql/test/resources/stubs/Dapper.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
// This file contains auto-generated code.
|
||||
// original-extractor-options: /r:Dapper.dll /r:System.Data.SqlClient.dll ...
|
||||
|
||||
namespace Dapper
|
||||
{
|
||||
// Generated from `Dapper.CommandDefinition` in `Dapper, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null`
|
||||
public struct CommandDefinition
|
||||
{
|
||||
public CommandDefinition(string commandText, object parameters = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null, Dapper.CommandFlags flags = CommandFlags.Buffered, System.Threading.CancellationToken cancellationToken = default) => throw null;
|
||||
}
|
||||
|
||||
// Generated from `Dapper.CommandFlags` in `Dapper, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null`
|
||||
[System.Flags]
|
||||
public enum CommandFlags
|
||||
{
|
||||
None = 0x0,
|
||||
Buffered = 0x1,
|
||||
Pipelined = 0x2,
|
||||
NoCache = 0x4
|
||||
}
|
||||
|
||||
// Generated from `Dapper.SqlMapper` in `Dapper, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null`
|
||||
static public class SqlMapper
|
||||
{
|
||||
public static System.Collections.Generic.IEnumerable<T> Query<T>(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null;
|
||||
public static System.Data.IDataReader ExecuteReader(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null;
|
||||
public static System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<T>> QueryAsync<T>(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null;
|
||||
public static System.Threading.Tasks.Task<dynamic> QueryFirstAsync(this System.Data.IDbConnection cnn, Dapper.CommandDefinition command) => throw null;
|
||||
public static System.Threading.Tasks.Task<dynamic> QueryFirstAsync(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null;
|
||||
public static System.Threading.Tasks.Task<int> ExecuteAsync(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null;
|
||||
public static object ExecuteScalar(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ The following properties are supported in ``qlpack.yml`` files.
|
||||
* - ``name``
|
||||
- ``org-queries``
|
||||
- All packs
|
||||
- The name of the QL pack defined using alphanumeric characters, hyphens, and periods. It must be unique as CodeQL cannot differentiate between QL packs with identical names. If you intend to distribute the pack, prefix the name with your (or your organization's) name followed by a hyphen. Use the pack name to specify queries to run using ``database analyze`` and to define dependencies between QL packs (see examples below).- '
|
||||
- The name of the QL pack defined using alphanumeric characters, hyphens, and periods. It must be unique as CodeQL cannot differentiate between QL packs with identical names. If you intend to distribute the pack, prefix the name with your (or your organization's) name followed by a hyphen. Use the pack name to specify queries to run using ``database analyze`` and to define dependencies between QL packs (see examples below).
|
||||
* - ``version``
|
||||
- ``0.0.0``
|
||||
- All packs
|
||||
|
||||
@@ -21,11 +21,3 @@ Learn more about the files you can use when running CodeQL processes and the res
|
||||
- :doc:`SARIF output <sarif-output>`: CodeQL supports SARIF as an output format for sharing static analysis results.
|
||||
- :doc:`Exit codes <exit-codes>`: The CodeQL CLI reports the status of each command it runs as an exit code.
|
||||
This exit code provides information for subsequent commands or for other tools that rely on the CodeQL CLI.
|
||||
|
||||
.. _cli-commands:
|
||||
|
||||
CodeQL CLI manual
|
||||
-----------------
|
||||
|
||||
To view detailed information about each CodeQL CLI command,
|
||||
including its usage and options, add the ``--help`` flag or visit the "`CodeQL CLI manual <../manual>`__."
|
||||
|
||||
@@ -138,7 +138,7 @@ see ":doc:`About QL packs <about-ql-packs>`."
|
||||
|
||||
- For the queries used in a particular LGTM Enterprise release, check out the
|
||||
branch tagged with the relevant release number. For example, the branch
|
||||
tagged ``v1.23.0`` corresponds to LGTM Enterprise 1.23. You must use this
|
||||
tagged ``v1.27.0`` corresponds to LGTM Enterprise 1.27. You must use this
|
||||
version if you want to upload data to LGTM Enterprise. For further
|
||||
information, see `Preparing CodeQL databases to upload to LGTM
|
||||
<https://help.semmle.com/lgtm-enterprise/admin/help/prepare-database-upload.html>`__
|
||||
|
||||
@@ -18,4 +18,4 @@ CodeQL CLI
|
||||
|
||||
using-the-codeql-cli
|
||||
codeql-cli-reference
|
||||
|
||||
CodeQL CLI manual <https://codeql.github.com/docs/codeql-cli/manual>
|
||||
|
||||
@@ -13,20 +13,6 @@ on the query and the expected results until the actual results and the expected
|
||||
results exactly match. This topic shows you how to create test files and execute
|
||||
tests on them using the ``test run`` subcommand.
|
||||
|
||||
.. container:: toggle
|
||||
|
||||
.. container:: name
|
||||
|
||||
**Information for LGTM Enterprise 1.23 users**
|
||||
|
||||
CodeQL tests are a new feature in CodeQL CLI version 2.0.2.
|
||||
|
||||
If you are an LGTM Enterprise 1.23 user, you should still use CodeQL CLI
|
||||
version 2.0.1 to prepare databases to upload to your instance of LGTM. You
|
||||
can use version 2.0.2 (or newer) to test your custom queries, but databases
|
||||
created with versions newer than 2.0.1 are not compatible with LGTM
|
||||
Enterprise 1.23. For more information, see ":ref:`Getting started with the CodeQL CLI <using-two-versions-of-the-codeql-cli>`."
|
||||
|
||||
Setting up a test QL pack for custom queries
|
||||
--------------------------------------------
|
||||
|
||||
|
||||
@@ -58,6 +58,11 @@ Configuring settings for testing queries
|
||||
|
||||
To increase the number of threads used for testing queries, you can update the **Running Tests > Number Of Threads** setting.
|
||||
|
||||
Configuring settings for telemetry and data collection
|
||||
--------------------------------------------------------
|
||||
|
||||
You can configure whether the CodeQL extension collects telemetry data. This is disabled by default. For more information, see ":doc:`About telemetry in CodeQL for Visual Studio Code <about-telemetry-in-codeql-for-visual-studio-code>`."
|
||||
|
||||
Further reading
|
||||
----------------
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ There are two ways to do this:
|
||||
**Click to show information for LGTM Enterprise users**
|
||||
|
||||
Your local version of the CodeQL queries and libraries should match your version of LGTM Enterprise. For example, if you
|
||||
use LGTM Enterprise 1.23, then you should clone the ``1.23.0`` branch of the `starter workspace <https://github.com/github/vscode-codeql-starter/>`__ (or the appropriate ``1.23.x`` branch, corresponding to each maintenance release).
|
||||
use LGTM Enterprise 1.27, then you should clone the ``1.27.0`` branch of the `starter workspace <https://github.com/github/vscode-codeql-starter/>`__ (or the appropriate ``1.27.x`` branch, corresponding to each maintenance release).
|
||||
|
||||
This ensures that the queries and libraries you write in VS Code also work in the query console on LGTM Enterprise.
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ System and Network
|
||||
- `FileSystemWriteAccess <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Concepts.qll/type.Concepts$FileSystemWriteAccess.html>`__ -- writing to the contents of a file
|
||||
- `PersistentReadAccess <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Concepts.qll/type.Concepts$PersistentReadAccess.html>`__ -- reading from persistent storage, like cookies
|
||||
- `PersistentWriteAccess <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Concepts.qll/type.Concepts$PersistentWriteAccess.html>`__ -- writing to persistent storage
|
||||
- `RemoteFlowSource <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/security/dataflow/RemoteFlowSources.qll/type.RemoteFlowSources$RemoteFlowSource.html>`__ -- source of untrusted user input
|
||||
- `RemoteFlowSource <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/security/dataflow/RemoteFlowSources.qll/type.RemoteFlowSources$Cached$RemoteFlowSource.html>`__ -- source of untrusted user input
|
||||
- `SystemCommandExecution <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Concepts.qll/type.Concepts$SystemCommandExecution.html>`__ -- execution of a system command
|
||||
|
||||
Files
|
||||
|
||||
@@ -12,7 +12,7 @@ You can model potential sources of untrusted user input in your code without mak
|
||||
Specifying remote flow sources in external files is currently in beta and subject to change.
|
||||
|
||||
As mentioned in the :doc:`Data flow cheat sheet for JavaScript <data-flow-cheat-sheet-for-javascript>`, the CodeQL libraries for JavaScript
|
||||
provide a class `RemoteFlowSource <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/security/dataflow/RemoteFlowSources.qll/type.RemoteFlowSources$RemoteFlowSource.html>`__ to represent sources of untrusted user input, sometimes also referred to as remote flow
|
||||
provide a class `RemoteFlowSource <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/security/dataflow/RemoteFlowSources.qll/type.RemoteFlowSources$Cached$RemoteFlowSource.html>`__ to represent sources of untrusted user input, sometimes also referred to as remote flow
|
||||
sources.
|
||||
|
||||
To model a new source of untrusted input, such as a previously unmodelled library API, you can
|
||||
|
||||
@@ -385,21 +385,23 @@ For more information, see ":ref:`monotonic-aggregates`."
|
||||
Binding sets
|
||||
============
|
||||
|
||||
**Available for**: |characteristic predicates|, |member predicates|, |non-member predicates|
|
||||
**Available for**: |classes|, |characteristic predicates|, |member predicates|, |non-member predicates|
|
||||
|
||||
``bindingset[...]``
|
||||
-------------------
|
||||
|
||||
You can use this annotation to explicitly state the binding sets for a predicate. A binding set
|
||||
is a subset of the predicate's arguments such that, if those arguments are constrained to a
|
||||
finite set of values, then the predicate itself is finite (that is, it evaluates to a finite
|
||||
You can use this annotation to explicitly state the binding sets for a predicate or class. A binding set
|
||||
is a subset of a predicate's or class body's arguments such that, if those arguments are constrained to a
|
||||
finite set of values, then the predicate or class itself is finite (that is, it evaluates to a finite
|
||||
set of tuples).
|
||||
|
||||
The ``bindingset`` annotation takes a comma-separated list of variables. Each variable must be
|
||||
an argument of the predicate, possibly including ``this`` (for characteristic predicates and
|
||||
member predicates) and ``result`` (for predicates that return a result).
|
||||
The ``bindingset`` annotation takes a comma-separated list of variables.
|
||||
|
||||
For more information, see ":ref:`predicate-binding`."
|
||||
- When you annotate a predicate, each variable must be an argument of the predicate, possibly including ``this``
|
||||
(for characteristic predicates and member predicates) and ``result`` (for predicates that return a result).
|
||||
For more information, see ":ref:`predicate-binding`."
|
||||
- When you annotate a class, each variable must be ``this`` or a field in the class.
|
||||
Binding sets for classes are supported from release 2.3.0 of the CodeQL CLI, and release 1.26 of LGTM Enterprise.
|
||||
|
||||
.. Links to use in substitutions
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
C#,C# up to 8.0,"Microsoft Visual Studio up to 2019 with .NET up to 4.8,
|
||||
|
||||
.NET Core up to 3.1","``.sln``, ``.csproj``, ``.cs``, ``.cshtml``, ``.xaml``"
|
||||
Go (aka Golang), "Go up to 1.15", "Go 1.11 or more recent", ``.go``
|
||||
Go (aka Golang), "Go up to 1.16", "Go 1.11 or more recent", ``.go``
|
||||
Java,"Java 7 to 15 [3]_","javac (OpenJDK and Oracle JDK),
|
||||
|
||||
Eclipse compiler for Java (ECJ) [4]_",``.java``
|
||||
|
||||
@@ -638,7 +638,21 @@ class BooleanLiteral extends Literal, @booleanliteral {
|
||||
override string getAPrimaryQlClass() { result = "BooleanLiteral" }
|
||||
}
|
||||
|
||||
/** An integer literal. For example, `23`. */
|
||||
/**
|
||||
* An integer literal. For example, `23`.
|
||||
*
|
||||
* An integer literal can never be negative except when:
|
||||
* - It is written in binary, octal or hexadecimal notation
|
||||
* - It is written in decimal notation, has the value `2147483648` and is preceded
|
||||
* by a minus; in this case the value of the IntegerLiteral is -2147483648 and
|
||||
* the preceding minus will *not* be modeled as `MinusExpr`.
|
||||
*
|
||||
* In all other cases the preceding minus, if any, will be modeled as a separate
|
||||
* `MinusExpr`.
|
||||
*
|
||||
* The last exception is necessary because `2147483648` on its own would not be
|
||||
* a valid integer literal (and could also not be parsed as CodeQL `int`).
|
||||
*/
|
||||
class IntegerLiteral extends Literal, @integerliteral {
|
||||
/** Gets the int representation of this literal. */
|
||||
int getIntValue() { result = getValue().toInt() }
|
||||
@@ -646,12 +660,32 @@ class IntegerLiteral extends Literal, @integerliteral {
|
||||
override string getAPrimaryQlClass() { result = "IntegerLiteral" }
|
||||
}
|
||||
|
||||
/** A long literal. For example, `23l`. */
|
||||
/**
|
||||
* A long literal. For example, `23L`.
|
||||
*
|
||||
* A long literal can never be negative except when:
|
||||
* - It is written in binary, octal or hexadecimal notation
|
||||
* - It is written in decimal notation, has the value `9223372036854775808` and
|
||||
* is preceded by a minus; in this case the value of the LongLiteral is
|
||||
* -9223372036854775808 and the preceding minus will *not* be modeled as
|
||||
* `MinusExpr`.
|
||||
*
|
||||
* In all other cases the preceding minus, if any, will be modeled as a separate
|
||||
* `MinusExpr`.
|
||||
*
|
||||
* The last exception is necessary because `9223372036854775808` on its own
|
||||
* would not be a valid long literal.
|
||||
*/
|
||||
class LongLiteral extends Literal, @longliteral {
|
||||
override string getAPrimaryQlClass() { result = "LongLiteral" }
|
||||
}
|
||||
|
||||
/** A floating point literal. For example, `4.2f`. */
|
||||
/**
|
||||
* A float literal. For example, `4.2f`.
|
||||
*
|
||||
* A float literal is never negative; a preceding minus, if any, will always
|
||||
* be modeled as separate `MinusExpr`.
|
||||
*/
|
||||
class FloatingPointLiteral extends Literal, @floatingpointliteral {
|
||||
/**
|
||||
* Gets the value of this literal as CodeQL 64-bit `float`. The value will
|
||||
@@ -662,7 +696,12 @@ class FloatingPointLiteral extends Literal, @floatingpointliteral {
|
||||
override string getAPrimaryQlClass() { result = "FloatingPointLiteral" }
|
||||
}
|
||||
|
||||
/** A double literal. For example, `4.2`. */
|
||||
/**
|
||||
* A double literal. For example, `4.2`.
|
||||
*
|
||||
* A double literal is never negative; a preceding minus, if any, will always
|
||||
* be modeled as separate `MinusExpr`.
|
||||
*/
|
||||
class DoubleLiteral extends Literal, @doubleliteral {
|
||||
/**
|
||||
* Gets the value of this literal as CodeQL 64-bit `float`. The result will
|
||||
|
||||
@@ -80,19 +80,15 @@ class Node extends TNode {
|
||||
result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getType()
|
||||
}
|
||||
|
||||
private Callable getEnclosingCallableImpl() {
|
||||
/** Gets the callable in which this node occurs. */
|
||||
Callable getEnclosingCallable() {
|
||||
result = this.asExpr().getEnclosingCallable() or
|
||||
result = this.asParameter().getCallable() or
|
||||
result = this.(ImplicitVarargsArray).getCall().getEnclosingCallable() or
|
||||
result = this.(InstanceParameterNode).getCallable() or
|
||||
result = this.(ImplicitInstanceAccess).getInstanceAccess().getEnclosingCallable() or
|
||||
result = this.(MallocNode).getClassInstanceExpr().getEnclosingCallable() or
|
||||
result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getEnclosingCallableImpl()
|
||||
}
|
||||
|
||||
/** Gets the callable in which this node occurs. */
|
||||
Callable getEnclosingCallable() {
|
||||
result = unique(DataFlowCallable c | c = this.getEnclosingCallableImpl() | c)
|
||||
result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getEnclosingCallable()
|
||||
}
|
||||
|
||||
private Type getImprovedTypeBound() {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
class NumericLiterals {
|
||||
void negativeLiterals() {
|
||||
float f = -1f;
|
||||
double d = -1d;
|
||||
int i1 = -2147483647;
|
||||
int i2 = -2147483648; // CodeQL models minus as part of literal
|
||||
int i3 = -0b10000000000000000000000000000000; // binary
|
||||
int i4 = -020000000000; // octal
|
||||
int i5 = -0x80000000; // hex
|
||||
long l1 = -9223372036854775807L;
|
||||
long l2 = -9223372036854775808L; // CodeQL models minus as part of literal
|
||||
long l3 = -0b1000000000000000000000000000000000000000000000000000000000000000L; // binary
|
||||
long l4 = -01000000000000000000000L; // octal
|
||||
long l5 = -0x8000000000000000L; // hex
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
| NumericLiterals.java:3:14:3:15 | 1f | 1.0 | NumericLiterals.java:3:13:3:15 | -... |
|
||||
| NumericLiterals.java:4:15:4:16 | 1d | 1.0 | NumericLiterals.java:4:14:4:16 | -... |
|
||||
| NumericLiterals.java:5:13:5:22 | 2147483647 | 2147483647 | NumericLiterals.java:5:12:5:22 | -... |
|
||||
| NumericLiterals.java:6:12:6:22 | -2147483648 | -2147483648 | NumericLiterals.java:6:7:6:22 | i2 |
|
||||
| NumericLiterals.java:7:13:7:46 | 0b10000000000000000000000000000000 | -2147483648 | NumericLiterals.java:7:12:7:46 | -... |
|
||||
| NumericLiterals.java:8:13:8:24 | 020000000000 | -2147483648 | NumericLiterals.java:8:12:8:24 | -... |
|
||||
| NumericLiterals.java:9:13:9:22 | 0x80000000 | -2147483648 | NumericLiterals.java:9:12:9:22 | -... |
|
||||
| NumericLiterals.java:10:14:10:33 | 9223372036854775807L | 9223372036854775807 | NumericLiterals.java:10:13:10:33 | -... |
|
||||
| NumericLiterals.java:11:13:11:33 | -9223372036854775808L | -9223372036854775808 | NumericLiterals.java:11:8:11:33 | l2 |
|
||||
| NumericLiterals.java:12:14:12:80 | 0b1000000000000000000000000000000000000000000000000000000000000000L | -9223372036854775808 | NumericLiterals.java:12:13:12:80 | -... |
|
||||
| NumericLiterals.java:13:14:13:37 | 01000000000000000000000L | -9223372036854775808 | NumericLiterals.java:13:13:13:37 | -... |
|
||||
| NumericLiterals.java:14:14:14:32 | 0x8000000000000000L | -9223372036854775808 | NumericLiterals.java:14:13:14:32 | -... |
|
||||
@@ -0,0 +1,9 @@
|
||||
import java
|
||||
|
||||
from Literal l
|
||||
where
|
||||
l instanceof IntegerLiteral or
|
||||
l instanceof LongLiteral or
|
||||
l instanceof FloatingPointLiteral or
|
||||
l instanceof DoubleLiteral
|
||||
select l, l.getValue(), l.getParent()
|
||||
3
javascript/change-notes/2021-03-30-sql-models.md
Normal file
3
javascript/change-notes/2021-03-30-sql-models.md
Normal file
@@ -0,0 +1,3 @@
|
||||
lgtm,codescanning
|
||||
* The SQL library models for `mysql`, `mysql2`, `mssql`, `pg`, `sqlite3`, `sequelize`, and `@google-cloud/spanner` have improved,
|
||||
leading to more SQL injection sinks.
|
||||
@@ -0,0 +1,5 @@
|
||||
lgtm,codescanning
|
||||
* Fixed a bug which caused some imports to be resolved incorrectly
|
||||
for projects containing multiple `tsconfig.json` files.
|
||||
* Fixed a bug which could cause some files in the `node_modules` folder
|
||||
to be extracted even though they should be excluded.
|
||||
3
javascript/change-notes/2021-04-08-redux.md
Normal file
3
javascript/change-notes/2021-04-08-redux.md
Normal file
@@ -0,0 +1,3 @@
|
||||
lgtm,codescanning
|
||||
* Support for Redux has improved. The security queries can now track taint through reducer functions and state managed by Redux.
|
||||
Affected packages are `redux`, `react-redux`, `@reduxjs/toolkit`, `redux-actions`, `redux-persist`, `reduce-reducers`, `redux-immutable`, and `immer`.
|
||||
@@ -21,7 +21,7 @@ public class ParsedProject {
|
||||
|
||||
/** Absolute paths to the files included in this project. */
|
||||
public Set<File> getOwnFiles() {
|
||||
return allFiles;
|
||||
return ownFiles;
|
||||
}
|
||||
|
||||
/** Absolute paths to the files included in or referenced by this project. */
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Finds cases where the 'userId' field in a request to another service
|
||||
* is an arbitrary user-controlled value, indicating lack of authentication.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @tags security
|
||||
* @id js/examples/backend-idor
|
||||
*/
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Tracks the return value of 'escapeHtml' into 'decodeURI', indicating
|
||||
* an ineffective sanitization attempt.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @tags security
|
||||
* @id js/examples/decoding-after-sanitization
|
||||
*/
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Tracks the return value of an HTML sanitizer into an escape-sequence decoder,
|
||||
* indicating an ineffective sanitization attempt.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @tags security
|
||||
* @id js/examples/decoding-after-sanitization-generalized
|
||||
*/
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Tracks user-controlled values into 'eval' calls (special case of js/code-injection),
|
||||
* and generates a visualizable path from the source to the sink.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @tags security
|
||||
* @id js/examples/eval-taint-path
|
||||
*/
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Tracks values from an 'authKey' property into a postMessage call with unrestricted origin,
|
||||
* indicating a leak of sensitive information.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @tags security
|
||||
* @id js/examples/information-disclosure
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* @name Extension of standard query: Stored XSS
|
||||
* @description Extends the standard Stored XSS query with an additional source.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @tags security
|
||||
* @id js/examples/stored-xss
|
||||
*/
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Extends the standard Stored XSS query with an additional source,
|
||||
* using TrackedNode to track MySQL connections globally.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @tags security
|
||||
* @id js/examples/stored-xss-trackednode
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* @name Template injection
|
||||
* @description Tracks user-controlled values to an unescaped lodash template placeholder.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @tags security
|
||||
* @id js/examples/template-injection
|
||||
*/
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* @kind treemap
|
||||
* @treemap.warnOn highValues
|
||||
* @metricType externalDependency
|
||||
* @precision medium
|
||||
* @id js/external-dependencies
|
||||
*/
|
||||
|
||||
|
||||
@@ -15,13 +15,8 @@ import javascript
|
||||
import semmle.javascript.security.dataflow.DomBasedXss::DomBasedXss
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where
|
||||
(
|
||||
cfg instanceof HtmlInjectionConfiguration or
|
||||
cfg instanceof JQueryHtmlOrSelectorInjectionConfiguration
|
||||
) and
|
||||
cfg.hasFlowPath(source, sink)
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink,
|
||||
sink.getNode().(Sink).getVulnerabilityKind() + " vulnerability due to $@.", source.getNode(),
|
||||
"user-provided value"
|
||||
|
||||
11
javascript/ql/src/Summary/LinesOfCode.ql
Normal file
11
javascript/ql/src/Summary/LinesOfCode.ql
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @id js/summary/lines-of-code
|
||||
* @name Total lines of JavaScript and TypeScript code in the database
|
||||
* @description The total number of lines of JavaScript or TypeScript code across all files checked into the repository, except in `node_modules`. This is a useful metric of the size of a database. For all files that were seen during extraction, this query counts the lines of code, excluding whitespace or comments.
|
||||
* @kind metric
|
||||
* @tags summary
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
select sum(File f | not f.getATopLevel().isExterns() | f.getNumberOfLinesOfCode())
|
||||
@@ -5,7 +5,7 @@
|
||||
* user-controlled exit node of portal `p1` to an escaping entry node of portal `p2`,
|
||||
* and have label `lbl2` at that point. Moreover, the path from `p1` to `p2` contains
|
||||
* no sanitizers specified by configuration `cfg`.
|
||||
* @kind flow-step-summary
|
||||
* @kind table
|
||||
* @id js/step-summary-extraction
|
||||
*/
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @description Extracts sink summaries, that is, tuples `(p, lbl, cfg)` representing the fact
|
||||
* that data with flow label `lbl` may flow from a user-controlled exit node of portal
|
||||
* `p` to a known sink for configuration `cfg`.
|
||||
* @kind sink-summary
|
||||
* @kind table
|
||||
* @id js/sink-summary-extraction
|
||||
*/
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @description Extracts source summaries, that is, tuples `(p, lbl, cfg)` representing the fact
|
||||
* that data may flow from a known source for configuration `cfg` to an escaping entry
|
||||
* node of portal `p`, and have flow label `lbl` at that point.
|
||||
* @kind source-summary
|
||||
* @kind table
|
||||
* @id js/source-summary-extraction
|
||||
*/
|
||||
|
||||
|
||||
@@ -105,6 +105,7 @@ import semmle.javascript.frameworks.PropertyProjection
|
||||
import semmle.javascript.frameworks.Puppeteer
|
||||
import semmle.javascript.frameworks.React
|
||||
import semmle.javascript.frameworks.ReactNative
|
||||
import semmle.javascript.frameworks.Redux
|
||||
import semmle.javascript.frameworks.Request
|
||||
import semmle.javascript.frameworks.RxJS
|
||||
import semmle.javascript.frameworks.ServerLess
|
||||
|
||||
15
javascript/ql/src/meta/alerts/ImportGraph.ql
Normal file
15
javascript/ql/src/meta/alerts/ImportGraph.ql
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @name Import graph
|
||||
* @description An edge in the import graph.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @id js/meta/alerts/import-graph
|
||||
* @tags meta
|
||||
* @precision very-low
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
from Import imprt, Module target
|
||||
where target = imprt.getImportedModule()
|
||||
select imprt, "Import targeting $@", target, target.getFile().getRelativePath()
|
||||
@@ -307,7 +307,7 @@ class ReachableBasicBlock extends BasicBlock {
|
||||
/**
|
||||
* Holds if this basic block strictly dominates `bb`.
|
||||
*/
|
||||
cached
|
||||
pragma[inline]
|
||||
predicate strictlyDominates(ReachableBasicBlock bb) { bbIDominates+(this, bb) }
|
||||
|
||||
/**
|
||||
@@ -315,15 +315,13 @@ class ReachableBasicBlock extends BasicBlock {
|
||||
*
|
||||
* This predicate is reflexive: each reachable basic block dominates itself.
|
||||
*/
|
||||
predicate dominates(ReachableBasicBlock bb) {
|
||||
bb = this or
|
||||
strictlyDominates(bb)
|
||||
}
|
||||
pragma[inline]
|
||||
predicate dominates(ReachableBasicBlock bb) { bbIDominates*(this, bb) }
|
||||
|
||||
/**
|
||||
* Holds if this basic block strictly post-dominates `bb`.
|
||||
*/
|
||||
cached
|
||||
pragma[inline]
|
||||
predicate strictlyPostDominates(ReachableBasicBlock bb) { bbIPostDominates+(this, bb) }
|
||||
|
||||
/**
|
||||
@@ -331,10 +329,8 @@ class ReachableBasicBlock extends BasicBlock {
|
||||
*
|
||||
* This predicate is reflexive: each reachable basic block post-dominates itself.
|
||||
*/
|
||||
predicate postDominates(ReachableBasicBlock bb) {
|
||||
bb = this or
|
||||
strictlyPostDominates(bb)
|
||||
}
|
||||
pragma[inline]
|
||||
predicate postDominates(ReachableBasicBlock bb) { bbIPostDominates*(this, bb) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user