mirror of
https://github.com/github/codeql.git
synced 2026-04-29 02:35:15 +02:00
Merge branch 'main' into add-cwe-208
This commit is contained in:
@@ -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() +
|
||||
|
||||
@@ -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.
|
||||
@@ -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. |
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(...)'."
|
||||
|
||||
@@ -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(...)'.",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 + ".");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 |
|
||||
@@ -0,0 +1 @@
|
||||
Linq/MissedCastOpportunity.ql
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 |
|
||||
@@ -0,0 +1 @@
|
||||
Linq/MissedWhereOpportunity.ql
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
4
ruby/ql/lib/change-notes/2023-08-08-splat-arguments.md
Normal file
4
ruby/ql/lib/change-notes/2023-08-08-splat-arguments.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Flow between splat arguments (`*args`) and positional parameters is now tracked more precisely.
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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() }
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
}
|
||||
|
||||
4
swift/ql/lib/change-notes/2023-08-08-ui-text-input.md
Normal file
4
swift/ql/lib/change-notes/2023-08-08-ui-text-input.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added local flow sources for `UITextInput` and related classes.
|
||||
@@ -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
|
||||
|
||||
@@ -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() + "]"
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user