Merge branch 'main' into add-cwe-208

This commit is contained in:
Brandon Stewart
2023-08-10 16:14:04 -04:00
committed by GitHub
44 changed files with 2053 additions and 1209 deletions

View File

@@ -24,7 +24,7 @@ import semmle.code.cpp.security.BufferWrite
from BufferWrite bw, int destSize
where
bw.hasExplicitLimit() and // has an explicit size limit
destSize = getBufferSize(bw.getDest(), _) and
destSize = max(getBufferSize(bw.getDest(), _)) and
bw.getExplicitLimit() > destSize // but it's larger than the destination
select bw,
"This '" + bw.getBWDesc() + "' operation is limited to " + bw.getExplicitLimit() +

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The `cpp/badly-bounded-write` query could report false positives when a pointer was first initialized with a literal and later assigned a dynamically allocated array. These false positives now no longer occur.

View File

@@ -1,10 +1,10 @@
| tests2.cpp:17:3:17:8 | call to wcscpy | This 'call to wcscpy' operation requires 12 bytes but the destination is only 8 bytes. |
| tests2.cpp:22:3:22:8 | call to wcscpy | This 'call to wcscpy' operation requires 16 bytes but the destination is only 12 bytes. |
| tests2.cpp:27:3:27:8 | call to wcscpy | This 'call to wcscpy' operation requires 20 bytes but the destination is only 16 bytes. |
| tests2.cpp:31:3:31:8 | call to wcscpy | This 'call to wcscpy' operation requires 24 bytes but the destination is only 20 bytes. |
| tests2.cpp:36:3:36:8 | call to wcscpy | This 'call to wcscpy' operation requires 28 bytes but the destination is only 24 bytes. |
| tests2.cpp:41:3:41:8 | call to wcscpy | This 'call to wcscpy' operation requires 32 bytes but the destination is only 28 bytes. |
| tests2.cpp:46:3:46:8 | call to wcscpy | This 'call to wcscpy' operation requires 36 bytes but the destination is only 32 bytes. |
| tests2.cpp:18:3:18:8 | call to wcscpy | This 'call to wcscpy' operation requires 12 bytes but the destination is only 8 bytes. |
| tests2.cpp:23:3:23:8 | call to wcscpy | This 'call to wcscpy' operation requires 16 bytes but the destination is only 12 bytes. |
| tests2.cpp:28:3:28:8 | call to wcscpy | This 'call to wcscpy' operation requires 20 bytes but the destination is only 16 bytes. |
| tests2.cpp:32:3:32:8 | call to wcscpy | This 'call to wcscpy' operation requires 24 bytes but the destination is only 20 bytes. |
| tests2.cpp:37:3:37:8 | call to wcscpy | This 'call to wcscpy' operation requires 28 bytes but the destination is only 24 bytes. |
| tests2.cpp:42:3:42:8 | call to wcscpy | This 'call to wcscpy' operation requires 32 bytes but the destination is only 28 bytes. |
| tests2.cpp:47:3:47:8 | call to wcscpy | This 'call to wcscpy' operation requires 36 bytes but the destination is only 32 bytes. |
| tests.c:54:3:54:9 | call to sprintf | This 'call to sprintf' operation requires 11 bytes but the destination is only 10 bytes. |
| tests.c:58:3:58:9 | call to sprintf | This 'call to sprintf' operation requires 11 bytes but the destination is only 10 bytes. |
| tests.c:62:17:62:24 | buffer10 | This 'scanf string argument' operation requires 11 bytes but the destination is only 10 bytes. |

View File

@@ -6,6 +6,7 @@ void *realloc(void *ptr, size_t size);
void *calloc(size_t nmemb, size_t size);
void free(void *ptr);
wchar_t *wcscpy(wchar_t *s1, const wchar_t *s2);
int snprintf(char *s, size_t n, const char *format, ...);
// --- Semmle tests ---
@@ -46,3 +47,18 @@ void tests2() {
wcscpy(buffer, L"12345678"); // BAD: buffer overflow
delete [] buffer;
}
char* dest1 = "a";
char* dest2 = "abcdefghijklmnopqrstuvwxyz";
void test3() {
const char src[] = "abcdefghijkl";
dest1 = (char*)malloc(sizeof(src));
if (!dest1)
return;
snprintf(dest1, sizeof(src), "%s", src); // GOOD
dest2 = (char*)malloc(3);
if (!dest2)
return;
snprintf(dest2, sizeof(src), "%s", src); // BAD [NOT DETECTED]: buffer overflow
}

View File

