Compare commits

...

62 Commits

Author SHA1 Message Date
Chris Smowton
b512d65918 Claim Java 17 support 2022-04-22 16:25:08 +01:00
Robert Marsh
ebdf553621 Merge pull request #8806 from dbartol/dbartol/typedefs/work
C++: Work around missing size for typedef
2022-04-22 10:09:24 -04:00
Rasmus Wriedt Larsen
3e8274ede8 Merge pull request #8812 from RasmusWL/stdlib-FileSystemAccess-improvement
Python: Minor Stdlib file system access improvement
2022-04-22 16:06:41 +02:00
CodeQL CI
06e5962da7 Merge pull request #8791 from asgerf/js/static-accessors
Approved by erik-krogh
2022-04-22 13:39:32 +01:00
Rasmus Wriedt Larsen
03c0366fd4 Merge branch 'main' into stdlib-FileSystemAccess-improvement 2022-04-22 14:31:31 +02:00
Erik Krogh Kristensen
789b0a46d1 Merge pull request #8578 from erik-krogh/labelNaming
JS: update `toString()` on API-graph labels.
2022-04-22 14:27:25 +02:00
Erik Krogh Kristensen
8fcbaea273 Merge branch 'main' into labelNaming 2022-04-22 13:19:44 +02:00
Erik Krogh Kristensen
c015ef6ef4 Merge pull request #8810 from erik-krogh/rubyPathgraph
Ruby: dont import the PathGraph module from Query.qll files
2022-04-22 12:02:59 +02:00
Mathias Vorreiter Pedersen
489355cdab Merge pull request #8793 from MathiasVP/exclude-internal-diagnostics
Exclude internal diagnostics from all selectors
2022-04-22 10:55:38 +01:00
Tom Hvitved
093a3879be Merge pull request #8794 from hvitved/ruby/capture-barrier-guards
Ruby: Handle captured variables in `BarrierGuard::getAGuardedNode()`
2022-04-22 11:47:36 +02:00
Erik Krogh Kristensen
a737350f27 RB: dont import the PathGraph module from Query.qll files 2022-04-22 11:46:06 +02:00
Anders Schack-Mulligen
bf921177f4 Merge pull request #8811 from erik-krogh/syncLate
Java: get tainttracking3/TaintTrackingImpl.qll in sync
2022-04-22 11:21:01 +02:00
Asger F
0187e9a3b1 Merge pull request #8808 from vovikhangcdv/doublevkay/fixing-PrototypePollutngAssignment-examples
Javascript: Fix PrototypePollutingAssignment example which is incorrect use of express leads to no result when scanning.
2022-04-22 11:18:23 +02:00
Tom Hvitved
be5363ea53 Merge pull request #8801 from hvitved/ruby/exclude-splat-in-taint-tracking
Ruby: Exclude `SplatExpr` from taint tracking
2022-04-22 11:12:05 +02:00
Rasmus Wriedt Larsen
650d57083b Python: Recognize path arguments to pathlib methods 2022-04-22 11:01:59 +02:00
Rasmus Wriedt Larsen
bcaba45202 Python: Expand pathlib tests 2022-04-22 11:01:59 +02:00
Rasmus Wriedt Larsen
059dea713d Python: Fix os.path.samefile modeling 2022-04-22 11:01:59 +02:00
Erik Krogh Kristensen
dca74a1f45 get tainttracking3/TaintTrackingImpl.qll in sync 2022-04-22 10:37:31 +02:00
Chris Smowton
d309e15072 Merge pull request #8748 from smowton/smowton/admin/dependent-dataflow-configs
Java: Avoid higher-numbered dataflow configs that depend on lower-numbered ones
2022-04-22 08:56:00 +01:00
Mathias Vorreiter Pedersen
35471ff23c Merge pull request #8809 from AlexDenisov/alexdenisov/rename-swift-db-columns
Swift: rename certain dbscheme columns
2022-04-22 08:17:37 +01:00
Mathias Vorreiter Pedersen
52dc016a7a Merge pull request #8798 from jketema/using
C++: Fix tests after extractor changes that improve `using` position accuracy
2022-04-22 08:15:34 +01:00
Alex Denisov
e85cdf2ec3 Swift: rename certain dbscheme columns 2022-04-22 08:31:09 +02:00
AlexDenisov
a5189eae9f Merge pull request #8735 from redsun82/swift-dbscheme-gen
Swift: dbscheme generator
2022-04-22 08:26:59 +02:00
Khang. Võ Vĩ
f4581ae866 fix PrototypePollutingAssignment examples 2022-04-22 11:55:45 +07:00
Dave Bartolomeo
83fdff54c4 Work around missing size for typedef
See #8805 for the underlying issue. The symptom was bad IR generation on a large number of functions because we never generated an `IRVariable` for a parameter with the affected type.
2022-04-21 16:16:09 -04:00
Tom Hvitved
c20ce62767 Ruby: Exclude SplatExpr from taint tracking
`SplatExpr`s are modelled using flow summaries, so there is no need to include them
explicitly in `defaultAdditionalTaintStep`.
2022-04-21 20:27:04 +02:00
Mathias Vorreiter Pedersen
813de65118 Merge pull request #8799 from jketema/comment-fix
C++: Fix the layout of comments in `getBufferSize`
2022-04-21 17:17:42 +01:00
Jeroen Ketema
a09fd8c35e C++: Fix the layout of comments in getBufferSize 2022-04-21 17:42:10 +02:00
Jeroen Ketema
8139e1a2a8 C++: Fix tests after extractor changes that improve using position accuracy 2022-04-21 17:36:11 +02:00
Tom Hvitved
bd09c61504 Merge pull request #8786 from hvitved/ruby/dataflow/argument-tokens
Ruby: Implement `Argument[any]` and `Argument[n..]`
2022-04-21 16:31:24 +02:00
Michael Nebel
0ec5aa6095 Merge pull request #8675 from michaelnebel/csharp/capturemodelimprovement
C#: CaptureModel improvements
2022-04-21 15:16:35 +02:00
Tom Hvitved
addb92f13b Ruby: Handle captured variables in BarrierGuard::getAGuardedNode() 2022-04-21 13:25:47 +02:00
Tom Hvitved
325b451288 Ruby: Add barrier guards test involving captured variables 2022-04-21 13:25:40 +02:00
Michael Nebel
6180970ae7 C#: Address review comments. 2022-04-21 13:05:32 +02:00
Mathias Vorreiter Pedersen
74906fcbaf Add exclusion rules for internal diagnostics queries to all the suite selectors (previously it was only excluded in the Code Scanning selector). 2022-04-21 10:11:26 +01:00
Asger Feldthaus
c6e66edb97 JS: Change note 2022-04-21 08:32:01 +02:00
Tom Hvitved
ea229d361c Sync files 2022-04-20 13:55:18 +02:00
Tom Hvitved
b4542c58c2 Ruby: Implement Argument[any] and Argument[n..] 2022-04-20 13:55:18 +02:00
Asger Feldthaus
44216b29a9 JS: Autoformat 2022-04-20 11:14:42 +02:00
Asger Feldthaus
4c66f50352 JS: More tests 2022-04-20 11:14:42 +02:00
Asger Feldthaus
fec2837c1e JS: Ensure accessors do not appear to be calls 2022-04-20 11:14:42 +02:00
Asger Feldthaus
ddb682b181 JS: Show all accessor calls in CG test 2022-04-20 11:14:41 +02:00
Asger Feldthaus
37a76f4441 JS: PropWrite is not a SourceNode 2022-04-20 11:14:41 +02:00
Asger Feldthaus
c9db6201ef JS: Add call-graph test for accessor calls 2022-04-20 11:14:41 +02:00
Asger Feldthaus
7d5c80433d JS: Handle accessor-calls to static accessors 2022-04-20 11:14:41 +02:00
Asger Feldthaus
37b3a6e5c0 JS: Add ClassNode.getStaticMember 2022-04-20 11:14:41 +02:00
Michael Nebel
c79c9dd573 C#: Don't generate models for any higher order callables. 2022-04-19 12:50:51 +02:00
Michael Nebel
8726766465 C#: Remove the API special case for GetHashCode, Equals and IEquatable.Equals as these are now excluded based on their type. 2022-04-19 12:50:51 +02:00
Michael Nebel
f6fd401df1 C#: Add some testcases, where we don't get a summary due to the use of simple types. 2022-04-19 12:50:51 +02:00
Michael Nebel
f9e5c6b77d C#: Don't use simple types in summaries test cases as these will be excluded in generation purely based on the type. 2022-04-19 12:50:51 +02:00
Michael Nebel
f533636ad7 C#: Remove taint when it flows via a primitive/simple type (as is the case for java). 2022-04-19 12:50:51 +02:00
Chris Smowton
90505949c7 Generally define lower-numbered data-flow configs in terms of higher-numbered ones
Since usually we have DataFlow3::Configurations that stand alone, DataFlow2::Configurations that depend on them, and finally DataFlow::Configurations that produce a top-level query result (for example), qll files where the reverse pattern holds will usually not be concurrently importable due to dataflow configuration recursion prevention.
2022-04-15 09:25:40 +01:00
Chris Smowton
27d87e9300 Add TaintTracking3 2022-04-15 09:25:26 +01:00
Paolo Tranquilli
24697feebc Swift: integrated template name in dataclass 2022-04-14 15:53:15 +02:00
Paolo Tranquilli
197ea5b8f3 Swift: use more @property in codegen 2022-04-14 12:28:52 +02:00
Paolo Tranquilli
71f9b25500 Swift: uses classes instead of Enum for Properties 2022-04-14 11:35:11 +02:00
Paolo Tranquilli
64496b4c97 Swift: cleanup and some docstrings for codegen
Also added code generation and clang formatting to the pre-commit
configuration.
2022-04-14 11:27:41 +02:00
Paolo Tranquilli
91fd83a554 Swift: dbscheme generator
This patch introduces the basic infrastructure of the code generation
suite and the `dbscheme` generator.

