mirror of
https://github.com/github/codeql.git
synced 2026-05-16 12:17:07 +02:00
Compare commits
4 Commits
codeql-cli
...
sauyon/jav
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
133ce0096b | ||
|
|
07959b5a90 | ||
|
|
3486ce2be5 | ||
|
|
a2c84023d6 |
@@ -1,2 +0,0 @@
|
||||
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
|
||||
// Allow this assignment if all comparison operations in the expression that this
|
||||
// whitelist 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,21 +62,6 @@ 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\\S]*") // The first element (path) is quoted
|
||||
s.regexpMatch("\"([^\"])*\"(\\s|.)*") // The first element (path) is quoted
|
||||
or
|
||||
s.regexpMatch("[^\\s]+") // There are no spaces in the string
|
||||
}
|
||||
|
||||
@@ -72,7 +72,6 @@ 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,5 +1,4 @@
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.PointerWrapper
|
||||
|
||||
/**
|
||||
@@ -15,23 +14,6 @@ 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,59 +3260,15 @@
|
||||
| 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 | |
|
||||
| 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:52:10:52:10 | p | smart_pointer.cpp:52:12:52:14 | call to get | TAINT |
|
||||
| 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 | |
|
||||
| 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:57:10:57:10 | p | smart_pointer.cpp:57:12:57:14 | call to get | TAINT |
|
||||
| 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,31 +65,4 @@ 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,7 +19,3 @@
|
||||
| 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,21 +153,3 @@ 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.
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* Support for the Dapper ORM library has been added to the SQL injection checks.
|
||||
@@ -1,2 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* The extractor has been improved to store default argument values for parameters that are extracted from referenced assemblies.
|
||||
@@ -164,39 +164,6 @@ 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,20 +14,5 @@ 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,7 +4,6 @@ using System.Linq;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Semmle.Extraction.Entities;
|
||||
using System.IO;
|
||||
using System;
|
||||
|
||||
namespace Semmle.Extraction.CSharp.Entities
|
||||
{
|
||||
@@ -125,17 +124,6 @@ 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;
|
||||
|
||||
@@ -151,28 +139,36 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
TypeMention.Create(Context, syntax.Type!, this, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
if (Symbol.HasExplicitDefaultValue && Context.Defines(Symbol))
|
||||
{
|
||||
// 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)
|
||||
// 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)
|
||||
{
|
||||
var i = method.Parameters.IndexOf(symbol);
|
||||
if (method.AssociatedSymbol is IPropertySymbol indexer)
|
||||
defaultValue = GetParameterDefaultValue(indexer.Parameters[i]);
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public override bool IsSourceDeclaration => Symbol.IsSourceDeclaration();
|
||||
|
||||
@@ -43,6 +43,12 @@ 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')
|
||||
|
||||
@@ -52,75 +58,56 @@ if os.path.isfile(outputFile):
|
||||
os.remove(outputFile) # It would interfere with the test.
|
||||
print("Removed previous", outputFile)
|
||||
|
||||
cmd = ['codeql', 'test', 'run', '--keep-databases', testDir]
|
||||
cmd = ['odasa', 'qltest', '--optimize', '--leave-temp-files', testDir]
|
||||
print('Running ' + ' '.join(cmd))
|
||||
if subprocess.check_call(cmd):
|
||||
print("codeql test failed. Please fix up the test before proceeding.")
|
||||
print("qltest failed. Please fix up the test before proceeding.")
|
||||
exit(1)
|
||||
|
||||
dbDir = os.path.join(testDir, os.path.basename(testDir) + ".testproj")
|
||||
dbDir = os.path.join(testDir, os.path.basename(testDir) + ".testproj", "db-csharp")
|
||||
|
||||
if not os.path.isdir(dbDir):
|
||||
print("Expected database directory " + dbDir + " not found.")
|
||||
print("Expected database directory " + dbDir + " not found. Please contact Semmle.")
|
||||
exit(1)
|
||||
|
||||
cmd = ['codeql', 'query', 'run', os.path.join(
|
||||
csharpQueries, 'MinimalStubsFromSource.ql'), '--database', dbDir, '--output', outputFile]
|
||||
cmd = ['odasa', 'runQuery', '--query', os.path.join(csharpQueries, 'MinimalStubsFromSource.ql'), '--db', dbDir, '--output-file', outputFile]
|
||||
print('Running ' + ' '.join(cmd))
|
||||
if subprocess.check_call(cmd):
|
||||
print('Failed to run the query to generate output file.')
|
||||
print('Failed to run the query to generate output file. Please contact Semmle.')
|
||||
exit(1)
|
||||
|
||||
# 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()
|
||||
# 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()
|
||||
|
||||
f = open(outputFile, "wb")
|
||||
f.write(contents)
|
||||
f.close()
|
||||
|
||||
cmd = ['codeql', 'test', 'run', testDir]
|
||||
cmd = ['odasa', 'qltest', '--optimize', 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 codeql test run "' + 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 odasa qltest --optimize "' + 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 codeql test run "' + testDir + '"')
|
||||
print('2. Re-run odasa qltest --optimize "' + testDir + '"')
|
||||
print('3. git add "' + outputFile + '"')
|
||||
print('4. Commit your changes.')
|
||||
|
||||
|
||||
@@ -2,3 +2,5 @@
|
||||
- qlpack: codeql-csharp
|
||||
- apply: code-scanning-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
- apply: codeql-suites/exclude-dependency-queries.yml
|
||||
from: codeql-csharp
|
||||
|
||||
@@ -2,3 +2,5 @@
|
||||
- qlpack: codeql-csharp
|
||||
- apply: security-and-quality-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
- apply: codeql-suites/exclude-dependency-queries.yml
|
||||
from: codeql-csharp
|
||||
|
||||
@@ -2,3 +2,5 @@
|
||||
- qlpack: codeql-csharp
|
||||
- apply: security-extended-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
- apply: codeql-suites/exclude-dependency-queries.yml
|
||||
from: codeql-csharp
|
||||
|
||||
@@ -321,9 +321,10 @@ private module SsaDefReaches {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate ssaDefReachesThroughBlock(Definition def, BasicBlock bb) {
|
||||
ssaDefReachesEndOfBlock(bb, def, _) and
|
||||
not defOccursInBlock(_, bb, def.getSourceVariable())
|
||||
private BasicBlock getAMaybeLiveSuccessor(Definition def, BasicBlock bb) {
|
||||
result = getABasicBlockSuccessor(bb) and
|
||||
not defOccursInBlock(_, bb, def.getSourceVariable()) and
|
||||
ssaDefReachesEndOfBlock(bb, def, _)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -336,11 +337,7 @@ private module SsaDefReaches {
|
||||
defOccursInBlock(def, bb1, _) and
|
||||
bb2 = getABasicBlockSuccessor(bb1)
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
varBlockReaches(def, bb1, mid) and
|
||||
ssaDefReachesThroughBlock(def, mid) and
|
||||
bb2 = getABasicBlockSuccessor(mid)
|
||||
)
|
||||
exists(BasicBlock mid | varBlockReaches(def, bb1, mid) | bb2 = getAMaybeLiveSuccessor(def, mid))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -358,10 +355,17 @@ private module SsaDefReaches {
|
||||
|
||||
private import SsaDefReaches
|
||||
|
||||
pragma[nomagic]
|
||||
predicate liveThrough(BasicBlock bb, SourceVariable v) {
|
||||
liveAtExit(bb, v) and
|
||||
not ssaRef(bb, _, v, SsaDef())
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -378,14 +382,9 @@ predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable
|
||||
liveAtExit(bb, v)
|
||||
)
|
||||
or
|
||||
// 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))
|
||||
ssaDefReachesEndOfBlockRec(bb, def, v) and
|
||||
liveAtExit(bb, v) and
|
||||
not ssaRef(bb, _, v, SsaDef())
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,6 +27,9 @@ 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 }
|
||||
|
||||
|
||||
83
csharp/ql/src/semmle/code/csharp/Enclosing.qll
Normal file
83
csharp/ql/src/semmle/code/csharp/Enclosing.qll
Normal file
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* 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,54 +138,6 @@ 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.ExprOrStmtParent
|
||||
private import semmle.code.csharp.Enclosing::Internal
|
||||
private import semmle.code.csharp.frameworks.System
|
||||
private import TypeRef
|
||||
|
||||
|
||||
@@ -321,9 +321,10 @@ private module SsaDefReaches {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate ssaDefReachesThroughBlock(Definition def, BasicBlock bb) {
|
||||
ssaDefReachesEndOfBlock(bb, def, _) and
|
||||
not defOccursInBlock(_, bb, def.getSourceVariable())
|
||||
private BasicBlock getAMaybeLiveSuccessor(Definition def, BasicBlock bb) {
|
||||
result = getABasicBlockSuccessor(bb) and
|
||||
not defOccursInBlock(_, bb, def.getSourceVariable()) and
|
||||
ssaDefReachesEndOfBlock(bb, def, _)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -336,11 +337,7 @@ private module SsaDefReaches {
|
||||
defOccursInBlock(def, bb1, _) and
|
||||
bb2 = getABasicBlockSuccessor(bb1)
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
varBlockReaches(def, bb1, mid) and
|
||||
ssaDefReachesThroughBlock(def, mid) and
|
||||
bb2 = getABasicBlockSuccessor(mid)
|
||||
)
|
||||
exists(BasicBlock mid | varBlockReaches(def, bb1, mid) | bb2 = getAMaybeLiveSuccessor(def, mid))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -358,10 +355,17 @@ private module SsaDefReaches {
|
||||
|
||||
private import SsaDefReaches
|
||||
|
||||
pragma[nomagic]
|
||||
predicate liveThrough(BasicBlock bb, SourceVariable v) {
|
||||
liveAtExit(bb, v) and
|
||||
not ssaRef(bb, _, v, SsaDef())
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -378,14 +382,9 @@ predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable
|
||||
liveAtExit(bb, v)
|
||||
)
|
||||
or
|
||||
// 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))
|
||||
ssaDefReachesEndOfBlockRec(bb, def, v) and
|
||||
liveAtExit(bb, v) and
|
||||
not ssaRef(bb, _, v, SsaDef())
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,14 +47,14 @@ class Node extends TNode {
|
||||
cached
|
||||
final DataFlowCallable getEnclosingCallable() {
|
||||
Stages::DataFlowStage::forceCachingInSameStage() and
|
||||
result = this.(NodeImpl).getEnclosingCallableImpl()
|
||||
result = unique(DataFlowCallable c | c = this.(NodeImpl).getEnclosingCallableImpl() | c)
|
||||
}
|
||||
|
||||
/** Gets the control flow node corresponding to this node, if any. */
|
||||
cached
|
||||
final ControlFlow::Node getControlFlowNode() {
|
||||
Stages::DataFlowStage::forceCachingInSameStage() and
|
||||
result = this.(NodeImpl).getControlFlowNodeImpl()
|
||||
result = unique(ControlFlow::Node n | n = this.(NodeImpl).getControlFlowNodeImpl() | n)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this node. */
|
||||
|
||||
@@ -321,9 +321,10 @@ private module SsaDefReaches {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate ssaDefReachesThroughBlock(Definition def, BasicBlock bb) {
|
||||
ssaDefReachesEndOfBlock(bb, def, _) and
|
||||
not defOccursInBlock(_, bb, def.getSourceVariable())
|
||||
private BasicBlock getAMaybeLiveSuccessor(Definition def, BasicBlock bb) {
|
||||
result = getABasicBlockSuccessor(bb) and
|
||||
not defOccursInBlock(_, bb, def.getSourceVariable()) and
|
||||
ssaDefReachesEndOfBlock(bb, def, _)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -336,11 +337,7 @@ private module SsaDefReaches {
|
||||
defOccursInBlock(def, bb1, _) and
|
||||
bb2 = getABasicBlockSuccessor(bb1)
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
varBlockReaches(def, bb1, mid) and
|
||||
ssaDefReachesThroughBlock(def, mid) and
|
||||
bb2 = getABasicBlockSuccessor(mid)
|
||||
)
|
||||
exists(BasicBlock mid | varBlockReaches(def, bb1, mid) | bb2 = getAMaybeLiveSuccessor(def, mid))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -358,10 +355,17 @@ private module SsaDefReaches {
|
||||
|
||||
private import SsaDefReaches
|
||||
|
||||
pragma[nomagic]
|
||||
predicate liveThrough(BasicBlock bb, SourceVariable v) {
|
||||
liveAtExit(bb, v) and
|
||||
not ssaRef(bb, _, v, SsaDef())
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -378,14 +382,9 @@ predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable
|
||||
liveAtExit(bb, v)
|
||||
)
|
||||
or
|
||||
// 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))
|
||||
ssaDefReachesEndOfBlockRec(bb, def, v) and
|
||||
liveAtExit(bb, v) and
|
||||
not ssaRef(bb, _, v, SsaDef())
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -321,9 +321,10 @@ private module SsaDefReaches {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate ssaDefReachesThroughBlock(Definition def, BasicBlock bb) {
|
||||
ssaDefReachesEndOfBlock(bb, def, _) and
|
||||
not defOccursInBlock(_, bb, def.getSourceVariable())
|
||||
private BasicBlock getAMaybeLiveSuccessor(Definition def, BasicBlock bb) {
|
||||
result = getABasicBlockSuccessor(bb) and
|
||||
not defOccursInBlock(_, bb, def.getSourceVariable()) and
|
||||
ssaDefReachesEndOfBlock(bb, def, _)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -336,11 +337,7 @@ private module SsaDefReaches {
|
||||
defOccursInBlock(def, bb1, _) and
|
||||
bb2 = getABasicBlockSuccessor(bb1)
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
varBlockReaches(def, bb1, mid) and
|
||||
ssaDefReachesThroughBlock(def, mid) and
|
||||
bb2 = getABasicBlockSuccessor(mid)
|
||||
)
|
||||
exists(BasicBlock mid | varBlockReaches(def, bb1, mid) | bb2 = getAMaybeLiveSuccessor(def, mid))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -358,10 +355,17 @@ private module SsaDefReaches {
|
||||
|
||||
private import SsaDefReaches
|
||||
|
||||
pragma[nomagic]
|
||||
predicate liveThrough(BasicBlock bb, SourceVariable v) {
|
||||
liveAtExit(bb, v) and
|
||||
not ssaRef(bb, _, v, SsaDef())
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -378,14 +382,9 @@ predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable
|
||||
liveAtExit(bb, v)
|
||||
)
|
||||
or
|
||||
// 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))
|
||||
ssaDefReachesEndOfBlockRec(bb, def, v) and
|
||||
liveAtExit(bb, v) and
|
||||
not ssaRef(bb, _, v, SsaDef())
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -636,41 +636,31 @@ private module Internal {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stepExpr(Expr pred, Expr succ) {
|
||||
Steps::stepOpen(pred, succ) and
|
||||
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
|
||||
// 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 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 predicate stepTC(Expr succ, Expr pred) = fastTC(stepExpr/2)(succ, pred)
|
||||
|
||||
private class Source extends Expr {
|
||||
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
|
||||
}
|
||||
Source() { not stepExpr(this, _) }
|
||||
}
|
||||
|
||||
private class Sink extends Expr {
|
||||
@@ -690,38 +680,24 @@ private module Internal {
|
||||
this = any(DispatchCallImpl c).getArgument(_)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
Expr getAPred() { stepExpr*(result, this) }
|
||||
|
||||
pragma[nomagic]
|
||||
AnalyzableFieldOrProperty getAPredRead() { this.getAPred() = result.getARead() }
|
||||
Source getASource() { stepTC(this, result) }
|
||||
}
|
||||
|
||||
/** 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)
|
||||
/** Holds if the expression `e` has an exact type. */
|
||||
private predicate hasExactType(Expr e) {
|
||||
e instanceof ObjectCreation or
|
||||
e instanceof BaseAccess
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
/** 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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.ExprOrStmtParent
|
||||
private import semmle.code.csharp.Enclosing::Internal
|
||||
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() { enclosingCallable(this, result) }
|
||||
override Callable getEnclosingCallable() { exprEnclosingCallable(this, result) }
|
||||
|
||||
/**
|
||||
* Holds if this expression is generated by the compiler and does not appear
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
/**
|
||||
* 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,8 +5,6 @@ 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 {
|
||||
@@ -85,37 +83,3 @@ 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,9 +28,6 @@ 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,15 +11,9 @@ class SourceControlFlowElement extends ControlFlowElement {
|
||||
}
|
||||
|
||||
class SourceControlFlowNode extends ControlFlow::Node {
|
||||
SourceControlFlowNode() {
|
||||
not this.getLocation().getFile() instanceof StubFile and
|
||||
not this.getLocation().getFile().fromLibrary()
|
||||
}
|
||||
SourceControlFlowNode() { not this.getLocation().getFile() instanceof StubFile }
|
||||
}
|
||||
|
||||
class SourceBasicBlock extends ControlFlow::BasicBlock {
|
||||
SourceBasicBlock() {
|
||||
not this.getLocation().getFile() instanceof StubFile and
|
||||
not this.getLocation().getFile().fromLibrary()
|
||||
}
|
||||
SourceBasicBlock() { not this.getLocation().getFile() instanceof StubFile }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import csharp
|
||||
|
||||
query predicate countSplits(ControlFlowElement cfe, int i) {
|
||||
not cfe.fromLibrary() and
|
||||
i = strictcount(ControlFlow::Nodes::ElementNode n | n.getElement() = cfe)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import csharp
|
||||
|
||||
from DefaultValueExpr l
|
||||
where l.fromSource()
|
||||
select l, l.getValue()
|
||||
|
||||
@@ -2,7 +2,5 @@ import csharp
|
||||
import semmle.code.csharp.dataflow.TaintTracking
|
||||
|
||||
from DataFlow::Node pred, DataFlow::Node succ
|
||||
where
|
||||
TaintTracking::localTaintStep(pred, succ) and
|
||||
not pred.asExpr().fromLibrary()
|
||||
where TaintTracking::localTaintStep(pred, succ)
|
||||
select pred, succ
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import csharp
|
||||
|
||||
from DataFlow::Node pred, DataFlow::Node succ
|
||||
where not pred.asExpr().fromLibrary() and DataFlow::localFlowStep(pred, succ)
|
||||
where DataFlow::localFlowStep(pred, succ)
|
||||
select pred, succ
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import csharp
|
||||
|
||||
from DataFlow::Node pred, DataFlow::Node succ
|
||||
where
|
||||
not pred.asExpr().fromLibrary() and
|
||||
TaintTracking::localTaintStep(pred, succ)
|
||||
where TaintTracking::localTaintStep(pred, succ)
|
||||
select pred, succ
|
||||
|
||||
@@ -4,7 +4,5 @@ import semmle.code.csharp.dataflow.ModulusAnalysis
|
||||
import semmle.code.csharp.dataflow.Bound
|
||||
|
||||
from ControlFlow::Nodes::ExprNode e, Bound b, int delta, int mod
|
||||
where
|
||||
not e.getExpr().fromLibrary() and
|
||||
exprModulus(e, b, delta, mod)
|
||||
where exprModulus(e, b, delta, mod)
|
||||
select e, b.toString(), delta, mod
|
||||
|
||||
@@ -18,5 +18,4 @@ 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 not pred.asExpr().fromLibrary() and DataFlow::localFlowStep(pred, succ)
|
||||
where DataFlow::localFlowStep(pred, succ)
|
||||
select pred, succ
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import csharp
|
||||
|
||||
from Parameter p
|
||||
where p.fromSource()
|
||||
select p, p.getDefaultValue()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
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()
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
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 }
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
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 }
|
||||
}
|
||||
Binary file not shown.
@@ -1,50 +0,0 @@
|
||||
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 |
|
||||
@@ -1,19 +0,0 @@
|
||||
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,6 +17,4 @@ class UnknownLocalVariableDeclExpr extends LocalVariableDeclAndInitExpr {
|
||||
override string toString() { result = "(unknown type) " + this.getName() }
|
||||
}
|
||||
|
||||
query predicate edges(ControlFlow::Node n1, ControlFlow::Node n2) {
|
||||
not n1.getElement().fromLibrary() and n2 = n1.getASuccessor()
|
||||
}
|
||||
query predicate edges(ControlFlow::Node n1, ControlFlow::Node n2) { 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 ${testdir}/../../../resources/stubs/Dapper.cs /r:System.Linq.Expressions.dll
|
||||
// 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
|
||||
|
||||
using System;
|
||||
|
||||
|
||||
@@ -5,13 +5,6 @@ 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 |
|
||||
@@ -22,29 +15,8 @@ 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 |
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
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
@@ -1,34 +0,0 @@
|
||||
// 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,3 +21,11 @@ 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,11 +58,6 @@ 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$Cached$RemoteFlowSource.html>`__ -- source of untrusted user input
|
||||
- `RemoteFlowSource <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/security/dataflow/RemoteFlowSources.qll/type.RemoteFlowSources$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$Cached$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$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.16", "Go 1.11 or more recent", ``.go``
|
||||
Go (aka Golang), "Go up to 1.15", "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``
|
||||
|
||||
2
java/change-notes/2021-03-25-add-spring-stringutils.md
Normal file
2
java/change-notes/2021-03-25-add-spring-stringutils.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* Added additional taint steps modeling the Spring StringUtils class (`org.springframework.util.StringUtils`).
|
||||
@@ -638,21 +638,7 @@ class BooleanLiteral extends Literal, @booleanliteral {
|
||||
override string getAPrimaryQlClass() { result = "BooleanLiteral" }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`).
|
||||
*/
|
||||
/** An integer literal. For example, `23`. */
|
||||
class IntegerLiteral extends Literal, @integerliteral {
|
||||
/** Gets the int representation of this literal. */
|
||||
int getIntValue() { result = getValue().toInt() }
|
||||
@@ -660,32 +646,12 @@ class IntegerLiteral extends Literal, @integerliteral {
|
||||
override string getAPrimaryQlClass() { result = "IntegerLiteral" }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
/** A long literal. For example, `23l`. */
|
||||
class LongLiteral extends Literal, @longliteral {
|
||||
override string getAPrimaryQlClass() { result = "LongLiteral" }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
*/
|
||||
/** A floating point literal. For example, `4.2f`. */
|
||||
class FloatingPointLiteral extends Literal, @floatingpointliteral {
|
||||
/**
|
||||
* Gets the value of this literal as CodeQL 64-bit `float`. The value will
|
||||
@@ -696,12 +662,7 @@ class FloatingPointLiteral extends Literal, @floatingpointliteral {
|
||||
override string getAPrimaryQlClass() { result = "FloatingPointLiteral" }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
*/
|
||||
/** A double literal. For example, `4.2`. */
|
||||
class DoubleLiteral extends Literal, @doubleliteral {
|
||||
/**
|
||||
* Gets the value of this literal as CodeQL 64-bit `float`. The result will
|
||||
|
||||
@@ -80,15 +80,19 @@ class Node extends TNode {
|
||||
result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getType()
|
||||
}
|
||||
|
||||
/** Gets the callable in which this node occurs. */
|
||||
Callable getEnclosingCallable() {
|
||||
private Callable getEnclosingCallableImpl() {
|
||||
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().getEnclosingCallable()
|
||||
result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getEnclosingCallableImpl()
|
||||
}
|
||||
|
||||
/** Gets the callable in which this node occurs. */
|
||||
Callable getEnclosingCallable() {
|
||||
result = unique(DataFlowCallable c | c = this.getEnclosingCallableImpl() | c)
|
||||
}
|
||||
|
||||
private Type getImprovedTypeBound() {
|
||||
|
||||
@@ -32,6 +32,7 @@ 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
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/** 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"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
53
java/ql/test/library-tests/frameworks/spring/flow.ql
Normal file
53
java/ql/test/library-tests/frameworks/spring/flow.ql
Normal file
@@ -0,0 +1,53 @@
|
||||
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
java/ql/test/library-tests/frameworks/spring/options
Normal file
1
java/ql/test/library-tests/frameworks/spring/options
Normal file
@@ -0,0 +1 @@
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/springframework-5.2.3
|
||||
@@ -1,16 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
| 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 | -... |
|
||||
@@ -1,9 +0,0 @@
|
||||
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()
|
||||
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
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.
|
||||
@@ -1,5 +0,0 @@
|
||||
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.
|
||||
@@ -1,3 +0,0 @@
|
||||
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 ownFiles;
|
||||
return allFiles;
|
||||
}
|
||||
|
||||
/** Absolute paths to the files included in or referenced by this project. */
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* @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,7 +105,6 @@ 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
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
* @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`.
|
||||
*/
|
||||
pragma[inline]
|
||||
cached
|
||||
predicate strictlyDominates(ReachableBasicBlock bb) { bbIDominates+(this, bb) }
|
||||
|
||||
/**
|
||||
@@ -315,13 +315,15 @@ class ReachableBasicBlock extends BasicBlock {
|
||||
*
|
||||
* This predicate is reflexive: each reachable basic block dominates itself.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate dominates(ReachableBasicBlock bb) { bbIDominates*(this, bb) }
|
||||
predicate dominates(ReachableBasicBlock bb) {
|
||||
bb = this or
|
||||
strictlyDominates(bb)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this basic block strictly post-dominates `bb`.
|
||||
*/
|
||||
pragma[inline]
|
||||
cached
|
||||
predicate strictlyPostDominates(ReachableBasicBlock bb) { bbIPostDominates+(this, bb) }
|
||||
|
||||
/**
|
||||
@@ -329,8 +331,10 @@ class ReachableBasicBlock extends BasicBlock {
|
||||
*
|
||||
* This predicate is reflexive: each reachable basic block post-dominates itself.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate postDominates(ReachableBasicBlock bb) { bbIPostDominates*(this, bb) }
|
||||
predicate postDominates(ReachableBasicBlock bb) {
|
||||
bb = this or
|
||||
strictlyPostDominates(bb)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -51,7 +51,7 @@ class PackageJSON extends JSONObject {
|
||||
string getAFile() { result = getFiles().getElementStringValue(_) }
|
||||
|
||||
/** Gets the main module of this package. */
|
||||
string getMain() { result = MainModulePath::of(this).getValue() }
|
||||
string getMain() { result = getPropStringValue("main") }
|
||||
|
||||
/** Gets the path of a command defined for this package. */
|
||||
string getBin(string cmd) {
|
||||
|
||||
@@ -80,13 +80,6 @@ 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`.
|
||||
*/
|
||||
@@ -97,8 +90,9 @@ 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), getStem(main.getComponent(n - 1)), priority)
|
||||
result = tryExtensions(main.resolveUpTo(n - 1), main.getComponent(n - 1), priority)
|
||||
)
|
||||
)
|
||||
or
|
||||
@@ -107,27 +101,6 @@ 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
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,31 +126,3 @@ 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
|
||||
(
|
||||
cfg2BB.getImmediateDominator+() = cfg1RBB and
|
||||
cfg1RBB.strictlyDominates(cfg2BB) and
|
||||
cfg = cfg2
|
||||
or
|
||||
cfg1BB.getImmediateDominator+() = cfg2BB and
|
||||
cfg2RBB.strictlyDominates(cfg1RBB) and
|
||||
cfg = cfg1
|
||||
)
|
||||
)
|
||||
@@ -681,7 +681,7 @@ module RangeAnalysis {
|
||||
midBB = midcfg.getBasicBlock() and
|
||||
midRBB = midBB.(ReachableBasicBlock) and
|
||||
cfgBB = cfg.getBasicBlock() and
|
||||
cfgBB.getImmediateDominator+() = midRBB
|
||||
midRBB.strictlyDominates(cfgBB)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
/** 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 semmle.javascript.Unit
|
||||
private import internal.Unit
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
|
||||
/**
|
||||
|
||||
@@ -1683,59 +1683,4 @@ 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,11 +52,6 @@ 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.Unit
|
||||
private import semmle.javascript.dataflow.internal.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 semmle.javascript.Unit
|
||||
private import internal.Unit
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
|
||||
private newtype TTypeTracker = MkTypeTracker(Boolean hasCall, OptionalPropertyName prop)
|
||||
|
||||
@@ -4,9 +4,14 @@
|
||||
*/
|
||||
|
||||
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.
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
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" }
|
||||
}
|
||||
@@ -88,7 +88,7 @@ module FunctionCompositionCall {
|
||||
RightToLeft() {
|
||||
this = DataFlow::moduleImport(["compose-function"]).getACall()
|
||||
or
|
||||
this = DataFlow::moduleMember(["redux", "ramda", "@reduxjs/toolkit"], "compose").getACall()
|
||||
this = DataFlow::moduleMember(["redux", "ramda"], "compose").getACall()
|
||||
or
|
||||
this = LodashUnderscore::member("flowRight").getACall()
|
||||
}
|
||||
|
||||
@@ -478,28 +478,6 @@ module NodeJSLib {
|
||||
DataFlow::moduleImport("util-promisifyall")
|
||||
].getACall()
|
||||
)
|
||||
or
|
||||
// const fs = require('fs');
|
||||
// let fs_copy = methods.reduce((obj, method) => {
|
||||
// obj[method] = fs[method];
|
||||
// return obj;
|
||||
// }, {});
|
||||
t.continue() = t2 and
|
||||
exists(
|
||||
DataFlow::MethodCallNode call, DataFlow::ParameterNode obj, DataFlow::SourceNode method
|
||||
|
|
||||
call.getMethodName() = "reduce" and
|
||||
result = call and
|
||||
obj = call.getABoundCallbackParameter(0, 0) and
|
||||
obj.flowsTo(any(DataFlow::FunctionNode f).getAReturn()) and
|
||||
exists(DataFlow::PropWrite write, DataFlow::PropRead read |
|
||||
write = obj.getAPropertyWrite() and
|
||||
method.flowsToExpr(write.getPropertyNameExpr()) and
|
||||
method.flowsToExpr(read.getPropertyNameExpr()) and
|
||||
read.getBase().getALocalSource() = fsModule(t2) and
|
||||
write.getRhs() = maybePromisified(read)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -28,52 +28,30 @@ module SQL {
|
||||
* Provides classes modelling the (API compatible) `mysql` and `mysql2` packages.
|
||||
*/
|
||||
private module MySql {
|
||||
private string moduleName() { result = ["mysql", "mysql2", "mysql2/promise"] }
|
||||
|
||||
/** Gets the package name `mysql` or `mysql2`. */
|
||||
API::Node mysql() { result = API::moduleImport(moduleName()) }
|
||||
API::Node mysql() { result = API::moduleImport(["mysql", "mysql2"]) }
|
||||
|
||||
/** Gets a reference to `mysql.createConnection`. */
|
||||
API::Node createConnection() {
|
||||
result = mysql().getMember(["createConnection", "createConnectionPromise"])
|
||||
}
|
||||
API::Node createConnection() { result = mysql().getMember("createConnection") }
|
||||
|
||||
/** Gets a reference to `mysql.createPool`. */
|
||||
API::Node createPool() { result = mysql().getMember(["createPool", "createPoolCluster"]) }
|
||||
API::Node createPool() { result = mysql().getMember("createPool") }
|
||||
|
||||
/** Gets a node that contains a MySQL pool created using `mysql.createPool()`. */
|
||||
API::Node pool() {
|
||||
result = createPool().getReturn()
|
||||
or
|
||||
result = pool().getMember("on").getReturn()
|
||||
or
|
||||
result = API::Node::ofType(moduleName(), ["Pool", "PoolCluster"])
|
||||
}
|
||||
API::Node pool() { result = createPool().getReturn() }
|
||||
|
||||
/** Gets a data flow node that contains a freshly created MySQL connection instance. */
|
||||
API::Node connection() {
|
||||
result = createConnection().getReturn()
|
||||
or
|
||||
result = createConnection().getReturn().getPromised()
|
||||
or
|
||||
result = pool().getMember("getConnection").getParameter(0).getParameter(1)
|
||||
or
|
||||
result = pool().getMember("getConnection").getPromised()
|
||||
or
|
||||
exists(API::CallNode call |
|
||||
call = pool().getMember("on").getACall() and
|
||||
call.getArgument(0).getStringValue() = ["connection", "acquire", "release"] and
|
||||
result = call.getParameter(1).getParameter(0)
|
||||
)
|
||||
or
|
||||
result = API::Node::ofType(moduleName(), ["Connection", "PoolConnection"])
|
||||
}
|
||||
|
||||
/** A call to the MySql `query` method. */
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
QueryCall() {
|
||||
exists(API::Node recv | recv = pool() or recv = connection() |
|
||||
this = recv.getMember(["query", "execute"]).getACall()
|
||||
this = recv.getMember("query").getACall()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -134,20 +112,7 @@ private module Postgres {
|
||||
// pool.connect(function(err, client) { ... })
|
||||
result = pool().getMember("connect").getParameter(0).getParameter(1)
|
||||
or
|
||||
// await pool.connect()
|
||||
result = pool().getMember("connect").getReturn().getPromised()
|
||||
or
|
||||
result = pgpConnection().getMember("client")
|
||||
or
|
||||
exists(API::CallNode call |
|
||||
call = pool().getMember("on").getACall() and
|
||||
call.getArgument(0).getStringValue() = ["connect", "acquire"] and
|
||||
result = call.getParameter(1).getParameter(0)
|
||||
)
|
||||
or
|
||||
result = client().getMember("on").getReturn()
|
||||
or
|
||||
result = API::Node::ofType("pg", ["Client", "PoolClient"])
|
||||
}
|
||||
|
||||
/** Gets a constructor that when invoked constructs a new connection pool. */
|
||||
@@ -164,10 +129,6 @@ private module Postgres {
|
||||
result = newPool().getInstance()
|
||||
or
|
||||
result = pgpDatabase().getMember("$pool")
|
||||
or
|
||||
result = pool().getMember("on").getReturn()
|
||||
or
|
||||
result = API::Node::ofType("pg", "Pool")
|
||||
}
|
||||
|
||||
/** A call to the Postgres `query` method. */
|
||||
@@ -179,11 +140,7 @@ private module Postgres {
|
||||
|
||||
/** An expression that is passed to the `query` method and hence interpreted as SQL. */
|
||||
class QueryString extends SQL::SqlString {
|
||||
QueryString() {
|
||||
this = any(QueryCall qc).getAQueryArgument().asExpr()
|
||||
or
|
||||
this = API::moduleImport("pg-cursor").getParameter(0).getARhs().asExpr()
|
||||
}
|
||||
QueryString() { this = any(QueryCall qc).getAQueryArgument().asExpr() }
|
||||
}
|
||||
|
||||
/** An expression that is passed as user name or password when creating a client or a pool. */
|
||||
@@ -342,17 +299,24 @@ private module Sqlite {
|
||||
}
|
||||
|
||||
/** Gets an expression that constructs a Sqlite database instance. */
|
||||
API::Node database() {
|
||||
API::Node newDb() {
|
||||
// new require('sqlite3').Database()
|
||||
result = sqlite().getMember("Database").getInstance()
|
||||
or
|
||||
result = API::Node::ofType("sqlite3", "Database")
|
||||
}
|
||||
|
||||
/** A call to a Sqlite query method. */
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
QueryCall() {
|
||||
this = database().getMember(["all", "each", "exec", "get", "prepare", "run"]).getACall()
|
||||
exists(string meth |
|
||||
meth = "all" or
|
||||
meth = "each" or
|
||||
meth = "exec" or
|
||||
meth = "get" or
|
||||
meth = "prepare" or
|
||||
meth = "run"
|
||||
|
|
||||
this = newDb().getMember(meth).getACall()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = getArgument(0) }
|
||||
@@ -371,32 +335,15 @@ private module MsSql {
|
||||
/** Gets a reference to the `mssql` module. */
|
||||
API::Node mssql() { result = API::moduleImport("mssql") }
|
||||
|
||||
/** Gets a node referring to an instance of the given class. */
|
||||
API::Node mssqlClass(string name) {
|
||||
result = mssql().getMember(name).getInstance()
|
||||
or
|
||||
result = API::Node::ofType("mssql", name)
|
||||
}
|
||||
|
||||
/** Gets an API node referring to a Request object. */
|
||||
/** Gets an expression that creates a request object. */
|
||||
API::Node request() {
|
||||
result = mssqlClass("Request")
|
||||
// new require('mssql').Request()
|
||||
result = mssql().getMember("Request").getInstance()
|
||||
or
|
||||
result = request().getMember(["input", "replaceInput", "output", "replaceOutput"]).getReturn()
|
||||
or
|
||||
result = [transaction(), pool()].getMember("request").getReturn()
|
||||
// request.input(...)
|
||||
result = request().getMember("input").getReturn()
|
||||
}
|
||||
|
||||
/** Gets an API node referring to a Transaction object. */
|
||||
API::Node transaction() {
|
||||
result = mssqlClass("Transaction")
|
||||
or
|
||||
result = pool().getMember("transaction").getReturn()
|
||||
}
|
||||
|
||||
/** Gets a API node referring to a ConnectionPool object. */
|
||||
API::Node pool() { result = mssqlClass("ConnectionPool") }
|
||||
|
||||
/** A tagged template evaluated as a query. */
|
||||
private class QueryTemplateExpr extends DatabaseAccess, DataFlow::ValueNode {
|
||||
override TaggedTemplateExpr astNode;
|
||||
@@ -412,7 +359,7 @@ private module MsSql {
|
||||
|
||||
/** A call to a MsSql query method. */
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
QueryCall() { this = [mssql(), request()].getMember(["query", "batch"]).getACall() }
|
||||
QueryCall() { this = request().getMember(["query", "batch"]).getACall() }
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = getArgument(0) }
|
||||
}
|
||||
@@ -463,34 +410,22 @@ private module MsSql {
|
||||
* Provides classes modelling the `sequelize` package.
|
||||
*/
|
||||
private module Sequelize {
|
||||
/** Gets an import of the `sequelize` module or one that re-exports it. */
|
||||
API::Node sequelize() { result = API::moduleImport(["sequelize", "sequelize-typescript"]) }
|
||||
/** Gets an import of the `sequelize` module. */
|
||||
API::Node sequelize() { result = API::moduleImport("sequelize") }
|
||||
|
||||
/** Gets an expression that creates an instance of the `Sequelize` class. */
|
||||
API::Node instance() {
|
||||
result = [sequelize(), sequelize().getMember("Sequelize")].getInstance()
|
||||
or
|
||||
result = API::Node::ofType(["sequelize", "sequelize-typescript"], ["Sequelize", "default"])
|
||||
}
|
||||
API::Node newSequelize() { result = sequelize().getInstance() }
|
||||
|
||||
/** A call to `Sequelize.query`. */
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
QueryCall() { this = instance().getMember("query").getACall() }
|
||||
QueryCall() { this = newSequelize().getMember("query").getACall() }
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
result = getArgument(0)
|
||||
or
|
||||
result = getOptionArgument(0, "query")
|
||||
}
|
||||
override DataFlow::Node getAQueryArgument() { result = getArgument(0) }
|
||||
}
|
||||
|
||||
/** An expression that is passed to `Sequelize.query` method and hence interpreted as SQL. */
|
||||
class QueryString extends SQL::SqlString {
|
||||
QueryString() {
|
||||
this = any(QueryCall qc).getAQueryArgument().asExpr()
|
||||
or
|
||||
this = sequelize().getMember(["literal", "asIs"]).getParameter(0).getARhs().asExpr()
|
||||
}
|
||||
QueryString() { this = any(QueryCall qc).getAQueryArgument().asExpr() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -543,8 +478,6 @@ private module Spanner {
|
||||
API::Node database() {
|
||||
result =
|
||||
spanner().getReturn().getMember("instance").getReturn().getMember("database").getReturn()
|
||||
or
|
||||
result = API::Node::ofType("@google-cloud/spanner", "Database")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -552,81 +485,61 @@ private module Spanner {
|
||||
*/
|
||||
API::Node v1SpannerClient() {
|
||||
result = spanner().getMember("v1").getMember("SpannerClient").getInstance()
|
||||
or
|
||||
result = API::Node::ofType("@google-cloud/spanner", "v1.SpannerClient")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that refers to a transaction object.
|
||||
*/
|
||||
API::Node transaction() {
|
||||
result =
|
||||
database()
|
||||
.getMember(["runTransaction", "runTransactionAsync"])
|
||||
.getParameter([0, 1])
|
||||
.getParameter(1)
|
||||
or
|
||||
result = API::Node::ofType("@google-cloud/spanner", "Transaction")
|
||||
}
|
||||
|
||||
/** Gets an API node referring to a `BatchTransaction` object. */
|
||||
API::Node batchTransaction() {
|
||||
result = database().getMember("batchTransaction").getReturn()
|
||||
or
|
||||
result = database().getMember("createBatchTransaction").getReturn().getPromised()
|
||||
or
|
||||
result = API::Node::ofType("@google-cloud/spanner", "BatchTransaction")
|
||||
result = database().getMember("runTransaction").getParameter(0).getParameter(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a Spanner method that executes a SQL query.
|
||||
*/
|
||||
abstract class SqlExecution extends DatabaseAccess, DataFlow::InvokeNode { }
|
||||
abstract class SqlExecution extends DatabaseAccess, DataFlow::InvokeNode {
|
||||
/**
|
||||
* Gets the position of the query argument; default is zero, which can be overridden
|
||||
* by subclasses.
|
||||
*/
|
||||
int getQueryArgumentPosition() { result = 0 }
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
result = getArgument(getQueryArgumentPosition()) or
|
||||
result = getOptionArgument(getQueryArgumentPosition(), "sql")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A SQL execution that takes the input directly in the first argument or in the `sql` option.
|
||||
* A call to `Database.run`, `Database.runPartitionedUpdate` or `Database.runStream`.
|
||||
*/
|
||||
class SqlExecutionDirect extends SqlExecution {
|
||||
SqlExecutionDirect() {
|
||||
class DatabaseRunCall extends SqlExecution {
|
||||
DatabaseRunCall() {
|
||||
this = database().getMember(["run", "runPartitionedUpdate", "runStream"]).getACall()
|
||||
or
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `Transaction.run`, `Transaction.runStream` or `Transaction.runUpdate`.
|
||||
*/
|
||||
class TransactionRunCall extends SqlExecution {
|
||||
TransactionRunCall() {
|
||||
this = transaction().getMember(["run", "runStream", "runUpdate"]).getACall()
|
||||
or
|
||||
this = batchTransaction().getMember("createQueryPartitions").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
result = getArgument(0)
|
||||
or
|
||||
result = getOptionArgument(0, "sql")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A SQL execution that takes an array of SQL strings or { sql: string } objects.
|
||||
* A call to `v1.SpannerClient.executeSql` or `v1.SpannerClient.executeStreamingSql`.
|
||||
*/
|
||||
class SqlExecutionBatch extends SqlExecution, API::CallNode {
|
||||
SqlExecutionBatch() { this = transaction().getMember("batchUpdate").getACall() }
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
// just use the whole array as the query argument, as arrays becomes tainted if one of the elements
|
||||
// are tainted
|
||||
result = getArgument(0)
|
||||
or
|
||||
result = getParameter(0).getUnknownMember().getMember("sql").getARhs()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A SQL execution that only takes the input in the `sql` option, and do not accept query strings
|
||||
* directly.
|
||||
*/
|
||||
class SqlExecutionWithOption extends SqlExecution {
|
||||
SqlExecutionWithOption() {
|
||||
class ExecuteSqlCall extends SqlExecution {
|
||||
ExecuteSqlCall() {
|
||||
this = v1SpannerClient().getMember(["executeSql", "executeStreamingSql"]).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = getOptionArgument(0, "sql") }
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
// `executeSql` and `executeStreamingSql` do not accept query strings directly
|
||||
result = getOptionArgument(0, "sql")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -200,21 +200,20 @@ module ServerWebSocket {
|
||||
result = DataFlow::moduleImport("sockjs").getAMemberCall("createServer")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a `socket.on("connection", (msg, req) => {})` call.
|
||||
*/
|
||||
private DataFlow::CallNode getAConnectionCall(LibraryName library) {
|
||||
result = getAServer(library).getAMemberCall(EventEmitter::on()) and
|
||||
result.getArgument(0).mayHaveStringValue("connection")
|
||||
}
|
||||
|
||||
/**
|
||||
* A server WebSocket instance.
|
||||
*/
|
||||
class ServerSocket extends EventEmitter::Range, DataFlow::SourceNode {
|
||||
LibraryName library;
|
||||
|
||||
ServerSocket() { this = getAConnectionCall(library).getCallback(1).getParameter(0) }
|
||||
ServerSocket() {
|
||||
exists(DataFlow::CallNode onCall |
|
||||
onCall = getAServer(library).getAMemberCall(EventEmitter::on()) and
|
||||
onCall.getArgument(0).mayHaveStringValue("connection")
|
||||
|
|
||||
this = onCall.getCallback(1).getParameter(0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the library that created this server socket.
|
||||
@@ -222,51 +221,6 @@ module ServerWebSocket {
|
||||
LibraryName getLibrary() { result = library }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `socket.on("connection", (msg, req) => {})` call seen as a HTTP route handler.
|
||||
* `req` is a `HTTP::IncomingMessage` instance.
|
||||
*/
|
||||
class ConnectionCallAsRouteHandler extends HTTP::RouteHandler, DataFlow::CallNode {
|
||||
ConnectionCallAsRouteHandler() { this = getAConnectionCall(_) }
|
||||
|
||||
override HTTP::HeaderDefinition getAResponseHeader(string name) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `req` parameter of a `socket.on("connection", (msg, req) => {})` call.
|
||||
*/
|
||||
class ServerHTTPRequest extends HTTP::Servers::RequestSource {
|
||||
ConnectionCallAsRouteHandler handler;
|
||||
|
||||
ServerHTTPRequest() { this = handler.getCallback(1).getParameter(1) }
|
||||
|
||||
override HTTP::RouteHandler getRouteHandler() { result = handler }
|
||||
}
|
||||
|
||||
/**
|
||||
* An access user-controlled HTTP request input in a request to a WebSocket server.
|
||||
*/
|
||||
class WebSocketRequestInput extends HTTP::RequestInputAccess {
|
||||
ServerHTTPRequest request;
|
||||
string kind;
|
||||
|
||||
WebSocketRequestInput() {
|
||||
kind = "url" and
|
||||
this = request.ref().getAPropertyRead("url")
|
||||
or
|
||||
kind = "header" and
|
||||
this = request.ref().getAPropertyRead(["headers", "rawHeaders"]).getAPropertyRead()
|
||||
or
|
||||
// req.headers.cookie
|
||||
kind = "cookie" and
|
||||
this = request.ref().getAPropertyRead("headers").getAPropertyRead("cookie")
|
||||
}
|
||||
|
||||
override string getKind() { result = kind }
|
||||
|
||||
override HTTP::RouteHandler getRouteHandler() { result = request.getRouteHandler() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A message sent from a WebSocket server.
|
||||
*/
|
||||
|
||||
@@ -31,9 +31,5 @@ module RequestForgery {
|
||||
override predicate isSanitizerEdge(DataFlow::Node source, DataFlow::Node sink) {
|
||||
sanitizingPrefixEdge(source, sink)
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
isAdditionalRequestForgeryStep(pred, succ)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,14 +59,4 @@ module RequestForgery {
|
||||
|
||||
override string getKind() { result = kind }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a taint step from `pred` to `succ` for request forgery.
|
||||
*/
|
||||
predicate isAdditionalRequestForgeryStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(DataFlow::NewNode url | url = DataFlow::globalVarRef("URL").getAnInstantiation() |
|
||||
succ = url and
|
||||
pred = url.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,14 +55,4 @@ module ShellCommandInjectionFromEnvironment {
|
||||
class ShellCommandSink extends Sink, DataFlow::ValueNode {
|
||||
ShellCommandSink() { any(SystemCommandExecution sys).isShellInterpreted(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A string-concatenation leaf that is surrounded by quotes, seen as a sanitizer for command-injection.
|
||||
*/
|
||||
class QuotingConcatSanitizer extends Sanitizer, StringOps::ConcatenationLeaf {
|
||||
QuotingConcatSanitizer() {
|
||||
this.getNextLeaf().getStringValue().regexpMatch("(\"|').*") and
|
||||
this.getPreviousLeaf().getStringValue().regexpMatch(".*(\"|')")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user