@@ -3,7 +3,9 @@
* Provides helper classes and methods related to LINQ.
*/
import csharp
private import csharp
private import semmle.code.csharp.frameworks.system.collections.Generic as GenericCollections
private import semmle.code.csharp.frameworks.system.Collections as Collections
//#################### PREDICATES ####################
private Stmt firstStmt(ForeachStmt fes) {
@@ -29,13 +31,40 @@ predicate isIEnumerableType(ValueOrRefType t) {
)
}
/**
* A class of foreach statements where the iterable expression
* supports the use of the LINQ extension methods on `IEnumerable<T>`.
*/
class ForeachStmtGenericEnumerable extends ForeachStmt {
ForeachStmtGenericEnumerable() {
exists(ValueOrRefType t | t = this.getIterableExpr().getType() |
t.getABaseType*().getUnboundDeclaration() instanceof
GenericCollections::SystemCollectionsGenericIEnumerableTInterface or
t.(ArrayType).getRank() = 1
)
}
}
/**
* A class of foreach statements where the iterable expression
* supports the use of the LINQ extension methods on `IEnumerable`.
*/
class ForeachStmtEnumerable extends ForeachStmt {
ForeachStmtEnumerable() {
exists(ValueOrRefType t | t = this.getIterableExpr().getType() |
t.getABaseType*() instanceof Collections::SystemCollectionsIEnumerableInterface or
t.(ArrayType).getRank() = 1
)
}
}
/**
* Holds if `foreach` statement `fes` could be converted to a `.All()` call.
* That is, the `ForeachStmt` contains a single `if` with a condition that
* accesses the loop variable and with a body that assigns `false` to a variable
* and `break`s out of the `foreach`.
*/
predicate missedAllOpportunity(ForeachStmt fes) {
predicate missedAllOpportunity(ForeachStmtGenericEnumerable fes) {
exists(IfStmt is |
// The loop contains an if statement with no else case, and nothing else.
is = firstStmt(fes) and
@@ -54,12 +83,12 @@ predicate missedAllOpportunity(ForeachStmt fes) {
}
/**
* Holds if `foreach` statement `fes` could be converted to a `.Cast()` call.
* Holds if the `foreach` statement `fes` can be converted to a `.Cast()` call.
* That is, the loop variable is accessed only in the first statement of the
* block, and the access is a cast. The first statement needs to be a
* `LocalVariableDeclStmt`.
* block, the access is a cast, and the first statement is a
* local variable declaration statement `s`.
*/
predicate missedCastOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
predicate missedCastOpportunity(ForeachStmtEnumerable fes, LocalVariableDeclStmt s) {
s = firstStmt(fes) and
forex(VariableAccess va | va = fes.getVariable().getAnAccess() |
va = s.getAVariableDeclExpr().getAChildExpr*()
@@ -71,12 +100,12 @@ predicate missedCastOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
}
/**
* Holds if `foreach` statement `fes` could be converted to an `.OfType()` call.
* Holds if `foreach` statement `fes` can be converted to an `.OfType()` call.
* That is, the loop variable is accessed only in the first statement of the
* block, and the access is a cast with the `as` operator. The first statement
* needs to be a `LocalVariableDeclStmt`.
* block, the access is a cast with the `as` operator, and the first statement
* is a local variable declaration statement `s`.
*/
predicate missedOfTypeOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
predicate missedOfTypeOpportunity(ForeachStmtEnumerable fes, LocalVariableDeclStmt s) {
s = firstStmt(fes) and
forex(VariableAccess va | va = fes.getVariable().getAnAccess() |
va = s.getAVariableDeclExpr().getAChildExpr*()
@@ -88,12 +117,12 @@ predicate missedOfTypeOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
}
/**
* Holds if `foreach` statement `fes` could be converted to a `.Select()` call.
* Holds if `foreach` statement `fes` can be converted to a `.Select()` call.
* That is, the loop variable is accessed only in the first statement of the
* block, and the access is not a cast. The first statement needs to be a
* `LocalVariableDeclStmt`.
* block, the access is not a cast, and the first statement is a
* local variable declaration statement `s`.
*/
predicate missedSelectOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
predicate missedSelectOpportunity(ForeachStmtGenericEnumerable fes, LocalVariableDeclStmt s) {
s = firstStmt(fes) and
forex(VariableAccess va | va = fes.getVariable().getAnAccess() |
va = s.getAVariableDeclExpr().getAChildExpr*()
@@ -107,7 +136,7 @@ predicate missedSelectOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
* variable, and the body of the `if` is either a `continue` or there's nothing
* else in the loop than the `if`.
*/
predicate missedWhereOpportunity(ForeachStmt fes, IfStmt is) {
predicate missedWhereOpportunity(ForeachStmtGenericEnumerable fes, IfStmt is) {
// The very first thing the foreach loop does is test its iteration variable.
is = firstStmt(fes) and
exists(VariableAccess va |

View File

@@ -861,6 +861,12 @@ class YieldReturnStmt extends YieldStmt {
override string getAPrimaryQlClass() { result = "YieldReturnStmt" }
}
bindingset[cfe1, cfe2]
pragma[inline_late]
private predicate sameCallable(ControlFlowElement cfe1, ControlFlowElement cfe2) {
cfe1.getEnclosingCallable() = cfe2.getEnclosingCallable()
}
/**
* A `try` statement, for example
*
@@ -947,8 +953,7 @@ class TryStmt extends Stmt, @try_stmt {
mid = this.getATriedElement() and
not mid instanceof TryStmt and
result = mid.getAChild() and
pragma[only_bind_into](mid.getEnclosingCallable()) =
pragma[only_bind_into](result.getEnclosingCallable())
sameCallable(mid, result)
)
}
}

View File

@@ -10,7 +10,6 @@
* language-features
*/
import csharp
import Linq.Helpers
/*
@@ -31,7 +30,7 @@ import Linq.Helpers
* bool allEven = lst.All(i => i % 2 == 0);
*/
from ForeachStmt fes
from ForeachStmtGenericEnumerable fes
where missedAllOpportunity(fes)
select fes,
"This foreach loop looks as if it might be testing whether every sequence element satisfies a predicate - consider using '.All(...)'."

View File

@@ -13,7 +13,7 @@
import csharp
import Linq.Helpers
from ForeachStmt fes, LocalVariableDeclStmt s
from ForeachStmtEnumerable fes, LocalVariableDeclStmt s
where missedCastOpportunity(fes, s)
select fes,
"This foreach loop immediately $@ - consider casting the sequence explicitly using '.Cast(...)'.",

View File

@@ -13,7 +13,7 @@
import csharp
import Linq.Helpers
from ForeachStmt fes, LocalVariableDeclStmt s
from ForeachStmtEnumerable fes, LocalVariableDeclStmt s
where missedOfTypeOpportunity(fes, s)
select fes,
"This foreach loop immediately uses 'as' to $@ - consider using '.OfType(...)' instead.", s,

View File

@@ -20,7 +20,7 @@ predicate oversized(LocalVariableDeclStmt s) {
)
}
from ForeachStmt fes, LocalVariableDeclStmt s
from ForeachStmtGenericEnumerable fes, LocalVariableDeclStmt s
where
missedSelectOpportunity(fes, s) and
not oversized(s)

View File

@@ -12,7 +12,7 @@
import csharp
import Linq.Helpers
from ForeachStmt fes, IfStmt is
from ForeachStmtGenericEnumerable fes, IfStmt is
where
missedWhereOpportunity(fes, is) and
not missedAllOpportunity(fes)

View File

@@ -0,0 +1,78 @@
using System;
using System.Collections;
using System.Collections.Generic;
class MissedCastOpportunity
{
public void M1(List<Animal> animals)
{
// BAD: Can be replaced with animals.Cast<Dog>().
foreach (Animal a in animals)
{
Dog d = (Dog)a;
d.Woof();
}
}
public void M2(NonEnumerableClass nec)
{
// GOOD: Not possible to use Linq here.
foreach (Animal a in nec)
{
Dog d = (Dog)a;
d.Woof();
}
}
public void M3(Animal[] animals)
{
// BAD: Can be replaced with animals.Cast<Dog>().
foreach (Animal animal in animals)
{
Dog d = (Dog)animal;
d.Woof();
}
}
public void M4(Array animals)
{
// BAD: Can be replaced with animals.Cast<Dog>().
foreach (Animal animal in animals)
{
Dog d = (Dog)animal;
d.Woof();
}
}
public void M5(IEnumerable animals)
{
// BAD: Can be replaced with animals.Cast<Dog>().
foreach (object animal in animals)
{
Dog d = (Dog)animal;
d.Woof();
}
}
public class NonEnumerableClass
{
public IEnumerator<Animal> GetEnumerator() => throw null;
}
public class Animal { }
class Dog : Animal
{
private string name;
public Dog(string name)
{
this.name = name;
}
public void Woof()
{
Console.WriteLine("Woof! My name is " + name + ".");
}
}
}

View File

@@ -0,0 +1,4 @@
| MissedCastOpportunity.cs:10:9:14:9 | foreach (... ... in ...) ... | This foreach loop immediately $@ - consider casting the sequence explicitly using '.Cast(...)'. | MissedCastOpportunity.cs:12:13:12:27 | ... ...; | casts its iteration variable to another type |
| MissedCastOpportunity.cs:30:9:34:9 | foreach (... ... in ...) ... | This foreach loop immediately $@ - consider casting the sequence explicitly using '.Cast(...)'. | MissedCastOpportunity.cs:32:13:32:32 | ... ...; | casts its iteration variable to another type |
| MissedCastOpportunity.cs:40:9:44:9 | foreach (... ... in ...) ... | This foreach loop immediately $@ - consider casting the sequence explicitly using '.Cast(...)'. | MissedCastOpportunity.cs:42:13:42:32 | ... ...; | casts its iteration variable to another type |
| MissedCastOpportunity.cs:50:9:54:9 | foreach (... ... in ...) ... | This foreach loop immediately $@ - consider casting the sequence explicitly using '.Cast(...)'. | MissedCastOpportunity.cs:52:13:52:32 | ... ...; | casts its iteration variable to another type |

View File

@@ -0,0 +1 @@
Linq/MissedCastOpportunity.ql

View File

@@ -0,0 +1,2 @@
semmle-extractor-options: /nostdlib /noconfig
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj

View File

@@ -0,0 +1,83 @@
using System;
using System.Linq;
using System.Collections.Generic;
class MissedWhereOpportunity
{
public void M1(List<int> lst)
{
// BAD: Can be replaced with lst.Where(e => e % 2 == 0)
foreach (int i in lst)
{
if (i % 2 != 0)
continue;
Console.WriteLine(i);
Console.WriteLine((i / 2));
}
// BAD: Can be replaced with lst.Where(e => e % 2 == 0)
foreach (int i in lst)
{
if (i % 2 == 0)
{
Console.WriteLine(i);
Console.WriteLine((i / 2));
}
}
}
public void M2(NonEnumerableClass nec)
{
// GOOD: Linq can't be used here.
foreach (int i in nec)
{
if (i % 2 == 0)
{
Console.WriteLine(i);
Console.WriteLine((i / 2));
}
}
}
public void M3(int[] arr)
{
// BAD: Can be replaced with arr.Where(e => e % 2 == 0)
foreach (var n in arr)
{
if (n % 2 == 0)
{
Console.WriteLine(n);
Console.WriteLine((n / 2));
}
}
}
public void M4(Array arr)
{
// GOOD: Linq can't be used here.
foreach (var element in arr)
{
if (element.GetHashCode() % 2 == 0)
{
Console.WriteLine(element);
}
}
}
public void M5(IEnumerable<int> elements)
{
// BAD: Can be replaced with elements.Where(e => e.GetHashCode() % 2 == 0)
foreach (var element in elements)
{
if (element.GetHashCode() % 2 == 0)
{
Console.WriteLine(element);
}
}
}
public class NonEnumerableClass
{
public IEnumerator<int> GetEnumerator() => throw null;
}
}

View File

@@ -0,0 +1,4 @@
| MissedWhereOpportunity.cs:10:9:16:9 | foreach (... ... in ...) ... | This foreach loop $@ - consider filtering the sequence explicitly using '.Where(...)'. | MissedWhereOpportunity.cs:12:17:12:26 | ... != ... | implicitly filters its target sequence |
| MissedWhereOpportunity.cs:19:9:26:9 | foreach (... ... in ...) ... | This foreach loop $@ - consider filtering the sequence explicitly using '.Where(...)'. | MissedWhereOpportunity.cs:21:17:21:26 | ... == ... | implicitly filters its target sequence |
| MissedWhereOpportunity.cs:45:9:52:9 | foreach (... ... in ...) ... | This foreach loop $@ - consider filtering the sequence explicitly using '.Where(...)'. | MissedWhereOpportunity.cs:47:17:47:26 | ... == ... | implicitly filters its target sequence |
| MissedWhereOpportunity.cs:70:9:76:9 | foreach (... ... in ...) ... | This foreach loop $@ - consider filtering the sequence explicitly using '.Where(...)'. | MissedWhereOpportunity.cs:72:17:72:46 | ... == ... | implicitly filters its target sequence |

View File

@@ -0,0 +1 @@
Linq/MissedWhereOpportunity.ql

View File

@@ -0,0 +1,2 @@
semmle-extractor-options: /nostdlib /noconfig
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj

View File

@@ -310,3 +310,4 @@ and the CodeQL library pack ``codeql/swift-all`` (`changelog <https://github.com
`SQLite3 <https://sqlite.org/index.html>`__, Database
`SQLite.swift <https://github.com/stephencelis/SQLite.swift>`__, Database
`WebKit <https://developer.apple.com/documentation/webkit>`__, User interface library
`UIKit <https://developer.apple.com/documentation/uikit>`__, User interface library

View File

@@ -552,9 +552,13 @@ open class KotlinFileExtractor(
logger.warnElement("Expected annotation property to define a getter", prop)
} else {
val getterId = useFunction<DbMethod>(getter)
val exprId = extractAnnotationValueExpression(v, id, i, "{$getterId}", getter.returnType, extractEnumTypeAccesses)
if (exprId != null) {
tw.writeAnnotValue(id, getterId, exprId)
if (getterId == null) {
logger.errorElement("Couldn't get ID for getter", getter)
} else {
val exprId = extractAnnotationValueExpression(v, id, i, "{$getterId}", getter.returnType, extractEnumTypeAccesses)
if (exprId != null) {
tw.writeAnnotValue(id, getterId, exprId)
}
}
}
}
@@ -979,6 +983,10 @@ open class KotlinFileExtractor(
private fun extractInstanceInitializerBlock(parent: StmtParent, enclosingConstructor: IrConstructor) {
with("object initializer block", enclosingConstructor) {
val constructorId = useFunction<DbConstructor>(enclosingConstructor)
if (constructorId == null) {
logger.errorElement("Cannot get ID for constructor", enclosingConstructor)
return
}
val enclosingClass = enclosingConstructor.parentClassOrNull
if (enclosingClass == null) {
logger.errorElement("Constructor's parent is not a class", enclosingConstructor)
@@ -1410,10 +1418,17 @@ open class KotlinFileExtractor(
val sourceDeclaration =
overriddenAttributes?.sourceDeclarationId ?:
if (typeSubstitution != null && overriddenAttributes?.id == null)
useFunction(f)
else
if (typeSubstitution != null && overriddenAttributes?.id == null) {
val sourceFunId = useFunction<DbCallable>(f)
if (sourceFunId == null) {
logger.errorElement("Cannot get source ID for function", f)
id // TODO: This is wrong; we ought to just fail in this case
} else {
sourceFunId
}
} else {
id
}
val extReceiver = f.extensionReceiverParameter
// The following parameter order is correct, because member $default methods (where the order would be [dispatchParam], [extensionParam], normalParams) are not extracted here
@@ -2926,7 +2941,11 @@ open class KotlinFileExtractor(
tw.writeStmts_throwstmt(throwId, stmtParent.parent, stmtParent.idx, callable)
tw.writeHasLocation(throwId, locId)
val newExprId = extractNewExpr(it, null, thrownType, locId, throwId, 0, callable, throwId)
extractTypeAccess(thrownType, locId, newExprId, -3, callable, throwId)
if (newExprId == null) {
logger.errorElement("No ID for newExpr in noWhenBranchMatchedException", c)
} else {
extractTypeAccess(thrownType, locId, newExprId, -3, callable, throwId)
}
}
}
isBuiltinCallInternal(c, "illegalArgumentException") -> {
@@ -3270,7 +3289,14 @@ open class KotlinFileExtractor(
idx: Int,
callable: Label<out DbCallable>,
enclosingStmt: Label<out DbStmt>
): Label<DbNewexpr> = extractNewExpr(useFunction<DbConstructor>(calledConstructor, constructorTypeArgs), constructedType, locId, parent, idx, callable, enclosingStmt)
): Label<DbNewexpr>? {
val funId = useFunction<DbConstructor>(calledConstructor, constructorTypeArgs)
if (funId == null) {
logger.error("Cannot get ID for newExpr function")
return null
}
return extractNewExpr(funId, constructedType, locId, parent, idx, callable, enclosingStmt)
}
private fun needsObinitFunction(c: IrClass) = c.primaryConstructor == null && c.constructors.count() > 1
@@ -3310,26 +3336,31 @@ open class KotlinFileExtractor(
extractDefaultsCallArguments(it, e.symbol.owner, callable, enclosingStmt, valueArgs, null, null)
}
} else {
extractNewExpr(e.symbol.owner, eType.arguments, type, locId, parent, idx, callable, enclosingStmt).also {
val realCallTarget = e.symbol.owner.realOverrideTarget
// Generated constructor calls to kotlin.Enum have no arguments in IR, but the constructor takes two parameters.
if (e is IrEnumConstructorCall &&
realCallTarget is IrConstructor &&
realCallTarget.parentClassOrNull?.fqNameWhenAvailable?.asString() == "kotlin.Enum" &&
realCallTarget.valueParameters.size == 2 &&
realCallTarget.valueParameters[0].type == pluginContext.irBuiltIns.stringType &&
realCallTarget.valueParameters[1].type == pluginContext.irBuiltIns.intType) {
val id0 = extractNull(pluginContext.irBuiltIns.stringType, locId, it, 0, callable, enclosingStmt)
tw.writeCompiler_generated(id0, CompilerGeneratedKinds.ENUM_CONSTRUCTOR_ARGUMENT.kind)
val id1 = extractConstantInteger(0, locId, it, 1, callable, enclosingStmt)
tw.writeCompiler_generated(id1, CompilerGeneratedKinds.ENUM_CONSTRUCTOR_ARGUMENT.kind)
} else {
extractCallValueArguments(it, e, enclosingStmt, callable, 0)
}
val newExprId = extractNewExpr(e.symbol.owner, eType.arguments, type, locId, parent, idx, callable, enclosingStmt)
if (newExprId == null) {
logger.errorElement("Cannot get newExpr ID", e)
return
}
val realCallTarget = e.symbol.owner.realOverrideTarget
// Generated constructor calls to kotlin.Enum have no arguments in IR, but the constructor takes two parameters.
if (e is IrEnumConstructorCall &&
realCallTarget is IrConstructor &&
realCallTarget.parentClassOrNull?.fqNameWhenAvailable?.asString() == "kotlin.Enum" &&
realCallTarget.valueParameters.size == 2 &&
realCallTarget.valueParameters[0].type == pluginContext.irBuiltIns.stringType &&
realCallTarget.valueParameters[1].type == pluginContext.irBuiltIns.intType) {
val id0 = extractNull(pluginContext.irBuiltIns.stringType, locId, newExprId, 0, callable, enclosingStmt)
tw.writeCompiler_generated(id0, CompilerGeneratedKinds.ENUM_CONSTRUCTOR_ARGUMENT.kind)
val id1 = extractConstantInteger(0, locId, newExprId, 1, callable, enclosingStmt)
tw.writeCompiler_generated(id1, CompilerGeneratedKinds.ENUM_CONSTRUCTOR_ARGUMENT.kind)
} else {
extractCallValueArguments(newExprId, e, enclosingStmt, callable, 0)
}
newExprId
}
if (isAnonymous) {
@@ -3698,9 +3729,13 @@ open class KotlinFileExtractor(
val locId = tw.getLocation(e)
val methodId = useFunction<DbConstructor>(e.symbol.owner)
if (methodId == null) {
logger.errorElement("Cannot get ID for delegating constructor", e)
} else {
tw.writeCallableBinding(id.cast<DbCaller>(), methodId)
}
tw.writeHasLocation(id, locId)
tw.writeCallableBinding(id.cast<DbCaller>(), methodId)
extractCallValueArguments(id, e, id, callable, 0)
val dr = e.dispatchReceiver
if (dr != null) {
@@ -4636,7 +4671,11 @@ open class KotlinFileExtractor(
extractExprContext(callId, locId, labels.methodId, retId)
val callableId = useFunction<DbCallable>(target.owner.realOverrideTarget, classTypeArgsIncludingOuterClasses)
tw.writeCallableBinding(callId.cast<DbCaller>(), callableId)
if (callableId == null) {
logger.error("Cannot get ID for reflection target")
} else {
tw.writeCallableBinding(callId.cast<DbCaller>(), callableId)
}
val useFirstArgAsDispatch: Boolean
if (dispatchReceiverInfo != null) {
@@ -4818,20 +4857,24 @@ open class KotlinFileExtractor(
val getterReturnType = parameterTypes.last()
if (getter != null) {
val getLabels = addFunctionManual(tw.getFreshIdLabel(), OperatorNameConventions.GET.asString(), getterParameterTypes, getterReturnType, classId, locId)
val getterCallableId = useFunction<DbCallable>(getter.owner.realOverrideTarget, classTypeArguments)
if (getterCallableId == null) {
logger.errorElement("Cannot get ID for getter", propertyReferenceExpr)
} else {
val getLabels = addFunctionManual(tw.getFreshIdLabel(), OperatorNameConventions.GET.asString(), getterParameterTypes, getterReturnType, classId, locId)
helper.extractCallToReflectionTarget(
getLabels,
getter,
getterReturnType,
expressionTypeArguments,
classTypeArguments
)
helper.extractCallToReflectionTarget(
getLabels,
getter,
getterReturnType,
expressionTypeArguments,
classTypeArguments
)
tw.writePropertyRefGetBinding(idPropertyRef, getterCallableId)
tw.writePropertyRefGetBinding(idPropertyRef, getterCallableId)
helper.extractPropertyReferenceInvoke(getLabels.methodId, getterParameterTypes, getterReturnType)
helper.extractPropertyReferenceInvoke(getLabels.methodId, getterParameterTypes, getterReturnType)
}
} else {
// Property without a getter.
if (backingField == null) {
@@ -4852,19 +4895,22 @@ open class KotlinFileExtractor(
}
if (setter != null) {
val setLabels = addFunctionManual(tw.getFreshIdLabel(), OperatorNameConventions.SET.asString(), parameterTypes, pluginContext.irBuiltIns.unitType, classId, locId)
val setterCallableId = useFunction<DbCallable>(setter.owner.realOverrideTarget, classTypeArguments)
if (setterCallableId == null) {
logger.errorElement("Cannot get ID for setter", propertyReferenceExpr)
} else {
val setLabels = addFunctionManual(tw.getFreshIdLabel(), OperatorNameConventions.SET.asString(), parameterTypes, pluginContext.irBuiltIns.unitType, classId, locId)
helper.extractCallToReflectionTarget(
setLabels,
setter,
pluginContext.irBuiltIns.unitType,
expressionTypeArguments,
classTypeArguments
)
helper.extractCallToReflectionTarget(
setLabels,
setter,
pluginContext.irBuiltIns.unitType,
expressionTypeArguments,
classTypeArguments
)
tw.writePropertyRefSetBinding(idPropertyRef, setterCallableId)
tw.writePropertyRefSetBinding(idPropertyRef, setterCallableId)
}
} else {
if (backingField != null && !backingField.owner.isFinal) {
val setLabels = addFunctionManual(tw.getFreshIdLabel(), OperatorNameConventions.SET.asString(), parameterTypes, pluginContext.irBuiltIns.unitType, classId, locId)
@@ -4999,7 +5045,11 @@ open class KotlinFileExtractor(
tw.writeCallableBinding(idMemberRef, ids.constructor)
val targetCallableId = useFunction<DbCallable>(target.owner.realOverrideTarget, classTypeArguments)
tw.writeMemberRefBinding(idMemberRef, targetCallableId)
if (targetCallableId == null) {
logger.errorElement("Cannot get ID for function reference callable", functionReferenceExpr)
} else {
tw.writeMemberRefBinding(idMemberRef, targetCallableId)
}
val helper = CallableReferenceHelper(functionReferenceExpr, locId, ids)
@@ -5145,7 +5195,11 @@ open class KotlinFileExtractor(
tw.writeExprsKotlinType(callId, callType.kotlinResult.id)
extractExprContext(callId, locId, funLabels.methodId, retId)
val calledMethodId = useFunction<DbMethod>(lambda)
tw.writeCallableBinding(callId, calledMethodId)
if (calledMethodId == null) {
logger.errorElement("Cannot get ID for called lambda", lambda)
} else {
tw.writeCallableBinding(callId, calledMethodId)
}
// this access
extractThisAccess(ids.type, funLabels.methodId, callId, -1, retId, locId)
@@ -5614,7 +5668,11 @@ open class KotlinFileExtractor(
tw.writeExprsKotlinType(callId, callType.kotlinResult.id)
extractExprContext(callId, locId, ids.function, returnId)
val calledMethodId = useFunction<DbMethod>(invokeMethod, functionType.arguments)
tw.writeCallableBinding(callId, calledMethodId)
if (calledMethodId == null) {
logger.errorElement("Cannot get ID for called method", invokeMethod)
} else {
tw.writeCallableBinding(callId, calledMethodId)
}
// <fn> access
val lhsId = tw.getFreshIdLabel<DbVaraccess>()
@@ -5737,14 +5795,17 @@ open class KotlinFileExtractor(
if (baseConstructor == null) {
logger.warnElement("Cannot find base constructor", elementToReportOn)
} else {
val superCallId = tw.getFreshIdLabel<DbSuperconstructorinvocationstmt>()
tw.writeStmts_superconstructorinvocationstmt(superCallId, constructorBlockId, 0, ids.constructor)
val baseConstructorId = useFunction<DbConstructor>(baseConstructor)
if (baseConstructorId == null) {
logger.errorElement("Cannot find base constructor ID", elementToReportOn)
} else {
val superCallId = tw.getFreshIdLabel<DbSuperconstructorinvocationstmt>()
tw.writeStmts_superconstructorinvocationstmt(superCallId, constructorBlockId, 0, ids.constructor)
tw.writeHasLocation(superCallId, locId)
tw.writeCallableBinding(superCallId.cast<DbCaller>(), baseConstructorId)
extractSuperconstructorArgs(superCallId)
tw.writeHasLocation(superCallId, locId)
tw.writeCallableBinding(superCallId.cast<DbCaller>(), baseConstructorId)
extractSuperconstructorArgs(superCallId)
}
}
}

View File

@@ -1322,16 +1322,30 @@ open class KotlinUsesExtractor(
else -> false
}
fun <T: DbCallable> useFunction(f: IrFunction, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>? = null, noReplace: Boolean = false): Label<out T> {
return useFunction(f, null, classTypeArgsIncludingOuterClasses, noReplace)
}
fun <T: DbCallable> useFunction(f: IrFunction, parentId: Label<out DbElement>?, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?, noReplace: Boolean = false): Label<out T> {
fun <T: DbCallable> useFunction(f: IrFunction, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>? = null, noReplace: Boolean = false): Label<out T>? {
if (f.isLocalFunction()) {
val ids = getLocallyVisibleFunctionLabels(f)
return ids.function.cast<T>()
}
val javaFun = kotlinFunctionToJavaEquivalent(f, noReplace)
val parentId = useDeclarationParent(javaFun.parent, false, classTypeArgsIncludingOuterClasses, true)
if (parentId == null) {
logger.error("Couldn't find parent ID for function ${f.name.asString()}")
return null
}
return useFunction(f, javaFun, parentId, classTypeArgsIncludingOuterClasses)
}
fun <T: DbCallable> useFunction(f: IrFunction, parentId: Label<out DbElement>, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?, noReplace: Boolean = false): Label<out T> {
if (f.isLocalFunction()) {
val ids = getLocallyVisibleFunctionLabels(f)
return ids.function.cast<T>()
}
val javaFun = kotlinFunctionToJavaEquivalent(f, noReplace)
return useFunction(f, javaFun, parentId, classTypeArgsIncludingOuterClasses)
}
private fun <T: DbCallable> useFunction(f: IrFunction, javaFun: IrFunction, parentId: Label<out DbElement>, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?): Label<out T> {
val label = getFunctionLabel(javaFun, parentId, classTypeArgsIncludingOuterClasses)
val id: Label<T> = tw.getLabelFor(label) {
extractPrivateSpecialisedDeclaration(f, classTypeArgsIncludingOuterClasses)

View File

@@ -4,8 +4,8 @@ var app = express();
// set up rate limiter: maximum of five requests per minute
var RateLimit = require('express-rate-limit');
var limiter = RateLimit({
windowMs: 1*60*1000, // 1 minute
max: 5
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // max 100 requests per windowMs
});
// apply rate limiter to all requests

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Flow between splat arguments (`*args`) and positional parameters is now tracked more precisely.

View File

@@ -1,5 +1,6 @@
private import TreeSitter
private import codeql.ruby.AST
private import codeql.ruby.CFG
private import codeql.ruby.ast.internal.AST
private import codeql.ruby.ast.internal.Parameter
private import codeql.ruby.ast.internal.Pattern
@@ -364,7 +365,7 @@ private module Cached {
cached
predicate isCapturedAccess(LocalVariableAccess access) {
exists(Scope scope1, Scope scope2 |
exists(Scope scope1, CfgScope scope2 |
scope1 = access.getVariable().getDeclaringScope() and
scope2 = access.getCfgScope() and
scope1 != scope2
@@ -375,10 +376,11 @@ private module Cached {
// class C
// def self.m // not a captured access
// end
//
// self.foo // not a captured access
// end
// ```
not scope2 instanceof Toplevel or
not access = any(SingletonMethod m).getObject()
not scope2 instanceof Toplevel
else any()
)
}

View File

@@ -439,6 +439,7 @@ private module Cached {
} or
THashSplatArgumentPosition() or
TSplatAllArgumentPosition() or
TSplatArgumentPosition(int pos) { exists(Call c | c.getArgument(pos) instanceof SplatExpr) } or
TAnyArgumentPosition() or
TAnyKeywordArgumentPosition()
@@ -468,6 +469,10 @@ private module Cached {
// synthetic parameter position.
TSynthHashSplatParameterPosition() or
TSplatAllParameterPosition() or
TSplatParameterPosition(int pos) {
exists(Parameter p | p.getPosition() = pos and p instanceof SplatParameter)
} or
TSynthSplatParameterPosition() or
TAnyParameterPosition() or
TAnyKeywordParameterPosition()
}
@@ -1288,8 +1293,12 @@ class ParameterPosition extends TParameterPosition {
predicate isSynthHashSplat() { this = TSynthHashSplatParameterPosition() }
predicate isSynthSplat() { this = TSynthSplatParameterPosition() }
predicate isSplatAll() { this = TSplatAllParameterPosition() }
predicate isSplat(int n) { this = TSplatParameterPosition(n) }
/**
* Holds if this position represents any parameter, except `self` parameters. This
* includes both positional, named, and block parameters.
@@ -1320,6 +1329,10 @@ class ParameterPosition extends TParameterPosition {
this.isAny() and result = "any"
or
this.isAnyNamed() and result = "any-named"
or
this.isSynthSplat() and result = "synthetic *"
or
exists(int pos | this.isSplat(pos) and result = "* (position " + pos + ")")
}
}
@@ -1354,6 +1367,8 @@ class ArgumentPosition extends TArgumentPosition {
predicate isSplatAll() { this = TSplatAllArgumentPosition() }
predicate isSplat(int n) { this = TSplatArgumentPosition(n) }
/** Gets a textual representation of this position. */
string toString() {
this.isSelf() and result = "self"
@@ -1371,6 +1386,8 @@ class ArgumentPosition extends TArgumentPosition {
this.isHashSplat() and result = "**"
or
this.isSplatAll() and result = "*"
or
exists(int pos | this.isSplat(pos) and result = "* (position " + pos + ")")
}
}
@@ -1401,6 +1418,11 @@ predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) {
or
ppos.isSplatAll() and apos.isSplatAll()
or
ppos.isSynthSplat() and apos.isSplatAll()
or
// Exact splat match
exists(int n | apos.isSplat(n) and ppos.isSplat(n))
or
ppos.isAny() and argumentPositionIsNotSelf(apos)
or
apos.isAny() and parameterPositionIsNotSelf(ppos)

View File

@@ -245,6 +245,7 @@ private class Argument extends CfgNodes::ExprCfgNode {
not this.getExpr() instanceof BlockArgument and
not this.getExpr().(Pair).getKey().getConstantValue().isSymbol(_) and
not this.getExpr() instanceof HashSplatExpr and
not this.getExpr() instanceof SplatExpr and
arg.isPositional(i)
)
or
@@ -261,8 +262,15 @@ private class Argument extends CfgNodes::ExprCfgNode {
arg.isHashSplat()
or
this = call.getArgument(0) and
not exists(call.getArgument(1)) and
this.getExpr() instanceof SplatExpr and
arg.isSplatAll()
or
exists(int pos | pos > 0 or exists(call.getArgument(pos + 1)) |
this = call.getArgument(pos) and
this.getExpr() instanceof SplatExpr and
arg.isSplat(pos)
)
}
/** Holds if this expression is the `i`th argument of `c`. */
@@ -300,6 +308,10 @@ private module Cached {
TSynthHashSplatParameterNode(DataFlowCallable c) {
isParameterNode(_, c, any(ParameterPosition p | p.isKeyword(_)))
} or
TSynthSplatParameterNode(DataFlowCallable c) {
exists(c.asCallable()) and // exclude library callables
isParameterNode(_, c, any(ParameterPosition p | p.isPositional(_)))
} or
TExprPostUpdateNode(CfgNodes::ExprCfgNode n) {
// filter out nodes that clearly don't need post-update nodes
isNonConstantExpr(n) and
@@ -318,7 +330,7 @@ private module Cached {
class TSourceParameterNode =
TNormalParameterNode or TBlockParameterNode or TSelfParameterNode or
TSynthHashSplatParameterNode;
TSynthHashSplatParameterNode or TSynthSplatParameterNode;
cached
Location getLocation(NodeImpl n) { result = n.getLocationImpl() }
@@ -514,6 +526,8 @@ predicate nodeIsHidden(Node n) {
n instanceof SynthHashSplatParameterNode
or
n instanceof SynthHashSplatArgumentNode
or
n instanceof SynthSplatParameterNode
}
/** An SSA definition, viewed as a node in a data flow graph. */
@@ -610,7 +624,15 @@ private module ParameterNodes {
pos.isHashSplat()
or
parameter = callable.getParameter(0).(SplatParameter) and
not exists(callable.getParameter(1)) and
pos.isSplatAll()
or
exists(int n | n > 0 |
parameter = callable.getParameter(n).(SplatParameter) and
pos.isSplat(n) and
// There are no positional parameters after the splat
not exists(SimpleParameter p, int m | m > n | p = callable.getParameter(m))
)
)
}
@@ -749,6 +771,70 @@ private module ParameterNodes {
final override string toStringImpl() { result = "**kwargs" }
}
/**
* A synthetic data-flow node to allow flow to positional parameters from a splat argument.
*
* For example, in the following code:
*
* ```rb
* def foo(x, y); end
*
* foo(*[a, b])
* ```
*
* We want `a` to flow to `x` and `b` to flow to `y`. We do this by constructing
* a `SynthSplatParameterNode` for the method `foo`, and matching the splat argument to this
* parameter node via `parameterMatch/2`. We then add read steps from this node to parameters
* `x` and `y`, for content at indices 0 and 1 respectively (see `readStep`).
*
* We don't yet correctly handle cases where the splat argument is not the first argument, e.g. in
* ```rb
* foo(a, *[b])
* ```
*/
class SynthSplatParameterNode extends ParameterNodeImpl, TSynthSplatParameterNode {
private DataFlowCallable callable;
SynthSplatParameterNode() { this = TSynthSplatParameterNode(callable) }
/**
* Gets a parameter which will contain the value given by `c`, assuming
* that the method was called with a single splat argument.
* For example, if the synth splat parameter is for the following method
*
* ```rb
* def foo(x, y, a:, *rest)
* end
* ```
*
* Then `getAParameter(element 0) = x` and `getAParameter(element 1) = y`.
*/
ParameterNode getAParameter(ContentSet c) {
exists(int n |
isParameterNode(result, callable, (any(ParameterPosition p | p.isPositional(n)))) and
(
c = getPositionalContent(n)
or
c.isSingleton(TUnknownElementContent())
)
)
}
final override Parameter getParameter() { none() }
final override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
c = callable and pos.isSynthSplat()
}
final override CfgScope getCfgScope() { result = callable.asCallable() }
final override DataFlowCallable getEnclosingCallable() { result = callable }
final override Location getLocationImpl() { result = callable.getLocation() }
final override string toStringImpl() { result = "synthetic *args" }
}
/** A parameter for a library callable with a flow summary. */
class SummaryParameterNode extends ParameterNodeImpl, FlowSummaryNode {
private ParameterPosition pos_;
@@ -1099,6 +1185,13 @@ private ContentSet getKeywordContent(string name) {
)
}
private ContentSet getPositionalContent(int n) {
exists(ConstantValue::ConstantIntegerValue i |
result.isSingleton(TKnownElementContent(i)) and
i.isInt(n)
)
}
/**
* Subset of `storeStep` that should be shared with type-tracking.
*/
@@ -1187,6 +1280,8 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
or
node2 = node1.(SynthHashSplatParameterNode).getAKeywordParameter(c)
or
node2 = node1.(SynthSplatParameterNode).getAParameter(c)
or
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
node2.(FlowSummaryNode).getSummaryNode())
}

View File

@@ -2796,6 +2796,7 @@
| UseUseExplosion.rb:21:3675:21:3680 | call to use | UseUseExplosion.rb:21:3670:21:3680 | else ... |
| UseUseExplosion.rb:21:3686:21:3696 | else ... | UseUseExplosion.rb:21:9:21:3700 | if ... |
| UseUseExplosion.rb:21:3691:21:3696 | call to use | UseUseExplosion.rb:21:3686:21:3696 | else ... |
| UseUseExplosion.rb:24:5:25:7 | synthetic *args | UseUseExplosion.rb:24:13:24:13 | i |
| UseUseExplosion.rb:24:5:25:7 | use | UseUseExplosion.rb:1:1:26:3 | C |
| file://:0:0:0:0 | [summary param] position 0 in & | file://:0:0:0:0 | [summary] read: Argument[0].Element[any] in & |
| file://:0:0:0:0 | [summary param] position 0 in + | file://:0:0:0:0 | [summary] read: Argument[0].Element[any] in + |
@@ -2840,6 +2841,7 @@
| file://:0:0:0:0 | [summary] read: Argument[0].Element[any] in Hash[] | file://:0:0:0:0 | [summary] read: Argument[0].Element[any].Element[1] in Hash[] |
| local_dataflow.rb:1:1:7:3 | self (foo) | local_dataflow.rb:3:8:3:10 | self |
| local_dataflow.rb:1:1:7:3 | self in foo | local_dataflow.rb:1:1:7:3 | self (foo) |
| local_dataflow.rb:1:1:7:3 | synthetic *args | local_dataflow.rb:1:9:1:9 | a |
| local_dataflow.rb:1:1:150:3 | self (local_dataflow.rb) | local_dataflow.rb:49:1:53:3 | self |
| local_dataflow.rb:1:9:1:9 | a | local_dataflow.rb:1:9:1:9 | a |
| local_dataflow.rb:1:9:1:9 | a | local_dataflow.rb:2:7:2:7 | a |
@@ -2874,6 +2876,7 @@
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | __synth__0__1 |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:9:10:9 | x |
| local_dataflow.rb:10:5:13:3 | call to each | local_dataflow.rb:10:1:13:3 | ... = ... |
| local_dataflow.rb:10:5:13:3 | synthetic *args | local_dataflow.rb:10:5:13:3 | __synth__0__1 |
| local_dataflow.rb:10:9:10:9 | x | local_dataflow.rb:12:5:12:5 | x |
| local_dataflow.rb:10:14:10:18 | [post] array | local_dataflow.rb:15:10:15:14 | array |
| local_dataflow.rb:10:14:10:18 | array | local_dataflow.rb:15:10:15:14 | array |
@@ -2883,6 +2886,7 @@
| local_dataflow.rb:15:1:17:3 | __synth__0__1 | local_dataflow.rb:15:1:17:3 | __synth__0__1 |
| local_dataflow.rb:15:1:17:3 | __synth__0__1 | local_dataflow.rb:15:1:17:3 | __synth__0__1 |
| local_dataflow.rb:15:1:17:3 | __synth__0__1 | local_dataflow.rb:15:5:15:5 | x |
| local_dataflow.rb:15:1:17:3 | synthetic *args | local_dataflow.rb:15:1:17:3 | __synth__0__1 |
| local_dataflow.rb:15:10:15:14 | [post] array | local_dataflow.rb:19:10:19:14 | array |
| local_dataflow.rb:15:10:15:14 | array | local_dataflow.rb:19:10:19:14 | array |
| local_dataflow.rb:16:9:16:10 | 10 | local_dataflow.rb:16:3:16:10 | break |
@@ -2890,6 +2894,7 @@
| local_dataflow.rb:19:1:21:3 | __synth__0__1 | local_dataflow.rb:19:1:21:3 | __synth__0__1 |
| local_dataflow.rb:19:1:21:3 | __synth__0__1 | local_dataflow.rb:19:1:21:3 | __synth__0__1 |
| local_dataflow.rb:19:1:21:3 | __synth__0__1 | local_dataflow.rb:19:5:19:5 | x |
| local_dataflow.rb:19:1:21:3 | synthetic *args | local_dataflow.rb:19:1:21:3 | __synth__0__1 |
| local_dataflow.rb:19:5:19:5 | x | local_dataflow.rb:20:6:20:6 | x |
| local_dataflow.rb:20:6:20:6 | x | local_dataflow.rb:20:6:20:10 | ... > ... |
| local_dataflow.rb:20:10:20:10 | 1 | local_dataflow.rb:20:6:20:10 | ... > ... |
@@ -2901,11 +2906,13 @@
| local_dataflow.rb:30:14:30:20 | "class" | local_dataflow.rb:30:5:30:24 | C |
| local_dataflow.rb:32:5:32:25 | bar | local_dataflow.rb:32:1:32:1 | x |
| local_dataflow.rb:32:5:32:25 | bar | local_dataflow.rb:32:1:32:25 | ... = ... |
| local_dataflow.rb:34:1:39:3 | synthetic *args | local_dataflow.rb:34:7:34:7 | x |
| local_dataflow.rb:34:7:34:7 | x | local_dataflow.rb:34:7:34:7 | x |
| local_dataflow.rb:34:7:34:7 | x | local_dataflow.rb:35:6:35:6 | x |
| local_dataflow.rb:35:6:35:6 | x | local_dataflow.rb:35:6:35:11 | ... == ... |
| local_dataflow.rb:35:11:35:11 | 4 | local_dataflow.rb:35:6:35:11 | ... == ... |
| local_dataflow.rb:36:13:36:13 | 7 | local_dataflow.rb:36:6:36:13 | return |
| local_dataflow.rb:41:1:47:3 | synthetic *args | local_dataflow.rb:41:7:41:7 | x |
| local_dataflow.rb:41:7:41:7 | x | local_dataflow.rb:41:7:41:7 | x |
| local_dataflow.rb:41:7:41:7 | x | local_dataflow.rb:42:6:42:6 | x |
| local_dataflow.rb:42:6:42:6 | x | local_dataflow.rb:42:6:42:11 | ... == ... |
@@ -2924,8 +2931,10 @@
| local_dataflow.rb:51:20:51:20 | x | local_dataflow.rb:51:20:51:24 | ... < ... |
| local_dataflow.rb:51:24:51:24 | 9 | local_dataflow.rb:51:20:51:24 | ... < ... |
| local_dataflow.rb:55:5:55:13 | Array | local_dataflow.rb:55:5:55:13 | call to [] |
| local_dataflow.rb:57:1:58:3 | synthetic *args | local_dataflow.rb:57:9:57:9 | x |
| local_dataflow.rb:60:1:90:3 | self (test_case) | local_dataflow.rb:78:12:78:20 | self |
| local_dataflow.rb:60:1:90:3 | self in test_case | local_dataflow.rb:60:1:90:3 | self (test_case) |
| local_dataflow.rb:60:1:90:3 | synthetic *args | local_dataflow.rb:60:15:60:15 | x |
| local_dataflow.rb:60:15:60:15 | x | local_dataflow.rb:60:15:60:15 | x |
| local_dataflow.rb:60:15:60:15 | x | local_dataflow.rb:61:12:61:12 | x |
| local_dataflow.rb:61:7:68:5 | SSA phi read(x) | local_dataflow.rb:69:12:69:12 | x |
@@ -3098,6 +3107,7 @@
| local_dataflow.rb:118:3:118:11 | call to source | local_dataflow.rb:118:3:118:31 | call to tap |
| local_dataflow.rb:118:3:118:11 | self | local_dataflow.rb:119:3:119:31 | self |
| local_dataflow.rb:118:17:118:31 | <captured entry> self | local_dataflow.rb:118:23:118:29 | self |
| local_dataflow.rb:118:17:118:31 | synthetic *args | local_dataflow.rb:118:20:118:20 | x |
| local_dataflow.rb:118:20:118:20 | x | local_dataflow.rb:118:20:118:20 | x |
| local_dataflow.rb:118:20:118:20 | x | local_dataflow.rb:118:28:118:28 | x |
| local_dataflow.rb:119:3:119:31 | [post] self | local_dataflow.rb:119:8:119:16 | self |
@@ -3112,8 +3122,10 @@
| local_dataflow.rb:123:8:123:20 | call to dup | local_dataflow.rb:123:8:123:45 | call to tap |
| local_dataflow.rb:123:8:123:45 | call to tap | local_dataflow.rb:123:8:123:49 | call to dup |
| local_dataflow.rb:123:26:123:45 | <captured entry> self | local_dataflow.rb:123:32:123:43 | self |
| local_dataflow.rb:123:26:123:45 | synthetic *args | local_dataflow.rb:123:29:123:29 | x |
| local_dataflow.rb:126:1:128:3 | self (use) | local_dataflow.rb:127:3:127:8 | self |
| local_dataflow.rb:126:1:128:3 | self in use | local_dataflow.rb:126:1:128:3 | self (use) |
| local_dataflow.rb:126:1:128:3 | synthetic *args | local_dataflow.rb:126:9:126:9 | x |
| local_dataflow.rb:130:1:150:3 | self (use_use_madness) | local_dataflow.rb:132:6:132:11 | self |
| local_dataflow.rb:130:1:150:3 | self in use_use_madness | local_dataflow.rb:130:1:150:3 | self (use_use_madness) |
| local_dataflow.rb:131:3:131:3 | x | local_dataflow.rb:132:10:132:10 | x |

View File

@@ -42,15 +42,67 @@ edges
| params_flow.rb:41:24:41:29 | ** ... [element :p1] | params_flow.rb:16:13:16:14 | p1 |
| params_flow.rb:41:26:41:29 | args [element :p1] | params_flow.rb:41:24:41:29 | ** ... [element :p1] |
| params_flow.rb:44:12:44:20 | call to taint | params_flow.rb:9:16:9:17 | p1 |
| params_flow.rb:46:1:46:4 | args [element 0] | params_flow.rb:47:13:47:16 | args [element 0] |
| params_flow.rb:46:1:46:4 | args [element 1] | params_flow.rb:47:13:47:16 | args [element 1] |
| params_flow.rb:46:9:46:17 | call to taint | params_flow.rb:46:1:46:4 | args [element 0] |
| params_flow.rb:46:20:46:28 | call to taint | params_flow.rb:46:1:46:4 | args [element 1] |
| params_flow.rb:47:12:47:16 | * ... [element 0] | params_flow.rb:9:16:9:17 | p1 |
| params_flow.rb:47:12:47:16 | * ... [element 1] | params_flow.rb:9:20:9:21 | p2 |
| params_flow.rb:47:13:47:16 | args [element 0] | params_flow.rb:47:12:47:16 | * ... [element 0] |
| params_flow.rb:47:13:47:16 | args [element 1] | params_flow.rb:47:12:47:16 | * ... [element 1] |
| params_flow.rb:49:13:49:14 | p1 | params_flow.rb:50:10:50:11 | p1 |
| params_flow.rb:54:9:54:17 | call to taint | params_flow.rb:49:13:49:14 | p1 |
| params_flow.rb:57:9:57:17 | call to taint | params_flow.rb:49:13:49:14 | p1 |
| params_flow.rb:62:1:62:4 | args | params_flow.rb:66:13:66:16 | args |
| params_flow.rb:62:8:62:16 | call to taint | params_flow.rb:62:1:62:4 | args |
| params_flow.rb:63:16:63:17 | *x [element 0] | params_flow.rb:64:10:64:10 | x [element 0] |
| params_flow.rb:64:10:64:10 | x [element 0] | params_flow.rb:64:10:64:13 | ...[...] |
| params_flow.rb:66:12:66:16 | * ... [element 0] | params_flow.rb:63:16:63:17 | *x [element 0] |
| params_flow.rb:66:13:66:16 | args | params_flow.rb:66:12:66:16 | * ... [element 0] |
| params_flow.rb:49:17:49:24 | *posargs [element 0] | params_flow.rb:51:11:51:17 | posargs [element 0] |
| params_flow.rb:51:11:51:17 | posargs [element 0] | params_flow.rb:51:11:51:20 | ...[...] |
| params_flow.rb:51:11:51:20 | ...[...] | params_flow.rb:51:10:51:21 | ( ... ) |
| params_flow.rb:55:9:55:17 | call to taint | params_flow.rb:49:13:49:14 | p1 |
| params_flow.rb:57:1:57:4 | args [element 0] | params_flow.rb:58:21:58:24 | args [element 0] |
| params_flow.rb:57:9:57:17 | call to taint | params_flow.rb:57:1:57:4 | args [element 0] |
| params_flow.rb:58:9:58:17 | call to taint | params_flow.rb:49:13:49:14 | p1 |
| params_flow.rb:58:20:58:24 | * ... [element 0] | params_flow.rb:49:17:49:24 | *posargs [element 0] |
| params_flow.rb:58:21:58:24 | args [element 0] | params_flow.rb:58:20:58:24 | * ... [element 0] |
| params_flow.rb:60:1:60:4 | args [element 0] | params_flow.rb:61:10:61:13 | args [element 0] |
| params_flow.rb:60:9:60:17 | call to taint | params_flow.rb:60:1:60:4 | args [element 0] |
| params_flow.rb:61:9:61:13 | * ... [element 0] | params_flow.rb:49:13:49:14 | p1 |
| params_flow.rb:61:10:61:13 | args [element 0] | params_flow.rb:61:9:61:13 | * ... [element 0] |
| params_flow.rb:63:1:63:4 | args | params_flow.rb:67:13:67:16 | args |
| params_flow.rb:63:8:63:16 | call to taint | params_flow.rb:63:1:63:4 | args |
| params_flow.rb:64:16:64:17 | *x [element 0] | params_flow.rb:65:10:65:10 | x [element 0] |
| params_flow.rb:65:10:65:10 | x [element 0] | params_flow.rb:65:10:65:13 | ...[...] |
| params_flow.rb:67:12:67:16 | * ... [element 0] | params_flow.rb:64:16:64:17 | *x [element 0] |
| params_flow.rb:67:13:67:16 | args | params_flow.rb:67:12:67:16 | * ... [element 0] |
| params_flow.rb:69:14:69:14 | x | params_flow.rb:70:10:70:10 | x |
| params_flow.rb:69:17:69:17 | y | params_flow.rb:71:10:71:10 | y |
| params_flow.rb:69:24:69:24 | w | params_flow.rb:74:10:74:10 | w |
| params_flow.rb:69:27:69:27 | r | params_flow.rb:75:10:75:10 | r |
| params_flow.rb:78:10:78:18 | call to taint | params_flow.rb:69:14:69:14 | x |
| params_flow.rb:78:21:78:29 | call to taint | params_flow.rb:69:17:69:17 | y |
| params_flow.rb:78:43:78:51 | call to taint | params_flow.rb:69:24:69:24 | w |
| params_flow.rb:78:54:78:62 | call to taint | params_flow.rb:69:27:69:27 | r |
| params_flow.rb:81:10:81:18 | call to taint | params_flow.rb:69:14:69:14 | x |
| params_flow.rb:83:14:83:14 | t | params_flow.rb:84:10:84:10 | t |
| params_flow.rb:83:17:83:17 | u | params_flow.rb:85:10:85:10 | u |
| params_flow.rb:83:23:83:23 | w | params_flow.rb:87:10:87:10 | w |
| params_flow.rb:94:10:94:18 | call to taint | params_flow.rb:83:14:83:14 | t |
| params_flow.rb:94:21:94:29 | call to taint | params_flow.rb:83:17:83:17 | u |
| params_flow.rb:94:39:94:47 | call to taint | params_flow.rb:83:23:83:23 | w |
| params_flow.rb:96:10:96:18 | call to taint | params_flow.rb:69:14:69:14 | x |
| params_flow.rb:96:21:96:29 | call to taint | params_flow.rb:69:17:69:17 | y |
| params_flow.rb:96:68:96:76 | call to taint | params_flow.rb:69:24:69:24 | w |
| params_flow.rb:96:79:96:87 | call to taint | params_flow.rb:69:27:69:27 | r |
| params_flow.rb:98:19:98:19 | a | params_flow.rb:99:10:99:10 | a |
| params_flow.rb:98:31:98:31 | b | params_flow.rb:102:10:102:10 | b |
| params_flow.rb:105:15:105:23 | call to taint | params_flow.rb:98:19:98:19 | a |
| params_flow.rb:106:15:106:23 | call to taint | params_flow.rb:98:19:98:19 | a |
| params_flow.rb:106:37:106:45 | call to taint | params_flow.rb:98:31:98:31 | b |
| params_flow.rb:108:37:108:37 | a | params_flow.rb:109:10:109:10 | a |
| params_flow.rb:108:44:108:44 | c | params_flow.rb:111:10:111:10 | c |
| params_flow.rb:114:33:114:41 | call to taint | params_flow.rb:108:37:108:37 | a |
| params_flow.rb:114:58:114:66 | call to taint | params_flow.rb:108:44:108:44 | c |
| params_flow.rb:117:1:117:1 | [post] x [element] | params_flow.rb:118:13:118:13 | x [element] |
| params_flow.rb:117:19:117:27 | call to taint | params_flow.rb:117:1:117:1 | [post] x [element] |
| params_flow.rb:118:12:118:13 | * ... [element] | params_flow.rb:9:16:9:17 | p1 |
| params_flow.rb:118:12:118:13 | * ... [element] | params_flow.rb:9:20:9:21 | p2 |
| params_flow.rb:118:13:118:13 | x [element] | params_flow.rb:118:12:118:13 | * ... [element] |
nodes
| params_flow.rb:9:16:9:17 | p1 | semmle.label | p1 |
| params_flow.rb:9:20:9:21 | p2 | semmle.label | p2 |
@@ -100,22 +152,89 @@ nodes
| params_flow.rb:41:24:41:29 | ** ... [element :p1] | semmle.label | ** ... [element :p1] |
| params_flow.rb:41:26:41:29 | args [element :p1] | semmle.label | args [element :p1] |
| params_flow.rb:44:12:44:20 | call to taint | semmle.label | call to taint |
| params_flow.rb:46:1:46:4 | args [element 0] | semmle.label | args [element 0] |
| params_flow.rb:46:1:46:4 | args [element 1] | semmle.label | args [element 1] |
| params_flow.rb:46:9:46:17 | call to taint | semmle.label | call to taint |
| params_flow.rb:46:20:46:28 | call to taint | semmle.label | call to taint |
| params_flow.rb:47:12:47:16 | * ... [element 0] | semmle.label | * ... [element 0] |
| params_flow.rb:47:12:47:16 | * ... [element 1] | semmle.label | * ... [element 1] |
| params_flow.rb:47:13:47:16 | args [element 0] | semmle.label | args [element 0] |
| params_flow.rb:47:13:47:16 | args [element 1] | semmle.label | args [element 1] |
| params_flow.rb:49:13:49:14 | p1 | semmle.label | p1 |
| params_flow.rb:49:17:49:24 | *posargs [element 0] | semmle.label | *posargs [element 0] |
| params_flow.rb:50:10:50:11 | p1 | semmle.label | p1 |
| params_flow.rb:54:9:54:17 | call to taint | semmle.label | call to taint |
| params_flow.rb:51:10:51:21 | ( ... ) | semmle.label | ( ... ) |
| params_flow.rb:51:11:51:17 | posargs [element 0] | semmle.label | posargs [element 0] |
| params_flow.rb:51:11:51:20 | ...[...] | semmle.label | ...[...] |
| params_flow.rb:55:9:55:17 | call to taint | semmle.label | call to taint |
| params_flow.rb:57:1:57:4 | args [element 0] | semmle.label | args [element 0] |
| params_flow.rb:57:9:57:17 | call to taint | semmle.label | call to taint |
| params_flow.rb:62:1:62:4 | args | semmle.label | args |
| params_flow.rb:62:8:62:16 | call to taint | semmle.label | call to taint |
| params_flow.rb:63:16:63:17 | *x [element 0] | semmle.label | *x [element 0] |
| params_flow.rb:64:10:64:10 | x [element 0] | semmle.label | x [element 0] |
| params_flow.rb:64:10:64:13 | ...[...] | semmle.label | ...[...] |
| params_flow.rb:66:12:66:16 | * ... [element 0] | semmle.label | * ... [element 0] |
| params_flow.rb:66:13:66:16 | args | semmle.label | args |
| params_flow.rb:58:9:58:17 | call to taint | semmle.label | call to taint |
| params_flow.rb:58:20:58:24 | * ... [element 0] | semmle.label | * ... [element 0] |
| params_flow.rb:58:21:58:24 | args [element 0] | semmle.label | args [element 0] |
| params_flow.rb:60:1:60:4 | args [element 0] | semmle.label | args [element 0] |
| params_flow.rb:60:9:60:17 | call to taint | semmle.label | call to taint |
| params_flow.rb:61:9:61:13 | * ... [element 0] | semmle.label | * ... [element 0] |
| params_flow.rb:61:10:61:13 | args [element 0] | semmle.label | args [element 0] |
| params_flow.rb:63:1:63:4 | args | semmle.label | args |
| params_flow.rb:63:8:63:16 | call to taint | semmle.label | call to taint |
| params_flow.rb:64:16:64:17 | *x [element 0] | semmle.label | *x [element 0] |
| params_flow.rb:65:10:65:10 | x [element 0] | semmle.label | x [element 0] |
| params_flow.rb:65:10:65:13 | ...[...] | semmle.label | ...[...] |
| params_flow.rb:67:12:67:16 | * ... [element 0] | semmle.label | * ... [element 0] |
| params_flow.rb:67:13:67:16 | args | semmle.label | args |
| params_flow.rb:69:14:69:14 | x | semmle.label | x |
| params_flow.rb:69:17:69:17 | y | semmle.label | y |
| params_flow.rb:69:24:69:24 | w | semmle.label | w |
| params_flow.rb:69:27:69:27 | r | semmle.label | r |
| params_flow.rb:70:10:70:10 | x | semmle.label | x |
| params_flow.rb:71:10:71:10 | y | semmle.label | y |
| params_flow.rb:74:10:74:10 | w | semmle.label | w |
| params_flow.rb:75:10:75:10 | r | semmle.label | r |
| params_flow.rb:78:10:78:18 | call to taint | semmle.label | call to taint |
| params_flow.rb:78:21:78:29 | call to taint | semmle.label | call to taint |
| params_flow.rb:78:43:78:51 | call to taint | semmle.label | call to taint |
| params_flow.rb:78:54:78:62 | call to taint | semmle.label | call to taint |
| params_flow.rb:81:10:81:18 | call to taint | semmle.label | call to taint |
| params_flow.rb:83:14:83:14 | t | semmle.label | t |
| params_flow.rb:83:17:83:17 | u | semmle.label | u |
| params_flow.rb:83:23:83:23 | w | semmle.label | w |
| params_flow.rb:84:10:84:10 | t | semmle.label | t |
| params_flow.rb:85:10:85:10 | u | semmle.label | u |
| params_flow.rb:87:10:87:10 | w | semmle.label | w |
| params_flow.rb:94:10:94:18 | call to taint | semmle.label | call to taint |
| params_flow.rb:94:21:94:29 | call to taint | semmle.label | call to taint |
| params_flow.rb:94:39:94:47 | call to taint | semmle.label | call to taint |
| params_flow.rb:96:10:96:18 | call to taint | semmle.label | call to taint |
| params_flow.rb:96:21:96:29 | call to taint | semmle.label | call to taint |
| params_flow.rb:96:68:96:76 | call to taint | semmle.label | call to taint |
| params_flow.rb:96:79:96:87 | call to taint | semmle.label | call to taint |
| params_flow.rb:98:19:98:19 | a | semmle.label | a |
| params_flow.rb:98:31:98:31 | b | semmle.label | b |
| params_flow.rb:99:10:99:10 | a | semmle.label | a |
| params_flow.rb:102:10:102:10 | b | semmle.label | b |
| params_flow.rb:105:15:105:23 | call to taint | semmle.label | call to taint |
| params_flow.rb:106:15:106:23 | call to taint | semmle.label | call to taint |
| params_flow.rb:106:37:106:45 | call to taint | semmle.label | call to taint |
| params_flow.rb:108:37:108:37 | a | semmle.label | a |
| params_flow.rb:108:44:108:44 | c | semmle.label | c |
| params_flow.rb:109:10:109:10 | a | semmle.label | a |
| params_flow.rb:111:10:111:10 | c | semmle.label | c |
| params_flow.rb:114:33:114:41 | call to taint | semmle.label | call to taint |
| params_flow.rb:114:58:114:66 | call to taint | semmle.label | call to taint |
| params_flow.rb:117:1:117:1 | [post] x [element] | semmle.label | [post] x [element] |
| params_flow.rb:117:19:117:27 | call to taint | semmle.label | call to taint |
| params_flow.rb:118:12:118:13 | * ... [element] | semmle.label | * ... [element] |
| params_flow.rb:118:13:118:13 | x [element] | semmle.label | x [element] |
subpaths
#select
| params_flow.rb:10:10:10:11 | p1 | params_flow.rb:14:12:14:19 | call to taint | params_flow.rb:10:10:10:11 | p1 | $@ | params_flow.rb:14:12:14:19 | call to taint | call to taint |
| params_flow.rb:10:10:10:11 | p1 | params_flow.rb:44:12:44:20 | call to taint | params_flow.rb:10:10:10:11 | p1 | $@ | params_flow.rb:44:12:44:20 | call to taint | call to taint |
| params_flow.rb:10:10:10:11 | p1 | params_flow.rb:46:9:46:17 | call to taint | params_flow.rb:10:10:10:11 | p1 | $@ | params_flow.rb:46:9:46:17 | call to taint | call to taint |
| params_flow.rb:10:10:10:11 | p1 | params_flow.rb:117:19:117:27 | call to taint | params_flow.rb:10:10:10:11 | p1 | $@ | params_flow.rb:117:19:117:27 | call to taint | call to taint |
| params_flow.rb:11:10:11:11 | p2 | params_flow.rb:14:22:14:29 | call to taint | params_flow.rb:11:10:11:11 | p2 | $@ | params_flow.rb:14:22:14:29 | call to taint | call to taint |
| params_flow.rb:11:10:11:11 | p2 | params_flow.rb:46:20:46:28 | call to taint | params_flow.rb:11:10:11:11 | p2 | $@ | params_flow.rb:46:20:46:28 | call to taint | call to taint |
| params_flow.rb:11:10:11:11 | p2 | params_flow.rb:117:19:117:27 | call to taint | params_flow.rb:11:10:11:11 | p2 | $@ | params_flow.rb:117:19:117:27 | call to taint | call to taint |
| params_flow.rb:17:10:17:11 | p1 | params_flow.rb:21:13:21:20 | call to taint | params_flow.rb:17:10:17:11 | p1 | $@ | params_flow.rb:21:13:21:20 | call to taint | call to taint |
| params_flow.rb:17:10:17:11 | p1 | params_flow.rb:22:27:22:34 | call to taint | params_flow.rb:17:10:17:11 | p1 | $@ | params_flow.rb:22:27:22:34 | call to taint | call to taint |
| params_flow.rb:17:10:17:11 | p1 | params_flow.rb:23:33:23:40 | call to taint | params_flow.rb:17:10:17:11 | p1 | $@ | params_flow.rb:23:33:23:40 | call to taint | call to taint |
@@ -131,6 +250,25 @@ subpaths
| params_flow.rb:28:10:28:22 | ( ... ) | params_flow.rb:37:34:37:42 | call to taint | params_flow.rb:28:10:28:22 | ( ... ) | $@ | params_flow.rb:37:34:37:42 | call to taint | call to taint |
| params_flow.rb:29:10:29:22 | ( ... ) | params_flow.rb:33:41:33:49 | call to taint | params_flow.rb:29:10:29:22 | ( ... ) | $@ | params_flow.rb:33:41:33:49 | call to taint | call to taint |
| params_flow.rb:29:10:29:22 | ( ... ) | params_flow.rb:34:14:34:22 | call to taint | params_flow.rb:29:10:29:22 | ( ... ) | $@ | params_flow.rb:34:14:34:22 | call to taint | call to taint |
| params_flow.rb:50:10:50:11 | p1 | params_flow.rb:54:9:54:17 | call to taint | params_flow.rb:50:10:50:11 | p1 | $@ | params_flow.rb:54:9:54:17 | call to taint | call to taint |
| params_flow.rb:50:10:50:11 | p1 | params_flow.rb:57:9:57:17 | call to taint | params_flow.rb:50:10:50:11 | p1 | $@ | params_flow.rb:57:9:57:17 | call to taint | call to taint |
| params_flow.rb:64:10:64:13 | ...[...] | params_flow.rb:62:8:62:16 | call to taint | params_flow.rb:64:10:64:13 | ...[...] | $@ | params_flow.rb:62:8:62:16 | call to taint | call to taint |
| params_flow.rb:50:10:50:11 | p1 | params_flow.rb:55:9:55:17 | call to taint | params_flow.rb:50:10:50:11 | p1 | $@ | params_flow.rb:55:9:55:17 | call to taint | call to taint |
| params_flow.rb:50:10:50:11 | p1 | params_flow.rb:58:9:58:17 | call to taint | params_flow.rb:50:10:50:11 | p1 | $@ | params_flow.rb:58:9:58:17 | call to taint | call to taint |
| params_flow.rb:50:10:50:11 | p1 | params_flow.rb:60:9:60:17 | call to taint | params_flow.rb:50:10:50:11 | p1 | $@ | params_flow.rb:60:9:60:17 | call to taint | call to taint |
| params_flow.rb:51:10:51:21 | ( ... ) | params_flow.rb:57:9:57:17 | call to taint | params_flow.rb:51:10:51:21 | ( ... ) | $@ | params_flow.rb:57:9:57:17 | call to taint | call to taint |
| params_flow.rb:65:10:65:13 | ...[...] | params_flow.rb:63:8:63:16 | call to taint | params_flow.rb:65:10:65:13 | ...[...] | $@ | params_flow.rb:63:8:63:16 | call to taint | call to taint |
| params_flow.rb:70:10:70:10 | x | params_flow.rb:78:10:78:18 | call to taint | params_flow.rb:70:10:70:10 | x | $@ | params_flow.rb:78:10:78:18 | call to taint | call to taint |
| params_flow.rb:70:10:70:10 | x | params_flow.rb:81:10:81:18 | call to taint | params_flow.rb:70:10:70:10 | x | $@ | params_flow.rb:81:10:81:18 | call to taint | call to taint |
| params_flow.rb:70:10:70:10 | x | params_flow.rb:96:10:96:18 | call to taint | params_flow.rb:70:10:70:10 | x | $@ | params_flow.rb:96:10:96:18 | call to taint | call to taint |
| params_flow.rb:71:10:71:10 | y | params_flow.rb:78:21:78:29 | call to taint | params_flow.rb:71:10:71:10 | y | $@ | params_flow.rb:78:21:78:29 | call to taint | call to taint |
| params_flow.rb:71:10:71:10 | y | params_flow.rb:96:21:96:29 | call to taint | params_flow.rb:71:10:71:10 | y | $@ | params_flow.rb:96:21:96:29 | call to taint | call to taint |
| params_flow.rb:74:10:74:10 | w | params_flow.rb:78:43:78:51 | call to taint | params_flow.rb:74:10:74:10 | w | $@ | params_flow.rb:78:43:78:51 | call to taint | call to taint |
| params_flow.rb:74:10:74:10 | w | params_flow.rb:96:68:96:76 | call to taint | params_flow.rb:74:10:74:10 | w | $@ | params_flow.rb:96:68:96:76 | call to taint | call to taint |
| params_flow.rb:75:10:75:10 | r | params_flow.rb:78:54:78:62 | call to taint | params_flow.rb:75:10:75:10 | r | $@ | params_flow.rb:78:54:78:62 | call to taint | call to taint |
| params_flow.rb:75:10:75:10 | r | params_flow.rb:96:79:96:87 | call to taint | params_flow.rb:75:10:75:10 | r | $@ | params_flow.rb:96:79:96:87 | call to taint | call to taint |
| params_flow.rb:84:10:84:10 | t | params_flow.rb:94:10:94:18 | call to taint | params_flow.rb:84:10:84:10 | t | $@ | params_flow.rb:94:10:94:18 | call to taint | call to taint |
| params_flow.rb:85:10:85:10 | u | params_flow.rb:94:21:94:29 | call to taint | params_flow.rb:85:10:85:10 | u | $@ | params_flow.rb:94:21:94:29 | call to taint | call to taint |
| params_flow.rb:87:10:87:10 | w | params_flow.rb:94:39:94:47 | call to taint | params_flow.rb:87:10:87:10 | w | $@ | params_flow.rb:94:39:94:47 | call to taint | call to taint |
| params_flow.rb:99:10:99:10 | a | params_flow.rb:105:15:105:23 | call to taint | params_flow.rb:99:10:99:10 | a | $@ | params_flow.rb:105:15:105:23 | call to taint | call to taint |
| params_flow.rb:99:10:99:10 | a | params_flow.rb:106:15:106:23 | call to taint | params_flow.rb:99:10:99:10 | a | $@ | params_flow.rb:106:15:106:23 | call to taint | call to taint |
| params_flow.rb:102:10:102:10 | b | params_flow.rb:106:37:106:45 | call to taint | params_flow.rb:102:10:102:10 | b | $@ | params_flow.rb:106:37:106:45 | call to taint | call to taint |
| params_flow.rb:109:10:109:10 | a | params_flow.rb:114:33:114:41 | call to taint | params_flow.rb:109:10:109:10 | a | $@ | params_flow.rb:114:33:114:41 | call to taint | call to taint |
| params_flow.rb:111:10:111:10 | c | params_flow.rb:114:58:114:66 | call to taint | params_flow.rb:111:10:111:10 | c | $@ | params_flow.rb:114:58:114:66 | call to taint | call to taint |

View File

@@ -7,8 +7,8 @@ def sink x
end
def positional(p1, p2)
sink p1 # $ hasValueFlow=1 $ hasValueFlow=16 $ MISSING: hasValueFlow=18
sink p2 # $ hasValueFlow=2 $ MISSING: hasValueFlow=17 $ MISSING: hasValueFlow=19
sink p1 # $ hasValueFlow=1 $ hasValueFlow=16 $ hasValueFlow=18 $ hasValueFlow=61
sink p2 # $ hasValueFlow=2 $ hasValueFlow=19 $ hasValueFlow=61 $ MISSING: hasValueFlow=17
end
positional(taint(1), taint(2))
@@ -47,8 +47,9 @@ args = [taint(18), taint(19)]
positional(*args)
def posargs(p1, *posargs)
sink p1 # $ hasValueFlow=20 $ hasValueFlow=23 $ MISSING: hasValueFlow=24
sink (posargs[0]) # $ MISSING: hasValueFlow=21 $ MISSING: hasValueFlow=22 $ MISSING: hasValueFlow=25
sink p1 # $ hasValueFlow=20 $ hasValueFlow=23 $ hasValueFlow=24
sink (posargs[0]) # $ hasValueFlow=22 $ MISSING: hasValueFlow=21 $ MISSING: hasValueFlow=25
sink (posargs[1])
end
posargs(taint(20), taint(21))
@@ -63,4 +64,55 @@ args = taint(26)
def splatstuff(*x)
sink x[0] # $ hasValueFlow=26
end
splatstuff(*args)
splatstuff(*args)
def splatmid(x, y, *z, w, r)
sink x # $ hasValueFlow=27 $ hasValueFlow=32 $ hasValueFlow=45
sink y # $ hasValueFlow=28 $ hasValueFlow=46 $ MISSING: hasValueFlow=33
sink z[0] # MISSING: $ hasValueFlow=47 $ hasValueFlow=29 $ hasValueFlow=34
sink z[1] # $ MISSING: hasValueFlow=48 $ hasValueFlow=35
sink w # $ hasValueFlow=30 $ hasValueFlow=50 $ MISSING: hasValueFlow=36
sink r # $ hasValueFlow=31 $ hasValueFlow=51 $ MISSING: hasValueFlow=37
end
splatmid(taint(27), taint(28), taint(29), taint(30), taint(31))
args = [taint(33), taint(34), taint(35), taint(36)]
splatmid(taint(32), *args, taint(37))
def pos_many(t, u, v, w, x, y, z)
sink t # $ hasValueFlow=38
sink u # $ hasValueFlow=39
sink v # $ MISSING: hasValueFlow=40
sink w # $ MISSING: hasValueFlow=41 $ SPURIOUS: hasValueFlow=44
sink x # $ MISSING: hasValueFlow=42
sink y # $ MISSING: hasValueFlow=43
sink z # $ MISSING: hasValueFlow=44
end
args = [taint(40), taint(41), taint(42), taint(43)]
pos_many(taint(38), taint(39), *args, taint(44))
splatmid(taint(45), taint(46), *[taint(47), taint(48), taint(49)], taint(50), taint(51))
def splatmidsmall(a, *splats, b)
sink a # $ hasValueFlow=52 $ hasValueFlow=55
sink splats[0] # $ MISSING: hasValueFlow=53
sink splats[1] # $ MISSING: hasValueFlow=54
sink b # $ hasValueFlow=57
end
splatmidsmall(taint(52), *[taint(53), taint(54)])
splatmidsmall(taint(55), taint(56), taint(57))
def splat_followed_by_keyword_param(a, *b, c:)
sink a # $ hasValueFlow=58
sink b[0] # $ MISSING: hasValueFlow=59
sink c # $ hasValueFlow=60
end
splat_followed_by_keyword_param(taint(58), taint(59), c: taint(60))
x = []
x[some_index()] = taint(61)
positional(*x)

View File

@@ -7,6 +7,7 @@ track
| type_tracker.rb:2:5:5:7 | field= | type tracker without call steps | type_tracker.rb:2:5:5:7 | field= |
| type_tracker.rb:2:5:5:7 | self in field= | type tracker with call steps | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:2:5:5:7 | self in field= | type tracker without call steps | type_tracker.rb:2:5:5:7 | self in field= |
| type_tracker.rb:2:5:5:7 | synthetic *args | type tracker without call steps | type_tracker.rb:2:5:5:7 | synthetic *args |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:2:16:2:18 | val |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:2:16:2:18 | val |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:3:14:3:23 | call to field |
@@ -46,6 +47,7 @@ track
| type_tracker.rb:18:1:21:3 | &block | type tracker without call steps | type_tracker.rb:18:1:21:3 | &block |
| type_tracker.rb:18:1:21:3 | positional | type tracker without call steps | type_tracker.rb:18:1:21:3 | positional |
| type_tracker.rb:18:1:21:3 | self in positional | type tracker without call steps | type_tracker.rb:18:1:21:3 | self in positional |
| type_tracker.rb:18:1:21:3 | synthetic *args | type tracker without call steps | type_tracker.rb:18:1:21:3 | synthetic *args |
| type_tracker.rb:18:16:18:17 | p1 | type tracker without call steps | type_tracker.rb:18:16:18:17 | p1 |
| type_tracker.rb:18:16:18:17 | p1 | type tracker without call steps | type_tracker.rb:18:16:18:17 | p1 |
| type_tracker.rb:18:20:18:21 | p2 | type tracker without call steps | type_tracker.rb:18:20:18:21 | p2 |
@@ -118,6 +120,7 @@ track
| type_tracker.rb:32:26:32:26 | 8 | type tracker without call steps with content element :p1 | type_tracker.rb:32:1:32:27 | ** |
| type_tracker.rb:34:1:53:3 | &block | type tracker without call steps | type_tracker.rb:34:1:53:3 | &block |
| type_tracker.rb:34:1:53:3 | self in throughArray | type tracker without call steps | type_tracker.rb:34:1:53:3 | self in throughArray |
| type_tracker.rb:34:1:53:3 | synthetic *args | type tracker without call steps | type_tracker.rb:34:1:53:3 | synthetic *args |
| type_tracker.rb:34:1:53:3 | throughArray | type tracker without call steps | type_tracker.rb:34:1:53:3 | throughArray |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:18:34:20 | obj |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:18:34:20 | obj |
@@ -272,6 +275,7 @@ trackEnd
| type_tracker.rb:2:5:5:7 | self in field= | type_tracker.rb:7:5:9:7 | self (field) |
| type_tracker.rb:2:5:5:7 | self in field= | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:2:5:5:7 | self in field= | type_tracker.rb:8:9:8:14 | self |
| type_tracker.rb:2:5:5:7 | synthetic *args | type_tracker.rb:2:5:5:7 | synthetic *args |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:2:16:2:18 | val |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:2:16:2:18 | val |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:2:16:2:18 | val |
@@ -340,6 +344,7 @@ trackEnd
| type_tracker.rb:18:1:21:3 | self in positional | type_tracker.rb:18:1:21:3 | self in positional |
| type_tracker.rb:18:1:21:3 | self in positional | type_tracker.rb:19:5:19:11 | self |
| type_tracker.rb:18:1:21:3 | self in positional | type_tracker.rb:20:5:20:11 | self |
| type_tracker.rb:18:1:21:3 | synthetic *args | type_tracker.rb:18:1:21:3 | synthetic *args |
| type_tracker.rb:18:16:18:17 | p1 | type_tracker.rb:18:16:18:17 | p1 |
| type_tracker.rb:18:16:18:17 | p1 | type_tracker.rb:18:16:18:17 | p1 |
| type_tracker.rb:18:16:18:17 | p1 | type_tracker.rb:18:16:18:17 | p1 |
@@ -427,6 +432,7 @@ trackEnd
| type_tracker.rb:32:26:32:26 | 8 | type_tracker.rb:32:26:32:26 | 8 |
| type_tracker.rb:34:1:53:3 | &block | type_tracker.rb:34:1:53:3 | &block |
| type_tracker.rb:34:1:53:3 | self in throughArray | type_tracker.rb:34:1:53:3 | self in throughArray |
| type_tracker.rb:34:1:53:3 | synthetic *args | type_tracker.rb:34:1:53:3 | synthetic *args |
| type_tracker.rb:34:1:53:3 | throughArray | type_tracker.rb:34:1:53:3 | throughArray |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:18:34:20 | obj |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:18:34:20 | obj |

View File

@@ -616,3 +616,62 @@ readAccess
| ssa.rb:100:10:100:11 | b4 |
| ssa.rb:101:5:101:10 | self |
| ssa.rb:101:10:101:10 | x |
captureAccess
| instance_variables.rb:28:3:28:4 | self |
| instance_variables.rb:32:12:32:13 | self |
| nested_scopes.rb:5:3:5:3 | a |
| nested_scopes.rb:7:5:7:5 | a |
| nested_scopes.rb:9:7:9:7 | a |
| nested_scopes.rb:11:9:11:9 | a |
| nested_scopes.rb:18:29:18:34 | self |
| nested_scopes.rb:18:34:18:34 | a |
| nested_scopes.rb:25:14:25:14 | a |
| nested_scopes.rb:31:11:31:11 | a |
| nested_scopes.rb:32:16:32:16 | a |
| nested_scopes.rb:34:12:34:12 | a |
| nested_scopes.rb:36:10:36:10 | a |
| nested_scopes.rb:38:8:38:8 | a |
| parameters.rb:3:4:3:9 | self |
| parameters.rb:4:4:4:9 | self |
| parameters.rb:17:5:17:28 | self |
| parameters.rb:54:19:54:19 | x |
| parameters.rb:55:4:55:9 | self |
| parameters.rb:55:9:55:9 | x |
| parameters.rb:56:4:56:9 | self |
| scopes.rb:3:4:3:9 | self |
| scopes.rb:3:9:3:9 | self |
| scopes.rb:5:4:5:9 | self |
| scopes.rb:10:4:10:9 | self |
| scopes.rb:10:9:10:9 | a |
| scopes.rb:11:4:11:4 | a |
| scopes.rb:11:4:11:4 | a |
| scopes.rb:12:4:12:9 | self |
| scopes.rb:12:9:12:9 | a |
| scopes.rb:13:4:13:4 | a |
| scopes.rb:14:4:14:9 | self |
| scopes.rb:14:9:14:9 | a |
| scopes.rb:15:4:15:9 | self |
| scopes.rb:16:4:16:9 | self |
| scopes.rb:17:4:17:9 | self |
| scopes.rb:29:3:29:3 | x |
| scopes.rb:32:3:32:3 | x |
| scopes.rb:35:3:35:3 | x |
| scopes.rb:42:2:42:4 | var |
| scopes.rb:43:2:43:4 | foo |
| scopes.rb:44:5:44:7 | var |
| scopes.rb:46:5:46:8 | var2 |
| scopes.rb:47:5:47:8 | var2 |
| ssa.rb:26:7:26:10 | elem |
| ssa.rb:27:5:27:13 | self |
| ssa.rb:27:10:27:13 | elem |
| ssa.rb:34:5:34:10 | self |
| ssa.rb:67:5:67:10 | self |
| ssa.rb:68:5:68:17 | self |
| ssa.rb:68:10:68:17 | captured |
| ssa.rb:69:5:69:12 | captured |
| ssa.rb:69:5:69:12 | captured |
| ssa.rb:77:6:77:23 | self |
| ssa.rb:77:15:77:22 | captured |
| ssa.rb:84:6:86:8 | self |
| ssa.rb:85:10:85:22 | self |
| ssa.rb:85:15:85:22 | captured |

View File

@@ -12,3 +12,5 @@ query predicate explicitWrite(VariableWriteAccess write, AstNode assignment) {
query predicate implicitWrite(VariableWriteAccess write) { write.isImplicitWrite() }
query predicate readAccess(VariableReadAccess read) { any() }
query predicate captureAccess(LocalVariableAccess access) { access.isCapturedAccess() }

View File

@@ -371,9 +371,10 @@ module Flow<InputSig Input> implements OutputSig<Input> {
query predicate uniqueWriteCfgNode(string msg) { uniqueWriteCfgNode(_, msg) }
private predicate localWriteStep(VariableWrite vw, string msg) {
exists(BasicBlock bb |
vw.hasCfgNode(bb, _) and
bb.getEnclosingCallable() != vw.getVariable().getCallable() and
exists(BasicBlock bb1, BasicBlock bb2 |
vw.hasCfgNode(bb1, _) and
vw.getSource().hasCfgNode(bb2, _) and
bb1.getEnclosingCallable() != bb2.getEnclosingCallable() and
msg = "VariableWrite is not a local step"
)
}

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added local flow sources for `UITextInput` and related classes.

View File

@@ -476,18 +476,25 @@ private predicate parseField(AccessPathToken c, Content::FieldContent f) {
)
}
private predicate parseEnum(AccessPathToken c, Content::EnumContent f) {
private predicate parseTuple(AccessPathToken c, Content::TupleContent t) {
c.getName() = "TupleElement" and
t.getIndex() = c.getAnArgument().toInt()
}
private predicate parseEnum(AccessPathToken c, Content::EnumContent e) {
c.getName() = "EnumElement" and
c.getAnArgument() = f.getSignature()
c.getAnArgument() = e.getSignature()
or
c.getName() = "OptionalSome" and
f.getSignature() = "some:0"
e.getSignature() = "some:0"
}
/** Holds if the specification component parses as a `Content`. */
predicate parseContent(AccessPathToken component, Content content) {
parseField(component, content)
or
parseTuple(component, content)
or
parseEnum(component, content)
or
component.getName() = "ArrayElement" and

View File

@@ -110,6 +110,11 @@ private string getContentSpecific(ContentSet cs) {
result = "Field[" + c.getField().getName() + "]"
)
or
exists(Content::TupleContent c |
cs.isSingleton(c) and
result = "TupleElement[" + c.getIndex().toString() + "]"
)
or
exists(Content::EnumContent c |
cs.isSingleton(c) and
result = "EnumElement[" + c.getSignature() + "]"

View File

@@ -1,15 +1,22 @@
/**
* Provides models for the `UITextField` Swift class.
* Provides models for the `UITextField` and related Swift class.
*/
import swift
private import codeql.swift.dataflow.ExternalFlow
/**
* A model for `UITextField` members that are flow sources.
* A model for `UITextField`, `UITextInput` and related class members that are flow sources.
*/
private class UITextFieldSource extends SourceModelCsv {
override predicate row(string row) {
row = [";UITextField;true;text;;;;local", ";UITextField;true;attributedText;;;;local"]
row =
[
";UITextField;true;text;;;;local", ";UITextField;true;attributedText;;;;local",
";UITextFieldDelegate;true;textField(_:shouldChangeCharactersIn:replacementString:);;;Parameter[2];local",
";UITextViewDelegate;true;textView(_:shouldChangeTextIn:replacementText:);;;Parameter[2];local",
";UITextInput;true;text(in:);;;ReturnValue;local",
";UITextInput;true;shouldChangeText(in:replacementText:);;;Parameter[1];local",
]
}
}

View File

@@ -1,3 +1,3 @@
multipleSuccessors
| test.swift:519:8:519:12 | let ...? | no-match | test.swift:519:27:519:27 | y |
| test.swift:519:8:519:12 | let ...? | no-match | test.swift:524:9:524:9 | tuple1 |
| test.swift:538:8:538:12 | let ...? | no-match | test.swift:538:27:538:27 | y |
| test.swift:538:8:538:12 | let ...? | no-match | test.swift:543:9:543:9 | tuple1 |

File diff suppressed because it is too large Load Diff

View File

@@ -26,6 +26,8 @@ private class TestSummaries extends SummaryModelCsv {
[
// model to allow data flow through `signum()` as though it were an identity function, for the benefit of testing flow through optional chaining (`x?.`).
";Int;true;signum();;;Argument[-1];ReturnValue;value",
// test Tuple content in MAD
";;false;tupleShiftLeft2(_:);;;Argument[0].TupleElement[1];ReturnValue.TupleElement[0];value",
// test Enum content in MAD
";;false;mkMyEnum2(_:);;;Argument[0];ReturnValue.EnumElement[mySingle:0];value",
";;false;mkOptional2(_:);;;Argument[0];ReturnValue.OptionalSome;value"

View File

@@ -365,6 +365,25 @@ func testTuples2() {
sink(arg: c)
}
func tupleShiftLeft1(_ t: (Int, Int)) -> (Int, Int) {
return (t.1, 0)
}
func tupleShiftLeft2(_ t: (Int, Int)) -> (Int, Int) { return (0, 0) } // modelled flow
func testTuples3() {
let t1 = (1, source())
let t2 = tupleShiftLeft1(t1)
let t3 = tupleShiftLeft2(t1)
sink(arg: t1.0)
sink(arg: t1.1) // $ flow=375
sink(arg: t2.0) // $ flow=375
sink(arg: t2.1)
sink(arg: t3.0) // $ flow=375
sink(arg: t3.1)
}
indirect enum MyEnum {
case myNone
case mySingle(Int)
@@ -406,7 +425,7 @@ func testEnums() {
case .myNone:
()
case .mySingle(let a):
sink(arg: a) // $ flow=403
sink(arg: a) // $ flow=422
case .myPair(let a, let b):
sink(arg: a)
sink(arg: b)
@@ -415,7 +434,7 @@ func testEnums() {
}
if case .mySingle(let x) = a {
sink(arg: x) // $ flow=403
sink(arg: x) // $ flow=422
}
if case .myPair(let x, let y) = a {
sink(arg: x)
@@ -431,7 +450,7 @@ func testEnums() {
sink(arg: a)
case .myPair(let a, let b):
sink(arg: a)
sink(arg: b) // $ flow=425
sink(arg: b) // $ flow=444
case let .myCons(a, _):
sink(arg: a)
}
@@ -441,7 +460,7 @@ func testEnums() {
}
if case .myPair(let x, let y) = a {
sink(arg: x)
sink(arg: y) // $ flow=425
sink(arg: y) // $ flow=444
}
let b: MyEnum = .myCons(42, a)
@@ -457,7 +476,7 @@ func testEnums() {
case let .myCons(a, .myPair(b, c)):
sink(arg: a)
sink(arg: b)
sink(arg: c) // $ flow=425
sink(arg: c) // $ flow=444
case let .myCons(a, _):
sink(arg: a)
}
@@ -466,20 +485,20 @@ func testEnums() {
sink(arg: x)
}
if case MyEnum.myPair(let x, let y) = .myPair(source(), 0) {
sink(arg: x) // $ flow=468
sink(arg: x) // $ flow=487
sink(arg: y)
}
if case let .myCons(_, .myPair(_, c)) = b {
sink(arg: c) // $ flow=425
sink(arg: c) // $ flow=444
}
switch (a, b) {
case let (.myPair(a, b), .myCons(c, .myPair(d, e))):
sink(arg: a)
sink(arg: b) // $ flow=425
sink(arg: b) // $ flow=444
sink(arg: c)
sink(arg: d)
sink(arg: e) // $ flow=425
sink(arg: e) // $ flow=444
default:
()
}
@@ -491,11 +510,11 @@ func testEnums() {
let c5 = mkMyEnum2(0)
let c6 = mkMyEnum2(source())
if case MyEnum.mySingle(let d1) = c1 { sink(arg: d1) }
if case MyEnum.mySingle(let d2) = c2 { sink(arg: d2) } // $ flow=488
if case MyEnum.mySingle(let d2) = c2 { sink(arg: d2) } // $ flow=507
if case MyEnum.mySingle(let d3) = c3 { sink(arg: d3) }
if case MyEnum.mySingle(let d4) = c4 { sink(arg: d4) } // $ flow=490
if case MyEnum.mySingle(let d4) = c4 { sink(arg: d4) } // $ flow=509
if case MyEnum.mySingle(let d5) = c5 { sink(arg: d5) }
if case MyEnum.mySingle(let d6) = c6 { sink(arg: d6) } // $ flow=492
if case MyEnum.mySingle(let d6) = c6 { sink(arg: d6) } // $ flow=511
let e1 = Optional.some(0)
let e2 = Optional.some(source())
@@ -504,11 +523,11 @@ func testEnums() {
let e5 = mkOptional2(0)
let e6 = mkOptional2(source())
sink(arg: e1!)
sink(arg: e2!) // $ flow=501
sink(arg: e2!) // $ flow=520
sink(arg: e3!)
sink(arg: e4!) // $ flow=503
sink(arg: e4!) // $ flow=522
sink(arg: e5!)
sink(arg: e6!) // $ flow=505
sink(arg: e6!) // $ flow=524
}
func source2() -> (Int, Int)? { return nil }
@@ -554,8 +573,8 @@ func testOptionalPropertyAccess(y: Int?) {
}
func testIdentityArithmetic() {
sink(arg: +source()) // $ flow=557
sink(arg: (source())) // $ flow=558
sink(arg: +source()) // $ flow=576
sink(arg: (source())) // $ flow=577
}
func sink(str: String) {}
@@ -572,13 +591,13 @@ class MyClass {
extension MyClass {
convenience init(contentsOfFile: String) {
self.init(s: source3())
sink(str: str) // $ flow=574
sink(str: str) // $ flow=593
}
}
func extensionInits(path: String) {
sink(str: MyClass(s: source3()).str) // $ flow=580
sink(str: MyClass(contentsOfFile: path).str) // $ flow=574
sink(str: MyClass(s: source3()).str) // $ flow=599
sink(str: MyClass(contentsOfFile: path).str) // $ flow=593
}
class InoutConstructorClass {
@@ -603,10 +622,10 @@ struct S {
func testKeyPath() {
let s = S(x: source())
let f = \S.x
sink(arg: s[keyPath: f]) // $ flow=604
sink(arg: s[keyPath: f]) // $ flow=623
let inferred : KeyPath<S, Int> = \.x
sink(arg: s[keyPath: inferred]) // $ flow=604
sink(arg: s[keyPath: inferred]) // $ flow=623
}
struct S2 {
@@ -621,13 +640,13 @@ func testNestedKeyPath() {
let s = S(x: source())
let s2 = S2(s: s)
let f = \S2.s.x
sink(arg: s2[keyPath: f]) // $ flow=621
sink(arg: s2[keyPath: f]) // $ flow=640
}
func testArrayKeyPath() {
let array = [source()]
let f = \[Int].[0]
sink(arg: array[keyPath: f]) // $ flow=628
sink(arg: array[keyPath: f]) // $ flow=647
}
struct S2_Optional {
@@ -642,7 +661,7 @@ func testOptionalKeyPath() {
let s = S(x: source())
let s2 = S2_Optional(s: s)
let f = \S2_Optional.s?.x
sink(opt: s2[keyPath: f]) // $ MISSING: flow=642
sink(opt: s2[keyPath: f]) // $ MISSING: flow=661
}
func testSwap() {
@@ -654,57 +673,57 @@ func testSwap() {
x = y
y = t
sink(arg: x)
sink(arg: y) // $ flow=649
sink(arg: y) // $ flow=668
x = source()
y = 0
swap(&x, &y)
sink(arg: x) // $ SPURIOUS: flow=659
sink(arg: y) // $ flow=659
sink(arg: x) // $ SPURIOUS: flow=678
sink(arg: y) // $ flow=678
}
func testArray() {
var arr1 = [1,2,3]
sink(arg: arr1[0])
arr1[1] = source()
sink(arg: arr1[0]) // $ flow=669
sink(arg: arr1[0]) // $ flow=688
sink(arg: arr1)
var arr2 = [source()]
sink(arg: arr2[0]) // $ flow=673
sink(arg: arr2[0]) // $ flow=692
var matrix = [[source()]]
sink(arg: matrix[0])
sink(arg: matrix[0][0]) // $ flow=676
sink(arg: matrix[0][0]) // $ flow=695
var matrix2 = [[1]]
matrix2[0][0] = source()
sink(arg: matrix2[0][0]) // $ flow=681
sink(arg: matrix2[0][0]) // $ flow=700
var arr3 = [1]
var arr4 = arr2 + arr3
sink(arg: arr3[0])
sink(arg: arr4[0]) // $ MISSING: flow=673
sink(arg: arr4[0]) // $ MISSING: flow=692
var arr5 = Array(repeating: source(), count: 2)
sink(arg: arr5[0]) // $ MISSING: flow=689
sink(arg: arr5[0]) // $ MISSING: flow=708
var arr6 = [1,2,3]
arr6.insert(source(), at: 2)
sink(arg: arr6[0]) // $ flow=693
sink(arg: arr6[0]) // $ flow=712
var arr7 = [source()]
sink(arg: arr7.randomElement()!) // $ flow=696
sink(arg: arr7.randomElement()!) // $ flow=715
}
func testSetCollections() {
var set1: Set = [1,2,3]
sink(arg: set1.randomElement()!)
set1.insert(source())
sink(arg: set1.randomElement()!) // $flow=703
sink(arg: set1.randomElement()!) // $ flow=722
let set2 = Set([source()])
sink(arg: set2.randomElement()!) // $ flow=706
sink(arg: set2.randomElement()!) // $ flow=725
}
struct MyOptionals {
@@ -730,13 +749,13 @@ func testWriteOptional() {
mo2!.v2 = source()
mo2!.v3 = source()
sink(arg: v1!) // $ flow=723
sink(arg: v2!) // $ flow=724
sink(arg: v3) // $ flow=725
sink(arg: mo1.v1!) // $ MISSING:flow=726
sink(arg: mo1.v2!) // $ flow=727
sink(arg: mo1.v3) // $ flow=728
sink(arg: mo2!.v1!) // $ MISSING:flow=729
sink(arg: mo2!.v2!) // $ MISSING:flow=730
sink(arg: mo2!.v3) // $ MISSING:flow=731
sink(arg: v1!) // $ flow=742
sink(arg: v2!) // $ flow=743
sink(arg: v3) // $ flow=744
sink(arg: mo1.v1!) // $ MISSING:flow=745
sink(arg: mo1.v2!) // $ flow=746
sink(arg: mo1.v3) // $ flow=747
sink(arg: mo2!.v1!) // $ MISSING:flow=748
sink(arg: mo2!.v2!) // $ MISSING:flow=749
sink(arg: mo2!.v3) // $ MISSING:flow=750
}

View File

@@ -1,33 +1,82 @@
// --- stubs ---
class NSObject { }
class NSAttributedString: NSObject {}
class UIResponder: NSObject {}
class UIView: UIResponder {}
class UIControl: UIView {}
struct _NSRange { }
typealias NSRange = _NSRange
class NSAttributedString: NSObject { }
class UIResponder: NSObject { }
class UIView: UIResponder { }
class UIControl: UIView { }
class UITextRange : NSObject {
}
protocol UITextInput {
func text(in range: UITextRange) -> String?
func shouldChangeText(in range: UITextRange, replacementText text: String) -> Bool
}
class UITextField: UIControl {
var text: String? {
get { nil }
set { }
}
var attributedText: NSAttributedString? {
get { nil }
set { }
}
var placeholder: String? {
get { nil }
set { }
}
}
class UISearchTextField : UITextField {
}
protocol UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
}
// --- tests ---
func testUITextField(textField: UITextField, searchTextField: UISearchTextField) {
func sink(arg: Any) { }
class MyTextInput : UITextInput {
func text(in range: UITextRange) -> String? { return nil }
func harmless(in range: UITextRange) -> String? { return nil }
func shouldChangeText(in range: UITextRange, replacementText text: String) -> Bool { // $ source=local
sink(arg: text) // $ tainted
return true
}
}
class MyUITextFieldDelegate : UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { // $ source=local
sink(arg: string) // $ tainted
return true
}
}
func test(textField: UITextField, searchTextField: UISearchTextField, myTextInput: MyTextInput, range: UITextRange) {
_ = textField.text // $ source=local
_ = textField.attributedText // $ source=local
_ = textField.placeholder // GOOD (not input)
_ = textField.text?.uppercased() // $ source=local
_ = searchTextField.text // $ source=local
_ = myTextInput.text(in: range)! // $ source=local
_ = myTextInput.harmless(in: range)! // GOOD (not input)
}