Compare commits

..

4 Commits

Author SHA1 Message Date
Sauyon Lee
133ce0096b Add change note for Spring StringUtils 2021-04-02 01:30:38 -07:00
Sauyon Lee
07959b5a90 Add tests for org.springframework.util;StringUtils taint models 2021-04-02 01:30:38 -07:00
Sauyon Lee
3486ce2be5 Add taint models for org.springframework.util.StringUtils 2021-04-02 01:30:37 -07:00
Sauyon Lee
a2c84023d6 Add spring stringutils stub 2021-04-02 01:30:37 -07:00
167 changed files with 1026 additions and 3952 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,2 +0,0 @@
lgtm,codescanning
* Support for the Dapper ORM library has been added to the SQL injection checks.

View File

@@ -1,2 +0,0 @@
lgtm,codescanning
* The extractor has been improved to store default argument values for parameters that are extracted from referenced assemblies.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,4 @@
import csharp
from DefaultValueExpr l
where l.fromSource()
select l, l.getValue()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,4 @@
import csharp
from Parameter p
where p.fromSource()
select p, p.getDefaultValue()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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>`__."

View File

@@ -18,4 +18,4 @@ CodeQL CLI
using-the-codeql-cli
codeql-cli-reference
CodeQL CLI manual <https://codeql.github.com/docs/codeql-cli/manual>

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* Added additional taint steps modeling the Spring StringUtils class (`org.springframework.util.StringUtils`).

View File

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

View File

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

View File

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

View File

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

View File

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

View 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 = ""
)
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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