mirror of
https://github.com/github/codeql.git
synced 2026-07-07 12:35:33 +02:00
Compare commits
62 Commits
codeql-cli
...
smowton/ad
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b512d65918 | ||
|
|
ebdf553621 | ||
|
|
3e8274ede8 | ||
|
|
06e5962da7 | ||
|
|
03c0366fd4 | ||
|
|
789b0a46d1 | ||
|
|
8fcbaea273 | ||
|
|
c015ef6ef4 | ||
|
|
489355cdab | ||
|
|
093a3879be | ||
|
|
a737350f27 | ||
|
|
bf921177f4 | ||
|
|
0187e9a3b1 | ||
|
|
be5363ea53 | ||
|
|
650d57083b | ||
|
|
bcaba45202 | ||
|
|
059dea713d | ||
|
|
dca74a1f45 | ||
|
|
d309e15072 | ||
|
|
35471ff23c | ||
|
|
52dc016a7a | ||
|
|
e85cdf2ec3 | ||
|
|
a5189eae9f | ||
|
|
f4581ae866 | ||
|
|
83fdff54c4 | ||
|
|
c20ce62767 | ||
|
|
813de65118 | ||
|
|
a09fd8c35e | ||
|
|
8139e1a2a8 | ||
|
|
bd09c61504 | ||
|
|
0ec5aa6095 | ||
|
|
addb92f13b | ||
|
|
325b451288 | ||
|
|
6180970ae7 | ||
|
|
74906fcbaf | ||
|
|
c6e66edb97 | ||
|
|
ea229d361c | ||
|
|
b4542c58c2 | ||
|
|
44216b29a9 | ||
|
|
4c66f50352 | ||
|
|
fec2837c1e | ||
|
|
ddb682b181 | ||
|
|
37a76f4441 | ||
|
|
c9db6201ef | ||
|
|
7d5c80433d | ||
|
|
37b3a6e5c0 | ||
|
|
c79c9dd573 | ||
|
|
8726766465 | ||
|
|
f6fd401df1 | ||
|
|
f9e5c6b77d | ||
|
|
f533636ad7 | ||
|
|
90505949c7 | ||
|
|
27d87e9300 | ||
|
|
24697feebc | ||
|
|
197ea5b8f3 | ||
|
|
71f9b25500 | ||
|
|
64496b4c97 | ||
|
|
91fd83a554 | ||
|
|
ae3b32409a | ||
|
|
20599d1846 | ||
|
|
e79eecb640 | ||
|
|
c5fb19c377 |
@@ -2,28 +2,41 @@
|
||||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
exclude: /test/.*$(?<!\.ql)(?<!\.qll)(?<!\.qlref)
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v3.2.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
|
||||
- repo: local
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: v13.0.1
|
||||
hooks:
|
||||
- id: codeql-format
|
||||
- id: clang-format
|
||||
files: ^swift/.*\.(h|c|cpp)$
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: codeql-format
|
||||
name: Fix QL file formatting
|
||||
files: \.qll?$
|
||||
language: system
|
||||
entry: codeql query format --in-place
|
||||
|
||||
- id: sync-files
|
||||
- id: sync-files
|
||||
name: Fix files required to be identical
|
||||
language: system
|
||||
entry: python3 config/sync-files.py --latest
|
||||
pass_filenames: false
|
||||
|
||||
- id: qhelp
|
||||
- id: qhelp
|
||||
name: Check query help generation
|
||||
files: \.qhelp$
|
||||
language: system
|
||||
entry: python3 misc/scripts/check-qhelp.py
|
||||
|
||||
- id: swift-codegen
|
||||
name: Run Swift checked in code generation
|
||||
files: ^swift/(codegen/|.*/generated/|ql/lib/swift\.dbscheme$)
|
||||
language: system
|
||||
entry: bazel run //swift/codegen
|
||||
pass_filenames: false
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
@@ -550,4 +551,4 @@
|
||||
"javascript/ql/lib/semmle/javascript/security/dataflow/HttpToFileAccessCustomizations.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/HttpToFileAccessCustomizations.qll"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,11 +27,11 @@ int getBufferSize(Expr bufferExpr, Element why) {
|
||||
result = bufferVar.getUnspecifiedType().(ArrayType).getSize() and
|
||||
why = bufferVar and
|
||||
not memberMayBeVarSize(_, bufferVar) and
|
||||
not result = 0 // zero sized arrays are likely to have special usage, for example
|
||||
or
|
||||
// zero sized arrays are likely to have special usage, for example
|
||||
// behaving a bit like a 'union' overlapping other fields.
|
||||
// buffer is an initialized array
|
||||
// e.g. int buffer[] = {1, 2, 3};
|
||||
not result = 0
|
||||
or
|
||||
// buffer is an initialized array, e.g., int buffer[] = {1, 2, 3};
|
||||
why = bufferVar.getInitializer().getExpr() and
|
||||
(
|
||||
why instanceof AggregateLiteral or
|
||||
|
||||
@@ -141,7 +141,7 @@ private predicate isOpaqueType(Type type) {
|
||||
* Holds if an `IROpaqueType` with the specified `tag` and `byteSize` should exist.
|
||||
*/
|
||||
predicate hasOpaqueType(Type tag, int byteSize) {
|
||||
isOpaqueType(tag) and byteSize = getTypeSize(tag)
|
||||
isOpaqueType(tag) and byteSize = getTypeSize(tag.getUnspecifiedType())
|
||||
or
|
||||
tag instanceof UnknownType and Raw::needsUnknownOpaqueType(byteSize)
|
||||
}
|
||||
@@ -153,17 +153,18 @@ private IRType getIRTypeForPRValue(Type type) {
|
||||
exists(Type unspecifiedType | unspecifiedType = type.getUnspecifiedType() |
|
||||
isOpaqueType(unspecifiedType) and
|
||||
exists(IROpaqueType opaqueType | opaqueType = result |
|
||||
opaqueType.getByteSize() = getTypeSize(type) and
|
||||
opaqueType.getByteSize() = getTypeSize(unspecifiedType) and
|
||||
opaqueType.getTag() = unspecifiedType
|
||||
)
|
||||
or
|
||||
unspecifiedType instanceof BoolType and result.(IRBooleanType).getByteSize() = type.getSize()
|
||||
unspecifiedType instanceof BoolType and
|
||||
result.(IRBooleanType).getByteSize() = unspecifiedType.getSize()
|
||||
or
|
||||
isSignedIntegerType(unspecifiedType) and
|
||||
result.(IRSignedIntegerType).getByteSize() = type.getSize()
|
||||
result.(IRSignedIntegerType).getByteSize() = unspecifiedType.getSize()
|
||||
or
|
||||
isUnsignedIntegerType(unspecifiedType) and
|
||||
result.(IRUnsignedIntegerType).getByteSize() = type.getSize()
|
||||
result.(IRUnsignedIntegerType).getByteSize() = unspecifiedType.getSize()
|
||||
or
|
||||
exists(FloatingPointType floatType, IRFloatingPointType irFloatType |
|
||||
floatType = unspecifiedType and
|
||||
@@ -173,7 +174,8 @@ private IRType getIRTypeForPRValue(Type type) {
|
||||
irFloatType.getDomain() = floatType.getDomain()
|
||||
)
|
||||
or
|
||||
isPointerIshType(unspecifiedType) and result.(IRAddressType).getByteSize() = getTypeSize(type)
|
||||
isPointerIshType(unspecifiedType) and
|
||||
result.(IRAddressType).getByteSize() = getTypeSize(unspecifiedType)
|
||||
or
|
||||
unspecifiedType instanceof FunctionPointerIshType and
|
||||
result.(IRFunctionAddressType).getByteSize() = getTypeSize(type)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
| templates.cpp:9:5:9:14 | using c | UsingDeclarationEntry, enclosingElement:std |
|
||||
| usings.cpp:8:1:8:11 | using nf | UsingDeclarationEntry, enclosingElement:(global namespace) |
|
||||
| templates.cpp:9:14:9:14 | using c | UsingDeclarationEntry, enclosingElement:std |
|
||||
| usings.cpp:8:10:8:11 | using nf | UsingDeclarationEntry, enclosingElement:(global namespace) |
|
||||
| usings.cpp:9:1:9:17 | using namespace N | UsingDirectiveEntry, enclosingElement:(global namespace) |
|
||||
| usings.cpp:18:3:18:13 | using bf | UsingDeclarationEntry, enclosingElement:D |
|
||||
| usings.cpp:21:5:21:14 | using gf | UsingDeclarationEntry, enclosingElement:{ ... } |
|
||||
| usings.cpp:34:3:34:20 | using tbf | UsingDeclarationEntry, enclosingElement:TD |
|
||||
| usings.cpp:42:5:42:22 | using foo | UsingDeclarationEntry, enclosingElement:nsbar |
|
||||
| usings.cpp:18:12:18:13 | using bf | UsingDeclarationEntry, enclosingElement:D |
|
||||
| usings.cpp:21:13:21:14 | using gf | UsingDeclarationEntry, enclosingElement:{ ... } |
|
||||
| usings.cpp:34:18:34:20 | using tbf | UsingDeclarationEntry, enclosingElement:TD |
|
||||
| usings.cpp:42:20:42:22 | using foo | UsingDeclarationEntry, enclosingElement:nsbar |
|
||||
|
||||
@@ -42,9 +42,7 @@ module AccessPath {
|
||||
* Parses a lower-bounded interval `n..` and gets the lower bound.
|
||||
*/
|
||||
bindingset[arg]
|
||||
private int parseLowerBound(string arg) {
|
||||
result = arg.regexpCapture("(-?\\d+)\\.\\.", 1).toInt()
|
||||
}
|
||||
int parseLowerBound(string arg) { result = arg.regexpCapture("(-?\\d+)\\.\\.", 1).toInt() }
|
||||
|
||||
/**
|
||||
* Parses an integer constant or interval (bounded or unbounded) that explicitly
|
||||
|
||||
@@ -7,6 +7,7 @@ private import semmle.code.csharp.commons.Util as Util
|
||||
private import semmle.code.csharp.commons.Collections as Collections
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch
|
||||
private import semmle.code.csharp.frameworks.System as System
|
||||
private import semmle.code.csharp.frameworks.system.linq.Expressions
|
||||
import semmle.code.csharp.dataflow.ExternalFlow as ExternalFlow
|
||||
import semmle.code.csharp.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
|
||||
import semmle.code.csharp.dataflow.internal.DataFlowPrivate as DataFlowPrivate
|
||||
@@ -18,21 +19,11 @@ module TaintTracking = CS::TaintTracking;
|
||||
class Type = CS::Type;
|
||||
|
||||
/**
|
||||
* Holds if `api` is an override or an interface implementation that
|
||||
* is irrelevant to the data flow analysis.
|
||||
* Holds if any of the parameters of `api` are `System.Func<>`.
|
||||
*/
|
||||
private predicate isIrrelevantOverrideOrImplementation(CS::Callable api) {
|
||||
exists(CS::Callable exclude, CS::Method m |
|
||||
(
|
||||
api = m.getAnOverrider*().getUnboundDeclaration()
|
||||
or
|
||||
api = m.getAnUltimateImplementor().getUnboundDeclaration()
|
||||
) and
|
||||
exclude = m.getUnboundDeclaration()
|
||||
|
|
||||
exists(System::SystemObjectClass c | exclude = [c.getGetHashCodeMethod(), c.getEqualsMethod()])
|
||||
or
|
||||
exists(System::SystemIEquatableTInterface i | exclude = i.getEqualsMethod())
|
||||
private predicate isHigherOrder(CS::Callable api) {
|
||||
exists(Type t | t = api.getAParameter().getType().getUnboundDeclaration() |
|
||||
t instanceof SystemLinqExpressions::DelegateExtType
|
||||
)
|
||||
}
|
||||
|
||||
@@ -44,7 +35,7 @@ private predicate isRelevantForModels(CS::Callable api) {
|
||||
api.getDeclaringType().getNamespace().getQualifiedName() != "" and
|
||||
not api instanceof CS::ConversionOperator and
|
||||
not api instanceof Util::MainMethod and
|
||||
not isIrrelevantOverrideOrImplementation(api)
|
||||
not isHigherOrder(api)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,8 +56,13 @@ predicate asPartialModel = DataFlowPrivate::Csv::asPartialModel/1;
|
||||
/**
|
||||
* Holds for type `t` for fields that are relevant as an intermediate
|
||||
* read or write step in the data flow analysis.
|
||||
* That is, flow through any data-flow node that does not have a relevant type
|
||||
* will be excluded.
|
||||
*/
|
||||
predicate isRelevantType(CS::Type t) { not t instanceof CS::Enum }
|
||||
predicate isRelevantType(CS::Type t) {
|
||||
not t instanceof CS::SimpleType and
|
||||
not t instanceof CS::Enum
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the CSV string representation of the qualifier.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
| NoSummaries;PublicClassFlow;false;PublicReturn;(System.Int32);;Argument[0];ReturnValue;generated:taint |
|
||||
| Summaries;BaseClassFlow;true;ReturnParam;(System.Int32);;Argument[0];ReturnValue;generated:taint |
|
||||
| NoSummaries;PublicClassFlow;false;PublicReturn;(System.Object);;Argument[0];ReturnValue;generated:taint |
|
||||
| Summaries;BaseClassFlow;true;ReturnParam;(System.Object);;Argument[0];ReturnValue;generated:taint |
|
||||
| Summaries;BasicFlow;false;ReturnField;();;Argument[Qualifier];ReturnValue;generated:taint |
|
||||
| Summaries;BasicFlow;false;ReturnParam0;(System.String,System.Object);;Argument[0];ReturnValue;generated:taint |
|
||||
| Summaries;BasicFlow;false;ReturnParam1;(System.String,System.Object);;Argument[1];ReturnValue;generated:taint |
|
||||
@@ -11,14 +11,14 @@
|
||||
| Summaries;CollectionFlow;false;AddFieldToList;(System.Collections.Generic.List<System.String>);;Argument[Qualifier];Argument[0].Element;generated:taint |
|
||||
| Summaries;CollectionFlow;false;AddToList;(System.Collections.Generic.List<System.Object>,System.Object);;Argument[1];Argument[0].Element;generated:taint |
|
||||
| Summaries;CollectionFlow;false;AssignFieldToArray;(System.Object[]);;Argument[Qualifier];Argument[0].Element;generated:taint |
|
||||
| Summaries;CollectionFlow;false;AssignToArray;(System.Int32,System.Int32[]);;Argument[0];Argument[1].Element;generated:taint |
|
||||
| Summaries;CollectionFlow;false;ReturnArrayElement;(System.Int32[]);;Argument[0].Element;ReturnValue;generated:taint |
|
||||
| Summaries;CollectionFlow;false;AssignToArray;(System.Object,System.Object[]);;Argument[0];Argument[1].Element;generated:taint |
|
||||
| Summaries;CollectionFlow;false;ReturnArrayElement;(System.Object[]);;Argument[0].Element;ReturnValue;generated:taint |
|
||||
| Summaries;CollectionFlow;false;ReturnFieldInAList;();;Argument[Qualifier];ReturnValue;generated:taint |
|
||||
| Summaries;CollectionFlow;false;ReturnListElement;(System.Collections.Generic.List<System.Object>);;Argument[0].Element;ReturnValue;generated:taint |
|
||||
| Summaries;DerivedClass1Flow;false;ReturnParam1;(System.Int32,System.Int32);;Argument[1];ReturnValue;generated:taint |
|
||||
| Summaries;DerivedClass2Flow;false;ReturnParam0;(System.Int32,System.Int32);;Argument[0];ReturnValue;generated:taint |
|
||||
| Summaries;DerivedClass2Flow;false;ReturnParam;(System.Int32);;Argument[0];ReturnValue;generated:taint |
|
||||
| Summaries;EqualsGetHashCodeNoFlow;false;Equals;(System.Int32);;Argument[0];ReturnValue;generated:taint |
|
||||
| Summaries;DerivedClass1Flow;false;ReturnParam1;(System.String,System.String);;Argument[1];ReturnValue;generated:taint |
|
||||
| Summaries;DerivedClass2Flow;false;ReturnParam0;(System.String,System.Int32);;Argument[0];ReturnValue;generated:taint |
|
||||
| Summaries;DerivedClass2Flow;false;ReturnParam;(System.Object);;Argument[0];ReturnValue;generated:taint |
|
||||
| Summaries;EqualsGetHashCodeNoFlow;false;Equals;(System.String);;Argument[0];ReturnValue;generated:taint |
|
||||
| Summaries;GenericFlow<>;false;AddFieldToGenericList;(System.Collections.Generic.List<T>);;Argument[Qualifier];Argument[0].Element;generated:taint |
|
||||
| Summaries;GenericFlow<>;false;AddToGenericList<>;(System.Collections.Generic.List<S>,S);;Argument[1];Argument[0].Element;generated:taint |
|
||||
| Summaries;GenericFlow<>;false;ReturnFieldInGenericList;();;Argument[Qualifier];ReturnValue;generated:taint |
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace NoSummaries;
|
||||
// Just to prove that, if a method like this is correctly exposed, a flow summary will be captured.
|
||||
public class PublicClassFlow
|
||||
{
|
||||
public int PublicReturn(int input)
|
||||
public object PublicReturn(object input)
|
||||
{
|
||||
return input;
|
||||
}
|
||||
@@ -14,19 +14,19 @@ public class PublicClassFlow
|
||||
|
||||
public sealed class PublicClassNoFlow
|
||||
{
|
||||
private int PrivateReturn(int input)
|
||||
private object PrivateReturn(object input)
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
internal int InternalReturn(int input)
|
||||
internal object InternalReturn(object input)
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
private class PrivateClassNoFlow
|
||||
{
|
||||
public int ReturnParam(int input)
|
||||
public object ReturnParam(object input)
|
||||
{
|
||||
return input;
|
||||
}
|
||||
@@ -36,7 +36,7 @@ public sealed class PublicClassNoFlow
|
||||
{
|
||||
public class NestedPublicClassFlow
|
||||
{
|
||||
public int ReturnParam(int input)
|
||||
public object ReturnParam(object input)
|
||||
{
|
||||
return input;
|
||||
}
|
||||
@@ -44,10 +44,10 @@ public sealed class PublicClassNoFlow
|
||||
}
|
||||
}
|
||||
|
||||
public class EquatableBound : IEquatable<int>
|
||||
public class EquatableBound : IEquatable<object>
|
||||
{
|
||||
public readonly bool tainted;
|
||||
public bool Equals(int other)
|
||||
public bool Equals(object other)
|
||||
{
|
||||
return tainted;
|
||||
}
|
||||
@@ -60,4 +60,42 @@ public class EquatableUnBound<T> : IEquatable<T>
|
||||
{
|
||||
return tainted;
|
||||
}
|
||||
}
|
||||
|
||||
// No methods in this class will have generated flow summaries as
|
||||
// simple types are used.
|
||||
public class SimpleTypes
|
||||
{
|
||||
public bool M1(bool b)
|
||||
{
|
||||
return b;
|
||||
}
|
||||
|
||||
public Boolean M2(Boolean b)
|
||||
{
|
||||
return b;
|
||||
}
|
||||
|
||||
public int M3(int i)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
public Int32 M4(Int32 i)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
public class HigherOrderParameters
|
||||
{
|
||||
public string M1(string s, Func<string, string> map)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
public object M2(Func<object, object> map, object o)
|
||||
{
|
||||
return map(o);
|
||||
}
|
||||
}
|
||||
@@ -48,12 +48,12 @@ public class CollectionFlow
|
||||
{
|
||||
private string tainted;
|
||||
|
||||
public int ReturnArrayElement(int[] input)
|
||||
public object ReturnArrayElement(object[] input)
|
||||
{
|
||||
return input[0];
|
||||
}
|
||||
|
||||
public void AssignToArray(int data, int[] target)
|
||||
public void AssignToArray(object data, object[] target)
|
||||
{
|
||||
target[0] = data;
|
||||
}
|
||||
@@ -146,7 +146,7 @@ public class GenericFlow<T>
|
||||
|
||||
public abstract class BaseClassFlow
|
||||
{
|
||||
public virtual int ReturnParam(int input)
|
||||
public virtual object ReturnParam(object input)
|
||||
{
|
||||
return input;
|
||||
}
|
||||
@@ -154,7 +154,7 @@ public abstract class BaseClassFlow
|
||||
|
||||
public class DerivedClass1Flow : BaseClassFlow
|
||||
{
|
||||
public int ReturnParam1(int input0, int input1)
|
||||
public string ReturnParam1(string input0, string input1)
|
||||
{
|
||||
return input1;
|
||||
}
|
||||
@@ -162,12 +162,12 @@ public class DerivedClass1Flow : BaseClassFlow
|
||||
|
||||
public class DerivedClass2Flow : BaseClassFlow
|
||||
{
|
||||
public override int ReturnParam(int input)
|
||||
public override object ReturnParam(object input)
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
public int ReturnParam0(int input0, int input1)
|
||||
public string ReturnParam0(string input0, int input1)
|
||||
{
|
||||
return input0;
|
||||
}
|
||||
@@ -195,13 +195,13 @@ public class OperatorFlow
|
||||
}
|
||||
|
||||
// No flow summary as this is an implicit conversion operator.
|
||||
public static implicit operator OperatorFlow(int i)
|
||||
public static implicit operator OperatorFlow(string s)
|
||||
{
|
||||
return new OperatorFlow(i);
|
||||
return new OperatorFlow(s);
|
||||
}
|
||||
|
||||
// No flow summary as this is an explicit conversion operator.
|
||||
public static explicit operator OperatorFlow(byte b)
|
||||
public static explicit operator OperatorFlow(string[] b)
|
||||
{
|
||||
return new OperatorFlow(b);
|
||||
}
|
||||
@@ -220,9 +220,9 @@ public class EqualsGetHashCodeNoFlow
|
||||
}
|
||||
|
||||
// Flow summary as this is not an override of the object Equals method.
|
||||
public int Equals(int i)
|
||||
public string Equals(string s)
|
||||
{
|
||||
return i;
|
||||
return s;
|
||||
}
|
||||
|
||||
// No flow summary as this is an override of the GetHashCode method.
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
.NET 5, .NET 6","``.sln``, ``.csproj``, ``.cs``, ``.cshtml``, ``.xaml``"
|
||||
Go (aka Golang), "Go up to 1.17", "Go 1.11 or more recent", ``.go``
|
||||
Java,"Java 7 to 17 [4]_","javac (OpenJDK and Oracle JDK),
|
||||
Java,"Java 7 to 18 [4]_","javac (OpenJDK and Oracle JDK),
|
||||
|
||||
Eclipse compiler for Java (ECJ) [5]_",``.java``
|
||||
JavaScript,ECMAScript 2021 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhtm``, ``.xhtml``, ``.vue``, ``.hbs``, ``.ejs``, ``.njk``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [6]_"
|
||||
@@ -30,7 +30,7 @@
|
||||
.. [1] C++20 support is currently in beta. Supported for GCC on Linux only. Modules are *not* supported.
|
||||
.. [2] Support for the clang-cl compiler is preliminary.
|
||||
.. [3] Support for the Arm Compiler (armcc) is preliminary.
|
||||
.. [4] Builds that execute on Java 7 to 17 can be analyzed. The analysis understands Java 17 standard language features.
|
||||
.. [4] Builds that execute on Java 7 to 18 can be analyzed. The analysis understands Java 18 standard language features.
|
||||
.. [5] ECJ is supported when the build invokes it via the Maven Compiler plugin or the Takari Lifecycle plugin.
|
||||
.. [6] JSX and Flow code, YAML, JSON, HTML, and XML files may also be analyzed with JavaScript files.
|
||||
.. [7] Requires glibc 2.17.
|
||||
|
||||
7
java/ql/lib/semmle/code/java/dataflow/TaintTracking3.qll
Normal file
7
java/ql/lib/semmle/code/java/dataflow/TaintTracking3.qll
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) taint-tracking analyses.
|
||||
*/
|
||||
module TaintTracking3 {
|
||||
import semmle.code.java.dataflow.internal.tainttracking3.TaintTrackingImpl
|
||||
}
|
||||
@@ -42,9 +42,7 @@ module AccessPath {
|
||||
* Parses a lower-bounded interval `n..` and gets the lower bound.
|
||||
*/
|
||||
bindingset[arg]
|
||||
private int parseLowerBound(string arg) {
|
||||
result = arg.regexpCapture("(-?\\d+)\\.\\.", 1).toInt()
|
||||
}
|
||||
int parseLowerBound(string arg) { result = arg.regexpCapture("(-?\\d+)\\.\\.", 1).toInt() }
|
||||
|
||||
/**
|
||||
* Parses an integer constant or interval (bounded or unbounded) that explicitly
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* Provides an implementation of global (interprocedural) taint tracking.
|
||||
* This file re-exports the local (intraprocedural) taint-tracking analysis
|
||||
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
|
||||
* exposed through the `Configuration` class. For some languages, this file
|
||||
* exists in several identical copies, allowing queries to use multiple
|
||||
* `Configuration` classes that depend on each other without introducing
|
||||
* mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
import TaintTrackingParameter::Public
|
||||
private import TaintTrackingParameter::Private
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural taint tracking analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the taint tracking library must define its own unique extension of
|
||||
* this abstract class.
|
||||
*
|
||||
* A taint-tracking configuration is a special data flow configuration
|
||||
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
|
||||
* necessarily preserve values but are still relevant from a taint tracking
|
||||
* perspective. (For example, string concatenation, where one of the operands
|
||||
* is tainted.)
|
||||
*
|
||||
* To create a configuration, extend this class with a subclass whose
|
||||
* characteristic predicate is a unique singleton string. For example, write
|
||||
*
|
||||
* ```ql
|
||||
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isSanitizer`.
|
||||
* // Optionally override `isSanitizerIn`.
|
||||
* // Optionally override `isSanitizerOut`.
|
||||
* // Optionally override `isSanitizerGuard`.
|
||||
* // Optionally override `isAdditionalTaintStep`.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```ql
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but it is unsupported to depend on
|
||||
* another `TaintTracking::Configuration` or a `DataFlow::Configuration` in the
|
||||
* overridden predicates that define sources, sinks, or additional steps.
|
||||
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
|
||||
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
|
||||
*/
|
||||
abstract class Configuration extends DataFlow::Configuration {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSource(DataFlow::Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source with the given initial
|
||||
* `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink accepting `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) { none() }
|
||||
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node) {
|
||||
this.isSanitizer(node) or
|
||||
defaultTaintSanitizer(node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node `node` is a taint sanitizer when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSanitizer(DataFlow::Node node, DataFlow::FlowState state) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
this.isSanitizer(node, state)
|
||||
}
|
||||
|
||||
/** Holds if taint propagation into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) }
|
||||
|
||||
/** Holds if taint propagation out of `node` is prohibited. */
|
||||
predicate isSanitizerOut(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { none() }
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
this.isAdditionalTaintStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
final override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
this.isAdditionalTaintStep(node1, state1, node2, state2)
|
||||
}
|
||||
|
||||
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::Content c) {
|
||||
(this.isSink(node) or this.isAdditionalTaintStep(node, _)) and
|
||||
defaultImplicitTaintRead(node, c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import semmle.code.java.dataflow.internal.TaintTrackingUtil as Public
|
||||
|
||||
module Private {
|
||||
import semmle.code.java.dataflow.DataFlow3::DataFlow3 as DataFlow
|
||||
}
|
||||
@@ -30,7 +30,7 @@ class Yaml extends RefType {
|
||||
Yaml() { this.getAnAncestor().hasQualifiedName("org.yaml.snakeyaml", "Yaml") }
|
||||
}
|
||||
|
||||
private class SafeYamlConstructionFlowConfig extends DataFlow2::Configuration {
|
||||
private class SafeYamlConstructionFlowConfig extends DataFlow3::Configuration {
|
||||
SafeYamlConstructionFlowConfig() { this = "SnakeYaml::SafeYamlConstructionFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) {
|
||||
@@ -65,7 +65,7 @@ private class SnakeYamlParse extends MethodAccess {
|
||||
}
|
||||
}
|
||||
|
||||
private class SafeYamlFlowConfig extends DataFlow3::Configuration {
|
||||
private class SafeYamlFlowConfig extends DataFlow2::Configuration {
|
||||
SafeYamlFlowConfig() { this = "SnakeYaml::SafeYamlFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SafeYaml }
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.DataFlow3
|
||||
import semmle.code.java.dataflow.DataFlow2
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.dataflow.TaintTracking2
|
||||
import semmle.code.java.dataflow.TaintTracking3
|
||||
import semmle.code.java.security.AndroidIntentRedirection
|
||||
|
||||
/**
|
||||
@@ -38,7 +38,7 @@ private class OriginalIntentSanitizer extends IntentRedirectionSanitizer {
|
||||
* Data flow configuration used to discard incoming Intents
|
||||
* flowing directly to sinks that start Android components.
|
||||
*/
|
||||
private class SameIntentBeingRelaunchedConfiguration extends DataFlow3::Configuration {
|
||||
private class SameIntentBeingRelaunchedConfiguration extends DataFlow2::Configuration {
|
||||
SameIntentBeingRelaunchedConfiguration() { this = "SameIntentBeingRelaunchedConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
@@ -74,7 +74,7 @@ private class IntentWithTaintedComponent extends DataFlow::Node {
|
||||
/**
|
||||
* A taint tracking configuration for tainted data flowing to an `Intent`'s component.
|
||||
*/
|
||||
private class TaintedIntentComponentConf extends TaintTracking2::Configuration {
|
||||
private class TaintedIntentComponentConf extends TaintTracking3::Configuration {
|
||||
TaintedIntentComponentConf() { this = "TaintedIntentComponentConf" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import java
|
||||
import semmle.code.java.frameworks.JAXB
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.DataFlow2
|
||||
import semmle.code.java.security.CleartextStorageQuery
|
||||
import semmle.code.java.security.CleartextStoragePropertiesQuery
|
||||
|
||||
@@ -74,7 +73,7 @@ private Expr getInstanceInput(DataFlow::Node instance, RefType t) {
|
||||
)
|
||||
}
|
||||
|
||||
private class ClassStoreFlowConfig extends DataFlow2::Configuration {
|
||||
private class ClassStoreFlowConfig extends DataFlow::Configuration {
|
||||
ClassStoreFlowConfig() { this = "ClassStoreFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof ClassStore }
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import java
|
||||
private import semmle.code.java.dataflow.DataFlow4
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.dataflow.TaintTracking2
|
||||
private import semmle.code.java.security.SensitiveActions
|
||||
|
||||
/** A sink representing persistent storage that saves data in clear text. */
|
||||
@@ -39,7 +40,7 @@ abstract class Storable extends Call {
|
||||
abstract Expr getAStore();
|
||||
}
|
||||
|
||||
private class SensitiveSourceFlowConfig extends TaintTracking::Configuration {
|
||||
private class SensitiveSourceFlowConfig extends TaintTracking2::Configuration {
|
||||
SensitiveSourceFlowConfig() { this = "SensitiveSourceFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SensitiveExpr }
|
||||
|
||||
@@ -383,8 +383,8 @@ module API {
|
||||
exists(Node pred, Label::ApiLabel lbl, string predpath |
|
||||
Impl::edge(pred, lbl, this) and
|
||||
predpath = pred.getAPath(length - 1) and
|
||||
exists(string space | if length = 1 then space = "" else space = " " |
|
||||
result = "(" + lbl + space + predpath + ")" and
|
||||
exists(string dot | if length = 1 then dot = "" else dot = "." |
|
||||
result = predpath + dot + lbl and
|
||||
// avoid producing strings longer than 1MB
|
||||
result.length() < 1000 * 1000
|
||||
)
|
||||
@@ -1330,22 +1330,22 @@ module API {
|
||||
/** Gets the EntryPoint associated with this label. */
|
||||
API::EntryPoint getEntryPoint() { result = e }
|
||||
|
||||
override string toString() { result = e }
|
||||
override string toString() { result = "getASuccessor(Label::entryPoint(\"" + e + "\"))" }
|
||||
}
|
||||
|
||||
/** A label that gets a promised value. */
|
||||
class LabelPromised extends ApiLabel, MkLabelPromised {
|
||||
override string toString() { result = "promised" }
|
||||
override string toString() { result = "getPromised()" }
|
||||
}
|
||||
|
||||
/** A label that gets a rejected promise. */
|
||||
class LabelPromisedError extends ApiLabel, MkLabelPromisedError {
|
||||
override string toString() { result = "promisedError" }
|
||||
override string toString() { result = "getPromisedError()" }
|
||||
}
|
||||
|
||||
/** A label that gets the return value of a function. */
|
||||
class LabelReturn extends ApiLabel, MkLabelReturn {
|
||||
override string toString() { result = "return" }
|
||||
override string toString() { result = "getReturn()" }
|
||||
}
|
||||
|
||||
/** A label for a module. */
|
||||
@@ -1357,12 +1357,13 @@ module API {
|
||||
/** Gets the module associated with this label. */
|
||||
string getMod() { result = mod }
|
||||
|
||||
override string toString() { result = "module " + mod }
|
||||
// moduleImport is not neccesarilly the predicate to use, but it's close enough for most cases.
|
||||
override string toString() { result = "moduleImport(\"" + mod + "\")" }
|
||||
}
|
||||
|
||||
/** A label that gets an instance from a `new` call. */
|
||||
class LabelInstance extends ApiLabel, MkLabelInstance {
|
||||
override string toString() { result = "instance" }
|
||||
override string toString() { result = "getInstance()" }
|
||||
}
|
||||
|
||||
/** A label for the member named `prop`. */
|
||||
@@ -1374,14 +1375,14 @@ module API {
|
||||
/** Gets the property associated with this label. */
|
||||
string getProperty() { result = prop }
|
||||
|
||||
override string toString() { result = "member " + prop }
|
||||
override string toString() { result = "getMember(\"" + prop + "\")" }
|
||||
}
|
||||
|
||||
/** A label for a member with an unknown name. */
|
||||
class LabelUnknownMember extends ApiLabel, MkLabelUnknownMember {
|
||||
LabelUnknownMember() { this = MkLabelUnknownMember() }
|
||||
|
||||
override string toString() { result = "member *" }
|
||||
override string toString() { result = "getUnknownMember()" }
|
||||
}
|
||||
|
||||
/** A label for parameter `i`. */
|
||||
@@ -1390,7 +1391,7 @@ module API {
|
||||
|
||||
LabelParameter() { this = MkLabelParameter(i) }
|
||||
|
||||
override string toString() { result = "parameter " + i }
|
||||
override string toString() { result = "getParameter(" + i + ")" }
|
||||
|
||||
/** Gets the index of the parameter for this label. */
|
||||
int getIndex() { result = i }
|
||||
@@ -1398,22 +1399,22 @@ module API {
|
||||
|
||||
/** A label for the receiver of call, that is, the value passed as `this`. */
|
||||
class LabelReceiver extends ApiLabel, MkLabelReceiver {
|
||||
override string toString() { result = "receiver" }
|
||||
override string toString() { result = "getReceiver()" }
|
||||
}
|
||||
|
||||
/** A label for a class decorated by the current value. */
|
||||
class LabelDecoratedClass extends ApiLabel, MkLabelDecoratedClass {
|
||||
override string toString() { result = "decorated-class" }
|
||||
override string toString() { result = "getADecoratedClass()" }
|
||||
}
|
||||
|
||||
/** A label for a method, field, or accessor decorated by the current value. */
|
||||
class LabelDecoratedMethod extends ApiLabel, MkLabelDecoratedMember {
|
||||
override string toString() { result = "decorated-member" }
|
||||
override string toString() { result = "decoratedMember()" }
|
||||
}
|
||||
|
||||
/** A label for a parameter decorated by the current value. */
|
||||
class LabelDecoratedParameter extends ApiLabel, MkLabelDecoratedParameter {
|
||||
override string toString() { result = "decorated-parameter" }
|
||||
override string toString() { result = "decoratedParameter()" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -589,6 +589,13 @@ module DataFlow {
|
||||
* Gets the node where the property write happens in the control flow graph.
|
||||
*/
|
||||
abstract ControlFlowNode getWriteNode();
|
||||
|
||||
/**
|
||||
* If this installs an accessor on an object, as opposed to a regular property,
|
||||
* gets the body of the accessor. `isSetter` is true if installing a setter, and
|
||||
* false is installing a getter.
|
||||
*/
|
||||
DataFlow::FunctionNode getInstalledAccessor(boolean isSetter) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -628,6 +635,17 @@ module DataFlow {
|
||||
|
||||
override Node getRhs() { result = valueNode(prop.(ValueProperty).getInit()) }
|
||||
|
||||
override DataFlow::FunctionNode getInstalledAccessor(boolean isSetter) {
|
||||
(
|
||||
prop instanceof PropertySetter and
|
||||
isSetter = true
|
||||
or
|
||||
prop instanceof PropertyGetter and
|
||||
isSetter = false
|
||||
) and
|
||||
result = valueNode(prop.getInit())
|
||||
}
|
||||
|
||||
override ControlFlowNode getWriteNode() { result = prop }
|
||||
}
|
||||
|
||||
@@ -688,6 +706,17 @@ module DataFlow {
|
||||
result = valueNode(prop.getInit())
|
||||
}
|
||||
|
||||
override DataFlow::FunctionNode getInstalledAccessor(boolean isSetter) {
|
||||
(
|
||||
prop instanceof SetterMethodDefinition and
|
||||
isSetter = true
|
||||
or
|
||||
prop instanceof GetterMethodDefinition and
|
||||
isSetter = false
|
||||
) and
|
||||
result = valueNode(prop.getInit())
|
||||
}
|
||||
|
||||
override ControlFlowNode getWriteNode() { result = prop }
|
||||
}
|
||||
|
||||
@@ -711,6 +740,17 @@ module DataFlow {
|
||||
result = valueNode(prop.getInit())
|
||||
}
|
||||
|
||||
override DataFlow::FunctionNode getInstalledAccessor(boolean isSetter) {
|
||||
(
|
||||
prop instanceof SetterMethodDefinition and
|
||||
isSetter = true
|
||||
or
|
||||
prop instanceof GetterMethodDefinition and
|
||||
isSetter = false
|
||||
) and
|
||||
result = valueNode(prop.getInit())
|
||||
}
|
||||
|
||||
override ControlFlowNode getWriteNode() { result = prop }
|
||||
}
|
||||
|
||||
|
||||
@@ -898,17 +898,31 @@ class ClassNode extends DataFlow::SourceNode instanceof ClassNode::Range {
|
||||
*/
|
||||
FunctionNode getAnInstanceMember() { result = super.getAnInstanceMember(_) }
|
||||
|
||||
/**
|
||||
* Gets the static method, getter, or setter declared in this class with the given name and kind.
|
||||
*/
|
||||
FunctionNode getStaticMember(string name, MemberKind kind) {
|
||||
result = super.getStaticMember(name, kind)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the static method declared in this class with the given name.
|
||||
*/
|
||||
FunctionNode getStaticMethod(string name) { result = super.getStaticMethod(name) }
|
||||
FunctionNode getStaticMethod(string name) {
|
||||
result = this.getStaticMember(name, MemberKind::method())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a static method, getter, or setter declared in this class with the given kind.
|
||||
*/
|
||||
FunctionNode getAStaticMember(MemberKind kind) { result = super.getAStaticMember(kind) }
|
||||
|
||||
/**
|
||||
* Gets a static method declared in this class.
|
||||
*
|
||||
* The constructor is not considered a static method.
|
||||
*/
|
||||
FunctionNode getAStaticMethod() { result = super.getAStaticMethod() }
|
||||
FunctionNode getAStaticMethod() { result = this.getAStaticMember(MemberKind::method()) }
|
||||
|
||||
/**
|
||||
* Gets a dataflow node that refers to the superclass of this class.
|
||||
@@ -1119,18 +1133,34 @@ module ClassNode {
|
||||
abstract FunctionNode getAnInstanceMember(MemberKind kind);
|
||||
|
||||
/**
|
||||
* Gets the static member of this class with the given name and kind.
|
||||
*/
|
||||
cached
|
||||
abstract FunctionNode getStaticMember(string name, MemberKind kind);
|
||||
|
||||
/**
|
||||
* DEPRECATED. Override `getStaticMember` instead.
|
||||
*
|
||||
* Gets the static method of this class with the given name.
|
||||
*/
|
||||
cached
|
||||
abstract FunctionNode getStaticMethod(string name);
|
||||
deprecated FunctionNode getStaticMethod(string name) { none() }
|
||||
|
||||
/**
|
||||
* Gets a static member of this class of the given kind.
|
||||
*/
|
||||
cached
|
||||
abstract FunctionNode getAStaticMember(MemberKind kind);
|
||||
|
||||
/**
|
||||
* DEPRECATED. Override `getAStaticMember` instead.
|
||||
*
|
||||
* Gets a static method of this class.
|
||||
*
|
||||
* The constructor is not considered a static method.
|
||||
*/
|
||||
cached
|
||||
abstract FunctionNode getAStaticMethod();
|
||||
deprecated FunctionNode getAStaticMethod() { none() }
|
||||
|
||||
/**
|
||||
* Gets a dataflow node representing a class to be used as the super-class
|
||||
@@ -1186,23 +1216,27 @@ module ClassNode {
|
||||
result = this.getConstructor().getReceiver().getAPropertySource()
|
||||
}
|
||||
|
||||
override FunctionNode getStaticMethod(string name) {
|
||||
override FunctionNode getStaticMember(string name, MemberKind kind) {
|
||||
exists(MethodDeclaration method |
|
||||
method = astNode.getMethod(name) and
|
||||
method.isStatic() and
|
||||
kind = MemberKind::of(method) and
|
||||
result = method.getBody().flow()
|
||||
)
|
||||
or
|
||||
kind.isMethod() and
|
||||
result = this.getAPropertySource(name)
|
||||
}
|
||||
|
||||
override FunctionNode getAStaticMethod() {
|
||||
override FunctionNode getAStaticMember(MemberKind kind) {
|
||||
exists(MethodDeclaration method |
|
||||
method = astNode.getAMethod() and
|
||||
method.isStatic() and
|
||||
kind = MemberKind::of(method) and
|
||||
result = method.getBody().flow()
|
||||
)
|
||||
or
|
||||
kind.isMethod() and
|
||||
result = this.getAPropertySource()
|
||||
}
|
||||
|
||||
@@ -1300,9 +1334,15 @@ module ClassNode {
|
||||
)
|
||||
}
|
||||
|
||||
override FunctionNode getStaticMethod(string name) { result = this.getAPropertySource(name) }
|
||||
override FunctionNode getStaticMember(string name, MemberKind kind) {
|
||||
kind.isMethod() and
|
||||
result = this.getAPropertySource(name)
|
||||
}
|
||||
|
||||
override FunctionNode getAStaticMethod() { result = this.getAPropertySource() }
|
||||
override FunctionNode getAStaticMember(MemberKind kind) {
|
||||
kind.isMethod() and
|
||||
result = this.getAPropertySource()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the prototype of this class.
|
||||
|
||||
@@ -189,25 +189,43 @@ module CallGraph {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ref` installs an accessor on an object. Such property writes should not
|
||||
* be considered calls to an accessor.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate isAccessorInstallation(DataFlow::PropWrite write) {
|
||||
exists(write.getInstalledAccessor(_))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a getter or setter invoked as a result of the given property access.
|
||||
*/
|
||||
cached
|
||||
DataFlow::FunctionNode getAnAccessorCallee(DataFlow::PropRef ref) {
|
||||
exists(DataFlow::ClassNode cls, string name |
|
||||
ref = cls.getAnInstanceMemberAccess(name) and
|
||||
result = cls.getInstanceMember(name, DataFlow::MemberKind::getter())
|
||||
not isAccessorInstallation(ref) and
|
||||
(
|
||||
exists(DataFlow::ClassNode cls, string name |
|
||||
ref = cls.getAnInstanceMemberAccess(name) and
|
||||
result = cls.getInstanceMember(name, DataFlow::MemberKind::getter())
|
||||
or
|
||||
ref = getAnInstanceMemberAssignment(cls, name) and
|
||||
result = cls.getInstanceMember(name, DataFlow::MemberKind::setter())
|
||||
or
|
||||
ref = cls.getAClassReference().getAPropertyRead(name) and
|
||||
result = cls.getStaticMember(name, DataFlow::MemberKind::getter())
|
||||
or
|
||||
ref = cls.getAClassReference().getAPropertyWrite(name) and
|
||||
result = cls.getStaticMember(name, DataFlow::MemberKind::setter())
|
||||
)
|
||||
or
|
||||
ref = getAnInstanceMemberAssignment(cls, name) and
|
||||
result = cls.getInstanceMember(name, DataFlow::MemberKind::setter())
|
||||
)
|
||||
or
|
||||
exists(DataFlow::ObjectLiteralNode object, string name |
|
||||
ref = getAnAllocationSiteRef(object).getAPropertyRead(name) and
|
||||
result = object.getPropertyGetter(name)
|
||||
or
|
||||
ref = getAnAllocationSiteRef(object).getAPropertyWrite(name) and
|
||||
result = object.getPropertySetter(name)
|
||||
exists(DataFlow::ObjectLiteralNode object, string name |
|
||||
ref = getAnAllocationSiteRef(object).getAPropertyRead(name) and
|
||||
result = object.getPropertyGetter(name)
|
||||
or
|
||||
ref = getAnAllocationSiteRef(object).getAPropertyWrite(name) and
|
||||
result = object.getPropertySetter(name)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ private module CachedSteps {
|
||||
* Holds if `invk` may invoke `f`.
|
||||
*/
|
||||
cached
|
||||
predicate calls(DataFlow::SourceNode invk, Function f) {
|
||||
predicate calls(DataFlow::Node invk, Function f) {
|
||||
f = invk.(DataFlow::InvokeNode).getACallee(0)
|
||||
or
|
||||
f = invk.(DataFlow::PropRef).getAnAccessorCallee().getFunction()
|
||||
|
||||
@@ -42,9 +42,7 @@ module AccessPath {
|
||||
* Parses a lower-bounded interval `n..` and gets the lower bound.
|
||||
*/
|
||||
bindingset[arg]
|
||||
private int parseLowerBound(string arg) {
|
||||
result = arg.regexpCapture("(-?\\d+)\\.\\.", 1).toInt()
|
||||
}
|
||||
int parseLowerBound(string arg) { result = arg.regexpCapture("(-?\\d+)\\.\\.", 1).toInt() }
|
||||
|
||||
/**
|
||||
* Parses an integer constant or interval (bounded or unbounded) that explicitly
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
let express = require('express');
|
||||
let app = express()
|
||||
|
||||
express.put('/todos/:id', (req, res) => {
|
||||
app.put('/todos/:id', (req, res) => {
|
||||
let id = req.params.id;
|
||||
let items = req.session.todos[id];
|
||||
if (!items) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
let express = require('express');
|
||||
let app = express()
|
||||
|
||||
express.put('/todos/:id', (req, res) => {
|
||||
app.put('/todos/:id', (req, res) => {
|
||||
let id = req.params.id;
|
||||
let items = req.session.todos.get(id);
|
||||
if (!items) {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The call graph now deals more precisely with calls to accessors (getters and setters).
|
||||
Previously, calls to static accessors were not resolved, and some method calls were
|
||||
incorrectly seen as calls to an accessor. Both issues have been fixed.
|
||||
@@ -1,12 +1,11 @@
|
||||
/**
|
||||
* A test query that verifies assertions about the API graph embedded in source-code comments.
|
||||
*
|
||||
* An assertion is a comment of the form `def <path>` or `use <path>`, and asserts that
|
||||
* there is a def/use feature reachable from the root along the given path (described using
|
||||
* s-expression syntax), and its associated data-flow node must start on the same line as the
|
||||
* comment.
|
||||
* An assertion is a comment of the form `def=<path>` or `use=<path>`, and asserts that
|
||||
* there is a def/use feature reachable from the root along the given path, and its
|
||||
* associated data-flow node must start on the same line as the comment.
|
||||
*
|
||||
* We also support negative assertions of the form `!def <path>` or `!use <path>`, which assert
|
||||
* We also support negative assertions of the form `MISSING: def <path>` or `MISSING: use <path>`, which assert
|
||||
* that there _isn't_ a node with the given path on the same line.
|
||||
*
|
||||
* The query only produces output for failed assertions, meaning that it should have no output
|
||||
@@ -39,44 +38,55 @@ private string getLoc(DataFlow::Node nd) {
|
||||
* An assertion matching a data-flow node against an API-graph feature.
|
||||
*/
|
||||
class Assertion extends Comment {
|
||||
string polarity;
|
||||
string expectedKind;
|
||||
string expectedLoc;
|
||||
string path;
|
||||
string polarity;
|
||||
|
||||
Assertion() {
|
||||
exists(string txt, string rex |
|
||||
txt = this.getText().trim() and
|
||||
rex = "(!?)(def|use) .*"
|
||||
rex = ".*?((?:MISSING: )?)(def|use)=([\\w\\(\\)\"\\.\\-\\/\\@\\:]*).*"
|
||||
|
|
||||
polarity = txt.regexpCapture(rex, 1) and
|
||||
expectedKind = txt.regexpCapture(rex, 2) and
|
||||
path = txt.regexpCapture(rex, 3) and
|
||||
expectedLoc = this.getFile().getAbsolutePath() + ":" + this.getLocation().getStartLine()
|
||||
)
|
||||
}
|
||||
|
||||
string getEdgeLabel(int i) { result = this.getText().regexpFind("(?<=\\()[^()]+", i, _).trim() }
|
||||
string getEdgeLabel(int i) {
|
||||
// matches a single edge. E.g. `getParameter(1)` or `getMember("foo")`.
|
||||
// The lookbehind/lookahead ensure that the boundary is correct, that is
|
||||
// either the edge is next to a ".", or it's the end of the string.
|
||||
result = path.regexpFind("(?<=\\.|^)([\\w\\(\\)\"\\-\\/\\@\\:]+)(?=\\.|$)", i, _).trim()
|
||||
}
|
||||
|
||||
int getPathLength() { result = max(int i | exists(this.getEdgeLabel(i))) + 1 }
|
||||
|
||||
predicate isNegative() { polarity = "MISSING: " }
|
||||
|
||||
API::Node lookup(int i) {
|
||||
i = this.getPathLength() and
|
||||
i = 0 and
|
||||
result = API::root()
|
||||
or
|
||||
result =
|
||||
this.lookup(i + 1)
|
||||
.getASuccessor(any(API::Label::ApiLabel label | label.toString() = this.getEdgeLabel(i)))
|
||||
this.lookup(i - 1)
|
||||
.getASuccessor(any(API::Label::ApiLabel label |
|
||||
label.toString() = this.getEdgeLabel(i - 1)
|
||||
))
|
||||
}
|
||||
|
||||
predicate isNegative() { polarity = "!" }
|
||||
API::Node lookup() { result = this.lookup(this.getPathLength()) }
|
||||
|
||||
predicate holds() { getLoc(getNode(this.lookup(0), expectedKind)) = expectedLoc }
|
||||
predicate holds() { getLoc(getNode(this.lookup(), expectedKind)) = expectedLoc }
|
||||
|
||||
string tryExplainFailure() {
|
||||
exists(int i, API::Node nd, string prefix, string suffix |
|
||||
nd = this.lookup(i) and
|
||||
i > 0 and
|
||||
not exists(this.lookup([0 .. i - 1])) and
|
||||
prefix = nd + " has no outgoing edge labelled " + this.getEdgeLabel(i - 1) + ";" and
|
||||
i < getPathLength() and
|
||||
not exists(this.lookup([i + 1 .. getPathLength()])) and
|
||||
prefix = nd + " has no outgoing edge labelled " + this.getEdgeLabel(i) + ";" and
|
||||
if exists(nd.getASuccessor())
|
||||
then
|
||||
suffix =
|
||||
@@ -91,13 +101,13 @@ class Assertion extends Comment {
|
||||
result = prefix + " " + suffix
|
||||
)
|
||||
or
|
||||
exists(API::Node nd, string kind | nd = this.lookup(0) |
|
||||
exists(API::Node nd, string kind | nd = this.lookup() |
|
||||
exists(getNode(nd, kind)) and
|
||||
not exists(getNode(nd, expectedKind)) and
|
||||
result = "Expected " + expectedKind + " node, but found " + kind + " node."
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Node nd | nd = getNode(this.lookup(0), expectedKind) |
|
||||
exists(DataFlow::Node nd | nd = getNode(this.lookup(), expectedKind) |
|
||||
not getLoc(nd) = expectedLoc and
|
||||
result = "Node not found on this line (but there is one on line " + min(getLoc(nd)) + ")."
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const assert = require("assert");
|
||||
|
||||
let o = {
|
||||
foo: 23 /* def (member foo (parameter 0 (member equal (member exports (module assert))))) */
|
||||
foo: 23 // def=moduleImport("assert").getMember("exports").getMember("equal").getParameter(0).getMember("foo")
|
||||
};
|
||||
assert.equal(o, o);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const fs = require('fs-extra');
|
||||
|
||||
module.exports.foo = async function foo() {
|
||||
return await fs.copy('/tmp/myfile', '/tmp/mynewfile'); /* use (promised (return (member copy (member exports (module fs-extra))))) */ /* def (promised (return (member foo (member exports (module async-await))))) */
|
||||
return await fs.copy('/tmp/myfile', '/tmp/mynewfile'); /* use=moduleImport("fs-extra").getMember("exports").getMember("copy").getReturn().getPromised()*/ /* def=moduleImport("async-await").getMember("exports").getMember("foo").getReturn().getPromised() */
|
||||
};
|
||||
|
||||
@@ -5,5 +5,5 @@ async function readFileUtf8(path: string): Promise<string> {
|
||||
}
|
||||
|
||||
async function test(path: string) {
|
||||
await readFileUtf8(path); /* use (promised (return (member readFile (member exports (module fs/promises))))) */
|
||||
await readFileUtf8(path); /* use=moduleImport("fs/promises").getMember("exports").getMember("readFile").getReturn() */
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import bar from 'foo';
|
||||
|
||||
let boundbar = bar.bind(
|
||||
"receiver", // def (receiver (member default (member exports (module foo))))
|
||||
"firstarg" // def (parameter 0 (member default (member exports (module foo))))
|
||||
"receiver", // def=moduleImport("foo").getMember("exports").getMember("default").getReceiver()
|
||||
"firstarg" // def=moduleImport("foo").getMember("exports").getMember("default").getParameter(0)
|
||||
);
|
||||
boundbar(
|
||||
"secondarg" // def (parameter 1 (member default (member exports (module foo))))
|
||||
"secondarg" // def=moduleImport("foo").getMember("exports").getMember("default").getParameter(1)
|
||||
)
|
||||
|
||||
let boundbar2 = boundbar.bind(
|
||||
"ignored", // !def (receiver (member default (member exports (module foo))))
|
||||
"othersecondarg" // def (parameter 1 (member default (member exports (module foo))))
|
||||
"ignored", // MISSING: def=moduleImport("foo").getMember("exports)".getMember("default").getReceiver()
|
||||
"othersecondarg" // def=moduleImport("foo").getMember("exports").getMember("default").getParameter(1)
|
||||
)
|
||||
boundbar2(
|
||||
"thirdarg" // def (parameter 2 (member default (member exports (module foo))))
|
||||
"thirdarg" // def=moduleImport("foo").getMember("exports").getMember("default").getParameter(2)
|
||||
)
|
||||
|
||||
let bar2 = bar;
|
||||
for (var i = 0; i < 2; ++i)
|
||||
bar2 = bar2.bind(
|
||||
null,
|
||||
i /* def (parameter 1 (member default (member exports (module foo)))) */ /* def (parameter 9 (member default (member exports (module foo)))) */
|
||||
i /* def=moduleImport("foo").getMember("exports").getMember("default").getParameter(1) */ /* def=moduleImport("foo").getMember("exports").getMember("default").getParameter(9) */
|
||||
);
|
||||
|
||||
@@ -3,5 +3,5 @@ const fs = require('fs');
|
||||
exports.foo = function (cb) {
|
||||
if (!cb)
|
||||
cb = function () { };
|
||||
cb(fs.readFileSync("/etc/passwd")); /* def (parameter 0 (parameter 0 (member foo (member exports (module branching-flow))))) */
|
||||
cb(fs.readFileSync("/etc/passwd")); /* def=moduleImport("branching-flow").getMember("exports").getMember("foo").getParameter(0).getParameter(0) */
|
||||
};
|
||||
@@ -9,19 +9,19 @@ util.inherits(MyStream, EventEmitter);
|
||||
|
||||
MyStream.prototype.write = (data) => this.emit('data', data);
|
||||
|
||||
function MyOtherStream() { /* use (instance (member MyOtherStream (member exports (module classes)))) */
|
||||
function MyOtherStream() { /* use=moduleImport("classes").getMember("exports").getMember("MyOtherStream").getInstance() */
|
||||
EventEmitter.call(this);
|
||||
}
|
||||
|
||||
util.inherits(MyOtherStream, EventEmitter);
|
||||
|
||||
MyOtherStream.prototype.write = function (data) { /* use (instance (member MyOtherStream (member exports (module classes)))) */
|
||||
MyOtherStream.prototype.write = function (data) { /* use=moduleImport("classes").getMember("exports").getMember("MyOtherStream").getInstance() */
|
||||
this.emit('data', data);
|
||||
return this;
|
||||
};
|
||||
|
||||
MyOtherStream.prototype.instanceProp = 1; /* def (member instanceProp (instance (member MyOtherStream (member exports (module classes))))) */
|
||||
MyOtherStream.prototype.instanceProp = 1; /* def=moduleImport("classes").getMember("exports").getMember("MyOtherStream").getInstance().getMember("instanceProp") */
|
||||
|
||||
MyOtherStream.classProp = 1; /* def (member classProp (member MyOtherStream (member exports (module classes)))) */
|
||||
MyOtherStream.classProp = 1; /* def=moduleImport("classes").getMember("exports").getMember("MyOtherStream").getMember("classProp") */
|
||||
|
||||
module.exports.MyOtherStream = MyOtherStream;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export class A {
|
||||
constructor(x) { /* use (parameter 0 (member A (member exports (module ctor-arg)))) */
|
||||
constructor(x) { /* use=moduleImport("ctor-arg").getMember("exports").getMember("A").getParameter(0) */
|
||||
console.log(x);
|
||||
}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
module.exports = CustomEntryPoint.foo; /* use (member foo (CustomEntryPoint)) */
|
||||
module.exports = CustomEntryPoint.foo; /* use=getASuccessor(Label::entryPoint("CustomEntryPoint")) */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const foo = require("foo");
|
||||
|
||||
while(foo)
|
||||
foo = foo.foo; /* use (member foo (member exports (module foo))) */ /* use (member foo (member foo (member exports (module foo)))) */
|
||||
foo = foo.foo; /* use=moduleImport("foo").getMember("exports").getMember("foo") */ /* use=moduleImport("foo").getMember("exports").getMember("foo").getMember("foo") */
|
||||
|
||||
@@ -2,4 +2,4 @@ const MyStream = require('classes').MyStream;
|
||||
|
||||
var s = new MyStream();
|
||||
for (let m of ["write"])
|
||||
s[m]("Hello, world!"); /* use (member * (instance (member MyStream (member exports (module classes))))) */
|
||||
s[m]("Hello, world!"); /* use=moduleImport("classes").getMember("exports").getMember("MyStream").getInstance().getUnknownMember() */
|
||||
@@ -1,3 +1,3 @@
|
||||
anotherUnknownFunction().foo = 42; /* !def (member foo (member exports (module imprecise-export))) */
|
||||
anotherUnknownFunction().foo = 42; /* MISSING: def=moduleExport("imprecise-export").getMember("exports").getMember("foo") */
|
||||
|
||||
module.exports = unknownFunction();
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
const http = require('http');
|
||||
let req = http.get(url, cb);
|
||||
req.on('connect', (
|
||||
req, /* use (parameter 0 (parameter 1 (member on (return (member get (member exports (module http))))))) */
|
||||
req, /* use=moduleImport("http").getMember("exports").getMember("get").getReturn().getMember("on").getParameter(1).getParameter(0) */
|
||||
clientSocket, head) => { /* ... */ });
|
||||
req.on('information', (
|
||||
info /* use (parameter 0 (parameter 1 (member on (return (member get (member exports (module http))))))) */
|
||||
info /* use=moduleImport("http").getMember("exports").getMember("get").getReturn().getMember("on").getParameter(1).getParameter(0) */
|
||||
) => { /* ... */ });
|
||||
|
||||
req.on('connect', () => { }) /* def (parameter 0 (member on (return (member get (member exports (module http)))))) */
|
||||
.on('information', () => { }) /* def (parameter 0 (member on (return (member on (return (member get (member exports (module http)))))))) */;
|
||||
req.on('connect', () => { }) /* def=moduleImport("http").getMember("exports").getMember("get").getReturn().getMember("on").getParameter(0) */
|
||||
.on('information', () => { }) /* def=moduleImport("http").getMember("exports").getMember("get").getReturn().getMember("on").getReturn().getMember("on").getParameter(0) */;
|
||||
@@ -1,2 +1,2 @@
|
||||
import foo from "@myorg/myotherpkg";
|
||||
foo(); /* use (member default (member exports (module @myorg/myotherpkg))) */
|
||||
foo(); /* use=moduleImport("@myorg/myotherpkg").getMember("exports").getMember("default") */
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module.exports.foo = function (x) { /* use (parameter 0 (member foo (member exports (module nested-property-export)))) */
|
||||
module.exports.foo = function (x) { /* use=moduleImport("nested-property-export").getMember("exports").getMember("foo").getParameter(0) */
|
||||
return x;
|
||||
};
|
||||
|
||||
module.exports.foo.bar = function (y) { /* use (parameter 0 (member bar (member foo (member exports (module nested-property-export))))) */
|
||||
module.exports.foo.bar = function (y) { /* use=moduleImport("nested-property-export").getMember("exports").getMember("foo").getMember("bar").getParameter(0) */
|
||||
return y;
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ const express = require('express');
|
||||
|
||||
var app1 = new express();
|
||||
app1.get('/',
|
||||
(req, res) => res.send('Hello World!') /* def (parameter 1 (member get (instance (member exports (module express))))) */
|
||||
(req, res) => res.send('Hello World!') /* def=moduleImport("express").getMember("exports").getInstance().getMember("get").getParameter(1) */
|
||||
);
|
||||
|
||||
function makeApp() {
|
||||
@@ -11,5 +11,5 @@ function makeApp() {
|
||||
|
||||
var app2 = makeApp();
|
||||
app2.get('/',
|
||||
(req, res) => res.send('Hello World!') /* def (parameter 1 (member get (instance (member exports (module express))))) */
|
||||
(req, res) => res.send('Hello World!') /* def=moduleImport("express").getMember("exports").getInstance().getMember("get").getParameter(1) */
|
||||
);
|
||||
@@ -2,7 +2,7 @@ const cp = require('child_process');
|
||||
|
||||
module.exports = function () {
|
||||
return cp.spawn.bind(
|
||||
cp, // def (receiver (member spawn (member exports (module child_process))))
|
||||
"cat" // def (parameter 0 (member spawn (member exports (module child_process))))
|
||||
cp, // def=moduleImport("child_process").getMember("exports").getMember("spawn").getReceiver()
|
||||
"cat" // def=moduleImport("child_process").getMember("exports").getMember("spawn").getParameter(0)
|
||||
);
|
||||
};
|
||||
@@ -8,14 +8,14 @@ module.exports.readFile = function (f) {
|
||||
if (err)
|
||||
rej(err);
|
||||
else
|
||||
res(data); /* def (promised (return (member readFile (member exports (module promises))))) */
|
||||
res(data); /* def=moduleImport("promises").getMember("exports").getMember("readFile").getReturn().getPromised() */
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.readFileAndEncode = function (f) {
|
||||
return fse.readFile(f)
|
||||
.then((data) => /* use (promised (return (member readFile (member exports (module fs-extra))))) */
|
||||
base64.encode(data) /* def (promised (return (member readFileAndEncode (member exports (module promises))))) */
|
||||
.then((data) => /* use=moduleImport("fs-extra").getMember("exports").getMember("readFile").getReturn().getPromised() */
|
||||
base64.encode(data) /* def=moduleImport("promises").getMember("exports").getMember("readFileAndEncode").getReturn().getPromised() */
|
||||
);
|
||||
};
|
||||
@@ -4,25 +4,25 @@ var readFile = require("fs").readFile;
|
||||
var readFileAsync = bluebird.promisify(readFile);
|
||||
|
||||
readFile(
|
||||
"tst.txt", // def (parameter 0 (member readFile (member exports (module fs))))
|
||||
"utf8", // def (parameter 1 (member readFile (member exports (module fs))))
|
||||
"tst.txt", // def=moduleImport("fs").getMember("exports").getMember("readFile").getParameter(0)
|
||||
"utf8", // def=moduleImport("fs").getMember("exports").getMember("readFile").getParameter(1)
|
||||
function (
|
||||
err, // use (parameter 0 (parameter 2 (member readFile (member exports (module fs)))))
|
||||
contents // use (parameter 1 (parameter 2 (member readFile (member exports (module fs)))))
|
||||
err, // use=moduleImport("fs").getMember("exports").getMember("readFile").getParameter(2).getParameter(0)
|
||||
contents // use=moduleImport("fs").getMember("exports").getMember("readFile").getParameter(2).getParameter(1)
|
||||
) { });
|
||||
|
||||
readFileAsync(
|
||||
"tst.txt" // def (parameter 0 (member readFile (member exports (module fs))))
|
||||
"tst.txt" // def=moduleImport("fs").getMember("exports").getMember("readFile").getParameter(0)
|
||||
).then(
|
||||
function (buf) { } // use (parameter 1 (parameter 1 (member readFile (member exports (module fs)))))
|
||||
function (buf) { } // use=moduleImport("fs").getMember("exports").getMember("readFile").getParameter(1).getParameter(1)
|
||||
).catch(
|
||||
function (err) { } // not yet modelled: (parameter 0 (parameter 1 (member readFile (member exports (module fs)))))
|
||||
);
|
||||
|
||||
try {
|
||||
let p = readFileAsync(
|
||||
"tst.txt", // def (parameter 0 (member readFile (member exports (module fs))))
|
||||
"utf8" // def (parameter 1 (member readFile (member exports (module fs))))
|
||||
"tst.txt", // def=moduleImport("fs").getMember("exports").getMember("readFile").getParameter(0)
|
||||
"utf8" // def=moduleImport("fs").getMember("exports").getMember("readFile").getParameter(1)
|
||||
);
|
||||
let data = await p; // use (parameter 1 (parameter 2 (member readFile (member exports (module fs)))))
|
||||
let data = await p; // use=moduleImport("fs").getMember("exports").getMember("readFile").getParameter(2).getParameter(1)
|
||||
} catch (e) { } // not yet modelled: (parameter 0 (parameter 2 (member readFile (member exports (module fs)))))
|
||||
|
||||
@@ -5,5 +5,5 @@ exports.assertNotNull = function (x) {
|
||||
|
||||
exports.foo = function(x) {
|
||||
exports.assertNotNull(x);
|
||||
sink(x.f); /* !use (member f (parameter 0 (member assertNotNull (member exports (module property-read-from-argument))))) */ /* use (member f (parameter 0 (member foo (member exports (module property-read-from-argument))))) */
|
||||
sink(x.f); /* MISSING: use=moduleImport("property-read-from-argument").getMember("exports").getMember("assertNotNull").getParameter(0).getMember("f") */ /* use=moduleImport("property-read-from-argument").getMember("exports").getMember("foo").getParameter(0).getMember("f") */
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module.exports = function () {
|
||||
return 42; /* def (return (member impl (member exports (module reexport)))) */
|
||||
return 42; /* def=moduleImport("reexport").getMember("exports").getMember("impl").getReturn() */
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function foo(x) { /* use (parameter 0 (member bar (member other (member exports (module reexport)))) */
|
||||
function foo(x) { /* use=moduleImport("reexport").getMember("exports").getMember("other").getMember("bar").getParameter(0) */
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module.exports.id = function id(x) { /* use (parameter 0 (member id (member util (member exports (module reexport)))) */
|
||||
module.exports.id = function id(x) { /* use=moduleImport("reexport").getMember("exports").getMember("util").getMember("id").getParameter(0) */
|
||||
return x;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
id: function id(x) { /* use (parameter 0 (member id (member util2 (member exports (module reexport)))) */
|
||||
id: function id(x) { /* use=moduleImport("reexport").getMember("exports").getMember("util2").getMember("id").getParameter(0) */
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export class A {
|
||||
foo() {
|
||||
return this; /* def (return (member foo (instance (member A (member exports (module return-self)))))) */
|
||||
return this; /* def=moduleImport("return-self").getMember("exports").getMember("A").getInstance().getMember("foo").getReturn() */
|
||||
}
|
||||
bar(x) { } /* use (parameter 0 (member bar (instance (member A (member exports (module return-self)))))) */
|
||||
bar(x) { } /* use=moduleImport("return-self").getMember("exports").getMember("A").getInstance().getMember("bar").getParameter(0) */
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ const lib = require('something');
|
||||
|
||||
function f() {
|
||||
return {
|
||||
x: new Object() /* def (member x (parameter 0 (member m1 (member exports (module something))))) */
|
||||
x: new Object() /* def=moduleImport("something").getMember("exports").getMember("m1").getParameter(0).getMember("x") */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ app.use(bodyParser.json());
|
||||
|
||||
app.post("/find", (req, res) => {
|
||||
let v = JSON.parse(req.body.x);
|
||||
getCollection().find({ id: v }); /* use (member find (instance (member Collection (member exports (module mongodb))))) */
|
||||
getCollection().find({ id: v }); // use=moduleImport("mongodb").getMember("exports").getMember("Collection").getInstance().getMember("find")
|
||||
});
|
||||
|
||||
import * as mongoose from "mongoose";
|
||||
@@ -19,14 +19,14 @@ declare function getMongooseModel(): mongoose.Model;
|
||||
declare function getMongooseQuery(): mongoose.Query;
|
||||
app.post("/find", (req, res) => {
|
||||
let v = JSON.parse(req.body.x);
|
||||
getMongooseModel().find({ id: v }); /* def (parameter 0 (member find (instance (member Model (member exports (module mongoose)))))) */
|
||||
getMongooseQuery().find({ id: v }); /* def (parameter 0 (member find (instance (member Query (member exports (module mongoose)))))) */
|
||||
getMongooseModel().find({ id: v }); // def=moduleImport("mongoose").getMember("exports").getMember("Model").getInstance().getMember("find").getParameter(0)
|
||||
getMongooseQuery().find({ id: v }); // def=moduleImport("mongoose").getMember("exports").getMember("Query").getInstance().getMember("find").getParameter(0)
|
||||
});
|
||||
|
||||
import * as puppeteer from 'puppeteer';
|
||||
class Renderer {
|
||||
private browser: puppeteer.Browser;
|
||||
foo(): void {
|
||||
const page = this.browser.newPage(); /* use (instance (member Browser (member exports (module puppeteer)))) */
|
||||
const page = this.browser.newPage(); /* use=moduleImport("puppeteer").getMember("exports").getMember("Browser").getInstance() */
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,15 @@
|
||||
spuriousCallee
|
||||
missingCallee
|
||||
| constructor-field.ts:40:5:40:14 | f3.build() | constructor-field.ts:13:3:13:12 | build() {} | -1 |
|
||||
| constructor-field.ts:71:1:71:11 | bf3.build() | constructor-field.ts:13:3:13:12 | build() {} | -1 |
|
||||
| constructor-field.ts:40:5:40:14 | f3.build() | constructor-field.ts:13:3:13:12 | build() {} | -1 | calls |
|
||||
| constructor-field.ts:71:1:71:11 | bf3.build() | constructor-field.ts:13:3:13:12 | build() {} | -1 | calls |
|
||||
badAnnotation
|
||||
accessorCall
|
||||
| accessors.js:12:1:12:5 | obj.f | accessors.js:5:8:5:12 | () {} |
|
||||
| accessors.js:15:1:15:5 | obj.f | accessors.js:8:8:8:13 | (x) {} |
|
||||
| accessors.js:26:1:26:3 | C.f | accessors.js:19:15:19:19 | () {} |
|
||||
| accessors.js:29:1:29:3 | C.f | accessors.js:22:15:22:20 | (x) {} |
|
||||
| accessors.js:41:1:41:9 | new D().f | accessors.js:34:8:34:12 | () {} |
|
||||
| accessors.js:44:1:44:9 | new D().f | accessors.js:37:8:37:13 | (x) {} |
|
||||
| accessors.js:48:1:48:5 | obj.f | accessors.js:5:8:5:12 | () {} |
|
||||
| accessors.js:51:1:51:3 | C.f | accessors.js:19:15:19:19 | () {} |
|
||||
| accessors.js:54:1:54:9 | new D().f | accessors.js:34:8:34:12 | () {} |
|
||||
|
||||
@@ -25,16 +25,28 @@ class AnnotatedFunction extends Function {
|
||||
}
|
||||
|
||||
/** A function annotated with `calls:NAME` */
|
||||
class AnnotatedCall extends InvokeExpr {
|
||||
class AnnotatedCall extends DataFlow::Node {
|
||||
string calls;
|
||||
string kind;
|
||||
|
||||
AnnotatedCall() { calls = getAnnotation(this, "calls") }
|
||||
AnnotatedCall() {
|
||||
this instanceof DataFlow::InvokeNode and
|
||||
calls = getAnnotation(this.asExpr(), kind) and
|
||||
kind = "calls"
|
||||
or
|
||||
this instanceof DataFlow::PropRef and
|
||||
calls = getAnnotation(this.getAstNode(), kind) and
|
||||
kind = "callsAccessor"
|
||||
}
|
||||
|
||||
string getCallTargetName() { result = calls }
|
||||
|
||||
AnnotatedFunction getAnExpectedCallee() { result.getCalleeName() = getCallTargetName() }
|
||||
AnnotatedFunction getAnExpectedCallee(string kind_) {
|
||||
result.getCalleeName() = getCallTargetName() and
|
||||
kind = kind_
|
||||
}
|
||||
|
||||
int getBoundArgs() { result = getAnnotation(this, "boundArgs").toInt() }
|
||||
int getBoundArgs() { result = getAnnotation(this.getAstNode(), "boundArgs").toInt() }
|
||||
|
||||
int getBoundArgsOrMinusOne() {
|
||||
result = getBoundArgs()
|
||||
@@ -42,25 +54,33 @@ class AnnotatedCall extends InvokeExpr {
|
||||
not exists(getBoundArgs()) and
|
||||
result = -1
|
||||
}
|
||||
|
||||
string getKind() { result = kind }
|
||||
}
|
||||
|
||||
predicate callEdge(AnnotatedCall call, AnnotatedFunction target, int boundArgs) {
|
||||
FlowSteps::calls(call.flow(), target) and boundArgs = -1
|
||||
FlowSteps::calls(call, target) and boundArgs = -1
|
||||
or
|
||||
FlowSteps::callsBound(call.flow(), target, boundArgs)
|
||||
FlowSteps::callsBound(call, target, boundArgs)
|
||||
}
|
||||
|
||||
query predicate spuriousCallee(AnnotatedCall call, AnnotatedFunction target, int boundArgs) {
|
||||
query predicate spuriousCallee(
|
||||
AnnotatedCall call, AnnotatedFunction target, int boundArgs, string kind
|
||||
) {
|
||||
callEdge(call, target, boundArgs) and
|
||||
kind = call.getKind() and
|
||||
not (
|
||||
target = call.getAnExpectedCallee() and
|
||||
target = call.getAnExpectedCallee(kind) and
|
||||
boundArgs = call.getBoundArgsOrMinusOne()
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingCallee(AnnotatedCall call, AnnotatedFunction target, int boundArgs) {
|
||||
query predicate missingCallee(
|
||||
AnnotatedCall call, AnnotatedFunction target, int boundArgs, string kind
|
||||
) {
|
||||
not callEdge(call, target, boundArgs) and
|
||||
target = call.getAnExpectedCallee() and
|
||||
kind = call.getKind() and
|
||||
target = call.getAnExpectedCallee(kind) and
|
||||
boundArgs = call.getBoundArgsOrMinusOne()
|
||||
}
|
||||
|
||||
@@ -72,3 +92,7 @@ query predicate badAnnotation(string name) {
|
||||
not name = any(AnnotatedCall cl).getCallTargetName() and
|
||||
name = any(AnnotatedFunction cl).getCalleeName()
|
||||
}
|
||||
|
||||
query predicate accessorCall(DataFlow::PropRef ref, Function target) {
|
||||
FlowSteps::calls(ref, target)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import 'dummy';
|
||||
|
||||
let obj = {
|
||||
/** name:obj.f.get */
|
||||
get f() {},
|
||||
|
||||
/** name:obj.f.set */
|
||||
set f(x) {}
|
||||
};
|
||||
|
||||
/** callsAccessor:obj.f.get */
|
||||
obj.f;
|
||||
|
||||
/** callsAccessor:obj.f.set */
|
||||
obj.f = 1;
|
||||
|
||||
class C {
|
||||
/** name:C.f.get */
|
||||
static get f() {}
|
||||
|
||||
/** name:C.f.set */
|
||||
static set f(x) {}
|
||||
}
|
||||
|
||||
/** callsAccessor:C.f.get */
|
||||
C.f;
|
||||
|
||||
/** callsAccessor:C.f.set */
|
||||
C.f = 1;
|
||||
|
||||
|
||||
class D {
|
||||
/** name:D.f.get */
|
||||
get f() {}
|
||||
|
||||
/** name:D.f.set */
|
||||
set f(x) {}
|
||||
}
|
||||
|
||||
/** callsAccessor:D.f.get */
|
||||
new D().f;
|
||||
|
||||
/** callsAccessor:D.f.set */
|
||||
new D().f = 1;
|
||||
|
||||
// Avoid regular calls being seen as calls to the accessor itself
|
||||
/** calls:NONE */
|
||||
obj.f();
|
||||
|
||||
/** calls:NONE */
|
||||
C.f();
|
||||
|
||||
/** calls:NONE */
|
||||
new D().f();
|
||||
@@ -24,6 +24,7 @@ getAReceiverNode
|
||||
| tst.js:23:1:23:15 | function D() {} | tst.js:25:13:25:12 | this |
|
||||
| tst.js:23:1:23:15 | function D() {} | tst.js:26:13:26:12 | this |
|
||||
| tst.js:23:1:23:15 | function D() {} | tst.js:27:4:27:3 | this |
|
||||
| tst.js:30:1:34:1 | class S ... x) {}\\n} | tst.js:30:21:30:20 | this |
|
||||
getFieldTypeAnnotation
|
||||
| fields.ts:1:1:3:1 | class B ... mber;\\n} | baseField | fields.ts:2:16:2:21 | number |
|
||||
| fields.ts:5:1:13:1 | class F ... > {};\\n} | x | fields.ts:6:27:6:32 | number |
|
||||
@@ -53,6 +54,11 @@ instanceMethod
|
||||
| tst.js:15:1:15:15 | function B() {} | foo | tst.js:17:19:17:31 | function() {} | B |
|
||||
| tst.js:19:1:19:15 | function C() {} | bar | tst.js:21:19:21:31 | function() {} | C |
|
||||
| tst.js:23:1:23:15 | function D() {} | m | tst.js:27:4:27:8 | () {} | D |
|
||||
staticMember
|
||||
| tst.js:3:1:10:1 | class A ... () {}\\n} | staticMethod | method | tst.js:5:22:5:26 | () {} | A |
|
||||
| tst.js:30:1:34:1 | class S ... x) {}\\n} | getter | getter | tst.js:32:20:32:24 | () {} | StaticMembers |
|
||||
| tst.js:30:1:34:1 | class S ... x) {}\\n} | method | method | tst.js:31:16:31:20 | () {} | StaticMembers |
|
||||
| tst.js:30:1:34:1 | class S ... x) {}\\n} | setter | setter | tst.js:33:20:33:25 | (x) {} | StaticMembers |
|
||||
superClass
|
||||
| fields.ts:5:1:13:1 | class F ... > {};\\n} | fields.ts:1:1:3:1 | class B ... mber;\\n} | Foo | Base |
|
||||
| tst.js:13:1:13:21 | class A ... ds A {} | tst.js:3:1:10:1 | class A ... () {}\\n} | A2 | A |
|
||||
|
||||
@@ -24,6 +24,12 @@ query predicate instanceMethod(
|
||||
cls.getInstanceMethod(name) = inst and clsName = cls.getName()
|
||||
}
|
||||
|
||||
query predicate staticMember(
|
||||
DataFlow::ClassNode cls, string name, string kind, DataFlow::FunctionNode inst, string clsName
|
||||
) {
|
||||
cls.getStaticMember(name, kind) = inst and clsName = cls.getName()
|
||||
}
|
||||
|
||||
query predicate superClass(
|
||||
DataFlow::ClassNode cls, DataFlow::ClassNode sup, string clsName, string supName
|
||||
) {
|
||||
|
||||
@@ -26,3 +26,9 @@ D.prototype = {
|
||||
set setter(x) {},
|
||||
m() {}
|
||||
}
|
||||
|
||||
class StaticMembers {
|
||||
static method() {}
|
||||
static get getter() {}
|
||||
static set setter(x) {}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
underlyingTypeNode
|
||||
| foo | Bar | foo.ts:3:1:5:1 | use (instance (member Bar (member exports (module foo)))) |
|
||||
| foo | Bar | foo.ts:3:12:3:12 | use (instance (member Bar (member exports (module foo)))) |
|
||||
| foo | Bar | foo.ts:3:1:5:1 | use moduleImport("foo").getMember("exports").getMember("Bar").getInstance() |
|
||||
| foo | Bar | foo.ts:3:12:3:12 | use moduleImport("foo").getMember("exports").getMember("Bar").getInstance() |
|
||||
#select
|
||||
| foo.ts:3:12:3:12 | x | foo.Bar in unknown scope |
|
||||
| foo.ts:4:10:4:10 | x | foo.Bar in unknown scope |
|
||||
|
||||
@@ -25,208 +25,208 @@ sqlString
|
||||
| tst.js:166:43:166:63 | 'col DE ... S LAST' |
|
||||
| tst.js:178:14:178:24 | 'count > ?' |
|
||||
knexLibrary
|
||||
| file://:0:0:0:0 | use (member exports (module knex)) |
|
||||
| file://:0:0:0:0 | use moduleImport("knex").getMember("exports") |
|
||||
knexObject
|
||||
| tst.js:3:14:3:30 | use (return (member exports (module knex))) |
|
||||
| tst.js:5:1:5:32 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:5:1:9:4 | use (return (member select (return (return (member exports (module knex)))))) |
|
||||
| tst.js:5:1:10:52 | use (return (member whereRaw (return (member select (return (return (member exports (module knex)))))))) |
|
||||
| tst.js:12:1:12:48 | use (return (member withUserParams (return (member exports (module knex))))) |
|
||||
| tst.js:12:1:12:59 | use (return (member table (return (member withUserParams (return (member exports (module knex))))))) |
|
||||
| tst.js:12:1:12:71 | use (return (member select (return (member table (return (member withUserParams (return (member exports (module knex))))))))) |
|
||||
| tst.js:14:1:14:13 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:14:1:14:27 | use (return (member from (return (member select (return (member exports (module knex))))))) |
|
||||
| tst.js:14:1:14:41 | use (return (member timeout (return (member from (return (member select (return (member exports (module knex))))))))) |
|
||||
| tst.js:15:1:15:38 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:15:1:15:52 | use (return (member from (return (member select (return (member exports (module knex))))))) |
|
||||
| tst.js:17:1:17:23 | use (return (member avg (return (member exports (module knex))))) |
|
||||
| tst.js:17:1:19:4 | use (return (member from (return (member avg (return (member exports (module knex))))))) |
|
||||
| tst.js:17:1:19:24 | use (return (member as (return (member from (return (member avg (return (member exports (module knex))))))))) |
|
||||
| tst.js:17:30:17:29 | use (receiver (parameter 0 (member from (return (member avg (return (member exports (module knex)))))))) |
|
||||
| tst.js:18:5:18:38 | use (return (member sum (receiver (parameter 0 (member from (return (member avg (return (member exports (module knex)))))))))) |
|
||||
| tst.js:18:5:18:49 | use (return (member from (return (member sum (receiver (parameter 0 (member from (return (member avg (return (member exports (module knex)))))))))))) |
|
||||
| tst.js:18:5:18:68 | use (return (member groupBy (return (member from (return (member sum (receiver (parameter 0 (member from (return (member avg (return (member exports (module knex)))))))))))))) |
|
||||
| tst.js:18:5:18:77 | use (return (member as (return (member groupBy (return (member from (return (member sum (receiver (parameter 0 (member from (return (member avg (return (member exports (module knex)))))))))))))))) |
|
||||
| tst.js:21:1:21:38 | use (return (member column (return (member exports (module knex))))) |
|
||||
| tst.js:21:1:21:47 | use (return (member select (return (member column (return (member exports (module knex))))))) |
|
||||
| tst.js:21:1:21:61 | use (return (member from (return (member select (return (member column (return (member exports (module knex))))))))) |
|
||||
| tst.js:23:1:23:16 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:23:1:23:30 | use (return (member from (return (member select (return (member exports (module knex))))))) |
|
||||
| tst.js:25:1:25:85 | use (return (member with (return (member exports (module knex))))) |
|
||||
| tst.js:25:1:25:97 | use (return (member select (return (member with (return (member exports (module knex))))))) |
|
||||
| tst.js:25:1:25:116 | use (return (member from (return (member select (return (member with (return (member exports (module knex))))))))) |
|
||||
| tst.js:25:25:25:84 | use (return (member raw (return (member exports (module knex))))) |
|
||||
| tst.js:27:1:31:4 | use (return (member withRecursive (return (member exports (module knex))))) |
|
||||
| tst.js:27:1:31:16 | use (return (member select (return (member withRecursive (return (member exports (module knex))))))) |
|
||||
| tst.js:27:1:31:34 | use (return (member from (return (member select (return (member withRecursive (return (member exports (module knex))))))))) |
|
||||
| tst.js:33:1:33:25 | use (return (member withSchema (return (member exports (module knex))))) |
|
||||
| tst.js:33:1:33:37 | use (return (member select (return (member withSchema (return (member exports (module knex))))))) |
|
||||
| tst.js:33:1:33:51 | use (return (member from (return (member select (return (member withSchema (return (member exports (module knex))))))))) |
|
||||
| tst.js:35:1:35:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:35:1:38:4 | use (return (member where (return (return (member exports (module knex)))))) |
|
||||
| tst.js:35:1:38:17 | use (return (member select (return (member where (return (return (member exports (module knex)))))))) |
|
||||
| tst.js:40:1:40:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:40:1:40:28 | use (return (member where (return (return (member exports (module knex)))))) |
|
||||
| tst.js:42:1:42:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:42:1:45:3 | use (return (member where (return (return (member exports (module knex)))))) |
|
||||
| tst.js:42:1:48:4 | use (return (member andWhere (return (member where (return (return (member exports (module knex)))))))) |
|
||||
| tst.js:46:13:46:12 | use (receiver (parameter 0 (member andWhere (return (member where (return (return (member exports (module knex))))))))) |
|
||||
| tst.js:47:5:47:29 | use (return (member where (receiver (parameter 0 (member andWhere (return (member where (return (return (member exports (module knex))))))))))) |
|
||||
| tst.js:50:1:50:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:50:1:52:2 | use (return (member where (return (return (member exports (module knex)))))) |
|
||||
| tst.js:50:1:52:28 | use (return (member orWhere (return (member where (return (return (member exports (module knex)))))))) |
|
||||
| tst.js:50:21:50:20 | use (receiver (parameter 0 (member where (return (return (member exports (module knex))))))) |
|
||||
| tst.js:51:3:51:21 | use (return (member where (receiver (parameter 0 (member where (return (return (member exports (module knex))))))))) |
|
||||
| tst.js:51:3:51:44 | use (return (member orWhere (return (member where (receiver (parameter 0 (member where (return (return (member exports (module knex))))))))))) |
|
||||
| tst.js:54:1:54:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:54:1:54:56 | use (return (member where (return (return (member exports (module knex)))))) |
|
||||
| tst.js:56:1:56:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:56:1:56:38 | use (return (member where (return (return (member exports (module knex)))))) |
|
||||
| tst.js:58:18:58:30 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:58:18:58:55 | use (return (member where (return (return (member exports (module knex)))))) |
|
||||
| tst.js:58:18:58:84 | use (return (member andWhere (return (member where (return (return (member exports (module knex)))))))) |
|
||||
| tst.js:58:18:58:108 | use (return (member orWhere (return (member andWhere (return (member where (return (return (member exports (module knex)))))))))) |
|
||||
| tst.js:58:18:58:121 | use (return (member select (return (member orWhere (return (member andWhere (return (member where (return (return (member exports (module knex)))))))))))) |
|
||||
| tst.js:59:1:59:16 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:59:1:59:44 | use (return (member where (return (return (member exports (module knex)))))) |
|
||||
| tst.js:61:1:61:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:61:1:61:28 | use (return (member where (return (return (member exports (module knex)))))) |
|
||||
| tst.js:61:1:61:64 | use (return (member orWhere (return (member where (return (return (member exports (module knex)))))))) |
|
||||
| tst.js:63:1:63:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:63:1:66:2 | use (return (member whereNot (return (return (member exports (module knex)))))) |
|
||||
| tst.js:63:1:66:15 | use (return (member select (return (member whereNot (return (return (member exports (module knex)))))))) |
|
||||
| tst.js:68:1:68:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:68:1:68:31 | use (return (member whereNot (return (return (member exports (module knex)))))) |
|
||||
| tst.js:70:1:70:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:70:1:72:2 | use (return (member whereNot (return (return (member exports (module knex)))))) |
|
||||
| tst.js:70:1:72:31 | use (return (member orWhereNot (return (member whereNot (return (return (member exports (module knex)))))))) |
|
||||
| tst.js:70:24:70:23 | use (receiver (parameter 0 (member whereNot (return (return (member exports (module knex))))))) |
|
||||
| tst.js:71:3:71:21 | use (return (member where (receiver (parameter 0 (member whereNot (return (return (member exports (module knex))))))))) |
|
||||
| tst.js:71:3:71:47 | use (return (member orWhereNot (return (member where (receiver (parameter 0 (member whereNot (return (return (member exports (module knex))))))))))) |
|
||||
| tst.js:74:19:74:31 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:74:19:75:30 | use (return (member whereNot (return (return (member exports (module knex)))))) |
|
||||
| tst.js:74:19:76:31 | use (return (member andWhere (return (member whereNot (return (return (member exports (module knex)))))))) |
|
||||
| tst.js:74:19:77:26 | use (return (member orWhere (return (member andWhere (return (member whereNot (return (return (member exports (module knex)))))))))) |
|
||||
| tst.js:74:19:78:15 | use (return (member select (return (member orWhere (return (member andWhere (return (member whereNot (return (return (member exports (module knex)))))))))))) |
|
||||
| tst.js:80:1:80:16 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:80:1:80:49 | use (return (member where (return (return (member exports (module knex)))))) |
|
||||
| tst.js:82:1:82:19 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:82:1:82:33 | use (return (member from (return (member select (return (member exports (module knex))))))) |
|
||||
| tst.js:82:1:83:27 | use (return (member whereIn (return (member from (return (member select (return (member exports (module knex))))))))) |
|
||||
| tst.js:82:1:84:29 | use (return (member orWhereIn (return (member whereIn (return (member from (return (member select (return (member exports (module knex))))))))))) |
|
||||
| tst.js:86:1:86:19 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:86:1:86:33 | use (return (member from (return (member select (return (member exports (module knex))))))) |
|
||||
| tst.js:86:1:89:4 | use (return (member whereIn (return (member from (return (member select (return (member exports (module knex))))))))) |
|
||||
| tst.js:91:1:91:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:91:1:91:41 | use (return (member whereNotIn (return (return (member exports (module knex)))))) |
|
||||
| tst.js:93:1:93:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:93:1:93:45 | use (return (member where (return (return (member exports (module knex)))))) |
|
||||
| tst.js:93:1:93:75 | use (return (member orWhereNotIn (return (member where (return (return (member exports (module knex)))))))) |
|
||||
| tst.js:95:1:95:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:95:1:95:37 | use (return (member whereNull (return (return (member exports (module knex)))))) |
|
||||
| tst.js:97:1:97:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:97:1:97:40 | use (return (member whereNotNull (return (return (member exports (module knex)))))) |
|
||||
| tst.js:99:1:99:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:99:1:101:2 | use (return (member whereExists (return (return (member exports (module knex)))))) |
|
||||
| tst.js:99:27:99:26 | use (receiver (parameter 0 (member whereExists (return (return (member exports (module knex))))))) |
|
||||
| tst.js:100:3:100:18 | use (return (member select (receiver (parameter 0 (member whereExists (return (return (member exports (module knex))))))))) |
|
||||
| tst.js:100:3:100:35 | use (return (member from (return (member select (receiver (parameter 0 (member whereExists (return (return (member exports (module knex))))))))))) |
|
||||
| tst.js:100:3:100:78 | use (return (member whereRaw (return (member from (return (member select (receiver (parameter 0 (member whereExists (return (return (member exports (module knex))))))))))))) |
|
||||
| tst.js:103:1:103:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:103:1:103:103 | use (return (member whereExists (return (return (member exports (module knex)))))) |
|
||||
| tst.js:103:27:103:42 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:103:27:103:59 | use (return (member from (return (member select (return (member exports (module knex))))))) |
|
||||
| tst.js:103:27:103:102 | use (return (member whereRaw (return (member from (return (member select (return (member exports (module knex))))))))) |
|
||||
| tst.js:105:1:105:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:105:1:107:2 | use (return (member whereNotExists (return (return (member exports (module knex)))))) |
|
||||
| tst.js:105:30:105:29 | use (receiver (parameter 0 (member whereNotExists (return (return (member exports (module knex))))))) |
|
||||
| tst.js:106:3:106:18 | use (return (member select (receiver (parameter 0 (member whereNotExists (return (return (member exports (module knex))))))))) |
|
||||
| tst.js:106:3:106:35 | use (return (member from (return (member select (receiver (parameter 0 (member whereNotExists (return (return (member exports (module knex))))))))))) |
|
||||
| tst.js:106:3:106:78 | use (return (member whereRaw (return (member from (return (member select (receiver (parameter 0 (member whereNotExists (return (return (member exports (module knex))))))))))))) |
|
||||
| tst.js:109:1:109:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:109:1:109:45 | use (return (member whereBetween (return (return (member exports (module knex)))))) |
|
||||
| tst.js:111:1:111:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:111:1:111:48 | use (return (member whereNotBetween (return (return (member exports (module knex)))))) |
|
||||
| tst.js:113:1:113:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:113:1:113:37 | use (return (member whereRaw (return (return (member exports (module knex)))))) |
|
||||
| tst.js:115:1:115:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:115:1:116:56 | use (return (member join (return (return (member exports (module knex)))))) |
|
||||
| tst.js:115:1:117:39 | use (return (member select (return (member join (return (return (member exports (module knex)))))))) |
|
||||
| tst.js:119:1:119:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:119:1:120:51 | use (return (member join (return (return (member exports (module knex)))))) |
|
||||
| tst.js:119:1:121:39 | use (return (member select (return (member join (return (return (member exports (module knex)))))))) |
|
||||
| tst.js:123:1:123:16 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:123:1:123:30 | use (return (member from (return (member select (return (member exports (module knex))))))) |
|
||||
| tst.js:123:1:125:2 | use (return (member join (return (member from (return (member select (return (member exports (module knex))))))))) |
|
||||
| tst.js:127:1:127:16 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:127:1:127:30 | use (return (member from (return (member select (return (member exports (module knex))))))) |
|
||||
| tst.js:127:1:132:2 | use (return (member join (return (member from (return (member select (return (member exports (module knex))))))))) |
|
||||
| tst.js:134:1:134:16 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:134:1:134:30 | use (return (member from (return (member select (return (member exports (module knex))))))) |
|
||||
| tst.js:134:1:134:90 | use (return (member join (return (member from (return (member select (return (member exports (module knex))))))))) |
|
||||
| tst.js:134:66:134:89 | use (return (member raw (return (member exports (module knex))))) |
|
||||
| tst.js:136:1:136:18 | use (return (member from (return (member exports (module knex))))) |
|
||||
| tst.js:136:1:136:72 | use (return (member innerJoin (return (member from (return (member exports (module knex))))))) |
|
||||
| tst.js:138:1:138:16 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:138:1:138:30 | use (return (member from (return (member select (return (member exports (module knex))))))) |
|
||||
| tst.js:138:1:138:83 | use (return (member leftJoin (return (member from (return (member select (return (member exports (module knex))))))))) |
|
||||
| tst.js:140:1:140:16 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:140:1:140:30 | use (return (member from (return (member select (return (member exports (module knex))))))) |
|
||||
| tst.js:140:1:140:88 | use (return (member leftOuterJoin (return (member from (return (member select (return (member exports (module knex))))))))) |
|
||||
| tst.js:142:1:142:16 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:142:1:142:30 | use (return (member from (return (member select (return (member exports (module knex))))))) |
|
||||
| tst.js:142:1:142:84 | use (return (member rightJoin (return (member from (return (member select (return (member exports (module knex))))))))) |
|
||||
| tst.js:144:1:144:16 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:144:1:144:30 | use (return (member from (return (member select (return (member exports (module knex))))))) |
|
||||
| tst.js:144:1:144:89 | use (return (member rightOuterJoin (return (member from (return (member select (return (member exports (module knex))))))))) |
|
||||
| tst.js:146:1:146:16 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:146:1:146:30 | use (return (member from (return (member select (return (member exports (module knex))))))) |
|
||||
| tst.js:146:1:146:88 | use (return (member fullOuterJoin (return (member from (return (member select (return (member exports (module knex))))))))) |
|
||||
| tst.js:148:1:148:16 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:148:1:148:30 | use (return (member from (return (member select (return (member exports (module knex))))))) |
|
||||
| tst.js:148:1:148:52 | use (return (member crossJoin (return (member from (return (member select (return (member exports (module knex))))))))) |
|
||||
| tst.js:150:1:150:16 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:150:1:150:33 | use (return (member from (return (member select (return (member exports (module knex))))))) |
|
||||
| tst.js:150:1:150:69 | use (return (member joinRaw (return (member from (return (member select (return (member exports (module knex))))))))) |
|
||||
| tst.js:150:1:150:84 | use (return (member where (return (member joinRaw (return (member from (return (member select (return (member exports (module knex))))))))))) |
|
||||
| tst.js:152:1:152:16 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:152:1:152:30 | use (return (member from (return (member select (return (member exports (module knex))))))) |
|
||||
| tst.js:152:1:154:2 | use (return (member join (return (member from (return (member select (return (member exports (module knex))))))))) |
|
||||
| tst.js:156:1:156:28 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:156:1:156:42 | use (return (member from (return (member select (return (member exports (module knex))))))) |
|
||||
| tst.js:156:1:156:63 | use (return (member where (return (member from (return (member select (return (member exports (module knex))))))))) |
|
||||
| tst.js:156:1:156:79 | use (return (member clear (return (member where (return (member from (return (member select (return (member exports (module knex))))))))))) |
|
||||
| tst.js:156:1:156:94 | use (return (member clear (return (member clear (return (member where (return (member from (return (member select (return (member exports (module knex))))))))))))) |
|
||||
| tst.js:158:1:158:17 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:158:1:158:53 | use (return (member distinct (return (return (member exports (module knex)))))) |
|
||||
| tst.js:160:1:160:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:160:1:160:31 | use (return (member distinctOn (return (return (member exports (module knex)))))) |
|
||||
| tst.js:162:1:162:44 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:162:1:162:58 | use (return (member from (return (member select (return (member exports (module knex))))))) |
|
||||
| tst.js:162:1:162:89 | use (return (member groupByRaw (return (member from (return (member select (return (member exports (module knex))))))))) |
|
||||
| tst.js:162:21:162:43 | use (return (member raw (return (member exports (module knex))))) |
|
||||
| tst.js:164:1:164:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:164:1:164:30 | use (return (member orderBy (return (return (member exports (module knex)))))) |
|
||||
| tst.js:166:1:166:16 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:166:1:166:30 | use (return (member from (return (member select (return (member exports (module knex))))))) |
|
||||
| tst.js:166:1:166:64 | use (return (member orderByRaw (return (member from (return (member select (return (member exports (module knex))))))))) |
|
||||
| tst.js:168:1:168:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:168:1:169:19 | use (return (member groupBy (return (return (member exports (module knex)))))) |
|
||||
| tst.js:168:1:170:26 | use (return (member orderBy (return (member groupBy (return (return (member exports (module knex)))))))) |
|
||||
| tst.js:168:1:171:28 | use (return (member having (return (member orderBy (return (member groupBy (return (return (member exports (module knex)))))))))) |
|
||||
| tst.js:173:1:173:16 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:173:1:173:30 | use (return (member from (return (member select (return (member exports (module knex))))))) |
|
||||
| tst.js:173:1:173:61 | use (return (member havingIn (return (member from (return (member select (return (member exports (module knex))))))))) |
|
||||
| tst.js:175:1:175:13 | use (return (return (member exports (module knex)))) |
|
||||
| tst.js:175:1:176:19 | use (return (member groupBy (return (return (member exports (module knex)))))) |
|
||||
| tst.js:175:1:177:26 | use (return (member orderBy (return (member groupBy (return (return (member exports (module knex)))))))) |
|
||||
| tst.js:175:1:178:32 | use (return (member havingRaw (return (member orderBy (return (member groupBy (return (return (member exports (module knex)))))))))) |
|
||||
| tst.js:180:1:180:16 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:181:1:181:16 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:182:1:182:16 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:183:1:183:16 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:184:1:184:16 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:185:1:185:16 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:186:1:186:16 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:187:1:187:16 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:188:1:188:16 | use (return (member select (return (member exports (module knex))))) |
|
||||
| tst.js:3:14:3:30 | use moduleImport("knex").getMember("exports").getReturn() |
|
||||
| tst.js:5:1:5:32 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:5:1:9:4 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("select").getReturn() |
|
||||
| tst.js:5:1:10:52 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("select").getReturn().getMember("whereRaw").getReturn() |
|
||||
| tst.js:12:1:12:48 | use moduleImport("knex").getMember("exports").getReturn().getMember("withUserParams").getReturn() |
|
||||
| tst.js:12:1:12:59 | use moduleImport("knex").getMember("exports").getReturn().getMember("withUserParams").getReturn().getMember("table").getReturn() |
|
||||
| tst.js:12:1:12:71 | use moduleImport("knex").getMember("exports").getReturn().getMember("withUserParams").getReturn().getMember("table").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:14:1:14:13 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:14:1:14:27 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:14:1:14:41 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn().getMember("timeout").getReturn() |
|
||||
| tst.js:15:1:15:38 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:15:1:15:52 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:17:1:17:23 | use moduleImport("knex").getMember("exports").getReturn().getMember("avg").getReturn() |
|
||||
| tst.js:17:1:19:4 | use moduleImport("knex").getMember("exports").getReturn().getMember("avg").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:17:1:19:24 | use moduleImport("knex").getMember("exports").getReturn().getMember("avg").getReturn().getMember("from").getReturn().getMember("as").getReturn() |
|
||||
| tst.js:17:30:17:29 | use moduleImport("knex").getMember("exports").getReturn().getMember("avg").getReturn().getMember("from").getParameter(0).getReceiver() |
|
||||
| tst.js:18:5:18:38 | use moduleImport("knex").getMember("exports").getReturn().getMember("avg").getReturn().getMember("from").getParameter(0).getReceiver().getMember("sum").getReturn() |
|
||||
| tst.js:18:5:18:49 | use moduleImport("knex").getMember("exports").getReturn().getMember("avg").getReturn().getMember("from").getParameter(0).getReceiver().getMember("sum").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:18:5:18:68 | use moduleImport("knex").getMember("exports").getReturn().getMember("avg").getReturn().getMember("from").getParameter(0).getReceiver().getMember("sum").getReturn().getMember("from").getReturn().getMember("groupBy").getReturn() |
|
||||
| tst.js:18:5:18:77 | use moduleImport("knex").getMember("exports").getReturn().getMember("avg").getReturn().getMember("from").getParameter(0).getReceiver().getMember("sum").getReturn().getMember("from").getReturn().getMember("groupBy").getReturn().getMember("as").getReturn() |
|
||||
| tst.js:21:1:21:38 | use moduleImport("knex").getMember("exports").getReturn().getMember("column").getReturn() |
|
||||
| tst.js:21:1:21:47 | use moduleImport("knex").getMember("exports").getReturn().getMember("column").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:21:1:21:61 | use moduleImport("knex").getMember("exports").getReturn().getMember("column").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:23:1:23:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:23:1:23:30 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:25:1:25:85 | use moduleImport("knex").getMember("exports").getReturn().getMember("with").getReturn() |
|
||||
| tst.js:25:1:25:97 | use moduleImport("knex").getMember("exports").getReturn().getMember("with").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:25:1:25:116 | use moduleImport("knex").getMember("exports").getReturn().getMember("with").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:25:25:25:84 | use moduleImport("knex").getMember("exports").getReturn().getMember("raw").getReturn() |
|
||||
| tst.js:27:1:31:4 | use moduleImport("knex").getMember("exports").getReturn().getMember("withRecursive").getReturn() |
|
||||
| tst.js:27:1:31:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("withRecursive").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:27:1:31:34 | use moduleImport("knex").getMember("exports").getReturn().getMember("withRecursive").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:33:1:33:25 | use moduleImport("knex").getMember("exports").getReturn().getMember("withSchema").getReturn() |
|
||||
| tst.js:33:1:33:37 | use moduleImport("knex").getMember("exports").getReturn().getMember("withSchema").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:33:1:33:51 | use moduleImport("knex").getMember("exports").getReturn().getMember("withSchema").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:35:1:35:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:35:1:38:4 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getReturn() |
|
||||
| tst.js:35:1:38:17 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:40:1:40:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:40:1:40:28 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getReturn() |
|
||||
| tst.js:42:1:42:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:42:1:45:3 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getReturn() |
|
||||
| tst.js:42:1:48:4 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getReturn().getMember("andWhere").getReturn() |
|
||||
| tst.js:46:13:46:12 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getReturn().getMember("andWhere").getParameter(0).getReceiver() |
|
||||
| tst.js:47:5:47:29 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getReturn().getMember("andWhere").getParameter(0).getReceiver().getMember("where").getReturn() |
|
||||
| tst.js:50:1:50:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:50:1:52:2 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getReturn() |
|
||||
| tst.js:50:1:52:28 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getReturn().getMember("orWhere").getReturn() |
|
||||
| tst.js:50:21:50:20 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getParameter(0).getReceiver() |
|
||||
| tst.js:51:3:51:21 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getParameter(0).getReceiver().getMember("where").getReturn() |
|
||||
| tst.js:51:3:51:44 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getParameter(0).getReceiver().getMember("where").getReturn().getMember("orWhere").getReturn() |
|
||||
| tst.js:54:1:54:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:54:1:54:56 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getReturn() |
|
||||
| tst.js:56:1:56:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:56:1:56:38 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getReturn() |
|
||||
| tst.js:58:18:58:30 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:58:18:58:55 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getReturn() |
|
||||
| tst.js:58:18:58:84 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getReturn().getMember("andWhere").getReturn() |
|
||||
| tst.js:58:18:58:108 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getReturn().getMember("andWhere").getReturn().getMember("orWhere").getReturn() |
|
||||
| tst.js:58:18:58:121 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getReturn().getMember("andWhere").getReturn().getMember("orWhere").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:59:1:59:16 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:59:1:59:44 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getReturn() |
|
||||
| tst.js:61:1:61:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:61:1:61:28 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getReturn() |
|
||||
| tst.js:61:1:61:64 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getReturn().getMember("orWhere").getReturn() |
|
||||
| tst.js:63:1:63:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:63:1:66:2 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereNot").getReturn() |
|
||||
| tst.js:63:1:66:15 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereNot").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:68:1:68:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:68:1:68:31 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereNot").getReturn() |
|
||||
| tst.js:70:1:70:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:70:1:72:2 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereNot").getReturn() |
|
||||
| tst.js:70:1:72:31 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereNot").getReturn().getMember("orWhereNot").getReturn() |
|
||||
| tst.js:70:24:70:23 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereNot").getParameter(0).getReceiver() |
|
||||
| tst.js:71:3:71:21 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereNot").getParameter(0).getReceiver().getMember("where").getReturn() |
|
||||
| tst.js:71:3:71:47 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereNot").getParameter(0).getReceiver().getMember("where").getReturn().getMember("orWhereNot").getReturn() |
|
||||
| tst.js:74:19:74:31 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:74:19:75:30 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereNot").getReturn() |
|
||||
| tst.js:74:19:76:31 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereNot").getReturn().getMember("andWhere").getReturn() |
|
||||
| tst.js:74:19:77:26 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereNot").getReturn().getMember("andWhere").getReturn().getMember("orWhere").getReturn() |
|
||||
| tst.js:74:19:78:15 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereNot").getReturn().getMember("andWhere").getReturn().getMember("orWhere").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:80:1:80:16 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:80:1:80:49 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getReturn() |
|
||||
| tst.js:82:1:82:19 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:82:1:82:33 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:82:1:83:27 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn().getMember("whereIn").getReturn() |
|
||||
| tst.js:82:1:84:29 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn().getMember("whereIn").getReturn().getMember("orWhereIn").getReturn() |
|
||||
| tst.js:86:1:86:19 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:86:1:86:33 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:86:1:89:4 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn().getMember("whereIn").getReturn() |
|
||||
| tst.js:91:1:91:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:91:1:91:41 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereNotIn").getReturn() |
|
||||
| tst.js:93:1:93:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:93:1:93:45 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getReturn() |
|
||||
| tst.js:93:1:93:75 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("where").getReturn().getMember("orWhereNotIn").getReturn() |
|
||||
| tst.js:95:1:95:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:95:1:95:37 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereNull").getReturn() |
|
||||
| tst.js:97:1:97:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:97:1:97:40 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereNotNull").getReturn() |
|
||||
| tst.js:99:1:99:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:99:1:101:2 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereExists").getReturn() |
|
||||
| tst.js:99:27:99:26 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereExists").getParameter(0).getReceiver() |
|
||||
| tst.js:100:3:100:18 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereExists").getParameter(0).getReceiver().getMember("select").getReturn() |
|
||||
| tst.js:100:3:100:35 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereExists").getParameter(0).getReceiver().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:100:3:100:78 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereExists").getParameter(0).getReceiver().getMember("select").getReturn().getMember("from").getReturn().getMember("whereRaw").getReturn() |
|
||||
| tst.js:103:1:103:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:103:1:103:103 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereExists").getReturn() |
|
||||
| tst.js:103:27:103:42 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:103:27:103:59 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:103:27:103:102 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn().getMember("whereRaw").getReturn() |
|
||||
| tst.js:105:1:105:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:105:1:107:2 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereNotExists").getReturn() |
|
||||
| tst.js:105:30:105:29 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereNotExists").getParameter(0).getReceiver() |
|
||||
| tst.js:106:3:106:18 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereNotExists").getParameter(0).getReceiver().getMember("select").getReturn() |
|
||||
| tst.js:106:3:106:35 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereNotExists").getParameter(0).getReceiver().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:106:3:106:78 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereNotExists").getParameter(0).getReceiver().getMember("select").getReturn().getMember("from").getReturn().getMember("whereRaw").getReturn() |
|
||||
| tst.js:109:1:109:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:109:1:109:45 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereBetween").getReturn() |
|
||||
| tst.js:111:1:111:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:111:1:111:48 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereNotBetween").getReturn() |
|
||||
| tst.js:113:1:113:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:113:1:113:37 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("whereRaw").getReturn() |
|
||||
| tst.js:115:1:115:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:115:1:116:56 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("join").getReturn() |
|
||||
| tst.js:115:1:117:39 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("join").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:119:1:119:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:119:1:120:51 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("join").getReturn() |
|
||||
| tst.js:119:1:121:39 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("join").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:123:1:123:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:123:1:123:30 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:123:1:125:2 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn().getMember("join").getReturn() |
|
||||
| tst.js:127:1:127:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:127:1:127:30 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:127:1:132:2 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn().getMember("join").getReturn() |
|
||||
| tst.js:134:1:134:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:134:1:134:30 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:134:1:134:90 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn().getMember("join").getReturn() |
|
||||
| tst.js:134:66:134:89 | use moduleImport("knex").getMember("exports").getReturn().getMember("raw").getReturn() |
|
||||
| tst.js:136:1:136:18 | use moduleImport("knex").getMember("exports").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:136:1:136:72 | use moduleImport("knex").getMember("exports").getReturn().getMember("from").getReturn().getMember("innerJoin").getReturn() |
|
||||
| tst.js:138:1:138:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:138:1:138:30 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:138:1:138:83 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn().getMember("leftJoin").getReturn() |
|
||||
| tst.js:140:1:140:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:140:1:140:30 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:140:1:140:88 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn().getMember("leftOuterJoin").getReturn() |
|
||||
| tst.js:142:1:142:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:142:1:142:30 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:142:1:142:84 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn().getMember("rightJoin").getReturn() |
|
||||
| tst.js:144:1:144:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:144:1:144:30 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:144:1:144:89 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn().getMember("rightOuterJoin").getReturn() |
|
||||
| tst.js:146:1:146:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:146:1:146:30 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:146:1:146:88 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn().getMember("fullOuterJoin").getReturn() |
|
||||
| tst.js:148:1:148:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:148:1:148:30 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:148:1:148:52 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn().getMember("crossJoin").getReturn() |
|
||||
| tst.js:150:1:150:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:150:1:150:33 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:150:1:150:69 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn().getMember("joinRaw").getReturn() |
|
||||
| tst.js:150:1:150:84 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn().getMember("joinRaw").getReturn().getMember("where").getReturn() |
|
||||
| tst.js:152:1:152:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:152:1:152:30 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:152:1:154:2 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn().getMember("join").getReturn() |
|
||||
| tst.js:156:1:156:28 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:156:1:156:42 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:156:1:156:63 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn().getMember("where").getReturn() |
|
||||
| tst.js:156:1:156:79 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn().getMember("where").getReturn().getMember("clear").getReturn() |
|
||||
| tst.js:156:1:156:94 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn().getMember("where").getReturn().getMember("clear").getReturn().getMember("clear").getReturn() |
|
||||
| tst.js:158:1:158:17 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:158:1:158:53 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("distinct").getReturn() |
|
||||
| tst.js:160:1:160:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:160:1:160:31 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("distinctOn").getReturn() |
|
||||
| tst.js:162:1:162:44 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:162:1:162:58 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:162:1:162:89 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn().getMember("groupByRaw").getReturn() |
|
||||
| tst.js:162:21:162:43 | use moduleImport("knex").getMember("exports").getReturn().getMember("raw").getReturn() |
|
||||
| tst.js:164:1:164:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:164:1:164:30 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("orderBy").getReturn() |
|
||||
| tst.js:166:1:166:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:166:1:166:30 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:166:1:166:64 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn().getMember("orderByRaw").getReturn() |
|
||||
| tst.js:168:1:168:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:168:1:169:19 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("groupBy").getReturn() |
|
||||
| tst.js:168:1:170:26 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("groupBy").getReturn().getMember("orderBy").getReturn() |
|
||||
| tst.js:168:1:171:28 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("groupBy").getReturn().getMember("orderBy").getReturn().getMember("having").getReturn() |
|
||||
| tst.js:173:1:173:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:173:1:173:30 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn() |
|
||||
| tst.js:173:1:173:61 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn().getMember("from").getReturn().getMember("havingIn").getReturn() |
|
||||
| tst.js:175:1:175:13 | use moduleImport("knex").getMember("exports").getReturn().getReturn() |
|
||||
| tst.js:175:1:176:19 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("groupBy").getReturn() |
|
||||
| tst.js:175:1:177:26 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("groupBy").getReturn().getMember("orderBy").getReturn() |
|
||||
| tst.js:175:1:178:32 | use moduleImport("knex").getMember("exports").getReturn().getReturn().getMember("groupBy").getReturn().getMember("orderBy").getReturn().getMember("havingRaw").getReturn() |
|
||||
| tst.js:180:1:180:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:181:1:181:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:182:1:182:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:183:1:183:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:184:1:184:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:185:1:185:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:186:1:186:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:187:1:187:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
| tst.js:188:1:188:16 | use moduleImport("knex").getMember("exports").getReturn().getMember("select").getReturn() |
|
||||
|
||||
@@ -123,11 +123,11 @@ getAffectedStateAccessPath
|
||||
| react-redux.jsx:61:13:61:25 | manualReducer | manual |
|
||||
| trivial.js:130:14:130:46 | wrapper ... state) | wrapped |
|
||||
getADispatchFunctionNode
|
||||
| react-redux.jsx:65:20:65:32 | use (return (member useDispatch (member exports (module react-redux)))) |
|
||||
| react-redux.jsx:65:20:65:32 | use moduleImport("react-redux").getMember("exports").getMember("useDispatch").getReturn() |
|
||||
getADispatchedValueNode
|
||||
| react-redux.jsx:27:12:30:5 | def (return (member manualAction (parameter 1 (react-redux-connect)))) |
|
||||
| react-redux.jsx:69:18:69:39 | def (parameter 0 (return (member useDispatch (member exports (module react-redux))))) |
|
||||
| react-redux.jsx:70:18:70:38 | def (parameter 0 (return (member useDispatch (member exports (module react-redux))))) |
|
||||
| react-redux.jsx:27:12:30:5 | def getASuccessor(Label::entryPoint("react-redux-connect")).getParameter(1).getMember("manualAction").getReturn() |
|
||||
| react-redux.jsx:69:18:69:39 | def moduleImport("react-redux").getMember("exports").getMember("useDispatch").getReturn().getParameter(0) |
|
||||
| react-redux.jsx:70:18:70:38 | def moduleImport("react-redux").getMember("exports").getMember("useDispatch").getReturn().getParameter(0) |
|
||||
getAnUntypedActionInReducer
|
||||
| exportedReducer.js:12:20:12:25 | action |
|
||||
| react-redux.jsx:32:31:32:36 | action |
|
||||
|
||||
@@ -25,3 +25,4 @@
|
||||
query path:
|
||||
- /^experimental\/.*/
|
||||
- Metrics/Summaries/FrameworkCoverage.ql
|
||||
- /Diagnostics/Internal/.*/
|
||||
@@ -28,6 +28,7 @@
|
||||
query path:
|
||||
- /^experimental\/.*/
|
||||
- Metrics/Summaries/FrameworkCoverage.ql
|
||||
- /Diagnostics/Internal/.*/
|
||||
- exclude:
|
||||
tags contain:
|
||||
- model-generator
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
query path:
|
||||
- /^experimental\/.*/
|
||||
- Metrics/Summaries/FrameworkCoverage.ql
|
||||
- /Diagnostics/Internal/.*/
|
||||
- exclude:
|
||||
tags contain:
|
||||
- model-generator
|
||||
|
||||
@@ -959,13 +959,18 @@ private module StdlibPrivate {
|
||||
}
|
||||
}
|
||||
|
||||
/** A call to `os.path.samefile` will raise an exception if an `os.stat()` call on either pathname fails. */
|
||||
/**
|
||||
* A call to `os.path.samefile` will raise an exception if an `os.stat()` call on either pathname fails.
|
||||
*
|
||||
* See https://docs.python.org/3.10/library/os.path.html#os.path.samefile
|
||||
*/
|
||||
private class OsPathSamefileCall extends FileSystemAccess::Range, DataFlow::CallCfgNode {
|
||||
OsPathSamefileCall() { this = OS::path().getMember("samefile").getACall() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result in [
|
||||
this.getArg(0), this.getArgByName("path1"), this.getArg(1), this.getArgByName("path2")
|
||||
// note that the f1/f2 names doesn't match the documentation, but is what actually works (tested on 3.8.10)
|
||||
this.getArg(0), this.getArgByName("f1"), this.getArg(1), this.getArgByName("f2")
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -2534,6 +2539,56 @@ private module StdlibPrivate {
|
||||
PathLibOpenCall() { attrbuteName = "open" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `link_to`, `hardlink_to`, or `symlink_to` method on a `pathlib.Path` instance.
|
||||
*
|
||||
* See
|
||||
* - https://docs.python.org/3/library/pathlib.html#pathlib.Path.link_to
|
||||
* - https://docs.python.org/3/library/pathlib.html#pathlib.Path.hardlink_to
|
||||
* - https://docs.python.org/3/library/pathlib.html#pathlib.Path.symlink_to
|
||||
*/
|
||||
private class PathLibLinkToCall extends PathlibFileAccess, API::CallNode {
|
||||
PathLibLinkToCall() { attrbuteName in ["link_to", "hardlink_to", "symlink_to"] }
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result = super.getAPathArgument()
|
||||
or
|
||||
result = this.getParameter(0, "target").getARhs()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `replace` or `rename` method on a `pathlib.Path` instance.
|
||||
*
|
||||
* See
|
||||
* - https://docs.python.org/3/library/pathlib.html#pathlib.Path.replace
|
||||
* - https://docs.python.org/3/library/pathlib.html#pathlib.Path.rename
|
||||
*/
|
||||
private class PathLibReplaceCall extends PathlibFileAccess, API::CallNode {
|
||||
PathLibReplaceCall() { attrbuteName in ["replace", "rename"] }
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result = super.getAPathArgument()
|
||||
or
|
||||
result = this.getParameter(0, "target").getARhs()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `samefile` method on a `pathlib.Path` instance.
|
||||
*
|
||||
* See https://docs.python.org/3/library/pathlib.html#pathlib.Path.samefile
|
||||
*/
|
||||
private class PathLibSameFileCall extends PathlibFileAccess, API::CallNode {
|
||||
PathLibSameFileCall() { attrbuteName = "samefile" }
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result = super.getAPathArgument()
|
||||
or
|
||||
result = this.getParameter(0, "other_path").getARhs()
|
||||
}
|
||||
}
|
||||
|
||||
/** An additional taint steps for objects of type `pathlib.Path` */
|
||||
private class PathlibPathTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
* there is a def/use feature reachable from the root along the given path, and its
|
||||
* associated data-flow node must start on the same line as the comment.
|
||||
*
|
||||
* We also support negative assertions of the form `MISSING: def <path>` or `MISSING: use <path>`, which assert
|
||||
* that there _isn't_ a node with the given path on the same line.
|
||||
*
|
||||
* The query only produces output for failed assertions, meaning that it should have no output
|
||||
* under normal circumstances.
|
||||
*
|
||||
|
||||
@@ -21,3 +21,15 @@ o(name) # $ getAPathArgument=name
|
||||
|
||||
wb = p.write_bytes
|
||||
wb(b"hello") # $ getAPathArgument=p fileWriteData=b"hello"
|
||||
|
||||
p.link_to("target") # $ getAPathArgument=p getAPathArgument="target"
|
||||
p.link_to(target="target") # $ getAPathArgument=p getAPathArgument="target"
|
||||
|
||||
p.samefile("other_path") # $ getAPathArgument=p getAPathArgument="other_path"
|
||||
p.samefile(other_path="other_path") # $ getAPathArgument=p getAPathArgument="other_path"
|
||||
|
||||
p.rename("target") # $ getAPathArgument=p getAPathArgument="target"
|
||||
p.rename(target="target") # $ getAPathArgument=p getAPathArgument="target"
|
||||
|
||||
p.replace("target") # $ getAPathArgument=p getAPathArgument="target"
|
||||
p.replace(target="target") # $ getAPathArgument=p getAPathArgument="target"
|
||||
|
||||
@@ -48,6 +48,9 @@ os.path.islink(path="path") # $ getAPathArgument="path"
|
||||
os.path.ismount("path") # $ getAPathArgument="path"
|
||||
os.path.ismount(path="path") # $ getAPathArgument="path"
|
||||
|
||||
os.path.samefile("f1", "f2") # $ getAPathArgument="f1" getAPathArgument="f2"
|
||||
os.path.samefile(f1="f1", f2="f2") # $ getAPathArgument="f1" getAPathArgument="f2"
|
||||
|
||||
# actual os.path implementations
|
||||
import posixpath
|
||||
import ntpath
|
||||
@@ -269,4 +272,4 @@ shutil.copystat("src", "dst") # $ getAPathArgument="src" getAPathArgument="dst"
|
||||
shutil.copystat(src="src", dst="dst") # $ getAPathArgument="src" getAPathArgument="dst"
|
||||
|
||||
shutil.disk_usage("path") # $ getAPathArgument="path"
|
||||
shutil.disk_usage(path="path") # $ getAPathArgument="path"
|
||||
shutil.disk_usage(path="path") # $ getAPathArgument="path"
|
||||
|
||||
@@ -42,9 +42,7 @@ module AccessPath {
|
||||
* Parses a lower-bounded interval `n..` and gets the lower bound.
|
||||
*/
|
||||
bindingset[arg]
|
||||
private int parseLowerBound(string arg) {
|
||||
result = arg.regexpCapture("(-?\\d+)\\.\\.", 1).toInt()
|
||||
}
|
||||
int parseLowerBound(string arg) { result = arg.regexpCapture("(-?\\d+)\\.\\.", 1).toInt() }
|
||||
|
||||
/**
|
||||
* Parses an integer constant or interval (bounded or unbounded) that explicitly
|
||||
|
||||
@@ -268,15 +268,17 @@ private module Cached {
|
||||
TPositionalParameterPosition(int pos) {
|
||||
pos = any(Parameter p).getPosition()
|
||||
or
|
||||
pos in [0 .. 100] // TODO: remove once `Argument[_]` summaries are replaced with `Argument[i..]`
|
||||
or
|
||||
FlowSummaryImplSpecific::ParsePositions::isParsedArgumentPosition(_, pos)
|
||||
} or
|
||||
TPositionalParameterLowerBoundPosition(int pos) {
|
||||
FlowSummaryImplSpecific::ParsePositions::isParsedArgumentLowerBoundPosition(_, pos)
|
||||
} or
|
||||
TKeywordParameterPosition(string name) {
|
||||
name = any(KeywordParameter kp).getName()
|
||||
or
|
||||
FlowSummaryImplSpecific::ParsePositions::isParsedKeywordArgumentPosition(_, name)
|
||||
}
|
||||
} or
|
||||
TAnyParameterPosition()
|
||||
}
|
||||
|
||||
import Cached
|
||||
@@ -468,9 +470,18 @@ class ParameterPosition extends TParameterPosition {
|
||||
/** Holds if this position represents a positional parameter at position `pos`. */
|
||||
predicate isPositional(int pos) { this = TPositionalParameterPosition(pos) }
|
||||
|
||||
/** Holds if this position represents any positional parameter starting from position `pos`. */
|
||||
predicate isPositionalLowerBound(int pos) { this = TPositionalParameterLowerBoundPosition(pos) }
|
||||
|
||||
/** Holds if this position represents a keyword parameter named `name`. */
|
||||
predicate isKeyword(string name) { this = TKeywordParameterPosition(name) }
|
||||
|
||||
/**
|
||||
* Holds if this position represents any parameter. This includes both positional
|
||||
* and named parameters.
|
||||
*/
|
||||
predicate isAny() { this = TAnyParameterPosition() }
|
||||
|
||||
/** Gets a textual representation of this position. */
|
||||
string toString() {
|
||||
this.isSelf() and result = "self"
|
||||
@@ -479,7 +490,11 @@ class ParameterPosition extends TParameterPosition {
|
||||
or
|
||||
exists(int pos | this.isPositional(pos) and result = "position " + pos)
|
||||
or
|
||||
exists(int pos | this.isPositionalLowerBound(pos) and result = "position " + pos + "..")
|
||||
or
|
||||
exists(string name | this.isKeyword(name) and result = "keyword " + name)
|
||||
or
|
||||
this.isAny() and result = "any"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,5 +533,11 @@ predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) {
|
||||
or
|
||||
exists(int pos | ppos.isPositional(pos) and apos.isPositional(pos))
|
||||
or
|
||||
exists(int pos1, int pos2 |
|
||||
ppos.isPositionalLowerBound(pos1) and apos.isPositional(pos2) and pos2 >= pos1
|
||||
)
|
||||
or
|
||||
exists(string name | ppos.isKeyword(name) and apos.isKeyword(name))
|
||||
or
|
||||
ppos.isAny() and exists(apos)
|
||||
}
|
||||
|
||||
@@ -767,10 +767,10 @@ private module OutNodes {
|
||||
import OutNodes
|
||||
|
||||
predicate jumpStep(Node pred, Node succ) {
|
||||
SsaImpl::captureFlowIn(pred.(SsaDefinitionNode).getDefinition(),
|
||||
SsaImpl::captureFlowIn(_, pred.(SsaDefinitionNode).getDefinition(),
|
||||
succ.(SsaDefinitionNode).getDefinition())
|
||||
or
|
||||
SsaImpl::captureFlowOut(pred.(SsaDefinitionNode).getDefinition(),
|
||||
SsaImpl::captureFlowOut(_, pred.(SsaDefinitionNode).getDefinition(),
|
||||
succ.(SsaDefinitionNode).getDefinition())
|
||||
or
|
||||
succ.asExpr().getExpr().(ConstantReadAccess).getValue() = pred.asExpr().getExpr()
|
||||
|
||||
@@ -5,6 +5,7 @@ private import codeql.ruby.CFG
|
||||
private import codeql.ruby.typetracking.TypeTracker
|
||||
private import codeql.ruby.dataflow.SSA
|
||||
private import FlowSummaryImpl as FlowSummaryImpl
|
||||
private import SsaImpl as SsaImpl
|
||||
|
||||
/**
|
||||
* An element, viewed as a node in a data flow graph. Either an expression
|
||||
@@ -256,6 +257,26 @@ abstract class BarrierGuard extends CfgNodes::ExprCfgNode {
|
||||
*/
|
||||
abstract predicate checks(CfgNode expr, boolean branch);
|
||||
|
||||
/**
|
||||
* Gets an implicit entry definition for a captured variable that
|
||||
* may be guarded, because a call to the capturing callable is guarded.
|
||||
*
|
||||
* This is restricted to calls where the variable is captured inside a
|
||||
* block.
|
||||
*/
|
||||
private Ssa::Definition getAMaybeGuardedCapturedDef() {
|
||||
exists(
|
||||
boolean branch, CfgNodes::ExprCfgNode testedNode, Ssa::Definition def,
|
||||
CfgNodes::ExprNodes::CallCfgNode call
|
||||
|
|
||||
def.getARead() = testedNode and
|
||||
this.checks(testedNode, branch) and
|
||||
SsaImpl::captureFlowIn(call, def, result) and
|
||||
this.controlsBlock(call.getBasicBlock(), branch) and
|
||||
result.getBasicBlock().getScope() = call.getExpr().(MethodCall).getBlock()
|
||||
)
|
||||
}
|
||||
|
||||
final Node getAGuardedNode() {
|
||||
exists(boolean branch, CfgNodes::ExprCfgNode testedNode, Ssa::Definition def |
|
||||
def.getARead() = testedNode and
|
||||
@@ -263,5 +284,7 @@ abstract class BarrierGuard extends CfgNodes::ExprCfgNode {
|
||||
this.checks(testedNode, branch) and
|
||||
this.controlsBlock(result.asExpr().getBasicBlock(), branch)
|
||||
)
|
||||
or
|
||||
result.asExpr() = this.getAMaybeGuardedCapturedDef().getARead()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,21 +61,32 @@ predicate summaryElement(
|
||||
*
|
||||
* This covers all the Ruby-specific components of a flow summary.
|
||||
*/
|
||||
bindingset[c]
|
||||
SummaryComponent interpretComponentSpecific(AccessPathToken c) {
|
||||
c = "Argument[_]" and
|
||||
result = FlowSummary::SummaryComponent::argument(any(ParameterPosition pos | pos.isPositional(_)))
|
||||
c.getName() = "Argument" and
|
||||
exists(string arg, ParameterPosition ppos |
|
||||
arg = c.getAnArgument() and
|
||||
result = FlowSummary::SummaryComponent::argument(ppos)
|
||||
|
|
||||
arg = "any" and
|
||||
ppos.isAny()
|
||||
or
|
||||
ppos.isPositionalLowerBound(AccessPath::parseLowerBound(arg))
|
||||
)
|
||||
or
|
||||
c = "ArrayElement" and
|
||||
result = FlowSummary::SummaryComponent::arrayElementAny()
|
||||
or
|
||||
c = "ArrayElement[?]" and
|
||||
result = FlowSummary::SummaryComponent::arrayElementUnknown()
|
||||
or
|
||||
exists(int i |
|
||||
c.getName() = "ArrayElement" and
|
||||
i = AccessPath::parseInt(c.getAnArgument()) and
|
||||
result = FlowSummary::SummaryComponent::arrayElementKnown(i)
|
||||
c.getName() = "ArrayElement" and
|
||||
(
|
||||
c.getNumArgument() = 0 and
|
||||
result = FlowSummary::SummaryComponent::arrayElementAny()
|
||||
or
|
||||
exists(string arg | arg = c.getAnArgument() |
|
||||
arg = "?" and
|
||||
result = FlowSummary::SummaryComponent::arrayElementUnknown()
|
||||
or
|
||||
exists(int i |
|
||||
i = AccessPath::parseInt(c.getAnArgument()) and
|
||||
result = FlowSummary::SummaryComponent::arrayElementKnown(i)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -201,6 +212,11 @@ module ParsePositions {
|
||||
i = AccessPath::parseInt(c)
|
||||
}
|
||||
|
||||
predicate isParsedArgumentLowerBoundPosition(string c, int i) {
|
||||
isArgBody(c) and
|
||||
i = AccessPath::parseLowerBound(c)
|
||||
}
|
||||
|
||||
predicate isParsedKeywordParameterPosition(string c, string paramName) {
|
||||
isParamBody(c) and
|
||||
c = paramName + ":"
|
||||
@@ -238,6 +254,11 @@ ParameterPosition parseArgBody(string s) {
|
||||
result.isPositional(i)
|
||||
)
|
||||
or
|
||||
exists(int i |
|
||||
ParsePositions::isParsedArgumentLowerBoundPosition(s, i) and
|
||||
result.isPositionalLowerBound(i)
|
||||
)
|
||||
or
|
||||
exists(string name |
|
||||
ParsePositions::isParsedKeywordArgumentPosition(s, name) and
|
||||
result.isKeyword(name)
|
||||
|
||||
@@ -72,16 +72,18 @@ private predicate hasVariableWriteWithCapturedRead(BasicBlock bb, LocalVariable
|
||||
* Holds if the call `call` at index `i` in basic block `bb` may reach
|
||||
* a callable that reads captured variable `v`.
|
||||
*/
|
||||
private predicate capturedCallRead(Call call, BasicBlock bb, int i, LocalVariable v) {
|
||||
private predicate capturedCallRead(
|
||||
CfgNodes::ExprNodes::CallCfgNode call, BasicBlock bb, int i, LocalVariable v
|
||||
) {
|
||||
exists(CfgScope scope |
|
||||
hasVariableWriteWithCapturedRead(bb.getAPredecessor*(), v, scope) and
|
||||
call = bb.getNode(i).getNode()
|
||||
call = bb.getNode(i)
|
||||
|
|
||||
// If the read happens inside a block, we restrict to the call that
|
||||
// contains the block
|
||||
not scope instanceof Block
|
||||
or
|
||||
scope = call.(MethodCall).getBlock()
|
||||
scope = call.getExpr().(MethodCall).getBlock()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -148,16 +150,18 @@ private module Cached {
|
||||
* that writes captured variable `v`.
|
||||
*/
|
||||
cached
|
||||
predicate capturedCallWrite(Call call, BasicBlock bb, int i, LocalVariable v) {
|
||||
predicate capturedCallWrite(
|
||||
CfgNodes::ExprNodes::CallCfgNode call, BasicBlock bb, int i, LocalVariable v
|
||||
) {
|
||||
exists(CfgScope scope |
|
||||
hasVariableReadWithCapturedWrite(bb.getASuccessor*(), v, scope) and
|
||||
call = bb.getNode(i).getNode()
|
||||
call = bb.getNode(i)
|
||||
|
|
||||
// If the write happens inside a block, we restrict to the call that
|
||||
// contains the block
|
||||
not scope instanceof Block
|
||||
or
|
||||
scope = call.(MethodCall).getBlock()
|
||||
scope = call.getExpr().(MethodCall).getBlock()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -189,7 +193,7 @@ private module Cached {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate defReachesCallReadInOuterScope(
|
||||
Definition def, Call call, LocalVariable v, CfgScope scope
|
||||
Definition def, CfgNodes::ExprNodes::CallCfgNode call, LocalVariable v, CfgScope scope
|
||||
) {
|
||||
exists(BasicBlock bb, int i |
|
||||
ssaDefReachesRead(v, def, bb, i) and
|
||||
@@ -217,8 +221,8 @@ private module Cached {
|
||||
* ```
|
||||
*/
|
||||
cached
|
||||
predicate captureFlowIn(Definition def, Definition entry) {
|
||||
exists(Call call, LocalVariable v, CfgScope scope |
|
||||
predicate captureFlowIn(CfgNodes::ExprNodes::CallCfgNode call, Definition def, Definition entry) {
|
||||
exists(LocalVariable v, CfgScope scope |
|
||||
defReachesCallReadInOuterScope(def, call, v, scope) and
|
||||
hasCapturedEntryWrite(entry, v, scope)
|
||||
|
|
||||
@@ -226,7 +230,7 @@ private module Cached {
|
||||
// contains the block
|
||||
not scope instanceof Block
|
||||
or
|
||||
scope = call.(MethodCall).getBlock()
|
||||
scope = call.getExpr().(MethodCall).getBlock()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -242,7 +246,9 @@ private module Cached {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate hasCapturedExitRead(Definition exit, Call call, LocalVariable v, CfgScope scope) {
|
||||
private predicate hasCapturedExitRead(
|
||||
Definition exit, CfgNodes::ExprNodes::CallCfgNode call, LocalVariable v, CfgScope scope
|
||||
) {
|
||||
exists(BasicBlock bb, int i |
|
||||
capturedCallWrite(call, bb, i, v) and
|
||||
exit.definesAt(v, bb, i) and
|
||||
@@ -261,8 +267,8 @@ private module Cached {
|
||||
* ```
|
||||
*/
|
||||
cached
|
||||
predicate captureFlowOut(Definition def, Definition exit) {
|
||||
exists(Call call, LocalVariable v, CfgScope scope |
|
||||
predicate captureFlowOut(CfgNodes::ExprNodes::CallCfgNode call, Definition def, Definition exit) {
|
||||
exists(LocalVariable v, CfgScope scope |
|
||||
defReachesExitReadInInnerScope(def, v, scope) and
|
||||
hasCapturedExitRead(exit, call, v, _)
|
||||
|
|
||||
@@ -270,7 +276,7 @@ private module Cached {
|
||||
// contains the block
|
||||
not scope instanceof Block
|
||||
or
|
||||
scope = call.(MethodCall).getBlock()
|
||||
scope = call.getExpr().(MethodCall).getBlock()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,11 @@ private module Cached {
|
||||
exists(CfgNodes::ExprNodes::OperationCfgNode op |
|
||||
op = nodeTo.asExpr() and
|
||||
op.getAnOperand() = nodeFrom.asExpr() and
|
||||
not op.getExpr() instanceof AssignExpr
|
||||
not op.getExpr() =
|
||||
any(Expr e |
|
||||
e instanceof AssignExpr or
|
||||
e instanceof SplatExpr
|
||||
)
|
||||
)
|
||||
or
|
||||
// string interpolation of `nodeFrom` into `nodeTo`
|
||||
|
||||
@@ -390,7 +390,7 @@ module File {
|
||||
}
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[_]" and
|
||||
input = "Argument[0..]" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = false
|
||||
}
|
||||
|
||||
@@ -507,7 +507,7 @@ module Array {
|
||||
ConcatSummary() { this = "concat" }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[_].ArrayElement" and
|
||||
input = "Argument[0..].ArrayElement" and
|
||||
output = "Argument[self].ArrayElement[?]" and
|
||||
preservesValue = true
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ module String {
|
||||
}
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = ["Argument[self]", "Argument[_]"] and
|
||||
input = "Argument[self,0..]" and
|
||||
output = ["ReturnValue", "Argument[self]"] and
|
||||
preservesValue = false
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
* otherwise `CodeInjectionCustomizations` should be imported instead.
|
||||
*/
|
||||
|
||||
import codeql.ruby.DataFlow::DataFlow::PathGraph
|
||||
import codeql.ruby.DataFlow
|
||||
import codeql.ruby.TaintTracking
|
||||
import CodeInjectionCustomizations::CodeInjection
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* otherwise `ServerSideRequestForgeryCustomizations` should be imported instead.
|
||||
*/
|
||||
|
||||
import codeql.ruby.DataFlow::DataFlow::PathGraph
|
||||
import codeql.ruby.DataFlow
|
||||
import codeql.ruby.TaintTracking
|
||||
import ServerSideRequestForgeryCustomizations::ServerSideRequestForgery
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
*/
|
||||
|
||||
private import ruby
|
||||
import codeql.ruby.DataFlow::DataFlow::PathGraph
|
||||
import codeql.ruby.DataFlow
|
||||
import codeql.ruby.TaintTracking
|
||||
import UrlRedirectCustomizations
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
* otherwise `RegExpInjectionCustomizations` should be imported instead.
|
||||
*/
|
||||
|
||||
import codeql.ruby.DataFlow::DataFlow::PathGraph
|
||||
import codeql.ruby.DataFlow
|
||||
import codeql.ruby.TaintTracking
|
||||
import RegExpInjectionCustomizations
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import ruby
|
||||
import codeql.ruby.DataFlow
|
||||
import codeql.ruby.security.ServerSideRequestForgeryQuery
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where config.hasFlowPath(source, sink)
|
||||
|
||||
@@ -10,9 +10,6 @@ edges
|
||||
| array_flow.rb:2:10:2:20 | call to source : | array_flow.rb:2:9:2:20 | * ... : |
|
||||
| array_flow.rb:2:10:2:20 | call to source : | array_flow.rb:2:9:2:20 | * ... [array element 0] : |
|
||||
| array_flow.rb:2:10:2:20 | call to source : | array_flow.rb:2:9:2:20 | * ... [array element 0] : |
|
||||
| array_flow.rb:2:10:2:20 | call to source : | array_flow.rb:3:10:3:10 | a : |
|
||||
| array_flow.rb:2:10:2:20 | call to source : | array_flow.rb:4:10:4:10 | a : |
|
||||
| array_flow.rb:2:10:2:20 | call to source : | array_flow.rb:5:10:5:10 | a : |
|
||||
| array_flow.rb:3:10:3:10 | a : | array_flow.rb:3:10:3:13 | ...[...] |
|
||||
| array_flow.rb:3:10:3:10 | a [array element 0] : | array_flow.rb:3:10:3:13 | ...[...] |
|
||||
| array_flow.rb:3:10:3:10 | a [array element 0] : | array_flow.rb:3:10:3:13 | ...[...] |
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
| barrier-guards.rb:21:8:21:19 | ... == ... | barrier-guards.rb:24:5:24:7 | foo | barrier-guards.rb:21:8:21:10 | foo | true |
|
||||
| barrier-guards.rb:27:8:27:19 | ... != ... | barrier-guards.rb:28:5:28:7 | foo | barrier-guards.rb:27:8:27:10 | foo | false |
|
||||
| barrier-guards.rb:37:4:37:20 | call to include? | barrier-guards.rb:38:5:38:7 | foo | barrier-guards.rb:37:17:37:19 | foo | true |
|
||||
| barrier-guards.rb:43:4:43:15 | ... == ... | barrier-guards.rb:45:9:45:11 | foo | barrier-guards.rb:43:4:43:6 | foo | true |
|
||||
|
||||
@@ -38,4 +38,27 @@ if FOO.include?(foo)
|
||||
foo
|
||||
else
|
||||
foo
|
||||
end
|
||||
end
|
||||
|
||||
if foo == "foo"
|
||||
capture {
|
||||
foo # guarded
|
||||
}
|
||||
end
|
||||
|
||||
if foo == "foo"
|
||||
capture {
|
||||
foo = "bar"
|
||||
foo # not guarded
|
||||
}
|
||||
end
|
||||
|
||||
if foo == "foo"
|
||||
my_lambda = -> () {
|
||||
foo # not guarded
|
||||
}
|
||||
|
||||
foo = "bar"
|
||||
|
||||
my_lambda()
|
||||
end
|
||||
|
||||
@@ -19,7 +19,10 @@ edges
|
||||
| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:37:36:37:42 | tainted |
|
||||
| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:37:36:37:42 | tainted |
|
||||
| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:51:24:51:30 | tainted : |
|
||||
| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:54:23:54:29 | tainted : |
|
||||
| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:54:22:54:28 | tainted : |
|
||||
| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:55:17:55:23 | tainted : |
|
||||
| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:58:32:58:38 | tainted : |
|
||||
| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:60:23:60:29 | tainted : |
|
||||
| summaries.rb:1:20:1:36 | call to source : | summaries.rb:1:11:1:36 | call to identity : |
|
||||
| summaries.rb:1:20:1:36 | call to source : | summaries.rb:1:11:1:36 | call to identity : |
|
||||
| summaries.rb:4:12:7:3 | call to apply_block : | summaries.rb:9:6:9:13 | tainted2 |
|
||||
@@ -52,10 +55,13 @@ edges
|
||||
| summaries.rb:44:8:44:8 | t : | summaries.rb:44:8:44:27 | call to matchedByNameRcv |
|
||||
| summaries.rb:48:24:48:41 | call to source : | summaries.rb:48:8:48:42 | call to preserveTaint |
|
||||
| summaries.rb:51:24:51:30 | tainted : | summaries.rb:51:6:51:31 | call to namedArg |
|
||||
| summaries.rb:54:23:54:29 | tainted : | summaries.rb:54:40:54:40 | x : |
|
||||
| summaries.rb:54:40:54:40 | x : | summaries.rb:55:8:55:8 | x |
|
||||
| summaries.rb:62:24:62:53 | call to source : | summaries.rb:62:8:62:54 | call to preserveTaint |
|
||||
| summaries.rb:65:26:65:56 | call to source : | summaries.rb:65:8:65:57 | call to preserveTaint |
|
||||
| summaries.rb:54:22:54:28 | tainted : | summaries.rb:54:6:54:29 | call to anyArg |
|
||||
| summaries.rb:55:17:55:23 | tainted : | summaries.rb:55:6:55:24 | call to anyArg |
|
||||
| summaries.rb:58:32:58:38 | tainted : | summaries.rb:58:6:58:39 | call to anyPositionFromOne |
|
||||
| summaries.rb:60:23:60:29 | tainted : | summaries.rb:60:40:60:40 | x : |
|
||||
| summaries.rb:60:40:60:40 | x : | summaries.rb:61:8:61:8 | x |
|
||||
| summaries.rb:68:24:68:53 | call to source : | summaries.rb:68:8:68:54 | call to preserveTaint |
|
||||
| summaries.rb:71:26:71:56 | call to source : | summaries.rb:71:8:71:57 | call to preserveTaint |
|
||||
nodes
|
||||
| summaries.rb:1:11:1:36 | call to identity : | semmle.label | call to identity : |
|
||||
| summaries.rb:1:11:1:36 | call to identity : | semmle.label | call to identity : |
|
||||
@@ -112,13 +118,19 @@ nodes
|
||||
| summaries.rb:48:24:48:41 | call to source : | semmle.label | call to source : |
|
||||
| summaries.rb:51:6:51:31 | call to namedArg | semmle.label | call to namedArg |
|
||||
| summaries.rb:51:24:51:30 | tainted : | semmle.label | tainted : |
|
||||
| summaries.rb:54:23:54:29 | tainted : | semmle.label | tainted : |
|
||||
| summaries.rb:54:40:54:40 | x : | semmle.label | x : |
|
||||
| summaries.rb:55:8:55:8 | x | semmle.label | x |
|
||||
| summaries.rb:62:8:62:54 | call to preserveTaint | semmle.label | call to preserveTaint |
|
||||
| summaries.rb:62:24:62:53 | call to source : | semmle.label | call to source : |
|
||||
| summaries.rb:65:8:65:57 | call to preserveTaint | semmle.label | call to preserveTaint |
|
||||
| summaries.rb:65:26:65:56 | call to source : | semmle.label | call to source : |
|
||||
| summaries.rb:54:6:54:29 | call to anyArg | semmle.label | call to anyArg |
|
||||
| summaries.rb:54:22:54:28 | tainted : | semmle.label | tainted : |
|
||||
| summaries.rb:55:6:55:24 | call to anyArg | semmle.label | call to anyArg |
|
||||
| summaries.rb:55:17:55:23 | tainted : | semmle.label | tainted : |
|
||||
| summaries.rb:58:6:58:39 | call to anyPositionFromOne | semmle.label | call to anyPositionFromOne |
|
||||
| summaries.rb:58:32:58:38 | tainted : | semmle.label | tainted : |
|
||||
| summaries.rb:60:23:60:29 | tainted : | semmle.label | tainted : |
|
||||
| summaries.rb:60:40:60:40 | x : | semmle.label | x : |
|
||||
| summaries.rb:61:8:61:8 | x | semmle.label | x |
|
||||
| summaries.rb:68:8:68:54 | call to preserveTaint | semmle.label | call to preserveTaint |
|
||||
| summaries.rb:68:24:68:53 | call to source : | semmle.label | call to source : |
|
||||
| summaries.rb:71:8:71:57 | call to preserveTaint | semmle.label | call to preserveTaint |
|
||||
| summaries.rb:71:26:71:56 | call to source : | semmle.label | call to source : |
|
||||
subpaths
|
||||
invalidSpecComponent
|
||||
invalidOutputSpecComponent
|
||||
@@ -150,9 +162,12 @@ invalidOutputSpecComponent
|
||||
| summaries.rb:44:8:44:27 | call to matchedByNameRcv | summaries.rb:40:7:40:17 | call to source : | summaries.rb:44:8:44:27 | call to matchedByNameRcv | $@ | summaries.rb:40:7:40:17 | call to source : | call to source : |
|
||||
| summaries.rb:48:8:48:42 | call to preserveTaint | summaries.rb:48:24:48:41 | call to source : | summaries.rb:48:8:48:42 | call to preserveTaint | $@ | summaries.rb:48:24:48:41 | call to source : | call to source : |
|
||||
| summaries.rb:51:6:51:31 | call to namedArg | summaries.rb:1:20:1:36 | call to source : | summaries.rb:51:6:51:31 | call to namedArg | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : |
|
||||
| summaries.rb:55:8:55:8 | x | summaries.rb:1:20:1:36 | call to source : | summaries.rb:55:8:55:8 | x | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : |
|
||||
| summaries.rb:62:8:62:54 | call to preserveTaint | summaries.rb:62:24:62:53 | call to source : | summaries.rb:62:8:62:54 | call to preserveTaint | $@ | summaries.rb:62:24:62:53 | call to source : | call to source : |
|
||||
| summaries.rb:65:8:65:57 | call to preserveTaint | summaries.rb:65:26:65:56 | call to source : | summaries.rb:65:8:65:57 | call to preserveTaint | $@ | summaries.rb:65:26:65:56 | call to source : | call to source : |
|
||||
| summaries.rb:54:6:54:29 | call to anyArg | summaries.rb:1:20:1:36 | call to source : | summaries.rb:54:6:54:29 | call to anyArg | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : |
|
||||
| summaries.rb:55:6:55:24 | call to anyArg | summaries.rb:1:20:1:36 | call to source : | summaries.rb:55:6:55:24 | call to anyArg | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : |
|
||||
| summaries.rb:58:6:58:39 | call to anyPositionFromOne | summaries.rb:1:20:1:36 | call to source : | summaries.rb:58:6:58:39 | call to anyPositionFromOne | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : |
|
||||
| summaries.rb:61:8:61:8 | x | summaries.rb:1:20:1:36 | call to source : | summaries.rb:61:8:61:8 | x | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : |
|
||||
| summaries.rb:68:8:68:54 | call to preserveTaint | summaries.rb:68:24:68:53 | call to source : | summaries.rb:68:8:68:54 | call to preserveTaint | $@ | summaries.rb:68:24:68:53 | call to source : | call to source : |
|
||||
| summaries.rb:71:8:71:57 | call to preserveTaint | summaries.rb:71:26:71:56 | call to source : | summaries.rb:71:8:71:57 | call to preserveTaint | $@ | summaries.rb:71:26:71:56 | call to source : | call to source : |
|
||||
warning
|
||||
| CSV type row should have 5 columns but has 2: test;TooFewColumns |
|
||||
| CSV type row should have 5 columns but has 8: test;TooManyColumns;;;Member[Foo].Instance;too;many;columns |
|
||||
|
||||
@@ -78,6 +78,8 @@ private class StepsFromModel extends ModelInput::SummaryModelCsv {
|
||||
";;Member[Foo].Method[onlyWithBlock].WithBlock;Argument[0];ReturnValue;taint",
|
||||
";;Member[Foo].Method[blockArg].Argument[block].Parameter[0].Method[preserveTaint];Argument[0];ReturnValue;taint",
|
||||
";;Member[Foo].Method[namedArg];Argument[foo:];ReturnValue;taint",
|
||||
";;Member[Foo].Method[anyArg];Argument[any];ReturnValue;taint",
|
||||
";;Member[Foo].Method[anyPositionFromOne];Argument[1..];ReturnValue;taint",
|
||||
";;Member[Foo].Method[intoNamedCallback];Argument[0];Argument[foo:].Parameter[0];taint",
|
||||
";;Member[Foo].Method[intoNamedParameter];Argument[0];Argument[0].Parameter[foo:];taint",
|
||||
";;Member[Foo].Method[startInNamedCallback].Argument[foo:].Parameter[0].Method[preserveTaint];Argument[0];ReturnValue;taint",
|
||||
|
||||
@@ -51,6 +51,12 @@ end
|
||||
sink(Foo.namedArg(foo: tainted)) # $ hasTaintFlow=tainted
|
||||
sink(Foo.namedArg(tainted))
|
||||
|
||||
sink(Foo.anyArg(foo: tainted)) # $ hasTaintFlow=tainted
|
||||
sink(Foo.anyArg(tainted)) # $ hasTaintFlow=tainted
|
||||
|
||||
sink(Foo.anyPositionFromOne(tainted))
|
||||
sink(Foo.anyPositionFromOne(0, tainted)) # $ hasTaintFlow=tainted
|
||||
|
||||
Foo.intoNamedCallback(tainted, foo: ->(x) {
|
||||
sink(x) # $ hasTaintFlow=tainted
|
||||
})
|
||||
|
||||
@@ -4,6 +4,32 @@ The Swift codeql package is an experimental and unsupported work in progress.
|
||||
|
||||
## Usage
|
||||
|
||||
Run `bazel run //swift:create-extractor-pack`, which will install `swift/extractor-pack`.
|
||||
Run
|
||||
|
||||
```bash
|
||||
bazel run //swift:create-extractor-pack
|
||||
```
|
||||
|
||||
which will install `swift/extractor-pack`.
|
||||
|
||||
Using `--search-path=swift/extractor-pack` will then pick up the Swift extractor. You can also use
|
||||
`--search-path=swift`, as the extractor pack is mentioned in `swift/.codeqlmanifest.json`.
|
||||
|
||||
Notice you can run `bazel run :create-extractor-pack` if you already are in the `swift` directory.
|
||||
|
||||
## Code generation
|
||||
|
||||
Make sure to install the [pip requirements](./codegen/requirements.txt) via
|
||||
|
||||
```bash
|
||||
python3 -m pip install -r codegen/requirements.txt
|
||||
```
|
||||
|
||||
Run
|
||||
|
||||
```bash
|
||||
bazel run //swift/codegen
|
||||
```
|
||||
|
||||
to update generated files. This can be shortened to
|
||||
`bazel run codegen` if you are in the `swift` directory.
|
||||
|
||||
4
swift/codegen/BUILD.bazel
Normal file
4
swift/codegen/BUILD.bazel
Normal file
@@ -0,0 +1,4 @@
|
||||
py_binary(
|
||||
name = "codegen",
|
||||
srcs = glob(["**/*.py"]),
|
||||
)
|
||||
8
swift/codegen/codegen.py
Executable file
8
swift/codegen/codegen.py
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
""" Driver script to run all checked in code generation """
|
||||
|
||||
from lib import generator
|
||||
import dbschemegen
|
||||
|
||||
if __name__ == "__main__":
|
||||
generator.run(dbschemegen.generate)
|
||||
90
swift/codegen/dbschemegen.py
Executable file
90
swift/codegen/dbschemegen.py
Executable file
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
|
||||
import inflection
|
||||
|
||||
from lib import paths, schema, generator
|
||||
from lib.dbscheme import *
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def dbtype(typename):
|
||||
""" translate a type to a dbscheme counterpart, using `@lower_underscore` format for classes """
|
||||
if typename[0].isupper():
|
||||
return "@" + inflection.underscore(typename)
|
||||
return typename
|
||||
|
||||
|
||||
def cls_to_dbscheme(cls: schema.Class):
|
||||
""" Yield all dbscheme entities needed to model class `cls` """
|
||||
if cls.derived:
|
||||
yield DbUnion(dbtype(cls.name), (dbtype(c) for c in cls.derived))
|
||||
# output a table specific to a class only if it is a leaf class or it has 1-to-1 properties
|
||||
# Leaf classes need a table to bind the `@` ids
|
||||
# 1-to-1 properties are added to a class specific table
|
||||
# in other cases, separate tables are used for the properties, and a class specific table is unneeded
|
||||
if not cls.derived or any(f.is_single for f in cls.properties):
|
||||
binding = not cls.derived
|
||||
keyset = DbKeySet(["id"]) if cls.derived else None
|
||||
yield DbTable(
|
||||
keyset=keyset,
|
||||
name=inflection.tableize(cls.name),
|
||||
columns=[
|
||||
DbColumn("id", type=dbtype(cls.name), binding=binding),
|
||||
] + [
|
||||
DbColumn(f.name, dbtype(f.type)) for f in cls.properties if f.is_single
|
||||
]
|
||||
)
|
||||
# use property-specific tables for 1-to-many and 1-to-at-most-1 properties
|
||||
for f in cls.properties:
|
||||
if f.is_optional:
|
||||
yield DbTable(
|
||||
keyset=DbKeySet(["id"]),
|
||||
name=inflection.tableize(f"{cls.name}_{f.name}"),
|
||||
columns=[
|
||||
DbColumn("id", type=dbtype(cls.name)),
|
||||
DbColumn(f.name, dbtype(f.type)),
|
||||
],
|
||||
)
|
||||
elif f.is_repeated:
|
||||
yield DbTable(
|
||||
keyset=DbKeySet(["id", "index"]),
|
||||
name=inflection.tableize(f"{cls.name}_{f.name}"),
|
||||
columns=[
|
||||
DbColumn("id", type=dbtype(cls.name)),
|
||||
DbColumn("index", type="int"),
|
||||
DbColumn(inflection.singularize(f.name), dbtype(f.type)),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def get_declarations(data: schema.Schema):
|
||||
return [d for cls in data.classes.values() for d in cls_to_dbscheme(cls)]
|
||||
|
||||
|
||||
def get_includes(data: schema.Schema, include_dir: pathlib.Path):
|
||||
includes = []
|
||||
for inc in data.includes:
|
||||
inc = include_dir / inc
|
||||
with open(inc) as inclusion:
|
||||
includes.append(DbSchemeInclude(src=inc.relative_to(paths.swift_dir), data=inclusion.read()))
|
||||
return includes
|
||||
|
||||
|
||||
def generate(opts, renderer):
|
||||
input = opts.schema.resolve()
|
||||
out = opts.dbscheme.resolve()
|
||||
|
||||
with open(input) as src:
|
||||
data = schema.load(src)
|
||||
|
||||
dbscheme = DbScheme(src=input.relative_to(paths.swift_dir),
|
||||
includes=get_includes(data, include_dir=input.parent),
|
||||
declarations=get_declarations(data))
|
||||
|
||||
renderer.render(dbscheme, out)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
generator.run(generate, tags=["schema", "dbscheme"])
|
||||
104
swift/codegen/lib/dbscheme.py
Normal file
104
swift/codegen/lib/dbscheme.py
Normal file
@@ -0,0 +1,104 @@
|
||||
""" dbscheme format representation """
|
||||
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from typing import ClassVar, List
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
dbscheme_keywords = {"case", "boolean", "int", "string", "type"}
|
||||
|
||||
|
||||
@dataclass
|
||||
class DbColumn:
|
||||
schema_name: str
|
||||
type: str
|
||||
binding: bool = False
|
||||
first: bool = False
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
if self.schema_name in dbscheme_keywords:
|
||||
return self.schema_name + "_"
|
||||
return self.schema_name
|
||||
|
||||
@property
|
||||
def lhstype(self):
|
||||
if self.type[0] == "@":
|
||||
return "unique int" if self.binding else "int"
|
||||
return self.type
|
||||
|
||||
@property
|
||||
def rhstype(self):
|
||||
if self.type[0] == "@" and self.binding:
|
||||
return self.type
|
||||
return self.type + " ref"
|
||||
|
||||
|
||||
@dataclass
|
||||
class DbKeySetId:
|
||||
id: str
|
||||
first: bool = False
|
||||
|
||||
|
||||
@dataclass
|
||||
class DbKeySet:
|
||||
ids: List[DbKeySetId]
|
||||
|
||||
def __post_init__(self):
|
||||
assert self.ids
|
||||
self.ids = [DbKeySetId(x) for x in self.ids]
|
||||
self.ids[0].first = True
|
||||
|
||||
|
||||
class DbDecl:
|
||||
is_table = False
|
||||
is_union = False
|
||||
|
||||
|
||||
@dataclass
|
||||
class DbTable(DbDecl):
|
||||
is_table: ClassVar = True
|
||||
|
||||
name: str
|
||||
columns: List[DbColumn]
|
||||
keyset: DbKeySet = None
|
||||
|
||||
def __post_init__(self):
|
||||
if self.columns:
|
||||
self.columns[0].first = True
|
||||
|
||||
|
||||
@dataclass
|
||||
class DbUnionCase:
|
||||
type: str
|
||||
first: bool = False
|
||||
|
||||
|
||||
@dataclass
|
||||
class DbUnion(DbDecl):
|
||||
is_union: ClassVar = True
|
||||
|
||||
lhs: str
|
||||
rhs: List[DbUnionCase]
|
||||
|
||||
def __post_init__(self):
|
||||
assert self.rhs
|
||||
self.rhs = [DbUnionCase(x) for x in self.rhs]
|
||||
self.rhs.sort(key=lambda c: c.type)
|
||||
self.rhs[0].first = True
|
||||
|
||||
|
||||
@dataclass
|
||||
class DbSchemeInclude:
|
||||
src: str
|
||||
data: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class DbScheme:
|
||||
template: ClassVar = 'dbscheme'
|
||||
|
||||
src: str
|
||||
includes: List[DbSchemeInclude]
|
||||
declarations: List[DbDecl]
|
||||
29
swift/codegen/lib/generator.py
Normal file
29
swift/codegen/lib/generator.py
Normal file
@@ -0,0 +1,29 @@
|
||||
""" generator script scaffolding """
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from . import options, render
|
||||
|
||||
|
||||
def _parse(tags):
|
||||
parser = argparse.ArgumentParser()
|
||||
for opt in options.get(tags):
|
||||
opt.add_to(parser)
|
||||
ret = parser.parse_args()
|
||||
log_level = logging.DEBUG if ret.verbose else logging.INFO
|
||||
logging.basicConfig(format="{levelname} {message}", style='{', level=log_level)
|
||||
return ret
|
||||
|
||||
|
||||
def run(*generators, tags=None):
|
||||
""" run generation functions in `generators`, parsing options tagged with `tags` (all if unspecified)
|
||||
|
||||
`generators` should be callables taking as input an option namespace and a `render.Renderer` instance
|
||||
"""
|
||||
opts = _parse(tags)
|
||||
renderer = render.Renderer(dryrun=opts.check)
|
||||
for g in generators:
|
||||
g(opts, renderer)
|
||||
sys.exit(1 if opts.check and renderer.done_something else 0)
|
||||
48
swift/codegen/lib/options.py
Normal file
48
swift/codegen/lib/options.py
Normal file
@@ -0,0 +1,48 @@
|
||||
""" generator options, categorized by tags """
|
||||
|
||||
import argparse
|
||||
import collections
|
||||
import pathlib
|
||||
from typing import Tuple
|
||||
|
||||
from . import paths
|
||||
|
||||
|
||||
def _init_options():
|
||||
Option("--check", "-c", action="store_true")
|
||||
Option("--verbose", "-v", action="store_true")
|
||||
Option("--schema", tags=["schema"], type=pathlib.Path, default=paths.swift_dir / "codegen/schema.yml")
|
||||
Option("--dbscheme", tags=["dbscheme"], type=pathlib.Path, default=paths.swift_dir / "ql/lib/swift.dbscheme")
|
||||
|
||||
|
||||
_options = collections.defaultdict(list)
|
||||
|
||||
|
||||
class Option:
|
||||
def __init__(self, *args, tags=None, **kwargs):
|
||||
tags = tags or []
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
if tags:
|
||||
for t in tags:
|
||||
_options[t].append(self)
|
||||
else:
|
||||
_options["*"].append(self)
|
||||
|
||||
def add_to(self, parser: argparse.ArgumentParser):
|
||||
parser.add_argument(*self.args, **self.kwargs)
|
||||
|
||||
|
||||
_init_options()
|
||||
|
||||
|
||||
def get(tags: Tuple[str]):
|
||||
""" get options marked by `tags`
|
||||
|
||||
Return all options if tags is falsy. Options tagged by wildcard '*' are always returned
|
||||
"""
|
||||
if not tags:
|
||||
return (o for tagged_opts in _options.values() for o in tagged_opts)
|
||||
else:
|
||||
# use specifically tagged options + those tagged with wildcard *
|
||||
return (o for tag in ('*',) + tags for o in _options[tag])
|
||||
17
swift/codegen/lib/paths.py
Normal file
17
swift/codegen/lib/paths.py
Normal file
@@ -0,0 +1,17 @@
|
||||
""" module providing useful filesystem paths """
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
import os
|
||||
|
||||
try:
|
||||
_workspace_dir = pathlib.Path(os.environ['BUILD_WORKSPACE_DIRECTORY']) # <- means we are using bazel run
|
||||
swift_dir = _workspace_dir / 'swift'
|
||||
lib_dir = swift_dir / 'codegen' / 'lib'
|
||||
except KeyError:
|
||||
_this_file = pathlib.Path(__file__).resolve()
|
||||
swift_dir = _this_file.parents[2]
|
||||
lib_dir = _this_file.parent
|
||||
|
||||
|
||||
exe_file = pathlib.Path(sys.argv[0]).resolve()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user