Notice that the checked in `schema.yml` should reflect swift 5.6 but
might need some tweaking.

Closes https://github.com/github/codeql-c-team/issues/979
2022-04-14 11:27:41 +02:00
Erik Krogh Kristensen
ae3b32409a update expected output of tests that relied on API::Node::toString() 2022-03-29 10:59:08 +02:00
Erik Krogh Kristensen
20599d1846 Merge branch 'main' of github.com:github/codeql into labelNaming 2022-03-28 15:30:33 +02:00
Erik Krogh Kristensen
e79eecb640 update toString() of API::Node, and update expected output that depends on the former 2022-03-28 15:23:45 +02:00
Erik Krogh Kristensen
c5fb19c377 update the JS API-graph labels toString() to print the predicate calls on the API-graphs 2022-03-28 13:19:16 +02:00
107 changed files with 4530 additions and 487 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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;
}
@@ -61,3 +61,41 @@ 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);
}
}

View File

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

View File

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

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

View File

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

View File

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

View File

@@ -0,0 +1,5 @@
import semmle.code.java.dataflow.internal.TaintTrackingUtil as Public
module Private {
import semmle.code.java.dataflow.DataFlow3::DataFlow3 as DataFlow
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1 +1 @@
module.exports = CustomEntryPoint.foo; /* use (member foo (CustomEntryPoint)) */
module.exports = CustomEntryPoint.foo; /* use=getASuccessor(Label::entryPoint("CustomEntryPoint")) */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -26,3 +26,9 @@ D.prototype = {
set setter(x) {},
m() {}
}
class StaticMembers {
static method() {}
static get getter() {}
static set setter(x) {}
}

View File

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

View File

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

View File

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

View File

@@ -25,3 +25,4 @@
query path:
- /^experimental\/.*/
- Metrics/Summaries/FrameworkCoverage.ql
- /Diagnostics/Internal/.*/

View File

@@ -28,6 +28,7 @@
query path:
- /^experimental\/.*/
- Metrics/Summaries/FrameworkCoverage.ql
- /Diagnostics/Internal/.*/
- exclude:
tags contain:
- model-generator

View File

@@ -33,6 +33,7 @@
query path:
- /^experimental\/.*/
- Metrics/Summaries/FrameworkCoverage.ql
- /Diagnostics/Internal/.*/
- exclude:
tags contain:
- model-generator

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,7 +6,6 @@
*/
private import ruby
import codeql.ruby.DataFlow::DataFlow::PathGraph
import codeql.ruby.DataFlow
import codeql.ruby.TaintTracking
import UrlRedirectCustomizations

View File

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

View File

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

View File

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

View File

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

View File

@@ -39,3 +39,26 @@ if FOO.include?(foo)
else
foo
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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,4 @@
py_binary(
name = "codegen",
srcs = glob(["**/*.py"]),
)

8
swift/codegen/codegen.py Executable file
View 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
View 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"])

View 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]

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

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

View 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