mirror of
https://github.com/github/codeql.git
synced 2026-07-05 03:25:31 +02:00
Compare commits
162 Commits
sauyon/jav
...
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 | ||
|
|
95ac2c8edd | ||
|
|
ab3edf37d7 | ||
|
|
43306f4700 | ||
|
|
57784dc746 | ||
|
|
4f9b6d1192 | ||
|
|
f8bbda0cdc | ||
|
|
9db235ac36 | ||
|
|
35f294f096 | ||
|
|
93500bd95a | ||
|
|
95937c9ac7 | ||
|
|
0b21b273ed | ||
|
|
937a620f4d | ||
|
|
e345064a53 | ||
|
|
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,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()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.PointerWrapper
|
||||
|
||||
/**
|
||||
@@ -14,6 +15,23 @@ private class UniqueOrSharedPtr extends Class, PointerWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
/** 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()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::make_shared` and `std::make_unique` template functions.
|
||||
*/
|
||||
|
||||
@@ -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 | |
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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.
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>`__."
|
||||
|
||||
@@ -18,4 +18,4 @@ CodeQL CLI
|
||||
|
||||
using-the-codeql-cli
|
||||
codeql-cli-reference
|
||||
|
||||
CodeQL CLI manual <https://codeql.github.com/docs/codeql-cli/manual>
|
||||
|
||||
@@ -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
|
||||
----------------
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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``
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* Added additional taint steps modeling the Spring StringUtils class (`org.springframework.util.StringUtils`).
|
||||
@@ -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() {
|
||||
|
||||
@@ -32,7 +32,6 @@ import semmle.code.java.frameworks.spring.SpringQualifier
|
||||
import semmle.code.java.frameworks.spring.SpringRef
|
||||
import semmle.code.java.frameworks.spring.SpringReplacedMethod
|
||||
import semmle.code.java.frameworks.spring.SpringSet
|
||||
import semmle.code.java.frameworks.spring.SpringStringUtils
|
||||
import semmle.code.java.frameworks.spring.SpringValue
|
||||
import semmle.code.java.frameworks.spring.SpringXMLElement
|
||||
import semmle.code.java.frameworks.spring.metrics.MetricSpringBean
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
/** Definitions of flow steps through utility methods of `org.springframework.util.SpringUtils`. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
private class SpringStringUtilsModel extends SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
"org.springframework.util;StringUtils;false;addStringToArray;;;Argument[0..1];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;applyRelativePath;;;Argument[0..1];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;arrayToCommaDelimitedString;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;arrayToDelimitedString;;;Argument[0..1];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;capitalize;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;cleanPath;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;collectionToCommaDelimitedString;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;collectionToDelimitedString;;;Argument[0..1];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;collectionToDelimitedString;(java.util.Collection,java.lang.String,java.lang.String,java.lang.String);;Argument[2..3];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;commaDelimitedListToSet;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;commaDelimitedListToStringArray;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;concatenateStringArrays;;;Argument[0..1];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;delete;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;deleteAny;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;delimitedListToStringArray;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;getFilename;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;getFilenameExtension;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;mergeStringArrays;;;Argument[0..1];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;quote;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;quoteIfString;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;removeDuplicateStrings;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;replace;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;replace;;;Argument[2];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;sortStringArray;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;split;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;splitArrayElementsIntoProperties;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;stripFilenameExtension;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;tokenizeToStringArray;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;toStringArray;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;trimAllWhitespace;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;trimArrayElements;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;trimLeadingCharacter;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;trimLeadingWhitespace;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;trimTrailingCharacter;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;trimTrailingWhitespace;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;trimWhitespace;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;uncapitalize;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;unqualify;;;Argument[0];ReturnValue;taint",
|
||||
"org.springframework.util;StringUtils;false;uriDecode;;;Argument[0];ReturnValue;taint"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Locale;
|
||||
import java.lang.String;
|
||||
|
||||
class StringUtilsTest {
|
||||
String taint() { return "tainted"; }
|
||||
|
||||
String[] taintArray() { return null; }
|
||||
|
||||
Locale taintLocale() { return null; }
|
||||
|
||||
Collection<String> taintedCollection() { return null; }
|
||||
|
||||
Enumeration<String> taintedEnumeration() { return null; }
|
||||
|
||||
void sink(Object o) {}
|
||||
|
||||
void test() throws Exception {
|
||||
sink(StringUtils.addStringToArray(null, taint())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.addStringToArray(taintArray(), "")); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.applyRelativePath("/", taint())); // $hasTaintFlow
|
||||
sink(StringUtils.applyRelativePath(taint(), "../../test")); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.arrayToCommaDelimitedString(taintArray())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.arrayToDelimitedString(taintArray(), ":")); // $hasTaintFlow
|
||||
sink(StringUtils.arrayToDelimitedString(null, taint())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.capitalize(taint())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.cleanPath(taint())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.collectionToCommaDelimitedString(taintedCollection())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.collectionToDelimitedString(taintedCollection(), ":")); // $hasTaintFlow
|
||||
sink(StringUtils.collectionToDelimitedString(null, taint())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.collectionToDelimitedString(taintedCollection(), ":", "", "")); // $hasTaintFlow
|
||||
sink(StringUtils.collectionToDelimitedString(null, taint(), "", "")); // $hasTaintFlow
|
||||
sink(StringUtils.collectionToDelimitedString(null, ":", taint(), "")); // $hasTaintFlow
|
||||
sink(StringUtils.collectionToDelimitedString(null, ":", "", taint())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.commaDelimitedListToSet(taint())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.commaDelimitedListToStringArray(taint())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.concatenateStringArrays(taintArray(), null)); // $hasTaintFlow
|
||||
sink(StringUtils.concatenateStringArrays(null, taintArray())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.delete(taint(), "")); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.deleteAny(taint(), "")); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.delimitedListToStringArray(taint(), ":")); // $hasTaintFlow
|
||||
sink(StringUtils.delimitedListToStringArray(taint(), ":", ".")); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.getFilename(taint())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.getFilenameExtension(taint())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.mergeStringArrays(taintArray(), null)); // $hasTaintFlow
|
||||
sink(StringUtils.mergeStringArrays(null, taintArray())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.parseLocale(taint()));
|
||||
|
||||
sink(StringUtils.parseLocaleString(taint()));
|
||||
|
||||
sink(StringUtils.parseTimeZoneString(taint()));
|
||||
|
||||
sink(StringUtils.quote(taint())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.quoteIfString(taint())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.removeDuplicateStrings(taintArray())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.replace(taint(), "", "")); // $hasTaintFlow
|
||||
sink(StringUtils.replace("", "", taint())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.sortStringArray(taintArray())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.split(taint(), "")); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.splitArrayElementsIntoProperties(taintArray(), "")); // $hasTaintFlow
|
||||
sink(StringUtils.splitArrayElementsIntoProperties(taintArray(), "", "")); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.stripFilenameExtension(taint())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.tokenizeToStringArray(taint(), "")); // $hasTaintFlow
|
||||
sink(StringUtils.tokenizeToStringArray(taint(), "", true, true)); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.toLanguageTag(taintLocale()));
|
||||
|
||||
sink(StringUtils.toStringArray(taintedCollection())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.toStringArray(taintedEnumeration())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.trimAllWhitespace(taint())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.trimArrayElements(taintArray())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.trimLeadingCharacter(taint(), 'a')); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.trimLeadingWhitespace(taint())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.trimTrailingCharacter(taint(), 'a')); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.trimTrailingWhitespace(taint())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.trimWhitespace(taint())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.uncapitalize(taint())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.unqualify(taint())); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.unqualify(taint(), '.')); // $hasTaintFlow
|
||||
|
||||
sink(StringUtils.uriDecode(taint(), java.nio.charset.StandardCharsets.UTF_8)); // $hasTaintFlow
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import java
|
||||
import semmle.code.java.frameworks.spring.Spring
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
class TaintFlowConf extends TaintTracking::Configuration {
|
||||
TaintFlowConf() { this = "qltest:frameworks:spring-taint-flow" }
|
||||
|
||||
override predicate isSource(DataFlow::Node n) {
|
||||
exists(string name | name.matches("taint%") |
|
||||
n.asExpr().(MethodAccess).getMethod().hasName(name)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node n) {
|
||||
exists(MethodAccess ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument())
|
||||
}
|
||||
}
|
||||
|
||||
class ValueFlowConf extends DataFlow::Configuration {
|
||||
ValueFlowConf() { this = "qltest:frameworks:spring-value-flow" }
|
||||
|
||||
override predicate isSource(DataFlow::Node n) {
|
||||
n.asExpr().(MethodAccess).getMethod().hasName("taint")
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node n) {
|
||||
exists(MethodAccess ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument())
|
||||
}
|
||||
}
|
||||
|
||||
class HasFlowTest extends InlineExpectationsTest {
|
||||
HasFlowTest() { this = "HasFlowTest" }
|
||||
|
||||
override string getARelevantTag() { result = ["hasTaintFlow", "hasValueFlow"] }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "hasTaintFlow" and
|
||||
exists(DataFlow::Node src, DataFlow::Node sink, TaintFlowConf conf | conf.hasFlow(src, sink) |
|
||||
not any(ValueFlowConf vconf).hasFlow(src, sink) and
|
||||
sink.getLocation() = location and
|
||||
element = sink.toString() and
|
||||
value = ""
|
||||
)
|
||||
or
|
||||
tag = "hasValueFlow" and
|
||||
exists(DataFlow::Node src, DataFlow::Node sink, ValueFlowConf conf | conf.hasFlow(src, sink) |
|
||||
sink.getLocation() = location and
|
||||
element = sink.toString() and
|
||||
value = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/springframework-5.2.3
|
||||
@@ -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()
|
||||
@@ -1,270 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.util;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
public abstract class StringUtils {
|
||||
public static boolean isEmpty(@Nullable Object str) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean hasLength(@Nullable CharSequence str) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean hasLength(@Nullable String str) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean hasText(@Nullable CharSequence str) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean hasText(@Nullable String str) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean containsWhitespace(@Nullable CharSequence str) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean containsWhitespace(@Nullable String str) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String trimWhitespace(String str) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String trimAllWhitespace(String str) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String trimLeadingWhitespace(String str) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String trimTrailingWhitespace(String str) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String trimLeadingCharacter(String str, char leadingCharacter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String trimTrailingCharacter(String str, char trailingCharacter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean startsWithIgnoreCase(@Nullable String str, @Nullable String prefix) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean endsWithIgnoreCase(@Nullable String str, @Nullable String suffix) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean substringMatch(CharSequence str, int index, CharSequence substring) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int countOccurrencesOf(String str, String sub) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static String replace(String inString, String oldPattern, @Nullable String newPattern) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String delete(String inString, String pattern) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String deleteAny(String inString, @Nullable String charsToDelete) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String quote(@Nullable String str) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Object quoteIfString(@Nullable Object obj) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String unqualify(String qualifiedName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String unqualify(String qualifiedName, char separator) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String capitalize(String str) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String uncapitalize(String str) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getFilename(@Nullable String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getFilenameExtension(@Nullable String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String stripFilenameExtension(String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String applyRelativePath(String path, String relativePath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String cleanPath(String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean pathEquals(String path1, String path2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String uriDecode(String source, Charset charset) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Locale parseLocale(String localeValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Locale parseLocaleString(String localeString) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String toLanguageTag(Locale locale) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static TimeZone parseTimeZoneString(String timeZoneString) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String[] toStringArray(@Nullable Collection<String> collection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String[] toStringArray(@Nullable Enumeration<String> enumeration) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String[] addStringToArray(@Nullable String[] array, String str) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String[] concatenateStringArrays(
|
||||
@Nullable String[] array1, @Nullable String[] array2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String[] mergeStringArrays(@Nullable String[] array1, @Nullable String[] array2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String[] sortStringArray(String[] array) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String[] trimArrayElements(String[] array) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String[] removeDuplicateStrings(String[] array) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String[] split(@Nullable String toSplit, @Nullable String delimiter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Properties splitArrayElementsIntoProperties(String[] array, String delimiter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Properties splitArrayElementsIntoProperties(
|
||||
String[] array, String delimiter, @Nullable String charsToDelete) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String[] tokenizeToStringArray(@Nullable String str, String delimiters) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String[] tokenizeToStringArray(
|
||||
@Nullable String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String[] delimitedListToStringArray(
|
||||
@Nullable String str, @Nullable String delimiter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String[] delimitedListToStringArray(
|
||||
@Nullable String str, @Nullable String delimiter, @Nullable String charsToDelete) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String[] commaDelimitedListToStringArray(@Nullable String str) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Set<String> commaDelimitedListToSet(@Nullable String str) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String collectionToDelimitedString(
|
||||
@Nullable Collection<?> coll, String delim, String prefix, String suffix) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String collectionToDelimitedString(@Nullable Collection<?> coll, String delim) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String collectionToCommaDelimitedString(@Nullable Collection<?> coll) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String arrayToDelimitedString(@Nullable Object[] arr, String delim) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String arrayToCommaDelimitedString(@Nullable Object[] arr) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
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. */
|
||||
|
||||
@@ -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())
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -51,7 +51,7 @@ class PackageJSON extends JSONObject {
|
||||
string getAFile() { result = getFiles().getElementStringValue(_) }
|
||||
|
||||
/** Gets the main module of this package. */
|
||||
string getMain() { result = getPropStringValue("main") }
|
||||
string getMain() { result = MainModulePath::of(this).getValue() }
|
||||
|
||||
/** Gets the path of a command defined for this package. */
|
||||
string getBin(string cmd) {
|
||||
|
||||
@@ -80,6 +80,13 @@ File tryExtensions(Folder dir, string basename, int priority) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets `name` without a file extension.
|
||||
* Or `name`, if `name` has no file extension.
|
||||
*/
|
||||
bindingset[name]
|
||||
private string getStem(string name) { result = name.regexpCapture("(.+?)(?:\\.([^.]+))?", 1) }
|
||||
|
||||
/**
|
||||
* Gets the main module described by `pkg` with the given `priority`.
|
||||
*/
|
||||
@@ -90,9 +97,8 @@ File resolveMainModule(PackageJSON pkg, int priority) {
|
||||
result = tryExtensions(main.resolve(), "index", priority)
|
||||
or
|
||||
not exists(main.resolve()) and
|
||||
not exists(main.getExtension()) and
|
||||
exists(int n | n = main.getNumComponent() |
|
||||
result = tryExtensions(main.resolveUpTo(n - 1), main.getComponent(n - 1), priority)
|
||||
result = tryExtensions(main.resolveUpTo(n - 1), getStem(main.getComponent(n - 1)), priority)
|
||||
)
|
||||
)
|
||||
or
|
||||
@@ -101,6 +107,27 @@ File resolveMainModule(PackageJSON pkg, int priority) {
|
||||
tryExtensions([folder, folder.getChildContainer(["src", "lib"])], "index",
|
||||
priority - prioritiesPerCandidate())
|
||||
)
|
||||
or
|
||||
// if there is no main module, then we look for files that are explicitly included in the published package.
|
||||
exists(PathExpr file |
|
||||
// `FilesPath` only exists if there is no main module for a given package.
|
||||
file = FilesPath::of(pkg) and priority = 100 // fixing the priority, because there might be multiple files in the package.
|
||||
|
|
||||
result = file.resolve()
|
||||
or
|
||||
result = min(int i, File f | f = tryExtensions(file.resolve(), "index", i) | f order by i)
|
||||
or
|
||||
// resolve "file.js" to e.g. "file.ts".
|
||||
not exists(file.resolve()) and
|
||||
exists(int n | n = file.getNumComponent() |
|
||||
result =
|
||||
min(int i, File res |
|
||||
res = tryExtensions(file.resolveUpTo(n - 1), getStem(file.getComponent(n - 1)), i)
|
||||
|
|
||||
res order by i
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,3 +153,31 @@ class MainModulePath extends PathExpr, @json_string {
|
||||
module MainModulePath {
|
||||
MainModulePath of(PackageJSON pkg) { result.getPackageJSON() = pkg }
|
||||
}
|
||||
|
||||
/**
|
||||
* A JSON string in a `package.json` file specifying a file that should be included in the published package.
|
||||
* These files are often imported directly from a client when a "main" module is not specified.
|
||||
* For performance reasons this only exists if there is no "main" field in the `package.json` file.
|
||||
*/
|
||||
private class FilesPath extends PathExpr, @json_string {
|
||||
PackageJSON pkg;
|
||||
|
||||
FilesPath() {
|
||||
this = pkg.getPropValue("files").(JSONArray).getElementValue(_) and
|
||||
not exists(MainModulePath::of(pkg))
|
||||
}
|
||||
|
||||
/** Gets the `package.json` file in which this path occurs. */
|
||||
PackageJSON getPackageJSON() { result = pkg }
|
||||
|
||||
override string getValue() { result = this.(JSONString).getValue() }
|
||||
|
||||
override Folder getAdditionalSearchRoot(int priority) {
|
||||
priority = 0 and
|
||||
result = pkg.getFile().getParentContainer()
|
||||
}
|
||||
}
|
||||
|
||||
private module FilesPath {
|
||||
FilesPath of(PackageJSON pkg) { result.getPackageJSON() = pkg }
|
||||
}
|
||||
|
||||
@@ -606,10 +606,10 @@ module RangeAnalysis {
|
||||
cfg2BB = cfg2.getBasicBlock() and
|
||||
cfg2RBB = cfg2BB.(ReachableBasicBlock) and
|
||||
(
|
||||
cfg1RBB.strictlyDominates(cfg2BB) and
|
||||
cfg2BB.getImmediateDominator+() = cfg1RBB and
|
||||
cfg = cfg2
|
||||
or
|
||||
cfg2RBB.strictlyDominates(cfg1RBB) and
|
||||
cfg1BB.getImmediateDominator+() = cfg2BB and
|
||||
cfg = cfg1
|
||||
)
|
||||
)
|
||||
@@ -681,7 +681,7 @@ module RangeAnalysis {
|
||||
midBB = midcfg.getBasicBlock() and
|
||||
midRBB = midBB.(ReachableBasicBlock) and
|
||||
cfgBB = cfg.getBasicBlock() and
|
||||
midRBB.strictlyDominates(cfgBB)
|
||||
cfgBB.getImmediateDominator+() = midRBB
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
10
javascript/ql/src/semmle/javascript/Unit.qll
Normal file
10
javascript/ql/src/semmle/javascript/Unit.qll
Normal file
@@ -0,0 +1,10 @@
|
||||
/** Provides the `Unit` class. */
|
||||
|
||||
/** The unit type. */
|
||||
private newtype TUnit = TMkUnit()
|
||||
|
||||
/** The trivial type with a single element. */
|
||||
class Unit extends TUnit {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "Unit" }
|
||||
}
|
||||
@@ -72,7 +72,7 @@ private import javascript
|
||||
private import internal.FlowSteps
|
||||
private import internal.AccessPaths
|
||||
private import internal.CallGraphs
|
||||
private import internal.Unit
|
||||
private import semmle.javascript.Unit
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
|
||||
/**
|
||||
|
||||
@@ -1683,4 +1683,59 @@ module DataFlow {
|
||||
import TypeTracking
|
||||
|
||||
predicate localTaintStep = TaintTracking::localTaintStep/2;
|
||||
|
||||
/**
|
||||
* Holds if the function in `succ` forwards all its arguments to a call to `pred` and returns
|
||||
* its result. This can thus be seen as a step `pred -> succ` used for tracking function values
|
||||
* through "wrapper functions", since the `succ` function partially replicates behavior of `pred`.
|
||||
*
|
||||
* Examples:
|
||||
* ```js
|
||||
* function f(x) {
|
||||
* return g(x); // step: g -> f
|
||||
* }
|
||||
*
|
||||
* function doExec(x) {
|
||||
* console.log(x);
|
||||
* return exec(x); // step: exec -> doExec
|
||||
* }
|
||||
*
|
||||
* function doEither(x, y) {
|
||||
* if (x > y) {
|
||||
* return foo(x, y); // step: foo -> doEither
|
||||
* } else {
|
||||
* return bar(x, y); // step: bar -> doEither
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* function wrapWithLogging(f) {
|
||||
* return (x) => {
|
||||
* console.log(x);
|
||||
* return f(x); // step: f -> anonymous function
|
||||
* }
|
||||
* }
|
||||
* wrapWithLogging(g); // step: g -> wrapWithLogging(g)
|
||||
* ```
|
||||
*/
|
||||
predicate functionForwardingStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(DataFlow::FunctionNode function, DataFlow::CallNode call |
|
||||
call.flowsTo(function.getReturnNode()) and
|
||||
forall(int i | exists([call.getArgument(i), function.getParameter(i)]) |
|
||||
function.getParameter(i).flowsTo(call.getArgument(i))
|
||||
) and
|
||||
pred = call.getCalleeNode() and
|
||||
succ = function
|
||||
)
|
||||
or
|
||||
// Given a generic wrapper function like,
|
||||
//
|
||||
// function wrap(f) { return (x, y) => f(x, y) };
|
||||
//
|
||||
// add steps through calls to that function: `g -> wrap(g)`
|
||||
exists(DataFlow::FunctionNode wrapperFunction, SourceNode param, Node paramUse |
|
||||
FlowSteps::argumentPassing(succ, pred, wrapperFunction.getFunction(), param) and
|
||||
param.flowsTo(paramUse) and
|
||||
functionForwardingStep(paramUse, wrapperFunction.getReturnNode().getALocalSource())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,11 @@ class SourceNode extends DataFlow::Node {
|
||||
*/
|
||||
predicate flowsToExpr(Expr sink) { flowsTo(DataFlow::valueNode(sink)) }
|
||||
|
||||
/**
|
||||
* Gets a node into which data may flow from this node in zero or more local steps.
|
||||
*/
|
||||
DataFlow::Node getALocalUse() { flowsTo(result) }
|
||||
|
||||
/**
|
||||
* Gets a reference (read or write) of property `propName` on this node.
|
||||
*/
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
import javascript
|
||||
private import semmle.javascript.dataflow.internal.FlowSteps as FlowSteps
|
||||
private import semmle.javascript.dataflow.internal.Unit
|
||||
private import semmle.javascript.Unit
|
||||
private import semmle.javascript.dataflow.InferredTypes
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
private import javascript
|
||||
private import internal.FlowSteps
|
||||
private import internal.StepSummary
|
||||
private import internal.Unit
|
||||
private import semmle.javascript.Unit
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
|
||||
private newtype TTypeTracker = MkTypeTracker(Boolean hasCall, OptionalPropertyName prop)
|
||||
|
||||
@@ -4,14 +4,9 @@
|
||||
*/
|
||||
|
||||
private import javascript
|
||||
private import semmle.javascript.Unit
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
|
||||
private newtype TUnit = MkUnit()
|
||||
|
||||
private class Unit extends TUnit {
|
||||
string toString() { result = "unit" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal extension point for adding flow edges prior to call graph construction
|
||||
* and type tracking.
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
private newtype TUnit = MkUnit()
|
||||
|
||||
/**
|
||||
* A class with only one instance.
|
||||
*/
|
||||
class Unit extends TUnit {
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = "Unit" }
|
||||
}
|
||||
@@ -105,35 +105,6 @@ module Angular2 {
|
||||
result = urlSegment().getAPropertyRead("parameters") and kind.isPath()
|
||||
}
|
||||
|
||||
/** Gets a reference to a `Params` object, usually containing values from the URL. */
|
||||
DataFlow::SourceNode paramDictionaryObject() { result = paramDictionaryObject(_) }
|
||||
|
||||
/**
|
||||
* A value from `@angular/router` derived from the URL.
|
||||
*/
|
||||
class AngularSource extends ClientSideRemoteFlowSource {
|
||||
ClientSideRemoteFlowKind kind;
|
||||
|
||||
AngularSource() {
|
||||
this = paramMap(kind).getAMethodCall(["get", "getAll"])
|
||||
or
|
||||
this = paramDictionaryObject(kind)
|
||||
or
|
||||
this = activatedRouteProp("fragment") and kind.isFragment()
|
||||
or
|
||||
this = urlSegment().getAPropertyRead("path") and kind.isPath()
|
||||
or
|
||||
// Note that Router.url and RouterStateSnapshot.url are strings, not UrlSegment[]
|
||||
this = router().getAPropertyRead("url") and kind.isUrl()
|
||||
or
|
||||
this = routerStateSnapshot().getAPropertyRead("url") and kind.isUrl()
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "Angular route parameter" }
|
||||
|
||||
override ClientSideRemoteFlowKind getKind() { result = kind }
|
||||
}
|
||||
|
||||
/** Gets a reference to a `DomSanitizer` object. */
|
||||
DataFlow::SourceNode domSanitizer() {
|
||||
result.hasUnderlyingType(["@angular/platform-browser", "@angular/core"], "DomSanitizer")
|
||||
|
||||
@@ -88,7 +88,7 @@ module FunctionCompositionCall {
|
||||
RightToLeft() {
|
||||
this = DataFlow::moduleImport(["compose-function"]).getACall()
|
||||
or
|
||||
this = DataFlow::moduleMember(["redux", "ramda"], "compose").getACall()
|
||||
this = DataFlow::moduleMember(["redux", "ramda", "@reduxjs/toolkit"], "compose").getACall()
|
||||
or
|
||||
this = LodashUnderscore::member("flowRight").getACall()
|
||||
}
|
||||
|
||||
@@ -489,39 +489,32 @@ module Express {
|
||||
string kind;
|
||||
|
||||
RequestInputAccess() {
|
||||
kind = "parameter" and
|
||||
this =
|
||||
[
|
||||
getAQueryObjectReference(DataFlow::TypeTracker::end(), rh),
|
||||
getAParamsObjectReference(DataFlow::TypeTracker::end(), rh)
|
||||
].getAPropertyRead()
|
||||
or
|
||||
exists(DataFlow::SourceNode request | request = rh.getARequestSource().ref() |
|
||||
exists(DataFlow::Node request | request = DataFlow::valueNode(rh.getARequestExpr()) |
|
||||
kind = "parameter" and
|
||||
this = request.getAMethodCall("param")
|
||||
(
|
||||
this.(DataFlow::MethodCallNode).calls(request, "param")
|
||||
or
|
||||
exists(DataFlow::PropRead base, string propName |
|
||||
// `req.params.name` or `req.query.name`
|
||||
base.accesses(request, propName) and
|
||||
this = base.getAPropertyReference(_)
|
||||
|
|
||||
propName = "params" or
|
||||
propName = "query"
|
||||
)
|
||||
)
|
||||
or
|
||||
// `req.originalUrl`
|
||||
kind = "url" and
|
||||
this = request.getAPropertyRead("originalUrl")
|
||||
this.(DataFlow::PropRef).accesses(request, "originalUrl")
|
||||
or
|
||||
// `req.cookies`
|
||||
kind = "cookie" and
|
||||
this = request.getAPropertyRead("cookies")
|
||||
or
|
||||
// `req.files`, treated the same as `req.body`.
|
||||
// `express-fileupload` uses .files, and `multer` uses .files or .file
|
||||
kind = "body" and
|
||||
this = request.getAPropertyRead(["files", "file"])
|
||||
this.(DataFlow::PropRef).accesses(request, "cookies")
|
||||
)
|
||||
or
|
||||
kind = "body" and
|
||||
this.asExpr() = rh.getARequestBodyAccess()
|
||||
or
|
||||
// `value` in `router.param('foo', (req, res, next, value) => { ... })`
|
||||
kind = "parameter" and
|
||||
exists(RouteSetup setup | rh = setup.getARouteHandler() |
|
||||
this = DataFlow::parameterNode(rh.getRouteHandlerParameter("parameter"))
|
||||
)
|
||||
}
|
||||
|
||||
override RouteHandler getRouteHandler() { result = rh }
|
||||
|
||||
@@ -125,42 +125,6 @@ module Fastify {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An access to a user-controlled Fastify request input.
|
||||
*/
|
||||
private class RequestInputAccess extends HTTP::RequestInputAccess {
|
||||
RouteHandler rh;
|
||||
string kind;
|
||||
|
||||
RequestInputAccess() {
|
||||
exists(string name | this = rh.getARequestSource().ref().getAPropertyRead(name) |
|
||||
kind = "parameter" and
|
||||
name = ["params", "query"]
|
||||
or
|
||||
kind = "body" and
|
||||
name = "body"
|
||||
)
|
||||
}
|
||||
|
||||
override RouteHandler getRouteHandler() { result = rh }
|
||||
|
||||
override string getKind() { result = kind }
|
||||
|
||||
override predicate isUserControlledObject() {
|
||||
kind = "body" and
|
||||
(
|
||||
usesFastifyPlugin(rh,
|
||||
DataFlow::moduleImport(["fastify-xml-body-parser", "fastify-formbody"]))
|
||||
or
|
||||
usesMiddleware(rh,
|
||||
any(ExpressLibraries::BodyParser bodyParser | bodyParser.producesUserControlledObjects()))
|
||||
)
|
||||
or
|
||||
kind = "parameter" and
|
||||
usesFastifyPlugin(rh, DataFlow::moduleImport("fastify-qs"))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `rh` uses `plugin`.
|
||||
*/
|
||||
@@ -193,25 +157,6 @@ module Fastify {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An access to a header on a Fastify request.
|
||||
*/
|
||||
private class RequestHeaderAccess extends HTTP::RequestHeaderAccess {
|
||||
RouteHandler rh;
|
||||
|
||||
RequestHeaderAccess() {
|
||||
this = rh.getARequestSource().ref().getAPropertyRead("headers").getAPropertyRead()
|
||||
}
|
||||
|
||||
override string getAHeaderName() {
|
||||
result = this.(DataFlow::PropRead).getPropertyName().toLowerCase()
|
||||
}
|
||||
|
||||
override RouteHandler getRouteHandler() { result = rh }
|
||||
|
||||
override string getKind() { result = "header" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument passed to the `send` or `end` method of an HTTP response object.
|
||||
*/
|
||||
|
||||
@@ -4,60 +4,3 @@
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* A source of remote flow from the `Busboy` library.
|
||||
*/
|
||||
private class BusBoyRemoteFlow extends RemoteFlowSource {
|
||||
BusBoyRemoteFlow() {
|
||||
this =
|
||||
API::moduleImport("busboy")
|
||||
.getInstance()
|
||||
.getMember("on")
|
||||
.getParameter(1)
|
||||
.getAParameter()
|
||||
.getAnImmediateUse()
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "parsed user value from Busbuy" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A source of remote flow from the `Formidable` library parsing a HTTP request.
|
||||
*/
|
||||
private class FormidableRemoteFlow extends RemoteFlowSource {
|
||||
FormidableRemoteFlow() {
|
||||
exists(API::Node formidable |
|
||||
formidable = API::moduleImport("formidable").getReturn()
|
||||
or
|
||||
formidable = API::moduleImport("formidable").getMember("formidable").getReturn()
|
||||
or
|
||||
formidable =
|
||||
API::moduleImport("formidable").getMember(["IncomingForm", "Formidable"]).getInstance()
|
||||
|
|
||||
this =
|
||||
formidable.getMember("parse").getACall().getABoundCallbackParameter(1, any(int i | i > 0))
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "parsed user value from Formidable" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A source of remote flow from the `Multiparty` library.
|
||||
*/
|
||||
private class MultipartyRemoteFlow extends RemoteFlowSource {
|
||||
MultipartyRemoteFlow() {
|
||||
exists(API::Node form | form = API::moduleImport("multiparty").getMember("Form").getInstance() |
|
||||
exists(API::CallNode parse | parse = form.getMember("parse").getACall() |
|
||||
this = parse.getParameter(1).getAParameter().getAnImmediateUse()
|
||||
)
|
||||
or
|
||||
exists(API::CallNode on | on = form.getMember("on").getACall() |
|
||||
on.getArgument(0).mayHaveStringValue(["part", "file", "field"]) and
|
||||
this = on.getParameter(1).getAParameter().getAnImmediateUse()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "parsed user value from Multiparty" }
|
||||
}
|
||||
|
||||
@@ -475,23 +475,6 @@ module HTTP {
|
||||
*/
|
||||
abstract Expr getServer();
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter containing data received by a NodeJS HTTP server.
|
||||
* E.g. `chunk` in: `http.createServer().on('request', (req, res) => req.on("data", (chunk) => ...))`.
|
||||
*/
|
||||
private class ServerRequestDataEvent extends RemoteFlowSource, DataFlow::ParameterNode {
|
||||
RequestSource req;
|
||||
|
||||
ServerRequestDataEvent() {
|
||||
exists(DataFlow::MethodCallNode mcn | mcn = req.ref().getAMethodCall(EventEmitter::on()) |
|
||||
mcn.getArgument(0).mayHaveStringValue("data") and
|
||||
this = mcn.getABoundCallbackParameter(1, 0)
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "NodeJS HTTP server data event" }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user